summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore341
-rw-r--r--.gitlab-ci.yml271
-rw-r--r--AUTHORS136
-rw-r--r--ChangeLog0
-rw-r--r--Makefile.am59
-rw-r--r--NEWS1023
-rw-r--r--README12
-rw-r--r--RELEASING79
-rw-r--r--TODO2
-rwxr-xr-xautogen.sh10
-rw-r--r--build-aux/Makefile.am10
-rw-r--r--build-aux/header-generator.xsl3
l---------build-aux/mm-daemon-enums-types.c.template1
l---------build-aux/mm-daemon-enums-types.h.template1
-rw-r--r--build-aux/mm-enums-types.c.template (renamed from build-aux/mm-enums-template.c)10
-rw-r--r--build-aux/mm-enums-types.h.template (renamed from build-aux/mm-enums-template.h)0
-rw-r--r--build-aux/mm-errors-quarks.c.template (renamed from build-aux/mm-errors-quarks-template.c)0
-rw-r--r--build-aux/mm-errors-types.c.template (renamed from build-aux/mm-errors-template.c)8
-rw-r--r--build-aux/mm-errors-types.h.template (renamed from build-aux/mm-errors-template.h)0
l---------build-aux/mm-helper-enums-types.c.template1
l---------build-aux/mm-helper-enums-types.h.template1
l---------build-aux/mm-huawei-enums-types.c.template1
l---------build-aux/mm-huawei-enums-types.h.template1
l---------build-aux/mm-port-enums-types.c.template1
l---------build-aux/mm-port-enums-types.h.template1
l---------build-aux/mm-telit-enums-types.c.template1
l---------build-aux/mm-telit-enums-types.h.template1
l---------build-aux/mm-ublox-enums-types.c.template1
l---------build-aux/mm-ublox-enums-types.h.template1
-rw-r--r--cli/Makefile.am39
-rw-r--r--cli/meson.build48
-rw-r--r--cli/mmcli-bearer.c316
-rw-r--r--cli/mmcli-call.c587
-rw-r--r--cli/mmcli-common.c1425
-rw-r--r--cli/mmcli-common.h128
-rw-r--r--cli/mmcli-completion175
-rw-r--r--cli/mmcli-manager.c316
-rw-r--r--cli/mmcli-modem-3gpp-profile-manager.c373
-rw-r--r--cli/mmcli-modem-3gpp-ussd.c364
-rw-r--r--cli/mmcli-modem-3gpp.c497
-rw-r--r--cli/mmcli-modem-cdma.c8
-rw-r--r--cli/mmcli-modem-firmware.c119
-rw-r--r--cli/mmcli-modem-location.c646
-rw-r--r--cli/mmcli-modem-messaging.c77
-rw-r--r--cli/mmcli-modem-oma.c63
-rw-r--r--cli/mmcli-modem-sar.c370
-rw-r--r--cli/mmcli-modem-signal.c267
-rw-r--r--cli/mmcli-modem-simple.c170
-rw-r--r--cli/mmcli-modem-time.c44
-rw-r--r--cli/mmcli-modem-voice.c778
-rw-r--r--cli/mmcli-modem.c556
-rw-r--r--cli/mmcli-output.c1608
-rw-r--r--cli/mmcli-output.h393
-rw-r--r--cli/mmcli-sim.c154
-rw-r--r--cli/mmcli-sms.c126
-rw-r--r--cli/mmcli.c110
-rw-r--r--cli/mmcli.h43
-rw-r--r--configure.ac518
-rw-r--r--data/Makefile.am31
-rw-r--r--data/ModemManager-icon.svg66
-rw-r--r--data/ModemManager.service.in10
-rw-r--r--data/its/polkit.its8
-rw-r--r--data/its/polkit.loc6
-rw-r--r--data/meson.build74
-rw-r--r--data/org.freedesktop.ModemManager1.conf.polkit163
-rw-r--r--data/org.freedesktop.ModemManager1.policy.in.in48
-rw-r--r--data/tests/org.freedesktop.ModemManager1.service.in2
-rw-r--r--data/valgrind.suppressions487
-rw-r--r--decode/packet.py2
-rw-r--r--docs/man/Makefile.am2
-rw-r--r--docs/man/ModemManager.847
-rw-r--r--docs/man/meson.build9
-rw-r--r--docs/man/mmcli.1 (renamed from docs/man/mmcli.8)446
-rw-r--r--docs/reference/api/Makefile.am17
-rw-r--r--docs/reference/api/ModemManager-dbus-reference.xml13
-rw-r--r--docs/reference/api/ModemManager-docs.xml107
-rw-r--r--docs/reference/api/ModemManager-migration-reference.xml2
-rw-r--r--docs/reference/api/ModemManager-overview.xml707
-rw-r--r--docs/reference/api/ModemManager-sections.txt167
-rw-r--r--docs/reference/api/meson.build57
-rw-r--r--docs/reference/libmm-glib/Makefile.am9
-rw-r--r--docs/reference/libmm-glib/libmm-glib-docs.xml140
-rw-r--r--docs/reference/libmm-glib/libmm-glib-sections.txt1119
-rw-r--r--docs/reference/libmm-glib/libmm-glib.types118
-rw-r--r--docs/reference/libmm-glib/meson.build52
-rw-r--r--examples/Makefile.am8
-rw-r--r--examples/modem-watcher-javascript/modemWatcher.js2
-rw-r--r--examples/modem-watcher-python/ModemWatcher.py86
-rwxr-xr-xexamples/modem-watcher-python/modem-watcher-python31
-rw-r--r--examples/network-scan-python/Makefile.am4
-rw-r--r--examples/network-scan-python/README17
-rwxr-xr-xexamples/network-scan-python/network-scan-python74
-rw-r--r--examples/sms-c/Makefile.am49
-rw-r--r--examples/sms-c/meson.build16
-rw-r--r--examples/sms-c/sms-c-async.c197
-rw-r--r--examples/sms-c/sms-c-sync.c143
-rw-r--r--examples/sms-python/Makefile.am4
-rw-r--r--examples/sms-python/README20
-rwxr-xr-xexamples/sms-python/sms-python59
-rw-r--r--gtester.make20
-rw-r--r--gtk-doc.make320
-rw-r--r--include/Makefile.am4
-rw-r--r--include/ModemManager-compat.h1071
-rw-r--r--include/ModemManager-enums.h891
-rw-r--r--include/ModemManager-errors.h442
-rw-r--r--include/ModemManager-tags.h282
-rw-r--r--include/ModemManager-version.h.in10
-rw-r--r--include/ModemManager.h5
-rw-r--r--include/meson.build41
-rw-r--r--introspection/Makefile.am28
-rw-r--r--introspection/all.xml4
-rw-r--r--introspection/meson.build36
-rw-r--r--introspection/org.freedesktop.ModemManager1.Bearer.xml288
-rw-r--r--introspection/org.freedesktop.ModemManager1.Call.xml236
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml223
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Location.xml162
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml21
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml146
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml17
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml172
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml25
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Oma.xml23
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Sar.xml102
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Signal.xml222
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Simple.xml88
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Time.xml11
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Voice.xml217
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.xml306
-rw-r--r--introspection/org.freedesktop.ModemManager1.Sim.xml91
-rw-r--r--introspection/org.freedesktop.ModemManager1.Sms.xml50
-rw-r--r--introspection/org.freedesktop.ModemManager1.xml109
-rw-r--r--libmm-glib/Makefile.am83
-rw-r--r--libmm-glib/generated/Makefile.am134
-rw-r--r--libmm-glib/generated/meson.build129
-rw-r--r--libmm-glib/generated/tests/Makefile.am3
-rw-r--r--libmm-glib/generated/tests/meson.build26
-rw-r--r--libmm-glib/libmm-glib.h17
-rw-r--r--libmm-glib/meson.build181
-rw-r--r--libmm-glib/mm-3gpp-profile.c753
-rw-r--r--libmm-glib/mm-3gpp-profile.h135
-rw-r--r--libmm-glib/mm-bearer-ip-config.c71
-rw-r--r--libmm-glib/mm-bearer-ip-config.h3
-rw-r--r--libmm-glib/mm-bearer-properties.c701
-rw-r--r--libmm-glib/mm-bearer-properties.h58
-rw-r--r--libmm-glib/mm-bearer-stats.c586
-rw-r--r--libmm-glib/mm-bearer-stats.h102
-rw-r--r--libmm-glib/mm-bearer.c567
-rw-r--r--libmm-glib/mm-bearer.h28
-rw-r--r--libmm-glib/mm-call-audio-format.c275
-rw-r--r--libmm-glib/mm-call-audio-format.h87
-rw-r--r--libmm-glib/mm-call-properties.c293
-rw-r--r--libmm-glib/mm-call-properties.h81
-rw-r--r--libmm-glib/mm-call.c967
-rw-r--r--libmm-glib/mm-call.h184
-rw-r--r--libmm-glib/mm-cdma-manual-activation-properties.c104
-rw-r--r--libmm-glib/mm-cdma-manual-activation-properties.h1
-rw-r--r--libmm-glib/mm-common-helpers.c1739
-rw-r--r--libmm-glib/mm-common-helpers.h303
-rw-r--r--libmm-glib/mm-compat.c185
-rw-r--r--libmm-glib/mm-compat.h385
-rw-r--r--libmm-glib/mm-firmware-properties.c88
-rw-r--r--libmm-glib/mm-firmware-properties.h1
-rw-r--r--libmm-glib/mm-firmware-update-settings.c348
-rw-r--r--libmm-glib/mm-firmware-update-settings.h96
-rw-r--r--libmm-glib/mm-helper-types.c2
-rw-r--r--libmm-glib/mm-helper-types.h9
-rw-r--r--libmm-glib/mm-helpers.h289
-rw-r--r--libmm-glib/mm-kernel-event-properties.c459
-rw-r--r--libmm-glib/mm-kernel-event-properties.h96
-rw-r--r--libmm-glib/mm-location-3gpp.c340
-rw-r--r--libmm-glib/mm-location-3gpp.h27
-rw-r--r--libmm-glib/mm-location-cdma-bs.c16
-rw-r--r--libmm-glib/mm-location-cdma-bs.h1
-rw-r--r--libmm-glib/mm-location-common.h12
-rw-r--r--libmm-glib/mm-location-gps-nmea.c85
-rw-r--r--libmm-glib/mm-location-gps-nmea.h7
-rw-r--r--libmm-glib/mm-location-gps-raw.c68
-rw-r--r--libmm-glib/mm-location-gps-raw.h1
-rw-r--r--libmm-glib/mm-manager.c640
-rw-r--r--libmm-glib/mm-manager.h43
-rw-r--r--libmm-glib/mm-modem-3gpp-profile-manager.c489
-rw-r--r--libmm-glib/mm-modem-3gpp-profile-manager.h112
-rw-r--r--libmm-glib/mm-modem-3gpp-ussd.c159
-rw-r--r--libmm-glib/mm-modem-3gpp-ussd.h1
-rw-r--r--libmm-glib/mm-modem-3gpp.c889
-rw-r--r--libmm-glib/mm-modem-3gpp.h88
-rw-r--r--libmm-glib/mm-modem-cdma.c117
-rw-r--r--libmm-glib/mm-modem-cdma.h1
-rw-r--r--libmm-glib/mm-modem-firmware.c153
-rw-r--r--libmm-glib/mm-modem-firmware.h9
-rw-r--r--libmm-glib/mm-modem-location.c882
-rw-r--r--libmm-glib/mm-modem-location.h50
-rw-r--r--libmm-glib/mm-modem-messaging.c361
-rw-r--r--libmm-glib/mm-modem-messaging.h1
-rw-r--r--libmm-glib/mm-modem-oma.c270
-rw-r--r--libmm-glib/mm-modem-oma.h13
-rw-r--r--libmm-glib/mm-modem-sar.c306
-rw-r--r--libmm-glib/mm-modem-sar.h104
-rw-r--r--libmm-glib/mm-modem-signal.c613
-rw-r--r--libmm-glib/mm-modem-signal.h60
-rw-r--r--libmm-glib/mm-modem-simple.c165
-rw-r--r--libmm-glib/mm-modem-simple.h1
-rw-r--r--libmm-glib/mm-modem-time.c162
-rw-r--r--libmm-glib/mm-modem-time.h1
-rw-r--r--libmm-glib/mm-modem-voice.c1141
-rw-r--r--libmm-glib/mm-modem-voice.h183
-rw-r--r--libmm-glib/mm-modem.c2225
-rw-r--r--libmm-glib/mm-modem.h43
-rw-r--r--libmm-glib/mm-network-timezone.c29
-rw-r--r--libmm-glib/mm-network-timezone.h7
-rw-r--r--libmm-glib/mm-object.c359
-rw-r--r--libmm-glib/mm-object.h54
-rw-r--r--libmm-glib/mm-pco.c317
-rw-r--r--libmm-glib/mm-pco.h89
-rw-r--r--libmm-glib/mm-signal-threshold-properties.c322
-rw-r--r--libmm-glib/mm-signal-threshold-properties.h86
-rw-r--r--libmm-glib/mm-signal.c158
-rw-r--r--libmm-glib/mm-signal.h44
-rw-r--r--libmm-glib/mm-sim-preferred-network.c229
-rw-r--r--libmm-glib/mm-sim-preferred-network.h71
-rw-r--r--libmm-glib/mm-sim.c458
-rw-r--r--libmm-glib/mm-sim.h24
-rw-r--r--libmm-glib/mm-simple-connect-properties.c240
-rw-r--r--libmm-glib/mm-simple-connect-properties.h34
-rw-r--r--libmm-glib/mm-simple-status.c116
-rw-r--r--libmm-glib/mm-simple-status.h3
-rw-r--r--libmm-glib/mm-sms-properties.c120
-rw-r--r--libmm-glib/mm-sms-properties.h5
-rw-r--r--libmm-glib/mm-sms.c194
-rw-r--r--libmm-glib/mm-sms.h1
-rw-r--r--libmm-glib/mm-unlock-retries.c29
-rw-r--r--libmm-glib/mm-unlock-retries.h5
-rw-r--r--libmm-glib/tests/Makefile.am24
-rw-r--r--libmm-glib/tests/meson.build21
-rw-r--r--libmm-glib/tests/test-common-helpers.c140
-rw-r--r--libmm-glib/tests/test-pco.c94
-rw-r--r--libqcdm/src/Makefile.am15
-rw-r--r--libqcdm/src/com.c2
-rw-r--r--libqcdm/src/commands.c215
-rw-r--r--libqcdm/src/commands.h74
-rw-r--r--libqcdm/src/dm-commands.h381
-rw-r--r--libqcdm/src/log-items.h160
-rw-r--r--libqcdm/src/logs.c183
-rw-r--r--libqcdm/src/logs.h50
-rw-r--r--libqcdm/src/meson.build29
-rw-r--r--libqcdm/src/nv-items.h19
-rw-r--r--libqcdm/src/result-private.h11
-rw-r--r--libqcdm/src/result.c43
-rw-r--r--libqcdm/src/result.h9
-rw-r--r--libqcdm/src/utils.c13
-rw-r--r--libqcdm/src/utils.h11
-rw-r--r--libqcdm/tests/Makefile.am10
-rw-r--r--libqcdm/tests/ipv6pref.c13
-rw-r--r--libqcdm/tests/meson.build37
-rw-r--r--libqcdm/tests/modepref.c27
-rw-r--r--libqcdm/tests/reset.c10
-rw-r--r--libqcdm/tests/test-qcdm-com.c39
-rw-r--r--libqcdm/tests/test-qcdm-result.c5
-rw-r--r--libqcdm/tests/test-qcdm.c16
-rw-r--r--libwmc/AUTHORS0
-rw-r--r--libwmc/ChangeLog0
-rw-r--r--libwmc/Makefile.am2
-rw-r--r--libwmc/NEWS0
-rw-r--r--libwmc/README0
-rwxr-xr-xlibwmc/autogen.sh22
-rw-r--r--libwmc/configure.ac48
-rw-r--r--libwmc/src/Makefile.am23
-rw-r--r--libwmc/src/com.c56
-rw-r--r--libwmc/src/com.h23
-rw-r--r--libwmc/src/commands.c467
-rw-r--r--libwmc/src/commands.h116
-rw-r--r--libwmc/src/errors.c58
-rw-r--r--libwmc/src/errors.h78
-rw-r--r--libwmc/src/protocol.h462
-rw-r--r--libwmc/src/result-private.h38
-rw-r--r--libwmc/src/result.c298
-rw-r--r--libwmc/src/result.h42
-rw-r--r--libwmc/src/utils.c460
-rw-r--r--libwmc/src/utils.h88
-rw-r--r--libwmc/tests/Makefile.am28
-rw-r--r--libwmc/tests/test-wmc-com.c505
-rw-r--r--libwmc/tests/test-wmc-com.h37
-rw-r--r--libwmc/tests/test-wmc-crc.c65
-rw-r--r--libwmc/tests/test-wmc-crc.h25
-rw-r--r--libwmc/tests/test-wmc-escaping.c165
-rw-r--r--libwmc/tests/test-wmc-escaping.h28
-rw-r--r--libwmc/tests/test-wmc-utils.c223
-rw-r--r--libwmc/tests/test-wmc-utils.h34
-rw-r--r--libwmc/tests/test-wmc.c110
-rw-r--r--libwmc/uml290.txt206
-rw-r--r--m4/ax_append_compile_flags.m446
-rw-r--r--m4/ax_append_flag.m450
-rw-r--r--m4/ax_append_link_flags.m444
-rw-r--r--m4/ax_check_compile_flag.m453
-rw-r--r--m4/ax_check_link_flag.m453
-rw-r--r--m4/ax_code_coverage.m4229
-rw-r--r--m4/ax_compiler_flags.m4158
-rw-r--r--m4/ax_compiler_flags_cflags.m4161
-rw-r--r--m4/ax_compiler_flags_gir.m460
-rw-r--r--m4/ax_compiler_flags_ldflags.m4111
-rw-r--r--m4/ax_is_release.m480
-rw-r--r--m4/ax_require_defined.m437
-rw-r--r--m4/compiler_warnings.m432
-rw-r--r--m4/gtk-doc.m4113
-rw-r--r--m4/introspection.m458
-rw-r--r--m4/mm-enable-plugin.m471
-rw-r--r--meson.build419
-rw-r--r--meson_options.txt95
-rw-r--r--plugins/Makefile.am1955
-rw-r--r--plugins/README.txt160
-rw-r--r--plugins/altair/mm-broadband-bearer-altair-lte.c220
-rw-r--r--plugins/altair/mm-broadband-modem-altair-lte.c693
-rw-r--r--plugins/altair/mm-modem-helpers-altair-lte.c191
-rw-r--r--plugins/altair/mm-modem-helpers-altair-lte.h7
-rw-r--r--plugins/altair/mm-plugin-altair-lte.c26
-rw-r--r--plugins/altair/tests/test-modem-helpers-altair-lte.c117
-rw-r--r--plugins/anydata/mm-broadband-modem-anydata.c106
-rw-r--r--plugins/anydata/mm-plugin-anydata.c18
-rw-r--r--plugins/broadmobi/77-mm-broadmobi-port-types.rules16
-rw-r--r--plugins/broadmobi/mm-plugin-broadmobi.c (renamed from plugins/gobi/mm-plugin-gobi.c)58
-rw-r--r--plugins/broadmobi/mm-plugin-broadmobi.h40
-rw-r--r--plugins/cinterion/77-mm-cinterion-port-types.rules58
-rw-r--r--plugins/cinterion/mm-broadband-bearer-cinterion.c687
-rw-r--r--plugins/cinterion/mm-broadband-bearer-cinterion.h54
-rw-r--r--plugins/cinterion/mm-broadband-modem-cinterion.c3102
-rw-r--r--plugins/cinterion/mm-broadband-modem-cinterion.h3
-rw-r--r--plugins/cinterion/mm-broadband-modem-mbim-cinterion.c168
-rw-r--r--plugins/cinterion/mm-broadband-modem-mbim-cinterion.h47
-rw-r--r--plugins/cinterion/mm-broadband-modem-qmi-cinterion.c128
-rw-r--r--plugins/cinterion/mm-common-cinterion.c466
-rw-r--r--plugins/cinterion/mm-common-cinterion.h51
-rw-r--r--plugins/cinterion/mm-modem-helpers-cinterion.c1579
-rw-r--r--plugins/cinterion/mm-modem-helpers-cinterion.h157
-rw-r--r--plugins/cinterion/mm-plugin-cinterion.c132
-rw-r--r--plugins/cinterion/mm-shared-cinterion.c1601
-rw-r--r--plugins/cinterion/mm-shared-cinterion.h153
-rw-r--r--plugins/cinterion/tests/test-modem-helpers-cinterion.c1514
-rw-r--r--plugins/dell/77-mm-dell-port-types.rules32
-rw-r--r--plugins/dell/mm-plugin-dell.c527
-rw-r--r--plugins/dell/mm-plugin-dell.h46
-rw-r--r--plugins/dlink/77-mm-dlink-port-types.rules16
-rw-r--r--plugins/dlink/mm-plugin-dlink.c94
-rw-r--r--plugins/dlink/mm-plugin-dlink.h40
-rw-r--r--plugins/fibocom/77-mm-fibocom-port-types.rules34
-rw-r--r--plugins/fibocom/mm-plugin-fibocom.c132
-rw-r--r--plugins/fibocom/mm-plugin-fibocom.h40
-rw-r--r--plugins/foxconn/77-mm-foxconn-port-types.rules26
-rw-r--r--plugins/foxconn/mm-broadband-modem-mbim-foxconn.c579
-rw-r--r--plugins/foxconn/mm-broadband-modem-mbim-foxconn.h49
-rwxr-xr-xplugins/foxconn/mm-foxconn-t77w968-carrier-mapping.conf299
-rw-r--r--plugins/foxconn/mm-plugin-foxconn.c120
-rw-r--r--plugins/foxconn/mm-plugin-foxconn.h46
-rw-r--r--plugins/foxconn/mm-shared.c (renamed from src/mm-auth.h)17
-rw-r--r--plugins/generic/mm-plugin-generic.c23
-rw-r--r--plugins/generic/tests/test-service-generic.c25
-rw-r--r--plugins/gobi/mm-broadband-modem-gobi.c124
-rw-r--r--plugins/gobi/mm-broadband-modem-gobi.h49
-rw-r--r--plugins/gobi/mm-plugin-gobi.h41
-rw-r--r--plugins/gosuncn/77-mm-gosuncn-port-types.rules17
-rw-r--r--plugins/gosuncn/mm-plugin-gosuncn.c113
-rw-r--r--plugins/gosuncn/mm-plugin-gosuncn.h40
-rw-r--r--plugins/haier/77-mm-haier-port-types.rules13
-rw-r--r--plugins/haier/mm-plugin-haier.c75
-rw-r--r--plugins/haier/mm-plugin-haier.h40
-rw-r--r--plugins/huawei/77-mm-huawei-net-port-types.rules28
-rw-r--r--plugins/huawei/mm-broadband-bearer-huawei.c542
-rw-r--r--plugins/huawei/mm-broadband-modem-huawei.c2506
-rw-r--r--plugins/huawei/mm-broadband-modem-huawei.h1
-rw-r--r--plugins/huawei/mm-modem-helpers-huawei.c484
-rw-r--r--plugins/huawei/mm-modem-helpers-huawei.h71
-rw-r--r--plugins/huawei/mm-plugin-huawei.c564
-rw-r--r--plugins/huawei/mm-sim-huawei.c38
-rw-r--r--plugins/huawei/tests/test-modem-helpers-huawei.c430
-rw-r--r--plugins/icera/mm-broadband-bearer-icera.c775
-rw-r--r--plugins/icera/mm-broadband-bearer-icera.h2
-rw-r--r--plugins/icera/mm-broadband-modem-icera.c973
-rw-r--r--plugins/icera/mm-broadband-modem-icera.h2
-rw-r--r--plugins/icera/mm-modem-helpers-icera.c176
-rw-r--r--plugins/icera/mm-modem-helpers-icera.h6
-rw-r--r--plugins/icera/mm-shared.c20
-rw-r--r--plugins/icera/tests/test-modem-helpers-icera.c125
-rw-r--r--plugins/iridium/mm-bearer-iridium.c117
-rw-r--r--plugins/iridium/mm-broadband-modem-iridium.c115
-rw-r--r--plugins/iridium/mm-plugin-iridium.c13
-rw-r--r--plugins/iridium/mm-sim-iridium.c7
-rw-r--r--plugins/linktop/mm-broadband-modem-linktop.c109
-rw-r--r--plugins/linktop/mm-modem-helpers-linktop.c54
-rw-r--r--plugins/linktop/mm-modem-helpers-linktop.h40
-rw-r--r--plugins/linktop/mm-plugin-linktop.c10
-rw-r--r--plugins/linktop/tests/test-modem-helpers-linktop.c71
-rw-r--r--plugins/longcheer/77-mm-longcheer-port-types.rules173
-rw-r--r--plugins/longcheer/mm-broadband-modem-longcheer.c102
-rw-r--r--plugins/longcheer/mm-plugin-longcheer.c162
-rw-r--r--plugins/mbm/77-mm-ericsson-mbm.rules55
-rw-r--r--plugins/mbm/mm-broadband-bearer-mbm.c835
-rw-r--r--plugins/mbm/mm-broadband-modem-mbm.c793
-rw-r--r--plugins/mbm/mm-modem-helpers-mbm.c176
-rw-r--r--plugins/mbm/mm-modem-helpers-mbm.h24
-rw-r--r--plugins/mbm/mm-plugin-mbm.c18
-rw-r--r--plugins/mbm/mm-sim-mbm.c77
-rw-r--r--plugins/mbm/tests/test-modem-helpers-mbm.c148
-rw-r--r--plugins/meson.build990
-rw-r--r--plugins/motorola/mm-broadband-modem-motorola.c3
-rw-r--r--plugins/motorola/mm-plugin-motorola.c10
-rw-r--r--plugins/mtk/77-mm-mtk-port-types.rules37
-rw-r--r--plugins/mtk/mm-broadband-modem-mtk.c319
-rw-r--r--plugins/mtk/mm-plugin-mtk.c48
-rw-r--r--plugins/nokia/77-mm-nokia-port-types.rules34
-rw-r--r--plugins/nokia/mm-broadband-modem-nokia.c158
-rw-r--r--plugins/nokia/mm-plugin-nokia-icera.c47
-rw-r--r--plugins/nokia/mm-plugin-nokia.c11
-rw-r--r--plugins/nokia/mm-sim-nokia.c1
-rw-r--r--plugins/novatel/mm-broadband-bearer-novatel-lte.c407
-rw-r--r--plugins/novatel/mm-broadband-bearer-novatel-lte.h2
-rw-r--r--plugins/novatel/mm-broadband-modem-novatel-lte.c432
-rw-r--r--plugins/novatel/mm-broadband-modem-novatel.c1011
-rw-r--r--plugins/novatel/mm-common-novatel.c145
-rw-r--r--plugins/novatel/mm-common-novatel.h31
-rw-r--r--plugins/novatel/mm-plugin-novatel-lte.c11
-rw-r--r--plugins/novatel/mm-plugin-novatel.c181
-rw-r--r--plugins/novatel/mm-shared.c20
-rw-r--r--plugins/novatel/mm-sim-novatel-lte.c92
-rw-r--r--plugins/option/mm-broadband-bearer-hso.c597
-rw-r--r--plugins/option/mm-broadband-bearer-hso.h2
-rw-r--r--plugins/option/mm-broadband-modem-hso.c337
-rw-r--r--plugins/option/mm-broadband-modem-option.c316
-rw-r--r--plugins/option/mm-plugin-hso.c39
-rw-r--r--plugins/option/mm-plugin-option.c18
-rw-r--r--plugins/option/mm-shared-option.c77
-rw-r--r--plugins/option/mm-shared-option.h49
-rw-r--r--plugins/option/mm-shared.c20
-rw-r--r--plugins/option/mm-sim-option.c84
-rw-r--r--plugins/option/mm-sim-option.h51
-rw-r--r--plugins/pantech/mm-broadband-modem-pantech.c25
-rw-r--r--plugins/pantech/mm-plugin-pantech.c22
-rw-r--r--plugins/pantech/mm-sim-pantech.c1
-rw-r--r--plugins/qcom-soc/77-mm-qcom-soc.rules40
-rw-r--r--plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c181
-rw-r--r--plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.h49
-rw-r--r--plugins/qcom-soc/mm-plugin-qcom-soc.c97
-rw-r--r--plugins/qcom-soc/mm-plugin-qcom-soc.h40
-rw-r--r--plugins/quectel/77-mm-quectel-port-types.rules70
-rw-r--r--plugins/quectel/mm-broadband-modem-mbim-quectel.c132
-rw-r--r--plugins/quectel/mm-broadband-modem-mbim-quectel.h48
-rw-r--r--plugins/quectel/mm-broadband-modem-qmi-quectel.c136
-rw-r--r--plugins/quectel/mm-broadband-modem-qmi-quectel.h47
-rw-r--r--plugins/quectel/mm-broadband-modem-quectel.c136
-rw-r--r--plugins/quectel/mm-broadband-modem-quectel.h47
-rw-r--r--plugins/quectel/mm-modem-helpers-quectel.c91
-rw-r--r--plugins/quectel/mm-modem-helpers-quectel.h32
-rw-r--r--plugins/quectel/mm-plugin-quectel.c125
-rw-r--r--plugins/quectel/mm-plugin-quectel.h40
-rw-r--r--plugins/quectel/mm-shared-quectel.c810
-rw-r--r--plugins/quectel/mm-shared-quectel.h93
-rw-r--r--plugins/quectel/tests/test-modem-helpers-quectel.c93
-rw-r--r--plugins/samsung/mm-broadband-modem-samsung.c8
-rw-r--r--plugins/samsung/mm-plugin-samsung.c12
-rw-r--r--plugins/sierra/77-mm-sierra.rules32
-rw-r--r--plugins/sierra/mm-broadband-bearer-sierra.c320
-rw-r--r--plugins/sierra/mm-broadband-modem-sierra-icera.c35
-rw-r--r--plugins/sierra/mm-broadband-modem-sierra.c852
-rw-r--r--plugins/sierra/mm-common-sierra.c328
-rw-r--r--plugins/sierra/mm-common-sierra.h17
-rw-r--r--plugins/sierra/mm-modem-helpers-sierra.c82
-rw-r--r--plugins/sierra/mm-modem-helpers-sierra.h26
-rw-r--r--plugins/sierra/mm-plugin-sierra-legacy.c98
-rw-r--r--plugins/sierra/mm-plugin-sierra-legacy.h40
-rw-r--r--plugins/sierra/mm-plugin-sierra.c287
-rw-r--r--plugins/sierra/mm-shared.c20
-rw-r--r--plugins/sierra/mm-sim-sierra.c47
-rw-r--r--plugins/sierra/tests/test-modem-helpers-sierra.c130
-rw-r--r--plugins/simtech/77-mm-simtech-port-types.rules53
-rw-r--r--plugins/simtech/mm-broadband-modem-qmi-simtech.c128
-rw-r--r--plugins/simtech/mm-broadband-modem-qmi-simtech.h47
-rw-r--r--plugins/simtech/mm-broadband-modem-simtech.c1167
-rw-r--r--plugins/simtech/mm-broadband-modem-simtech.h2
-rw-r--r--plugins/simtech/mm-modem-helpers-simtech.c209
-rw-r--r--plugins/simtech/mm-modem-helpers-simtech.h67
-rw-r--r--plugins/simtech/mm-plugin-simtech.c88
-rw-r--r--plugins/simtech/mm-shared-simtech.c1261
-rw-r--r--plugins/simtech/mm-shared-simtech.h129
-rw-r--r--plugins/simtech/tests/test-modem-helpers-simtech.c343
-rw-r--r--plugins/symbol.map8
-rw-r--r--plugins/telit/77-mm-telit-port-types.rules155
-rw-r--r--plugins/telit/mm-broadband-modem-mbim-telit.c192
-rw-r--r--plugins/telit/mm-broadband-modem-mbim-telit.h47
-rw-r--r--plugins/telit/mm-broadband-modem-telit.c1352
-rw-r--r--plugins/telit/mm-broadband-modem-telit.h2
-rw-r--r--plugins/telit/mm-common-telit.c375
-rw-r--r--plugins/telit/mm-common-telit.h40
-rw-r--r--plugins/telit/mm-modem-helpers-telit.c854
-rw-r--r--plugins/telit/mm-modem-helpers-telit.h68
-rw-r--r--plugins/telit/mm-plugin-telit.c118
-rw-r--r--plugins/telit/mm-shared-telit.c613
-rw-r--r--plugins/telit/mm-shared-telit.h90
-rw-r--r--plugins/telit/mm-shared.c20
-rw-r--r--plugins/telit/tests/test-mm-modem-helpers-telit.c629
-rw-r--r--plugins/tests/test-fixture.c115
-rw-r--r--plugins/tests/test-helpers.c52
-rw-r--r--plugins/tests/test-helpers.h26
-rw-r--r--plugins/tests/test-keyfiles.c79
-rw-r--r--plugins/tests/test-port-context.c55
-rw-r--r--plugins/tests/test-udev-rules.c246
-rw-r--r--plugins/thuraya/mm-broadband-modem-thuraya.c284
-rw-r--r--plugins/thuraya/mm-broadband-modem-thuraya.h50
-rw-r--r--plugins/thuraya/mm-modem-helpers-thuraya.c148
-rw-r--r--plugins/thuraya/mm-modem-helpers-thuraya.h28
-rw-r--r--plugins/thuraya/mm-plugin-thuraya.c84
-rw-r--r--plugins/thuraya/mm-plugin-thuraya.h48
-rw-r--r--plugins/thuraya/tests/test-mm-modem-helpers-thuraya.c106
-rw-r--r--plugins/tplink/77-mm-tplink-port-types.rules15
-rw-r--r--plugins/tplink/mm-plugin-tplink.c94
-rw-r--r--plugins/tplink/mm-plugin-tplink.h40
-rw-r--r--plugins/ublox/77-mm-ublox-port-types.rules78
-rw-r--r--plugins/ublox/README162
-rw-r--r--plugins/ublox/mm-broadband-bearer-ublox.c1034
-rw-r--r--plugins/ublox/mm-broadband-bearer-ublox.h63
-rw-r--r--plugins/ublox/mm-broadband-modem-ublox.c1917
-rw-r--r--plugins/ublox/mm-broadband-modem-ublox.h49
-rw-r--r--plugins/ublox/mm-modem-helpers-ublox.c2065
-rw-r--r--plugins/ublox/mm-modem-helpers-ublox.h213
-rw-r--r--plugins/ublox/mm-plugin-ublox.c264
-rw-r--r--plugins/ublox/mm-plugin-ublox.h40
-rw-r--r--plugins/ublox/mm-sim-ublox.c163
-rw-r--r--plugins/ublox/mm-sim-ublox.h51
-rw-r--r--plugins/ublox/tests/test-modem-helpers-ublox.c1026
-rw-r--r--plugins/via/mm-broadband-modem-via.c308
-rw-r--r--plugins/via/mm-plugin-via.c17
-rw-r--r--plugins/wavecom/mm-broadband-modem-wavecom.c819
-rw-r--r--plugins/wavecom/mm-plugin-wavecom.c10
-rw-r--r--plugins/x22x/77-mm-x22x-port-types.rules51
-rw-r--r--plugins/x22x/mm-broadband-modem-x22x.c152
-rw-r--r--plugins/x22x/mm-broadband-modem-x22x.h2
-rw-r--r--plugins/x22x/mm-plugin-x22x.c167
-rw-r--r--plugins/xmm/mm-broadband-modem-mbim-xmm.c139
-rw-r--r--plugins/xmm/mm-broadband-modem-mbim-xmm.h47
-rw-r--r--plugins/xmm/mm-broadband-modem-xmm.c151
-rw-r--r--plugins/xmm/mm-broadband-modem-xmm.h47
-rw-r--r--plugins/xmm/mm-modem-helpers-xmm.c1012
-rw-r--r--plugins/xmm/mm-modem-helpers-xmm.h75
-rw-r--r--plugins/xmm/mm-shared-xmm.c1553
-rw-r--r--plugins/xmm/mm-shared-xmm.h184
-rw-r--r--plugins/xmm/mm-shared.c20
-rw-r--r--plugins/xmm/tests/test-modem-helpers-xmm.c780
-rw-r--r--plugins/zte/77-mm-zte-port-types.rules254
-rw-r--r--plugins/zte/mm-broadband-modem-zte-icera.c43
-rw-r--r--plugins/zte/mm-broadband-modem-zte.c225
-rw-r--r--plugins/zte/mm-common-zte.c2
-rw-r--r--plugins/zte/mm-plugin-zte.c52
-rw-r--r--po/LINGUAS19
-rw-r--r--po/Makevars78
-rw-r--r--po/POTFILES.in2
-rw-r--r--po/cs.po120
-rw-r--r--po/da.po113
-rw-r--r--po/de.po60
-rw-r--r--po/fi.po116
-rw-r--r--po/fr.po117
-rw-r--r--po/fur.po120
-rw-r--r--po/he.po105
-rw-r--r--po/hu.po118
-rw-r--r--po/id.po111
-rw-r--r--po/it.po118
-rw-r--r--po/lt.po122
-rw-r--r--po/meson.build4
-rw-r--r--po/pl.po119
-rw-r--r--po/pt_BR.po115
-rw-r--r--po/ru.po118
-rw-r--r--po/sk.po117
-rw-r--r--po/sv.po115
-rw-r--r--po/tr.po114
-rw-r--r--po/uk.po62
-rw-r--r--po/zh_CN.po103
-rw-r--r--src/77-mm-pcmcia-device-blacklist.rules9
-rw-r--r--src/77-mm-platform-serial-whitelist.rules13
-rw-r--r--src/77-mm-usb-device-blacklist.rules131
-rw-r--r--src/77-mm-usb-serial-adapters-greylist.rules38
-rw-r--r--src/80-mm-candidate.rules30
-rw-r--r--src/Makefile.am383
-rw-r--r--src/kerneldevice/mm-kernel-device-generic-rules.c442
-rw-r--r--src/kerneldevice/mm-kernel-device-generic-rules.h62
-rw-r--r--src/kerneldevice/mm-kernel-device-generic.c1281
-rw-r--r--src/kerneldevice/mm-kernel-device-generic.h56
-rw-r--r--src/kerneldevice/mm-kernel-device-helpers.c61
-rw-r--r--src/kerneldevice/mm-kernel-device-helpers.h26
-rw-r--r--src/kerneldevice/mm-kernel-device-qrtr.c232
-rw-r--r--src/kerneldevice/mm-kernel-device-qrtr.h68
-rw-r--r--src/kerneldevice/mm-kernel-device-udev.c850
-rw-r--r--src/kerneldevice/mm-kernel-device-udev.h57
-rw-r--r--src/kerneldevice/mm-kernel-device.c491
-rw-r--r--src/kerneldevice/mm-kernel-device.h125
-rw-r--r--src/main.c111
-rw-r--r--src/meson.build319
-rw-r--r--src/mm-auth-provider-polkit.c208
-rw-r--r--src/mm-auth-provider-polkit.h46
-rw-r--r--src/mm-auth-provider.c218
-rw-r--r--src/mm-auth-provider.h55
-rw-r--r--src/mm-auth.c53
-rw-r--r--src/mm-base-bearer.c1021
-rw-r--r--src/mm-base-bearer.h139
-rw-r--r--src/mm-base-call.c1581
-rw-r--r--src/mm-base-call.h140
-rw-r--r--src/mm-base-manager.c1486
-rw-r--r--src/mm-base-manager.h31
-rw-r--r--src/mm-base-modem-at.c265
-rw-r--r--src/mm-base-modem-at.h172
-rw-r--r--src/mm-base-modem.c1587
-rw-r--r--src/mm-base-modem.h133
-rw-r--r--src/mm-base-sim.c1938
-rw-r--r--src/mm-base-sim.h143
-rw-r--r--src/mm-base-sms.c533
-rw-r--r--src/mm-base-sms.h11
-rw-r--r--src/mm-bearer-list.c274
-rw-r--r--src/mm-bearer-list.h40
-rw-r--r--src/mm-bearer-mbim.c1893
-rw-r--r--src/mm-bearer-mbim.h4
-rw-r--r--src/mm-bearer-qmi.c2508
-rw-r--r--src/mm-bearer-qmi.h6
-rw-r--r--src/mm-broadband-bearer.c1691
-rw-r--r--src/mm-broadband-bearer.h19
-rw-r--r--src/mm-broadband-modem-mbim.c7403
-rw-r--r--src/mm-broadband-modem-mbim.h24
-rw-r--r--src/mm-broadband-modem-qmi.c12113
-rw-r--r--src/mm-broadband-modem-qmi.h25
-rw-r--r--src/mm-broadband-modem.c9910
-rw-r--r--src/mm-broadband-modem.h27
-rw-r--r--src/mm-call-list.c321
-rw-r--r--src/mm-call-list.h87
-rw-r--r--src/mm-call-qmi.c365
-rw-r--r--src/mm-call-qmi.h52
-rw-r--r--src/mm-charsets.c924
-rw-r--r--src/mm-charsets.h123
-rw-r--r--src/mm-context.c343
-rw-r--r--src/mm-context.h40
-rw-r--r--src/mm-device.c676
-rw-r--r--src/mm-device.h123
-rw-r--r--src/mm-error-helpers.c556
-rw-r--r--src/mm-error-helpers.h10
-rw-r--r--src/mm-filter.c550
-rw-r--r--src/mm-filter.h115
-rw-r--r--src/mm-iface-modem-3gpp-profile-manager.c1673
-rw-r--r--src/mm-iface-modem-3gpp-profile-manager.h248
-rw-r--r--src/mm-iface-modem-3gpp-ussd.c422
-rw-r--r--src/mm-iface-modem-3gpp-ussd.h85
-rw-r--r--src/mm-iface-modem-3gpp.c2497
-rw-r--r--src/mm-iface-modem-3gpp.h135
-rw-r--r--src/mm-iface-modem-cdma.c782
-rw-r--r--src/mm-iface-modem-cdma.h1
-rw-r--r--src/mm-iface-modem-firmware.c412
-rw-r--r--src/mm-iface-modem-firmware.h18
-rw-r--r--src/mm-iface-modem-location.c971
-rw-r--r--src/mm-iface-modem-location.h42
-rw-r--r--src/mm-iface-modem-messaging.c587
-rw-r--r--src/mm-iface-modem-messaging.h8
-rw-r--r--src/mm-iface-modem-oma.c369
-rw-r--r--src/mm-iface-modem-oma.h1
-rw-r--r--src/mm-iface-modem-sar.c570
-rw-r--r--src/mm-iface-modem-sar.h102
-rw-r--r--src/mm-iface-modem-signal.c663
-rw-r--r--src/mm-iface-modem-signal.h25
-rw-r--r--src/mm-iface-modem-simple.c427
-rw-r--r--src/mm-iface-modem-simple.h1
-rw-r--r--src/mm-iface-modem-time.c853
-rw-r--r--src/mm-iface-modem-time.h19
-rw-r--r--src/mm-iface-modem-voice.c3217
-rw-r--r--src/mm-iface-modem-voice.h282
-rw-r--r--src/mm-iface-modem.c4044
-rw-r--r--src/mm-iface-modem.h157
-rw-r--r--src/mm-log-object.c89
-rw-r--r--src/mm-log-object.h38
-rw-r--r--src/mm-log-test.h46
-rw-r--r--src/mm-log.c261
-rw-r--r--src/mm-log.h56
-rw-r--r--src/mm-modem-helpers-mbim.c775
-rw-r--r--src/mm-modem-helpers-mbim.h67
-rw-r--r--src/mm-modem-helpers-qmi.c1732
-rw-r--r--src/mm-modem-helpers-qmi.h106
-rw-r--r--src/mm-modem-helpers.c3701
-rw-r--r--src/mm-modem-helpers.h419
-rw-r--r--src/mm-netlink.c464
-rw-r--r--src/mm-netlink.h54
-rw-r--r--src/mm-plugin-manager.c2190
-rw-r--r--src/mm-plugin-manager.h34
-rw-r--r--src/mm-plugin.c782
-rw-r--r--src/mm-plugin.h62
-rw-r--r--src/mm-port-mbim.c791
-rw-r--r--src/mm-port-mbim.h57
-rw-r--r--src/mm-port-net.c122
-rw-r--r--src/mm-port-net.h63
-rw-r--r--src/mm-port-probe-at.c2
-rw-r--r--src/mm-port-probe-at.h3
-rw-r--r--src/mm-port-probe.c1481
-rw-r--r--src/mm-port-probe.h36
-rw-r--r--src/mm-port-qmi.c2712
-rw-r--r--src/mm-port-qmi.h154
-rw-r--r--src/mm-port-serial-at.c77
-rw-r--r--src/mm-port-serial-at.h21
-rw-r--r--src/mm-port-serial-gps.c22
-rw-r--r--src/mm-port-serial-gps.h1
-rw-r--r--src/mm-port-serial-qcdm.c333
-rw-r--r--src/mm-port-serial-qcdm.h20
-rw-r--r--src/mm-port-serial.c1025
-rw-r--r--src/mm-port-serial.h56
-rw-r--r--src/mm-port.c76
-rw-r--r--src/mm-port.h36
-rw-r--r--src/mm-private-boxed-types.c69
-rw-r--r--src/mm-private-boxed-types.h3
-rw-r--r--src/mm-qrtr-bus-watcher.c354
-rw-r--r--src/mm-qrtr-bus-watcher.h69
-rw-r--r--src/mm-serial-parsers.c45
-rw-r--r--src/mm-serial-parsers.h1
-rw-r--r--src/mm-shared-qmi.c6936
-rw-r--r--src/mm-shared-qmi.h257
-rw-r--r--src/mm-shared.h35
-rw-r--r--src/mm-sim-mbim.c617
-rw-r--r--src/mm-sim-mbim.h11
-rw-r--r--src/mm-sim-qmi.c1709
-rw-r--r--src/mm-sim-qmi.h29
-rw-r--r--src/mm-sleep-monitor.c265
-rw-r--r--src/mm-sleep-monitor.h43
-rw-r--r--src/mm-sms-list.c136
-rw-r--r--src/mm-sms-list.h1
-rw-r--r--src/mm-sms-mbim.c132
-rw-r--r--src/mm-sms-mbim.h1
-rw-r--r--src/mm-sms-part-3gpp.c542
-rw-r--r--src/mm-sms-part-3gpp.h51
-rw-r--r--src/mm-sms-part-cdma.c532
-rw-r--r--src/mm-sms-part-cdma.h27
-rw-r--r--src/mm-sms-part.h26
-rw-r--r--src/mm-sms-qmi.c350
-rw-r--r--src/mm-sms-qmi.h1
-rw-r--r--src/mm-utils.h83
-rw-r--r--src/tests/Makefile.am212
-rw-r--r--src/tests/meson.build38
-rw-r--r--src/tests/test-at-serial-port.c108
-rw-r--r--src/tests/test-charsets.c342
-rw-r--r--src/tests/test-error-helpers.c151
-rw-r--r--src/tests/test-modem-helpers-qmi.c25
-rw-r--r--src/tests/test-modem-helpers.c2969
-rw-r--r--src/tests/test-qcdm-serial-port.c31
-rw-r--r--src/tests/test-sms-part-3gpp.c167
-rw-r--r--src/tests/test-sms-part-cdma.c104
-rw-r--r--src/tests/test-udev-rules.c55
-rw-r--r--test/Makefile.am132
-rw-r--r--test/lsudev.c202
-rw-r--r--test/meson.build22
-rw-r--r--test/mmrules.c153
-rw-r--r--test/mmsmsmonitor.c195
-rw-r--r--test/mmsmspdu.c214
-rw-r--r--test/mmtty.c347
-rw-r--r--tools/Makefile.am3
-rwxr-xr-xtools/test-modemmanager-service.py490
-rw-r--r--tools/tests/Makefile.am49
-rw-r--r--tools/tests/meson.build30
-rw-r--r--tools/tests/services/meson.build8
-rw-r--r--tools/tests/services/org.freedesktop.ModemManager1.service.in3
-rw-r--r--tools/tests/test-stub.c459
-rw-r--r--tools/tests/test-wrapper.sh.in5
-rw-r--r--uml290/Makefile.am10
-rw-r--r--uml290/uml290mode.c722
759 files changed, 174435 insertions, 49907 deletions
diff --git a/.gitignore b/.gitignore
index 5888e9d9..05b9619e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,167 +1,188 @@
-INSTALL
-Makefile
-Makefile.in
-aclocal.m4
-autom4te.cache*
-compile
-config.*
-configure
-depcomp
-install-sh
-ltmain.sh
-intltool-*
-missing
-stamp-h1
-gtk-doc.make
*~
*.o
*.lo
*.la
*.loT
-libtool
-.deps
-.libs
-*-glue.h
-*.tar.bz2
+*.gcno
+*.gcda
+*.tar.xz
*.pyc
*.dirstamp
-org.freedesktop.ModemManager.service
-org.freedesktop.ModemManager.conf
-callouts/mm-modem-probe
-test/lsudev
-
-include/ModemManager-version.h
-
-libmm-glib/generated/mm-gdbus-*.[ch]
-libmm-glib/generated/mm-enums-types.[ch]
-libmm-glib/generated/mm-errors-types.[ch]
-libmm-glib/generated/mm-errors-quarks.c
-libmm-glib/generated/*.xml
-libmm-glib/generated/tests/mm-gdbus-*.[ch]
-libmm-glib/tests/test-common-helpers
-libmm-glib/*.gir
-libmm-glib/*.typelib
-
-src/ModemManager
-src/mm-daemon-enums-types.c
-src/mm-daemon-enums-types.h
-src/mm-port-enums-types.c
-src/mm-port-enums-types.h
-src/mm-marshal.[ch]
-src/tests/test-modem-helpers
-src/tests/test-modem-helpers-qmi
-src/tests/test-charsets
-src/tests/test-qcdm-serial-port
-src/tests/test-at-serial-port
-src/tests/test-sms-part-3gpp
-src/tests/test-sms-part-cdma
-
-cli/mmcli
-
-vapi/libmm-glib.vapi
-
-libqcdm/tests/test-qcdm
-libqcdm/tests/modepref
-libqcdm/tests/ipv6pref
-libqcdm/tests/reset
-
-libwmc/tests/test-wmc
-
-data/org.freedesktop.ModemManager1.conf
-data/org.freedesktop.ModemManager1.service
-data/org.freedesktop.ModemManager1.policy
-data/org.freedesktop.ModemManager1.policy.in
-data/ModemManager.service
-data/ModemManager.pc
-data/mm-common.pc
-data/mm-glib.pc
-data/tests/org.freedesktop.ModemManager1.service
-
-include/ModemManager-names.h
-
-po/Makefile.in.in
-po/POTFILES
-po/stamp-it
-po/.intltool-merge-cache
-po/ModemManager.pot
-po/Makevars.template
-po/Rules-quot
-po/boldquot.sed
-po/en@boldquot.header
-po/en@quot.header
-po/insert-header.sin
-po/quot.sed
-po/remove-potcdate.sin
-po/*.gmo
-
-docs/reference/api/version.xml
-docs/reference/api/ModemManager.args
-docs/reference/api/ModemManager.hierarchy
-docs/reference/api/ModemManager.interfaces
-docs/reference/api/ModemManager.prerequisites
-docs/reference/api/ModemManager.signals
-docs/reference/api/*.stamp
-docs/reference/api/*.txt
-docs/reference/api/html
-docs/reference/api/tmpl
-docs/reference/api/xml
-docs/reference/api/*.png
-
-docs/reference/libmm-glib/version.xml
-docs/reference/libmm-glib/libmm-glib.args
-docs/reference/libmm-glib/libmm-glib.hierarchy
-docs/reference/libmm-glib/libmm-glib.interfaces
-docs/reference/libmm-glib/libmm-glib.prerequisites
-docs/reference/libmm-glib/libmm-glib.signals
-docs/reference/libmm-glib/*.stamp
-docs/reference/libmm-glib/*.txt
-docs/reference/libmm-glib/html
-docs/reference/libmm-glib/tmpl
-docs/reference/libmm-glib/xml
-
-m4/gtk-doc.m4
-m4/intltool.m4
-m4/libtool.m4
-m4/lt*.m4
-m4/codeset.m4
-m4/gettext.m4
-m4/glibc2.m4
-m4/glibc21.m4
-m4/iconv.m4
-m4/intdiv0.m4
-m4/intl.m4
-m4/intldir.m4
-m4/intlmacosx.m4
-m4/intmax.m4
-m4/inttypes-pri.m4
-m4/inttypes_h.m4
-m4/lcmessage.m4
-m4/lib-ld.m4
-m4/lib-link.m4
-m4/lib-prefix.m4
-m4/lock.m4
-m4/longlong.m4
-m4/nls.m4
-m4/po.m4
-m4/printf-posix.m4
-m4/progtest.m4
-m4/size_max.m4
-m4/stdint_h.m4
-m4/uintmax_t.m4
-m4/visibility.m4
-m4/wchar_t.m4
-m4/wint_t.m4
-m4/xsize.m4
-
-uml290/uml290mode
-
-plugins/test-suite.log
-plugins/test-modem-helpers-huawei*
-plugins/test-modem-helpers-altair*
-plugins/test-modem-helpers-cinterion*
-plugins/test-modem-helpers-icera*
-plugins/test-modem-helpers-mbm*
-plugins/test-service-*
-
-TAGS
-ABOUT-NLS
+.deps
+.libs
+Makefile
+Makefile.in
+
+/INSTALL
+/aclocal.m4
+/autom4te.cache*
+/compile
+/config.*
+/configure
+/depcomp
+/install-sh
+/ltmain.sh
+/missing
+/stamp-h1
+/libtool
+/tags
+/TAGS
+/ABOUT-NLS
+/ChangeLog
+
+/include/ModemManager-version.h
+/include/ModemManager-names.h
+
+/libmm-glib/generated/mm-gdbus-*.[ch]
+/libmm-glib/generated/mm-enums-types.[ch]
+/libmm-glib/generated/mm-errors-types.[ch]
+/libmm-glib/generated/mm-errors-quarks.c
+/libmm-glib/generated/*.xml
+/libmm-glib/generated/tests/mm-gdbus-*.[ch]
+/libmm-glib/tests/test-common-helpers
+/libmm-glib/tests/test-pco
+/libmm-glib/*.gir
+/libmm-glib/*.typelib
+
+/src/ModemManager
+/src/mm-daemon-enums-types.c
+/src/mm-daemon-enums-types.h
+/src/mm-port-enums-types.c
+/src/mm-port-enums-types.h
+/src/mm-helper-enums-types.c
+/src/mm-helper-enums-types.h
+/src/mm-marshal.[ch]
+/src/tests/test-modem-helpers
+/src/tests/test-modem-helpers-qmi
+/src/tests/test-charsets
+/src/tests/test-qcdm-serial-port
+/src/tests/test-at-serial-port
+/src/tests/test-sms-part-3gpp
+/src/tests/test-sms-part-cdma
+/src/tests/test-udev-rules
+/src/tests/test-error-helpers
+
+/cli/mmcli
+
+/vapi/libmm-glib.vapi
+
+/libqcdm/tests/test-qcdm
+/libqcdm/tests/modepref
+/libqcdm/tests/ipv6pref
+/libqcdm/tests/reset
+
+/data/org.freedesktop.ModemManager1.conf
+/data/org.freedesktop.ModemManager1.service
+/data/org.freedesktop.ModemManager1.policy
+/data/org.freedesktop.ModemManager1.policy.in
+/data/ModemManager.service
+/data/ModemManager.pc
+/data/mm-common.pc
+/data/mm-glib.pc
+/data/tests/org.freedesktop.ModemManager1.service
+
+/po/Makefile.in.in
+/po/POTFILES
+/po/stamp-it
+/po/ModemManager.pot
+/po/Makevars.template
+/po/Rules-quot
+/po/boldquot.sed
+/po/en@boldquot.header
+/po/en@quot.header
+/po/insert-header.sin
+/po/quot.sed
+/po/remove-potcdate.sin
+/po/remove-potcdate.sed
+/po/stamp-po
+/po/*.gmo
+
+/docs/reference/api/version.xml
+/docs/reference/api/ModemManager.args
+/docs/reference/api/ModemManager.hierarchy
+/docs/reference/api/ModemManager.interfaces
+/docs/reference/api/ModemManager.prerequisites
+/docs/reference/api/ModemManager.signals
+/docs/reference/api/ModemManager.actions
+/docs/reference/api/*.stamp
+/docs/reference/api/*.txt
+/docs/reference/api/html
+/docs/reference/api/tmpl
+/docs/reference/api/xml
+/docs/reference/api/*.png
+
+/docs/reference/libmm-glib/version.xml
+/docs/reference/libmm-glib/libmm-glib.args
+/docs/reference/libmm-glib/libmm-glib.hierarchy
+/docs/reference/libmm-glib/libmm-glib.interfaces
+/docs/reference/libmm-glib/libmm-glib.prerequisites
+/docs/reference/libmm-glib/libmm-glib.signals
+/docs/reference/libmm-glib/libmm-glib.actions
+/docs/reference/libmm-glib/libmm-glib.types
+/docs/reference/libmm-glib/*.stamp
+/docs/reference/libmm-glib/*.txt
+/docs/reference/libmm-glib/html
+/docs/reference/libmm-glib/tmpl
+/docs/reference/libmm-glib/xml
+
+/m4/gtk-doc.m4
+/m4/libtool.m4
+/m4/lt*.m4
+/m4/codeset.m4
+/m4/gettext.m4
+/m4/glibc2.m4
+/m4/glibc21.m4
+/m4/iconv.m4
+/m4/intdiv0.m4
+/m4/intl.m4
+/m4/intldir.m4
+/m4/intlmacosx.m4
+/m4/intmax.m4
+/m4/inttypes-pri.m4
+/m4/inttypes_h.m4
+/m4/lcmessage.m4
+/m4/lib-ld.m4
+/m4/lib-link.m4
+/m4/lib-prefix.m4
+/m4/lock.m4
+/m4/longlong.m4
+/m4/nls.m4
+/m4/po.m4
+/m4/printf-posix.m4
+/m4/progtest.m4
+/m4/size_max.m4
+/m4/stdint_h.m4
+/m4/uintmax_t.m4
+/m4/visibility.m4
+/m4/wchar_t.m4
+/m4/wint_t.m4
+/m4/xsize.m4
+/m4/extern-inline.m4
+/m4/fcntl-o.m4
+/m4/threadlib.m4
+
+/plugins/test-suite.log
+/plugins/test-udev-rules
+/plugins/test-keyfiles
+/plugins/test-modem-helpers-*
+/plugins/test-service-*
+
+/plugins/ublox/mm-ublox-enums-types.[ch]
+/plugins/telit/mm-telit-enums-types.[ch]
+/plugins/huawei/mm-huawei-enums-types.[ch]
+
+/test/lsudev
+/test/mmtty
+/test/mmrules
+/test/mmsmspdu
+/test/mmsmsmonitor
+
+/tools/tests/services/org.freedesktop.ModemManager1.service
+/tools/tests/test-stub
+/tools/tests/test-wrapper.sh
+
+/examples/sms-c/sms-c-sync
+/examples/sms-c/sms-c-async
+
+/ModemManager-*-coverage.info
+/ModemManager-*-coverage/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..28eb9f2b
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,271 @@
+include:
+ - project: freedesktop/ci-templates
+ ref: 290b79e0e78eab67a83766f4e9691be554fc4afd
+ file:
+ - templates/ubuntu.yml
+
+stages:
+ - container prep
+ - build
+
+.common_variables:
+ variables:
+ FDO_UPSTREAM_REPO: mobile-broadband/ModemManager
+ FDO_DISTRIBUTION_VERSION: '20.04'
+ FDO_DISTRIBUTION_TAG: '2021-10-03.1'
+ FDO_DISTRIBUTION_PACKAGES: ca-certificates git gcc autoconf automake libtool
+ libgettextpo-dev libgirepository1.0-dev libglib2.0-dev
+ libgudev-1.0-dev python3-dbus python3-gi autopoint
+ xsltproc dbus gettext gtk-doc-tools libglib2.0-doc
+ gobject-introspection python-is-python3 libsystemd-dev
+ libpolkit-gobject-1-dev valac meson ninja-build
+ libdbus-1-dev bash-completion udev policykit-1
+
+build container:
+ extends:
+ - .fdo.container-build@ubuntu
+ - .common_variables
+ stage: container prep
+ only:
+ - master
+ - branches
+ - merge_requests
+ - tags
+ - pushes
+
+build-no-qmi:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - merge_requests
+ - tags
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libmbim.git
+ - pushd libmbim
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - NOCONFIGURE=1 ./autogen.sh
+ - ./configure --prefix=/usr --disable-gtk-doc --disable-introspection --without-qmi
+ - make
+ - make check
+ - make install
+
+build-qmi-qrtr-no-mbim:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - merge_requests
+ - tags
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqrtr-glib.git
+ - pushd libqrtr-glib
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
+ - pushd libqmi
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false -Dmbim_qmux=disabled -Dqrtr=enabled -Dcollection=basic
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - NOCONFIGURE=1 ./autogen.sh
+ - ./configure --prefix=/usr --disable-gtk-doc --disable-introspection --without-mbim
+ - make
+ - make check
+ - make install
+
+build-qmi-no-qrtr-no-mbim:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - merge_requests
+ - tags
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
+ - pushd libqmi
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false -Dmbim_qmux=disabled -Dqrtr=disabled -Dcollection=basic
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - NOCONFIGURE=1 ./autogen.sh
+ - ./configure --prefix=/usr --disable-gtk-doc --disable-introspection --without-mbim
+ - make
+ - make check
+ - make install
+
+build-no-qmi-no-mbim:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - merge_requests
+ - tags
+ - schedules
+ script:
+ - NOCONFIGURE=1 ./autogen.sh
+ - ./configure --prefix=/usr --disable-gtk-doc --disable-introspection --without-qmi --without-mbim
+ - make
+ - make check
+ - make install
+
+build-qmi-newest-commands:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - merge_requests
+ - tags
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
+ - pushd libqmi
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false -Dmbim_qmux=disabled -Dqrtr=disabled -Dcollection=basic
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - NOCONFIGURE=1 ./autogen.sh
+ - ./configure --prefix=/usr --disable-gtk-doc --disable-introspection --without-mbim CFLAGS="-DWITH_NEWEST_QMI_COMMANDS"
+ - make
+ - make check
+ - make install
+
+build-single-plugins:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libmbim.git
+ - pushd libmbim
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqrtr-glib.git
+ - pushd libqrtr-glib
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
+ - pushd libqmi
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false -Dmbim_qmux=enabled -Dqrtr=enabled -Dcollection=basic
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - for plugin in generic altair_lte anydata broadmobi cinterion
+ dell dlink fibocom foxconn gosuncn haier huawei iridium
+ linktop longcheer mbm motorola mtk nokia nokia_icera
+ novatel novatel_lte option option_hso pantech
+ qcom_soc quectel samsung sierra_legacy sierra simtech
+ telit thuraya tplink ublox via wavecom x22x zte; do
+ meson setup build_$plugin --prefix=/usr -Dgtk_doc=false -Dintrospection=disabled -Dbash_completion=false -Dauto_features=disabled -Dqmi=enabled -Dmbim=enabled -Dqrtr=enabled -Dplugin_$plugin=enabled;
+ ninja -C build_$plugin;
+ done
+
+build-default:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - merge_requests
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libmbim.git
+ - pushd libmbim
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=enabled -Dbash_completion=false
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqrtr-glib.git
+ - pushd libqrtr-glib
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=enabled
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
+ - pushd libqmi
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=enabled -Dbash_completion=false -Dmbim_qmux=enabled -Dqrtr=enabled -Dcollection=basic
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - NOCONFIGURE=1 ./autogen.sh
+ - ./configure --prefix=/usr --enable-gtk-doc --enable-introspection --with-polkit=strict --with-suspend-resume=systemd --with-systemdsystemunitdir=/lib/systemd/system
+ - make
+ - make check
+ - make install
+ - make distcheck
+
+build-default-artifacts:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - tags
+ script:
+ - !reference [build-default, script]
+ - sha256sum $CI_PROJECT_NAME-*.tar.xz | awk '{print $1;}' > pkg_hash.txt
+ artifacts:
+ name: "$CI_PROJECT_NAME-$CI_COMMIT_SHORT_SHA"
+ paths:
+ - /builds/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/$CI_PROJECT_NAME-*.tar.xz
+ - /builds/$CI_PROJECT_ROOT_NAMESPACE/$CI_PROJECT_NAME/pkg_hash.txt
+ expire_in: 2 days
+
+build-meson-release:
+ stage: build
+ extends:
+ - .fdo.distribution-image@ubuntu
+ - .common_variables
+ only:
+ - master
+ - merge_requests
+ - tags
+ - schedules
+ script:
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libmbim.git
+ - pushd libmbim
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=enabled -Dbash_completion=false
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqrtr-glib.git
+ - pushd libqrtr-glib
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=enabled
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - git clone --depth 1 https://gitlab.freedesktop.org/mobile-broadband/libqmi.git
+ - pushd libqmi
+ - meson setup build --prefix=/usr -Dgtk_doc=false -Dintrospection=enabled -Dbash_completion=false -Dmbim_qmux=enabled -Dqrtr=enabled -Dcollection=basic
+ - ninja -C build
+ - ninja -C build install
+ - popd
+ - meson setup build --buildtype=release --prefix=/usr -Dwerror=true -Dgtk_doc=false -Dintrospection=enabled -Dqmi=enabled -Dmbim=enabled -Dqrtr=enabled -Dpolkit=strict -Dsystemd_suspend_resume=true -Dsystemdsystemunitdir=/lib/systemd/system
+ - ninja -C build
+ - ninja -C build install
+ - ninja -C build uninstall
diff --git a/AUTHORS b/AUTHORS
index d6c2b31c..98b44ce0 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -9,63 +9,185 @@ Previous maintainers:
Other contributors:
Ben Chan
Nathan Williams
+ Daniele Palmas
+ Carlo Lobrano
+ Teijo Kinnunen
+ Eric Caruso
+ Lubomir Rintel
+ Andrew Lassalle
+ Giacinto Cifelli
+ Dylan Van Assche
Torgny Johansson
+ Marco Bascetta
+ Riccardo Vangelisti
Alexander Sack
Guido Günther
+ Joel Selvaraj
+ Loic Poulain
+ Michal Mazur
+ Pavan Holla
Eric Shienbrood
Elly Jones
+ Bob Ham
+ Yegor Yefremov
+ Bjørn Mork
+ David McCullough
+ Freedom Liu
Prathmesh Prabhu
Thieu Le
Thomas Tuttle
- Bjørn Mork
+ Maxim Anisimov
+ Torsten Hilbrich
Marius B. Kotsbak
- Christian Persch
- David McCullough
Michael Biebl
+ Paul Bartell
+ Sven Schwermer
+ Alfonso Sánchez-Beato
+ Christian Persch
+ Stephan Gerhold
+ Thomas Sailer
Colin Walters
Franko Fang
+ Frederic Martinsons
+ Jakub Sitnicki
Jason Glasgow
Jeroen Elebaut
- Ori Inbar
+ Matthew Starr
+ Reinhard Speyerer
+ Yunlian Jiang
+ ori inbar
David Rochberg
+ Ivan Mikhanchuk
Jiří Klimeš
+ Krzysztof Drobinski
+ Louis-Alexis Eyraud
Martyn Russell
+ Thomas Haller
+ Tomas Jura
+ ZhangMingjie
+ Alexander Dahl
+ Amol Lad
+ Clayton Craft
+ Colin Helliwell
+ David Leonard
Graham Inggs
+ Javier Viguera
+ Jessy Diamond Exum
+ Justin Standring
Lionel Landwerlin
+ Madhav
+ Maksim Salau
Martin Pitt
+ Matthew Stanger
+ Milo Casagrande
+ Nick Stevens
Norbert Frese
+ Piotr Drąg
Thomas Bechtold
- Yegor Yefremov
- Yunlian Jiang
+ Vincent Palatin
+ Yuri Chornoivan
Adrian Bunk
+ Adrien Plazas
+ Alexander Couzens
+ Alyssa Ross
Amit Mendapara
+ Anders Jonsson
+ Andika Triwidada
Andrew Bird
Anton Blanchard
+ ArenM
+ Arnaud Ferraris
Arnd Hannemann
+ Arto Jantunen
Arun Raghavan
+ Balázs Úr
+ Baruch Siach
+ Beniamino Galvani
+ Benjamin Tissoires
+ Benoît Monin
+ Brendan Peter
+ Brian Norris
+ Brian, Sam
Bryan Duff
+ Claude Paroz
David (Pololu)
David Castellanos
+ David Herrmann
David Härdeman
+ David Khouya
Dmitry Ivanyushin
+ Dušan Kazik
+ Enrico Mioso
Eugene Crosser
Evan Nemerson
+ Fabrice Bellet
Fangxiaozhi (Franko)
+ Felipe Borges
+ Felix Janda
Gerald Richter
+ Greg Suarez
+ Iain Lane
+ Iñigo Martínez
+ Jarvis-Jiang-G
+ Jason Simmons
Jens Seidel
+ Johny Mattsson
Julian Oes
Jun Woo Lee
+ Khem Raj
+ Krzysztof Kotlenga
+ Lech Perczak
+ LiuQiFeng
+ Luis A. Lozano
+ Lukas Senger
M. I. Spozta
+ Marc Murphy
Mario Blättermann
+ Mario Limonciello
+ Mark-Jablonsky
+ Michael Farrell
Michał Sroczyński
+ Mohammed Sadiq
+ Moo
+ Murithi Borona
Nathan J. Williams
+ Nicholas Smith
Noel J. Bergman
+ Oskar Enoksson
+ Piotr Figiel
Quentin.Li
+ Rafael Fontenelle
+ Roshan Pius
+ Sam Spilsbury
+ Sławomir Bocheński
+ Tabor Kelly
+ Thomas Dendale
Thomas Grenman
+ Thomas Voß
+ Timo Jyrinki
+ Ting-Yuan Huang
Tom Bechtold
Tom Goetz
Tore Anderson
+ Tyler
+ Ulrich Ölmann
+ Valentin Blot
+ Ville Skyttä
Vincent Untz
Vitaly Gimly
- Yuri Chornoivan
+ Walter Hagstrom
+ Yaron Shahrabani
+ Zrshuo Zhang
+ amol.lad
+ carlyin
+ emintufan
+ goapunk
kuonirat
+ mozzwald
+ mstanger
+ poma
+ scootergrisen
+ wi24rd
+ wicadmin
+ yparitcher
+ Артемий Судаков
diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
index e69de29b..00000000
--- a/ChangeLog
+++ /dev/null
diff --git a/Makefile.am b/Makefile.am
index f6bea8e1..4dd7ad63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,26 +1,53 @@
-SUBDIRS = . build-aux data include libqcdm libwmc libmm-glib src plugins cli vapi introspection uml290 po test examples docs
+SUBDIRS = \
+ . \
+ build-aux \
+ po \
+ data \
+ include \
+ libqcdm \
+ libmm-glib \
+ src \
+ plugins \
+ cli \
+ vapi \
+ introspection \
+ test \
+ tools \
+ examples \
+ docs \
+ $(NULL)
-DISTCHECK_CONFIGURE_FLAGS = \
+ChangeLog:
+ $(AM_V_GEN) if test -d "$(srcdir)/.git"; then \
+ (GIT_DIR=$(top_srcdir)/.git $(top_srcdir)/missing --run git log --stat) | fmt --split-only > $@.tmp \
+ && mv -f $@.tmp $@ \
+ || ($(RM) $@.tmp; \
+ echo Failed to generate ChangeLog, your ChangeLog may be outdated >&2; \
+ (test -f $@ || echo git-log is required to generate this file >> $@)); \
+ else \
+ test -f $@ || \
+ (echo A git checkout and git-log is required to generate ChangeLog >&2 && \
+ echo A git checkout and git-log is required to generate this file >> $@); \
+ fi
+
+AM_DISTCHECK_CONFIGURE_FLAGS = \
--with-udev-base-dir="$$dc_install_base" \
--with-systemdsystemunitdir="$$dc_install_base/$(SYSTEMD_UNIT_DIR)" \
- --enable-gtk-doc=yes
-
-INTLTOOL_FILES = \
- intltool-extract.in \
- intltool-merge.in \
- intltool-update.in
-
-DISTCLEANFILES = \
- intltool-extract \
- intltool-merge \
- intltool-update \
- po/.intltool-merge-cache
+ --enable-gtk-doc=yes \
+ $(NULL)
EXTRA_DIST = \
- $(INTLTOOL_FILES) \
autogen.sh \
gtester.make \
- COPYING.LIB
+ COPYING.LIB \
+ $(NULL)
ACLOCAL_AMFLAGS = -I m4
+
+@CODE_COVERAGE_RULES@
+
+if CODE_COVERAGE_ENABLED
+clean-local:
+ -find $(top_builddir) -name "*.gcno" -delete
+endif
diff --git a/NEWS b/NEWS
index e066dcb5..42519fed 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,1027 @@
+ModemManager 1.18.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version now requires:
+ ** glib2 >= 2.56
+ ** libgudev >= 232
+ ** libmbim >= 1.26.0 (for the optional MBIM support)
+ ** libqmi >= 1.30.2 (for the optional QMI support)
+ ** libqrtr-glib >= 1.0.0 (for the optional QRTR support)
+
+ * The ModemManager.service file for systemd integration provided in the sources
+ is updated as follows:
+ ** 'CAP_NET_ADMIN' is now required in the 'CapabilityBoundingSet' field.
+ ** 'AF_NETLINK' and 'AF_QIPCRTR' are now required in the
+ 'RestrictAddressFamilies' field.
+ If the system where ModemManager is being integrated provides a custom
+ systemd service configuration, these updates should be considered.
+
+ * The LEGACY and PARANOID filter types that were allowed options in the
+ '--filter-policy' option in the ModemManager daemon were deprecated in
+ version 1.16.0 and have now been completely removed, along with the vid:pid
+ blacklist of devices and the vid:pid greylist of RS232<->USB adapters.
+
+ * Building from git no longer requires autoconf-archive, the needed AX_ macros
+ are now shipped inside m4/.
+
+ * In addition to building from a source release tarball, or building from git
+ checkouts using the GNU autotools suite (autoconf/automake/libtool), this
+ release includes the initial support for the meson build system. The meson
+ port is not fully complete yet, as there are some missing things in the doc
+ generation and test steps, but for system integration or development
+ purposes, the port should be fully operational. This major release, including
+ all its stable updates in the 1.18.x series, will be the last ones providing
+ support for GNU autotools. The next major release will likely be a meson-only
+ one, and will therefore not be based on a source release tarball any more,
+ but on specific git tags instead.
+
+The most important features and changes in this release are the following:
+
+ * Data session multiplexing can now be enabled in QMI and MBIM modems, e.g. so
+ that multiple different APNs can be connected separately over a single
+ network interface. The multiplexing is disabled by default in this release,
+ except for cases where it's required (e.g. if non-multiplexed sessions aren't
+ supported) like IPA based Qualcomm SoCs. Users can request the multiplexing
+ support explicitly via settings when creating the connection bearer object.
+
+ In order to allow easy testing of the multiplexing feature without requiring
+ any additional change in the stack, a new '--test-multiplex-requested' option
+ in the daemon allows to switch the default (when not explicitly given by the
+ user) to attempt to use multiplexing.
+
+ It is worth noting that when multiplexing is enabled, the data network
+ interface used by the modem will be a virtual network interface created in
+ runtime, and will therefore have a different name than the real network
+ interface exposed by the modem. If there are additional settings in the
+ system relying on the data network interface name (e.g. iptables rules), they
+ may need to be updated.
+
+ * The ModemManager daemon can run now in a 'quick suspend/resume' mode, in
+ which no explicit data disconnection is triggered on suspend, and no explicit
+ device re-probing from scratch is launched on resume. Instead, the daemon
+ will try to refresh the state of all interfaces upon suspend, e.g. to see if
+ the module keeps registered to the same operator, to see if it is still
+ connected, and so on.
+
+ This mode of operation is useful when the WWAN module stays awake while the
+ host is suspended, and can be enabled with the '--test-quick-suspend-resume'
+ option in the daemon.
+
+ * API:
+ ** New '3gppProfileManager' interface, providing operations on the list of
+ connection profiles stored in the 3GPP module. This interface is
+ implemented for all AT, QMI and MBIM protocols.
+ ** New 'DisableFacilityLock()' method in the 3GPP interface, implemented for
+ QMI and MBIM devices.
+ ** The 'MaxBearers' property is now deprecated, as it didn't provide any
+ additional information to what 'MaxActiveBearers' already provides.
+ ** New 'MaxActiveMultiplexedBearers' property, listing how many bearers can
+ be connected at the same time if multiplexing is enabled.
+ ** New settings in the bearer properties, applicable to both the
+ 'Simple.Connect()' and 'Modem.CreateBearer()' methods:
+ *** 'multiplex': which allows the user to specify whether multiplexing
+ should be avoided ('none'), whether it should be mandatory
+ ('required') or whether it should be enabled if available or skipped
+ if unavailable ('requested').
+ *** 'profile-id': which allows the user to request a connection attempt
+ with an existing profile stored in the module.
+ *** 'apn-type': which allows the user to specify the purpose of a given
+ connection, e.g. the user could create a connection to an APN
+ providing default internet connectivity and another one to an APN
+ providing access to the MMS gateway. This setting may or may not be
+ stored in the module itself, it depends on the type of module.
+ ** New 'Multiplexed' boolean property in the Bearer object, specifying
+ whether the bearer is connected through a multiplexed interface.
+ ** New 'ConnectionError' property in the bearer object, specifying the last
+ error reported by the module during a failed connection attempt or during
+ a network-initiated disconnection.
+ ** Updated the list of enum values in the MMMobileEquipmentError' type,
+ according to the error codes defined in v17.1.0 of 3GPP TS 27.007.
+
+ * Core:
+ ** iconv() features support is detected at runtime, and logged when the
+ daemon starts.
+ ** Updated the base modem object to allow plugins to specify the types of
+ data ports they support, based on the specific plugin implementations,
+ e.g. so that a modem supporting only AT+PPP can ignore NET ports and
+ vice versa.
+ ** Added support for modems exposing control ports via QRTR channels.
+
+ * Modem interface:
+ ** The Dual SIM logic that would iterate over all slots during initialization
+ is updated, so that we only report the information that we can gather
+ without any explicit slot change. E.g. with QMI we can know whether there
+ is a SIM in the non-active slot, and the ICCID of that SIM, but we cannot
+ know the MCCMNC or the operator name of the SIM unless we change to that
+ slot. We must not do slot changes arbitrarily like that, and so that logic
+ is removed, even if we lose some of the information that we were providing
+ in the interface.
+
+ * Location interface:
+ ** The multi-sentence NMEA trace support is updated to include additional
+ possible trace types in addition to GSV (e.g. ALM, GSV, RTE, SFI) and also
+ when coming from other constellations, not just GPS.
+
+ * SIM:
+ ** New 'PreferredNetworks' property and 'SetPreferredNetworks' method,
+ implemented using '+CPOL' for generic AT modems and 'NAS Get/Set Preferred
+ Networks' for QMI modems. Several different modules and plugins (e.g.
+ Sierra Wireless EM7345, Telit LN930, SIM7070, all Option and Iridium
+ devices...) have this feature explicitly disabled due to '+CPOL' not
+ behaving properly (even crashing the module sometimes).
+
+ * QMI:
+ ** The logic that decides which data mode (802.3 or raw-ip) is used in
+ modules managed by the qmi_wwan driver changes in this release. Until now,
+ if a module reported itself as configured in 802.3 mode on boot, that mode
+ would be the one used in normal operation. Due to the new multiplexing
+ feature, this is no longer true, and if possible the daemon will always
+ try to switch the module to raw-ip, and fallback to 802.3 only if raw-ip
+ is unsupported.
+ ** Enabled both AT and QMI indications for the messaging and voice interfaces
+ so that new SMS and call events are reported via both channels. This
+ solves issues seen in the Pinephone when waking up from suspend.
+ ** Enabled network reject indications.
+ ** If operator name not updated through standard indications, it will be
+ explicitly queried with 'NAS Get Plmn Name'.
+ ** Added support for transfer-route MT messages.
+ ** Increased the QMI open timeout to 45s, as required by the newest modules.
+ ** Implemented additional logic to read the status of the different facility
+ locks in the module.
+ ** Updated ICCID reading logic to parse it as hex instead of BCD.
+ ** Improved handling of the MNC PCS digit in the operations involving MCCMNC.
+ ** Automatically run the 'DPM Open Port' logic on IPA based setups to bind
+ the hardware tx/rx endpoints with the logical ones in the QMI protocol.
+ ** Implemented support for the Voice interface and its operations, not only
+ standard voice call management, but also support for the supplementary
+ services. Voice call management will be done completely using QMI, even
+ if the new call indications are notified via AT URCs.
+
+ * MBIM:
+ ** Implemented support for Dual SIM in non-QMI MBIM devices, using the
+ Microsoft Basic Connect Extensions service.
+ ** Increased the timeout for the MBIM_CID_HOME_PROVIDER query to 30s.
+ ** Updated to load model string using QMI over MBIM if available.
+ ** Increased the MBIM open timeout to 45s, as required by the newest modules.
+
+ * SMS:
+ ** Defined a common timeout of 180s for all send operations.
+
+ * libmm-glib:
+ ** Updated with new methods and types to handle all the DBus API updates.
+ ** Extended with additional methods in the Location3gpp object to get/set the
+ full operator MCCMNC string, instead of integers without MNC PCS digit
+ info.
+ ** Extended the 'ModemLocation' interface with methods to get the signaled
+ location updates; i.e. without requiring an explicit GetLocation(), and
+ obviously only supported when location signaling is explicitly enabled.
+ ** Updated the way the internal monitored properties are handled in the
+ different types, now using some handy helper macros to share the same
+ logic among all.
+
+ * Plugins:
+ ** zte: disabled CIND/CMER support.
+ ** qcom-soc: added support for QRTR+IPA based setups.
+ ** qcom-soc: added support for the WWAN subsystem instead of RPMSG.
+ ** quectel: enabled QGPSXTRA by default when starting the GNSS engine.
+ ** quectel: add support for EM120/160 PCIe modules.
+ ** quectel: added Firehose update method.
+ ** ublox: added additional URAT combinations.
+ ** ublox: flagged UBANDSEL as unsupported in the SARA-R4 and -N4 modules.
+ ** cinterion: added new custom MBIM based modem with shared reset operation.
+ ** cinterion: ignored the MBIM Intel Firmware Update service completely.
+ ** foxconn: added custom carrier config setup for the T77W968 module.
+
+The following features which were backported to 1.16.x releases are also present
+in ModemManager 1.18.0:
+
+ * core: added support for the new 'WWAN' subsystem in Linux kernel 5.13,
+ enabling PCIe-only modules.
+ * core: The charset conversion methods rework, including the avoiding of the
+ iconv() //TRANSLIT extension support, which isn't available in all libc
+ implementations.
+ * qmi: the logic managing allowed/preferred modes was fixed for multimode
+ devices like the MC7304, making sure the acquisition order preference always
+ had the same items.
+ * serial: when modem is connected with AT+PPP, ignore forced disconnections, so
+ that we don't take ownership of the PPP port before pppd has released it.
+ * foxconn: added support for the T99W175 (SDX55) module, including built-in FCC
+ unlock procedure.
+ * foxconn: added new MBIM QDU firmware update method.
+
+
+ModemManager 1.16.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version now requires:
+ ** libqmi >= 1.28.0 (for the optional QMI support)
+
+ * The 1.16.x branch will be the last one supporting the 'LEGACY' and 'PARANOID'
+ filter modes; standard distributions are advised to use the default 'STRICT'
+ mode if they aren't using it already (i.e. running the daemon without any
+ explicit '--filter-policy' option).
+
+ * A new 'qcom-soc' plugin is implemented to be able to use ModemManager in
+ Qualcomm SoCs like the MSM8916 or MSM8974. This plugin uses a combination of
+ RPMSG based control ports plus BAM-DMUX based network ports. This plugin is
+ disabled by default, even when `--enable-all-plugins` is used, and if wanted
+ it must be explicitly enabled with `--enable-plugin-qcom-soc`. Systems
+ targeting this kind of SoCs, like postmarketos, should enable it. Standard
+ distributions may or may not include it, up to the targeted hardware in each
+ distribution.
+
+ * Gentoo's 'libelogind' library may now be used to detect the systemd
+ suspend/resume support.
+
+The API is backwards compatible with the previous releases, the only updates
+are the following:
+
+ * Modem interface:
+ ** Updated the 'Ports' property so that it exposes all ports that are
+ owned by the modem even if they are explicitly ignored and not used.
+ ** New 'SimSlots' property that exposes the available SIM slots in the modem,
+ including the SIM object paths in each of them if the cards are present.
+ ** New 'PrimarySimSlot' property indicating which of the slots in the
+ 'SimSlots' array is the one currently active.
+ ** New 'SetPrimarySimSlot' method to select which SIM slot in the 'SimSlots'
+ array should be considered active. When the switch happens, the modem will
+ be fully re-probed.
+
+ * Signal interface:
+ ** New 'Nr5g' dictionary property including signal information for the 5GNR
+ access technology.
+
+ * SIM interface:
+ ** New 'Active' boolean property, indicating whether the SIM object is the
+ currently active one.
+ ** New 'Eid' string property, indicating the EID of the card, if any.
+
+ * New udev tags:
+ ** New 'ID_MM_PORT_TYPE_QMI' tag to explicitly flag a port as being QMI, when
+ there is no other way to guess the type of port; e.g. this tag is not
+ needed for ports exposed by the qmi_wwan driver.
+ ** New 'ID_MM_PORT_TYPE_MBIM' tag to explicitly flag a port as being MBIM,
+ when there is no other way to guess the type of port; e.g. this tag is not
+ needed for ports exposed by the cdc_mbim driver.
+
+The most important features and changes in this release are the following:
+
+ * Implemented support for Multi SIM Single Standby support, for systems that
+ have multiple SIM slots and they can select which of them (only one) is
+ active at any given time. Currently implemented for QMI modems only.
+
+ * If the modem enabling phase fails in a fatal way, an implicit disabling
+ sequence is now run, in order to avoid leaving the modem in an inconsistent
+ state.
+
+ * If the connection attempt includes user/password information but no explicit
+ authentication type, CHAP will now be used by default instead of PAP.
+
+ * Full USB device removal events reported via udev are no longer used. The
+ device removal logic relies exclusively on independent port removal events,
+ as that logic is supported for all subsystems and kernel device backends
+ (e.g. also working for non-USB devices and for systems without udev like
+ OpenWRT).
+
+ * Added support to monitor the 'rpmsg' subsystem, but only in plugins that
+ explicitly require its use (e.g. the 'qcom-soc' plugin).
+
+ * New options in the ModemManager daemon:
+ ** Added new '--test-no-suspend-resume' option to disable the runtime
+ suspend/resume support even if the daemon was built with it.
+ ** Added new '--test-no-udev' option to disable the runtime udev support even
+ if the daemon was built with it.
+
+ * Serial:
+ ** Also match OK or ERROR responses that are not at end of line.
+
+ * SIM:
+ ** Force reprobing the modem if a new SIM is detected in a modem that
+ initially started in Failed state without SIM.
+ ** Force reprobing the modem if the lock status cannot be read after sending
+ SIM-PUK, so that it transitions to the Failed state.
+ ** Force reprobing the modem if a PUK lock is discovered after sending
+ SIM-PIN, so that it transitions to the Failed state.
+
+ * QMI:
+ ** The logic no longer depends on the service version reported by each
+ client, the support for each feature is explicitly probed instead.
+ ** Implemented SIM profile (eUICC) change detection.
+ ** Support for QMI modems on kernels < 3.6 is dropped. Only kernels where the
+ QMI control ports are exposed in the 'usbmisc' subsystem are supported.
+ ** Implemented additional step in the connection logic to allow binding the
+ WDS client to a given SIO port, required in the BAM-DMUX driver setup.
+ ** Implemented support for the initial EPS bearer settings logic.
+ ** Disabled explicit signal and access technology polling if indications have
+ been correctly enabled.
+
+ * MBIM:
+ ** Enable SIM hot swap detection logic with QMI over MBIM.
+ ** Allow plugins to specify explicitly that QMI over MBIM is not supported.
+
+ * libmm-glib:
+ ** Added missing APIs to get/set RM protocol in the Simple connect settings.
+
+ * Plugins:
+ ** gosuncn: new plugin, for now just with port type hints for the GM800.
+ ** quectel: implemented GPS support with +QGPS.
+ ** quectel: implemented custom time support check to prefer +CTZU=3 instead
+ of +CTZU=1 so that the modem reports localtime instead of UTC in +CCLK.
+ ** sierra: added support for XMM-specific features (e.g. EM7345).
+ ** cinterion: implemented support for the initial EPS bearer settings logic.
+ ** cinterion: added SIM hot swap support to AT-based modems.
+ ** huawei: updated to avoid applying multiple port type hint methods.
+ ** huawei: updated the ^GETPORTMODE logic so that we don't assume the hints
+ in the response apply to specific USB interfaces.
+
+The following features which were backported to 1.14.x releases are also present
+in ModemManager 1.16.0:
+
+ * location: allow CID only updates.
+ * sms: allow sending/receiving UTF-16 as if it were UCS-2.
+ * modem: don't consider charset setup failure as fatal.
+ * QMI: fix reporting signal strength indications.
+ * QMI: fix parsing of USSD indications with UTF-16 data.
+ * QMI: run network registration with NAS Set System Selection Preference.
+ * QMI: when connection aborted, ensure network handles are released.
+ * MBIM: don't fail IPv4v6 connection attempt if only IPv4 succeeds.
+ * cinterion: improve user/password handling in AT^SGAUTH calls.
+ * cinterion: removed limitation to IPv4 only PDP contexts.
+ * cinterion: configure the PLAS9 to send URCs correctly.
+ * quectel: add support for MBIM devices.
+ * telit: add initial delay for AT ports to become responsive.
+ * udev: updated all AT/QCDM/GPS port type hints in all plugins to bind them to
+ TTY ports only.
+
+
+ModemManager 1.14.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version requires:
+ ** GLib/GObject/GIO >= 2.48.0
+ ** libmbim >= 1.24.0 (for the optional MBIM support)
+ ** libqmi >= 1.26.0 (for the optional QMI support)
+
+ * Build updated with several improvements:
+ ** The build has been updated to use by default all warnings enabled by
+ AX_COMPILER_FLAGS(), and therefore when building the release from a git
+ checkout, autoconf-archive >= 2017.03.21 is now required. This new build
+ dependency isn't required when building from the release tarball.
+ ** Also when building from a git checkout, beware because by default
+ --enable-compile-warnings=error is enabled, which implies -Werror. If
+ you'd like to build from git and avoid -Werror, you should explicitly use
+ --enable-compile-warnings=yes (to keep the warnings but without being
+ errors), --enable-compile-warnings=no (to disable all the extra warnings
+ enabled by default) or --disable-Werror (to unconditionally make all
+ compiler warnings non-fatal).
+ ** Users can now preselect which plugins to build and install during
+ configure, with the --enable-plugin-[PLUGIN-NAME] options. The user can
+ also build and install all except for some, just by using the
+ --enable-all-plugins option followed by --disable-plugin-[PLUGIN-NAME].
+ By default all plugins are enabled, so all of them built and installed.
+ This new set of options are useful for users building custom systems
+ where they already know what kind of devices they're going to have, it
+ isn't recommended for standard distributions.
+
+The only updates in the public API are the following:
+
+ * Modem interface:
+ ** Added support for AT-based and/or QMI-based 5G devices, which will report
+ the new MM_MODEM_CAPABILITY_5GNR capability.
+ ** Deprecated the MM_MODEM_CAPABILITY_LTE_ADVANCED capability, as it was
+ never used in any implementation.
+
+ * Bearer interface:
+ ** Added additional 'attempts', 'failed-attempts', 'total-rx-bytes',
+ 'total-tx-bytes' and 'total-duration' values in the 'Stats' property
+ exposed by the Bearer objects.
+
+The most important features and changes in this release are the following:
+
+ * The daemon switched to 'STRICT' filter mode by default. The old 'DEFAULT'
+ mode is renamed to 'LEGACY' and is considered now deprecated. Under the
+ 'STRICT' filter mode, the TTY blacklist and greylist are ignored, and the
+ port probing mechanism uses its own heuristics to guess whether a given TTY
+ is owned by a modem or not.
+
+ * Added a new implicit whitelist rules applicable in 'STRICT' filter mode, so
+ that all devices with a USB vid matching one of the vids allowed by the
+ different installed plugins are implicitly allowed.
+
+ * Updated daemon logging so that we always prefix the messages with a string
+ identifying which modem the log refers to, making it easy to grep logs for
+ one specific device if the system has more than one.
+
+ * Updated the probing logic to make sure we don't attempt a re-probe when the
+ device is gone.
+
+ * Probing logic now allows new ports detected up to 1500ms since last port
+ added, useful on OpenWrt setups where ports are notified one by one via
+ hotplug events.
+
+ * AT:
+ ** Moved the charset definition logic to the initialization phase instead of
+ the enabling phase, because the feature support checks may already require
+ string processing based on the current charset.
+ ** Updated manual registration operation to attempt using current charset
+ (e.g. UCS2) if ASCII fails.
+
+ * QMI:
+ ** Devices using the LOC service for GNSS will now also setup the list of
+ required NMEA traces before starting the engine.
+ ** Implemented 3GPP USSD support using the Voice service.
+ ** Update carrier code if registration changes from one roaming operator to
+ another.
+ ** Explicitly disable autoconnect during modem enabling phase, because it
+ interferes with our connection management logic.
+ ** Fallback to raw-ip if WDA Get Data Format requests arguments, as in most
+ new 5G devices.
+ ** Updated to always use the asynchronous close() operation.
+ ** Handle disconnection indications during connection attempts.
+
+ * MBIM:
+ ** Update carrier code if registration changes from one roaming operator to
+ another.
+ ** Implement reset in Intel-based and Qualcomm-based devices.
+ ** Avoid LTE attach config/status if unsupported.
+ ** Updated to make sure all allocated QMI CIDs are released during shutdown.
+
+ * SIM interface:
+ ** Don't allow sending PIN/PUK if not required.
+
+ * 3GPP interface:
+ ** Fixed manual re-registration to the same operator.
+
+ * CDMA interface:
+ ** Don't allow multiple concurrent activation attempts.
+ ** Disallow empty carrier code in automatic activation.
+
+ * Bearer interface:
+ ** Updated to avoid connection checks or stats updates while disconnecting.
+
+ * libmm-glib:
+ ** New 'mm_location_gps_nmea_get_traces()' method to retrieve a NULL
+ terminated array of strings with all cached NMEA traces.
+ ** Deprecated the 'mm_location_gps_nmea_build_full()' method.
+
+ * mmcli:
+ ** Added a new 'any' lookup keyword for the --modem and --sim options, useful
+ when the system is only expected to have one single device.
+
+ * Plugins:
+ ** broadmobi: new plugin, right now just with port type hints for the BM818.
+ ** foxconn: new plugin to support the T77W968 (both with and without eSIM).
+ ** dell,dw5821e: added support for the DW5821e with eSIM variant.
+ ** huawei: don't delay reporting network initiated disconnects.
+ ** huawei: try to read port type hints from interface descriptions.
+ ** huawei: avoid using the QCDM port during a voice call.
+ ** cinterion: skip sim ready check for modules that don't support it.
+ ** cinterion: implemented radio/band handling for LTE modems.
+ ** cinterion: added Signal interface support bsaed on AT^SMONI.
+ ** cinterion: added support for MBIM based devices like the PLS62-W.
+ ** quectel: updated to detect SIM hot swap via +QUSIM URCs.
+ ** fibocom: added support for QMI based devices like the FM150.
+ ** ublox: ignore error when disconnecting last LTE bearer.
+ ** ublox: implement support to enable and detect +UUDTMF URCs.
+ ** ublox: added blacklist rules for GPS modules in the plugin itself.
+ ** sierra: implement manual and automatic CDMA activation.
+ ** novatel: implement manual and automatic CDMA activation.
+
+All the features and fixes which were backported to 1.12.x releases are also
+present in ModemManager 1.14.0.
+
+
+ModemManager 1.12.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version requires:
+ ** libmbim >= 1.18.0 (for the optional MBIM support)
+ ** libqmi >= 1.24.0 (for the optional QMI support)
+
+ * The build allows to configure with '--with-at-command-via-dbus' in order to
+ unconditionally enable the Modem.Command() method to allow running AT
+ commands through ModemManager. This is not suggested for standard
+ distributions, though.
+
+The most important features and changes in this release are the following:
+
+ * Modem interface:
+ ** Updated logic to avoid assuming that setting bands or modes is immediate,
+ the daemon will now actively monitor for those updates to happen before
+ returning success.
+
+ * 3GPP interface:
+ ** libmm-glib: deprecated the mm_pco_list_free() helper method.
+
+ * Simple interface:
+ ** api,libmm-glib: deprecated the 'subscription state' property.
+
+ * Location interface:
+ ** Fixed 'unknown' lat/long/alt numeric values.
+ ** Added support for MSB A-GPS in addition to MSA A-GPS.
+
+ * Voice interface:
+ ** Improved voice call management with call id detection and tracking.
+ ** Improved detailed call state transitions on generic modems that support
+ call list polling.
+ ** Added support for GSM supplementary services, including call waiting,
+ call transfer, call deflection, multiparty calls...
+ ** Added emergency call support, allowing voice call to emergency numbers
+ even without SIM or with SIM-PIN locked.
+ ** Deprecated all properties except for 'number' in the CreateCall()
+ method.
+
+ * Messaging interface:
+ ** Updated to report SMS timestamps in correct ISO8601 format.
+
+ * Bearer:
+ ** Improved unused CID lookup to allow selecting non-sequential CIDs.
+ ** Disabled all AT protocol based context monitoring when PPP is used for
+ the connection, in order to properly sync with pppd, which should be the
+ one detecting the disconnections (already in 1.10.6).
+
+ * QMI:
+ ** Improved support to list stored firmware images in Sierra devices.
+ ** Added additional lock check retries on 'SIM not inserted' errors.
+ ** Updated explicit registration attempt to report success only when the
+ target requested network is registered.
+ ** Added MSB A-GPS support.
+ ** Implemented automatic carrier configuration selection using PDC service
+ (already in 1.10.2).
+
+ * mmcli:
+ ** New machine-readable JSON output with '--output-json'.
+ ** Updated to allow using the modem UID to specify SIM operations.
+
+ * udev:
+ ** New ID_MM_PORT_TYPE_AUDIO generic udev tag to identify ports that
+ should be used for in-band audio.
+ ** Removed support for the ID_MM_PLATFORM_DRIVER_PROBE udev tag, as it
+ is no longer required given that the more generic explicit whitelist
+ may be used to flag which devices should be probed.
+ ** Renamed ID_MM_DEVICE_MANUAL_SCAN_ONLY to ID_MM_TTY_MANUAL_SCAN_ONLY,
+ given that the tag only applies to TTYs.
+ ** ID_MM_DEVICE_IGNORE is no longer used internally in ModemManager, and
+ is instead targeted to users that want to explicitly ignore specific
+ devices regardless of what filter type is in use (already in 1.10.6).
+
+ * dbus:
+ ** Updated to always report the registered MM_CORE_ERROR_CANCELLED error
+ instead of the implicit G_IO_ERROR_CANCELLED ones generated by GLib.
+
+ * GObject introspection:
+ ** Fixed setup to explicitly skip all non-API methods.
+
+ * Plugins:
+ ** tplink: new plugin.
+ ** dlink: new plugin.
+ ** xmm: added MSB A-GPS support.
+ ** dell,dw5821e: update to allow unmanaged GPS support on the TTY even
+ when raw/NMEA GPS is enabled via QMI/LOC.
+ ** quectel: updated to allow TTY-only devices.
+ ** telit: added GPS support.
+ ** telit: improved band management with #BND.
+ ** simtech: added improved voice call support.
+ ** simtech: added support for LTE devices.
+ ** simtech: improved signal quality reporting logic.
+ ** simtech: added GPS support for the SIM7000/SIM7600 family.
+ ** cinterion: added support for time updates.
+ ** cinterion: added improved voice call support.
+ ** ublox: added improved voice call support.
+ ** ublox: improved band management with UBANDSEL.
+
+All the features and fixes which were backported to 1.10.x releases are also
+present in ModemManager 1.12.0.
+
+
+ModemManager 1.10.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version requires:
+ ** libmbim >= 1.18.0 (for the optional MBIM support)
+ ** libqmi >= 1.22.0 (for the optional QMI support)
+
+The most important features and changes in this release are the following:
+
+ * udev:
+ ** Consolidated common tag names among all the supported plugins. E.g.
+ ID_MM_PORT_TYPE_GPS, ID_MM_PORT_TYPE_AT_*, ID_MM_PORT_TYPE_QCDM...
+ All these generic tags are included as symbols in the API, and
+ compatibility will be maintained for these. Custom setups of
+ ModemManager relying on previously available per-plugin udev tags may
+ need to manually port them to this new generic subset.
+ ** New tag to allow specifying flow control settings to use in serial ports.
+
+ * Core:
+ ** Avoid probing other protocols on TTYs tagged in udev with specific port
+ type tags (e.g. avoid probing QCDM if a port is tagged as AT). This allows
+ faster port probing and modem detection for known modem layouts.
+ ** Implemented support to enable and handle +CGEV URCs for asynchronous
+ connection state updates in AT-controlled devices.
+
+ * Manager interface:
+ ** New runtime daemon version reporting.
+ ** New support for requesting device inhibition, e.g. so that ModemManager
+ stops completely using a modem device until the inhibition is released.
+ This feature is implemented to allow fwupd taking over of a device
+ completely for as long as it needs during a firmware update.
+
+ * Modem interface:
+ ** All methods are always connected, even in Failed state.
+ ** Allow parallel Enable()/Disable() calls.
+ ** Deprecated redundant ListBearers() method, the read-only Bearers property
+ is already showing the same information.
+
+ * Bearer interface:
+ ** New 'BearerType' property, e.g. to specify whether a bearer is the initial
+ LTE default bearer or not.
+ ** Deprecated 'number' field in bearer settings. Applications do not need to
+ send the 'number' field in Bearer.Connect() or in Modem.Simple.Connect(),
+ as the setting is totally ignored.
+
+ * mmcli:
+ ** New 'key-value' output, easier to parse by scripts than the default.
+ ** Removed redundant '--location-get-XXX' actions, as the '--location-get'
+ already reports the location information for all sources.
+ ** Removed redundant '--simple-status' action, as the same information can be
+ obtained through different mmcli operations.
+ ** New manager '--inhibit-device' action and modem-specific '--inhibit', to
+ allow requesting device inhibition.
+
+ * 3GPP interface:
+ ** New support for exposing the network reported Protocol Configuration
+ Options (PCO), to be used instead of the new deprecated Subscription
+ State property.
+ ** New support for exposing the initial LTE default bearer status.
+ ** New support for configuring the initial LTE default bearer settings.
+
+ * Location interface:
+ ** New LTE Tracking Area Code (TAC) in 3GPP location information.
+ ** New support for injecting assistance data (e.g. Qualcomm XTRA) into the
+ GNSS engine, useful when there is no mobile connection to use MSA A-GPS.
+
+ * Firmware interface:
+ ** Support for reporting firmware update support properties, e.g. specifying
+ which update methods are supported. This information will be consumed by
+ fwupd in order to allow upgrading firmware in devices managed by
+ ModemManager.
+
+ * Voice interface:
+ ** Multiple improvements and fixes in the voice call management logic
+ implemented with generic AT commands.
+ ** Added AudioPort and AudioFormat properties to the Call object.
+ ** Added new generic audio channel setup/cleanup handlers in the Call object.
+
+ * QMI:
+ ** New LOC service based GNSS support, including A-GPS setup via SUPL server.
+ ** New support for the "extended" LTE band list.
+ ** New support for reading IMSI and ICCID with the UIM service.
+
+ * MBIM:
+ ** Implemented support for processing Protocol Configuration Options using
+ Microsoft-defined Basic Connect Extensions.
+ ** Implemented support for LTE attach status and configuration using
+ Microsoft-defined Basic Connect Extensions.
+ ** Implemented support for the extended signal interface and for 3GPP location
+ details using the AT&T specific service.
+ ** Implemented support for 3GPP USSD operations using the standard USSD
+ service.
+ ** For Qualcomm-based MBIM devices, those with QMI-over-MBIM support, a whole
+ new set of features is now available, including: QMI LOC/PDS location
+ support, allowed/preferred mode management, frequency band selection,
+ power management operations...
+
+ * Plugins:
+ ** xmm: new XMM plugin, with shared logic (allowed/preferred mode management,
+ frequency band selection, power management operations, extended signal
+ quality reporting, GPS/A-GPS...) for Intel XMM based devices.
+ ** fibocom: new plugin, with support for generic MBIM and XMM-based devices.
+ ** dell: added support for XMM-based devices, like the DW5820e.
+ ** dell: added custom support for the DW5821e, including 'unmanaged' GPS and
+ firmware update integration details.
+ ** cinterion: new shared interface to include all logic shared between Option
+ and Option/HSO devices.
+ ** sierra-legacy: implemented connection monitoring support.
+ ** u-blox: added support for extended call state transitions.
+ ** u-blox: added CDC-ECM support for SARA/LISA-U2xx modems.
+ ** altair-lte: migrated from SubscriptionState to PCO.
+
+All the features and fixes which were backported to 1.8.x releases are also present
+in ModemManager 1.10.0.
+
+
+ModemManager 1.8.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+The following notes are directed to package maintainers:
+
+ * This version requires:
+ ** GLib 2.36.0
+ ** gettext 0.19.8 (for the optional rebuild of documentation)
+ ** libmbim >= 1.16.0 (for the optional MBIM support)
+ ** libqmi >= 1.20.0 (for the optional QMI support)
+ ** libsystemd >= 209 or libsystemd-login >= 183 (for the optional suspend
+ and resume support)
+ ** libsystemd >= 209 (for the optional systemd journal support)
+ ** polkit >= 0.97 (for the optional PolicyKit support)
+
+ * This version no longer requires:
+ ** intltool (replaced by new features in gettext)
+
+ * Distributions using systemd should explicitly use the following configure
+ options:
+ ** '--with-systemd-suspend-resume': the only supported source for suspend and
+ resume events is now systemd, so the previous '--with-suspend-resume=[]'
+ option was renamed.
+ ** '--with-systemd-journal' to use the new journal support.
+
+ * Distributions wanting to avoid ModemManager poking TTY ports that isn't
+ supposed to touch may start using the new 'STRICT' filter policy, given
+ as an option to the ModemManager daemon (e.g. patching the default systemd
+ service file provided). See below for more info about the filter policy and
+ the side effects of using the STRICT approach:
+ ** E.g. ModemManager --filter-policy=STRICT
+
+The most important features and changes in this release are the following:
+
+ * New 'filter policy' setting in the ModemManager daemon to decide which ports
+ are probed and how. Currently these levels are defined:
+ ** WHITELIST-ONLY: Only devices or ports explicitly whitelisted with the new
+ 'ID_MM_DEVICE_PROCESS' udev tag are probed.
+ ** DEFAULT: All ports are allowed to be probed except for the ones explicitly
+ greylisted as RS232 adapters or completely blacklisted. This is the
+ default approach that was used until now, and the default as well in this
+ release if a different one isn't requested.
+ ** STRICT: The daemon defines a set of heuristics to try to detect modems and
+ ports to probe. Only the TTY ports that are very very likely to be modem
+ ports are probed, therefore completely avoiding the need of having a
+ separate blacklist or RS232 adapter greylist. But note that this policy
+ may end up ignoring some devices, like TTY controlled modems without an
+ associated network port.
+ ** PARANOID: This is equivalent to running the STRICT mode but also applying
+ the blacklist and RS232 greylist filters explicitly.
+
+ * Device 'naming'. This release includes logic to allow 'naming' devices with
+ the ID_MM_PHYSDEV_UID udev tag, so that the names can then be used in e.g.
+ mmcli and also exposed in the 'Device' property in the Modem interface. This
+ new setup makes it possible to give the devices unique names that are kept
+ across reboots.
+
+ * Allow skipping the automatic scan for devices in the daemon with the new
+ '--no-auto-scan' daemon option. Instead, the daemon may be called with the
+ '--initial-kernel-events=[PATH]' option including a predefined list of ports
+ or otherwise get the port additions reported during runtime with the
+ mmcli --report-kernel-event=[] command.
+
+ * Allow building and running without 'udev'. In this setup, the previously
+ explained '--no-auto-scan' is enabled by default, so ports are not
+ automatically detected .Note that this setup is not suggested for standard
+ distributions: if udev is available in the system, it is the preferred method
+ to manage the port addition and removal.
+
+ * SIM hot swap. The core includes the needed logic to support SIM hot swap in
+ the different devices, although for now it's only tested for Telit and MBIM
+ modems. If a SIM hot swap is detected, the modem is flagged as failed and
+ reprobed from scratch.
+
+ * Connection status monitoring logic. In order to try to detect network
+ initiated disconnections, a generic setup is provided to plugins so that they
+ can implement explicit connection status checks that would be executed
+ periodically.
+
+ * New support for 3GPP CSFB states and operation modes. We now support
+ registration states reported as "SMS only" or "CSFB not preferred", and
+ provide APIs to set and get the "UE mode of operation for EPS".
+
+This version comes with the following new features:
+
+ * Build and system:
+ ** Updated the systemd service file with additional security related rules.
+ ** Added support for systemd journal logging.
+ ** Updated most of the code to use GTask instead of the deprecated
+ GSimpleAsyncResult based operations.
+ ** ChangeLog is built from git during the dist tarball generation.
+
+ * New translations: Polish, Brazilian Portuguese, Slovak, Hungarian, Czech,
+ Ukrainian, Swedish and Indonesian.
+
+ * API:
+ ** Defined additional GSM, UMTS and LTE frequency bands.
+ ** The MMModemBand enumeration values (EUTRAN, UTRAN and CDMA) have been
+ renamed to consolidate how they are defined. A compatibility layer has
+ been provided to avoid breaking the API.
+ ** New 'HardwareRevision' property in the Modem interface.
+ ** New 'EpsUeModeOperation' property and 'SetEpsUeModeOperation' method in
+ the Modem 3GPP interface.
+
+ * Core:
+ ** Updated libqcdm to load signal strength from QCDM EVDO Pilot Sets.
+ ** Updated udev rules with new per-vendor rules for quicker processing.
+ ** Explicitly ignored ports are never probed now, but they will be reported
+ as owned by the device and exposed in the Ports property.
+ ** New 'ID_MM_TTY_BAUDRATE' udev tag to specify the baudrate to use in RS232
+ TTY ports.
+ ** If using UCS2, still assume that the operator name may be given in ASCII.
+ ** Explicitly open QCDM ports anytime it's needed, instead of assuming they
+ are open when enabled.
+ ** Query supported ME event reporting options and automatically set the best
+ choice based on the supported ones.
+ ** Query supported flow control modems and automatically set the best choice
+ based on the supported ones.
+ ** Explicitly configure flow control settings on the TTY as soon as it is
+ connected, only applicable for RS232 devices.
+ ** Implemented generic unlock retries loading.
+
+ * MBIM:
+ ** Explicitly reprobe the modem when the mbim-proxy is detected dead.
+ ** Workaround implemented to keep track of the PIN1 unlock retries as the
+ protocol isn't very good allowing this.
+ ** Load and expose HW revision.
+
+ * QMI:
+ ** Explicitly reprobe the modem when the qmi-proxy is detected dead.
+ ** Load and expose HW revision.
+ ** Detect port closed and forbid client allocation operations.
+ ** New optional connection status monitoring support, enabled by default for
+ the Netgear AC341U.
+
+ * Location interface:
+ ** Disabled by default for MBIM modems.
+
+ * Messaging interface:
+ ** Try decoding with UTF-16BE when UCS-2 reported.
+
+ * Plugins:
+ ** u-blox: new plugin, currently supporting the TOBY-L2, TOBY-L4, TOBY-R2,
+ LARA-R2 and LISA-R2.
+ ** quectel: new plugin, supporting generic AT/QMI based modems.
+ ** cinterion: implemented support for devices exposing a WWAN network
+ interface.
+ ** cinterion: support changing modes in LTE capable devices.
+ ** cinterion: added GPS support for devices controlled only with AT^SGPSC.
+ ** cinterion: use ^SIND unsolicited messages for access tech reporting.
+ ** huawei: implemented Signal interface.
+ ** telit: added support for RS232, QMI and MBIM modems.
+ ** novatel: cleaned up registration state and access tech reporting.
+ ** novatel-lte: implemented unlock retries loading.
+ ** dell: speed probing time up and reduce udev dependency.
+ ** mbm: added GPS support for the Dell DW5560.
+
+The following features which were backported to 1.6.x releases are also present
+in ModemManager 1.8.0:
+
+ * Build and System:
+ ** Explicitly use -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36 and
+ -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36 so that we don't get warnings
+ for GLib features that are flagged as deprecated in newer versions.
+ ** Dropped After=syslog.target rule in systemd service file.
+ ** Added all ModemManager udev tags also in "bind" events.
+
+ * Core:
+ ** Improved detection of 4G-only modems.
+ ** TTY is set as connected as soon as ATD replies.
+ ** Removed the default ID_MM_PLATFORM_DRIVER_PROBE whitelist.
+
+ * Signal interface:
+ ** Report RSCP if available.
+ ** New generic implementation of the Signal interface in AT-based devices
+ using AT+CESQ.
+
+ * QMI:
+ ** Run FCC auth sequence if "InvalidTransition" is reported when going
+ online.
+ ** Added WDS reporting event support.
+
+ * Plugins:
+ ** huawei: let the E3372 run NDISDUP via TTY (when the exposed cdc-wdm is
+ non-functional).
+ ** telit: added support for the GE910.
+
+ModemManager 1.6.0
+-------------------------------------------
+This is a new stable release of ModemManager.
+
+ * This version requires:
+ ** GLib 2.36.0
+ ** gettext 0.19.3
+ ** libmbim >= 1.14.0 (for the optional MBIM support)
+ ** libqmi >= 1.16.0 (for the optional QMI support)
+ ** libsystemd >= 209 or libsystemd-login >= 183 (for the optional suspend
+ and resume support)
+
+ * For distributions using systemd, it is suggested that the new optional
+ suspend/resume is explicitly requested during configure with the new
+ '--with-suspend-resume=systemd' argument.
+
+This version comes with the following new features:
+
+ * Core:
+ ** Implemented support for suspend/resume detection, currently working
+ when systemd is in use. Whenever the system is suspended, we'll flag the
+ modems as invalid so that they are re-probed from scratch when the system
+ is resumed.
+ ** Added cancellation support for the probing operations.
+ ** Reworked and simplified the serial port response processing.
+
+ * Location interface:
+ ** Added A-GPS support, currently available only for QMI based modems with
+ PDS service.
+ ** Added support for updating the default GPS refresh time.
+
+ * Time interface:
+ ** New default implementation for all AT-based modems.
+
+ * Voice interface:
+ ** New DBus interface to allow the management of voice calls, which currently
+ assumes that the audio channel is setup out of ModemManager.
+
+ * Bearer:
+ ** New support for reporting statistics of the ongoing connection with a new
+ 'Stats' property, currently available for QMI and MBIM based modems.
+
+ * QMI:
+ ** Implemented support for devices which only work in "raw IP" mode, like
+ the Sierra MC7455.
+ ** Implemented support for SIM related operations using the UIM service, as
+ newer modems with multi-SIM capabilities don't suppor the legacy DMS UIM
+ operations.
+ ** Implemented support for detecting network-initiated disconnections.
+
+ * MBIM:
+ ** If online mode fails, try to use the 'DMS Set FCC Authentication' QMI
+ message via the QMI-over-MBIM support, if supported by the device.
+
+ * udev:
+ ** Added new supported 'ID_MM_PORT_IGNORE' tag to allow fully ignoring ports
+ specified by the user.
+
+ * mmcli:
+ ** Added command completion.
+ ** Added new operations to use the Voice interface.
+ ** Added new operations to manage the A-GPS settings.
+
+ * Build:
+ ** Added code coverage support.
+
+ * Plugins:
+ ** haier: new plugin to support the Haier CE81B.
+ ** thuraya: new plugin for Thuraya satellite modems.
+ ** sierra-legacy,sierra: the implementation for Sierra modems is now split
+ into two different plugins: a 'legacy' one for the old PPP and DirectIP
+ based modems and the standard one for the newer QMI and MBIM based ones.
+ ** dell: new plugin for Dell rebranded devices from Novatel, Sierra or
+ Ericsson.
+ ** gobi: removed the plugin. All non-vendor specific QMI devices should now
+ be managed by the generic plugin.
+ ** mbm: dynamically load the list of supported modes.
+ ** mbm: fixed several connection/disconnection issues.
+ ** simtech: support QMI devices.
+ ** huawei: implemented Voice call management support.
+ ** huawei: use static IP addressing in NDISDUP capable devices if the AT^DHCP
+ response provides the IP details.
+
+The following features which were backported to 1.4.x releases are also present
+in ModemManager 1.6.0:
+
+ * MBIM:
+ ** The mbim-proxy is used by default.
+ ** Implemented support for disconnection status notification while connected.
+ ** Disabled CDMA capabilities, until properly supported.
+
+ * QMI:
+ ** The qmi-proxy is used by default.
+ ** If online mode fails, use 'DMS Set FCC Authentication', required by some
+ rebranded Sierra modems (e.g. Dell branded ones).
+ ** Implemented support for loading SIM operator id and name.
+ ** Implemented power-cycle reset functionality.
+
+ * Plugins:
+ ** telit: added support for new devices, like HE910, UE910 and UL865.
+ ** telit: implemented dynamic port identification.
+ ** telit: implemented unlock retries loading.
+ ** telit: implemented supported/current bands management.
+ ** telit: implemented supported/current modes management.
+ ** telit: implemented modem reset and power down.
+ ** mbm: implemented GPS support for Ericsson HS2350 and H5321gw modems.
+
+
ModemManager 1.4.0
-------------------------------------------
This is a new stable release of ModemManager.
diff --git a/README b/README
index 27d6101e..d4c60d23 100644
--- a/README
+++ b/README
@@ -15,7 +15,7 @@ Implementation.
ModemManager is a DBus system bus activated service (meaning it's started
automatically when a request arrives). It is written in C, using glib and gio.
Several GInterfaces specify different features that the modems support,
-including the generic MMIfaceModem3gpp and MMIfaceModemCdma which provice basic
+including the generic MMIfaceModem3gpp and MMIfaceModemCdma which provide basic
operations for 3GPP (GSM, UMTS, LTE) or CDMA (CDMA1x, EV-DO) modems. If a given
feature is not available in the modem, the specific interface will not be
exported in DBus.
@@ -33,4 +33,12 @@ and need to add or change some public method, feel free to suggest it!
License.
The ModemManager and mmcli binaries are both GPLv2+.
-The libmm-glib library is LGPLv2+. \ No newline at end of file
+The libmm-glib library is LGPLv2+.
+
+Code of Conduct.
+Please note that this project is released with a Contributor Code of Conduct.
+By participating in this project you agree to abide by its terms, which you can
+find in the following link:
+ https://www.freedesktop.org/wiki/CodeOfConduct
+CoC issues may be raised to the project maintainers at the following address:
+ modemmanager-devel-owner@lists.freedesktop.org
diff --git a/RELEASING b/RELEASING
new file mode 100644
index 00000000..cfe3ea22
--- /dev/null
+++ b/RELEASING
@@ -0,0 +1,79 @@
+
+
+The ModemManager releases are generated using the GNU autotools.
+
+0.1) For major releases:
+ * Increment mm_minor_version and reset mm_micro_version.
+ * Assuming API/ABI compatibility in libmm-glib, increment both
+ mm_glib_lt_current and mm_glib_lt_age.
+
+0.2) For stable branch releases:
+ * Increment mm_micro_version.
+
+1) Configure and build the whole project, making sure gtk-doc is enabled:
+
+ $ NOCONFIGURE=1 ./autogen.sh
+ $ ./configure --enable-gtk-doc
+ $ make -j8
+
+2) Run distcheck so that the source distribution tarball is generated, and the
+ project test suite is run on it:
+
+ $ make distcheck
+
+3) Compute checksum of the tarball so that it can be referenced in the release
+ email:
+
+ $ sha256sum ModemManager-${VERSION}.tar.xz
+
+4) Sign release tarball, and verify it (*):
+
+ $ gpg --detach-sign --armor ModemManager-${VERSION}.tar.xz
+ $ gpg --verify ModemManager-${VERSION}.tar.xz.asc ModemManager-${VERSION}.tar.xz
+
+5) Upload source tarball (.tar.xz) and signature (.tar.xz.asc) to
+ freedesktop.org.
+ $ scp ModemManager-${VERSION}.tar.xz* fd.o:${ModemManager}/
+
+6) Create directories for the manpages and gtk-doc documentation in
+ freedesktop.org, and also update the 'latest' links:
+ $ ssh fd.o
+ [fd.o] $ cd ${ModemManager}/man/
+ [fd.o] $ rm latest
+ [fd.o] $ mkdir -p ${VERSION}
+ [fd.o] $ ln -s ${VERSION} latest
+ [fd.o] $ cd ${ModemManager}/doc/
+ [fd.o] $ rm latest
+ [fd.o] $ mkdir -p ${VERSION}/ModemManager
+ [fd.o] $ mkdir -p ${VERSION}/libmm-glib
+ [fd.o] $ ln -s ${VERSION} latest
+
+7) Generate HTML for the manpages:
+ $ roffit < docs/man/mmcli.1 > mmcli.1.html
+ $ roffit < docs/man/ModemManager.8 > ModemManager.8.html
+
+8) Upload manpages in HTML to freedesktop.org:
+ $ scp *.html fd.o:${ModemManager}/man/${VERSION}/
+
+9) Upload the gtk-doc in HTML available inside the source tarball to
+ freedesktop.org. It must be the one inside the tarball because it contains
+ the correct fixed refs to the online documentation of the dependencies
+ (e.g. the glib/gobject/gio documentation URLs in http://developer.gnome.org)
+ $ tar -Jxvf ModemManager-${VERSION}.tar.xz
+ $ scp ModemManager-${VERSION}/docs/reference/api/html/* fd.o:${ModemManager}/doc/${VERSION}/ModemManager/
+ $ scp ModemManager-${VERSION}/docs/reference/libmm-glib/html/* fd.o:${ModemManager}/doc/${VERSION}/libmm-glib/
+
+10.1) For major releases:
+ * Fork new stable branch (e.g. mm-${MAJOR}-${MINOR})
+ * Post-release version bump in the master branch, increment mm_minor_version.
+ * Post-release version bump in the stable branch, increment mm_micro_version.
+
+10.2) For stable branch releases:
+ * Post-release version bump, increment mm_micro_version.
+
+-------------------------------------------------------------------------------
+
+*) Verifying the release signature requires the public key of the person who
+ signed it, e.g.:
+
+ $ curl https://www.freedesktop.org/software/ModemManager/0x3CAD53398973FFFA.asc | gpg --import
diff --git a/TODO b/TODO
index 7851551b..d0925b61 100644
--- a/TODO
+++ b/TODO
@@ -18,7 +18,7 @@ initialization sequence, as we want to use the primary port for that always).
Therefore, looking for ways to mitigate probing time in the specific bad cases
is a good way of minimizing this problem. Some ideas:
- ** If one AT probing suceeds, don't allow timeouts in remaining ports when
+ ** If one AT probing succeeds, don't allow timeouts in remaining ports when
probing for AT.
diff --git a/autogen.sh b/autogen.sh
index 694a22a1..7460d254 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -14,11 +14,9 @@ PKG_NAME=ModemManager
}
(cd $srcdir;
- gtkdocize || exit 1
- autopoint --force
- AUTOPOINT='intltoolize --automake --copy' autoreconf --force --install --verbose
- if test -z "$NOCONFIGURE"; then
- ./configure --enable-maintainer-mode "$@"
- fi
+ GTKDOCIZE="true" autoreconf --force --install --verbose
)
+if test -z "$NOCONFIGURE"; then
+ $srcdir/configure --enable-maintainer-mode "$@"
+fi
diff --git a/build-aux/Makefile.am b/build-aux/Makefile.am
index c27e0f05..acb7afe4 100644
--- a/build-aux/Makefile.am
+++ b/build-aux/Makefile.am
@@ -1,8 +1,8 @@
EXTRA_DIST = \
header-generator.xsl \
- mm-enums-template.h \
- mm-enums-template.c \
- mm-errors-template.h \
- mm-errors-template.c \
- mm-errors-quarks-template.c
+ mm-enums-types.h.template \
+ mm-enums-types.c.template \
+ mm-errors-types.h.template \
+ mm-errors-types.c.template \
+ mm-errors-quarks.c.template
diff --git a/build-aux/header-generator.xsl b/build-aux/header-generator.xsl
index a13c4877..c0ba266a 100644
--- a/build-aux/header-generator.xsl
+++ b/build-aux/header-generator.xsl
@@ -120,6 +120,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2015 - Riccardo Vangelisti riccardo.vangelisti@sadel.it
+ * Copyright (C) 2015 - Marco Bascetta marco.bascetta@sadel.it
*/
#ifndef _MODEM_MANAGER_NAMES_H_
@@ -133,6 +135,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#define MM_DBUS_BEARER_PREFIX MM_DBUS_PATH "/Bearer"
#define MM_DBUS_SIM_PREFIX MM_DBUS_PATH "/SIM"
#define MM_DBUS_SMS_PREFIX MM_DBUS_PATH "/SMS"
+#define MM_DBUS_CALL_PREFIX MM_DBUS_PATH "/Call"
/* Prefix for DBus errors */
#define MM_DBUS_ERROR_PREFIX "org.freedesktop.ModemManager1.Error"
diff --git a/build-aux/mm-daemon-enums-types.c.template b/build-aux/mm-daemon-enums-types.c.template
new file mode 120000
index 00000000..471d2931
--- /dev/null
+++ b/build-aux/mm-daemon-enums-types.c.template
@@ -0,0 +1 @@
+mm-enums-types.c.template \ No newline at end of file
diff --git a/build-aux/mm-daemon-enums-types.h.template b/build-aux/mm-daemon-enums-types.h.template
new file mode 120000
index 00000000..12f144f5
--- /dev/null
+++ b/build-aux/mm-daemon-enums-types.h.template
@@ -0,0 +1 @@
+mm-enums-types.h.template \ No newline at end of file
diff --git a/build-aux/mm-enums-template.c b/build-aux/mm-enums-types.c.template
index 15f3a813..2a7f264b 100644
--- a/build-aux/mm-enums-template.c
+++ b/build-aux/mm-enums-types.c.template
@@ -24,16 +24,16 @@ static const G@Type@Value @enum_name@_values[] = {
GType
@enum_name@_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile)) {
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
GType g_define_type_id =
g_@type@_register_static (g_intern_static_string ("@EnumName@"),
@enum_name@_values);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
}
/**
@@ -51,7 +51,7 @@ const gchar *
guint i;
for (i = 0; @enum_name@_values[i].value_nick; i++) {
- if (val == @enum_name@_values[i].value)
+ if ((gint)val == @enum_name@_values[i].value)
return @enum_name@_values[i].value_nick;
}
diff --git a/build-aux/mm-enums-template.h b/build-aux/mm-enums-types.h.template
index 652c53dc..652c53dc 100644
--- a/build-aux/mm-enums-template.h
+++ b/build-aux/mm-enums-types.h.template
diff --git a/build-aux/mm-errors-quarks-template.c b/build-aux/mm-errors-quarks.c.template
index 4386a601..4386a601 100644
--- a/build-aux/mm-errors-quarks-template.c
+++ b/build-aux/mm-errors-quarks.c.template
diff --git a/build-aux/mm-errors-template.c b/build-aux/mm-errors-types.c.template
index 3b3127f5..8370adf1 100644
--- a/build-aux/mm-errors-template.c
+++ b/build-aux/mm-errors-types.c.template
@@ -12,9 +12,9 @@
GType
@enum_name@_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile))
+ if (g_once_init_enter (&g_define_type_id_initialized))
{
static const G@Type@Value values[] = {
/*** END value-header ***/
@@ -28,10 +28,10 @@ GType
};
GType g_define_type_id =
g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
}
/*** END value-tail ***/
diff --git a/build-aux/mm-errors-template.h b/build-aux/mm-errors-types.h.template
index 72c90cf5..72c90cf5 100644
--- a/build-aux/mm-errors-template.h
+++ b/build-aux/mm-errors-types.h.template
diff --git a/build-aux/mm-helper-enums-types.c.template b/build-aux/mm-helper-enums-types.c.template
new file mode 120000
index 00000000..471d2931
--- /dev/null
+++ b/build-aux/mm-helper-enums-types.c.template
@@ -0,0 +1 @@
+mm-enums-types.c.template \ No newline at end of file
diff --git a/build-aux/mm-helper-enums-types.h.template b/build-aux/mm-helper-enums-types.h.template
new file mode 120000
index 00000000..12f144f5
--- /dev/null
+++ b/build-aux/mm-helper-enums-types.h.template
@@ -0,0 +1 @@
+mm-enums-types.h.template \ No newline at end of file
diff --git a/build-aux/mm-huawei-enums-types.c.template b/build-aux/mm-huawei-enums-types.c.template
new file mode 120000
index 00000000..471d2931
--- /dev/null
+++ b/build-aux/mm-huawei-enums-types.c.template
@@ -0,0 +1 @@
+mm-enums-types.c.template \ No newline at end of file
diff --git a/build-aux/mm-huawei-enums-types.h.template b/build-aux/mm-huawei-enums-types.h.template
new file mode 120000
index 00000000..12f144f5
--- /dev/null
+++ b/build-aux/mm-huawei-enums-types.h.template
@@ -0,0 +1 @@
+mm-enums-types.h.template \ No newline at end of file
diff --git a/build-aux/mm-port-enums-types.c.template b/build-aux/mm-port-enums-types.c.template
new file mode 120000
index 00000000..471d2931
--- /dev/null
+++ b/build-aux/mm-port-enums-types.c.template
@@ -0,0 +1 @@
+mm-enums-types.c.template \ No newline at end of file
diff --git a/build-aux/mm-port-enums-types.h.template b/build-aux/mm-port-enums-types.h.template
new file mode 120000
index 00000000..12f144f5
--- /dev/null
+++ b/build-aux/mm-port-enums-types.h.template
@@ -0,0 +1 @@
+mm-enums-types.h.template \ No newline at end of file
diff --git a/build-aux/mm-telit-enums-types.c.template b/build-aux/mm-telit-enums-types.c.template
new file mode 120000
index 00000000..471d2931
--- /dev/null
+++ b/build-aux/mm-telit-enums-types.c.template
@@ -0,0 +1 @@
+mm-enums-types.c.template \ No newline at end of file
diff --git a/build-aux/mm-telit-enums-types.h.template b/build-aux/mm-telit-enums-types.h.template
new file mode 120000
index 00000000..12f144f5
--- /dev/null
+++ b/build-aux/mm-telit-enums-types.h.template
@@ -0,0 +1 @@
+mm-enums-types.h.template \ No newline at end of file
diff --git a/build-aux/mm-ublox-enums-types.c.template b/build-aux/mm-ublox-enums-types.c.template
new file mode 120000
index 00000000..471d2931
--- /dev/null
+++ b/build-aux/mm-ublox-enums-types.c.template
@@ -0,0 +1 @@
+mm-enums-types.c.template \ No newline at end of file
diff --git a/build-aux/mm-ublox-enums-types.h.template b/build-aux/mm-ublox-enums-types.h.template
new file mode 120000
index 00000000..12f144f5
--- /dev/null
+++ b/build-aux/mm-ublox-enums-types.h.template
@@ -0,0 +1 @@
+mm-enums-types.h.template \ No newline at end of file
diff --git a/cli/Makefile.am b/cli/Makefile.am
index 1bb2a8d6..820d5d3a 100644
--- a/cli/Makefile.am
+++ b/cli/Makefile.am
@@ -1,34 +1,63 @@
bin_PROGRAMS = mmcli
mmcli_CPPFLAGS = \
+ $(WARN_CFLAGS) \
$(MMCLI_CFLAGS) \
-I$(top_srcdir) \
-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
+ -I${top_builddir}/libmm-glib/generated \
+ $(NULL)
mmcli_SOURCES = \
mmcli.h \
mmcli.c \
- mmcli-common.h \
- mmcli-common.c \
+ mmcli-common.h mmcli-common.c \
+ mmcli-output.h mmcli-output.c \
mmcli-manager.c \
mmcli-modem.c \
mmcli-modem-3gpp.c \
+ mmcli-modem-3gpp-profile-manager.c \
+ mmcli-modem-3gpp-ussd.c \
mmcli-modem-cdma.c \
mmcli-modem-simple.c \
mmcli-modem-location.c \
mmcli-modem-messaging.c \
+ mmcli-modem-voice.c \
mmcli-modem-time.c \
mmcli-modem-firmware.c \
+ mmcli-modem-sar.c \
mmcli-modem-signal.c \
mmcli-modem-oma.c \
mmcli-bearer.c \
mmcli-sim.c \
- mmcli-sms.c
+ mmcli-sms.c \
+ mmcli-call.c \
+ $(NULL)
mmcli_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+mmcli_LDFLAGS = \
+ $(WARN_LDFLAGS) \
$(MMCLI_LIBS) \
- $(top_builddir)/libmm-glib/libmm-glib.la
+ $(NULL)
+
+if WITH_UDEV
+mmcli_CPPFLAGS += $(GUDEV_CFLAGS)
+mmcli_LDFLAGS += $(GUDEV_LIBS)
+endif
+
+completiondir = $(datadir)/bash-completion/completions
+
+install-data-hook:
+ $(mkinstalldirs) $(DESTDIR)$(completiondir)
+ $(INSTALL_DATA) $(srcdir)/mmcli-completion $(DESTDIR)$(completiondir)/mmcli
+
+uninstall-hook:
+ rm -f $(DESTDIR)$(completiondir)/mmcli
+
+EXTRA_DIST = mmcli-completion
diff --git a/cli/meson.build b/cli/meson.build
new file mode 100644
index 00000000..24d3f8ee
--- /dev/null
+++ b/cli/meson.build
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+sources = files(
+ 'mmcli-bearer.c',
+ 'mmcli.c',
+ 'mmcli-call.c',
+ 'mmcli-common.c',
+ 'mmcli-manager.c',
+ 'mmcli-modem-3gpp.c',
+ 'mmcli-modem-3gpp-profile-manager.c',
+ 'mmcli-modem-3gpp-ussd.c',
+ 'mmcli-modem.c',
+ 'mmcli-modem-cdma.c',
+ 'mmcli-modem-firmware.c',
+ 'mmcli-modem-location.c',
+ 'mmcli-modem-messaging.c',
+ 'mmcli-modem-oma.c',
+ 'mmcli-modem-sar.c',
+ 'mmcli-modem-signal.c',
+ 'mmcli-modem-simple.c',
+ 'mmcli-modem-time.c',
+ 'mmcli-modem-voice.c',
+ 'mmcli-output.c',
+ 'mmcli-sim.c',
+ 'mmcli-sms.c',
+)
+
+deps = [
+ gudev_dep,
+ libmm_glib_dep,
+]
+
+executable(
+ 'mmcli',
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ install: true,
+)
+
+if enable_bash_completion
+ install_data(
+ 'mmcli-completion',
+ install_dir: bash_completion_completionsdir,
+ rename: 'mmcli',
+ )
+endif
diff --git a/cli/mmcli-bearer.c b/cli/mmcli-bearer.c
index 5829e60a..db06dbfa 100644
--- a/cli/mmcli-bearer.c
+++ b/cli/mmcli-bearer.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2011-2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -67,7 +68,7 @@ mmcli_bearer_get_option_group (void)
/* Status options */
group = g_option_group_new ("bearer",
- "Bearer options",
+ "Bearer options:",
"Show bearer options",
NULL,
NULL);
@@ -107,7 +108,7 @@ mmcli_bearer_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -126,136 +127,213 @@ context_free (Context *ctx)
void
mmcli_bearer_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
print_bearer_info (MMBearer *bearer)
{
- MMBearerIpConfig *ipv4_config;
- MMBearerIpConfig *ipv6_config;
- MMBearerProperties *properties;
-
- ipv4_config = mm_bearer_get_ipv4_config (bearer);
- ipv6_config = mm_bearer_get_ipv6_config (bearer);
- properties = mm_bearer_get_properties (bearer);
-
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE_UNKNOWN
-#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
-#undef VALIDATE_NONE
-#define VALIDATE_NONE(str) (str ? str : "none")
-
- g_print ("Bearer '%s'\n",
- mm_bearer_get_path (bearer));
- g_print (" -------------------------\n"
- " Status | connected: '%s'\n"
- " | suspended: '%s'\n"
- " | interface: '%s'\n"
- " | IP timeout: '%u'\n",
- mm_bearer_get_connected (bearer) ? "yes" : "no",
- mm_bearer_get_suspended (bearer) ? "yes" : "no",
- VALIDATE_UNKNOWN (mm_bearer_get_interface (bearer)),
- mm_bearer_get_ip_timeout (bearer));
-
- if (properties) {
- gchar *ip_family_str;
-
- ip_family_str = (mm_bearer_ip_family_build_string_from_mask (
- mm_bearer_properties_get_ip_type (properties)));
- g_print (" -------------------------\n"
- " Properties | apn: '%s'\n"
- " | roaming: '%s'\n"
- " | IP type: '%s'\n"
- " | user: '%s'\n"
- " | password: '%s'\n"
- " | number: '%s'\n"
- " | Rm protocol: '%s'\n",
- VALIDATE_NONE (mm_bearer_properties_get_apn (properties)),
- mm_bearer_properties_get_allow_roaming (properties) ? "allowed" : "forbidden",
- VALIDATE_UNKNOWN (ip_family_str),
- VALIDATE_NONE (mm_bearer_properties_get_user (properties)),
- VALIDATE_NONE (mm_bearer_properties_get_password (properties)),
- VALIDATE_NONE (mm_bearer_properties_get_number (properties)),
- VALIDATE_UNKNOWN (mm_modem_cdma_rm_protocol_get_string (
- mm_bearer_properties_get_rm_protocol (properties))));
- g_free (ip_family_str);
+ g_autoptr(MMBearerIpConfig) ipv4_config = NULL;
+ g_autoptr(MMBearerIpConfig) ipv6_config = NULL;
+ g_autoptr(MMBearerProperties) properties = NULL;
+ g_autoptr(MMBearerStats) stats = NULL;
+ g_autoptr(GError) connection_error = NULL;
+ gint profile_id;
+ gchar *profile_id_str;
+
+ ipv4_config = mm_bearer_get_ipv4_config (bearer);
+ ipv6_config = mm_bearer_get_ipv6_config (bearer);
+ properties = mm_bearer_get_properties (bearer);
+ stats = mm_bearer_get_stats (bearer);
+ profile_id = mm_bearer_get_profile_id (bearer);
+ connection_error = mm_bearer_get_connection_error (bearer);
+
+ profile_id_str = (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) ? g_strdup_printf ("%d", profile_id) : NULL;
+
+ mmcli_output_string (MMC_F_BEARER_GENERAL_DBUS_PATH, mm_bearer_get_path (bearer));
+ mmcli_output_string (MMC_F_BEARER_GENERAL_TYPE, mm_bearer_type_get_string (mm_bearer_get_bearer_type (bearer)));
+
+ mmcli_output_string (MMC_F_BEARER_STATUS_CONNECTED, mm_bearer_get_connected (bearer) ? "yes" : "no");
+ mmcli_output_string_take (MMC_F_BEARER_STATUS_CONNECTION_ERROR_NAME, connection_error ? g_dbus_error_encode_gerror (connection_error) : NULL);
+ mmcli_output_string (MMC_F_BEARER_STATUS_CONNECTION_ERROR_MESSAGE, connection_error ? connection_error->message : NULL);
+ mmcli_output_string (MMC_F_BEARER_STATUS_SUSPENDED, mm_bearer_get_suspended (bearer) ? "yes" : "no");
+ mmcli_output_string (MMC_F_BEARER_STATUS_MULTIPLEXED, mm_bearer_get_multiplexed (bearer) ? "yes" : "no");
+ mmcli_output_string (MMC_F_BEARER_STATUS_INTERFACE, mm_bearer_get_interface (bearer));
+ mmcli_output_string_take (MMC_F_BEARER_STATUS_IP_TIMEOUT, g_strdup_printf ("%u", mm_bearer_get_ip_timeout (bearer)));
+ mmcli_output_string_take (MMC_F_BEARER_STATUS_PROFILE_ID, profile_id_str);
+
+ /* Properties */
+ {
+ const gchar *apn = NULL;
+ gchar *apn_type_str = NULL;
+ const gchar *roaming = NULL;
+ gchar *ip_family_str = NULL;
+ const gchar *user = NULL;
+ const gchar *password = NULL;
+ const gchar *rm_protocol = NULL;
+ gchar *allowed_auth_str = NULL;
+ gchar *properties_profile_id_str = NULL;
+
+ if (properties) {
+ gint properties_profile_id;
+
+ properties_profile_id = mm_bearer_properties_get_profile_id (properties);
+ if (properties_profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
+ properties_profile_id_str = g_strdup_printf ("%d", properties_profile_id);
+
+ apn = mm_bearer_properties_get_apn (properties);
+ apn_type_str = mm_bearer_apn_type_build_string_from_mask (mm_bearer_properties_get_apn_type (properties));
+ ip_family_str = mm_bearer_ip_family_build_string_from_mask (mm_bearer_properties_get_ip_type (properties));
+ allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (mm_bearer_properties_get_allowed_auth (properties));
+ user = mm_bearer_properties_get_user (properties);
+ password = mm_bearer_properties_get_password (properties);
+ if (mm_bearer_get_bearer_type (bearer) != MM_BEARER_TYPE_DEFAULT_ATTACH) {
+ roaming = mm_bearer_properties_get_allow_roaming (properties) ? "allowed" : "forbidden";
+ rm_protocol = mm_modem_cdma_rm_protocol_get_string (mm_bearer_properties_get_rm_protocol (properties));
+ }
+ }
+
+ mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_PROFILE_ID, properties_profile_id_str);
+ mmcli_output_string (MMC_F_BEARER_PROPERTIES_APN, apn);
+ mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_APN_TYPE, apn_type_str);
+ mmcli_output_string (MMC_F_BEARER_PROPERTIES_ROAMING, roaming);
+ mmcli_output_string_take (MMC_F_BEARER_PROPERTIES_IP_TYPE, ip_family_str);
+ mmcli_output_string (MMC_F_BEARER_PROPERTIES_USER, user);
+ mmcli_output_string (MMC_F_BEARER_PROPERTIES_PASSWORD, password);
+ mmcli_output_string (MMC_F_BEARER_PROPERTIES_RM_PROTOCOL, rm_protocol);
+ mmcli_output_string_list_take (MMC_F_BEARER_PROPERTIES_ALLOWED_AUTH, allowed_auth_str);
+ }
+
+ /* IPv4 config */
+ {
+ const gchar *method = NULL;
+ const gchar *address = NULL;
+ gchar *prefix = NULL;
+ const gchar *gateway = NULL;
+ const gchar **dns = NULL;
+ gchar *mtu = NULL;
+
+ if (ipv4_config) {
+ method = mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv4_config));
+ if (mm_bearer_ip_config_get_method (ipv4_config) != MM_BEARER_IP_METHOD_UNKNOWN) {
+ guint mtu_n;
+
+ address = mm_bearer_ip_config_get_address (ipv4_config);
+ prefix = g_strdup_printf ("%u", mm_bearer_ip_config_get_prefix (ipv4_config));
+ gateway = mm_bearer_ip_config_get_gateway (ipv4_config);
+ dns = mm_bearer_ip_config_get_dns (ipv4_config);
+ mtu_n = mm_bearer_ip_config_get_mtu (ipv4_config);
+ if (mtu_n)
+ mtu = g_strdup_printf ("%u", mtu_n);
+ }
+ }
+
+ mmcli_output_string (MMC_F_BEARER_IPV4_CONFIG_METHOD, method);
+ mmcli_output_string (MMC_F_BEARER_IPV4_CONFIG_ADDRESS, address);
+ mmcli_output_string_take (MMC_F_BEARER_IPV4_CONFIG_PREFIX, prefix);
+ mmcli_output_string (MMC_F_BEARER_IPV4_CONFIG_GATEWAY, gateway);
+ mmcli_output_string_array (MMC_F_BEARER_IPV4_CONFIG_DNS, dns, FALSE);
+ mmcli_output_string_take (MMC_F_BEARER_IPV4_CONFIG_MTU, mtu);
}
- /* IPv4 */
- g_print (" -------------------------\n"
- " IPv4 configuration | method: '%s'\n",
- (ipv4_config ?
- mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv4_config)) :
- "none"));
- if (ipv4_config &&
- mm_bearer_ip_config_get_method (ipv4_config) != MM_BEARER_IP_METHOD_UNKNOWN) {
- const gchar **dns = mm_bearer_ip_config_get_dns (ipv4_config);
- guint i, mtu;
-
- g_print (" | address: '%s'\n"
- " | prefix: '%u'\n"
- " | gateway: '%s'\n",
- VALIDATE_UNKNOWN (mm_bearer_ip_config_get_address (ipv4_config)),
- mm_bearer_ip_config_get_prefix (ipv4_config),
- VALIDATE_UNKNOWN (mm_bearer_ip_config_get_gateway (ipv4_config)));
-
- if (dns && dns[0]) {
- g_print (
- " | DNS: '%s'", dns[0]);
- /* Additional DNS addresses */
- for (i = 1; dns[i]; i++)
- g_print (", '%s'", dns[i]);
- } else {
- g_print (
- " | DNS: none");
+ /* IPv6 config */
+ {
+ const gchar *method = NULL;
+ const gchar *address = NULL;
+ gchar *prefix = NULL;
+ const gchar *gateway = NULL;
+ const gchar **dns = NULL;
+ gchar *mtu = NULL;
+
+ if (ipv6_config) {
+ method = mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv6_config));
+ if (mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_UNKNOWN) {
+ guint mtu_n;
+
+ address = mm_bearer_ip_config_get_address (ipv6_config);
+ prefix = g_strdup_printf ("%u", mm_bearer_ip_config_get_prefix (ipv6_config));
+ gateway = mm_bearer_ip_config_get_gateway (ipv6_config);
+ dns = mm_bearer_ip_config_get_dns (ipv6_config);
+ mtu_n = mm_bearer_ip_config_get_mtu (ipv6_config);
+ if (mtu_n)
+ mtu = g_strdup_printf ("%u", mtu_n);
+ }
}
- g_print ("\n");
- mtu = mm_bearer_ip_config_get_mtu (ipv4_config);
- if (mtu)
- g_print (" | MTU: '%u'\n", mtu);
+ mmcli_output_string (MMC_F_BEARER_IPV6_CONFIG_METHOD, method);
+ mmcli_output_string (MMC_F_BEARER_IPV6_CONFIG_ADDRESS, address);
+ mmcli_output_string_take (MMC_F_BEARER_IPV6_CONFIG_PREFIX, prefix);
+ mmcli_output_string (MMC_F_BEARER_IPV6_CONFIG_GATEWAY, gateway);
+ mmcli_output_string_array (MMC_F_BEARER_IPV6_CONFIG_DNS, dns, FALSE);
+ mmcli_output_string_take (MMC_F_BEARER_IPV6_CONFIG_MTU, mtu);
}
- /* IPv6 */
- g_print (" -------------------------\n"
- " IPv6 configuration | method: '%s'\n",
- (ipv6_config ?
- mm_bearer_ip_method_get_string (mm_bearer_ip_config_get_method (ipv6_config)) :
- "none"));
- if (ipv6_config &&
- mm_bearer_ip_config_get_method (ipv6_config) != MM_BEARER_IP_METHOD_UNKNOWN) {
- const gchar **dns = mm_bearer_ip_config_get_dns (ipv6_config);
- guint i, mtu;
-
- g_print (" | address: '%s'\n"
- " | prefix: '%u'\n"
- " | gateway: '%s'\n",
- VALIDATE_UNKNOWN(mm_bearer_ip_config_get_address (ipv6_config)),
- mm_bearer_ip_config_get_prefix (ipv6_config),
- VALIDATE_UNKNOWN(mm_bearer_ip_config_get_gateway (ipv6_config)));
-
- if (dns && dns[0]) {
- g_print (
- " | DNS: '%s'", dns[0]);
- /* Additional DNS addresses */
- for (i = 1; dns[i]; i++)
- g_print (", '%s'", dns[i]);
- } else {
- g_print (
- " | DNS: none");
+ /* Stats */
+ {
+ guint64 start_date = 0;
+ gchar *duration = NULL;
+ gchar *bytes_rx = NULL;
+ gchar *bytes_tx = NULL;
+ gchar *attempts = NULL;
+ gchar *failed_attempts = NULL;
+ gchar *total_duration = NULL;
+ gchar *total_bytes_rx = NULL;
+ gchar *total_bytes_tx = NULL;
+ gchar *uplink_speed = NULL;
+ gchar *downlink_speed = NULL;
+
+ if (stats) {
+ guint64 val;
+
+ start_date = mm_bearer_stats_get_start_date (stats);
+
+ val = mm_bearer_stats_get_duration (stats);
+ if (val)
+ duration = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_rx_bytes (stats);
+ if (val)
+ bytes_rx = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_tx_bytes (stats);
+ if (val)
+ bytes_tx = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_attempts (stats);
+ if (val)
+ attempts = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_failed_attempts (stats);
+ if (val)
+ failed_attempts = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_total_duration (stats);
+ if (val)
+ total_duration = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_total_rx_bytes (stats);
+ if (val)
+ total_bytes_rx = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_total_tx_bytes (stats);
+ if (val)
+ total_bytes_tx = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_uplink_speed (stats);
+ if (val)
+ uplink_speed = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
+ val = mm_bearer_stats_get_downlink_speed (stats);
+ if (val)
+ downlink_speed = g_strdup_printf ("%" G_GUINT64_FORMAT, val);
}
- g_print ("\n");
- mtu = mm_bearer_ip_config_get_mtu (ipv6_config);
- if (mtu)
- g_print (" | MTU: '%u'\n", mtu);
+ if (start_date)
+ mmcli_output_start_date (start_date);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_DURATION, duration);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_BYTES_RX, bytes_rx);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_BYTES_TX, bytes_tx);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_ATTEMPTS, attempts);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_FAILED_ATTEMPTS, failed_attempts);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_TOTAL_DURATION, total_duration);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_TOTAL_BYTES_RX, total_bytes_rx);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_TOTAL_BYTES_TX, total_bytes_tx);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_UPLINK_SPEED, uplink_speed);
+ mmcli_output_string_take (MMC_F_BEARER_STATS_DOWNLINK_SPEED, downlink_speed);
}
- g_clear_object (&properties);
- g_clear_object (&ipv4_config);
- g_clear_object (&ipv6_config);
+ mmcli_output_dump ();
}
static void
diff --git a/cli/mmcli-call.c b/cli/mmcli-call.c
new file mode 100644
index 00000000..ecd1cb77
--- /dev/null
+++ b/cli/mmcli-call.c
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control call status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ * Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mmcli.h"
+#include "mmcli-common.h"
+#include "mmcli-output.h"
+
+/* Context */
+typedef struct {
+ MMManager *manager;
+ MMObject *object;
+ GCancellable *cancellable;
+ MMCall *call;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean info_flag; /* set when no action found */
+static gboolean start_flag;
+static gboolean accept_flag;
+static gchar *deflect_str;
+static gboolean join_multiparty_flag;
+static gboolean leave_multiparty_flag;
+static gboolean hangup_flag;
+static gchar *dtmf_request;
+
+static GOptionEntry entries[] = {
+ { "start", 0, 0, G_OPTION_ARG_NONE, &start_flag,
+ "Start the call.",
+ NULL,
+ },
+ { "accept", 0, 0, G_OPTION_ARG_NONE, &accept_flag,
+ "Accept the incoming call",
+ NULL,
+ },
+ { "deflect", 0, 0, G_OPTION_ARG_STRING, &deflect_str,
+ "Deflect the incoming call",
+ "[NUMBER]",
+ },
+ { "join-multiparty", 0, 0, G_OPTION_ARG_NONE, &join_multiparty_flag,
+ "Join multiparty call",
+ NULL,
+ },
+ { "leave-multiparty", 0, 0, G_OPTION_ARG_NONE, &leave_multiparty_flag,
+ "Leave multiparty call",
+ NULL,
+ },
+ { "hangup", 0, 0, G_OPTION_ARG_NONE, &hangup_flag,
+ "Hang up the call",
+ NULL,
+ },
+ { "send-dtmf", 0, 0, G_OPTION_ARG_STRING, &dtmf_request,
+ "Send specified DTMF tone",
+ "[0-9A-D*#]"
+ },
+ { NULL }
+};
+
+GOptionGroup *
+mmcli_call_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ /* Status options */
+ group = g_option_group_new ("call",
+ "Call options:",
+ "Show call options",
+ NULL,
+ NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+gboolean
+mmcli_call_options_enabled (void)
+{
+ static guint n_actions = 0;
+ static gboolean checked = FALSE;
+
+ if (checked)
+ return !!n_actions;
+
+ n_actions = (start_flag +
+ accept_flag +
+ !!deflect_str +
+ join_multiparty_flag +
+ leave_multiparty_flag +
+ hangup_flag +
+ !!dtmf_request);
+
+ if (n_actions == 0 && mmcli_get_common_call_string ()) {
+ /* default to info */
+ info_flag = TRUE;
+ n_actions++;
+ }
+
+ if (n_actions > 1) {
+ g_printerr ("error: too many call actions requested\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (info_flag)
+ mmcli_force_sync_operation ();
+
+ checked = TRUE;
+ return !!n_actions;
+}
+
+static void
+context_free (void)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ if (ctx->call)
+ g_object_unref (ctx->call);
+ if (ctx->object)
+ g_object_unref (ctx->object);
+ if (ctx->manager)
+ g_object_unref (ctx->manager);
+ g_free (ctx);
+}
+
+void
+mmcli_call_shutdown (void)
+{
+ context_free ();
+}
+
+static void
+print_call_info (MMCall *call)
+{
+ MMCallAudioFormat *audio_format;
+ const gchar *encoding = NULL;
+ const gchar *resolution = NULL;
+ gchar *rate = NULL;
+
+ audio_format = mm_call_peek_audio_format (call);
+
+ mmcli_output_string (MMC_F_CALL_GENERAL_DBUS_PATH, mm_call_get_path (call));
+ mmcli_output_string (MMC_F_CALL_PROPERTIES_NUMBER, mm_call_get_number (call));
+ mmcli_output_string (MMC_F_CALL_PROPERTIES_DIRECTION, mm_call_direction_get_string (mm_call_get_direction (call)));
+ mmcli_output_string (MMC_F_CALL_PROPERTIES_MULTIPARTY, mm_call_get_multiparty (call) ? "yes" : "no");
+ mmcli_output_string (MMC_F_CALL_PROPERTIES_STATE, mm_call_state_get_string (mm_call_get_state (call)));
+ mmcli_output_string (MMC_F_CALL_PROPERTIES_STATE_REASON, mm_call_state_reason_get_string (mm_call_get_state_reason (call)));
+ mmcli_output_string (MMC_F_CALL_PROPERTIES_AUDIO_PORT, mm_call_get_audio_port (call));
+
+ if (audio_format) {
+ rate = g_strdup_printf ("%u", mm_call_audio_format_get_rate (audio_format));
+ encoding = mm_call_audio_format_get_encoding (audio_format);
+ resolution = mm_call_audio_format_get_resolution (audio_format);
+ }
+
+ mmcli_output_string (MMC_F_CALL_AUDIO_FORMAT_ENCODING, encoding);
+ mmcli_output_string (MMC_F_CALL_AUDIO_FORMAT_RESOLUTION, resolution);
+ mmcli_output_string_take (MMC_F_CALL_AUDIO_FORMAT_RATE, rate);
+
+ mmcli_output_dump ();
+}
+
+static void
+start_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't start the call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully started the call\n");
+}
+
+static void
+start_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_start_finish (call, result, &error);
+ start_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+accept_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't accept the call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully accepted the call\n");
+}
+
+static void
+accept_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_accept_finish (call, result, &error);
+ accept_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+deflect_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't deflect the call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully deflected the call\n");
+}
+
+static void
+deflect_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_deflect_finish (call, result, &error);
+ deflect_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+join_multiparty_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't join multiparty call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully joined multiparty call\n");
+}
+
+static void
+join_multiparty_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_join_multiparty_finish (call, result, &error);
+ join_multiparty_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+leave_multiparty_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't leave multiparty call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully left multiparty call\n");
+}
+
+static void
+leave_multiparty_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_leave_multiparty_finish (call, result, &error);
+ leave_multiparty_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+hangup_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't hang up the call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully hung up the call\n");
+}
+
+static void
+hangup_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_hangup_finish (call, result, &error);
+ hangup_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+send_dtmf_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't send dtmf to call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully send dtmf\n");
+}
+
+static void
+send_dtmf_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_send_dtmf_finish (call, result, &error);
+ send_dtmf_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+get_call_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer none)
+{
+ ctx->call = mmcli_get_call_finish (result,
+ &ctx->manager,
+ &ctx->object);
+ /* Setup operation timeout */
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->call));
+
+ if (info_flag)
+ g_assert_not_reached ();
+
+ /* Requesting to start the call? */
+ if (start_flag) {
+ mm_call_start (ctx->call,
+ ctx->cancellable,
+ (GAsyncReadyCallback)start_ready,
+ NULL);
+ return;
+ }
+
+ /* Requesting to accept the call? */
+ if (accept_flag) {
+ mm_call_accept (ctx->call,
+ ctx->cancellable,
+ (GAsyncReadyCallback)accept_ready,
+ NULL);
+ return;
+ }
+
+ /* Requesting to deflect the call? */
+ if (deflect_str) {
+ mm_call_deflect (ctx->call,
+ deflect_str,
+ ctx->cancellable,
+ (GAsyncReadyCallback)deflect_ready,
+ NULL);
+ return;
+ }
+
+ /* Requesting to join multiparty call? */
+ if (join_multiparty_flag) {
+ mm_call_join_multiparty (ctx->call,
+ ctx->cancellable,
+ (GAsyncReadyCallback)join_multiparty_ready,
+ NULL);
+ return;
+ }
+
+ /* Requesting to leave multiparty call? */
+ if (leave_multiparty_flag) {
+ mm_call_leave_multiparty (ctx->call,
+ ctx->cancellable,
+ (GAsyncReadyCallback)leave_multiparty_ready,
+ NULL);
+ return;
+ }
+
+ /* Requesting to hangup the call? */
+ if (hangup_flag) {
+ mm_call_hangup (ctx->call,
+ ctx->cancellable,
+ (GAsyncReadyCallback)hangup_ready,
+ NULL);
+ return;
+ }
+
+ /* Requesting to send dtmf the call? */
+ if (dtmf_request) {
+ mm_call_send_dtmf (ctx->call,
+ dtmf_request,
+ ctx->cancellable,
+ (GAsyncReadyCallback)send_dtmf_ready,
+ NULL);
+ return;
+ }
+
+
+
+ g_warn_if_reached ();
+}
+
+void
+mmcli_call_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable)
+{
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ if (cancellable)
+ ctx->cancellable = g_object_ref (cancellable);
+
+ /* Get proper call */
+ mmcli_get_call (connection,
+ mmcli_get_common_call_string (),
+ cancellable,
+ (GAsyncReadyCallback)get_call_ready,
+ NULL);
+}
+
+void
+mmcli_call_run_synchronous (GDBusConnection *connection)
+{
+ GError *error = NULL;
+
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ ctx->call = mmcli_get_call_sync (connection,
+ mmcli_get_common_call_string (),
+ &ctx->manager,
+ &ctx->object);
+
+ /* Setup operation timeout: 2 minutes */
+ g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (ctx->call), 2 * 60 * 1000);
+
+ /* Request to get info from call? */
+ if (info_flag) {
+ g_debug ("Printing call info...");
+ print_call_info (ctx->call);
+ return;
+ }
+
+ /* Requesting to start the call? */
+ if (start_flag) {
+ gboolean operation_result;
+
+ operation_result = mm_call_start_sync (ctx->call,
+ NULL,
+ &error);
+ start_process_reply (operation_result, error);
+ return;
+ }
+
+ /* Requesting to accept the call? */
+ if (accept_flag) {
+ gboolean operation_result;
+
+ operation_result = mm_call_accept_sync (ctx->call,
+ NULL,
+ &error);
+ accept_process_reply (operation_result, error);
+ return;
+ }
+
+ /* Requesting to deflect the call? */
+ if (deflect_str) {
+ gboolean operation_result;
+
+ operation_result = mm_call_deflect_sync (ctx->call,
+ deflect_str,
+ NULL,
+ &error);
+ deflect_process_reply (operation_result, error);
+ return;
+ }
+
+ /* Requesting to join multiparty call? */
+ if (join_multiparty_flag) {
+ gboolean operation_result;
+
+ operation_result = mm_call_join_multiparty_sync (ctx->call,
+ NULL,
+ &error);
+ join_multiparty_process_reply (operation_result, error);
+ return;
+ }
+
+ /* Requesting to leave multiparty call? */
+ if (leave_multiparty_flag) {
+ gboolean operation_result;
+
+ operation_result = mm_call_leave_multiparty_sync (ctx->call,
+ NULL,
+ &error);
+ leave_multiparty_process_reply (operation_result, error);
+ return;
+ }
+
+ /* Requesting to hangup the call? */
+ if (hangup_flag) {
+ gboolean operation_result;
+
+ operation_result = mm_call_hangup_sync (ctx->call,
+ NULL,
+ &error);
+ hangup_process_reply (operation_result, error);
+ return;
+ }
+
+ /* Requesting to send a dtmf? */
+ if (dtmf_request) {
+ gboolean operation_result;
+
+ operation_result = mm_call_send_dtmf_sync (ctx->call,
+ dtmf_request,
+ NULL,
+ &error);
+ send_dtmf_process_reply (operation_result, error);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
diff --git a/cli/mmcli-common.c b/cli/mmcli-common.c
index 2d7f66ea..4163f42f 100644
--- a/cli/mmcli-common.c
+++ b/cli/mmcli-common.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -26,14 +26,23 @@
#include "mmcli-common.h"
+/******************************************************************************/
+/* Manager */
+
+MMManager *
+mmcli_get_manager_finish (GAsyncResult *res)
+{
+ return g_task_propagate_pointer (G_TASK (res), NULL);
+}
+
static void
manager_new_ready (GDBusConnection *connection,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
MMManager *manager;
- gchar *name_owner;
- GError *error = NULL;
+ gchar *name_owner;
+ GError *error = NULL;
manager = mm_manager_new_finish (res, &error);
if (!manager) {
@@ -51,36 +60,25 @@ manager_new_ready (GDBusConnection *connection,
g_debug ("ModemManager process found at '%s'", name_owner);
g_free (name_owner);
-
-
- g_simple_async_result_set_op_res_gpointer (simple, manager, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-MMManager *
-mmcli_get_manager_finish (GAsyncResult *res)
-{
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ g_task_return_pointer (task, manager, g_object_unref);
+ g_object_unref (task);
}
void
-mmcli_get_manager (GDBusConnection *connection,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mmcli_get_manager (GDBusConnection *connection,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
+
+ task = g_task_new (connection, cancellable, callback, user_data);
- result = g_simple_async_result_new (G_OBJECT (connection),
- callback,
- user_data,
- mmcli_get_manager);
mm_manager_new (connection,
G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
cancellable,
(GAsyncReadyCallback)manager_new_ready,
- result);
+ task);
}
MMManager *
@@ -112,9 +110,90 @@ mmcli_get_manager_sync (GDBusConnection *connection)
return manager;
}
+/******************************************************************************/
+/* Common to all objects */
+
+#define ANY_OBJECT_STR "any"
+
+static void
+get_object_lookup_info (const gchar *str,
+ const gchar *object_type,
+ const gchar *object_prefix,
+ gchar **object_path,
+ gchar **modem_uid,
+ gboolean *find_any)
+{
+ gboolean all_numeric;
+ guint i;
+
+ /* Empty string not allowed */
+ if (!str || !str[0]) {
+ g_printerr ("error: no %s was specified\n", object_type);
+ exit (EXIT_FAILURE);
+ }
+
+ /* User string may come in four ways:
+ * a) full DBus path
+ * b) object index
+ * c) modem UID (for modem or SIM lookup only)
+ * d) "any" string (for modem or SIM lookup only)
+ */
+
+ *object_path = NULL;
+ if (modem_uid)
+ *modem_uid = NULL;
+ if (find_any)
+ *find_any = FALSE;
+
+ /* If match the DBus prefix, we have a DBus object path */
+ if (g_str_has_prefix (str, object_prefix)) {
+ g_debug ("Assuming '%s' is the full %s path", str, object_type);
+ *object_path = g_strdup (str);
+ return;
+ }
+
+ /* If all numeric, we have the object index */
+ all_numeric = TRUE;
+ for (i = 0; str[i]; i++) {
+ if (!g_ascii_isdigit (str[i])) {
+ all_numeric = FALSE;
+ break;
+ }
+ }
+ if (all_numeric) {
+ g_debug ("Assuming '%s' is the %s index", str, object_type);
+ *object_path = g_strdup_printf ("%s/%s", object_prefix, str);
+ return;
+ }
+
+ /* If it matches the lookup keyword or any of its substrings, we have
+ * to look for the first available object */
+ if ((find_any) && (g_ascii_strncasecmp (str, ANY_OBJECT_STR, strlen (str)) == 0)) {
+ g_debug ("Will look for first available %s", object_type);
+ *find_any = TRUE;
+ return;
+ }
+
+ /* Otherwise we have the UID */
+ if (modem_uid) {
+ g_debug ("Assuming '%s' is the modem UID", str);
+ *modem_uid = g_strdup (str);
+ return;
+ }
+
+ /* If UID is not a valid input for the object type, error out */
+ g_printerr ("error: invalid %s string specified: '%s'\n", object_type, str);
+ exit (EXIT_FAILURE);
+}
+
+/******************************************************************************/
+/* Modem */
+
static MMObject *
-find_modem (MMManager *manager,
- const gchar *modem_path)
+find_modem (MMManager *manager,
+ const gchar *modem_path,
+ const gchar *modem_uid,
+ gboolean modem_any)
{
GList *modems;
GList *l;
@@ -122,17 +201,25 @@ find_modem (MMManager *manager,
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
for (l = modems; l; l = g_list_next (l)) {
- MMObject *modem = MM_OBJECT (l->data);
+ MMObject *obj;
+ MMModem *modem;
- if (g_str_equal (mm_object_get_path (modem), modem_path)) {
- found = g_object_ref (modem);
+ obj = MM_OBJECT (l->data);
+ modem = mm_object_get_modem (obj);
+ if (!modem)
+ continue;
+
+ if (modem_any ||
+ (modem_path && g_str_equal (mm_object_get_path (obj), modem_path)) ||
+ (modem_uid && g_str_equal (mm_modem_get_device (modem), modem_uid))) {
+ found = g_object_ref (obj);
break;
}
}
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
+ g_list_free_full (modems, g_object_unref);
if (!found) {
- g_printerr ("error: couldn't find modem at '%s'\n", modem_path);
+ g_printerr ("error: couldn't find modem\n");
exit (EXIT_FAILURE);
}
@@ -142,14 +229,14 @@ find_modem (MMManager *manager,
}
typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gchar *modem_path;
+ gchar *modem_path;
+ gchar *modem_uid;
+ gboolean modem_any;
} GetModemContext;
typedef struct {
MMManager *manager;
- MMObject *object;
+ MMObject *object;
} GetModemResults;
static void
@@ -161,174 +248,157 @@ get_modem_results_free (GetModemResults *results)
}
static void
-get_modem_context_complete_and_free (GetModemContext *ctx)
+get_modem_context_free (GetModemContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
g_free (ctx->modem_path);
+ g_free (ctx->modem_uid);
g_free (ctx);
}
MMObject *
-mmcli_get_modem_finish (GAsyncResult *res,
- MMManager **o_manager)
+mmcli_get_modem_finish (GAsyncResult *res,
+ MMManager **o_manager)
{
GetModemResults *results;
+ MMObject *obj;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ results = g_task_propagate_pointer (G_TASK (res), NULL);
+ g_assert (results);
if (o_manager)
*o_manager = g_object_ref (results->manager);
-
- return g_object_ref (results->object);
+ obj = g_object_ref (results->object);
+ get_modem_results_free (results);
+ return obj;
}
static void
get_manager_ready (GDBusConnection *connection,
- GAsyncResult *res,
- GetModemContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
GetModemResults *results;
+ GetModemContext *ctx;
+
+ ctx = g_task_get_task_data (task);
results = g_new (GetModemResults, 1);
results->manager = mmcli_get_manager_finish (res);
- results->object = find_modem (results->manager, ctx->modem_path);
-
- /* Set operation results */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- results,
- (GDestroyNotify)get_modem_results_free);
-
- get_modem_context_complete_and_free (ctx);
-}
-
-static gchar *
-get_modem_path (const gchar *path_or_index)
-{
- gchar *modem_path;
-
- /* We must have a given modem specified */
- if (!path_or_index) {
- g_printerr ("error: no modem was specified\n");
- exit (EXIT_FAILURE);
- }
-
- /* Modem path may come in two ways: full DBus path or just modem index.
- * If it is a modem index, we'll need to generate the DBus path ourselves */
- if (g_str_has_prefix (path_or_index, MM_DBUS_MODEM_PREFIX)) {
- g_debug ("Assuming '%s' is the full modem path", path_or_index);
- modem_path = g_strdup (path_or_index);
- } else if (g_ascii_isdigit (path_or_index[0])) {
- g_debug ("Assuming '%s' is the modem index", path_or_index);
- modem_path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%s", path_or_index);
- } else {
- g_printerr ("error: invalid path or index string specified: '%s'\n",
- path_or_index);
- exit (EXIT_FAILURE);
- }
-
- return modem_path;
+ results->object = find_modem (results->manager, ctx->modem_path, ctx->modem_uid, ctx->modem_any);
+ g_task_return_pointer (task, results, (GDestroyNotify)get_modem_results_free);
+ g_object_unref (task);
}
void
-mmcli_get_modem (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mmcli_get_modem (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
GetModemContext *ctx;
+ task = g_task_new (connection, cancellable, callback, user_data);
+
ctx = g_new0 (GetModemContext, 1);
- ctx->modem_path = get_modem_path (path_or_index);
- ctx->result = g_simple_async_result_new (G_OBJECT (connection),
- callback,
- user_data,
- mmcli_get_modem);
+ get_object_lookup_info (str, "modem", MM_DBUS_MODEM_PREFIX,
+ &ctx->modem_path, &ctx->modem_uid, &ctx->modem_any);
+ g_assert (!!ctx->modem_path + !!ctx->modem_uid + ctx->modem_any == 1);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) get_modem_context_free);
mmcli_get_manager (connection,
cancellable,
(GAsyncReadyCallback)get_manager_ready,
- ctx);
+ task);
}
MMObject *
-mmcli_get_modem_sync (GDBusConnection *connection,
- const gchar *modem_str,
- MMManager **o_manager)
+mmcli_get_modem_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **o_manager)
{
MMManager *manager;
MMObject *found;
- gchar *modem_path;
+ gchar *modem_path = NULL;
+ gchar *modem_uid = NULL;
+ gboolean modem_any = FALSE;
manager = mmcli_get_manager_sync (connection);
- modem_path = get_modem_path (modem_str);
- found = find_modem (manager, modem_path);
+ get_object_lookup_info (str, "modem", MM_DBUS_MODEM_PREFIX,
+ &modem_path, &modem_uid, &modem_any);
+ g_assert (!!modem_path + !!modem_uid + modem_any == 1);
+ found = find_modem (manager, modem_path, modem_uid, modem_any);
if (o_manager)
*o_manager = manager;
else
g_object_unref (manager);
g_free (modem_path);
+ g_free (modem_uid);
return found;
}
+/******************************************************************************/
+/* Bearer */
+
typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gchar *bearer_path;
+ gchar *bearer_path;
MMManager *manager;
- GList *modems;
- MMObject *current;
- MMBearer *bearer;
+ GList *modems;
+ MMObject *current;
} GetBearerContext;
+typedef struct {
+ MMManager *manager;
+ MMObject *object;
+ MMBearer *bearer;
+} GetBearerResults;
+
+static void
+get_bearer_results_free (GetBearerResults *results)
+{
+ g_object_unref (results->manager);
+ g_object_unref (results->object);
+ g_object_unref (results->bearer);
+ g_free (results);
+}
+
static void
get_bearer_context_free (GetBearerContext *ctx)
{
if (ctx->current)
g_object_unref (ctx->current);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
if (ctx->manager)
g_object_unref (ctx->manager);
- if (ctx->bearer)
- g_object_unref (ctx->bearer);
- g_list_free_full (ctx->modems, (GDestroyNotify) g_object_unref);
+ g_list_free_full (ctx->modems, g_object_unref);
g_free (ctx->bearer_path);
g_free (ctx);
}
-static void
-get_bearer_context_complete (GetBearerContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- ctx->result = NULL;
-}
-
MMBearer *
-mmcli_get_bearer_finish (GAsyncResult *res,
- MMManager **o_manager,
- MMObject **o_object)
+mmcli_get_bearer_finish (GAsyncResult *res,
+ MMManager **o_manager,
+ MMObject **o_object)
{
- GetBearerContext *ctx;
+ GetBearerResults *results;
+ MMBearer *obj;
- ctx = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ results = g_task_propagate_pointer (G_TASK (res), NULL);
+ g_assert (results);
if (o_manager)
- *o_manager = g_object_ref (ctx->manager);
+ *o_manager = g_object_ref (results->manager);
if (o_object)
- *o_object = g_object_ref (ctx->current);
- return g_object_ref (ctx->bearer);
+ *o_object = g_object_ref (results->object);
+ obj = g_object_ref (results->bearer);
+ get_bearer_results_free (results);
+ return obj;
}
-static void look_for_bearer_in_modem (GetBearerContext *ctx);
+static void look_for_bearer_in_modem (GTask *task);
static MMBearer *
-find_bearer_in_list (GList *list,
+find_bearer_in_list (GList *list,
const gchar *bearer_path)
{
GList *l;
@@ -346,12 +416,17 @@ find_bearer_in_list (GList *list,
}
static void
-list_bearers_ready (MMModem *modem,
+list_bearers_ready (MMModem *modem,
GAsyncResult *res,
- GetBearerContext *ctx)
+ GTask *task)
{
- GList *bearers;
- GError *error = NULL;
+ GetBearerContext *ctx;
+ GetBearerResults *results;
+ MMBearer *found;
+ GList *bearers;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
bearers = mm_modem_list_bearers_finish (modem, res, &error);
if (error) {
@@ -361,27 +436,99 @@ list_bearers_ready (MMModem *modem,
exit (EXIT_FAILURE);
}
- ctx->bearer = find_bearer_in_list (bearers, ctx->bearer_path);
- g_list_free_full (bearers, (GDestroyNotify) g_object_unref);
+ found = find_bearer_in_list (bearers, ctx->bearer_path);
+ g_list_free_full (bearers, g_object_unref);
+
+ if (!found) {
+ /* Not found, try with next modem */
+ look_for_bearer_in_modem (task);
+ return;
+ }
/* Found! */
- if (ctx->bearer) {
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- ctx,
- (GDestroyNotify)get_bearer_context_free);
- get_bearer_context_complete (ctx);
+ results = g_new (GetBearerResults, 1);
+ results->manager = g_object_ref (ctx->manager);
+ results->object = g_object_ref (ctx->current);
+ results->bearer = found;
+ g_task_return_pointer (task, results, (GDestroyNotify) get_bearer_results_free);
+ g_object_unref (task);
+}
+
+static void
+look_for_bearer_in_modem_bearer_list (GTask *task)
+{
+ GetBearerContext *ctx;
+ MMModem *modem;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->current);
+ modem = mm_object_get_modem (ctx->current);
+ mm_modem_list_bearers (modem,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)list_bearers_ready,
+ task);
+ g_object_unref (modem);
+}
+
+static void
+get_initial_eps_bearer_ready (MMModem3gpp *modem3gpp,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GetBearerContext *ctx;
+ MMBearer *bearer;
+ GetBearerResults *results;
+
+ ctx = g_task_get_task_data (task);
+
+ bearer = mm_modem_3gpp_get_initial_eps_bearer_finish (modem3gpp, res, NULL);
+ if (!bearer) {
+ look_for_bearer_in_modem_bearer_list (task);
return;
}
- /* Not found, try with next modem */
- look_for_bearer_in_modem (ctx);
+ /* Found! */
+ results = g_new (GetBearerResults, 1);
+ results->manager = g_object_ref (ctx->manager);
+ results->object = g_object_ref (ctx->current);
+ results->bearer = bearer;
+ g_task_return_pointer (task, results, (GDestroyNotify) get_bearer_results_free);
+ g_object_unref (task);
}
static void
-look_for_bearer_in_modem (GetBearerContext *ctx)
+look_for_bearer_in_modem_3gpp_eps_initial_bearer (GTask *task)
{
- MMModem *modem;
+ GetBearerContext *ctx;
+ MMModem3gpp *modem3gpp;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->current);
+ modem3gpp = mm_object_get_modem_3gpp (ctx->current);
+ if (!modem3gpp) {
+ look_for_bearer_in_modem_bearer_list (task);
+ return;
+ }
+
+ if (!g_strcmp0 (mm_modem_3gpp_get_initial_eps_bearer_path (modem3gpp), ctx->bearer_path))
+ mm_modem_3gpp_get_initial_eps_bearer (modem3gpp,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)get_initial_eps_bearer_ready,
+ task);
+ else
+ look_for_bearer_in_modem_bearer_list (task);
+ g_object_unref (modem3gpp);
+}
+
+static void
+look_for_bearer_in_modem (GTask *task)
+{
+ GetBearerContext *ctx;
+ MMModem *modem;
+
+ ctx = g_task_get_task_data (task);
if (!ctx->modems) {
g_printerr ("error: couldn't find bearer at '%s': 'not found in any modem'\n",
@@ -400,27 +547,26 @@ look_for_bearer_in_modem (GetBearerContext *ctx)
g_debug ("Skipping modem '%s' when looking for bearers "
"(not fully initialized)",
mm_object_get_path (ctx->current));
- g_object_unref (modem);
- look_for_bearer_in_modem (ctx);
- return;
+ look_for_bearer_in_modem (task);
+ } else {
+ g_debug ("Looking for bearer '%s' in modem '%s'...",
+ ctx->bearer_path,
+ mm_object_get_path (ctx->current));
+ look_for_bearer_in_modem_3gpp_eps_initial_bearer (task);
}
- g_debug ("Looking for bearer '%s' in modem '%s'...",
- ctx->bearer_path,
- mm_object_get_path (ctx->current));
-
- mm_modem_list_bearers (modem,
- ctx->cancellable,
- (GAsyncReadyCallback)list_bearers_ready,
- ctx);
g_object_unref (modem);
}
static void
get_bearer_manager_ready (GDBusConnection *connection,
- GAsyncResult *res,
- GetBearerContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
+ GetBearerContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
ctx->manager = mmcli_get_manager_finish (res);
ctx->modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager));
if (!ctx->modems) {
@@ -429,73 +575,48 @@ get_bearer_manager_ready (GDBusConnection *connection,
exit (EXIT_FAILURE);
}
- look_for_bearer_in_modem (ctx);
-}
-
-static gchar *
-get_bearer_path (const gchar *path_or_index)
-{
- gchar *bearer_path;
-
- /* We must have a given bearer specified */
- if (!path_or_index) {
- g_printerr ("error: no bearer was specified\n");
- exit (EXIT_FAILURE);
- }
-
- /* Bearer path may come in two ways: full DBus path or just bearer index.
- * If it is a bearer index, we'll need to generate the DBus path ourselves */
- if (g_str_has_prefix (path_or_index, MM_DBUS_BEARER_PREFIX)) {
- g_debug ("Assuming '%s' is the full bearer path", path_or_index);
- bearer_path = g_strdup (path_or_index);
- } else if (g_ascii_isdigit (path_or_index[0])) {
- g_debug ("Assuming '%s' is the bearer index", path_or_index);
- bearer_path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%s", path_or_index);
- } else {
- g_printerr ("error: invalid path or index string specified: '%s'\n",
- path_or_index);
- exit (EXIT_FAILURE);
- }
-
- return bearer_path;
+ look_for_bearer_in_modem (task);
}
void
-mmcli_get_bearer (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mmcli_get_bearer (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
GetBearerContext *ctx;
+ task = g_task_new (connection, cancellable, callback, user_data);
+
ctx = g_new0 (GetBearerContext, 1);
- ctx->bearer_path = get_bearer_path (path_or_index);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (connection),
- callback,
- user_data,
- mmcli_get_bearer);
+ get_object_lookup_info (str, "bearer", MM_DBUS_BEARER_PREFIX,
+ &ctx->bearer_path, NULL, NULL);
+ g_assert (ctx->bearer_path);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) get_bearer_context_free);
+
mmcli_get_manager (connection,
cancellable,
(GAsyncReadyCallback)get_bearer_manager_ready,
- ctx);
+ task);
}
MMBearer *
-mmcli_get_bearer_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **o_manager,
- MMObject **o_object)
+mmcli_get_bearer_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **o_manager,
+ MMObject **o_object)
{
MMManager *manager;
GList *modems;
GList *l;
MMBearer *found = NULL;
- gchar *bearer_path;
+ gchar *bearer_path = NULL;
- bearer_path = get_bearer_path (path_or_index);
+ get_object_lookup_info (str, "bearer", MM_DBUS_BEARER_PREFIX,
+ &bearer_path, NULL, NULL);
+ g_assert (bearer_path);
manager = mmcli_get_manager_sync (connection);
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
@@ -506,38 +627,52 @@ mmcli_get_bearer_sync (GDBusConnection *connection,
}
for (l = modems; !found && l; l = g_list_next (l)) {
- GError *error = NULL;
- MMObject *object;
- MMModem *modem;
- GList *bearers;
+ GError *error = NULL;
+ MMObject *object;
+ MMModem *modem;
+ MMModem3gpp *modem3gpp;
object = MM_OBJECT (l->data);
modem = mm_object_get_modem (object);
+ modem3gpp = mm_object_get_modem_3gpp (object);
/* Don't look for bearers in modems which are not fully initialized */
if (mm_modem_get_state (modem) < MM_MODEM_STATE_DISABLED) {
g_debug ("Skipping modem '%s' when looking for bearers "
"(not fully initialized)",
mm_object_get_path (object));
- g_object_unref (modem);
- continue;
+ goto next;
}
- bearers = mm_modem_list_bearers_sync (modem, NULL, &error);
- if (error) {
- g_printerr ("error: couldn't list bearers at '%s': '%s'\n",
- mm_modem_get_path (modem),
- error->message);
- exit (EXIT_FAILURE);
- }
+ if (modem3gpp && !g_strcmp0 (mm_modem_3gpp_get_initial_eps_bearer_path (modem3gpp), bearer_path)) {
+ found = mm_modem_3gpp_get_initial_eps_bearer_sync (modem3gpp, NULL, &error);
+ if (!found) {
+ g_printerr ("error: couldn't get initial EPS bearer object at '%s': '%s'\n",
+ mm_modem_get_path (modem),
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+ } else {
+ GList *bearers;
- found = find_bearer_in_list (bearers, bearer_path);
- g_list_free_full (bearers, (GDestroyNotify) g_object_unref);
+ bearers = mm_modem_list_bearers_sync (modem, NULL, &error);
+ if (error) {
+ g_printerr ("error: couldn't list bearers at '%s': '%s'\n",
+ mm_modem_get_path (modem),
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ found = find_bearer_in_list (bearers, bearer_path);
+ g_list_free_full (bearers, g_object_unref);
+ }
if (found && o_object)
*o_object = g_object_ref (object);
- g_object_unref (modem);
+ next:
+ g_clear_object (&modem);
+ g_clear_object (&modem3gpp);
}
if (!found) {
@@ -546,7 +681,7 @@ mmcli_get_bearer_sync (GDBusConnection *connection,
exit (EXIT_FAILURE);
}
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
+ g_list_free_full (modems, g_object_unref);
g_free (bearer_path);
if (o_manager)
@@ -557,203 +692,295 @@ mmcli_get_bearer_sync (GDBusConnection *connection,
return found;
}
+/******************************************************************************/
+/* SIM */
+
typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gchar *sim_path;
+ gchar *sim_path;
+ gchar *modem_uid;
+ gboolean sim_any;
MMManager *manager;
- MMObject *modem;
- MMSim *sim;
+ MMObject *current;
} GetSimContext;
+typedef struct {
+ MMManager *manager;
+ MMObject *object;
+ MMSim *sim;
+} GetSimResults;
+
+static void
+get_sim_results_free (GetSimResults *results)
+{
+ g_object_unref (results->manager);
+ g_object_unref (results->object);
+ g_object_unref (results->sim);
+ g_free (results);
+}
+
static void
get_sim_context_free (GetSimContext *ctx)
{
- if (ctx->modem)
- g_object_unref (ctx->modem);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
+ if (ctx->current)
+ g_object_unref (ctx->current);
if (ctx->manager)
g_object_unref (ctx->manager);
- if (ctx->sim)
- g_object_unref (ctx->sim);
+ g_free (ctx->modem_uid);
g_free (ctx->sim_path);
g_free (ctx);
}
-static void
-get_sim_context_complete (GetSimContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- ctx->result = NULL;
-}
-
MMSim *
-mmcli_get_sim_finish (GAsyncResult *res,
- MMManager **o_manager,
- MMObject **o_object)
+mmcli_get_sim_finish (GAsyncResult *res,
+ MMManager **o_manager,
+ MMObject **o_object)
{
- GetSimContext *ctx;
+ GetSimResults *results;
+ MMSim *obj;
- ctx = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ results = g_task_propagate_pointer (G_TASK (res), NULL);
+ g_assert (results);
if (o_manager)
- *o_manager = g_object_ref (ctx->manager);
+ *o_manager = g_object_ref (results->manager);
if (o_object)
- *o_object = g_object_ref (ctx->modem);
- return g_object_ref (ctx->sim);
+ *o_object = g_object_ref (results->object);
+ obj = g_object_ref (results->sim);
+ get_sim_results_free (results);
+ return obj;
}
static void
-get_sim_ready (MMModem *modem,
+list_sim_slots_ready (MMModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GPtrArray) sim_slots = NULL;
+ GetSimContext *ctx;
+ GetSimResults *results = NULL;
+ guint i;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ sim_slots = mm_modem_list_sim_slots_finish (modem, res, &error);
+ if (error) {
+ g_printerr ("error: couldn't list SIM slots at '%s': '%s'\n",
+ mm_modem_get_path (modem),
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ for (i = 0; i < sim_slots->len; i++) {
+ MMSim *sim;
+
+ sim = MM_SIM (g_ptr_array_index (sim_slots, i));
+ if (sim && g_str_equal (mm_sim_get_path (sim), ctx->sim_path)) {
+ /* Found! */
+ results = g_new (GetSimResults, 1);
+ results->manager = g_object_ref (ctx->manager);
+ results->object = g_object_ref (ctx->current);
+ results->sim = g_object_ref (sim);
+ break;
+ }
+ }
+
+ if (results) {
+ g_task_return_pointer (task, results, (GDestroyNotify) get_sim_results_free);
+ g_object_unref (task);
+ return;
+ }
+
+ g_printerr ("error: couldn't get additional SIM '%s' at '%s'\n",
+ ctx->sim_path,
+ mm_modem_get_path (modem));
+ exit (EXIT_FAILURE);
+}
+
+static void
+get_sim_ready (MMModem *modem,
GAsyncResult *res,
- GetSimContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ GetSimContext *ctx;
+ GetSimResults *results;
+ MMSim *sim;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
- ctx->sim = mm_modem_get_sim_finish (modem, res, &error);
+ sim = mm_modem_get_sim_finish (modem, res, &error);
if (error) {
- g_printerr ("error: couldn't get sim '%s' at '%s': '%s'\n",
+ g_printerr ("error: couldn't get SIM '%s' at '%s': '%s'\n",
ctx->sim_path,
mm_modem_get_path (modem),
error->message);
exit (EXIT_FAILURE);
}
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- ctx,
- (GDestroyNotify)get_sim_context_free);
- get_sim_context_complete (ctx);
+ /* Found! */
+ results = g_new (GetSimResults, 1);
+ results->manager = g_object_ref (ctx->manager);
+ results->object = g_object_ref (ctx->current);
+ results->sim = sim;
+ g_task_return_pointer (task, results, (GDestroyNotify) get_sim_results_free);
+ g_object_unref (task);
}
static void
get_sim_manager_ready (GDBusConnection *connection,
- GAsyncResult *res,
- GetSimContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- GList *l;
- GList *modems;
+ GetSimContext *ctx;
+ GList *l;
+ GList *modems;
+
+ ctx = g_task_get_task_data (task);
ctx->manager = mmcli_get_manager_finish (res);
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager));
if (!modems) {
- g_printerr ("error: couldn't find sim at '%s': 'no modems found'\n",
+ g_printerr ("error: couldn't find SIM at '%s': 'no modems found'\n",
ctx->sim_path);
exit (EXIT_FAILURE);
}
- for (l = modems; l; l = g_list_next (l)) {
- MMObject *object;
- MMModem *modem;
+ for (l = modems; l && !ctx->current; l = g_list_next (l)) {
+ MMObject *object;
+ MMModem *modem;
+ const gchar *const *sim_slot_paths;
object = MM_OBJECT (l->data);
modem = mm_object_get_modem (object);
+ sim_slot_paths = mm_modem_get_sim_slot_paths (modem);
+
+ /* check if we can match the first object found */
+ if (ctx->sim_any) {
+ g_assert (!ctx->sim_path);
+ ctx->sim_path = g_strdup (mm_modem_get_sim_path (modem));
+ }
+ /* check if modem UID matches */
+ else if (ctx->modem_uid) {
+ if (g_str_equal (ctx->modem_uid, mm_modem_get_device (modem))) {
+ g_assert (!ctx->sim_path);
+ ctx->sim_path = g_strdup (mm_modem_get_sim_path (modem));
+ } else {
+ g_object_unref (modem);
+ continue;
+ }
+ }
+
if (g_str_equal (ctx->sim_path, mm_modem_get_sim_path (modem))) {
- ctx->modem = g_object_ref (object);
+ ctx->current = g_object_ref (object);
mm_modem_get_sim (modem,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)get_sim_ready,
- ctx);
- break;
+ task);
+ } else if (sim_slot_paths) {
+ guint i;
+
+ for (i = 0; sim_slot_paths[i]; i++) {
+ if (g_str_equal (ctx->sim_path, sim_slot_paths[i])) {
+ ctx->current = g_object_ref (object);
+ mm_modem_list_sim_slots (modem,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)list_sim_slots_ready,
+ task);
+ break;
+ }
+ }
}
g_object_unref (modem);
}
+ g_list_free_full (modems, g_object_unref);
- if (!ctx->modem) {
- g_printerr ("error: couldn't find sim at '%s'\n",
- ctx->sim_path);
+ if (!ctx->current) {
+ g_printerr ("error: couldn't find SIM\n");
exit (EXIT_FAILURE);
}
-
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
-}
-
-static gchar *
-get_sim_path (const gchar *path_or_index)
-{
- gchar *sim_path;
-
- /* We must have a given sim specified */
- if (!path_or_index) {
- g_printerr ("error: no sim was specified\n");
- exit (EXIT_FAILURE);
- }
-
- /* Sim path may come in two ways: full DBus path or just sim index.
- * If it is a sim index, we'll need to generate the DBus path ourselves */
- if (g_str_has_prefix (path_or_index, MM_DBUS_SIM_PREFIX)) {
- g_debug ("Assuming '%s' is the full SIM path", path_or_index);
- sim_path = g_strdup (path_or_index);
- } else if (g_ascii_isdigit (path_or_index[0])) {
- g_debug ("Assuming '%s' is the SIM index", path_or_index);
- sim_path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%s", path_or_index);
- } else {
- g_printerr ("error: invalid index string specified: '%s'\n",
- path_or_index);
- exit (EXIT_FAILURE);
- }
-
- return sim_path;
}
void
-mmcli_get_sim (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mmcli_get_sim (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
GetSimContext *ctx;
+ task = g_task_new (connection, cancellable, callback, user_data);
+
ctx = g_new0 (GetSimContext, 1);
- ctx->sim_path = get_sim_path (path_or_index);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (connection),
- callback,
- user_data,
- mmcli_get_sim);
+ get_object_lookup_info (str, "SIM", MM_DBUS_SIM_PREFIX,
+ &ctx->sim_path, &ctx->modem_uid, &ctx->sim_any);
+ g_assert (!!ctx->sim_path + !!ctx->modem_uid + ctx->sim_any == 1);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) get_sim_context_free);
+
mmcli_get_manager (connection,
cancellable,
(GAsyncReadyCallback)get_sim_manager_ready,
- ctx);
+ task);
}
MMSim *
-mmcli_get_sim_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **o_manager,
- MMObject **o_object)
+mmcli_get_sim_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **o_manager,
+ MMObject **o_object)
{
MMManager *manager;
GList *modems;
GList *l;
MMSim *found = NULL;
- gchar *sim_path;
+ gchar *sim_path = NULL;
+ gchar *modem_uid = NULL;
+ gboolean sim_any = FALSE;
- sim_path = get_sim_path (path_or_index);
+ get_object_lookup_info (str, "SIM", MM_DBUS_SIM_PREFIX,
+ &sim_path, &modem_uid, &sim_any);
+ g_assert (!!sim_path + !!modem_uid + sim_any == 1);
manager = mmcli_get_manager_sync (connection);
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
if (!modems) {
- g_printerr ("error: couldn't find sim at '%s': 'no modems found'\n",
+ g_printerr ("error: couldn't find SIM at '%s': 'no modems found'\n",
sim_path);
exit (EXIT_FAILURE);
}
for (l = modems; !found && l; l = g_list_next (l)) {
- GError *error = NULL;
- MMObject *object;
- MMModem *modem;
+ GError *error = NULL;
+ MMObject *object;
+ MMModem *modem;
+ const gchar *const *sim_slot_paths;
object = MM_OBJECT (l->data);
modem = mm_object_get_modem (object);
+ sim_slot_paths = mm_modem_get_sim_slot_paths (modem);
+
+ /* check if we can match the first object found */
+ if (sim_any) {
+ g_assert (!sim_path);
+ sim_path = g_strdup (mm_modem_get_sim_path (modem));
+ }
+ /* check if modem UID matches */
+ else if (modem_uid) {
+ if (g_str_equal (modem_uid, mm_modem_get_device (modem))) {
+ g_assert (!sim_path);
+ sim_path = g_strdup (mm_modem_get_sim_path (modem));
+ } else {
+ g_object_unref (modem);
+ continue;
+ }
+ }
+
if (g_str_equal (sim_path, mm_modem_get_sim_path (modem))) {
found = mm_modem_get_sim_sync (modem, NULL, &error);
if (error) {
- g_printerr ("error: couldn't get sim '%s' in modem '%s': '%s'\n",
+ g_printerr ("error: couldn't get SIM '%s' in modem '%s': '%s'\n",
sim_path,
mm_modem_get_path (modem),
error->message);
@@ -762,17 +989,45 @@ mmcli_get_sim_sync (GDBusConnection *connection,
if (found && o_object)
*o_object = g_object_ref (object);
+ } else if (sim_slot_paths) {
+ guint i;
+
+ for (i = 0; !found && sim_slot_paths[i]; i++) {
+ if (g_str_equal (sim_path, sim_slot_paths[i])) {
+ g_autoptr(GPtrArray) sim_slots = NULL;
+ guint j;
+
+ sim_slots = mm_modem_list_sim_slots_sync (modem, NULL, &error);
+ if (error) {
+ g_printerr ("error: couldn't get SIM slots in modem '%s': '%s'\n",
+ mm_modem_get_path (modem),
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ for (j = 0; j < sim_slots->len; j++) {
+ MMSim *sim;
+
+ sim = MM_SIM (g_ptr_array_index (sim_slots, j));
+ if (sim && g_str_equal (sim_path, mm_sim_get_path (sim))) {
+ found = g_object_ref (sim);
+ if (o_object)
+ *o_object = g_object_ref (object);
+ }
+ }
+ }
+ }
}
g_object_unref (modem);
}
if (!found) {
- g_printerr ("error: couldn't find sim at '%s'\n", sim_path);
+ g_printerr ("error: couldn't find SIM\n");
exit (EXIT_FAILURE);
}
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
+ g_list_free_full (modems, g_object_unref);
g_free (sim_path);
if (o_manager)
@@ -783,59 +1038,66 @@ mmcli_get_sim_sync (GDBusConnection *connection,
return found;
}
+/******************************************************************************/
+/* SMS */
+
typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gchar *sms_path;
+ gchar *sms_path;
MMManager *manager;
- GList *modems;
- MMObject *current;
- MMSms *sms;
+ GList *modems;
+ MMObject *current;
} GetSmsContext;
+typedef struct {
+ MMManager *manager;
+ MMObject *object;
+ MMSms *sms;
+} GetSmsResults;
+
+static void
+get_sms_results_free (GetSmsResults *results)
+{
+ g_object_unref (results->manager);
+ g_object_unref (results->object);
+ g_object_unref (results->sms);
+ g_free (results);
+}
+
static void
get_sms_context_free (GetSmsContext *ctx)
{
if (ctx->current)
g_object_unref (ctx->current);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
if (ctx->manager)
g_object_unref (ctx->manager);
- if (ctx->sms)
- g_object_unref (ctx->sms);
- g_list_free_full (ctx->modems, (GDestroyNotify) g_object_unref);
+ g_list_free_full (ctx->modems, g_object_unref);
g_free (ctx->sms_path);
g_free (ctx);
}
-static void
-get_sms_context_complete (GetSmsContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- ctx->result = NULL;
-}
-
MMSms *
-mmcli_get_sms_finish (GAsyncResult *res,
- MMManager **o_manager,
- MMObject **o_object)
+mmcli_get_sms_finish (GAsyncResult *res,
+ MMManager **o_manager,
+ MMObject **o_object)
{
- GetSmsContext *ctx;
+ GetSmsResults *results;
+ MMSms *obj;
- ctx = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ results = g_task_propagate_pointer (G_TASK (res), NULL);
+ g_assert (results);
if (o_manager)
- *o_manager = g_object_ref (ctx->manager);
+ *o_manager = g_object_ref (results->manager);
if (o_object)
- *o_object = g_object_ref (ctx->current);
- return g_object_ref (ctx->sms);
+ *o_object = g_object_ref (results->object);
+ obj = g_object_ref (results->sms);
+ get_sms_results_free (results);
+ return obj;
}
-static void look_for_sms_in_modem (GetSmsContext *ctx);
+static void look_for_sms_in_modem (GTask *task);
static MMSms *
-find_sms_in_list (GList *list,
+find_sms_in_list (GList *list,
const gchar *sms_path)
{
GList *l;
@@ -844,7 +1106,7 @@ find_sms_in_list (GList *list,
MMSms *sms = MM_SMS (l->data);
if (g_str_equal (mm_sms_get_path (sms), sms_path)) {
- g_debug ("Sms found at '%s'\n", sms_path);
+ g_debug ("SMS found at '%s'\n", sms_path);
return g_object_ref (sms);
}
}
@@ -854,11 +1116,16 @@ find_sms_in_list (GList *list,
static void
list_sms_ready (MMModemMessaging *modem,
- GAsyncResult *res,
- GetSmsContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- GList *sms_list;
- GError *error = NULL;
+ GetSmsContext *ctx;
+ GetSmsResults *results;
+ MMSms *found;
+ GList *sms_list;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
sms_list = mm_modem_messaging_list_finish (modem, res, &error);
if (error) {
@@ -868,28 +1135,32 @@ list_sms_ready (MMModemMessaging *modem,
exit (EXIT_FAILURE);
}
- ctx->sms = find_sms_in_list (sms_list, ctx->sms_path);
- g_list_free_full (sms_list, (GDestroyNotify) g_object_unref);
+ found = find_sms_in_list (sms_list, ctx->sms_path);
+ g_list_free_full (sms_list, g_object_unref);
- /* Found! */
- if (ctx->sms) {
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- ctx,
- (GDestroyNotify)get_sms_context_free);
- get_sms_context_complete (ctx);
+ if (!found) {
+ /* Not found, try with next modem */
+ look_for_sms_in_modem (task);
return;
}
- /* Not found, try with next modem */
- look_for_sms_in_modem (ctx);
+ /* Found! */
+ results = g_new (GetSmsResults, 1);
+ results->manager = g_object_ref (ctx->manager);
+ results->object = g_object_ref (ctx->current);
+ results->sms = found;
+ g_task_return_pointer (task, results, (GDestroyNotify) get_sms_results_free);
+ g_object_unref (task);
}
static void
-look_for_sms_in_modem (GetSmsContext *ctx)
+look_for_sms_in_modem (GTask *task)
{
+ GetSmsContext *ctx;
MMModemMessaging *modem;
+ ctx = g_task_get_task_data (task);
+
if (!ctx->modems) {
g_printerr ("error: couldn't find SMS at '%s': 'not found in any modem'\n",
ctx->sms_path);
@@ -901,27 +1172,31 @@ look_for_sms_in_modem (GetSmsContext *ctx)
ctx->modems = g_list_delete_link (ctx->modems, ctx->modems);
modem = mm_object_get_modem_messaging (ctx->current);
- if (modem) {
- g_debug ("Looking for sms '%s' in modem '%s'...",
- ctx->sms_path,
- mm_object_get_path (ctx->current));
- mm_modem_messaging_list (modem,
- ctx->cancellable,
- (GAsyncReadyCallback)list_sms_ready,
- ctx);
- g_object_unref (modem);
+ if (!modem) {
+ /* Current modem has no messaging capabilities, try with next modem */
+ look_for_sms_in_modem (task);
return;
}
- /* Current modem has no messaging capabilities, try with next modem */
- look_for_sms_in_modem (ctx);
+ g_debug ("Looking for sms '%s' in modem '%s'...",
+ ctx->sms_path,
+ mm_object_get_path (ctx->current));
+ mm_modem_messaging_list (modem,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)list_sms_ready,
+ task);
+ g_object_unref (modem);
}
static void
get_sms_manager_ready (GDBusConnection *connection,
- GAsyncResult *res,
- GetSmsContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
+ GetSmsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
ctx->manager = mmcli_get_manager_finish (res);
ctx->modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager));
if (!ctx->modems) {
@@ -930,78 +1205,51 @@ get_sms_manager_ready (GDBusConnection *connection,
exit (EXIT_FAILURE);
}
- look_for_sms_in_modem (ctx);
-}
-
-static gchar *
-get_sms_path (const gchar *path_or_index)
-{
- gchar *sms_path;
-
- /* We must have a given sms specified */
- if (!path_or_index) {
- g_printerr ("error: no SMS was specified\n");
- exit (EXIT_FAILURE);
- }
-
- /* Sms path may come in two ways: full DBus path or just sms index.
- * If it is a sms index, we'll need to generate the DBus path ourselves */
- if (g_str_has_prefix (path_or_index, MM_DBUS_SMS_PREFIX)) {
- g_debug ("Assuming '%s' is the full SMS path", path_or_index);
- sms_path = g_strdup (path_or_index);
- } else if (g_ascii_isdigit (path_or_index[0])) {
- g_debug ("Assuming '%s' is the SMS index", path_or_index);
- sms_path = g_strdup_printf (MM_DBUS_SMS_PREFIX "/%s", path_or_index);
- } else {
- g_printerr ("error: invalid path or index string specified: '%s'\n",
- path_or_index);
- exit (EXIT_FAILURE);
- }
-
- return sms_path;
+ look_for_sms_in_modem (task);
}
void
-mmcli_get_sms (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mmcli_get_sms (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
GetSmsContext *ctx;
+ task = g_task_new (connection, cancellable, callback, user_data);
+
ctx = g_new0 (GetSmsContext, 1);
- ctx->sms_path = get_sms_path (path_or_index);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (connection),
- callback,
- user_data,
- mmcli_get_sms);
+ get_object_lookup_info (str, "SMS", MM_DBUS_SMS_PREFIX,
+ &ctx->sms_path, NULL, NULL);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) get_sms_context_free);
+
mmcli_get_manager (connection,
cancellable,
(GAsyncReadyCallback)get_sms_manager_ready,
- ctx);
+ task);
}
MMSms *
-mmcli_get_sms_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **o_manager,
- MMObject **o_object)
+mmcli_get_sms_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **o_manager,
+ MMObject **o_object)
{
MMManager *manager;
GList *modems;
GList *l;
MMSms *found = NULL;
- gchar *sms_path;
+ gchar *sms_path = NULL;
- sms_path = get_sms_path (path_or_index);
+ get_object_lookup_info (str, "SMS", MM_DBUS_SMS_PREFIX,
+ &sms_path, NULL, NULL);
manager = mmcli_get_manager_sync (connection);
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
if (!modems) {
- g_printerr ("error: couldn't find sms at '%s': 'no modems found'\n",
+ g_printerr ("error: couldn't find SMS at '%s': 'no modems found'\n",
sms_path);
exit (EXIT_FAILURE);
}
@@ -1028,7 +1276,7 @@ mmcli_get_sms_sync (GDBusConnection *connection,
}
found = find_sms_in_list (sms_list, sms_path);
- g_list_free_full (sms_list, (GDestroyNotify) g_object_unref);
+ g_list_free_full (sms_list, g_object_unref);
if (found && o_object)
*o_object = g_object_ref (object);
@@ -1042,7 +1290,7 @@ mmcli_get_sms_sync (GDBusConnection *connection,
exit (EXIT_FAILURE);
}
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
+ g_list_free_full (modems, g_object_unref);
g_free (sms_path);
if (o_manager)
@@ -1053,6 +1301,271 @@ mmcli_get_sms_sync (GDBusConnection *connection,
return found;
}
+/******************************************************************************/
+/* Call */
+
+typedef struct {
+ gchar *call_path;
+ MMManager *manager;
+ GList *modems;
+ MMObject *current;
+} GetCallContext;
+
+typedef struct {
+ MMManager *manager;
+ MMObject *object;
+ MMCall *call;
+} GetCallResults;
+
+static void
+get_call_results_free (GetCallResults *results)
+{
+ g_object_unref (results->manager);
+ g_object_unref (results->object);
+ g_object_unref (results->call);
+ g_free (results);
+}
+
+static void
+get_call_context_free (GetCallContext *ctx)
+{
+ if (ctx->current)
+ g_object_unref (ctx->current);
+ if (ctx->manager)
+ g_object_unref (ctx->manager);
+ g_list_free_full (ctx->modems, g_object_unref);
+ g_free (ctx->call_path);
+ g_free (ctx);
+}
+
+MMCall *
+mmcli_get_call_finish (GAsyncResult *res,
+ MMManager **o_manager,
+ MMObject **o_object)
+{
+ GetCallResults *results;
+ MMCall *obj;
+
+ results = g_task_propagate_pointer (G_TASK (res), NULL);
+ g_assert (results);
+ if (o_manager)
+ *o_manager = g_object_ref (results->manager);
+ if (o_object)
+ *o_object = g_object_ref (results->object);
+ obj = g_object_ref (results->call);
+ get_call_results_free (results);
+ return obj;
+}
+
+static void look_for_call_in_modem (GTask *task);
+
+static MMCall *
+find_call_in_list (GList *list,
+ const gchar *call_path)
+{
+ GList *l;
+
+ for (l = list; l; l = g_list_next (l)) {
+ MMCall *call = MM_CALL (l->data);
+
+ if (g_str_equal (mm_call_get_path (call), call_path)) {
+ g_debug ("Call found at '%s'\n", call_path);
+ return g_object_ref (call);
+ }
+ }
+
+ return NULL;
+}
+
+static void
+list_calls_ready (MMModemVoice *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GetCallContext *ctx;
+ GetCallResults *results;
+ MMCall *found;
+ GList *call_list;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ call_list = mm_modem_voice_list_calls_finish (modem, res, &error);
+ if (error) {
+ g_printerr ("error: couldn't list call at '%s': '%s'\n",
+ mm_modem_voice_get_path (modem),
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ found = find_call_in_list (call_list, ctx->call_path);
+ g_list_free_full (call_list, g_object_unref);
+
+ if (!found) {
+ /* Not found, try with next modem */
+ look_for_call_in_modem (task);
+ return;
+ }
+
+ /* Found! */
+ results = g_new (GetCallResults, 1);
+ results->manager = g_object_ref (ctx->manager);
+ results->object = g_object_ref (ctx->current);
+ results->call = found;
+ g_task_return_pointer (task, results, (GDestroyNotify) get_call_results_free);
+ g_object_unref (task);
+}
+
+static void
+look_for_call_in_modem (GTask *task)
+{
+ GetCallContext *ctx;
+ MMModemVoice *modem;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!ctx->modems) {
+ g_printerr ("error: couldn't find call at '%s': 'not found in any modem'\n",
+ ctx->call_path);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Loop looking for the call in each modem found */
+ ctx->current = MM_OBJECT (ctx->modems->data);
+ ctx->modems = g_list_delete_link (ctx->modems, ctx->modems);
+
+ modem = mm_object_get_modem_voice (ctx->current);
+ if (!modem) {
+ /* Current modem has no messaging capabilities, try with next modem */
+ look_for_call_in_modem (task);
+ return;
+ }
+
+ g_debug ("Looking for call '%s' in modem '%s'...",
+ ctx->call_path,
+ mm_object_get_path (ctx->current));
+ mm_modem_voice_list_calls (modem,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)list_calls_ready,
+ task);
+ g_object_unref (modem);
+}
+
+static void
+get_call_manager_ready (GDBusConnection *connection,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GetCallContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->manager = mmcli_get_manager_finish (res);
+ ctx->modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager));
+ if (!ctx->modems) {
+ g_printerr ("error: couldn't find call at '%s': 'no modems found'\n",
+ ctx->call_path);
+ exit (EXIT_FAILURE);
+ }
+
+ look_for_call_in_modem (task);
+}
+
+void
+mmcli_get_call (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GetCallContext *ctx;
+
+ task = g_task_new (connection, cancellable, callback, user_data);
+
+ ctx = g_new0 (GetCallContext, 1);
+ get_object_lookup_info (str, "call", MM_DBUS_CALL_PREFIX,
+ &ctx->call_path, NULL, NULL);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) get_call_context_free);
+
+ mmcli_get_manager (connection,
+ cancellable,
+ (GAsyncReadyCallback)get_call_manager_ready,
+ task);
+}
+
+MMCall *
+mmcli_get_call_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **o_manager,
+ MMObject **o_object)
+{
+ MMManager *manager;
+ GList *modems;
+ GList *l;
+ MMCall *found = NULL;
+ gchar *call_path = NULL;
+
+ get_object_lookup_info (str, "call", MM_DBUS_CALL_PREFIX,
+ &call_path, NULL, NULL);
+
+ manager = mmcli_get_manager_sync (connection);
+ modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
+ if (!modems) {
+ g_printerr ("error: couldn't find call at '%s': 'no modems found'\n",
+ call_path);
+ exit (EXIT_FAILURE);
+ }
+
+ for (l = modems; !found && l; l = g_list_next (l)) {
+ GError *error = NULL;
+ MMObject *object;
+ MMModemVoice *voice;
+ GList *call_list;
+
+ object = MM_OBJECT (l->data);
+ voice = mm_object_get_modem_voice (object);
+
+ /* If doesn't implement voice, continue to next one */
+ if (!voice)
+ continue;
+
+ call_list = mm_modem_voice_list_calls_sync (voice, NULL, &error);
+ if (error) {
+ g_printerr ("error: couldn't list call at '%s': '%s'\n",
+ mm_modem_voice_get_path (voice),
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ found = find_call_in_list (call_list, call_path);
+ g_list_free_full (call_list, g_object_unref);
+
+ if (found && o_object)
+ *o_object = g_object_ref (object);
+
+ g_object_unref (voice);
+ }
+
+ if (!found) {
+ g_printerr ("error: couldn't find call at '%s': 'not found in any modem'\n",
+ call_path);
+ exit (EXIT_FAILURE);
+ }
+
+ g_list_free_full (modems, g_object_unref);
+ g_free (call_path);
+
+ if (o_manager)
+ *o_manager = manager;
+ else
+ g_object_unref (manager);
+
+ return found;
+}
+
+/******************************************************************************/
+
const gchar *
mmcli_get_state_reason_string (MMModemStateChangeReason reason)
{
@@ -1065,10 +1578,9 @@ mmcli_get_state_reason_string (MMModemStateChangeReason reason)
return "Suspend";
case MM_MODEM_STATE_CHANGE_REASON_FAILURE:
return "Failure";
+ default:
+ g_assert_not_reached ();
}
-
- g_warn_if_reached ();
- return NULL;
}
/* Common options */
@@ -1076,24 +1588,29 @@ static gchar *modem_str;
static gchar *bearer_str;
static gchar *sim_str;
static gchar *sms_str;
+static gchar *call_str;
static GOptionEntry entries[] = {
{ "modem", 'm', 0, G_OPTION_ARG_STRING, &modem_str,
- "Specify modem by path or index. Shows modem information if no action specified.",
- "[PATH|INDEX]"
+ "Specify modem by path, index, UID or 'any'. Shows modem information if no action specified.",
+ "[PATH|INDEX|UID|any]"
},
{ "bearer", 'b', 0, G_OPTION_ARG_STRING, &bearer_str,
"Specify bearer by path or index. Shows bearer information if no action specified.",
"[PATH|INDEX]"
},
{ "sim", 'i', 0, G_OPTION_ARG_STRING, &sim_str,
- "Specify SIM card by path or index. Shows SIM card information if no action specified.",
- "[PATH|INDEX]"
+ "Specify SIM card by path, index, UID or 'any'. Shows SIM card information if no action specified.",
+ "[PATH|INDEX|UID|any]"
},
{ "sms", 's', 0, G_OPTION_ARG_STRING, &sms_str,
"Specify SMS by path or index. Shows SMS information if no action specified.",
"[PATH|INDEX]"
},
+ { "call", 'o', 0, G_OPTION_ARG_STRING, &call_str,
+ "Specify Call by path or index. Shows Call information if no action specified.",
+ "[PATH|INDEX]"
+ },
{ NULL }
};
@@ -1137,6 +1654,12 @@ mmcli_get_common_sms_string (void)
return sms_str;
}
+const gchar *
+mmcli_get_common_call_string (void)
+{
+ return call_str;
+}
+
gchar *
mmcli_prefix_newlines (const gchar *prefix,
const gchar *str)
diff --git a/cli/mmcli-common.h b/cli/mmcli-common.h
index 615f5218..237ebb55 100644
--- a/cli/mmcli-common.h
+++ b/cli/mmcli-common.h
@@ -1,14 +1,19 @@
/* -*- 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
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * 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
+ * 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:
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
*/
@@ -21,63 +26,75 @@
#define _LIBMM_INSIDE_MMCLI
#include <libmm-glib.h>
-void mmcli_get_manager (GDBusConnection *connection,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMManager *mmcli_get_manager_finish (GAsyncResult *res);
-MMManager *mmcli_get_manager_sync (GDBusConnection *connection);
+void mmcli_get_manager (GDBusConnection *connection,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMManager *mmcli_get_manager_finish (GAsyncResult *res);
+MMManager *mmcli_get_manager_sync (GDBusConnection *connection);
+void mmcli_get_modem (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMObject *mmcli_get_modem_finish (GAsyncResult *res,
+ MMManager **o_manager);
+MMObject *mmcli_get_modem_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **o_manager);
-void mmcli_get_modem (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMObject *mmcli_get_modem_finish (GAsyncResult *res,
- MMManager **o_manager);
-MMObject *mmcli_get_modem_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **o_manager);
+void mmcli_get_bearer (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBearer *mmcli_get_bearer_finish (GAsyncResult *res,
+ MMManager **manager,
+ MMObject **object);
+MMBearer *mmcli_get_bearer_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **manager,
+ MMObject **object);
-void mmcli_get_bearer (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMBearer *mmcli_get_bearer_finish (GAsyncResult *res,
- MMManager **manager,
- MMObject **object);
-MMBearer *mmcli_get_bearer_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **manager,
- MMObject **object);
+void mmcli_get_sim (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMSim *mmcli_get_sim_finish (GAsyncResult *res,
+ MMManager **manager,
+ MMObject **object);
+MMSim *mmcli_get_sim_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **manager,
+ MMObject **object);
-void mmcli_get_sim (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMSim *mmcli_get_sim_finish (GAsyncResult *res,
- MMManager **manager,
- MMObject **object);
-MMSim *mmcli_get_sim_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **manager,
- MMObject **object);
+void mmcli_get_sms (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMSms *mmcli_get_sms_finish (GAsyncResult *res,
+ MMManager **manager,
+ MMObject **object);
+MMSms *mmcli_get_sms_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **manager,
+ MMObject **object);
-void mmcli_get_sms (GDBusConnection *connection,
- const gchar *path_or_index,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMSms *mmcli_get_sms_finish (GAsyncResult *res,
- MMManager **manager,
- MMObject **object);
-MMSms *mmcli_get_sms_sync (GDBusConnection *connection,
- const gchar *path_or_index,
- MMManager **manager,
- MMObject **object);
+void mmcli_get_call (GDBusConnection *connection,
+ const gchar *str,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMCall *mmcli_get_call_finish (GAsyncResult *res,
+ MMManager **manager,
+ MMObject **object);
+MMCall *mmcli_get_call_sync (GDBusConnection *connection,
+ const gchar *str,
+ MMManager **manager,
+ MMObject **object);
const gchar *mmcli_get_state_reason_string (MMModemStateChangeReason reason);
@@ -86,6 +103,7 @@ const gchar *mmcli_get_common_modem_string (void);
const gchar *mmcli_get_common_bearer_string (void);
const gchar *mmcli_get_common_sim_string (void);
const gchar *mmcli_get_common_sms_string (void);
+const gchar *mmcli_get_common_call_string (void);
gchar *mmcli_prefix_newlines (const gchar *prefix,
const gchar *str);
diff --git a/cli/mmcli-completion b/cli/mmcli-completion
new file mode 100644
index 00000000..33398949
--- /dev/null
+++ b/cli/mmcli-completion
@@ -0,0 +1,175 @@
+# mmcli(1) completion -*- shell-script -*-
+
+_mmcli()
+{
+ local cur prev words cword split
+ _init_completion -s || return
+
+ case $prev in
+ '-G'|'--set-logging')
+ COMPREPLY=( $(compgen -W "[ERR,WARN,INFO,DEBUG]" -- $cur) )
+ return 0
+ ;;
+ '-m'|'--modem')
+ COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) )
+ return 0
+ ;;
+ '-b'|'--bearer')
+ COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) )
+ return 0
+ ;;
+ '-i'|'--sim')
+ COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) )
+ return 0
+ ;;
+ '-s'|'--sms')
+ COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) )
+ return 0
+ ;;
+ '--factory-reset')
+ COMPREPLY=( $(compgen -W "[CODE]" -- $cur) )
+ return 0
+ ;;
+ '--command')
+ COMPREPLY=( $(compgen -W "[COMMAND]" -- $cur) )
+ return 0
+ ;;
+ '--create-bearer')
+ COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) )
+ return 0
+ ;;
+ '--delete-bearer')
+ COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) )
+ return 0
+ ;;
+ '--set-current-capabilities')
+ COMPREPLY=( $(compgen -W "[CAPABILITY1|CAPABILITY2...]" -- $cur) )
+ return 0
+ ;;
+ '--set-allowed-modes')
+ COMPREPLY=( $(compgen -W "[MODE1|MODE2...]" -- $cur) )
+ return 0
+ ;;
+ '--set-preferred-mode')
+ COMPREPLY=( $(compgen -W "[MODE]" -- $cur) )
+ return 0
+ ;;
+ '--set-current-bands')
+ COMPREPLY=( $(compgen -W "[BAND1|BAND2...]" -- $cur) )
+ return 0
+ ;;
+ '--3gpp-register-in-operator')
+ COMPREPLY=( $(compgen -W "[MCCMNC]" -- $cur) )
+ return 0
+ ;;
+ '--3gpp-ussd-initiate')
+ COMPREPLY=( $(compgen -W "[command]" -- $cur) )
+ return 0
+ ;;
+ '--3gpp-ussd-respond')
+ COMPREPLY=( $(compgen -W "[response]" -- $cur) )
+ return 0
+ ;;
+ '--3gpp-disable-facility-lock')
+ COMPREPLY=( $(compgen -W "[FACILITY,CONTROL_KEY]" -- $cur) )
+ return 0
+ ;;
+ '--cdma-activate')
+ COMPREPLY=( $(compgen -W "[CARRIER]" -- $cur) )
+ return 0
+ ;;
+ '--cdma-activate-manual')
+ COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) )
+ return 0
+ ;;
+ '--cdma-activate-manual-with-prl-file')
+ _filedir
+ return 0
+ ;;
+ '--simple-connect')
+ COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) )
+ return 0
+ ;;
+ '--location-set-supl-server')
+ COMPREPLY=( $(compgen -W "[IP:PORT|URL]" -- $cur) )
+ return 0
+ ;;
+ '--messaging-create-sms')
+ COMPREPLY=( $(compgen -W "[key=value,...]" -- $cur) )
+ return 0
+ ;;
+ '--messaging-create-sms-with-data')
+ _filedir
+ return 0
+ ;;
+ '--messaging-delete-sms')
+ COMPREPLY=( $(compgen -W "[PATH|INDEX]" -- $cur) )
+ return 0
+ ;;
+ '--firmware-select')
+ COMPREPLY=( $(compgen -W "[Unique-ID]" -- $cur) )
+ return 0
+ ;;
+ '--signal-setup')
+ COMPREPLY=( $(compgen -W "[Rate]" -- $cur) )
+ return 0
+ ;;
+ '--oma-setup')
+ COMPREPLY=( $(compgen -W "[FEATURE1|FEATURE2...]" -- $cur) )
+ return 0
+ ;;
+ '--oma-start-client-initiated-session')
+ COMPREPLY=( $(compgen -W "[Session-Type]" -- $cur) )
+ return 0
+ ;;
+ '--oma-accept-network-initiated-session')
+ COMPREPLY=( $(compgen -W "[Session-ID]" -- $cur) )
+ return 0
+ ;;
+ '--oma-reject-network-initiated-session')
+ COMPREPLY=( $(compgen -W "[Session-ID]" -- $cur) )
+ return 0
+ ;;
+ '--pin')
+ COMPREPLY=( $(compgen -W "[PIN]" -- $cur) )
+ return 0
+ ;;
+ '--puk')
+ COMPREPLY=( $(compgen -W "[PUK]" -- $cur) )
+ return 0
+ ;;
+ '--change-pin')
+ COMPREPLY=( $(compgen -W "[New-PIN]" -- $cur) )
+ return 0
+ ;;
+ '--store-in-storage')
+ COMPREPLY=( $(compgen -W "[Storage]" -- $cur) )
+ return 0
+ ;;
+ '--create-file-with-data')
+ _filedir
+ return 0
+ ;;
+ '--timeout')
+ COMPREPLY=( $(compgen -W "[SECONDS]" -- $cur) )
+ return 0
+ ;;
+ '-V'|'--version')
+ return 0
+ ;;
+ '-h'|'--help'|'--help-all'|'--help-manager'|'--help-common'|'--help-modem'|'--help-3gpp'|'--help-cdma'|'--help-simple'|'--help-location'|'--help-messaging'|'--help-time'|'--help-firmware'|'--help-signal'|'--help-oma'|'--help-sim'|'--help-bearer'|'--help-sms')
+ return 0
+ ;;
+ esac
+
+ $split && return 0
+
+ if [[ $cur == -* ]]; then
+ COMPREPLY=( $( compgen -W '$( _parse_help "$1" --help-all )' -- "$cur" ) )
+ [[ $COMPREPLY == *= ]] && compopt -o nospace
+ return 0
+ fi
+} &&
+complete -F _mmcli mmcli
+
+# ex: ts=4 sw=4 et filetype=sh
diff --git a/cli/mmcli-manager.c b/cli/mmcli-manager.c
index a420e644..70903f91 100644
--- a/cli/mmcli-manager.c
+++ b/cli/mmcli-manager.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -15,8 +15,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
* Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2011-2016 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
@@ -29,26 +29,45 @@
#include <glib.h>
#include <gio/gio.h>
+#if defined WITH_UDEV
+# include <gudev/gudev.h>
+#endif
+
#define _LIBMM_INSIDE_MMCLI
#include "libmm-glib.h"
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
MMManager *manager;
GCancellable *cancellable;
+#if defined WITH_UDEV
+ GUdevClient *udev;
+#endif
} Context;
static Context *ctx;
/* Options */
+static gboolean get_daemon_version_flag;
static gboolean list_modems_flag;
static gboolean monitor_modems_flag;
static gboolean scan_modems_flag;
static gchar *set_logging_str;
+static gchar *inhibit_device_str;
+static gchar *report_kernel_event_str;
+
+#if defined WITH_UDEV
+static gboolean report_kernel_event_auto_scan;
+#endif
static GOptionEntry entries[] = {
+ { "get-daemon-version", 'B', 0, G_OPTION_ARG_NONE, &get_daemon_version_flag,
+ "Get ModemManager daemon version",
+ NULL
+ },
{ "set-logging", 'G', 0, G_OPTION_ARG_STRING, &set_logging_str,
"Set logging level in the ModemManager daemon",
"[ERR,WARN,INFO,DEBUG]",
@@ -65,6 +84,20 @@ static GOptionEntry entries[] = {
"Request to re-scan looking for modems",
NULL
},
+ { "inhibit-device", 'I', 0, G_OPTION_ARG_STRING, &inhibit_device_str,
+ "Inhibit device given a unique device identifier",
+ "[UID]"
+ },
+ { "report-kernel-event", 'K', 0, G_OPTION_ARG_STRING, &report_kernel_event_str,
+ "Report kernel event",
+ "[\"key=value,...\"]"
+ },
+#if defined WITH_UDEV
+ { "report-kernel-event-auto-scan", 0, 0, G_OPTION_ARG_NONE, &report_kernel_event_auto_scan,
+ "Automatically report kernel events based on udev notifications",
+ NULL
+ },
+#endif
{ NULL }
};
@@ -75,7 +108,7 @@ mmcli_manager_get_option_group (void)
/* Status options */
group = g_option_group_new ("manager",
- "Manager options",
+ "Manager options:",
"Show manager options",
NULL,
NULL);
@@ -93,29 +126,54 @@ mmcli_manager_options_enabled (void)
if (checked)
return !!n_actions;
- n_actions = (list_modems_flag +
+ n_actions = (get_daemon_version_flag +
+ list_modems_flag +
monitor_modems_flag +
scan_modems_flag +
- !!set_logging_str);
+ !!set_logging_str +
+ !!inhibit_device_str +
+ !!report_kernel_event_str);
+
+#if defined WITH_UDEV
+ n_actions += report_kernel_event_auto_scan;
+#endif
if (n_actions > 1) {
g_printerr ("error: too many manager actions requested\n");
exit (EXIT_FAILURE);
}
- if (monitor_modems_flag)
+ if (get_daemon_version_flag)
+ mmcli_force_sync_operation ();
+ else if (monitor_modems_flag) {
+ if (mmcli_output_get () != MMC_OUTPUT_TYPE_HUMAN) {
+ g_printerr ("error: modem monitoring not available in keyvalue output\n");
+ exit (EXIT_FAILURE);
+ }
+ mmcli_force_async_operation ();
+ } else if (inhibit_device_str)
+ mmcli_force_async_operation ();
+
+#if defined WITH_UDEV
+ if (report_kernel_event_auto_scan)
mmcli_force_async_operation ();
+#endif
checked = TRUE;
return !!n_actions;
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
+#if defined WITH_UDEV
+ if (ctx->udev)
+ g_object_unref (ctx->udev);
+#endif
+
if (ctx->manager)
g_object_unref (ctx->manager);
if (ctx->cancellable)
@@ -126,7 +184,83 @@ context_free (Context *ctx)
void
mmcli_manager_shutdown (void)
{
- context_free (ctx);
+ context_free ();
+}
+
+static void
+inhibition_cancelled (GCancellable *cancellable)
+{
+ GError *error = NULL;
+
+ if (!mm_manager_uninhibit_device_sync (ctx->manager, inhibit_device_str, NULL, &error)) {
+ g_printerr ("error: couldn't uninhibit device: '%s'\n",
+ error ? error->message : "unknown error");
+ } else
+ g_print ("successfully uninhibited device with uid '%s'\n", inhibit_device_str);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+inhibit_device_ready (MMManager *manager,
+ GAsyncResult *result)
+{
+ GError *error = NULL;
+
+ if (!mm_manager_inhibit_device_finish (manager, result, &error)) {
+ g_printerr ("error: couldn't inhibit device: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully inhibited device with uid '%s'\n", inhibit_device_str);
+ g_print ("type Ctrl+C to abort this program and remove the inhibition\n");
+
+ g_cancellable_connect (ctx->cancellable,
+ G_CALLBACK (inhibition_cancelled),
+ NULL,
+ NULL);
+}
+
+static void
+report_kernel_event_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't report kernel event: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully reported kernel event\n");
+}
+
+static void
+report_kernel_event_ready (MMManager *manager,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_manager_report_kernel_event_finish (manager, result, &error);
+ report_kernel_event_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static MMKernelEventProperties *
+build_kernel_event_properties_from_input (const gchar *properties_string)
+{
+ GError *error = NULL;
+ MMKernelEventProperties *properties;
+
+ properties = mm_kernel_event_properties_new_from_string (properties_string, &error);
+ if (!properties) {
+ g_printerr ("error: cannot parse properties string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ return properties;
}
static void
@@ -187,58 +321,61 @@ scan_devices_ready (MMManager *manager,
mmcli_async_operation_done ();
}
+#define FOUND_ACTION_PREFIX " "
+#define ADDED_ACTION_PREFIX "(+) "
+#define REMOVED_ACTION_PREFIX "(-) "
+
static void
-print_modem_short_info (MMObject *modem)
+output_modem_info (MMObject *obj,
+ const gchar *prefix)
{
- const gchar *manufacturer, *model;
+ gchar *extra;
+ const gchar *manufacturer;
+ const gchar *model;
+ MMModem *modem;
- manufacturer = mm_modem_get_manufacturer (mm_object_peek_modem (modem));
- model = mm_modem_get_model (mm_object_peek_modem (modem));
+ modem = mm_object_peek_modem (obj);
+ if (!modem)
+ return;
- g_print ("\t%s [%s] %s\n",
- mm_object_get_path (modem),
- manufacturer ? manufacturer : "unknown",
- model ? model : "unknown");
+ manufacturer = mm_modem_get_manufacturer (modem);
+ model = mm_modem_get_model (modem);
+ extra = g_strdup_printf ("[%s] %s",
+ manufacturer ? manufacturer : "manufacturer unknown",
+ model ? model : "model unknown");
+ mmcli_output_listitem (MMC_F_MODEM_LIST_DBUS_PATH,
+ prefix,
+ mm_object_get_path (obj),
+ extra);
+ g_free (extra);
}
static void
device_added (MMManager *manager,
MMObject *modem)
{
- g_print ("Added modem:\n");
- print_modem_short_info (modem);
- fflush (stdout);
+ output_modem_info (modem, ADDED_ACTION_PREFIX);
+ mmcli_output_list_dump (MMC_F_MODEM_LIST_DBUS_PATH);
}
static void
device_removed (MMManager *manager,
MMObject *modem)
{
- g_print ("Removed modem:\n");
- print_modem_short_info (modem);
- fflush (stdout);
+ output_modem_info (modem, REMOVED_ACTION_PREFIX);
+ mmcli_output_list_dump (MMC_F_MODEM_LIST_DBUS_PATH);
}
static void
list_current_modems (MMManager *manager)
{
GList *modems;
+ GList *l;
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (ctx->manager));
-
- g_print ("\n");
- if (!modems)
- g_print ("No modems were found\n");
- else {
- GList *l;
-
- g_print ("Found %u modems:\n", g_list_length (modems));
- for (l = modems; l; l = g_list_next (l)) {
- print_modem_short_info (MM_OBJECT (l->data));
- }
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
- }
- g_print ("\n");
+ for (l = modems; l; l = g_list_next (l))
+ output_modem_info ((MMObject *)(l->data), FOUND_ACTION_PREFIX);
+ mmcli_output_list_dump (MMC_F_MODEM_LIST_DBUS_PATH);
}
static void
@@ -247,6 +384,26 @@ cancelled (GCancellable *cancellable)
mmcli_async_operation_done ();
}
+#if defined WITH_UDEV
+
+static void
+handle_uevent (GUdevClient *client,
+ const char *action,
+ GUdevDevice *device,
+ gpointer none)
+{
+ MMKernelEventProperties *properties;
+
+ properties = mm_kernel_event_properties_new ();
+ mm_kernel_event_properties_set_action (properties, action);
+ mm_kernel_event_properties_set_subsystem (properties, g_udev_device_get_subsystem (device));
+ mm_kernel_event_properties_set_name (properties, g_udev_device_get_name (device));
+ mm_manager_report_kernel_event (ctx->manager, properties, NULL, NULL, NULL);
+ g_object_unref (properties);
+}
+
+#endif
+
static void
get_manager_ready (GObject *source,
GAsyncResult *result,
@@ -276,6 +433,56 @@ get_manager_ready (GObject *source,
return;
}
+ /* Request to report kernel event? */
+ if (report_kernel_event_str) {
+ MMKernelEventProperties *properties;
+
+ properties = build_kernel_event_properties_from_input (report_kernel_event_str);
+ mm_manager_report_kernel_event (ctx->manager,
+ properties,
+ ctx->cancellable,
+ (GAsyncReadyCallback)report_kernel_event_ready,
+ NULL);
+ g_object_unref (properties);
+ return;
+ }
+
+#if defined WITH_UDEV
+ if (report_kernel_event_auto_scan) {
+ const gchar *subsys[] = { "tty", "usbmisc", "net", "rpmsg", "wwan", NULL };
+ guint i;
+
+ ctx->udev = g_udev_client_new (subsys);
+ g_signal_connect (ctx->udev, "uevent", G_CALLBACK (handle_uevent), NULL);
+
+ for (i = 0; subsys[i]; i++) {
+ GList *list, *iter;
+
+ list = g_udev_client_query_by_subsystem (ctx->udev, subsys[i]);
+ for (iter = list; iter; iter = g_list_next (iter)) {
+ MMKernelEventProperties *properties;
+ GUdevDevice *device;
+
+ device = G_UDEV_DEVICE (iter->data);
+ properties = mm_kernel_event_properties_new ();
+ mm_kernel_event_properties_set_action (properties, "add");
+ mm_kernel_event_properties_set_subsystem (properties, subsys[i]);
+ mm_kernel_event_properties_set_name (properties, g_udev_device_get_name (device));
+ mm_manager_report_kernel_event (ctx->manager, properties, NULL, NULL, NULL);
+ g_object_unref (properties);
+ }
+ g_list_free_full (list, g_object_unref);
+ }
+
+ /* If we get cancelled, operation done */
+ g_cancellable_connect (ctx->cancellable,
+ G_CALLBACK (cancelled),
+ NULL,
+ NULL);
+ return;
+ }
+#endif
+
/* Request to monitor modems? */
if (monitor_modems_flag) {
g_signal_connect (ctx->manager,
@@ -303,6 +510,16 @@ get_manager_ready (GObject *source,
return;
}
+ /* Request to inhibit device? */
+ if (inhibit_device_str) {
+ mm_manager_inhibit_device (ctx->manager,
+ inhibit_device_str,
+ ctx->cancellable,
+ (GAsyncReadyCallback)inhibit_device_ready,
+ NULL);
+ return;
+ }
+
g_warn_if_reached ();
}
@@ -332,10 +549,23 @@ mmcli_manager_run_synchronous (GDBusConnection *connection)
exit (EXIT_FAILURE);
}
+#if defined WITH_UDEV
+ if (report_kernel_event_auto_scan) {
+ g_printerr ("error: monitoring udev events cannot be done synchronously\n");
+ exit (EXIT_FAILURE);
+ }
+#endif
+
/* Initialize context */
ctx = g_new0 (Context, 1);
ctx->manager = mmcli_get_manager_sync (connection);
+ /* Get daemon version? */
+ if (get_daemon_version_flag) {
+ g_print ("ModemManager daemon %s running\n", mm_manager_get_version (ctx->manager));
+ return;
+ }
+
/* Setup operation timeout */
mmcli_force_operation_timeout (mm_manager_peek_proxy (ctx->manager));
@@ -362,6 +592,20 @@ mmcli_manager_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Request to report kernel event? */
+ if (report_kernel_event_str) {
+ MMKernelEventProperties *properties;
+ gboolean result;
+
+ properties = build_kernel_event_properties_from_input (report_kernel_event_str);
+ result = mm_manager_report_kernel_event_sync (ctx->manager,
+ properties,
+ NULL,
+ &error);
+ report_kernel_event_process_reply (result, error);
+ return;
+ }
+
/* Request to list modems? */
if (list_modems_flag) {
list_current_modems (ctx->manager);
diff --git a/cli/mmcli-modem-3gpp-profile-manager.c b/cli/mmcli-modem-3gpp-profile-manager.c
new file mode 100644
index 00000000..d0e563aa
--- /dev/null
+++ b/cli/mmcli-modem-3gpp-profile-manager.c
@@ -0,0 +1,373 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mmcli.h"
+#include "mmcli-common.h"
+#include "mmcli-output.h"
+
+/* Context */
+typedef struct {
+ MMManager *manager;
+ GCancellable *cancellable;
+ MMObject *object;
+ MMModem3gppProfileManager *modem_3gpp_profile_manager;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean list_flag;
+static gchar *set_str;
+static gint delete_int = MM_3GPP_PROFILE_ID_UNKNOWN;
+
+static GOptionEntry entries[] = {
+ { "3gpp-profile-manager-list", 0, 0, G_OPTION_ARG_NONE, &list_flag,
+ "List available profiles",
+ NULL
+ },
+ { "3gpp-profile-manager-set", 0, 0, G_OPTION_ARG_STRING, &set_str,
+ "Create or update a profile with the given settings.",
+ "[\"key=value,...\"]"
+ },
+ { "3gpp-profile-manager-delete", 0, 0, G_OPTION_ARG_INT, &delete_int,
+ "Delete the profile with the given ID",
+ "[Profile ID]"
+ },
+ { NULL }
+};
+
+GOptionGroup *
+mmcli_modem_3gpp_profile_manager_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ group = g_option_group_new ("3gpp-profile-manager",
+ "3GPP profile management options:",
+ "Show 3GPP profile management related options",
+ NULL,
+ NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+gboolean
+mmcli_modem_3gpp_profile_manager_options_enabled (void)
+{
+ static guint n_actions = 0;
+ static gboolean checked = FALSE;
+
+ if (checked)
+ return !!n_actions;
+
+ n_actions = (list_flag +
+ !!set_str +
+ (delete_int != MM_3GPP_PROFILE_ID_UNKNOWN));
+
+ if (n_actions > 1) {
+ g_printerr ("error: too many 3GPP profile management actions requested\n");
+ exit (EXIT_FAILURE);
+ }
+
+ checked = TRUE;
+ return !!n_actions;
+}
+
+static void
+context_free (void)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ if (ctx->modem_3gpp_profile_manager)
+ g_object_unref (ctx->modem_3gpp_profile_manager);
+ if (ctx->object)
+ g_object_unref (ctx->object);
+ if (ctx->manager)
+ g_object_unref (ctx->manager);
+ g_free (ctx);
+}
+
+static void
+ensure_modem_3gpp_profile_manager (void)
+{
+ if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) {
+ g_printerr ("error: modem not enabled yet\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (!ctx->modem_3gpp_profile_manager) {
+ g_printerr ("error: modem has no 3GPP profile management capabilities\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Success */
+}
+
+void
+mmcli_modem_3gpp_profile_manager_shutdown (void)
+{
+ context_free ();
+}
+
+static void
+delete_process_reply (gboolean result,
+ const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't delete profile: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully deleted the profile\n");
+}
+
+static void
+delete_ready (MMModem3gppProfileManager *modem_3gpp_profile_manager,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_3gpp_profile_manager_delete_finish (modem_3gpp_profile_manager, result, &error);
+ delete_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+set_process_reply (MM3gppProfile *stored,
+ const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't set profile: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ mmcli_output_profile_set (stored);
+ mmcli_output_dump ();
+
+ g_object_unref (stored);
+}
+
+static void
+set_ready (MMModem3gppProfileManager *modem_3gpp_profile_manager,
+ GAsyncResult *result)
+{
+ MM3gppProfile *stored;
+ GError *error = NULL;
+
+ stored = mm_modem_3gpp_profile_manager_set_finish (modem_3gpp_profile_manager, result, &error);
+ set_process_reply (stored, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+list_process_reply (GList *result,
+ const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't list profiles: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ mmcli_output_profile_list (result);
+ mmcli_output_dump ();
+
+ g_list_free_full (result, g_object_unref);
+}
+
+static void
+list_ready (MMModem3gppProfileManager *modem_3gpp_profile_manager,
+ GAsyncResult *result)
+{
+ GError *error = NULL;
+ GList *profiles = NULL;
+
+ mm_modem_3gpp_profile_manager_list_finish (modem_3gpp_profile_manager, result, &profiles, &error);
+ list_process_reply (profiles, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+get_modem_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer none)
+{
+ ctx->object = mmcli_get_modem_finish (result, &ctx->manager);
+ ctx->modem_3gpp_profile_manager = mm_object_get_modem_3gpp_profile_manager (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_3gpp_profile_manager)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_profile_manager));
+
+ ensure_modem_3gpp_profile_manager ();
+
+ /* Request to list? */
+ if (list_flag) {
+ g_debug ("Asynchronously listing profiles...");
+ mm_modem_3gpp_profile_manager_list (ctx->modem_3gpp_profile_manager,
+ ctx->cancellable,
+ (GAsyncReadyCallback)list_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to set? */
+ if (set_str) {
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) requested = NULL;
+
+ g_debug ("Asynchronously setting profiles...");
+ requested = mm_3gpp_profile_new_from_string (set_str, &error);
+ if (!requested) {
+ g_printerr ("Error parsing profile string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ mm_modem_3gpp_profile_manager_set (ctx->modem_3gpp_profile_manager,
+ requested,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to delete? */
+ if (delete_int != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ g_debug ("Asynchronously deleting profile...");
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, delete_int);
+ mm_modem_3gpp_profile_manager_delete (ctx->modem_3gpp_profile_manager,
+ profile,
+ ctx->cancellable,
+ (GAsyncReadyCallback)delete_ready,
+ NULL);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
+
+void
+mmcli_modem_3gpp_profile_manager_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable)
+{
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ if (cancellable)
+ ctx->cancellable = g_object_ref (cancellable);
+
+ /* Get proper modem */
+ mmcli_get_modem (connection,
+ mmcli_get_common_modem_string (),
+ cancellable,
+ (GAsyncReadyCallback)get_modem_ready,
+ NULL);
+}
+
+void
+mmcli_modem_3gpp_profile_manager_run_synchronous (GDBusConnection *connection)
+{
+ GError *error = NULL;
+
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ ctx->object = mmcli_get_modem_sync (connection,
+ mmcli_get_common_modem_string (),
+ &ctx->manager);
+ ctx->modem_3gpp_profile_manager = mm_object_get_modem_3gpp_profile_manager (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_3gpp_profile_manager)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_profile_manager));
+
+ ensure_modem_3gpp_profile_manager ();
+
+ /* Request to list? */
+ if (list_flag) {
+ GList *profiles;
+
+ g_debug ("Synchronously listing profiles...");
+ mm_modem_3gpp_profile_manager_list_sync (ctx->modem_3gpp_profile_manager,
+ ctx->cancellable,
+ &profiles,
+ &error);
+ list_process_reply (profiles, error);
+ return;
+ }
+
+ /* Request to set? */
+ if (set_str) {
+ g_autoptr(MM3gppProfile) requested = NULL;
+ MM3gppProfile *stored;
+
+ g_debug ("Synchronously setting profile...");
+ requested = mm_3gpp_profile_new_from_string (set_str, &error);
+ if (!requested) {
+ g_printerr ("Error parsing profile string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ stored = mm_modem_3gpp_profile_manager_set_sync (ctx->modem_3gpp_profile_manager,
+ requested,
+ ctx->cancellable,
+ &error);
+ set_process_reply (stored, error);
+ return;
+ }
+
+ /* Request to delete? */
+ if (delete_int != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_autoptr(MM3gppProfile) profile = NULL;
+ gboolean result;
+
+ g_debug ("Synchronously deleting profile...");
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, delete_int);
+ result = mm_modem_3gpp_profile_manager_delete_sync (ctx->modem_3gpp_profile_manager,
+ profile,
+ ctx->cancellable,
+ &error);
+ delete_process_reply (result, error);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
diff --git a/cli/mmcli-modem-3gpp-ussd.c b/cli/mmcli-modem-3gpp-ussd.c
new file mode 100644
index 00000000..29b36e54
--- /dev/null
+++ b/cli/mmcli-modem-3gpp-ussd.c
@@ -0,0 +1,364 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2011 - 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2012 - 2021 Google, Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mmcli.h"
+#include "mmcli-common.h"
+#include "mmcli-output.h"
+
+/* Context */
+typedef struct {
+ MMManager *manager;
+ GCancellable *cancellable;
+ MMObject *object;
+ MMModem3gppUssd *modem_3gpp_ussd;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean ussd_status_flag;
+static gchar *ussd_initiate_str;
+static gchar *ussd_respond_str;
+static gboolean ussd_cancel_flag;
+
+static GOptionEntry entries[] = {
+ { "3gpp-ussd-status", 0, 0, G_OPTION_ARG_NONE, &ussd_status_flag,
+ "Show status of any ongoing USSD session",
+ NULL
+ },
+ { "3gpp-ussd-initiate", 0, 0, G_OPTION_ARG_STRING, &ussd_initiate_str,
+ "Request a given modem to initiate a USSD session",
+ "[command]"
+ },
+ { "3gpp-ussd-respond", 0, 0, G_OPTION_ARG_STRING, &ussd_respond_str,
+ "Request a given modem to respond to a USSD request",
+ "[response]"
+ },
+ { "3gpp-ussd-cancel", 0, 0, G_OPTION_ARG_NONE, &ussd_cancel_flag,
+ "Request to cancel any ongoing USSD session",
+ NULL
+ },
+ { NULL }
+};
+
+GOptionGroup *
+mmcli_modem_3gpp_ussd_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ group = g_option_group_new ("3gpp-ussd",
+ "3GPP USSD options:",
+ "Show 3GPP USSD related options",
+ NULL,
+ NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+gboolean
+mmcli_modem_3gpp_ussd_options_enabled (void)
+{
+ static guint n_actions = 0;
+ static gboolean checked = FALSE;
+
+ if (checked)
+ return !!n_actions;
+
+ n_actions = (ussd_status_flag +
+ !!ussd_initiate_str +
+ !!ussd_respond_str +
+ ussd_cancel_flag);
+
+ if (n_actions > 1) {
+ g_printerr ("error: too many 3GPP USSD actions requested\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* USSD initiate and respond will wait for URCs to get finished, so
+ * these are truly async. */
+ if (ussd_initiate_str || ussd_respond_str)
+ mmcli_force_async_operation ();
+
+ if (ussd_status_flag)
+ mmcli_force_sync_operation ();
+
+ checked = TRUE;
+ return !!n_actions;
+}
+
+static void
+context_free (void)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ if (ctx->modem_3gpp_ussd)
+ g_object_unref (ctx->modem_3gpp_ussd);
+ if (ctx->object)
+ g_object_unref (ctx->object);
+ if (ctx->manager)
+ g_object_unref (ctx->manager);
+ g_free (ctx);
+}
+
+static void
+ensure_modem_3gpp_ussd (void)
+{
+ if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) {
+ g_printerr ("error: modem not enabled yet\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (!ctx->modem_3gpp_ussd) {
+ g_printerr ("error: modem has no 3GPP USSD capabilities\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Success */
+}
+
+void
+mmcli_modem_3gpp_ussd_shutdown (void)
+{
+ context_free ();
+}
+
+static void
+print_ussd_status (void)
+{
+ mmcli_output_string (MMC_F_GENERAL_DBUS_PATH, mm_modem_3gpp_ussd_get_path (ctx->modem_3gpp_ussd));
+ mmcli_output_string (MMC_F_3GPP_USSD_STATUS, mm_modem_3gpp_ussd_session_state_get_string (
+ mm_modem_3gpp_ussd_get_state (ctx->modem_3gpp_ussd)));
+ mmcli_output_string (MMC_F_3GPP_USSD_NETWORK_REQUEST, mm_modem_3gpp_ussd_get_network_request (ctx->modem_3gpp_ussd));
+ mmcli_output_string (MMC_F_3GPP_USSD_NETWORK_NOTIFICATION, mm_modem_3gpp_ussd_get_network_notification (ctx->modem_3gpp_ussd));
+ mmcli_output_dump ();
+}
+
+static void
+ussd_initiate_process_reply (gchar *result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't initiate USSD session: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("USSD session initiated; "
+ "new reply from network: '%s'\n", result);
+ g_free (result);
+}
+
+static void
+ussd_initiate_ready (MMModem3gppUssd *modem_3gpp_ussd,
+ GAsyncResult *result)
+{
+ gchar *operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_3gpp_ussd_initiate_finish (modem_3gpp_ussd, result, &error);
+ ussd_initiate_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+ussd_respond_process_reply (gchar *result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't send response in USSD session: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("response successfully sent in USSD session; "
+ "new reply from network: '%s'\n", result);
+ g_free (result);
+}
+
+static void
+ussd_respond_ready (MMModem3gppUssd *modem_3gpp_ussd,
+ GAsyncResult *result)
+{
+ gchar *operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_3gpp_ussd_respond_finish (modem_3gpp_ussd, result, &error);
+ ussd_respond_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+ussd_cancel_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't cancel USSD session: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully cancelled USSD session\n");
+}
+
+static void
+ussd_cancel_ready (MMModem3gppUssd *modem_3gpp_ussd,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_3gpp_ussd_cancel_finish (modem_3gpp_ussd, result, &error);
+ ussd_cancel_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+get_modem_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer none)
+{
+ ctx->object = mmcli_get_modem_finish (result, &ctx->manager);
+ ctx->modem_3gpp_ussd = mm_object_get_modem_3gpp_ussd (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_3gpp_ussd)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_ussd));
+
+ ensure_modem_3gpp_ussd ();
+
+ if (ussd_status_flag)
+ g_assert_not_reached ();
+
+ /* Request to initiate USSD session? */
+ if (ussd_initiate_str) {
+ g_debug ("Asynchronously initiating USSD session...");
+ mm_modem_3gpp_ussd_initiate (ctx->modem_3gpp_ussd,
+ ussd_initiate_str,
+ ctx->cancellable,
+ (GAsyncReadyCallback)ussd_initiate_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to respond in USSD session? */
+ if (ussd_respond_str) {
+ g_debug ("Asynchronously sending response in USSD session...");
+ mm_modem_3gpp_ussd_respond (ctx->modem_3gpp_ussd,
+ ussd_respond_str,
+ ctx->cancellable,
+ (GAsyncReadyCallback)ussd_respond_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to cancel USSD session? */
+ if (ussd_cancel_flag) {
+ g_debug ("Asynchronously cancelling USSD session...");
+ mm_modem_3gpp_ussd_cancel (ctx->modem_3gpp_ussd,
+ ctx->cancellable,
+ (GAsyncReadyCallback)ussd_cancel_ready,
+ NULL);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
+
+void
+mmcli_modem_3gpp_ussd_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable)
+{
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ if (cancellable)
+ ctx->cancellable = g_object_ref (cancellable);
+
+ /* Get proper modem */
+ mmcli_get_modem (connection,
+ mmcli_get_common_modem_string (),
+ cancellable,
+ (GAsyncReadyCallback)get_modem_ready,
+ NULL);
+}
+
+void
+mmcli_modem_3gpp_ussd_run_synchronous (GDBusConnection *connection)
+{
+ GError *error = NULL;
+
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ ctx->object = mmcli_get_modem_sync (connection,
+ mmcli_get_common_modem_string (),
+ &ctx->manager);
+ ctx->modem_3gpp_ussd = mm_object_get_modem_3gpp_ussd (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_3gpp_ussd)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_ussd));
+
+ ensure_modem_3gpp_ussd ();
+
+ if (ussd_initiate_str)
+ g_assert_not_reached ();
+ if (ussd_respond_str)
+ g_assert_not_reached ();
+
+ /* Request to show USSD status? */
+ if (ussd_status_flag) {
+ g_debug ("Printing USSD status...");
+ print_ussd_status ();
+ return;
+ }
+
+ /* Request to cancel USSD session? */
+ if (ussd_cancel_flag) {
+ gboolean result;
+
+ g_debug ("Asynchronously cancelling USSD session...");
+ result = mm_modem_3gpp_ussd_cancel_sync (ctx->modem_3gpp_ussd,
+ NULL,
+ &error);
+ ussd_cancel_process_reply (result, error);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
diff --git a/cli/mmcli-modem-3gpp.c b/cli/mmcli-modem-3gpp.c
index 3c10be25..58bdcc83 100644
--- a/cli/mmcli-modem-3gpp.c
+++ b/cli/mmcli-modem-3gpp.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -15,8 +15,8 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * Copyright (C) 2011 - 2012 Aleksander Morgado <aleksander@gnu.org>
- * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2011 - 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2012 - 2021 Google, Inc.
*/
#include "config.h"
@@ -34,25 +34,25 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
- MMManager *manager;
+ MMManager *manager;
GCancellable *cancellable;
- MMObject *object;
- MMModem3gpp *modem_3gpp;
- MMModem3gppUssd *modem_3gpp_ussd;
+ MMObject *object;
+ MMModem3gpp *modem_3gpp;
} Context;
static Context *ctx;
/* Options */
-static gboolean scan_flag;
-static gboolean register_home_flag;
-static gchar *register_in_operator_str;
-static gboolean ussd_status_flag;
-static gchar *ussd_initiate_str;
-static gchar *ussd_respond_str;
-static gboolean ussd_cancel_flag;
+static gboolean scan_flag;
+static gboolean register_home_flag;
+static gchar *register_in_operator_str;
+static gchar *set_eps_ue_mode_operation_str;
+static gchar *set_initial_eps_bearer_settings_str;
+static gchar *disable_facility_lock_str;
+static gchar *set_packet_service_state_str;
static GOptionEntry entries[] = {
{ "3gpp-scan", 0, 0, G_OPTION_ARG_NONE, &scan_flag,
@@ -67,21 +67,21 @@ static GOptionEntry entries[] = {
"Request a given modem to register in the network of the given operator",
"[MCCMNC]"
},
- { "3gpp-ussd-status", 0, 0, G_OPTION_ARG_NONE, &ussd_status_flag,
- "Show status of any ongoing USSD session",
- NULL
+ { "3gpp-set-eps-ue-mode-operation", 0, 0, G_OPTION_ARG_STRING, &set_eps_ue_mode_operation_str,
+ "Set the UE mode of operation for EPS",
+ "[ps-1|ps-2|csps-1|csps-2]"
},
- { "3gpp-ussd-initiate", 0, 0, G_OPTION_ARG_STRING, &ussd_initiate_str,
- "Request a given modem to initiate a USSD session",
- "[command]"
+ { "3gpp-set-initial-eps-bearer-settings", 0, 0, G_OPTION_ARG_STRING, &set_initial_eps_bearer_settings_str,
+ "Set the initial EPS bearer settings",
+ "[\"key=value,...\"]"
},
- { "3gpp-ussd-respond", 0, 0, G_OPTION_ARG_STRING, &ussd_respond_str,
- "Request a given modem to respond to a USSD request",
- "[response]"
+ { "3gpp-disable-facility-lock", 0, 0, G_OPTION_ARG_STRING, &disable_facility_lock_str,
+ "Disable facility personalization",
+ "[facility,key]"
},
- { "3gpp-ussd-cancel", 0, 0, G_OPTION_ARG_NONE, &ussd_cancel_flag,
- "Request to cancel any ongoing USSD session",
- NULL
+ { "3gpp-set-packet-service-state", 0, 0, G_OPTION_ARG_STRING, &set_packet_service_state_str,
+ "Set packet service state",
+ "[attached|detached]"
},
{ NULL }
};
@@ -92,7 +92,7 @@ mmcli_modem_3gpp_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("3gpp",
- "3GPP options",
+ "3GPP options:",
"Show 3GPP related options",
NULL,
NULL);
@@ -104,7 +104,7 @@ mmcli_modem_3gpp_get_option_group (void)
gboolean
mmcli_modem_3gpp_options_enabled (void)
{
- static guint n_actions = 0;
+ static guint n_actions = 0;
static gboolean checked = FALSE;
if (checked)
@@ -113,10 +113,10 @@ mmcli_modem_3gpp_options_enabled (void)
n_actions = (scan_flag +
register_home_flag +
!!register_in_operator_str +
- ussd_status_flag +
- !!ussd_initiate_str +
- !!ussd_respond_str +
- ussd_cancel_flag);
+ !!set_eps_ue_mode_operation_str +
+ !!set_initial_eps_bearer_settings_str +
+ !!disable_facility_lock_str +
+ !!set_packet_service_state_str);
if (n_actions > 1) {
g_printerr ("error: too many 3GPP actions requested\n");
@@ -128,20 +128,12 @@ mmcli_modem_3gpp_options_enabled (void)
if (scan_flag)
mmcli_force_async_operation ();
- /* USSD initiate and respond will wait for URCs to get finished, so
- * these are truly async. */
- if (ussd_initiate_str || ussd_respond_str)
- mmcli_force_async_operation ();
-
- if (ussd_status_flag)
- mmcli_force_sync_operation ();
-
checked = TRUE;
return !!n_actions;
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -150,8 +142,6 @@ context_free (Context *ctx)
g_object_unref (ctx->cancellable);
if (ctx->modem_3gpp)
g_object_unref (ctx->modem_3gpp);
- if (ctx->modem_3gpp_ussd)
- g_object_unref (ctx->modem_3gpp_ussd);
if (ctx->object)
g_object_unref (ctx->object);
if (ctx->manager)
@@ -160,31 +150,21 @@ context_free (Context *ctx)
}
static void
-ensure_modem_3gpp (void)
+ensure_modem_enabled (void)
{
if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) {
g_printerr ("error: modem not enabled yet\n");
exit (EXIT_FAILURE);
}
- if (!ctx->modem_3gpp) {
- g_printerr ("error: modem has no 3GPP capabilities\n");
- exit (EXIT_FAILURE);
- }
-
/* Success */
}
static void
-ensure_modem_3gpp_ussd (void)
+ensure_modem_3gpp (void)
{
- if (mm_modem_get_unlock_required (mm_object_peek_modem (ctx->object)) != MM_MODEM_LOCK_NONE) {
- g_printerr ("error: modem not unlocked yet\n");
- exit (EXIT_FAILURE);
- }
-
- if (!ctx->modem_3gpp_ussd) {
- g_printerr ("error: modem has no USSD capabilities\n");
+ if (!ctx->modem_3gpp) {
+ g_printerr ("error: modem has no 3GPP capabilities\n");
exit (EXIT_FAILURE);
}
@@ -194,39 +174,11 @@ ensure_modem_3gpp_ussd (void)
void
mmcli_modem_3gpp_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
-print_network_info (MMModem3gppNetwork *network)
-{
- const gchar *name;
- gchar *access_technologies;
-
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE
-#define VALIDATE(str) (str ? str : "unknown")
-
- access_technologies = (mm_modem_access_technology_build_string_from_mask (
- mm_modem_3gpp_network_get_access_technology (network)));
-
- /* Prefer long name */
- name = mm_modem_3gpp_network_get_operator_long (network);
- if (!name)
- name = mm_modem_3gpp_network_get_operator_short (network);
-
- g_print ("%s - %s (%s, %s)\n",
- VALIDATE (mm_modem_3gpp_network_get_operator_code (network)),
- VALIDATE (name),
- access_technologies,
- mm_modem_3gpp_network_availability_get_string (
- mm_modem_3gpp_network_get_availability (network)));
- g_free (access_technologies);
-}
-
-static void
-scan_process_reply (GList *result,
+scan_process_reply (GList *result,
const GError *error)
{
if (!result) {
@@ -235,27 +187,17 @@ scan_process_reply (GList *result,
exit (EXIT_FAILURE);
}
- g_print ("\n");
- if (!result)
- g_print ("No networks were found\n");
- else {
- GList *l;
+ mmcli_output_scan_networks (result);
+ mmcli_output_dump ();
- g_print ("Found %u networks:\n", g_list_length (result));
- for (l = result; l; l = g_list_next (l)) {
- print_network_info ((MMModem3gppNetwork *)(l->data));
- }
- g_list_free_full (result, (GDestroyNotify) mm_modem_3gpp_network_free);
- }
- g_print ("\n");
+ g_list_free_full (result, (GDestroyNotify) mm_modem_3gpp_network_free);
}
static void
scan_ready (MMModem3gpp *modem_3gpp,
- GAsyncResult *result,
- gpointer nothing)
+ GAsyncResult *result)
{
- GList *operation_result;
+ GList *operation_result;
GError *error = NULL;
operation_result = mm_modem_3gpp_scan_finish (modem_3gpp, result, &error);
@@ -265,7 +207,7 @@ scan_ready (MMModem3gpp *modem_3gpp,
}
static void
-register_process_reply (gboolean result,
+register_process_reply (gboolean result,
const GError *error)
{
if (!result) {
@@ -279,11 +221,10 @@ register_process_reply (gboolean result,
static void
register_ready (MMModem3gpp *modem_3gpp,
- GAsyncResult *result,
- gpointer nothing)
+ GAsyncResult *result)
{
- gboolean operation_result;
- GError *error = NULL;
+ gboolean operation_result;
+ GError *error = NULL;
operation_result = mm_modem_3gpp_register_finish (modem_3gpp, result, &error);
register_process_reply (operation_result, error);
@@ -292,130 +233,196 @@ register_ready (MMModem3gpp *modem_3gpp,
}
static void
-print_ussd_status (void)
+set_initial_eps_bearer_settings_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't set initial EPS bearer properties: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Successfully set initial EPS bearer properties\n");
+}
+
+static void
+set_initial_eps_bearer_settings_ready (MMModem3gpp *modem,
+ GAsyncResult *res)
{
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE
-#define VALIDATE(str) (str ? str : "none")
-
- g_print ("\n"
- "%s\n"
- " ----------------------------\n"
- " USSD | status: '%s'\n"
- " | network request: '%s'\n"
- " | network notification: '%s'\n",
- mm_modem_3gpp_ussd_get_path (ctx->modem_3gpp_ussd),
- mm_modem_3gpp_ussd_session_state_get_string (
- mm_modem_3gpp_ussd_get_state (ctx->modem_3gpp_ussd)),
- VALIDATE (mm_modem_3gpp_ussd_get_network_request (ctx->modem_3gpp_ussd)),
- VALIDATE (mm_modem_3gpp_ussd_get_network_notification (ctx->modem_3gpp_ussd)));
+ gboolean result;
+ GError *error = NULL;
+
+ result = mm_modem_3gpp_set_initial_eps_bearer_settings_finish (modem, res, &error);
+ set_initial_eps_bearer_settings_process_reply (result, error);
+
+ mmcli_async_operation_done ();
}
static void
-ussd_initiate_process_reply (gchar *result,
- const GError *error)
+set_eps_ue_mode_operation_process_reply (gboolean result,
+ const GError *error)
{
if (!result) {
- g_printerr ("error: couldn't initiate USSD session: '%s'\n",
+ g_printerr ("error: couldn't set UE mode of operation for EPS: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
- g_print ("USSD session initiated; "
- "new reply from network: '%s'\n", result);
- g_free (result);
+ g_print ("successfully set UE mode of operation for EPS\n");
}
static void
-ussd_initiate_ready (MMModem3gppUssd *modem_3gpp_ussd,
- GAsyncResult *result,
- gpointer nothing)
+set_eps_ue_mode_operation_ready (MMModem3gpp *modem,
+ GAsyncResult *result)
{
- gchar *operation_result;
+ gboolean operation_result;
GError *error = NULL;
- operation_result = mm_modem_3gpp_ussd_initiate_finish (modem_3gpp_ussd, result, &error);
- ussd_initiate_process_reply (operation_result, error);
+ operation_result = mm_modem_3gpp_set_eps_ue_mode_operation_finish (modem, result, &error);
+ set_eps_ue_mode_operation_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void
-ussd_respond_process_reply (gchar *result,
- const GError *error)
+parse_eps_ue_mode_operation (MMModem3gppEpsUeModeOperation *uemode)
+{
+ GError *error = NULL;
+
+ *uemode = mm_common_get_eps_ue_mode_operation_from_string (set_eps_ue_mode_operation_str, &error);
+ if (error) {
+ g_printerr ("error: couldn't parse UE mode of operation for EPS: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+}
+
+static void
+disable_facility_lock_process_reply (gboolean result,
+ const GError *error)
{
if (!result) {
- g_printerr ("error: couldn't send response in USSD session: '%s'\n",
+ g_printerr ("error: couldn't disable facility lock: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
- g_print ("response successfully sent in USSD session; "
- "new reply from network: '%s'\n", result);
- g_free (result);
+ g_print ("successfully disabled facility lock\n");
+}
+
+static gboolean
+disable_facility_lock_parse_input (const gchar *str,
+ MMModem3gppFacility *out_facility,
+ gchar **out_control_key)
+{
+ g_auto(GStrv) properties = NULL;
+ MMModem3gppFacility facility;
+
+ properties = g_strsplit (str, ",", -1);
+ if (!properties || !properties[0] || !properties[1])
+ return FALSE;
+
+ /* Facilities is a bitmask, if 0 is returned we failed parsing */
+ facility = mm_common_get_3gpp_facility_from_string (properties[0], NULL);
+ if (!facility)
+ return FALSE;
+
+ *out_facility = facility;
+ *out_control_key = g_strdup (properties[1]);
+ return TRUE;
}
static void
-ussd_respond_ready (MMModem3gppUssd *modem_3gpp_ussd,
- GAsyncResult *result,
- gpointer nothing)
+disable_facility_lock_ready (MMModem3gpp *modem_3gpp,
+ GAsyncResult *result,
+ gpointer nothing)
{
- gchar *operation_result;
+ gboolean operation_result;
GError *error = NULL;
- operation_result = mm_modem_3gpp_ussd_respond_finish (modem_3gpp_ussd, result, &error);
- ussd_respond_process_reply (operation_result, error);
+ operation_result = mm_modem_3gpp_disable_facility_lock_finish (modem_3gpp, result, &error);
+ disable_facility_lock_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void
-ussd_cancel_process_reply (gboolean result,
- const GError *error)
+set_packet_service_state_process_reply (gboolean result,
+ const GError *error)
{
if (!result) {
- g_printerr ("error: couldn't cancel USSD session: '%s'\n",
+ g_printerr ("error: couldn't set packet service state: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
- g_print ("successfully cancelled USSD session\n");
+ g_print ("successfully set packet service state\n");
}
static void
-ussd_cancel_ready (MMModem3gppUssd *modem_3gpp_ussd,
- GAsyncResult *result,
- gpointer nothing)
+set_packet_service_state_ready (MMModem3gpp *modem_3gpp,
+ GAsyncResult *result,
+ gpointer nothing)
{
gboolean operation_result;
GError *error = NULL;
- operation_result = mm_modem_3gpp_ussd_cancel_finish (modem_3gpp_ussd, result, &error);
- ussd_cancel_process_reply (operation_result, error);
+ operation_result = mm_modem_3gpp_set_packet_service_state_finish (modem_3gpp, result, &error);
+ set_packet_service_state_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
+static gboolean
+set_packet_service_state_parse_input (const gchar *str,
+ MMModem3gppPacketServiceState *out_state)
+{
+ MMModem3gppPacketServiceState state;
+
+ state = mm_common_get_3gpp_packet_service_state_from_string (str, NULL);
+ if (state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN)
+ return FALSE;
+
+ *out_state = state;
+ return TRUE;
+}
+
static void
get_modem_ready (GObject *source,
- GAsyncResult *result,
- gpointer none)
+ GAsyncResult *result)
{
ctx->object = mmcli_get_modem_finish (result, &ctx->manager);
ctx->modem_3gpp = mm_object_get_modem_3gpp (ctx->object);
- ctx->modem_3gpp_ussd = mm_object_get_modem_3gpp_ussd (ctx->object);
/* Setup operation timeout */
if (ctx->modem_3gpp)
mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp));
- if (ctx->modem_3gpp_ussd)
- mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_ussd));
ensure_modem_3gpp ();
- if (ussd_status_flag)
- g_assert_not_reached ();
+ /* Request to disable facility lock */
+ if (disable_facility_lock_str) {
+ g_autofree gchar *control_key = NULL;
+ MMModem3gppFacility facility;
+
+ if (!disable_facility_lock_parse_input (disable_facility_lock_str,
+ &facility,
+ &control_key)) {
+ g_printerr ("Error parsing properties string.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Asynchronously disabling facility lock...");
+ mm_modem_3gpp_disable_facility_lock (ctx->modem_3gpp,
+ facility,
+ control_key,
+ ctx->cancellable,
+ (GAsyncReadyCallback)disable_facility_lock_ready,
+ NULL);
+ return;
+ }
+
+ ensure_modem_enabled ();
/* Request to scan networks? */
if (scan_flag) {
@@ -438,41 +445,58 @@ get_modem_ready (GObject *source,
return;
}
- /* Request to initiate USSD session? */
- if (ussd_initiate_str) {
- ensure_modem_3gpp_ussd ();
+ /* Request to set UE mode of operation for EPS? */
+ if (set_eps_ue_mode_operation_str) {
+ MMModem3gppEpsUeModeOperation uemode;
+
+ parse_eps_ue_mode_operation (&uemode);
- g_debug ("Asynchronously initiating USSD session...");
- mm_modem_3gpp_ussd_initiate (ctx->modem_3gpp_ussd,
- ussd_initiate_str,
- ctx->cancellable,
- (GAsyncReadyCallback)ussd_initiate_ready,
- NULL);
+ g_debug ("Asynchronously setting UE mode of operation for EPS...");
+ mm_modem_3gpp_set_eps_ue_mode_operation (ctx->modem_3gpp,
+ uemode,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_eps_ue_mode_operation_ready,
+ NULL);
return;
}
- /* Request to respond in USSD session? */
- if (ussd_respond_str) {
- ensure_modem_3gpp_ussd ();
+ /* Request to set initial EPS bearer properties? */
+ if (set_initial_eps_bearer_settings_str) {
+ GError *error = NULL;
+ MMBearerProperties *config;
- g_debug ("Asynchronously sending response in USSD session...");
- mm_modem_3gpp_ussd_respond (ctx->modem_3gpp_ussd,
- ussd_respond_str,
- ctx->cancellable,
- (GAsyncReadyCallback)ussd_respond_ready,
- NULL);
+ config = mm_bearer_properties_new_from_string (set_initial_eps_bearer_settings_str, &error);
+ if (!config) {
+ g_printerr ("Error parsing properties string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Asynchronously setting initial EPS bearer properties...");
+ mm_modem_3gpp_set_initial_eps_bearer_settings (ctx->modem_3gpp,
+ config,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_initial_eps_bearer_settings_ready,
+ NULL);
+ g_object_unref (config);
return;
+
}
- /* Request to cancel USSD session? */
- if (ussd_cancel_flag) {
- ensure_modem_3gpp_ussd ();
+ /* Request to set packet service state */
+ if (set_packet_service_state_str) {
+ MMModem3gppPacketServiceState state;
- g_debug ("Asynchronously cancelling USSD session...");
- mm_modem_3gpp_ussd_cancel (ctx->modem_3gpp_ussd,
- ctx->cancellable,
- (GAsyncReadyCallback)ussd_cancel_ready,
- NULL);
+ if (!set_packet_service_state_parse_input (set_packet_service_state_str, &state)) {
+ g_printerr ("Error parsing packet service state string.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Asynchronously setting packet service state...");
+ mm_modem_3gpp_set_packet_service_state (ctx->modem_3gpp,
+ state,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_packet_service_state_ready,
+ NULL);
return;
}
@@ -507,22 +531,40 @@ mmcli_modem_3gpp_run_synchronous (GDBusConnection *connection)
mmcli_get_common_modem_string (),
&ctx->manager);
ctx->modem_3gpp = mm_object_get_modem_3gpp (ctx->object);
- ctx->modem_3gpp_ussd = mm_object_get_modem_3gpp_ussd (ctx->object);
/* Setup operation timeout */
if (ctx->modem_3gpp)
mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp));
- if (ctx->modem_3gpp_ussd)
- mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_3gpp_ussd));
ensure_modem_3gpp ();
if (scan_flag)
g_assert_not_reached ();
- if (ussd_initiate_str)
- g_assert_not_reached ();
- if (ussd_respond_str)
- g_assert_not_reached ();
+
+ /* Request to remove carrier lock */
+ if (disable_facility_lock_str) {
+ g_autofree gchar *control_key = NULL;
+ MMModem3gppFacility facility;
+ gboolean result;
+
+ if (!disable_facility_lock_parse_input (disable_facility_lock_str,
+ &facility,
+ &control_key)) {
+ g_printerr ("Error parsing properties string.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Synchronously disabling facility lock...");
+ result = mm_modem_3gpp_disable_facility_lock_sync (ctx->modem_3gpp,
+ facility,
+ control_key,
+ NULL,
+ &error);
+ disable_facility_lock_process_reply (result, error);
+ return;
+ }
+
+ ensure_modem_enabled ();
/* Request to register the modem? */
if (register_in_operator_str || register_home_flag) {
@@ -538,26 +580,59 @@ mmcli_modem_3gpp_run_synchronous (GDBusConnection *connection)
return;
}
- /* Request to show USSD status? */
- if (ussd_status_flag) {
- ensure_modem_3gpp_ussd ();
+ /* Request to set UE mode of operation for EPS? */
+ if (set_eps_ue_mode_operation_str) {
+ MMModem3gppEpsUeModeOperation uemode;
+ gboolean result;
+
+ parse_eps_ue_mode_operation (&uemode);
- g_debug ("Printing USSD status...");
- print_ussd_status ();
+ g_debug ("Synchronously setting UE mode of operation for EPS...");
+ result = mm_modem_3gpp_set_eps_ue_mode_operation_sync (ctx->modem_3gpp,
+ uemode,
+ NULL,
+ &error);
+ set_eps_ue_mode_operation_process_reply (result, error);
return;
}
- /* Request to cancel USSD session? */
- if (ussd_cancel_flag) {
- gboolean result;
+ /* Request to set initial EPS bearer properties? */
+ if (set_initial_eps_bearer_settings_str) {
+ gboolean result;
+ MMBearerProperties *config;
+
+ config = mm_bearer_properties_new_from_string (set_initial_eps_bearer_settings_str, &error);
+ if (!config) {
+ g_printerr ("Error parsing properties string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
- ensure_modem_3gpp_ussd ();
+ g_debug ("Synchronously setting initial EPS bearer properties...");
+ result = mm_modem_3gpp_set_initial_eps_bearer_settings_sync (ctx->modem_3gpp,
+ config,
+ NULL,
+ &error);
+ set_initial_eps_bearer_settings_process_reply (result, error);
+ g_object_unref (config);
+ return;
+ }
+
+ /* Request to set packet service state */
+ if (set_packet_service_state_str) {
+ gboolean result;
+ MMModem3gppPacketServiceState state;
+
+ if (!set_packet_service_state_parse_input (set_packet_service_state_str, &state)) {
+ g_printerr ("Error parsing packet service state string.\n");
+ exit (EXIT_FAILURE);
+ }
- g_debug ("Asynchronously cancelling USSD session...");
- result = mm_modem_3gpp_ussd_cancel_sync (ctx->modem_3gpp_ussd,
- NULL,
- &error);
- ussd_cancel_process_reply (result, error);
+ g_debug ("Asynchronously setting packet service state...");
+ result = mm_modem_3gpp_set_packet_service_state_sync (ctx->modem_3gpp,
+ state,
+ NULL,
+ &error);
+ set_packet_service_state_process_reply (result, error);
return;
}
diff --git a/cli/mmcli-modem-cdma.c b/cli/mmcli-modem-cdma.c
index 9f669f89..cba33a1d 100644
--- a/cli/mmcli-modem-cdma.c
+++ b/cli/mmcli-modem-cdma.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -70,7 +70,7 @@ mmcli_modem_cdma_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("cdma",
- "CDMA options",
+ "CDMA options:",
"Show CDMA related options",
NULL,
NULL);
@@ -107,7 +107,7 @@ mmcli_modem_cdma_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -142,7 +142,7 @@ ensure_modem_cdma (void)
void
mmcli_modem_cdma_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
diff --git a/cli/mmcli-modem-firmware.c b/cli/mmcli-modem-firmware.c
index 11f47237..34ceb420 100644
--- a/cli/mmcli-modem-firmware.c
+++ b/cli/mmcli-modem-firmware.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -44,10 +45,15 @@ typedef struct {
static Context *ctx;
/* Options */
+static gboolean status_flag;
static gboolean list_flag;
static gchar *select_str;
static GOptionEntry entries[] = {
+ { "firmware-status", 0, 0, G_OPTION_ARG_NONE, &status_flag,
+ "Show status of firmware management.",
+ NULL
+ },
{ "firmware-list", 0, 0, G_OPTION_ARG_NONE, &list_flag,
"List firmware images installed in a given modem",
NULL
@@ -65,7 +71,7 @@ mmcli_modem_firmware_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("firmware",
- "Firmware options",
+ "Firmware options:",
"Show Firmware options",
NULL,
NULL);
@@ -83,7 +89,8 @@ mmcli_modem_firmware_options_enabled (void)
if (checked)
return !!n_actions;
- n_actions = (list_flag +
+ n_actions = (status_flag +
+ list_flag +
!!select_str);
if (n_actions > 1) {
@@ -91,12 +98,15 @@ mmcli_modem_firmware_options_enabled (void)
exit (EXIT_FAILURE);
}
+ if (status_flag)
+ mmcli_force_sync_operation ();
+
checked = TRUE;
return !!n_actions;
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -126,7 +136,50 @@ ensure_modem_firmware (void)
void
mmcli_modem_firmware_shutdown (void)
{
- context_free (ctx);
+ context_free ();
+}
+
+static void
+print_firmware_status (void)
+{
+ MMFirmwareUpdateSettings *update_settings;
+ gchar *method = NULL;
+ const gchar **device_ids = NULL;
+ const gchar *version = NULL;
+ const gchar *fastboot_at = NULL;
+
+ update_settings = mm_modem_firmware_peek_update_settings (ctx->modem_firmware);
+ if (update_settings) {
+ MMModemFirmwareUpdateMethod m;
+
+ m = mm_firmware_update_settings_get_method (update_settings);
+ if (m != MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) {
+ method = mm_modem_firmware_update_method_build_string_from_mask (m);
+ device_ids = mm_firmware_update_settings_get_device_ids (update_settings);
+ version = mm_firmware_update_settings_get_version (update_settings);
+ }
+
+ if (m & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT)
+ fastboot_at = mm_firmware_update_settings_get_fastboot_at (update_settings);
+ }
+
+ /* There's not much to print in this status info, and if the modem
+ * does not support any firmware update method, we would just be returning
+ * an empty response to the --firmware-status action. So, instead, just
+ * return an error message explicitly when in human output type.
+ * We can remove this error message as soon as there is some parameter
+ * that will always be printed.
+ */
+ if (!method && !fastboot_at && mmcli_output_get () == MMC_OUTPUT_TYPE_HUMAN) {
+ g_printerr ("error: firmware status unsupported\n");
+ exit (EXIT_FAILURE);
+ }
+
+ mmcli_output_string_list_take (MMC_F_FIRMWARE_METHOD, method);
+ mmcli_output_string_array (MMC_F_FIRMWARE_DEVICE_IDS, device_ids, TRUE);
+ mmcli_output_string (MMC_F_FIRMWARE_VERSION, version);
+ mmcli_output_string (MMC_F_FIRMWARE_FASTBOOT_AT, fastboot_at);
+ mmcli_output_dump ();
}
static void
@@ -143,48 +196,11 @@ list_process_reply (MMFirmwareProperties *selected,
exit (EXIT_FAILURE);
}
- g_print ("\n");
- if (!result) {
- g_print ("No firmware images were found\n");
- } else {
- GList *l;
- guint i;
-
- g_print ("Found %u firmware images:\n", g_list_length (result));
- for (l = result, i = 0; l; l = g_list_next (l), i++) {
- MMFirmwareProperties *props = MM_FIRMWARE_PROPERTIES (l->data);
-
- g_print ("\t[%u] %s%s\n"
- "\t\tType: '%s'\n",
- i,
- mm_firmware_properties_get_unique_id (props),
- ((selected &&
- g_str_equal (mm_firmware_properties_get_unique_id (props),
- mm_firmware_properties_get_unique_id (selected))) ?
- " (CURRENT)" : ""),
- mm_firmware_image_type_get_string (
- mm_firmware_properties_get_image_type (props)));
-
- if (mm_firmware_properties_get_image_type (props) == MM_FIRMWARE_IMAGE_TYPE_GOBI) {
- g_print ("\t\t[Gobi] PRI version: '%s'\n"
- "\t\t[Gobi] PRI info: '%s'\n"
- "\t\t[Gobi] Boot version: '%s'\n"
- "\t\t[Gobi] PRI Unique ID: '%s'\n"
- "\t\t[Gobi] Modem Unique ID: '%s'\n",
- VALIDATE_UNKNOWN (mm_firmware_properties_get_gobi_pri_version (props)),
- VALIDATE_UNKNOWN (mm_firmware_properties_get_gobi_pri_info (props)),
- VALIDATE_UNKNOWN (mm_firmware_properties_get_gobi_boot_version (props)),
- VALIDATE_UNKNOWN (mm_firmware_properties_get_gobi_pri_unique_id (props)),
- VALIDATE_UNKNOWN (mm_firmware_properties_get_gobi_modem_unique_id (props)));
- }
-
- g_object_unref (props);
- }
- g_list_free (result);
- }
+ mmcli_output_firmware_list (result, selected);
+ mmcli_output_dump ();
- if (selected)
- g_object_unref (selected);
+ g_list_free_full (result, g_object_unref);
+ g_clear_object (&selected);
}
static void
@@ -241,6 +257,9 @@ get_modem_ready (GObject *source,
ensure_modem_firmware ();
+ if (status_flag)
+ g_assert_not_reached ();
+
/* Request to list images? */
if (list_flag) {
g_debug ("Asynchronously listing firmware images in modem...");
@@ -300,6 +319,13 @@ mmcli_modem_firmware_run_synchronous (GDBusConnection *connection)
ensure_modem_firmware ();
+ /* Request to get firmware status? */
+ if (status_flag) {
+ g_debug ("Printing firmware status...");
+ print_firmware_status ();
+ return;
+ }
+
/* Request to list firmware images? */
if (list_flag) {
GList *installed = NULL;
@@ -318,7 +344,6 @@ mmcli_modem_firmware_run_synchronous (GDBusConnection *connection)
/* Request to select a given image? */
if (select_str) {
gboolean result;
- GError *error = NULL;
g_debug ("Synchronously selecting firmware image in modem...");
result = mm_modem_firmware_select_sync (ctx->modem_firmware,
diff --git a/cli/mmcli-modem-location.c b/cli/mmcli-modem-location.c
index e055a8eb..936ff425 100644
--- a/cli/mmcli-modem-location.c
+++ b/cli/mmcli-modem-location.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -16,7 +16,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2012 Google, Inc.
- * Copyright (C) 2012 Lanedo GmbH <aleksander@lanedo.com>
+ * Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2012-2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
@@ -34,6 +35,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -48,32 +50,39 @@ static Context *ctx;
static gboolean status_flag;
static gboolean enable_3gpp_flag;
static gboolean disable_3gpp_flag;
-static gboolean get_3gpp_flag;
-static gboolean enable_agps_flag;
-static gboolean disable_agps_flag;
+static gboolean enable_agps_msa_flag;
+static gboolean disable_agps_msa_flag;
+static gboolean enable_agps_msb_flag;
+static gboolean disable_agps_msb_flag;
static gboolean enable_gps_nmea_flag;
static gboolean disable_gps_nmea_flag;
-static gboolean get_gps_nmea_flag;
static gboolean enable_gps_raw_flag;
static gboolean disable_gps_raw_flag;
-static gboolean get_gps_raw_flag;
static gboolean enable_cdma_bs_flag;
static gboolean disable_cdma_bs_flag;
-static gboolean get_cdma_bs_flag;
static gboolean enable_gps_unmanaged_flag;
static gboolean disable_gps_unmanaged_flag;
-static gboolean get_all_flag;
+static gboolean set_enable_signal_flag;
+static gboolean set_disable_signal_flag;
+static gboolean get_flag;
+static gboolean monitor_flag;
static gchar *set_supl_server_str;
+static gchar *inject_assistance_data_str;
+static gchar *set_gps_refresh_rate_str;
static GOptionEntry entries[] = {
{ "location-status", 0, 0, G_OPTION_ARG_NONE, &status_flag,
"Show status of location gathering.",
NULL
},
- { "location-get", 0, 0, G_OPTION_ARG_NONE, &get_all_flag,
+ { "location-get", 0, 0, G_OPTION_ARG_NONE, &get_flag,
"Get all available location information.",
NULL
},
+ { "location-monitor", 0, 0, G_OPTION_ARG_NONE, &monitor_flag,
+ "Monitor all available location information.",
+ NULL
+ },
{ "location-enable-3gpp", 0, 0, G_OPTION_ARG_NONE, &enable_3gpp_flag,
"Enable 3GPP location gathering.",
NULL
@@ -82,16 +91,20 @@ static GOptionEntry entries[] = {
"Disable 3GPP location gathering.",
NULL
},
- { "location-get-3gpp", 0, 0, G_OPTION_ARG_NONE, &get_3gpp_flag,
- "Get 3GPP-based location.",
+ { "location-enable-agps-msa", 0, 0, G_OPTION_ARG_NONE, &enable_agps_msa_flag,
+ "Enable MSA A-GPS location gathering.",
NULL
},
- { "location-enable-agps", 0, 0, G_OPTION_ARG_NONE, &enable_agps_flag,
- "Enable A-GPS location gathering.",
+ { "location-disable-agps-msa", 0, 0, G_OPTION_ARG_NONE, &disable_agps_msa_flag,
+ "Disable MSA A-GPS location gathering.",
NULL
},
- { "location-disable-agps", 0, 0, G_OPTION_ARG_NONE, &disable_agps_flag,
- "Disable A-GPS location gathering.",
+ { "location-enable-agps-msb", 0, 0, G_OPTION_ARG_NONE, &enable_agps_msb_flag,
+ "Enable MSB A-GPS location gathering.",
+ NULL
+ },
+ { "location-disable-agps-msb", 0, 0, G_OPTION_ARG_NONE, &disable_agps_msb_flag,
+ "Disable MSB A-GPS location gathering.",
NULL
},
{ "location-enable-gps-nmea", 0, 0, G_OPTION_ARG_NONE, &enable_gps_nmea_flag,
@@ -102,10 +115,6 @@ static GOptionEntry entries[] = {
"Disable NMEA-based GPS location gathering.",
NULL
},
- { "location-get-gps-nmea", 0, 0, G_OPTION_ARG_NONE, &get_gps_nmea_flag,
- "Get NMEA GPS traces.",
- NULL
- },
{ "location-enable-gps-raw", 0, 0, G_OPTION_ARG_NONE, &enable_gps_raw_flag,
"Enable raw GPS location gathering.",
NULL
@@ -114,10 +123,6 @@ static GOptionEntry entries[] = {
"Disable raw GPS location gathering.",
NULL
},
- { "location-get-gps-raw", 0, 0, G_OPTION_ARG_NONE, &get_gps_raw_flag,
- "Get raw GPS location.",
- NULL
- },
{ "location-enable-cdma-bs", 0, 0, G_OPTION_ARG_NONE, &enable_cdma_bs_flag,
"Enable CDMA base station location gathering.",
NULL
@@ -126,10 +131,6 @@ static GOptionEntry entries[] = {
"Disable CDMA base station location gathering.",
NULL
},
- { "location-get-cdma-bs", 0, 0, G_OPTION_ARG_NONE, &get_cdma_bs_flag,
- "Get CDMA base station location.",
- NULL
- },
{ "location-enable-gps-unmanaged", 0, 0, G_OPTION_ARG_NONE, &enable_gps_unmanaged_flag,
"Enable unmanaged GPS location gathering.",
NULL
@@ -140,7 +141,23 @@ static GOptionEntry entries[] = {
},
{ "location-set-supl-server", 0, 0, G_OPTION_ARG_STRING, &set_supl_server_str,
"Set SUPL server address",
- "[IP:PORT] or [URL]"
+ "[IP:PORT] or [FQDN:PORT]"
+ },
+ { "location-inject-assistance-data", 0, 0, G_OPTION_ARG_FILENAME, &inject_assistance_data_str,
+ "Inject assistance data in the GNSS module",
+ "[PATH]"
+ },
+ { "location-set-gps-refresh-rate", 0, 0, G_OPTION_ARG_STRING, &set_gps_refresh_rate_str,
+ "Set GPS refresh rate in seconds, or 0 disable the explicit rate.",
+ "[RATE]"
+ },
+ { "location-set-enable-signal", 0, 0, G_OPTION_ARG_NONE, &set_enable_signal_flag,
+ "Enable location update signaling in DBus property.",
+ NULL
+ },
+ { "location-set-disable-signal", 0, 0, G_OPTION_ARG_NONE, &set_disable_signal_flag,
+ "Disable location update signaling in DBus property.",
+ NULL
},
{ NULL }
};
@@ -151,7 +168,7 @@ mmcli_modem_location_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("location",
- "Location options",
+ "Location options:",
"Show Location options",
NULL,
NULL);
@@ -160,6 +177,16 @@ mmcli_modem_location_get_option_group (void)
return group;
}
+#define any_location_setup_flag ( \
+ enable_3gpp_flag || disable_3gpp_flag || \
+ enable_agps_msa_flag || disable_agps_msa_flag || \
+ enable_agps_msb_flag || disable_agps_msb_flag || \
+ enable_gps_nmea_flag || disable_gps_nmea_flag || \
+ enable_gps_raw_flag || disable_gps_raw_flag || \
+ enable_cdma_bs_flag || disable_cdma_bs_flag || \
+ enable_gps_unmanaged_flag || disable_gps_unmanaged_flag || \
+ set_enable_signal_flag || set_disable_signal_flag)
+
gboolean
mmcli_modem_location_options_enabled (void)
{
@@ -169,48 +196,38 @@ mmcli_modem_location_options_enabled (void)
if (checked)
return !!n_actions;
- if ((enable_3gpp_flag && disable_3gpp_flag) ||
- (enable_agps_flag && disable_agps_flag) ||
- (enable_gps_nmea_flag && disable_gps_nmea_flag) ||
- (enable_gps_raw_flag && disable_gps_raw_flag) ||
+ if ((enable_3gpp_flag && disable_3gpp_flag) ||
+ (enable_agps_msa_flag && disable_agps_msa_flag) ||
+ (enable_agps_msb_flag && disable_agps_msb_flag) ||
+ (enable_gps_nmea_flag && disable_gps_nmea_flag) ||
+ (enable_gps_raw_flag && disable_gps_raw_flag) ||
(enable_gps_unmanaged_flag && disable_gps_unmanaged_flag) ||
- (enable_cdma_bs_flag && disable_cdma_bs_flag)) {
+ (enable_cdma_bs_flag && disable_cdma_bs_flag)) {
g_printerr ("error: cannot enable and disable the same source\n");
exit (EXIT_FAILURE);
}
- if (get_all_flag) {
- get_3gpp_flag = TRUE;
- get_gps_nmea_flag = TRUE;
- get_gps_raw_flag = TRUE;
- get_cdma_bs_flag = TRUE;
+ if (set_enable_signal_flag && set_disable_signal_flag) {
+ g_printerr ("error: cannot enable and disable location signaling\n");
+ exit (EXIT_FAILURE);
}
n_actions = (status_flag +
- !!(enable_3gpp_flag +
- disable_3gpp_flag +
- enable_agps_flag +
- disable_agps_flag +
- enable_gps_nmea_flag +
- disable_gps_nmea_flag +
- enable_gps_raw_flag +
- disable_gps_raw_flag +
- enable_cdma_bs_flag +
- disable_cdma_bs_flag +
- enable_gps_unmanaged_flag +
- disable_gps_unmanaged_flag) +
- !!(get_3gpp_flag +
- get_gps_nmea_flag +
- get_gps_raw_flag +
- get_cdma_bs_flag) +
- !!set_supl_server_str);
+ any_location_setup_flag +
+ get_flag +
+ monitor_flag +
+ !!set_supl_server_str +
+ !!inject_assistance_data_str +
+ !!set_gps_refresh_rate_str);
if (n_actions > 1) {
g_printerr ("error: too many Location actions requested\n");
exit (EXIT_FAILURE);
}
- if (status_flag)
+ if (monitor_flag)
+ mmcli_force_async_operation ();
+ else if (status_flag)
mmcli_force_sync_operation ();
checked = TRUE;
@@ -218,7 +235,7 @@ mmcli_modem_location_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -253,38 +270,52 @@ ensure_modem_location (void)
void
mmcli_modem_location_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
print_location_status (void)
{
- gchar *capabilities_str;
- gchar *enabled_str;
-
- capabilities_str = (mm_modem_location_source_build_string_from_mask (
- mm_modem_location_get_capabilities (ctx->modem_location)));
- enabled_str = (mm_modem_location_source_build_string_from_mask (
- mm_modem_location_get_enabled (ctx->modem_location)));
- g_print ("\n"
- "%s\n"
- " ----------------------------\n"
- " Location | capabilities: '%s'\n"
- " | enabled: '%s'\n"
- " | signals: '%s'\n",
- mm_modem_location_get_path (ctx->modem_location),
- capabilities_str,
- enabled_str,
- mm_modem_location_signals_location (ctx->modem_location) ? "yes" : "no");
-
- /* If A-GPS supported, show SUPL server setup */
- if (mm_modem_location_get_capabilities (ctx->modem_location) & MM_MODEM_LOCATION_SOURCE_AGPS)
- g_print (" ----------------------------\n"
- " A-GPS | SUPL server: '%s'\n",
- mm_modem_location_get_supl_server (ctx->modem_location));
-
- g_free (capabilities_str);
- g_free (enabled_str);
+ gchar *capabilities;
+ gchar *enabled;
+ gchar *gps_refresh_rate = NULL;
+ const gchar *gps_supl_server = NULL;
+ gchar *gps_assistance = NULL;
+ const gchar **gps_assistance_servers = NULL;
+
+ capabilities = (mm_modem_location_source_build_string_from_mask (
+ mm_modem_location_get_capabilities (ctx->modem_location)));
+ enabled = (mm_modem_location_source_build_string_from_mask (
+ mm_modem_location_get_enabled (ctx->modem_location)));
+
+ /* If GPS supported, show GPS refresh rate and supported assistance data */
+ if (mm_modem_location_get_capabilities (ctx->modem_location) & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) {
+ guint rate;
+ MMModemLocationAssistanceDataType mask;
+
+ rate = mm_modem_location_get_gps_refresh_rate (ctx->modem_location);
+ gps_refresh_rate = g_strdup_printf ("%u", rate);
+
+ /* If A-GPS supported, show SUPL server setup */
+ if (mm_modem_location_get_capabilities (ctx->modem_location) & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB))
+ gps_supl_server = mm_modem_location_get_supl_server (ctx->modem_location);
+
+ mask = mm_modem_location_get_supported_assistance_data (ctx->modem_location);
+ gps_assistance = mm_modem_location_assistance_data_type_build_string_from_mask (mask);
+
+ /* If any assistance data supported, show server list */
+ if (mask != MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE)
+ gps_assistance_servers = mm_modem_location_get_assistance_data_servers (ctx->modem_location);
+ }
+
+ mmcli_output_string_list_take (MMC_F_LOCATION_CAPABILITIES, capabilities);
+ mmcli_output_string_list_take (MMC_F_LOCATION_ENABLED, enabled);
+ mmcli_output_string (MMC_F_LOCATION_SIGNALS, mm_modem_location_signals_location (ctx->modem_location) ? "yes" : "no");
+ mmcli_output_string_take_typed (MMC_F_LOCATION_GPS_REFRESH_RATE, gps_refresh_rate, "seconds");
+ mmcli_output_string (MMC_F_LOCATION_GPS_SUPL_SERVER, gps_supl_server);
+ mmcli_output_string_list_take (MMC_F_LOCATION_GPS_ASSISTANCE, gps_assistance);
+ mmcli_output_string_array (MMC_F_LOCATION_GPS_ASSISTANCE_SERVERS, gps_assistance_servers, TRUE);
+ mmcli_output_dump ();
}
static void
@@ -339,6 +370,91 @@ set_supl_server_ready (MMModemLocation *modem_location,
mmcli_async_operation_done ();
}
+static gboolean
+parse_inject_assistance_data (guint8 **o_data,
+ gsize *o_data_size)
+{
+ gboolean result = FALSE;
+ GFile *file = NULL;
+ gchar *data;
+ gsize data_size;
+ GError *error = NULL;
+
+ file = g_file_new_for_commandline_arg (inject_assistance_data_str);
+
+ if (!g_file_load_contents (file, NULL, &data, &data_size, NULL, &error)) {
+ g_printerr ("error: cannot load file contents: %s\n", error->message);
+ goto out;
+ }
+
+ if (data_size == 0) {
+ g_printerr ("error: file is empty\n");
+ goto out;
+ }
+
+ *o_data = (guint8 *)data;
+ *o_data_size = data_size;
+ result = TRUE;
+
+out:
+ if (error)
+ g_error_free (error);
+ g_object_unref (file);
+ return result;
+}
+
+static void
+inject_assistance_data_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't inject assistance data: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully injected assistance data\n");
+}
+
+static void
+inject_assistance_data_ready (MMModemLocation *modem_location,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_location_inject_assistance_data_finish (modem_location, result, &error);
+ inject_assistance_data_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+set_gps_refresh_rate_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't set GPS refresh rate: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully set GPS refresh rate\n");
+}
+
+static void
+set_gps_refresh_rate_ready (MMModemLocation *modem_location,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_location_set_gps_refresh_rate_finish (modem_location, result, &error);
+ set_gps_refresh_rate_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
static MMModemLocationSource
build_sources_from_flags (void)
{
@@ -352,10 +468,15 @@ build_sources_from_flags (void)
if (disable_3gpp_flag)
sources &= ~MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI;
- if (enable_agps_flag)
- sources |= MM_MODEM_LOCATION_SOURCE_AGPS;
- if (disable_agps_flag)
- sources &= ~MM_MODEM_LOCATION_SOURCE_AGPS;
+ if (enable_agps_msa_flag)
+ sources |= MM_MODEM_LOCATION_SOURCE_AGPS_MSA;
+ if (disable_agps_msa_flag)
+ sources &= ~MM_MODEM_LOCATION_SOURCE_AGPS_MSA;
+
+ if (enable_agps_msb_flag)
+ sources |= MM_MODEM_LOCATION_SOURCE_AGPS_MSB;
+ if (disable_agps_msb_flag)
+ sources &= ~MM_MODEM_LOCATION_SOURCE_AGPS_MSB;
if (enable_gps_nmea_flag)
sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
@@ -380,6 +501,16 @@ build_sources_from_flags (void)
return sources;
}
+static gboolean
+build_signals_location_from_flags (void)
+{
+ if (set_enable_signal_flag)
+ return TRUE;
+ if (set_disable_signal_flag)
+ return FALSE;
+ return mm_modem_location_signals_location (ctx->modem_location);
+}
+
static void
get_location_process_reply (MMLocation3gpp *location_3gpp,
MMLocationGpsNmea *location_gps_nmea,
@@ -387,123 +518,102 @@ get_location_process_reply (MMLocation3gpp *location_3gpp,
MMLocationCdmaBs *location_cdma_bs,
const GError *error)
{
- /* First, check for failures */
- if (!get_all_flag) {
- gboolean exit_error = FALSE;
-
- if (get_3gpp_flag && !location_3gpp) {
- g_printerr ("error: couldn't get 3GPP-based location from the modem: '%s'\n",
- error ? error->message : "not available");
- exit_error = TRUE;
- }
-
- if (get_gps_nmea_flag && !location_gps_nmea) {
- g_printerr ("error: couldn't get NMEA GPS traces from the modem: '%s'\n",
- error ? error->message : "not available");
- exit_error = TRUE;
- }
-
- if (get_gps_raw_flag && !location_gps_raw) {
- g_printerr ("error: couldn't get raw GPS location from the modem: '%s'\n",
- error ? error->message : "not available");
- exit_error = TRUE;
- }
-
- if (get_cdma_bs_flag && !location_cdma_bs) {
- g_printerr ("error: couldn't get CDMA base station location from the modem: '%s'\n",
- error ? error->message : "not available");
- exit_error = TRUE;
- }
-
- if (exit_error)
- exit (EXIT_FAILURE);
- } else if (error) {
+ gchar **nmea = NULL;
+ gchar *mcc = NULL;
+ gchar *mnc = NULL;
+ gchar *lac = NULL;
+ gchar *tac = NULL;
+ gchar *cid = NULL;
+ const gchar *gps_utc = NULL;
+ gchar *gps_longitude = NULL;
+ gchar *gps_latitude = NULL;
+ gchar *gps_altitude = NULL;
+ gchar *cdma_bs_longitude = NULL;
+ gchar *cdma_bs_latitude = NULL;
+
+ if (error) {
g_printerr ("error: couldn't get location from the modem: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
- g_print ("\n"
- "%s\n",
- mm_modem_location_get_path (ctx->modem_location));
-
- if (get_3gpp_flag) {
- if (location_3gpp)
- g_print (" -------------------------\n"
- " 3GPP location | Mobile country code: '%u'\n"
- " | Mobile network code: '%u'\n"
- " | Location area code: '%lu'\n"
- " | Cell ID: '%lu'\n",
- mm_location_3gpp_get_mobile_country_code (location_3gpp),
- mm_location_3gpp_get_mobile_network_code (location_3gpp),
- mm_location_3gpp_get_location_area_code (location_3gpp),
- mm_location_3gpp_get_cell_id (location_3gpp));
- else
- g_print (" -------------------------\n"
- " 3GPP location | Not available\n");
- }
-
- if (get_gps_nmea_flag) {
- gchar *full = NULL;
+ if (location_3gpp) {
+ const gchar *operator_code;
- if (location_gps_nmea)
- full = mm_location_gps_nmea_build_full (location_gps_nmea);
+ operator_code = mm_location_3gpp_get_operator_code (location_3gpp);
+ mcc = g_strndup (operator_code ? operator_code : "0", 3);
+ mnc = g_strdup (operator_code ? operator_code + 3 : "0");
+ lac = g_strdup_printf ("%04lX", mm_location_3gpp_get_location_area_code (location_3gpp));
+ tac = g_strdup_printf ("%06lX", mm_location_3gpp_get_tracking_area_code (location_3gpp));
+ cid = g_strdup_printf ("%08lX", mm_location_3gpp_get_cell_id (location_3gpp));
+ }
- if (full) {
- gchar *prefixed;
+ if (location_gps_nmea)
+ nmea = mm_location_gps_nmea_get_traces (location_gps_nmea);
- prefixed = mmcli_prefix_newlines (" | ", full);
- g_print (" -------------------------\n"
- " GPS NMEA traces | %s\n",
- prefixed);
- g_free (prefixed);
- g_free (full);
- } else
- g_print (" -------------------------\n"
- " GPS NMEA traces | Not available\n");
+ if (location_gps_raw) {
+ gps_utc = mm_location_gps_raw_get_utc_time (location_gps_raw);
+ gps_longitude = g_strdup_printf ("%lf", mm_location_gps_raw_get_longitude (location_gps_raw));
+ gps_latitude = g_strdup_printf ("%lf", mm_location_gps_raw_get_latitude (location_gps_raw));
+ gps_altitude = g_strdup_printf ("%lf", mm_location_gps_raw_get_altitude (location_gps_raw));
}
- if (get_gps_raw_flag) {
- if (location_gps_raw)
- g_print (" -------------------------\n"
- " Raw GPS | UTC time: '%s'\n"
- " | Longitude: '%lf'\n"
- " | Latitude: '%lf'\n"
- " | Altitude: '%lf'\n",
- mm_location_gps_raw_get_utc_time (location_gps_raw),
- mm_location_gps_raw_get_longitude (location_gps_raw),
- mm_location_gps_raw_get_latitude (location_gps_raw),
- mm_location_gps_raw_get_altitude (location_gps_raw));
- else
- g_print (" -------------------------\n"
- " Raw GPS | Not available\n");
+ if (location_cdma_bs) {
+ cdma_bs_longitude = g_strdup_printf ("%lf", mm_location_cdma_bs_get_longitude (location_cdma_bs));
+ cdma_bs_latitude = g_strdup_printf ("%lf", mm_location_cdma_bs_get_latitude (location_cdma_bs));
}
- if (get_cdma_bs_flag) {
- if (location_cdma_bs)
- g_print (" -------------------------\n"
- " CDMA BS | Longitude: '%lf'\n"
- " | Latitude: '%lf'\n",
- mm_location_cdma_bs_get_longitude (location_cdma_bs),
- mm_location_cdma_bs_get_latitude (location_cdma_bs));
- else
- g_print (" -------------------------\n"
- " CDMA BS | Not available\n");
- }
+ mmcli_output_string_take (MMC_F_LOCATION_3GPP_MCC, mcc);
+ mmcli_output_string_take (MMC_F_LOCATION_3GPP_MNC, mnc);
+ mmcli_output_string_take (MMC_F_LOCATION_3GPP_LAC, lac);
+ mmcli_output_string_take (MMC_F_LOCATION_3GPP_TAC, tac);
+ mmcli_output_string_take (MMC_F_LOCATION_3GPP_CID, cid);
+ mmcli_output_string_array_multiline_take (MMC_F_LOCATION_GPS_NMEA, nmea);
+ mmcli_output_string (MMC_F_LOCATION_GPS_UTC, gps_utc);
+ mmcli_output_string_take (MMC_F_LOCATION_GPS_LONG, gps_longitude);
+ mmcli_output_string_take (MMC_F_LOCATION_GPS_LAT, gps_latitude);
+ mmcli_output_string_take (MMC_F_LOCATION_GPS_ALT, gps_altitude);
+ mmcli_output_string_take (MMC_F_LOCATION_CDMABS_LONG, cdma_bs_longitude);
+ mmcli_output_string_take (MMC_F_LOCATION_CDMABS_LAT, cdma_bs_latitude);
+ mmcli_output_dump ();
+
+ g_clear_object (&location_3gpp);
+ g_clear_object (&location_gps_nmea);
+ g_clear_object (&location_gps_raw);
+ g_clear_object (&location_cdma_bs);
+}
- if (location_3gpp)
- g_object_unref (location_3gpp);
- if (location_gps_nmea)
- g_object_unref (location_gps_nmea);
- if (location_gps_raw)
- g_object_unref (location_gps_raw);
- if (location_cdma_bs)
- g_object_unref (location_cdma_bs);
+static void
+cancelled (GCancellable *cancellable)
+{
+ mmcli_async_operation_done ();
+}
+
+static void
+print_signaled_location (MMModemLocation *modem_location)
+{
+ MMLocation3gpp *location_3gpp;
+ MMLocationGpsNmea *location_gps_nmea;
+ MMLocationGpsRaw *location_gps_raw;
+ MMLocationCdmaBs *location_cdma_bs;
+
+ location_3gpp = mm_modem_location_get_signaled_3gpp (modem_location);
+ location_gps_nmea = mm_modem_location_get_signaled_gps_nmea (modem_location);
+ location_gps_raw = mm_modem_location_get_signaled_gps_raw (modem_location);
+ location_cdma_bs = mm_modem_location_get_signaled_cdma_bs (modem_location);
+
+ get_location_process_reply (location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, NULL);
+}
+
+static void
+signaled_location_updated (MMModemLocation *modem_location)
+{
+ print_signaled_location (modem_location);
}
static void
-get_location_ready (MMModemLocation *modem_location,
- GAsyncResult *result)
+get_location_ready (MMModemLocation *modem_location,
+ GAsyncResult *result)
{
MMLocation3gpp *location_3gpp = NULL;
MMLocationGpsNmea *location_gps_nmea = NULL;
@@ -513,13 +623,12 @@ get_location_ready (MMModemLocation *modem_location,
mm_modem_location_get_full_finish (modem_location,
result,
- get_3gpp_flag ? &location_3gpp : NULL,
- get_gps_nmea_flag ? &location_gps_nmea : NULL,
- get_gps_raw_flag ? &location_gps_raw : NULL,
- get_cdma_bs_flag ? &location_cdma_bs : NULL,
+ &location_3gpp,
+ &location_gps_nmea,
+ &location_gps_raw,
+ &location_cdma_bs,
&error);
get_location_process_reply (location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, error);
-
mmcli_async_operation_done ();
}
@@ -541,22 +650,11 @@ get_modem_ready (GObject *source,
g_assert_not_reached ();
/* Request to setup location gathering? */
- if (enable_3gpp_flag ||
- disable_3gpp_flag ||
- enable_agps_flag ||
- disable_agps_flag ||
- enable_gps_nmea_flag ||
- disable_gps_nmea_flag ||
- enable_gps_raw_flag ||
- disable_gps_raw_flag ||
- enable_cdma_bs_flag ||
- disable_cdma_bs_flag ||
- enable_gps_unmanaged_flag ||
- disable_gps_unmanaged_flag) {
+ if (any_location_setup_flag) {
g_debug ("Asynchronously setting up location gathering...");
mm_modem_location_setup (ctx->modem_location,
build_sources_from_flags (),
- mm_modem_location_signals_location (ctx->modem_location),
+ build_signals_location_from_flags (),
ctx->cancellable,
(GAsyncReadyCallback)setup_ready,
NULL);
@@ -564,10 +662,7 @@ get_modem_ready (GObject *source,
}
/* Request to get location from the modem? */
- if (get_3gpp_flag ||
- get_gps_nmea_flag ||
- get_gps_raw_flag ||
- get_cdma_bs_flag) {
+ if (get_flag) {
g_debug ("Asynchronously getting location from the modem...");
mm_modem_location_get_full (ctx->modem_location,
ctx->cancellable,
@@ -576,6 +671,22 @@ get_modem_ready (GObject *source,
return;
}
+ /* Request to monitor location? */
+ if (monitor_flag) {
+ print_signaled_location (ctx->modem_location);
+ g_signal_connect (ctx->modem_location,
+ "notify::location",
+ G_CALLBACK (signaled_location_updated),
+ NULL);
+
+ /* If we get cancelled, operation done */
+ g_cancellable_connect (ctx->cancellable,
+ G_CALLBACK (cancelled),
+ NULL,
+ NULL);
+ return;
+ }
+
/* Request to set SUPL server? */
if (set_supl_server_str) {
g_debug ("Asynchronously setting SUPL server...");
@@ -587,6 +698,45 @@ get_modem_ready (GObject *source,
return;
}
+ /* Request to inject assistance data? */
+ if (inject_assistance_data_str) {
+ guint8 *data;
+ gsize data_size;
+
+ if (!parse_inject_assistance_data (&data, &data_size)) {
+ g_printerr ("error: couldn't inject assistance data: invalid parameters given: '%s'\n",
+ inject_assistance_data_str);
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Asynchronously injecting assistance data...");
+ mm_modem_location_inject_assistance_data (ctx->modem_location,
+ data, data_size,
+ ctx->cancellable,
+ (GAsyncReadyCallback)inject_assistance_data_ready,
+ NULL);
+ g_free (data);
+ return;
+ }
+
+ /* Request to set GPS refresh rate? */
+ if (set_gps_refresh_rate_str) {
+ guint rate;
+
+ if (!mm_get_uint_from_str (set_gps_refresh_rate_str, &rate)) {
+ g_printerr ("error: couldn't set GPS refresh rate: invalid rate given: '%s'\n",
+ set_gps_refresh_rate_str);
+ exit (EXIT_FAILURE);
+ }
+ g_debug ("Asynchronously setting GPS refresh rate...");
+ mm_modem_location_set_gps_refresh_rate (ctx->modem_location,
+ rate,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_gps_refresh_rate_ready,
+ NULL);
+ return;
+ }
+
g_warn_if_reached ();
}
@@ -633,24 +783,13 @@ mmcli_modem_location_run_synchronous (GDBusConnection *connection)
}
/* Request to setup location gathering? */
- if (enable_3gpp_flag ||
- disable_3gpp_flag ||
- enable_agps_flag ||
- disable_agps_flag ||
- enable_gps_nmea_flag ||
- disable_gps_nmea_flag ||
- enable_gps_raw_flag ||
- disable_gps_raw_flag ||
- enable_cdma_bs_flag ||
- disable_cdma_bs_flag ||
- enable_gps_unmanaged_flag ||
- disable_gps_unmanaged_flag) {
+ if (any_location_setup_flag) {
gboolean result;
g_debug ("Synchronously setting up location gathering...");
result = mm_modem_location_setup_sync (ctx->modem_location,
build_sources_from_flags (),
- mm_modem_location_signals_location (ctx->modem_location),
+ build_signals_location_from_flags (),
NULL,
&error);
setup_process_reply (result, error);
@@ -658,10 +797,7 @@ mmcli_modem_location_run_synchronous (GDBusConnection *connection)
}
/* Request to get location from the modem? */
- if (get_3gpp_flag ||
- get_gps_nmea_flag ||
- get_gps_raw_flag ||
- get_cdma_bs_flag) {
+ if (get_flag) {
MMLocation3gpp *location_3gpp = NULL;
MMLocationGpsNmea *location_gps_nmea = NULL;
MMLocationGpsRaw *location_gps_raw = NULL;
@@ -669,10 +805,10 @@ mmcli_modem_location_run_synchronous (GDBusConnection *connection)
g_debug ("Synchronously getting location from the modem...");
mm_modem_location_get_full_sync (ctx->modem_location,
- get_3gpp_flag ? &location_3gpp : NULL,
- get_gps_nmea_flag ? &location_gps_nmea : NULL,
- get_gps_raw_flag ? &location_gps_raw : NULL,
- get_cdma_bs_flag ? &location_cdma_bs : NULL,
+ &location_3gpp,
+ &location_gps_nmea,
+ &location_gps_raw,
+ &location_cdma_bs,
NULL,
&error);
get_location_process_reply (location_3gpp, location_gps_nmea, location_gps_raw, location_cdma_bs, error);
@@ -692,5 +828,47 @@ mmcli_modem_location_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Request to inject assistance data? */
+ if (inject_assistance_data_str) {
+ gboolean result;
+ guint8 *data;
+ gsize data_size;
+
+ if (!parse_inject_assistance_data (&data, &data_size)) {
+ g_printerr ("error: couldn't inject assistance data: invalid parameters given: '%s'\n",
+ inject_assistance_data_str);
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Synchronously setting assistance data...");
+ result = mm_modem_location_inject_assistance_data_sync (ctx->modem_location,
+ data, data_size,
+ NULL,
+ &error);
+ inject_assistance_data_process_reply (result, error);
+ g_free (data);
+ return;
+ }
+
+ /* Request to set GPS refresh rate? */
+ if (set_gps_refresh_rate_str) {
+ gboolean result;
+ guint rate;
+
+ if (!mm_get_uint_from_str (set_gps_refresh_rate_str, &rate)) {
+ g_printerr ("error: couldn't set GPS refresh rate: invalid rate given: '%s'\n",
+ set_gps_refresh_rate_str);
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Synchronously setting GPS refresh rate...");
+ result = mm_modem_location_set_gps_refresh_rate_sync (ctx->modem_location,
+ rate,
+ NULL,
+ &error);
+ set_gps_refresh_rate_process_reply (result, error);
+ return;
+ }
+
g_warn_if_reached ();
}
diff --git a/cli/mmcli-modem-messaging.c b/cli/mmcli-modem-messaging.c
index 5d5921f9..513c6975 100644
--- a/cli/mmcli-modem-messaging.c
+++ b/cli/mmcli-modem-messaging.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -64,7 +65,7 @@ static GOptionEntry entries[] = {
"Create a new SMS in a given modem",
"[\"key=value,...\"]"
},
- { "messaging-create-sms-with-data", 0, 0, G_OPTION_ARG_STRING, &create_with_data_str,
+ { "messaging-create-sms-with-data", 0, 0, G_OPTION_ARG_FILENAME, &create_with_data_str,
"Pass the given file as data contents when creating a new SMS",
"[File path]"
},
@@ -81,7 +82,7 @@ mmcli_modem_messaging_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("messaging",
- "Messaging options",
+ "Messaging options:",
"Show Messaging options",
NULL,
NULL);
@@ -123,7 +124,7 @@ mmcli_modem_messaging_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -160,7 +161,7 @@ ensure_modem_messaging (void)
void
mmcli_modem_messaging_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static MMSmsProperties *
@@ -208,64 +209,48 @@ static void
print_messaging_status (void)
{
MMSmsStorage *supported = NULL;
- guint supported_len = 0;
- gchar *supported_str = NULL;
+ guint supported_len = 0;
+ gchar *supported_str = NULL;
- mm_modem_messaging_get_supported_storages (ctx->modem_messaging,
- &supported,
- &supported_len);
+ mm_modem_messaging_get_supported_storages (ctx->modem_messaging, &supported, &supported_len);
if (supported)
supported_str = mm_common_build_sms_storages_string (supported, supported_len);
+ g_free (supported);
-#undef VALIDATE_UNKNOWN
-#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
-
- g_print ("\n"
- "%s\n"
- " ----------------------------\n"
- " Messaging | supported storages: '%s'\n"
- " | default storage: '%s'\n",
- mm_modem_messaging_get_path (ctx->modem_messaging),
- VALIDATE_UNKNOWN (supported_str),
- VALIDATE_UNKNOWN (mm_sms_storage_get_string (
- mm_modem_messaging_get_default_storage (
- ctx->modem_messaging))));
- g_free (supported_str);
+ mmcli_output_string_take (MMC_F_MESSAGING_SUPPORTED_STORAGES, supported_str);
+ mmcli_output_string (MMC_F_MESSAGING_DEFAULT_STORAGES, mm_sms_storage_get_string (
+ mm_modem_messaging_get_default_storage (ctx->modem_messaging)));
+ mmcli_output_dump ();
}
static void
-print_sms_short_info (MMSms *sms)
+output_sms_info (MMSms *sms)
{
- g_print ("\t%s (%s)\n",
- mm_sms_get_path (sms),
- mm_sms_state_get_string (mm_sms_get_state (sms)));
+ gchar *extra;
+
+ extra = g_strdup_printf ("(%s)", mm_sms_state_get_string (mm_sms_get_state (sms)));
+ mmcli_output_listitem (MMC_F_SMS_LIST_DBUS_PATH,
+ " ",
+ mm_sms_get_path (sms),
+ extra);
+ g_free (extra);
}
static void
list_process_reply (GList *result,
const GError *error)
{
+ GList *l;
+
if (error) {
g_printerr ("error: couldn't list SMS: '%s'\n",
error->message);
exit (EXIT_FAILURE);
}
- g_print ("\n");
- if (!result) {
- g_print ("No SMS messages were found\n");
- } else {
- GList *l;
-
- g_print ("Found %u SMS messages:\n", g_list_length (result));
- for (l = result; l; l = g_list_next (l)) {
- MMSms *sms = MM_SMS (l->data);
-
- print_sms_short_info (sms);
- g_object_unref (sms);
- }
- g_list_free (result);
- }
+ for (l = result; l; l = g_list_next (l))
+ output_sms_info (MM_SMS (l->data));
+ mmcli_output_list_dump (MMC_F_SMS_LIST_DBUS_PATH);
}
static void
@@ -292,8 +277,7 @@ create_process_reply (MMSms *sms,
exit (EXIT_FAILURE);
}
- g_print ("Successfully created new SMS:\n");
- print_sms_short_info (sms);
+ g_print ("Successfully created new SMS: %s\n", mm_sms_get_path (sms));
g_object_unref (sms);
}
@@ -454,7 +438,7 @@ mmcli_modem_messaging_run_synchronous (GDBusConnection *connection)
ensure_modem_messaging ();
- /* Request to get location status? */
+ /* Request to get messaging status? */
if (status_flag) {
g_debug ("Printing messaging status...");
print_messaging_status ();
@@ -474,7 +458,6 @@ mmcli_modem_messaging_run_synchronous (GDBusConnection *connection)
/* Request to create a new SMS? */
if (create_str) {
MMSms *sms;
- GError *error = NULL;
MMSmsProperties *properties;
properties = build_sms_properties_from_input (create_str,
diff --git a/cli/mmcli-modem-oma.c b/cli/mmcli-modem-oma.c
index 2e61fe20..800c272e 100644
--- a/cli/mmcli-modem-oma.c
+++ b/cli/mmcli-modem-oma.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -86,7 +87,7 @@ mmcli_modem_oma_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("oma",
- "OMA options",
+ "OMA options:",
"Show OMA options",
NULL,
NULL);
@@ -124,7 +125,7 @@ mmcli_modem_oma_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -154,57 +155,50 @@ ensure_modem_oma (void)
void
mmcli_modem_oma_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
print_oma_status (void)
{
- gchar *features_str;
+ gchar *features_str;
const MMOmaPendingNetworkInitiatedSession *pending_sessions;
- guint n_pending_sessions;
-
-#undef VALIDATE_UNKNOWN
-#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
+ guint n_pending_sessions;
+ const gchar *current_session_type = NULL;
+ const gchar *current_session_state = NULL;
+ GPtrArray *aux = NULL;
features_str = mm_oma_feature_build_string_from_mask (mm_modem_oma_get_features (ctx->modem_oma));
- /* Global IDs */
- g_print ("\n"
- "%s\n",
- VALIDATE_UNKNOWN (mm_modem_oma_get_path (ctx->modem_oma)));
-
- /* Overall setup */
- g_print (" -------------------------\n"
- " Setup | features: '%s'\n",
- VALIDATE_UNKNOWN (features_str));
-
/* Current session */
if (mm_modem_oma_get_session_type (ctx->modem_oma) != MM_OMA_SESSION_TYPE_UNKNOWN) {
- g_print (" -------------------------\n"
- " Current session | type: '%s'\n"
- " | state: '%s'\n",
- VALIDATE_UNKNOWN (mm_oma_session_type_get_string (mm_modem_oma_get_session_type (ctx->modem_oma))),
- VALIDATE_UNKNOWN (mm_oma_session_state_get_string (mm_modem_oma_get_session_state (ctx->modem_oma))));
+ current_session_type = mm_oma_session_type_get_string (mm_modem_oma_get_session_type (ctx->modem_oma));
+ current_session_state = mm_oma_session_state_get_string (mm_modem_oma_get_session_state (ctx->modem_oma));
}
/* If 1 or more pending sessions... */
- if (mm_modem_peek_pending_network_initiated_sessions (ctx->modem_oma, &pending_sessions, &n_pending_sessions) &&
+ if (mm_modem_oma_peek_pending_network_initiated_sessions (ctx->modem_oma, &pending_sessions, &n_pending_sessions) &&
n_pending_sessions > 0) {
guint i;
- g_print (" -------------------------\n"
- " Pending sessions |\n");
+ aux = g_ptr_array_new ();
+
for (i = 0; i < n_pending_sessions; i++) {
- g_print (" [%u] | type: '%s'\n"
- " | id: '%u'\n",
- i,
- VALIDATE_UNKNOWN (mm_oma_session_type_get_string (pending_sessions[i].session_type)),
- pending_sessions[i].session_id);
+ gchar *info;
+
+ info = g_strdup_printf ("id: %u, type: %s",
+ pending_sessions[i].session_id,
+ mm_oma_session_type_get_string (pending_sessions[i].session_type));
+ g_ptr_array_add (aux, info);
}
+ g_ptr_array_add (aux, NULL);
}
- g_free (features_str);
+ mmcli_output_string_take (MMC_F_OMA_FEATURES, features_str);
+ mmcli_output_string (MMC_F_OMA_CURRENT_TYPE, current_session_type);
+ mmcli_output_string (MMC_F_OMA_CURRENT_STATE, current_session_state);
+ mmcli_output_string_array_take (MMC_F_OMA_PENDING_SESSIONS, aux ? (gchar **) g_ptr_array_free (aux, FALSE) : NULL, TRUE);
+ mmcli_output_dump ();
}
static void
@@ -445,7 +439,6 @@ mmcli_modem_oma_run_synchronous (GDBusConnection *connection)
/* Request to setup OMA features? */
if (setup_str) {
gboolean result;
- GError *error = NULL;
MMOmaFeature features;
features = mm_common_get_oma_features_from_string (setup_str, &error);
@@ -466,7 +459,6 @@ mmcli_modem_oma_run_synchronous (GDBusConnection *connection)
/* Request to start session? */
if (start_str) {
gboolean result;
- GError *error = NULL;
MMOmaSessionType session_type;
session_type = mm_common_get_oma_session_type_from_string (start_str, &error);
@@ -487,7 +479,6 @@ mmcli_modem_oma_run_synchronous (GDBusConnection *connection)
/* Request to accept or reject session? */
if (accept_str || reject_str) {
gboolean result;
- GError *error = NULL;
guint session_id;
if (!mm_get_uint_from_str (accept_str ? accept_str : reject_str, &session_id)) {
diff --git a/cli/mmcli-modem-sar.c b/cli/mmcli-modem-sar.c
new file mode 100644
index 00000000..3c97ca68
--- /dev/null
+++ b/cli/mmcli-modem-sar.c
@@ -0,0 +1,370 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Fibocom Wireless Inc.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mmcli.h"
+#include "mmcli-common.h"
+#include "mmcli-output.h"
+
+/* Context */
+typedef struct {
+ MMManager *manager;
+ GCancellable *cancellable;
+ MMObject *object;
+ MMModemSar *modem_sar;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean status_flag;
+static gboolean sar_enable_flag;
+static gboolean sar_disable_flag;
+static gint power_level_int = -1;
+
+static GOptionEntry entries[] = {
+ { "sar-status", 0, 0, G_OPTION_ARG_NONE, &status_flag,
+ "Current status of the SAR",
+ NULL
+ },
+ { "sar-enable", 0, 0, G_OPTION_ARG_NONE, &sar_enable_flag,
+ "Enable dynamic SAR",
+ NULL
+ },
+ { "sar-disable", 0, 0, G_OPTION_ARG_NONE, &sar_disable_flag,
+ "Disable dynamic SAR",
+ NULL
+ },
+ { "sar-set-power-level", 0, 0, G_OPTION_ARG_INT, &power_level_int,
+ "Set current dynamic SAR power level for all antennas on the device",
+ "[power level]"
+ },
+ { NULL }
+};
+
+GOptionGroup *
+mmcli_modem_sar_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ group = g_option_group_new ("sar",
+ "SAR options:",
+ "Show SAR options",
+ NULL,
+ NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+gboolean
+mmcli_modem_sar_options_enabled (void)
+{
+ static guint n_actions = 0;
+ static gboolean checked = FALSE;
+
+ if (checked)
+ return !!n_actions;
+
+ n_actions = (status_flag +
+ sar_enable_flag +
+ sar_disable_flag +
+ (power_level_int >= 0));
+
+ if (n_actions > 1) {
+ g_printerr ("error: too many SAR actions requested\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (status_flag)
+ mmcli_force_sync_operation ();
+
+ checked = TRUE;
+ return !!n_actions;
+}
+
+static void
+context_free (void)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ if (ctx->modem_sar)
+ g_object_unref (ctx->modem_sar);
+ if (ctx->object)
+ g_object_unref (ctx->object);
+ if (ctx->manager)
+ g_object_unref (ctx->manager);
+ g_free (ctx);
+}
+
+static void
+ensure_modem_sar (void)
+{
+ if (!ctx->modem_sar) {
+ g_printerr ("error: modem has no SAR capabilities\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Success */
+}
+
+void
+mmcli_modem_sar_shutdown (void)
+{
+ context_free ();
+}
+
+static void
+print_sar_status (void)
+{
+ guint power_level = 0;
+ gboolean sar_state;
+
+ sar_state = mm_modem_sar_get_state (ctx->modem_sar);
+ power_level = mm_modem_sar_get_power_level (ctx->modem_sar);
+
+ mmcli_output_string (MMC_F_SAR_STATE, sar_state ? "yes" : "no");
+ mmcli_output_string_take (MMC_F_SAR_POWER_LEVEL, g_strdup_printf ("%d", power_level));
+ mmcli_output_dump ();
+}
+
+static void
+enable_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't enable SAR: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Successfully enabled SAR\n");
+}
+
+static void
+disable_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't disable SAR: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Successfully disabled SAR\n");
+}
+
+static void
+enable_ready (MMModemSar *modem,
+ GAsyncResult *result)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = mm_modem_sar_enable_finish (modem, result, &error);
+ enable_process_reply (res, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+disable_ready (MMModemSar *modem,
+ GAsyncResult *result)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = mm_modem_sar_enable_finish (modem, result, &error);
+ disable_process_reply (res, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+set_power_level_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't set the SAR power level: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Successfully set the SAR power level\n");
+}
+
+static void
+set_power_level_ready (MMModemSar *modem,
+ GAsyncResult *result)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = mm_modem_sar_set_power_level_finish (modem, result, &error);
+ set_power_level_process_reply (res, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+get_modem_ready (GObject *source,
+ GAsyncResult *result)
+{
+ ctx->object = mmcli_get_modem_finish (result, &ctx->manager);
+ ctx->modem_sar = mm_object_get_modem_sar (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_sar)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_sar));
+
+ ensure_modem_sar ();
+
+ g_assert (!status_flag);
+
+ /* Request to enable SAR */
+ if (sar_enable_flag) {
+ g_debug ("Asynchronously enabling SAR ...");
+ mm_modem_sar_enable (ctx->modem_sar,
+ TRUE,
+ ctx->cancellable,
+ (GAsyncReadyCallback)enable_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to disable SAR */
+ if (sar_disable_flag) {
+ g_debug ("Asynchronously disabling SAR ...");
+ mm_modem_sar_enable (ctx->modem_sar,
+ FALSE,
+ ctx->cancellable,
+ (GAsyncReadyCallback)disable_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to set power level of SAR */
+ if (power_level_int >= 0) {
+ g_debug ("Asynchronously starting set sar power level to %u ...", power_level_int);
+ mm_modem_sar_set_power_level (ctx->modem_sar,
+ power_level_int,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_power_level_ready,
+ NULL);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
+
+void
+mmcli_modem_sar_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable)
+{
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ if (cancellable)
+ ctx->cancellable = g_object_ref (cancellable);
+
+ /* Get proper modem */
+ mmcli_get_modem (connection,
+ mmcli_get_common_modem_string (),
+ cancellable,
+ (GAsyncReadyCallback)get_modem_ready,
+ NULL);
+}
+
+void
+mmcli_modem_sar_run_synchronous (GDBusConnection *connection)
+{
+ GError *error = NULL;
+
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ ctx->object = mmcli_get_modem_sync (connection,
+ mmcli_get_common_modem_string (),
+ &ctx->manager);
+ ctx->modem_sar = mm_object_get_modem_sar (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_sar)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_sar));
+
+ ensure_modem_sar ();
+
+ /* Request to get status? */
+ if (status_flag) {
+ g_debug ("Printing SAR status...");
+ print_sar_status ();
+ return;
+ }
+
+ /* Request to enable SAR */
+ if (sar_enable_flag) {
+ gboolean result;
+ g_debug ("Synchronously enabling SAR ...");
+ result = mm_modem_sar_enable_sync (ctx->modem_sar,
+ TRUE,
+ ctx->cancellable,
+ &error);
+ enable_process_reply (result, error);
+ return;
+ }
+
+ /* Request to disable SAR */
+ if (sar_disable_flag) {
+ gboolean result;
+ g_debug ("Synchronously disabling SAR ...");
+ result = mm_modem_sar_enable_sync (ctx->modem_sar,
+ FALSE,
+ ctx->cancellable,
+ &error);
+ disable_process_reply (result, error);
+ return;
+ }
+
+ /* Request to set power level of SAR */
+ if (power_level_int >=0 ) {
+ gboolean result;
+ g_debug ("Synchronously starting set power level to %u ...", power_level_int);
+ result = mm_modem_sar_set_power_level_sync (ctx->modem_sar,
+ power_level_int,
+ ctx->cancellable,
+ &error);
+ set_power_level_process_reply (result, error);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
diff --git a/cli/mmcli-modem-signal.c b/cli/mmcli-modem-signal.c
index c15b6b58..7d1e2ff6 100644
--- a/cli/mmcli-modem-signal.c
+++ b/cli/mmcli-modem-signal.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -46,14 +47,19 @@ static Context *ctx;
/* Options */
static gboolean get_flag;
static gchar *setup_str;
+static gchar *setup_thresholds_str;
static GOptionEntry entries[] = {
{ "signal-setup", 0, 0, G_OPTION_ARG_STRING, &setup_str,
- "Setup extended signal information retrieval",
+ "Setup signal quality information polling, in seconds",
"[Rate]"
},
+ { "signal-setup-thresholds", 0, 0, G_OPTION_ARG_STRING, &setup_thresholds_str,
+ "Setup signal quality information thresholds (allowed keys: rssi-threshold, error-rate-threshold)",
+ "[\"key=value,...\"]"
+ },
{ "signal-get", 0, 0, G_OPTION_ARG_NONE, &get_flag,
- "Get all extended signal quality information",
+ "Get all signal quality information",
NULL
},
{ NULL }
@@ -65,7 +71,7 @@ mmcli_modem_signal_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("signal",
- "Signal options",
+ "Signal options:",
"Show Signal options",
NULL,
NULL);
@@ -84,6 +90,7 @@ mmcli_modem_signal_options_enabled (void)
return !!n_actions;
n_actions = (!!setup_str +
+ !!setup_thresholds_str +
get_flag);
if (n_actions > 1) {
@@ -99,7 +106,7 @@ mmcli_modem_signal_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -129,71 +136,168 @@ ensure_modem_signal (void)
void
mmcli_modem_signal_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
print_signal_info (void)
{
MMSignal *signal;
+ gdouble value;
+ gchar *refresh_rate;
+ gchar *rssi_threshold;
+ gchar *error_rate_threshold;
+ gchar *cdma1x_rssi = NULL;
+ gchar *cdma1x_ecio = NULL;
+ gchar *cdma1x_error_rate = NULL;
+ gchar *evdo_rssi = NULL;
+ gchar *evdo_ecio = NULL;
+ gchar *evdo_sinr = NULL;
+ gchar *evdo_io = NULL;
+ gchar *evdo_error_rate = NULL;
+ gchar *gsm_rssi = NULL;
+ gchar *gsm_error_rate = NULL;
+ gchar *umts_rssi = NULL;
+ gchar *umts_rscp = NULL;
+ gchar *umts_ecio = NULL;
+ gchar *umts_error_rate = NULL;
+ gchar *lte_rssi = NULL;
+ gchar *lte_rsrp = NULL;
+ gchar *lte_rsrq = NULL;
+ gchar *lte_snr = NULL;
+ gchar *lte_error_rate = NULL;
+ gchar *nr5g_rsrp = NULL;
+ gchar *nr5g_rsrq = NULL;
+ gchar *nr5g_snr = NULL;
+ gchar *nr5g_error_rate = NULL;
+
+ refresh_rate = g_strdup_printf ("%u", mm_modem_signal_get_rate (ctx->modem_signal));
+ rssi_threshold = g_strdup_printf ("%u", mm_modem_signal_get_rssi_threshold (ctx->modem_signal));
+ error_rate_threshold = g_strdup_printf ("%s", mm_modem_signal_get_error_rate_threshold (ctx->modem_signal) ? "yes" : "no");
- g_print ("\n"
- "%s\n"
- " -------------------------\n"
- " Refresh rate: '%u' seconds\n",
- mm_modem_signal_get_path (ctx->modem_signal),
- mm_modem_signal_get_rate (ctx->modem_signal));
-
- /* CDMA */
signal = mm_modem_signal_peek_cdma (ctx->modem_signal);
- if (signal)
- g_print (" -------------------------\n"
- " CDMA1x | RSSI: '%.2lf' dBm\n"
- " | EcIo: '%.2lf' dBm\n",
- mm_signal_get_rssi (signal),
- mm_signal_get_ecio (signal));
-
- /* EVDO */
+ if (signal) {
+ if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN)
+ cdma1x_rssi = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_ecio (signal)) != MM_SIGNAL_UNKNOWN)
+ cdma1x_ecio = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN)
+ cdma1x_error_rate = g_strdup_printf ("%.2lf", value);
+ }
+
signal = mm_modem_signal_peek_evdo (ctx->modem_signal);
- if (signal)
- g_print (" -------------------------\n"
- " EV-DO | RSSI: '%.2lf' dBm\n"
- " | EcIo: '%.2lf' dBm\n"
- " | SINR: '%.2lf' dBm\n"
- " | Io: '%.2lf' dB\n",
- mm_signal_get_rssi (signal),
- mm_signal_get_ecio (signal),
- mm_signal_get_sinr (signal),
- mm_signal_get_io (signal));
-
- /* GSM */
+ if (signal) {
+ if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN)
+ evdo_rssi = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_ecio (signal)) != MM_SIGNAL_UNKNOWN)
+ evdo_ecio = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_sinr (signal)) != MM_SIGNAL_UNKNOWN)
+ evdo_sinr = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_io (signal)) != MM_SIGNAL_UNKNOWN)
+ evdo_io = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN)
+ evdo_error_rate = g_strdup_printf ("%.2lf", value);
+ }
+
signal = mm_modem_signal_peek_gsm (ctx->modem_signal);
- if (signal)
- g_print (" -------------------------\n"
- " GSM | RSSI: '%.2lf' dBm\n",
- mm_signal_get_rssi (signal));
+ if (signal) {
+ if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN)
+ gsm_rssi = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN)
+ gsm_error_rate = g_strdup_printf ("%.2lf", value);
+ }
- /* UMTS */
signal = mm_modem_signal_peek_umts (ctx->modem_signal);
- if (signal)
- g_print (" -------------------------\n"
- " UMTS | RSSI: '%.2lf' dBm\n"
- " | EcIo: '%.2lf' dBm\n",
- mm_signal_get_rssi (signal),
- mm_signal_get_ecio (signal));
-
- /* LTE */
+ if (signal) {
+ if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN)
+ umts_rssi = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_rscp (signal)) != MM_SIGNAL_UNKNOWN)
+ umts_rscp = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_ecio (signal)) != MM_SIGNAL_UNKNOWN)
+ umts_ecio = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN)
+ umts_error_rate = g_strdup_printf ("%.2lf", value);
+ }
+
signal = mm_modem_signal_peek_lte (ctx->modem_signal);
- if (signal)
- g_print (" -------------------------\n"
- " LTE | RSSI: '%.2lf' dBm\n"
- " | RSRQ: '%.2lf' dB\n"
- " | RSRP: '%.2lf' dBm\n"
- " | SNR: '%.2lf' dB\n",
- mm_signal_get_rssi (signal),
- mm_signal_get_rsrq (signal),
- mm_signal_get_rsrp (signal),
- mm_signal_get_snr (signal));
+ if (signal) {
+ if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN)
+ lte_rssi = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_rsrq (signal)) != MM_SIGNAL_UNKNOWN)
+ lte_rsrq = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_rsrp (signal)) != MM_SIGNAL_UNKNOWN)
+ lte_rsrp = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_snr (signal)) != MM_SIGNAL_UNKNOWN)
+ lte_snr = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN)
+ lte_error_rate = g_strdup_printf ("%.2lf", value);
+ }
+
+ signal = mm_modem_signal_peek_nr5g (ctx->modem_signal);
+ if (signal) {
+ if ((value = mm_signal_get_rsrq (signal)) != MM_SIGNAL_UNKNOWN)
+ nr5g_rsrq = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_rsrp (signal)) != MM_SIGNAL_UNKNOWN)
+ nr5g_rsrp = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_snr (signal)) != MM_SIGNAL_UNKNOWN)
+ nr5g_snr = g_strdup_printf ("%.2lf", value);
+ if ((value = mm_signal_get_error_rate (signal)) != MM_SIGNAL_UNKNOWN)
+ nr5g_error_rate = g_strdup_printf ("%.2lf", value);
+ }
+
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_REFRESH_RATE, refresh_rate, "seconds");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_RSSI_THRESHOLD, rssi_threshold, "dBm");
+ mmcli_output_string_take (MMC_F_SIGNAL_ERROR_RATE_THRESHOLD, error_rate_threshold);
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_CDMA1X_RSSI, cdma1x_rssi, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_CDMA1X_ECIO, cdma1x_ecio, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_CDMA1X_ERROR_RATE, cdma1x_error_rate, "%%");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_RSSI, evdo_rssi, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_ECIO, evdo_ecio, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_SINR, evdo_sinr, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_IO, evdo_io, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_EVDO_ERROR_RATE, evdo_error_rate, "%%");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_GSM_RSSI, gsm_rssi, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_GSM_ERROR_RATE, gsm_error_rate, "%%");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_RSSI, umts_rssi, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_RSCP, umts_rscp, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_ECIO, umts_ecio, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_UMTS_ERROR_RATE, umts_error_rate, "%%");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSSI, lte_rssi, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSRQ, lte_rsrq, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSRP, lte_rsrp, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_SNR, lte_snr, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_ERROR_RATE, lte_error_rate, "%%");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSRQ, nr5g_rsrq, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSRP, nr5g_rsrp, "dBm");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_SNR, nr5g_snr, "dB");
+ mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_ERROR_RATE, nr5g_error_rate, "%%");
+ mmcli_output_dump ();
+}
+
+static void
+setup_thresholds_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't setup signal quality information thresholds: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Successfully setup signal quality information thresholds\n");
+}
+
+static void
+setup_thresholds_ready (MMModemSignal *modem,
+ GAsyncResult *result)
+{
+ gboolean res;
+ GError *error = NULL;
+
+ res = mm_modem_signal_setup_thresholds_finish (modem, result, &error);
+ setup_thresholds_process_reply (res, error);
+
+ mmcli_async_operation_done ();
}
static void
@@ -201,12 +305,12 @@ setup_process_reply (gboolean result,
const GError *error)
{
if (!result) {
- g_printerr ("error: couldn't setup extended signal information retrieval: '%s'\n",
+ g_printerr ("error: couldn't setup signal quality information polling: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
- g_print ("Successfully setup extended signal information retrieval\n");
+ g_print ("Successfully setup signal quality information polling\n");
}
static void
@@ -243,7 +347,7 @@ get_modem_ready (GObject *source,
guint rate;
if (!mm_get_uint_from_str (setup_str, &rate)) {
- g_printerr ("error: invalid rate value '%s'", setup_str);
+ g_printerr ("error: invalid rate value '%s'\n", setup_str);
exit (EXIT_FAILURE);
}
@@ -256,6 +360,26 @@ get_modem_ready (GObject *source,
return;
}
+ /* Request to setup threshold? */
+ if (setup_thresholds_str) {
+ g_autoptr(MMSignalThresholdProperties) properties = NULL;
+ g_autoptr(GError) error = NULL;
+
+ properties = mm_signal_threshold_properties_new_from_string (setup_thresholds_str, &error);
+ if (!properties) {
+ g_printerr ("error: failed to parse properties string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Asynchronously setting up threshold values...");
+ mm_modem_signal_setup_thresholds (ctx->modem_signal,
+ properties,
+ ctx->cancellable,
+ (GAsyncReadyCallback)setup_thresholds_ready,
+ NULL);
+ return;
+ }
+
g_warn_if_reached ();
}
@@ -302,11 +426,11 @@ mmcli_modem_signal_run_synchronous (GDBusConnection *connection)
/* Request to set rate? */
if (setup_str) {
- guint rate;
+ guint rate;
gboolean result;
if (!mm_get_uint_from_str (setup_str, &rate)) {
- g_printerr ("error: invalid rate value '%s'", setup_str);
+ g_printerr ("error: invalid rate value '%s'\n", setup_str);
exit (EXIT_FAILURE);
}
@@ -319,6 +443,25 @@ mmcli_modem_signal_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Request to setup threshold? */
+ if (setup_thresholds_str) {
+ g_autoptr(MMSignalThresholdProperties) properties = NULL;
+ gboolean result;
+
+ properties = mm_signal_threshold_properties_new_from_string (setup_thresholds_str, &error);
+ if (!properties) {
+ g_printerr ("error: failed to parse properties string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_debug ("Asynchronously setting up threshold values...");
+ result = mm_modem_signal_setup_thresholds_sync (ctx->modem_signal,
+ properties,
+ NULL,
+ &error);
+ setup_thresholds_process_reply (result, error);
+ return;
+ }
g_warn_if_reached ();
}
diff --git a/cli/mmcli-modem-simple.c b/cli/mmcli-modem-simple.c
index 745e5387..174f144b 100644
--- a/cli/mmcli-modem-simple.c
+++ b/cli/mmcli-modem-simple.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -46,7 +46,6 @@ static Context *ctx;
/* Options */
static gchar *connect_str;
static gboolean disconnect_flag;
-static gboolean status_flag;
static GOptionEntry entries[] = {
{ "simple-connect", 0, 0, G_OPTION_ARG_STRING, &connect_str,
@@ -57,10 +56,6 @@ static GOptionEntry entries[] = {
"Disconnect all connected bearers.",
NULL
},
- { "simple-status", 0, 0, G_OPTION_ARG_NONE, &status_flag,
- "Show compilation of status properties.",
- NULL
- },
{ NULL }
};
@@ -70,7 +65,7 @@ mmcli_modem_simple_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("simple",
- "Simple options",
+ "Simple options:",
"Show Simple options",
NULL,
NULL);
@@ -89,8 +84,7 @@ mmcli_modem_simple_options_enabled (void)
return !!n_actions;
n_actions = (!!connect_str +
- disconnect_flag +
- status_flag);
+ disconnect_flag);
if (n_actions > 1) {
g_printerr ("error: too many Simple actions requested\n");
@@ -107,7 +101,7 @@ mmcli_modem_simple_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -137,7 +131,7 @@ ensure_modem_simple (void)
void
mmcli_modem_simple_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
@@ -196,136 +190,6 @@ disconnect_ready (MMModemSimple *modem_simple,
}
static void
-status_process_reply (MMSimpleStatus *result,
- const GError *error)
-{
- MMModemState state;
-
- if (!result) {
- g_printerr ("error: couldn't get status from the modem: '%s'\n",
- error ? error->message : "unknown error");
- exit (EXIT_FAILURE);
- }
-
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE_UNKNOWN
-#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
-
- g_print ("\n"
- "%s\n",
- VALIDATE_UNKNOWN (mm_modem_simple_get_path (ctx->modem_simple)));
-
- state = mm_simple_status_get_state (result);
-
-
- g_print (" -------------------------\n"
- " Status | state: '%s'\n",
- mm_modem_state_get_string (state));
-
- if (state >= MM_MODEM_STATE_REGISTERED) {
- const MMModemBand *bands = NULL;
- guint n_bands = 0;
- gchar *bands_str;
- gchar *access_tech_str;
- guint signal_quality;
- gboolean signal_quality_recent = FALSE;
-
- signal_quality = (mm_simple_status_get_signal_quality (
- result,
- &signal_quality_recent));
- mm_simple_status_get_current_bands (result, &bands, &n_bands);
- bands_str = mm_common_build_bands_string (bands, n_bands);
- access_tech_str = (mm_modem_access_technology_build_string_from_mask (
- mm_simple_status_get_access_technologies (result)));
-
- g_print (" | signal quality: '%u' (%s)\n"
- " | bands: '%s'\n"
- " | access tech: '%s'\n",
- signal_quality, signal_quality_recent ? "recent" : "cached",
- VALIDATE_UNKNOWN (bands_str),
- VALIDATE_UNKNOWN (access_tech_str));
-
- if ((mm_simple_status_get_3gpp_registration_state (result) ==
- MM_MODEM_3GPP_REGISTRATION_STATE_HOME) ||
- (mm_simple_status_get_3gpp_registration_state (result) ==
- MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)) {
- g_print (" -------------------------\n"
- " 3GPP | registration: '%s'\n"
- " | operator code: '%s'\n"
- " | operator name: '%s'\n"
- " | subscription: '%s'\n",
- mm_modem_3gpp_registration_state_get_string (
- mm_simple_status_get_3gpp_registration_state (result)),
- VALIDATE_UNKNOWN (mm_simple_status_get_3gpp_operator_code (result)),
- VALIDATE_UNKNOWN (mm_simple_status_get_3gpp_operator_name (result)),
- mm_modem_3gpp_subscription_state_get_string (
- mm_simple_status_get_3gpp_subscription_state (result)));
- }
-
- if ((mm_simple_status_get_cdma_cdma1x_registration_state (result) !=
- MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) ||
- (mm_simple_status_get_cdma_evdo_registration_state (result) !=
- MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)) {
- guint sid;
- guint nid;
- gchar *sid_str = NULL;
- gchar *nid_str = NULL;
-
- sid = mm_simple_status_get_cdma_sid (result);
- sid_str = (sid != MM_MODEM_CDMA_SID_UNKNOWN ?
- g_strdup_printf ("%u", sid) :
- NULL);
- nid = mm_simple_status_get_cdma_nid (result);
- nid_str = (nid != MM_MODEM_CDMA_NID_UNKNOWN ?
- g_strdup_printf ("%u", nid) :
- NULL);
-
- g_print (" -------------------------\n"
- " CDMA | sid: '%s'\n"
- " | nid: '%s'\n"
- " | registration: CDMA1x '%s'\n"
- " | EV-DO '%s'\n",
- VALIDATE_UNKNOWN (sid_str),
- VALIDATE_UNKNOWN (nid_str),
- mm_modem_cdma_registration_state_get_string (
- mm_simple_status_get_cdma_cdma1x_registration_state (result)),
- mm_modem_cdma_registration_state_get_string (
- mm_simple_status_get_cdma_evdo_registration_state (result)));
-
- g_free (sid_str);
- g_free (nid_str);
- }
-
- g_free (access_tech_str);
- g_free (bands_str);
- } else {
- g_print (" -------------------------\n"
- " 3GPP | subscription: '%s'\n",
- mm_modem_3gpp_subscription_state_get_string (
- mm_simple_status_get_3gpp_subscription_state (result)));
- }
-
-
- g_print ("\n");
- g_object_unref (result);
-}
-
-static void
-status_ready (MMModemSimple *modem_simple,
- GAsyncResult *result,
- gpointer nothing)
-{
- MMSimpleStatus *operation_result;
- GError *error = NULL;
-
- operation_result = mm_modem_simple_get_status_finish (modem_simple, result, &error);
- status_process_reply (operation_result, error);
-
- mmcli_async_operation_done ();
-}
-
-static void
get_modem_ready (GObject *source,
GAsyncResult *result,
gpointer none)
@@ -373,17 +237,6 @@ get_modem_ready (GObject *source,
return;
}
- /* Request to get status from the modem? */
- if (status_flag) {
- g_debug ("Asynchronously getting status from the modem...");
-
- mm_modem_simple_get_status (ctx->modem_simple,
- ctx->cancellable,
- (GAsyncReadyCallback)status_ready,
- NULL);
- return;
- }
-
g_warn_if_reached ();
}
@@ -439,18 +292,5 @@ mmcli_modem_simple_run_synchronous (GDBusConnection *connection)
return;
}
- /* Request to get status from the modem? */
- if (status_flag) {
- MMSimpleStatus *result;
-
- g_debug ("Synchronously getting status from the modem...");
-
- result = mm_modem_simple_get_status_sync (ctx->modem_simple,
- NULL,
- &error);
- status_process_reply (result, error);
- return;
- }
-
g_warn_if_reached ();
}
diff --git a/cli/mmcli-modem-time.c b/cli/mmcli-modem-time.c
index 7c37aaf4..98fa06e0 100644
--- a/cli/mmcli-modem-time.c
+++ b/cli/mmcli-modem-time.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -60,7 +61,7 @@ mmcli_modem_time_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("time",
- "Time options",
+ "Time options:",
"Show Time options",
NULL,
NULL);
@@ -90,7 +91,7 @@ mmcli_modem_time_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -125,7 +126,7 @@ ensure_modem_time (void)
void
mmcli_modem_time_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
@@ -137,14 +138,11 @@ get_network_time_process_reply (gchar *time_string,
gchar *dst_offset = NULL;
gchar *leap_seconds = NULL;
- if (error)
+ if (error) {
g_printerr ("error: couldn't get current network time: '%s'\n",
error->message);
-
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE
-#define VALIDATE(str) (str ? str : "not available")
+ exit (EXIT_FAILURE);
+ }
if (timezone) {
if (mm_network_timezone_get_offset (timezone) != MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN)
@@ -157,27 +155,11 @@ get_network_time_process_reply (gchar *time_string,
leap_seconds = g_strdup_printf ("%" G_GINT32_FORMAT, mm_network_timezone_get_leap_seconds (timezone));
}
- g_print ("\n"
- "%s\n"
- " ----------------------------\n"
- " Time | Current: '%s'\n"
- " ----------------------------\n"
- " Timezone | Offset: '%s'\n"
- " | DST offset: '%s'\n"
- " | Leap seconds: '%s'\n",
- mm_modem_time_get_path (ctx->modem_time),
- VALIDATE (time_string),
- VALIDATE (offset),
- VALIDATE (dst_offset),
- VALIDATE (leap_seconds));
-
- g_free (offset);
- g_free (dst_offset);
- g_free (leap_seconds);
- g_free (time_string);
-
- if (error)
- exit (EXIT_FAILURE);
+ mmcli_output_string_take (MMC_F_TIME_CURRENT, time_string);
+ mmcli_output_string_take (MMC_F_TIMEZONE_CURRENT, offset);
+ mmcli_output_string_take (MMC_F_TIMEZONE_DST_OFFSET, dst_offset);
+ mmcli_output_string_take (MMC_F_TIMEZONE_LEAP_SECONDS, leap_seconds);
+ mmcli_output_dump ();
}
static void
diff --git a/cli/mmcli-modem-voice.c b/cli/mmcli-modem-voice.c
new file mode 100644
index 00000000..ab216ea7
--- /dev/null
+++ b/cli/mmcli-modem-voice.c
@@ -0,0 +1,778 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mmcli.h"
+#include "mmcli-common.h"
+#include "mmcli-output.h"
+
+/* Context */
+typedef struct {
+ GDBusConnection *connection;
+ MMManager *manager;
+ GCancellable *cancellable;
+ MMObject *object;
+ MMModemVoice *modem_voice;
+} Context;
+static Context *ctx;
+
+/* Options */
+static gboolean status_flag;
+static gboolean list_flag;
+static gchar *create_str;
+static gchar *delete_str;
+static gboolean hold_and_accept_flag;
+static gboolean hangup_and_accept_flag;
+static gboolean hangup_all_flag;
+static gboolean transfer_flag;
+static gboolean call_waiting_enable_flag;
+static gboolean call_waiting_disable_flag;
+static gboolean call_waiting_query_flag;
+
+static GOptionEntry entries[] = {
+ { "voice-status", 0, 0, G_OPTION_ARG_NONE, &status_flag,
+ "Show status of voice support.",
+ NULL
+ },
+ { "voice-list-calls", 0, 0, G_OPTION_ARG_NONE, &list_flag,
+ "List calls available in a given modem",
+ NULL
+ },
+ { "voice-create-call", 0, 0, G_OPTION_ARG_STRING, &create_str,
+ "Create a new call in a given modem",
+ "[\"key=value,...\"]"
+ },
+ { "voice-delete-call", 0, 0, G_OPTION_ARG_STRING, &delete_str,
+ "Delete a call from a given modem",
+ "[PATH|INDEX]"
+ },
+ { "voice-hold-and-accept", 0, 0, G_OPTION_ARG_NONE, &hold_and_accept_flag,
+ "Places all active calls in hold and accepts the next waiting or held call",
+ NULL
+ },
+ { "voice-hangup-and-accept", 0, 0, G_OPTION_ARG_NONE, &hangup_and_accept_flag,
+ "Hangs up all active calls and accepts the next waiting or held call",
+ NULL
+ },
+ { "voice-hangup-all", 0, 0, G_OPTION_ARG_NONE, &hangup_all_flag,
+ "Hangs up all ongoing (active, waiting, held) calls",
+ NULL
+ },
+ { "voice-transfer", 0, 0, G_OPTION_ARG_NONE, &transfer_flag,
+ "Joins active and held calls and disconnects from them",
+ NULL
+ },
+ { "voice-enable-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_enable_flag,
+ "Enables the call waiting network service",
+ NULL
+ },
+ { "voice-disable-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_disable_flag,
+ "Disables the call waiting network service",
+ NULL
+ },
+ { "voice-query-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_query_flag,
+ "Queries the status of the call waiting network service",
+ NULL
+ },
+ { NULL }
+};
+
+GOptionGroup *
+mmcli_modem_voice_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ group = g_option_group_new ("voice",
+ "Voice options:",
+ "Show Voice options",
+ NULL,
+ NULL);
+ g_option_group_add_entries (group, entries);
+
+ return group;
+}
+
+gboolean
+mmcli_modem_voice_options_enabled (void)
+{
+ static guint n_actions = 0;
+ static gboolean checked = FALSE;
+
+ if (checked)
+ return !!n_actions;
+
+ n_actions = (status_flag +
+ list_flag +
+ !!create_str +
+ !!delete_str +
+ hold_and_accept_flag +
+ hangup_and_accept_flag +
+ hangup_all_flag +
+ transfer_flag +
+ call_waiting_enable_flag +
+ call_waiting_disable_flag +
+ call_waiting_query_flag);
+
+ if (n_actions > 1) {
+ g_printerr ("error: too many Voice actions requested\n");
+ exit (EXIT_FAILURE);
+ }
+
+ if (status_flag)
+ mmcli_force_sync_operation ();
+
+ checked = TRUE;
+ return !!n_actions;
+}
+
+static void
+context_free (void)
+{
+ if (!ctx)
+ return;
+
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+ if (ctx->modem_voice)
+ g_object_unref (ctx->modem_voice);
+ if (ctx->object)
+ g_object_unref (ctx->object);
+ if (ctx->manager)
+ g_object_unref (ctx->manager);
+ if (ctx->connection)
+ g_object_unref (ctx->connection);
+ g_free (ctx);
+}
+
+static void
+ensure_modem_voice (void)
+{
+ if (!ctx->modem_voice) {
+ g_printerr ("error: modem has no voice capabilities\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Success */
+}
+
+void
+mmcli_modem_voice_shutdown (void)
+{
+ context_free ();
+}
+
+static MMCallProperties *
+build_call_properties_from_input (const gchar *properties_string)
+{
+ GError *error = NULL;
+ MMCallProperties *properties;
+
+ properties = mm_call_properties_new_from_string (properties_string, &error);
+ if (!properties) {
+ g_printerr ("error: cannot parse properties string: '%s'\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ return properties;
+}
+
+static void
+print_voice_status (void)
+{
+ mmcli_output_string (MMC_F_VOICE_EMERGENCY_ONLY, mm_modem_voice_get_emergency_only (ctx->modem_voice) ? "yes" : "no");
+ mmcli_output_dump ();
+}
+
+static void
+output_call_info (MMCall *call)
+{
+ gchar *extra;
+
+ extra = g_strdup_printf ("%s (%s)",
+ mm_call_direction_get_string (mm_call_get_direction (call)),
+ mm_call_state_get_string (mm_call_get_state (call)));
+ mmcli_output_listitem (MMC_F_CALL_LIST_DBUS_PATH,
+ " ",
+ mm_call_get_path (call),
+ extra);
+ g_free (extra);
+}
+
+static void
+call_waiting_query_process_reply (const GError *error,
+ gboolean status)
+{
+ if (error) {
+ g_printerr ("error: couldn't query call waiting network service status: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("call waiting service is %s\n", status ? "enabled" : "disabled");
+}
+
+static void
+call_waiting_query_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GError *error = NULL;
+ gboolean status = FALSE;
+
+ mm_modem_voice_call_waiting_query_finish (modem, result, &status, &error);
+ call_waiting_query_process_reply (error, status);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+call_waiting_setup_process_reply (const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't setup call waiting network service: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("operation successful\n");
+}
+
+static void
+call_waiting_setup_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GError *error = NULL;
+
+ mm_modem_voice_call_waiting_setup_finish (modem, result, &error);
+ call_waiting_setup_process_reply (error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+transfer_process_reply (const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't hangup all: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("operation successful\n");
+}
+
+static void
+transfer_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GError *error = NULL;
+
+ mm_modem_voice_transfer_finish (modem, result, &error);
+ transfer_process_reply (error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+hangup_all_process_reply (const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't hangup all: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("operation successful\n");
+}
+
+static void
+hangup_all_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GError *error = NULL;
+
+ mm_modem_voice_hangup_all_finish (modem, result, &error);
+ hangup_all_process_reply (error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+hangup_and_accept_process_reply (const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't hangup and accept: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("operation successful\n");
+}
+
+static void
+hangup_and_accept_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GError *error = NULL;
+
+ mm_modem_voice_hangup_and_accept_finish (modem, result, &error);
+ hangup_and_accept_process_reply (error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+hold_and_accept_process_reply (const GError *error)
+{
+ if (error) {
+ g_printerr ("error: couldn't hold and accept: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("operation successful\n");
+}
+
+static void
+hold_and_accept_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GError *error = NULL;
+
+ mm_modem_voice_hold_and_accept_finish (modem, result, &error);
+ hold_and_accept_process_reply (error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+list_process_reply (GList *result,
+ const GError *error)
+{
+ GList *l;
+
+ if (error) {
+ g_printerr ("error: couldn't list call: '%s'\n",
+ error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ for (l = result; l; l = g_list_next (l))
+ output_call_info (MM_CALL (l->data));
+ mmcli_output_list_dump (MMC_F_CALL_LIST_DBUS_PATH);
+}
+
+static void
+list_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ GList *operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_voice_list_calls_finish (modem, result, &error);
+ list_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+create_process_reply (MMCall *call,
+ const GError *error)
+{
+ if (!call) {
+ g_printerr ("error: couldn't create new call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("Successfully created new call: %s\n", mm_call_get_path (call));
+ g_object_unref (call);
+}
+
+static void
+create_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ MMCall *call;
+ GError *error = NULL;
+
+ call = mm_modem_voice_create_call_finish (modem, result, &error);
+ create_process_reply (call, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+delete_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't delete call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully deleted call from modem\n");
+}
+
+static void
+delete_ready (MMModemVoice *modem,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_modem_voice_delete_call_finish (modem, result, &error);
+ delete_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+get_call_to_delete_ready (GDBusConnection *connection,
+ GAsyncResult *res)
+{
+ MMCall *call;
+ MMObject *obj = NULL;
+
+ call = mmcli_get_call_finish (res, NULL, &obj);
+ if (!g_str_equal (mm_object_get_path (obj), mm_modem_voice_get_path (ctx->modem_voice))) {
+ g_printerr ("error: call '%s' not owned by modem '%s'",
+ mm_call_get_path (call),
+ mm_modem_voice_get_path (ctx->modem_voice));
+ exit (EXIT_FAILURE);
+ }
+
+ mm_modem_voice_delete_call (ctx->modem_voice,
+ mm_call_get_path (call),
+ ctx->cancellable,
+ (GAsyncReadyCallback)delete_ready,
+ NULL);
+ g_object_unref (call);
+ g_object_unref (obj);
+}
+
+static void
+get_modem_ready (GObject *source,
+ GAsyncResult *result,
+ gpointer none)
+{
+ ctx->object = mmcli_get_modem_finish (result, &ctx->manager);
+ ctx->modem_voice = mm_object_get_modem_voice (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_voice)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_voice));
+
+ ensure_modem_voice ();
+
+ if (status_flag)
+ g_assert_not_reached ();
+
+ /* Request to list call? */
+ if (list_flag) {
+ g_debug ("Asynchronously listing calls in modem...");
+ mm_modem_voice_list_calls (ctx->modem_voice,
+ ctx->cancellable,
+ (GAsyncReadyCallback)list_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to create a new call? */
+ if (create_str) {
+ MMCallProperties *properties;
+
+ properties = build_call_properties_from_input (create_str);
+ g_debug ("Asynchronously creating new call in modem...");
+ mm_modem_voice_create_call (ctx->modem_voice,
+ properties,
+ ctx->cancellable,
+ (GAsyncReadyCallback)create_ready,
+ NULL);
+ g_object_unref (properties);
+ return;
+ }
+
+ /* Request to delete a given call? */
+ if (delete_str) {
+ mmcli_get_call (ctx->connection,
+ delete_str,
+ ctx->cancellable,
+ (GAsyncReadyCallback)get_call_to_delete_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to hold and accept? */
+ if (hold_and_accept_flag) {
+ g_debug ("Asynchronously holding and accepting next call...");
+ mm_modem_voice_hold_and_accept (ctx->modem_voice,
+ ctx->cancellable,
+ (GAsyncReadyCallback)hold_and_accept_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to hangup and accept? */
+ if (hangup_and_accept_flag) {
+ g_debug ("Asynchronously hanging up and accepting next call...");
+ mm_modem_voice_hangup_and_accept (ctx->modem_voice,
+ ctx->cancellable,
+ (GAsyncReadyCallback)hangup_and_accept_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to hangup all? */
+ if (hangup_all_flag) {
+ g_debug ("Asynchronously hanging up all calls...");
+ mm_modem_voice_hangup_all (ctx->modem_voice,
+ ctx->cancellable,
+ (GAsyncReadyCallback)hangup_all_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to transfer? */
+ if (transfer_flag) {
+ g_debug ("Asynchronously transferring calls...");
+ mm_modem_voice_transfer (ctx->modem_voice,
+ ctx->cancellable,
+ (GAsyncReadyCallback)transfer_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to enable call waiting? */
+ if (call_waiting_enable_flag) {
+ g_debug ("Asynchronously enabling call waiting...");
+ mm_modem_voice_call_waiting_setup (ctx->modem_voice,
+ TRUE,
+ ctx->cancellable,
+ (GAsyncReadyCallback)call_waiting_setup_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to disable call waiting? */
+ if (call_waiting_disable_flag) {
+ g_debug ("Asynchronously enabling call waiting...");
+ mm_modem_voice_call_waiting_setup (ctx->modem_voice,
+ FALSE,
+ ctx->cancellable,
+ (GAsyncReadyCallback)call_waiting_setup_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to query call waiting? */
+ if (call_waiting_query_flag) {
+ g_debug ("Asynchronously querying call waiting status...");
+ mm_modem_voice_call_waiting_query (ctx->modem_voice,
+ ctx->cancellable,
+ (GAsyncReadyCallback)call_waiting_query_ready,
+ NULL);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
+
+void
+mmcli_modem_voice_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable)
+{
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ if (cancellable)
+ ctx->cancellable = g_object_ref (cancellable);
+ ctx->connection = g_object_ref (connection);
+
+ /* Get proper modem */
+ mmcli_get_modem (connection,
+ mmcli_get_common_modem_string (),
+ cancellable,
+ (GAsyncReadyCallback)get_modem_ready,
+ NULL);
+}
+
+void
+mmcli_modem_voice_run_synchronous (GDBusConnection *connection)
+{
+ GError *error = NULL;
+
+ /* Initialize context */
+ ctx = g_new0 (Context, 1);
+ ctx->object = mmcli_get_modem_sync (connection,
+ mmcli_get_common_modem_string (),
+ &ctx->manager);
+ ctx->modem_voice = mm_object_get_modem_voice (ctx->object);
+
+ /* Setup operation timeout */
+ if (ctx->modem_voice)
+ mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_voice));
+
+ ensure_modem_voice ();
+
+ /* Request to get voice status? */
+ if (status_flag) {
+ g_debug ("Printing voice status...");
+ print_voice_status ();
+ return;
+ }
+
+ /* Request to list the call? */
+ if (list_flag) {
+ GList *result;
+
+ g_debug ("Synchronously listing call...");
+ result = mm_modem_voice_list_calls_sync (ctx->modem_voice, NULL, &error);
+ list_process_reply (result, error);
+ return;
+ }
+
+ /* Request to create a new call? */
+ if (create_str) {
+ MMCall *call;
+ MMCallProperties *properties;
+
+ properties = build_call_properties_from_input (create_str);
+
+ g_debug ("Synchronously creating new call in modem...");
+ call = mm_modem_voice_create_call_sync (ctx->modem_voice,
+ properties,
+ NULL,
+ &error);
+ g_object_unref (properties);
+
+ create_process_reply (call, error);
+ return;
+ }
+
+ /* Request to delete a given call? */
+ if (delete_str) {
+ gboolean result;
+ MMCall *call;
+ MMObject *obj = NULL;
+
+ call = mmcli_get_call_sync (connection,
+ delete_str,
+ NULL,
+ &obj);
+ if (!g_str_equal (mm_object_get_path (obj), mm_modem_voice_get_path (ctx->modem_voice))) {
+ g_printerr ("error: call '%s' not owned by modem '%s'",
+ mm_call_get_path (call),
+ mm_modem_voice_get_path (ctx->modem_voice));
+ exit (EXIT_FAILURE);
+ }
+
+ result = mm_modem_voice_delete_call_sync (ctx->modem_voice,
+ mm_call_get_path (call),
+ NULL,
+ &error);
+ g_object_unref (call);
+ g_object_unref (obj);
+
+ delete_process_reply (result, error);
+ return;
+ }
+
+ /* Request to hold and accept? */
+ if (hold_and_accept_flag) {
+ g_debug ("Synchronously holding and accepting call...");
+ mm_modem_voice_hold_and_accept_sync (ctx->modem_voice, NULL, &error);
+ hold_and_accept_process_reply (error);
+ return;
+ }
+
+ /* Request to hangup and accept? */
+ if (hangup_and_accept_flag) {
+ g_debug ("Synchronously hanging up and accepting call...");
+ mm_modem_voice_hangup_and_accept_sync (ctx->modem_voice, NULL, &error);
+ hangup_and_accept_process_reply (error);
+ return;
+ }
+
+ /* Request to hangup all? */
+ if (hangup_all_flag) {
+ g_debug ("Synchronously hanging up all calls...");
+ mm_modem_voice_hangup_all_sync (ctx->modem_voice, NULL, &error);
+ hangup_all_process_reply (error);
+ return;
+ }
+
+ /* Request to transfer? */
+ if (transfer_flag) {
+ g_debug ("Synchronously transferring calls...");
+ mm_modem_voice_transfer_sync (ctx->modem_voice, NULL, &error);
+ transfer_process_reply (error);
+ return;
+ }
+
+ /* Request to enable call waiting? */
+ if (call_waiting_enable_flag) {
+ g_debug ("Synchronously enabling call waiting...");
+ mm_modem_voice_call_waiting_setup_sync (ctx->modem_voice, TRUE, NULL, &error);
+ call_waiting_setup_process_reply (error);
+ return;
+ }
+
+ /* Request to disable call waiting? */
+ if (call_waiting_disable_flag) {
+ g_debug ("Synchronously disabling call waiting...");
+ mm_modem_voice_call_waiting_setup_sync (ctx->modem_voice, FALSE, NULL, &error);
+ call_waiting_setup_process_reply (error);
+ return;
+ }
+
+ /* Request to query call waiting? */
+ if (call_waiting_query_flag) {
+ gboolean status = FALSE;
+
+ g_debug ("Synchronously querying call waiting status...");
+ mm_modem_voice_call_waiting_query_sync (ctx->modem_voice, NULL, &status, &error);
+ call_waiting_query_process_reply (error, status);
+ return;
+ }
+
+ g_warn_if_reached ();
+}
diff --git a/cli/mmcli-modem.c b/cli/mmcli-modem.c
index f119f6ef..5702d845 100644
--- a/cli/mmcli-modem.c
+++ b/cli/mmcli-modem.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2011-2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
@@ -32,6 +32,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -56,13 +57,14 @@ static gboolean set_power_state_off_flag;
static gboolean reset_flag;
static gchar *factory_reset_str;
static gchar *command_str;
-static gboolean list_bearers_flag;
static gchar *create_bearer_str;
static gchar *delete_bearer_str;
static gchar *set_current_capabilities_str;
static gchar *set_allowed_modes_str;
static gchar *set_preferred_mode_str;
static gchar *set_current_bands_str;
+static gint set_primary_sim_slot_int;
+static gboolean inhibit_flag;
static GOptionEntry entries[] = {
{ "monitor-state", 'w', 0, G_OPTION_ARG_NONE, &monitor_state_flag,
@@ -101,10 +103,6 @@ static GOptionEntry entries[] = {
"Send an AT command to the modem",
"[COMMAND]"
},
- { "list-bearers", 0, 0, G_OPTION_ARG_NONE, &list_bearers_flag,
- "List packet data bearers available in a given modem",
- NULL
- },
{ "create-bearer", 0, 0, G_OPTION_ARG_STRING, &create_bearer_str,
"Create a new packet data bearer in a given modem",
"[\"key=value,...\"]"
@@ -129,6 +127,14 @@ static GOptionEntry entries[] = {
"Set bands to be used by a given modem.",
"[BAND1|BAND2...]"
},
+ { "set-primary-sim-slot", 0, 0, G_OPTION_ARG_INT, &set_primary_sim_slot_int,
+ "Switch to the selected SIM slot",
+ "[SLOT NUMBER]"
+ },
+ { "inhibit", 0, 0, G_OPTION_ARG_NONE, &inhibit_flag,
+ "Inhibit the modem",
+ NULL
+ },
{ NULL }
};
@@ -139,7 +145,7 @@ mmcli_modem_get_option_group (void)
/* Status options */
group = g_option_group_new ("modem",
- "Modem options",
+ "Modem options:",
"Show modem options",
NULL,
NULL);
@@ -164,7 +170,6 @@ mmcli_modem_options_enabled (void)
set_power_state_low_flag +
set_power_state_off_flag +
reset_flag +
- list_bearers_flag +
!!create_bearer_str +
!!delete_bearer_str +
!!factory_reset_str +
@@ -172,7 +177,9 @@ mmcli_modem_options_enabled (void)
!!set_current_capabilities_str +
!!set_allowed_modes_str +
!!set_preferred_mode_str +
- !!set_current_bands_str);
+ !!set_current_bands_str +
+ (set_primary_sim_slot_int > 0) +
+ inhibit_flag);
if (n_actions == 0 && mmcli_get_common_modem_string ()) {
/* default to info */
@@ -193,7 +200,7 @@ mmcli_modem_options_enabled (void)
exit (EXIT_FAILURE);
}
- if (monitor_state_flag)
+ if (monitor_state_flag || inhibit_flag)
mmcli_force_async_operation ();
if (info_flag)
@@ -204,7 +211,7 @@ mmcli_modem_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -229,7 +236,43 @@ context_free (Context *ctx)
void
mmcli_modem_shutdown (void)
{
- context_free (ctx);
+ context_free ();
+}
+
+static void
+inhibition_cancelled (GCancellable *cancellable,
+ const gchar *uid)
+{
+ GError *error = NULL;
+
+ if (!mm_manager_uninhibit_device_sync (ctx->manager, uid, NULL, &error)) {
+ g_printerr ("error: couldn't uninhibit device: '%s'\n",
+ error ? error->message : "unknown error");
+ } else
+ g_print ("successfully uninhibited device with uid '%s'\n", uid);
+
+ mmcli_async_operation_done ();
+}
+
+static void
+inhibit_device_ready (MMManager *manager,
+ GAsyncResult *result,
+ gchar *uid)
+{
+ GError *error = NULL;
+
+ if (!mm_manager_inhibit_device_finish (manager, result, &error)) {
+ g_printerr ("error: couldn't inhibit device: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully inhibited device with uid '%s'\n", uid);
+ g_print ("type Ctrl+C to abort this program and remove the inhibition\n");
+
+ g_cancellable_connect (ctx->cancellable,
+ G_CALLBACK (inhibition_cancelled),
+ uid, g_free);
}
static void
@@ -248,8 +291,6 @@ print_bearer_short_info (MMBearer *bearer)
static void
print_modem_info (void)
{
- gchar *drivers_string;
- gchar *prefixed_revision;
gchar *supported_capabilities_string;
MMModemCapability *capabilities = NULL;
guint n_capabilities = 0;
@@ -266,7 +307,6 @@ print_modem_info (void)
gchar *current_bands_string;
gchar *supported_ip_families_string;
gchar *unlock_retries_string;
- gchar *own_numbers_string;
MMModemBand *bands = NULL;
guint n_bands = 0;
MMModemPortInfo *ports = NULL;
@@ -275,14 +315,8 @@ print_modem_info (void)
MMUnlockRetries *unlock_retries;
guint signal_quality = 0;
gboolean signal_quality_recent = FALSE;
- gchar *bearer_paths_string;
-
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE_UNKNOWN
-#define VALIDATE_UNKNOWN(str) (str ? str : "unknown")
-#undef VALIDATE_PATH
-#define VALIDATE_PATH(str) ((str && !g_str_equal (str, "/")) ? str : "none")
+ const gchar *sim_path;
+ const gchar **bearer_paths;
/* Strings in heap */
mm_modem_get_supported_capabilities (ctx->modem, &capabilities, &n_capabilities);
@@ -315,215 +349,148 @@ print_modem_info (void)
unlock_retries_string = mm_unlock_retries_build_string (unlock_retries);
g_object_unref (unlock_retries);
- if (mm_modem_get_own_numbers (ctx->modem)) {
- own_numbers_string = g_strjoinv (", ", (gchar **)mm_modem_get_own_numbers (ctx->modem));
- if (!own_numbers_string[0]) {
- g_free (own_numbers_string);
- own_numbers_string = NULL;
- }
- } else
- own_numbers_string = NULL;
+ signal_quality = mm_modem_get_signal_quality (ctx->modem, &signal_quality_recent);
- if (mm_modem_get_drivers (ctx->modem)) {
- drivers_string = g_strjoinv (", ", (gchar **)mm_modem_get_drivers (ctx->modem));
- if (!drivers_string[0]) {
- g_free (drivers_string);
- drivers_string = NULL;
+ mmcli_output_string (MMC_F_GENERAL_DBUS_PATH, mm_modem_get_path (ctx->modem));
+ mmcli_output_string (MMC_F_GENERAL_DEVICE_ID, mm_modem_get_device_identifier (ctx->modem));
+
+ mmcli_output_string (MMC_F_HARDWARE_MANUFACTURER, mm_modem_get_manufacturer (ctx->modem));
+ mmcli_output_string (MMC_F_HARDWARE_MODEL, mm_modem_get_model (ctx->modem));
+ mmcli_output_string (MMC_F_HARDWARE_REVISION, mm_modem_get_revision (ctx->modem));
+ mmcli_output_string (MMC_F_HARDWARE_CARRIER_CONF, mm_modem_get_carrier_configuration (ctx->modem));
+ mmcli_output_string (MMC_F_HARDWARE_CARRIER_CONF_REV, mm_modem_get_carrier_configuration_revision (ctx->modem));
+ mmcli_output_string (MMC_F_HARDWARE_HW_REVISION, mm_modem_get_hardware_revision (ctx->modem));
+ mmcli_output_string_multiline (MMC_F_HARDWARE_SUPPORTED_CAPABILITIES, supported_capabilities_string);
+ mmcli_output_string_multiline (MMC_F_HARDWARE_CURRENT_CAPABILITIES, current_capabilities_string);
+ mmcli_output_string (MMC_F_HARDWARE_EQUIPMENT_ID, mm_modem_get_equipment_identifier (ctx->modem));
+
+ mmcli_output_string (MMC_F_SYSTEM_DEVICE, mm_modem_get_device (ctx->modem));
+ mmcli_output_string_array (MMC_F_SYSTEM_DRIVERS, (const gchar **) mm_modem_get_drivers (ctx->modem), FALSE);
+ mmcli_output_string (MMC_F_SYSTEM_PLUGIN, mm_modem_get_plugin (ctx->modem));
+ mmcli_output_string (MMC_F_SYSTEM_PRIMARY_PORT, mm_modem_get_primary_port (ctx->modem));
+ mmcli_output_string_list (MMC_F_SYSTEM_PORTS, ports_string);
+
+ mmcli_output_string_array (MMC_F_NUMBERS_OWN, (const gchar **) mm_modem_get_own_numbers (ctx->modem), FALSE);
+
+ mmcli_output_string (MMC_F_STATUS_LOCK, mm_modem_lock_get_string (mm_modem_get_unlock_required (ctx->modem)));
+ mmcli_output_string_list (MMC_F_STATUS_UNLOCK_RETRIES, unlock_retries_string);
+ mmcli_output_state (mm_modem_get_state (ctx->modem), mm_modem_get_state_failed_reason (ctx->modem));
+ mmcli_output_string (MMC_F_STATUS_POWER_STATE, mm_modem_power_state_get_string (mm_modem_get_power_state (ctx->modem)));
+ mmcli_output_string_list (MMC_F_STATUS_ACCESS_TECH, access_technologies_string);
+ mmcli_output_signal_quality (signal_quality, signal_quality_recent);
+
+ mmcli_output_string_multiline (MMC_F_MODES_SUPPORTED, supported_modes_string);
+ mmcli_output_string_take (MMC_F_MODES_CURRENT, g_strdup_printf ("allowed: %s; preferred: %s",
+ allowed_modes_string, preferred_mode_string));
+
+ mmcli_output_string_list (MMC_F_BANDS_SUPPORTED, supported_bands_string);
+ mmcli_output_string_list (MMC_F_BANDS_CURRENT, current_bands_string);
+
+ mmcli_output_string_list (MMC_F_IP_SUPPORTED, supported_ip_families_string);
+
+ /* 3GPP */
+ {
+ const gchar *imei = NULL;
+ gchar *facility_locks = NULL;
+ const gchar *operator_code = NULL;
+ const gchar *operator_name = NULL;
+ const gchar *registration = NULL;
+ const gchar *packet_service_state = NULL;
+ const gchar *eps_ue_mode = NULL;
+ GList *pco_list = NULL;
+ const gchar *initial_eps_bearer_path = NULL;
+ const gchar *initial_eps_bearer_apn = NULL;
+ gchar *initial_eps_bearer_ip_family_str = NULL;
+ const gchar *initial_eps_bearer_user = NULL;
+ const gchar *initial_eps_bearer_password = NULL;
+
+ if (ctx->modem_3gpp) {
+ imei = mm_modem_3gpp_get_imei (ctx->modem_3gpp);
+ facility_locks = mm_modem_3gpp_facility_build_string_from_mask (mm_modem_3gpp_get_enabled_facility_locks (ctx->modem_3gpp));
+ operator_code = mm_modem_3gpp_get_operator_code (ctx->modem_3gpp);
+ operator_name = mm_modem_3gpp_get_operator_name (ctx->modem_3gpp);
+ registration = mm_modem_3gpp_registration_state_get_string (mm_modem_3gpp_get_registration_state (ctx->modem_3gpp));
+ packet_service_state = mm_modem_3gpp_packet_service_state_get_string (mm_modem_3gpp_get_packet_service_state (ctx->modem_3gpp));
+ eps_ue_mode = mm_modem_3gpp_eps_ue_mode_operation_get_string (mm_modem_3gpp_get_eps_ue_mode_operation (ctx->modem_3gpp));
+ pco_list = mm_modem_3gpp_get_pco (ctx->modem_3gpp);
+ initial_eps_bearer_path = mm_modem_3gpp_get_initial_eps_bearer_path (ctx->modem_3gpp);
+
+ if (mm_modem_get_current_capabilities (ctx->modem) & (MM_MODEM_CAPABILITY_LTE)) {
+ MMBearerProperties *initial_eps_bearer_properties;
+
+ initial_eps_bearer_properties = mm_modem_3gpp_peek_initial_eps_bearer_settings (ctx->modem_3gpp);
+ if (initial_eps_bearer_properties) {
+ initial_eps_bearer_apn = mm_bearer_properties_get_apn (initial_eps_bearer_properties);
+ initial_eps_bearer_ip_family_str = mm_bearer_ip_family_build_string_from_mask (mm_bearer_properties_get_ip_type (initial_eps_bearer_properties));
+ initial_eps_bearer_user = mm_bearer_properties_get_user (initial_eps_bearer_properties);
+ initial_eps_bearer_password = mm_bearer_properties_get_password (initial_eps_bearer_properties);
+ }
+ }
}
- } else
- drivers_string = NULL;
-
- /* Rework possible multiline strings */
- if (mm_modem_get_revision (ctx->modem))
- prefixed_revision = mmcli_prefix_newlines (" | ",
- mm_modem_get_revision (ctx->modem));
- else
- prefixed_revision = NULL;
-
- if (supported_modes_string) {
- gchar *prefixed;
-
- prefixed = mmcli_prefix_newlines (" | ",
- supported_modes_string);
- g_free (supported_modes_string);
- supported_modes_string = prefixed;
- }
- if (supported_capabilities_string) {
- gchar *prefixed;
+ mmcli_output_string (MMC_F_3GPP_IMEI, imei);
+ mmcli_output_string_list (MMC_F_3GPP_ENABLED_LOCKS, facility_locks);
+ mmcli_output_string (MMC_F_3GPP_OPERATOR_ID, operator_code);
+ mmcli_output_string (MMC_F_3GPP_OPERATOR_NAME, operator_name);
+ mmcli_output_string (MMC_F_3GPP_REGISTRATION, registration);
+ mmcli_output_string (MMC_F_3GPP_PACKET_SERVICE_STATE, packet_service_state);
+ mmcli_output_string (MMC_F_3GPP_EPS_UE_MODE, eps_ue_mode);
+ mmcli_output_string (MMC_F_3GPP_EPS_INITIAL_BEARER_PATH, g_strcmp0 (initial_eps_bearer_path, "/") != 0 ? initial_eps_bearer_path : NULL);
+ mmcli_output_string (MMC_F_3GPP_EPS_BEARER_SETTINGS_APN, initial_eps_bearer_apn);
+ mmcli_output_string_take (MMC_F_3GPP_EPS_BEARER_SETTINGS_IP_TYPE, initial_eps_bearer_ip_family_str);
+ mmcli_output_string (MMC_F_3GPP_EPS_BEARER_SETTINGS_USER, initial_eps_bearer_user);
+ mmcli_output_string (MMC_F_3GPP_EPS_BEARER_SETTINGS_PASSWORD, initial_eps_bearer_password);
+ mmcli_output_pco_list (pco_list);
- prefixed = mmcli_prefix_newlines (" | ",
- supported_capabilities_string);
- g_free (supported_capabilities_string);
- supported_capabilities_string = prefixed;
+ g_free (facility_locks);
+ g_list_free_full (pco_list, g_object_unref);
}
- /* Get signal quality info */
- signal_quality = mm_modem_get_signal_quality (ctx->modem, &signal_quality_recent);
-
- if (mm_modem_get_bearer_paths (ctx->modem)) {
- bearer_paths_string = g_strjoinv (", ", (gchar **)mm_modem_get_bearer_paths (ctx->modem));
- if (!bearer_paths_string[0]) {
- g_free (bearer_paths_string);
- bearer_paths_string = NULL;
+ /* CDMA */
+ {
+ const gchar *meid = NULL;
+ const gchar *esn = NULL;
+ gchar *sid = NULL;
+ gchar *nid = NULL;
+ const gchar *registration_cdma1x = NULL;
+ const gchar *registration_evdo = NULL;
+ const gchar *activation = NULL;
+
+ if (ctx->modem_cdma) {
+ guint sid_n;
+ guint nid_n;
+
+ meid = mm_modem_cdma_get_meid (ctx->modem_cdma);
+ esn = mm_modem_cdma_get_esn (ctx->modem_cdma);
+ sid_n = mm_modem_cdma_get_sid (ctx->modem_cdma);
+ if (sid_n != MM_MODEM_CDMA_SID_UNKNOWN)
+ sid = g_strdup_printf ("%u", sid_n);
+ nid_n = mm_modem_cdma_get_nid (ctx->modem_cdma);
+ if (nid_n != MM_MODEM_CDMA_NID_UNKNOWN)
+ nid = g_strdup_printf ("%u", nid_n);
+ registration_cdma1x = mm_modem_cdma_registration_state_get_string (mm_modem_cdma_get_cdma1x_registration_state (ctx->modem_cdma));
+ registration_evdo = mm_modem_cdma_registration_state_get_string (mm_modem_cdma_get_evdo_registration_state (ctx->modem_cdma));
+ activation = mm_modem_cdma_activation_state_get_string (mm_modem_cdma_get_activation_state (ctx->modem_cdma));
}
- } else
- bearer_paths_string = NULL;
-
- /* Global IDs */
- g_print ("\n"
- "%s (device id '%s')\n",
- VALIDATE_UNKNOWN (mm_modem_get_path (ctx->modem)),
- VALIDATE_UNKNOWN (mm_modem_get_device_identifier (ctx->modem)));
-
- /* Hardware related stuff */
- g_print (" -------------------------\n"
- " Hardware | manufacturer: '%s'\n"
- " | model: '%s'\n"
- " | revision: '%s'\n"
- " | supported: '%s'\n"
- " | current: '%s'\n"
- " | equipment id: '%s'\n",
- VALIDATE_UNKNOWN (mm_modem_get_manufacturer (ctx->modem)),
- VALIDATE_UNKNOWN (mm_modem_get_model (ctx->modem)),
- VALIDATE_UNKNOWN (prefixed_revision),
- VALIDATE_UNKNOWN (supported_capabilities_string),
- VALIDATE_UNKNOWN (current_capabilities_string),
- VALIDATE_UNKNOWN (mm_modem_get_equipment_identifier (ctx->modem)));
-
- /* System related stuff */
- g_print (" -------------------------\n"
- " System | device: '%s'\n"
- " | drivers: '%s'\n"
- " | plugin: '%s'\n"
- " | primary port: '%s'\n"
- " | ports: '%s'\n",
- VALIDATE_UNKNOWN (mm_modem_get_device (ctx->modem)),
- VALIDATE_UNKNOWN (drivers_string),
- VALIDATE_UNKNOWN (mm_modem_get_plugin (ctx->modem)),
- VALIDATE_UNKNOWN (mm_modem_get_primary_port (ctx->modem)),
- VALIDATE_UNKNOWN (ports_string));
-
- /* Numbers related stuff */
- g_print (" -------------------------\n"
- " Numbers | own : '%s'\n",
- VALIDATE_UNKNOWN (own_numbers_string));
-
- /* Status related stuff */
- g_print (" -------------------------\n"
- " Status | lock: '%s'\n"
- " | unlock retries: '%s'\n"
- " | state: '%s'\n",
- mm_modem_lock_get_string (mm_modem_get_unlock_required (ctx->modem)),
- VALIDATE_UNKNOWN (unlock_retries_string),
- VALIDATE_UNKNOWN (mm_modem_state_get_string (mm_modem_get_state (ctx->modem))));
-
- if (mm_modem_get_state (ctx->modem) == MM_MODEM_STATE_FAILED)
- g_print (" | failed reason: '%s'\n",
- VALIDATE_UNKNOWN (mm_modem_state_failed_reason_get_string (mm_modem_get_state_failed_reason (ctx->modem))));
-
- g_print (" | power state: '%s'\n"
- " | access tech: '%s'\n"
- " | signal quality: '%u' (%s)\n",
- VALIDATE_UNKNOWN (mm_modem_power_state_get_string (mm_modem_get_power_state (ctx->modem))),
- VALIDATE_UNKNOWN (access_technologies_string),
- signal_quality, signal_quality_recent ? "recent" : "cached");
-
- /* Modes */
- g_print (" -------------------------\n"
- " Modes | supported: '%s'\n"
- " | current: 'allowed: %s; preferred: %s'\n",
- VALIDATE_UNKNOWN (supported_modes_string),
- VALIDATE_UNKNOWN (allowed_modes_string),
- VALIDATE_UNKNOWN (preferred_mode_string));
-
- /* Band related stuff */
- g_print (" -------------------------\n"
- " Bands | supported: '%s'\n"
- " | current: '%s'\n",
- VALIDATE_UNKNOWN (supported_bands_string),
- VALIDATE_UNKNOWN (current_bands_string));
-
- /* IP families */
- g_print (" -------------------------\n"
- " IP | supported: '%s'\n",
- VALIDATE_UNKNOWN (supported_ip_families_string));
-
- /* If available, 3GPP related stuff */
- if (ctx->modem_3gpp) {
- gchar *facility_locks;
-
- facility_locks = (mm_modem_3gpp_facility_build_string_from_mask (
- mm_modem_3gpp_get_enabled_facility_locks (ctx->modem_3gpp)));
- g_print (" -------------------------\n"
- " 3GPP | imei: '%s'\n"
- " | enabled locks: '%s'\n"
- " | operator id: '%s'\n"
- " | operator name: '%s'\n"
- " | subscription: '%s'\n"
- " | registration: '%s'\n",
- VALIDATE_UNKNOWN (mm_modem_3gpp_get_imei (ctx->modem_3gpp)),
- facility_locks,
- VALIDATE_UNKNOWN (mm_modem_3gpp_get_operator_code (ctx->modem_3gpp)),
- VALIDATE_UNKNOWN (mm_modem_3gpp_get_operator_name (ctx->modem_3gpp)),
- mm_modem_3gpp_subscription_state_get_string (
- mm_modem_3gpp_get_subscription_state ((ctx->modem_3gpp))),
- mm_modem_3gpp_registration_state_get_string (
- mm_modem_3gpp_get_registration_state ((ctx->modem_3gpp))));
- g_free (facility_locks);
+ mmcli_output_string (MMC_F_CDMA_MEID, meid);
+ mmcli_output_string (MMC_F_CDMA_ESN, esn);
+ mmcli_output_string_take (MMC_F_CDMA_SID, sid);
+ mmcli_output_string_take (MMC_F_CDMA_NID, nid);
+ mmcli_output_string (MMC_F_CDMA_REGISTRATION_CDMA1X, registration_cdma1x);
+ mmcli_output_string (MMC_F_CDMA_REGISTRATION_EVDO, registration_evdo);
+ mmcli_output_string (MMC_F_CDMA_ACTIVATION, activation);
}
- /* If available, CDMA related stuff */
- if (ctx->modem_cdma) {
- guint sid;
- guint nid;
- gchar *sid_str;
- gchar *nid_str;
-
- sid = mm_modem_cdma_get_sid (ctx->modem_cdma);
- sid_str = (sid != MM_MODEM_CDMA_SID_UNKNOWN ?
- g_strdup_printf ("%u", sid) :
- NULL);
- nid = mm_modem_cdma_get_nid (ctx->modem_cdma);
- nid_str = (nid != MM_MODEM_CDMA_NID_UNKNOWN ?
- g_strdup_printf ("%u", nid) :
- NULL);
-
- g_print (" -------------------------\n"
- " CDMA | meid: '%s'\n"
- " | esn: '%s'\n"
- " | sid: '%s'\n"
- " | nid: '%s'\n"
- " | registration: CDMA1x '%s'\n"
- " | EV-DO '%s'\n"
- " | activation: '%s'\n",
- VALIDATE_UNKNOWN (mm_modem_cdma_get_meid (ctx->modem_cdma)),
- VALIDATE_UNKNOWN (mm_modem_cdma_get_esn (ctx->modem_cdma)),
- VALIDATE_UNKNOWN (sid_str),
- VALIDATE_UNKNOWN (nid_str),
- mm_modem_cdma_registration_state_get_string (
- mm_modem_cdma_get_cdma1x_registration_state (ctx->modem_cdma)),
- mm_modem_cdma_registration_state_get_string (
- mm_modem_cdma_get_evdo_registration_state (ctx->modem_cdma)),
- mm_modem_cdma_activation_state_get_string (
- mm_modem_cdma_get_activation_state (ctx->modem_cdma)));
-
- g_free (sid_str);
- g_free (nid_str);
- }
+ sim_path = mm_modem_get_sim_path (ctx->modem);
+ mmcli_output_string (MMC_F_SIM_PATH, g_strcmp0 (sim_path, "/") != 0 ? sim_path : NULL);
+ mmcli_output_sim_slots (mm_modem_dup_sim_slot_paths (ctx->modem),
+ mm_modem_get_primary_sim_slot (ctx->modem));
- /* SIM */
- g_print (" -------------------------\n"
- " SIM | path: '%s'\n",
- VALIDATE_PATH (mm_modem_get_sim_path (ctx->modem)));
- g_print ("\n");
+ bearer_paths = (const gchar **) mm_modem_get_bearer_paths (ctx->modem);
+ mmcli_output_string_array (MMC_F_BEARER_PATHS, (bearer_paths && bearer_paths[0]) ? bearer_paths : NULL, TRUE);
- /* Bearers */
- g_print (" -------------------------\n"
- " Bearers | paths: '%s'\n",
- VALIDATE_PATH (bearer_paths_string));
- g_print ("\n");
+ mmcli_output_dump ();
g_free (ports_string);
g_free (supported_ip_families_string);
@@ -532,14 +499,10 @@ print_modem_info (void)
g_free (access_technologies_string);
g_free (supported_capabilities_string);
g_free (current_capabilities_string);
- g_free (prefixed_revision);
g_free (allowed_modes_string);
g_free (preferred_mode_string);
g_free (supported_modes_string);
g_free (unlock_retries_string);
- g_free (own_numbers_string);
- g_free (drivers_string);
- g_free (bearer_paths_string);
}
static void
@@ -722,48 +685,6 @@ command_get_timeout (MMModem *modem)
}
static void
-list_bearers_process_reply (GList *result,
- const GError *error)
-{
- if (error) {
- g_printerr ("error: couldn't list bearers: '%s'\n",
- error->message);
- exit (EXIT_FAILURE);
- }
-
- g_print ("\n");
- if (!result) {
- g_print ("No bearers were found\n");
- } else {
- GList *l;
-
- g_print ("Found %u bearers:\n", g_list_length (result));
- for (l = result; l; l = g_list_next (l)) {
- MMBearer *bearer = MM_BEARER (l->data);
-
- g_print ("\n");
- print_bearer_short_info (bearer);
- g_object_unref (bearer);
- }
- g_list_free (result);
- }
-}
-
-static void
-list_bearers_ready (MMModem *modem,
- GAsyncResult *result,
- gpointer nothing)
-{
- GList *operation_result;
- GError *error = NULL;
-
- operation_result = mm_modem_list_bearers_finish (modem, result, &error);
- list_bearers_process_reply (operation_result, error);
-
- mmcli_async_operation_done ();
-}
-
-static void
create_bearer_process_reply (MMBearer *bearer,
const GError *error)
{
@@ -979,6 +900,32 @@ parse_current_bands (MMModemBand **bands,
}
static void
+set_primary_sim_slot_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't request primary SIM switch: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully requested primary SIM switch in modem\n");
+}
+
+static void
+set_primary_sim_slot_ready (MMModem *modem,
+ GAsyncResult *result)
+{
+ gboolean operation_result;
+ g_autoptr(GError) error = NULL;
+
+ operation_result = mm_modem_set_primary_sim_slot_finish (modem, result, &error);
+ set_primary_sim_slot_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
state_changed (MMModem *modem,
MMModemState old_state,
MMModemState new_state,
@@ -993,6 +940,19 @@ state_changed (MMModem *modem,
}
static void
+device_removed (MMManager *manager,
+ MMObject *object)
+{
+ if (object != ctx->object)
+ return;
+
+ g_print ("\t%s: Removed\n", mm_object_get_path (object));
+ fflush (stdout);
+
+ mmcli_async_operation_done ();
+}
+
+static void
get_modem_ready (GObject *source,
GAsyncResult *result,
gpointer none)
@@ -1022,6 +982,11 @@ get_modem_ready (GObject *source,
G_CALLBACK (state_changed),
NULL);
+ g_signal_connect (ctx->manager,
+ "object-removed",
+ G_CALLBACK (device_removed),
+ NULL);
+
current = mm_modem_get_state (ctx->modem);
g_print ("\t%s: Initial state, '%s'\n",
mm_object_get_path (ctx->object),
@@ -1127,16 +1092,6 @@ get_modem_ready (GObject *source,
return;
}
- /* Request to list bearers? */
- if (list_bearers_flag) {
- g_debug ("Asynchronously listing bearers in modem...");
- mm_modem_list_bearers (ctx->modem,
- ctx->cancellable,
- (GAsyncReadyCallback)list_bearers_ready,
- NULL);
- return;
- }
-
/* Request to create a new bearer? */
if (create_bearer_str) {
GError *error = NULL;
@@ -1212,6 +1167,31 @@ get_modem_ready (GObject *source,
return;
}
+ /* Request to switch SIM? */
+ if (set_primary_sim_slot_int > 0) {
+ mm_modem_set_primary_sim_slot (ctx->modem,
+ set_primary_sim_slot_int,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_primary_sim_slot_ready,
+ NULL);
+ return;
+ }
+
+ /* Request to inhibit the modem? */
+ if (inhibit_flag) {
+ gchar *uid;
+
+ g_debug ("Asynchronously inhibiting modem...");
+ uid = mm_modem_dup_device (ctx->modem);
+
+ mm_manager_inhibit_device (ctx->manager,
+ uid,
+ ctx->cancellable,
+ (GAsyncReadyCallback)inhibit_device_ready,
+ uid);
+ return;
+ }
+
g_warn_if_reached ();
}
@@ -1238,7 +1218,7 @@ mmcli_modem_run_synchronous (GDBusConnection *connection)
{
GError *error = NULL;
- if (monitor_state_flag)
+ if (monitor_state_flag || inhibit_flag)
g_assert_not_reached ();
/* Initialize context */
@@ -1358,20 +1338,9 @@ mmcli_modem_run_synchronous (GDBusConnection *connection)
return;
}
- /* Request to list the bearers? */
- if (list_bearers_flag) {
- GList *result;
-
- g_debug ("Synchronously listing bearers...");
- result = mm_modem_list_bearers_sync (ctx->modem, NULL, &error);
- list_bearers_process_reply (result, error);
- return;
- }
-
/* Request to create a new bearer? */
if (create_bearer_str) {
MMBearer *bearer;
- GError *error = NULL;
MMBearerProperties *properties;
properties = mm_bearer_properties_new_from_string (create_bearer_str, &error);
@@ -1467,5 +1436,14 @@ mmcli_modem_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Request to switch current SIM? */
+ if (set_primary_sim_slot_int > 0) {
+ gboolean result;
+
+ result = mm_modem_set_primary_sim_slot_sync (ctx->modem, set_primary_sim_slot_int, NULL, &error);
+ set_primary_sim_slot_process_reply (result, error);
+ return;
+ }
+
g_warn_if_reached ();
}
diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c
new file mode 100644
index 00000000..712018b6
--- /dev/null
+++ b/cli/mmcli-output.c
@@ -0,0 +1,1608 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <libmm-glib.h>
+#include "mm-common-helpers.h"
+#include "mmcli-output.h"
+
+/******************************************************************************/
+/* List of sections (grouped fields) displayed in the human-friendly output */
+
+typedef struct {
+ const gchar *name;
+} SectionInfo;
+
+static SectionInfo section_infos[] = {
+ [MMC_S_MODEM_GENERAL] = { "General" },
+ [MMC_S_MODEM_HARDWARE] = { "Hardware" },
+ [MMC_S_MODEM_SYSTEM] = { "System" },
+ [MMC_S_MODEM_NUMBERS] = { "Numbers" },
+ [MMC_S_MODEM_STATUS] = { "Status" },
+ [MMC_S_MODEM_MODES] = { "Modes" },
+ [MMC_S_MODEM_BANDS] = { "Bands" },
+ [MMC_S_MODEM_IP] = { "IP" },
+ [MMC_S_MODEM_3GPP] = { "3GPP" },
+ [MMC_S_MODEM_3GPP_EPS] = { "3GPP EPS" },
+ [MMC_S_MODEM_3GPP_SCAN] = { "3GPP scan" },
+ [MMC_S_MODEM_3GPP_USSD] = { "3GPP USSD" },
+ [MMC_S_MODEM_3GPP_PROFILE_MANAGER] = { "3GPP profile manager" },
+ [MMC_S_MODEM_CDMA] = { "CDMA" },
+ [MMC_S_MODEM_SIM] = { "SIM" },
+ [MMC_S_MODEM_BEARER] = { "Bearer" },
+ [MMC_S_MODEM_TIME] = { "Time" },
+ [MMC_S_MODEM_TIMEZONE] = { "Timezone" },
+ [MMC_S_MODEM_MESSAGING] = { "Messaging" },
+ [MMC_S_MODEM_SIGNAL] = { "Signal" },
+ [MMC_S_MODEM_SIGNAL_CDMA1X] = { "CDMA1x" },
+ [MMC_S_MODEM_SIGNAL_EVDO] = { "EV-DO" },
+ [MMC_S_MODEM_SIGNAL_GSM] = { "GSM" },
+ [MMC_S_MODEM_SIGNAL_UMTS] = { "UMTS" },
+ [MMC_S_MODEM_SIGNAL_LTE] = { "LTE" },
+ [MMC_S_MODEM_SIGNAL_5G] = { "5G" },
+ [MMC_S_MODEM_OMA] = { "OMA" },
+ [MMC_S_MODEM_OMA_CURRENT] = { "Current session" },
+ [MMC_S_MODEM_OMA_PENDING] = { "Pending sessions" },
+ [MMC_S_MODEM_LOCATION] = { "Location" },
+ [MMC_S_MODEM_LOCATION_3GPP] = { "3GPP" },
+ [MMC_S_MODEM_LOCATION_GPS] = { "GPS" },
+ [MMC_S_MODEM_LOCATION_CDMABS] = { "CDMA BS" },
+ [MMC_S_MODEM_FIRMWARE] = { "Firmware" },
+ [MMC_S_MODEM_FIRMWARE_FASTBOOT] = { "Fastboot settings" },
+ [MMC_S_MODEM_VOICE] = { "Voice" },
+ [MMC_S_MODEM_SAR] = { "SAR" },
+ [MMC_S_BEARER_GENERAL] = { "General" },
+ [MMC_S_BEARER_STATUS] = { "Status" },
+ [MMC_S_BEARER_PROPERTIES] = { "Properties" },
+ [MMC_S_BEARER_IPV4_CONFIG] = { "IPv4 configuration" },
+ [MMC_S_BEARER_IPV6_CONFIG] = { "IPv6 configuration" },
+ [MMC_S_BEARER_STATS] = { "Statistics" },
+ [MMC_S_CALL_GENERAL] = { "General" },
+ [MMC_S_CALL_PROPERTIES] = { "Properties" },
+ [MMC_S_CALL_AUDIO_FORMAT] = { "Audio format" },
+ [MMC_S_SMS_GENERAL] = { "General" },
+ [MMC_S_SMS_CONTENT] = { "Content" },
+ [MMC_S_SMS_PROPERTIES] = { "Properties" },
+ [MMC_S_SIM_GENERAL] = { "General" },
+ [MMC_S_SIM_PROPERTIES] = { "Properties" },
+};
+
+/******************************************************************************/
+/* List of fields */
+
+typedef struct {
+ const gchar *key;
+ const gchar *name;
+ MmcS section;
+} FieldInfo;
+
+static FieldInfo field_infos[] = {
+ [MMC_F_GENERAL_DBUS_PATH] = { "modem.dbus-path", "path", MMC_S_MODEM_GENERAL, },
+ [MMC_F_GENERAL_DEVICE_ID] = { "modem.generic.device-identifier", "device id", MMC_S_MODEM_GENERAL, },
+ [MMC_F_HARDWARE_MANUFACTURER] = { "modem.generic.manufacturer", "manufacturer", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_MODEL] = { "modem.generic.model", "model", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_REVISION] = { "modem.generic.revision", "firmware revision", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_CARRIER_CONF] = { "modem.generic.carrier-configuration", "carrier config", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_CARRIER_CONF_REV] = { "modem.generic.carrier-configuration-revision", "carrier config revision", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_HW_REVISION] = { "modem.generic.hardware-revision", "h/w revision", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_SUPPORTED_CAPABILITIES] = { "modem.generic.supported-capabilities", "supported", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_CURRENT_CAPABILITIES] = { "modem.generic.current-capabilities", "current", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_HARDWARE_EQUIPMENT_ID] = { "modem.generic.equipment-identifier", "equipment id", MMC_S_MODEM_HARDWARE, },
+ [MMC_F_SYSTEM_DEVICE] = { "modem.generic.device", "device", MMC_S_MODEM_SYSTEM, },
+ [MMC_F_SYSTEM_DRIVERS] = { "modem.generic.drivers", "drivers", MMC_S_MODEM_SYSTEM, },
+ [MMC_F_SYSTEM_PLUGIN] = { "modem.generic.plugin", "plugin", MMC_S_MODEM_SYSTEM, },
+ [MMC_F_SYSTEM_PRIMARY_PORT] = { "modem.generic.primary-port", "primary port", MMC_S_MODEM_SYSTEM, },
+ [MMC_F_SYSTEM_PORTS] = { "modem.generic.ports", "ports", MMC_S_MODEM_SYSTEM, },
+ [MMC_F_NUMBERS_OWN] = { "modem.generic.own-numbers", "own", MMC_S_MODEM_NUMBERS, },
+ [MMC_F_STATUS_LOCK] = { "modem.generic.unlock-required", "lock", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_UNLOCK_RETRIES] = { "modem.generic.unlock-retries", "unlock retries", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_STATE] = { "modem.generic.state", "state", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_FAILED_REASON] = { "modem.generic.state-failed-reason", "failed reason", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_POWER_STATE] = { "modem.generic.power-state", "power state", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_ACCESS_TECH] = { "modem.generic.access-technologies", "access tech", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_SIGNAL_QUALITY_VALUE] = { "modem.generic.signal-quality.value", "signal quality", MMC_S_MODEM_STATUS, },
+ [MMC_F_STATUS_SIGNAL_QUALITY_RECENT] = { "modem.generic.signal-quality.recent", NULL, MMC_S_UNKNOWN, },
+ [MMC_F_MODES_SUPPORTED] = { "modem.generic.supported-modes", "supported", MMC_S_MODEM_MODES, },
+ [MMC_F_MODES_CURRENT] = { "modem.generic.current-modes", "current", MMC_S_MODEM_MODES, },
+ [MMC_F_BANDS_SUPPORTED] = { "modem.generic.supported-bands", "supported", MMC_S_MODEM_BANDS, },
+ [MMC_F_BANDS_CURRENT] = { "modem.generic.current-bands", "current", MMC_S_MODEM_BANDS, },
+ [MMC_F_IP_SUPPORTED] = { "modem.generic.supported-ip-families", "supported", MMC_S_MODEM_IP, },
+ [MMC_F_3GPP_IMEI] = { "modem.3gpp.imei", "imei", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_ENABLED_LOCKS] = { "modem.3gpp.enabled-locks", "enabled locks", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_OPERATOR_ID] = { "modem.3gpp.operator-code", "operator id", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_OPERATOR_NAME] = { "modem.3gpp.operator-name", "operator name", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_REGISTRATION] = { "modem.3gpp.registration-state", "registration", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_PACKET_SERVICE_STATE] = { "modem.3gpp.packet-service-state", "packet service state", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_PCO] = { "modem.3gpp.pco", "pco", MMC_S_MODEM_3GPP, },
+ [MMC_F_3GPP_EPS_UE_MODE] = { "modem.3gpp.eps.ue-mode-operation", "ue mode of operation", MMC_S_MODEM_3GPP_EPS, },
+ [MMC_F_3GPP_EPS_INITIAL_BEARER_PATH] = { "modem.3gpp.eps.initial-bearer.dbus-path", "initial bearer path", MMC_S_MODEM_3GPP_EPS, },
+ [MMC_F_3GPP_EPS_BEARER_SETTINGS_APN] = { "modem.3gpp.eps.initial-bearer.settings.apn", "initial bearer apn", MMC_S_MODEM_3GPP_EPS, },
+ [MMC_F_3GPP_EPS_BEARER_SETTINGS_IP_TYPE] = { "modem.3gpp.eps.initial-bearer.settings.ip-type", "initial bearer ip type", MMC_S_MODEM_3GPP_EPS, },
+ [MMC_F_3GPP_EPS_BEARER_SETTINGS_USER] = { "modem.3gpp.eps.initial-bearer.settings.user", "initial bearer user", MMC_S_MODEM_3GPP_EPS, },
+ [MMC_F_3GPP_EPS_BEARER_SETTINGS_PASSWORD] = { "modem.3gpp.eps.initial-bearer.settings.password", "initial bearer password", MMC_S_MODEM_3GPP_EPS, },
+ [MMC_F_3GPP_SCAN_NETWORKS] = { "modem.3gpp.scan-networks", "networks", MMC_S_MODEM_3GPP_SCAN, },
+ [MMC_F_3GPP_USSD_STATUS] = { "modem.3gpp.ussd.status", "status", MMC_S_MODEM_3GPP_USSD, },
+ [MMC_F_3GPP_USSD_NETWORK_REQUEST] = { "modem.3gpp.ussd.network-request", "network request", MMC_S_MODEM_3GPP_USSD, },
+ [MMC_F_3GPP_USSD_NETWORK_NOTIFICATION] = { "modem.3gpp.ussd.network-notification", "network notification", MMC_S_MODEM_3GPP_USSD, },
+ [MMC_F_3GPP_PROFILE_MANAGER_LIST] = { "modem.3gpp.profile-manager.list", "list", MMC_S_MODEM_3GPP_PROFILE_MANAGER, },
+ [MMC_F_3GPP_PROFILE_MANAGER_SET] = { "modem.3gpp.profile-manager.set", "set", MMC_S_MODEM_3GPP_PROFILE_MANAGER, },
+ [MMC_F_CDMA_MEID] = { "modem.cdma.meid", "meid", MMC_S_MODEM_CDMA, },
+ [MMC_F_CDMA_ESN] = { "modem.cdma.esn", "esn", MMC_S_MODEM_CDMA, },
+ [MMC_F_CDMA_SID] = { "modem.cdma.sid", "sid", MMC_S_MODEM_CDMA, },
+ [MMC_F_CDMA_NID] = { "modem.cdma.nid", "nid", MMC_S_MODEM_CDMA, },
+ [MMC_F_CDMA_REGISTRATION_CDMA1X] = { "modem.cdma.cdma1x-registration-state", "registration cdma1x", MMC_S_MODEM_CDMA, },
+ [MMC_F_CDMA_REGISTRATION_EVDO] = { "modem.cdma.evdo-registration-state", "registration evdo", MMC_S_MODEM_CDMA, },
+ [MMC_F_CDMA_ACTIVATION] = { "modem.cdma.activation-state", "activation", MMC_S_MODEM_CDMA, },
+ [MMC_F_SIM_PATH] = { "modem.generic.sim", "primary sim path", MMC_S_MODEM_SIM, },
+ [MMC_F_SIM_PRIMARY_SLOT] = { "modem.generic.primary-sim-slot", NULL, MMC_S_MODEM_SIM, },
+ [MMC_F_SIM_SLOT_PATHS] = { "modem.generic.sim-slots", "sim slot paths", MMC_S_MODEM_SIM, },
+ [MMC_F_BEARER_PATHS] = { "modem.generic.bearers", "paths", MMC_S_MODEM_BEARER, },
+ [MMC_F_TIME_CURRENT] = { "modem.time.current", "current", MMC_S_MODEM_TIME, },
+ [MMC_F_TIMEZONE_CURRENT] = { "modem.timezone.current", "current", MMC_S_MODEM_TIMEZONE, },
+ [MMC_F_TIMEZONE_DST_OFFSET] = { "modem.time.dst-offset", "dst offset", MMC_S_MODEM_TIMEZONE, },
+ [MMC_F_TIMEZONE_LEAP_SECONDS] = { "modem.time.leap-seconds", "leap seconds", MMC_S_MODEM_TIMEZONE, },
+ [MMC_F_MESSAGING_SUPPORTED_STORAGES] = { "modem.messaging.supported-storages", "supported storages", MMC_S_MODEM_MESSAGING, },
+ [MMC_F_MESSAGING_DEFAULT_STORAGES] = { "modem.messaging.default-storages", "default storages", MMC_S_MODEM_MESSAGING, },
+ [MMC_F_SIGNAL_REFRESH_RATE] = { "modem.signal.refresh.rate", "refresh rate", MMC_S_MODEM_SIGNAL, },
+ [MMC_F_SIGNAL_RSSI_THRESHOLD] = { "modem.signal.threshold.rssi", "rssi threshold", MMC_S_MODEM_SIGNAL, },
+ [MMC_F_SIGNAL_ERROR_RATE_THRESHOLD] = { "modem.signal.threshold.error-rate", "error rate threshold", MMC_S_MODEM_SIGNAL, },
+ [MMC_F_SIGNAL_CDMA1X_RSSI] = { "modem.signal.cdma1x.rssi", "rssi", MMC_S_MODEM_SIGNAL_CDMA1X, },
+ [MMC_F_SIGNAL_CDMA1X_ECIO] = { "modem.signal.cdma1x.ecio", "ecio", MMC_S_MODEM_SIGNAL_CDMA1X, },
+ [MMC_F_SIGNAL_CDMA1X_ERROR_RATE] = { "modem.signal.cdma1x.error-rate", "error rate", MMC_S_MODEM_SIGNAL_CDMA1X, },
+ [MMC_F_SIGNAL_EVDO_RSSI] = { "modem.signal.evdo.rssi", "rssi", MMC_S_MODEM_SIGNAL_EVDO, },
+ [MMC_F_SIGNAL_EVDO_ECIO] = { "modem.signal.evdo.ecio", "ecio", MMC_S_MODEM_SIGNAL_EVDO, },
+ [MMC_F_SIGNAL_EVDO_SINR] = { "modem.signal.evdo.sinr", "sinr", MMC_S_MODEM_SIGNAL_EVDO, },
+ [MMC_F_SIGNAL_EVDO_IO] = { "modem.signal.evdo.io", "io", MMC_S_MODEM_SIGNAL_EVDO, },
+ [MMC_F_SIGNAL_EVDO_ERROR_RATE] = { "modem.signal.evdo.error-rate", "error rate", MMC_S_MODEM_SIGNAL_EVDO, },
+ [MMC_F_SIGNAL_GSM_RSSI] = { "modem.signal.gsm.rssi", "rssi", MMC_S_MODEM_SIGNAL_GSM, },
+ [MMC_F_SIGNAL_GSM_ERROR_RATE] = { "modem.signal.gsm.error-rate", "error rate", MMC_S_MODEM_SIGNAL_GSM, },
+ [MMC_F_SIGNAL_UMTS_RSSI] = { "modem.signal.umts.rssi", "rssi", MMC_S_MODEM_SIGNAL_UMTS, },
+ [MMC_F_SIGNAL_UMTS_RSCP] = { "modem.signal.umts.rscp", "rscp", MMC_S_MODEM_SIGNAL_UMTS, },
+ [MMC_F_SIGNAL_UMTS_ECIO] = { "modem.signal.umts.ecio", "ecio", MMC_S_MODEM_SIGNAL_UMTS, },
+ [MMC_F_SIGNAL_UMTS_ERROR_RATE] = { "modem.signal.umts.error-rate", "error rate", MMC_S_MODEM_SIGNAL_UMTS, },
+ [MMC_F_SIGNAL_LTE_RSSI] = { "modem.signal.lte.rssi", "rssi", MMC_S_MODEM_SIGNAL_LTE, },
+ [MMC_F_SIGNAL_LTE_RSRQ] = { "modem.signal.lte.rsrq", "rsrq", MMC_S_MODEM_SIGNAL_LTE, },
+ [MMC_F_SIGNAL_LTE_RSRP] = { "modem.signal.lte.rsrp", "rsrp", MMC_S_MODEM_SIGNAL_LTE, },
+ [MMC_F_SIGNAL_LTE_SNR] = { "modem.signal.lte.snr", "s/n", MMC_S_MODEM_SIGNAL_LTE, },
+ [MMC_F_SIGNAL_LTE_ERROR_RATE] = { "modem.signal.lte.error-rate", "error rate", MMC_S_MODEM_SIGNAL_LTE, },
+ [MMC_F_SIGNAL_5G_RSRQ] = { "modem.signal.5g.rsrq", "rsrq", MMC_S_MODEM_SIGNAL_5G, },
+ [MMC_F_SIGNAL_5G_RSRP] = { "modem.signal.5g.rsrp", "rsrp", MMC_S_MODEM_SIGNAL_5G, },
+ [MMC_F_SIGNAL_5G_SNR] = { "modem.signal.5g.snr", "s/n", MMC_S_MODEM_SIGNAL_5G, },
+ [MMC_F_SIGNAL_5G_ERROR_RATE] = { "modem.signal.5g.error-rate", "error rate", MMC_S_MODEM_SIGNAL_5G, },
+ [MMC_F_OMA_FEATURES] = { "modem.oma.features", "features", MMC_S_MODEM_OMA, },
+ [MMC_F_OMA_CURRENT_TYPE] = { "modem.oma.current.type", "type", MMC_S_MODEM_OMA_CURRENT, },
+ [MMC_F_OMA_CURRENT_STATE] = { "modem.oma.current.state", "state", MMC_S_MODEM_OMA_CURRENT, },
+ [MMC_F_OMA_PENDING_SESSIONS] = { "modem.oma.pending-sessions", "sessions", MMC_S_MODEM_OMA_PENDING, },
+ [MMC_F_LOCATION_CAPABILITIES] = { "modem.location.capabilities", "capabilities", MMC_S_MODEM_LOCATION, },
+ [MMC_F_LOCATION_ENABLED] = { "modem.location.enabled", "enabled", MMC_S_MODEM_LOCATION, },
+ [MMC_F_LOCATION_SIGNALS] = { "modem.location.signals", "signals", MMC_S_MODEM_LOCATION, },
+ [MMC_F_LOCATION_GPS_REFRESH_RATE] = { "modem.location.gps.refresh-rate", "refresh rate", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_SUPL_SERVER] = { "modem.location.gps.supl-server", "a-gps supl server", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_ASSISTANCE] = { "modem.location.gps.assistance", "supported assistance", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_ASSISTANCE_SERVERS] = { "modem.location.gps.assistance-servers", "assistance servers", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_3GPP_MCC] = { "modem.location.3gpp.mcc", "operator code", MMC_S_MODEM_LOCATION_3GPP, },
+ [MMC_F_LOCATION_3GPP_MNC] = { "modem.location.3gpp.mnc", "operator name", MMC_S_MODEM_LOCATION_3GPP, },
+ [MMC_F_LOCATION_3GPP_LAC] = { "modem.location.3gpp.lac", "location area code", MMC_S_MODEM_LOCATION_3GPP, },
+ [MMC_F_LOCATION_3GPP_TAC] = { "modem.location.3gpp.tac", "tracking area code", MMC_S_MODEM_LOCATION_3GPP, },
+ [MMC_F_LOCATION_3GPP_CID] = { "modem.location.3gpp.cid", "cell id", MMC_S_MODEM_LOCATION_3GPP, },
+ [MMC_F_LOCATION_GPS_NMEA] = { "modem.location.gps.nmea", "nmea", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_UTC] = { "modem.location.gps.utc", "utc", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_LONG] = { "modem.location.gps.longitude", "longitude", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_LAT] = { "modem.location.gps.latitude", "latitude", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_GPS_ALT] = { "modem.location.gps.altitude", "altitude", MMC_S_MODEM_LOCATION_GPS, },
+ [MMC_F_LOCATION_CDMABS_LONG] = { "modem.location.cdma-bs.longitude", "longitude", MMC_S_MODEM_LOCATION_CDMABS, },
+ [MMC_F_LOCATION_CDMABS_LAT] = { "modem.location.cdma-bs.latitude", "latitude", MMC_S_MODEM_LOCATION_CDMABS, },
+ [MMC_F_FIRMWARE_LIST] = { "modem.firmware.list", "list", MMC_S_MODEM_FIRMWARE, },
+ [MMC_F_FIRMWARE_METHOD] = { "modem.firmware.method", "method", MMC_S_MODEM_FIRMWARE, },
+ [MMC_F_FIRMWARE_DEVICE_IDS] = { "modem.firmware.device-ids", "device ids", MMC_S_MODEM_FIRMWARE, },
+ [MMC_F_FIRMWARE_VERSION] = { "modem.firmware.version", "version", MMC_S_MODEM_FIRMWARE, },
+ [MMC_F_FIRMWARE_FASTBOOT_AT] = { "modem.firmware.fastboot.at", "at command", MMC_S_MODEM_FIRMWARE_FASTBOOT, },
+ [MMC_F_VOICE_EMERGENCY_ONLY] = { "modem.voice.emergency-only", "emergency only", MMC_S_MODEM_VOICE, },
+ [MMC_F_BEARER_GENERAL_DBUS_PATH] = { "bearer.dbus-path", "path", MMC_S_BEARER_GENERAL, },
+ [MMC_F_BEARER_GENERAL_TYPE] = { "bearer.type", "type", MMC_S_BEARER_GENERAL, },
+ [MMC_F_BEARER_STATUS_CONNECTED] = { "bearer.status.connected", "connected", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_CONNECTION_ERROR_NAME] = { "bearer.status.connection-error.name", "connection error name", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_CONNECTION_ERROR_MESSAGE] = { "bearer.status.connection-error.message", "connection error message", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_SUSPENDED] = { "bearer.status.suspended", "suspended", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_MULTIPLEXED] = { "bearer.status.multiplexed", "multiplexed", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_INTERFACE] = { "bearer.status.interface", "interface", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_IP_TIMEOUT] = { "bearer.status.ip-timeout", "ip timeout", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_STATUS_PROFILE_ID] = { "bearer.status.profile-id", "profile id", MMC_S_BEARER_STATUS, },
+ [MMC_F_BEARER_PROPERTIES_APN] = { "bearer.properties.apn", "apn", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_APN_TYPE] = { "bearer.properties.apn-type", "apn type", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_ROAMING] = { "bearer.properties.roaming", "roaming", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_IP_TYPE] = { "bearer.properties.ip-type", "ip type", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_ALLOWED_AUTH] = { "bearer.properties.allowed-auth", "allowed-auth", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_USER] = { "bearer.properties.user", "user", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_PASSWORD] = { "bearer.properties.password", "password", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_PROFILE_ID] = { "bearer.properties.profile-id", "profile id", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_NUMBER] = { "bearer.properties.number", "number", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_PROPERTIES_RM_PROTOCOL] = { "bearer.properties.rm-protocol", "rm protocol", MMC_S_BEARER_PROPERTIES, },
+ [MMC_F_BEARER_IPV4_CONFIG_METHOD] = { "bearer.ipv4-config.method", "method", MMC_S_BEARER_IPV4_CONFIG, },
+ [MMC_F_BEARER_IPV4_CONFIG_ADDRESS] = { "bearer.ipv4-config.address", "address", MMC_S_BEARER_IPV4_CONFIG, },
+ [MMC_F_BEARER_IPV4_CONFIG_PREFIX] = { "bearer.ipv4-config.prefix", "prefix", MMC_S_BEARER_IPV4_CONFIG, },
+ [MMC_F_BEARER_IPV4_CONFIG_GATEWAY] = { "bearer.ipv4-config.gateway", "gateway", MMC_S_BEARER_IPV4_CONFIG, },
+ [MMC_F_BEARER_IPV4_CONFIG_DNS] = { "bearer.ipv4-config.dns", "dns", MMC_S_BEARER_IPV4_CONFIG, },
+ [MMC_F_BEARER_IPV4_CONFIG_MTU] = { "bearer.ipv4-config.mtu", "mtu", MMC_S_BEARER_IPV4_CONFIG, },
+ [MMC_F_BEARER_IPV6_CONFIG_METHOD] = { "bearer.ipv6-config.method", "method", MMC_S_BEARER_IPV6_CONFIG, },
+ [MMC_F_BEARER_IPV6_CONFIG_ADDRESS] = { "bearer.ipv6-config.address", "address", MMC_S_BEARER_IPV6_CONFIG, },
+ [MMC_F_BEARER_IPV6_CONFIG_PREFIX] = { "bearer.ipv6-config.prefix", "prefix", MMC_S_BEARER_IPV6_CONFIG, },
+ [MMC_F_BEARER_IPV6_CONFIG_GATEWAY] = { "bearer.ipv6-config.gateway", "gateway", MMC_S_BEARER_IPV6_CONFIG, },
+ [MMC_F_BEARER_IPV6_CONFIG_DNS] = { "bearer.ipv6-config.dns", "dns", MMC_S_BEARER_IPV6_CONFIG, },
+ [MMC_F_BEARER_IPV6_CONFIG_MTU] = { "bearer.ipv6-config.mtu", "mtu", MMC_S_BEARER_IPV6_CONFIG, },
+ [MMC_F_BEARER_STATS_START_DATE] = { "bearer.stats.start-date", "start date", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_DURATION] = { "bearer.stats.duration", "duration", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_UPLINK_SPEED] = { "bearer.stats.uplink-speed", "uplink-speed", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_DOWNLINK_SPEED] = { "bearer.stats.downlink-speed", "downlink-speed", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_BYTES_RX] = { "bearer.stats.bytes-rx", "bytes rx", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_BYTES_TX] = { "bearer.stats.bytes-tx", "bytes tx", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_ATTEMPTS] = { "bearer.stats.attempts", "attempts", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_FAILED_ATTEMPTS] = { "bearer.stats.failed-attempts", "attempts", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_TOTAL_DURATION] = { "bearer.stats.total-duration", "total-duration", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_TOTAL_BYTES_RX] = { "bearer.stats.total-bytes-rx", "total-bytes rx", MMC_S_BEARER_STATS, },
+ [MMC_F_BEARER_STATS_TOTAL_BYTES_TX] = { "bearer.stats.total-bytes-tx", "total-bytes tx", MMC_S_BEARER_STATS, },
+ [MMC_F_CALL_GENERAL_DBUS_PATH] = { "call.dbus-path", "path", MMC_S_CALL_GENERAL, },
+ [MMC_F_CALL_PROPERTIES_NUMBER] = { "call.properties.number", "number", MMC_S_CALL_PROPERTIES, },
+ [MMC_F_CALL_PROPERTIES_DIRECTION] = { "call.properties.direction", "direction", MMC_S_CALL_PROPERTIES, },
+ [MMC_F_CALL_PROPERTIES_MULTIPARTY] = { "call.properties.multiparty", "multiparty", MMC_S_CALL_PROPERTIES, },
+ [MMC_F_CALL_PROPERTIES_STATE] = { "call.properties.state", "state", MMC_S_CALL_PROPERTIES, },
+ [MMC_F_CALL_PROPERTIES_STATE_REASON] = { "call.properties.state-reason", "state reason", MMC_S_CALL_PROPERTIES, },
+ [MMC_F_CALL_PROPERTIES_AUDIO_PORT] = { "call.properties.audio-port", "audio port", MMC_S_CALL_PROPERTIES, },
+ [MMC_F_CALL_AUDIO_FORMAT_ENCODING] = { "call.audio-format.encoding", "encoding", MMC_S_CALL_AUDIO_FORMAT, },
+ [MMC_F_CALL_AUDIO_FORMAT_RESOLUTION] = { "call.audio-format.resolution", "resolution", MMC_S_CALL_AUDIO_FORMAT, },
+ [MMC_F_CALL_AUDIO_FORMAT_RATE] = { "call.audio-format.rate", "rate", MMC_S_CALL_AUDIO_FORMAT, },
+ [MMC_F_SMS_GENERAL_DBUS_PATH] = { "sms.dbus-path", "path", MMC_S_SMS_GENERAL, },
+ [MMC_F_SMS_CONTENT_NUMBER] = { "sms.content.number", "number", MMC_S_SMS_CONTENT, },
+ [MMC_F_SMS_CONTENT_TEXT] = { "sms.content.text", "text", MMC_S_SMS_CONTENT, },
+ [MMC_F_SMS_CONTENT_DATA] = { "sms.content.data", "data", MMC_S_SMS_CONTENT, },
+ [MMC_F_SMS_PROPERTIES_PDU_TYPE] = { "sms.properties.pdu-type", "pdu type", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_STATE] = { "sms.properties.state", "state", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_VALIDITY] = { "sms.properties.validity", "validity", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_STORAGE] = { "sms.properties.storage", "storage", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_SMSC] = { "sms.properties.smsc", "smsc", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_CLASS] = { "sms.properties.class", "class", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_TELESERVICE_ID] = { "sms.properties.teleservice-id", "teleservice id", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_SERVICE_CATEGORY] = { "sms.properties.service-category", "service category", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_DELIVERY_REPORT] = { "sms.properties.delivery-report", "delivery report", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_MSG_REFERENCE] = { "sms.properties.message-reference", "message reference", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_TIMESTAMP] = { "sms.properties.timestamp", "timestamp", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_DELIVERY_STATE] = { "sms.properties.delivery-state", "delivery state", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SMS_PROPERTIES_DISCH_TIMESTAMP] = { "sms.properties.discharge-timestamp", "discharge timestamp", MMC_S_SMS_PROPERTIES, },
+ [MMC_F_SIM_GENERAL_DBUS_PATH] = { "sim.dbus-path", "path", MMC_S_SIM_GENERAL, },
+ [MMC_F_SIM_PROPERTIES_ACTIVE] = { "sim.properties.active", "active", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_IMSI] = { "sim.properties.imsi", "imsi", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_ICCID] = { "sim.properties.iccid", "iccid", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_EID] = { "sim.properties.eid", "eid", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_OPERATOR_ID] = { "sim.properties.operator-code", "operator id", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_OPERATOR_NAME] = { "sim.properties.operator-name", "operator name", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS] = { "sim.properties.emergency-numbers", "emergency numbers", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS] = { "sim.properties.preferred-networks", "preferred networks", MMC_S_SIM_PROPERTIES, },
+ [MMC_F_SAR_STATE] = { "modem.sar.state", "enabled", MMC_S_MODEM_SAR, },
+ [MMC_F_SAR_POWER_LEVEL] = { "modem.sar.power-level", "power level", MMC_S_MODEM_SAR, },
+ [MMC_F_MODEM_LIST_DBUS_PATH] = { "modem-list", "modems", MMC_S_UNKNOWN, },
+ [MMC_F_SMS_LIST_DBUS_PATH] = { "modem.messaging.sms", "sms messages", MMC_S_UNKNOWN, },
+ [MMC_F_CALL_LIST_DBUS_PATH] = { "modem.voice.call", "calls", MMC_S_UNKNOWN, },
+};
+
+/******************************************************************************/
+/* Output type selection */
+
+static MmcOutputType selected_type = MMC_OUTPUT_TYPE_NONE;
+
+void
+mmcli_output_set (MmcOutputType type)
+{
+ selected_type = type;
+}
+
+MmcOutputType
+mmcli_output_get (void)
+{
+ return selected_type;
+}
+
+/******************************************************************************/
+/* Generic output management */
+
+typedef enum {
+ VALUE_TYPE_SINGLE,
+ VALUE_TYPE_MULTIPLE,
+ VALUE_TYPE_LISTITEM,
+} ValueType;
+
+typedef struct {
+ MmcF field;
+ ValueType type;
+} OutputItem;
+
+typedef struct {
+ OutputItem base;
+ gchar *value;
+} OutputItemSingle;
+
+typedef struct {
+ OutputItem base;
+ gchar **values;
+ gboolean multiline;
+} OutputItemMultiple;
+
+typedef struct {
+ OutputItem base;
+ gchar *prefix;
+ gchar *value;
+ gchar *extra;
+} OutputItemListitem;
+
+static GList *output_items;
+
+static void
+output_item_free (OutputItem *item)
+{
+ switch (item->type) {
+ case VALUE_TYPE_SINGLE:
+ g_free (((OutputItemSingle *)item)->value);
+ g_slice_free (OutputItemSingle, (OutputItemSingle *)item);
+ break;
+ case VALUE_TYPE_MULTIPLE:
+ g_strfreev (((OutputItemMultiple *)item)->values);
+ g_slice_free (OutputItemMultiple, (OutputItemMultiple *)item);
+ break;
+ case VALUE_TYPE_LISTITEM:
+ g_free (((OutputItemListitem *)item)->prefix);
+ g_free (((OutputItemListitem *)item)->value);
+ g_free (((OutputItemListitem *)item)->extra);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+filter_out_value (const gchar *value)
+{
+ return (!g_strcmp0 (value, "unknown") || !g_strcmp0 (value, "none"));
+}
+
+static void
+output_item_new_take_single (MmcF field,
+ gchar *value)
+{
+ OutputItemSingle *item;
+
+ item = g_slice_new0 (OutputItemSingle);
+ item->base.field = field;
+ item->base.type = VALUE_TYPE_SINGLE;
+
+ if (filter_out_value (value))
+ g_free (value);
+ else
+ item->value = value;
+
+ output_items = g_list_prepend (output_items, item);
+}
+
+static void
+output_item_new_take_multiple (MmcF field,
+ gchar **values,
+ gboolean multiline)
+{
+ OutputItemMultiple *item;
+
+ item = g_slice_new0 (OutputItemMultiple);
+ item->base.field = field;
+ item->base.type = VALUE_TYPE_MULTIPLE;
+ item->multiline = multiline;
+
+ if (values && (g_strv_length (values) == 1) && filter_out_value (values[0]))
+ g_strfreev (values);
+ else
+ item->values = values;
+
+ output_items = g_list_prepend (output_items, item);
+}
+
+static void
+output_item_new_take_listitem (MmcF field,
+ gchar *prefix,
+ gchar *value,
+ gchar *extra)
+{
+ OutputItemListitem *item;
+
+ item = g_slice_new0 (OutputItemListitem);
+ item->base.field = field;
+ item->base.type = VALUE_TYPE_LISTITEM;
+ item->prefix = prefix;
+ item->value = value;
+ item->extra = extra;
+
+ output_items = g_list_prepend (output_items, item);
+}
+
+void
+mmcli_output_string_list (MmcF field,
+ const gchar *str)
+{
+ gchar **split;
+
+ split = str ? g_strsplit (str, ",", -1) : NULL;
+ if (split) {
+ guint i;
+ for (i = 0; split[i]; i++)
+ g_strstrip (split[i]);
+ }
+
+ output_item_new_take_multiple (field, split, FALSE);
+}
+
+void
+mmcli_output_string_list_take (MmcF field,
+ gchar *str)
+{
+ mmcli_output_string_list (field, str);
+ g_free (str);
+}
+
+void
+mmcli_output_string_multiline (MmcF field,
+ const gchar *str)
+{
+ gchar **split;
+
+ split = str ? g_strsplit (str, "\n", -1) : NULL;
+ if (split) {
+ guint i;
+ for (i = 0; split[i]; i++)
+ g_strstrip (split[i]);
+ }
+
+ output_item_new_take_multiple (field, split, TRUE);
+}
+
+void
+mmcli_output_string_multiline_take (MmcF field,
+ gchar *str)
+{
+ mmcli_output_string_multiline (field, str);
+ g_free (str);
+}
+
+void
+mmcli_output_string_array (MmcF field,
+ const gchar **strv,
+ gboolean multiline)
+{
+ output_item_new_take_multiple (field, g_strdupv ((gchar **)strv), multiline);
+}
+
+void
+mmcli_output_string_array_take (MmcF field,
+ gchar **strv,
+ gboolean multiline)
+{
+ output_item_new_take_multiple (field, strv, multiline);
+}
+
+void
+mmcli_output_string_array_multiline_take (MmcF field,
+ gchar **strv)
+{
+ gchar **merged;
+ GPtrArray *pointers;
+
+ merged = NULL;
+ if (strv) {
+ guint i;
+
+ pointers = g_ptr_array_new ();
+ for (i = 0; strv[i]; i++) {
+ gchar **split;
+
+ split = strv[i] ? g_strsplit (strv[i], "\n", -1) : NULL;
+ if (split) {
+ guint n;
+
+ for (n = 0; split[n]; n++) {
+ if (split[n][0]) {
+ g_strstrip (split[n]);
+ g_ptr_array_add (pointers, g_strdup (split[n]));
+ }
+ }
+ g_strfreev (split);
+ }
+ }
+ g_strfreev (strv);
+ g_ptr_array_add (pointers, NULL);
+ merged = (gchar **)g_ptr_array_free (pointers, FALSE);
+ }
+
+ output_item_new_take_multiple (field, merged, TRUE);
+}
+
+void
+mmcli_output_string (MmcF field,
+ const gchar *str)
+{
+ output_item_new_take_single (field, g_strdup (str));
+}
+
+void
+mmcli_output_string_take (MmcF field,
+ gchar *str)
+{
+ output_item_new_take_single (field, str);
+}
+
+void
+mmcli_output_string_take_typed (MmcF field,
+ gchar *value,
+ const gchar *type)
+{
+ if (value && selected_type == MMC_OUTPUT_TYPE_HUMAN) {
+ gchar *aux;
+
+ aux = g_strdup_printf ("%s %s", value, type);
+ g_free (value);
+ output_item_new_take_single (field, aux);
+ return;
+ }
+
+ output_item_new_take_single (field, value);
+}
+
+void
+mmcli_output_listitem (MmcF field,
+ const gchar *prefix,
+ const gchar *value,
+ const gchar *extra)
+{
+ output_item_new_take_listitem (field, g_strdup (prefix), g_strdup (value), g_strdup (extra));
+}
+
+/******************************************************************************/
+/* (Custom) Signal quality output */
+
+void
+mmcli_output_signal_quality (guint value,
+ gboolean recent)
+{
+ /* Merge value and recent flag in a single item in human output */
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN) {
+ output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_VALUE,
+ g_strdup_printf ("%u%% (%s)", value, recent ? "recent" : "cached"));
+ return;
+ }
+
+ output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_VALUE,
+ g_strdup_printf ("%u", value));
+ output_item_new_take_single (MMC_F_STATUS_SIGNAL_QUALITY_RECENT,
+ g_strdup_printf ("%s", recent ? "yes" : "no"));
+}
+
+/******************************************************************************/
+/* (Custom) Bearer start date output */
+
+void
+mmcli_output_start_date (guint64 value)
+{
+ /* Merge value and recent flag in a single item in human output */
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN) {
+ output_item_new_take_single (MMC_F_BEARER_STATS_START_DATE, mm_new_iso8601_time_from_unix_time (value));
+ return;
+ }
+
+ output_item_new_take_single (MMC_F_BEARER_STATS_START_DATE,
+ g_strdup_printf ("%" G_GUINT64_FORMAT, value));
+}
+
+/******************************************************************************/
+/* (Custom) State output */
+
+void
+mmcli_output_state (MMModemState state,
+ MMModemStateFailedReason reason)
+{
+#define KNRM "\x1B[0m"
+#define KRED "\x1B[31m"
+#define KGRN "\x1B[32m"
+#define KYEL "\x1B[33m"
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN) {
+ if (state == MM_MODEM_STATE_FAILED)
+ output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup_printf (KRED "%s" KNRM, mm_modem_state_get_string (state)));
+ else if (state == MM_MODEM_STATE_CONNECTED)
+ output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup_printf (KGRN "%s" KNRM, mm_modem_state_get_string (state)));
+ else if (state == MM_MODEM_STATE_CONNECTING)
+ output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup_printf (KYEL "%s" KNRM, mm_modem_state_get_string (state)));
+ else
+ output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup (mm_modem_state_get_string (state))) ;
+
+ if (state == MM_MODEM_STATE_FAILED)
+ output_item_new_take_single (MMC_F_STATUS_FAILED_REASON,
+ g_strdup_printf (KRED "%s" KNRM, mm_modem_state_failed_reason_get_string (reason)));
+ return;
+ }
+
+ output_item_new_take_single (MMC_F_STATUS_STATE, g_strdup (mm_modem_state_get_string (state)));
+ output_item_new_take_single (MMC_F_STATUS_FAILED_REASON,
+ (state == MM_MODEM_STATE_FAILED) ?
+ g_strdup (mm_modem_state_failed_reason_get_string (reason)) :
+ NULL);
+}
+
+/******************************************************************************/
+/* (Custom) SIM slots output */
+
+void
+mmcli_output_sim_slots (gchar **sim_slot_paths,
+ guint primary_sim_slot)
+{
+ guint i;
+
+ if (selected_type != MMC_OUTPUT_TYPE_HUMAN || !sim_slot_paths) {
+ mmcli_output_string_take (MMC_F_SIM_PRIMARY_SLOT, primary_sim_slot ? g_strdup_printf ("%u", primary_sim_slot) : NULL);
+ mmcli_output_string_array_take (MMC_F_SIM_SLOT_PATHS, sim_slot_paths ? sim_slot_paths : NULL, TRUE);
+ return;
+ }
+
+ /* Include SIM slot number in each item */
+ for (i = 0; sim_slot_paths[i]; i++) {
+ gchar *aux;
+ guint slot_number = i + 1;
+
+ aux = g_strdup_printf ("slot %u: %s%s",
+ slot_number,
+ g_str_equal (sim_slot_paths[i], "/") ? "none" : sim_slot_paths[i],
+ (primary_sim_slot == slot_number) ? " (active)" : "");
+ g_free (sim_slot_paths[i]);
+ sim_slot_paths[i] = aux;
+ }
+
+ mmcli_output_string_array_take (MMC_F_SIM_SLOT_PATHS, sim_slot_paths, TRUE);
+}
+
+/******************************************************************************/
+/* (Custom) Network scan output */
+
+static gchar *
+build_network_info (MMModem3gppNetwork *network)
+{
+ const gchar *operator_code;
+ const gchar *operator_name;
+ gchar *access_technologies;
+ const gchar *availability;
+ gchar *out;
+
+ operator_code = mm_modem_3gpp_network_get_operator_code (network);
+ operator_name = mm_modem_3gpp_network_get_operator_long (network);
+ if (!operator_name)
+ operator_name = mm_modem_3gpp_network_get_operator_short (network);
+ access_technologies = mm_modem_access_technology_build_string_from_mask (mm_modem_3gpp_network_get_access_technology (network));
+ availability = mm_modem_3gpp_network_availability_get_string (mm_modem_3gpp_network_get_availability (network));
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN)
+ out = g_strdup_printf ("%s - %s (%s, %s)",
+ operator_code ? operator_code : "code n/a",
+ operator_name ? operator_name : "name n/a",
+ access_technologies,
+ availability);
+ else
+ out = g_strdup_printf ("operator-code: %s, operator-name: %s, access-technologies: %s, availability: %s",
+ operator_code ? operator_code : "--",
+ operator_name ? operator_name : "--",
+ access_technologies,
+ availability);
+ g_free (access_technologies);
+
+ return out;
+}
+
+void
+mmcli_output_scan_networks (GList *network_list)
+{
+ gchar **networks = NULL;
+
+ if (network_list) {
+ GPtrArray *aux;
+ GList *l;
+
+ aux = g_ptr_array_new ();
+ for (l = network_list; l; l = g_list_next (l))
+ g_ptr_array_add (aux, build_network_info ((MMModem3gppNetwork *)(l->data)));
+ g_ptr_array_add (aux, NULL);
+ networks = (gchar **) g_ptr_array_free (aux, FALSE);
+ }
+
+ /* When printing human result, we want to show some result even if no networks
+ * are found, so we force a explicit string result. */
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !networks)
+ output_item_new_take_single (MMC_F_3GPP_SCAN_NETWORKS, g_strdup ("n/a"));
+ else
+ output_item_new_take_multiple (MMC_F_3GPP_SCAN_NETWORKS, networks, TRUE);
+}
+
+/******************************************************************************/
+/* (Custom) Firmware list output */
+
+static void
+build_firmware_info_human (GPtrArray *array,
+ MMFirmwareProperties *props,
+ gboolean selected)
+{
+ g_ptr_array_add (array, g_strdup (mm_firmware_properties_get_unique_id (props)));
+ g_ptr_array_add (array, g_strdup_printf (" current: %s", selected ? "yes" : "no"));
+
+ if (mm_firmware_properties_get_image_type (props) == MM_FIRMWARE_IMAGE_TYPE_GOBI) {
+ const gchar *aux;
+
+ if ((aux = mm_firmware_properties_get_gobi_pri_version (props)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" gobi pri version: %s", aux));
+ if ((aux = mm_firmware_properties_get_gobi_pri_info (props)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" gobi pri info: %s", aux));
+ if ((aux = mm_firmware_properties_get_gobi_boot_version (props)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" gobi boot version: %s", aux));
+ if ((aux = mm_firmware_properties_get_gobi_pri_unique_id (props)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" gobi pri unique id: %s", aux));
+ if ((aux = mm_firmware_properties_get_gobi_modem_unique_id (props)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" gobi modem unique id: %s", aux));
+ }
+}
+
+static void
+build_firmware_info_keyvalue (GPtrArray *array,
+ MMFirmwareProperties *props,
+ gboolean selected)
+{
+ GString *str;
+
+ str = g_string_new ("");
+ g_string_append_printf (str, "unique-id: %s", mm_firmware_properties_get_unique_id (props));
+ g_string_append_printf (str, ", current: %s", selected ? "yes" : "no");
+
+ if (mm_firmware_properties_get_image_type (props) == MM_FIRMWARE_IMAGE_TYPE_GOBI) {
+ const gchar *aux;
+
+ if ((aux = mm_firmware_properties_get_gobi_pri_version (props)) != NULL)
+ g_string_append_printf (str, ", gobi-pri-version: %s", aux);
+ if ((aux = mm_firmware_properties_get_gobi_pri_info (props)) != NULL)
+ g_string_append_printf (str, ", gobi-pri-info: %s", aux);
+ if ((aux = mm_firmware_properties_get_gobi_boot_version (props)) != NULL)
+ g_string_append_printf (str, ", gobi-boot-version: %s", aux);
+ if ((aux = mm_firmware_properties_get_gobi_pri_unique_id (props)) != NULL)
+ g_string_append_printf (str, ", gobi-pri-unique id: %s", aux);
+ if ((aux = mm_firmware_properties_get_gobi_modem_unique_id (props)) != NULL)
+ g_string_append_printf (str, ", gobi-modem-unique id: %s", aux);
+ }
+
+ g_ptr_array_add (array, g_string_free (str, FALSE));
+}
+
+void
+mmcli_output_firmware_list (GList *firmware_list,
+ MMFirmwareProperties *selected)
+{
+ gchar **firmwares = NULL;
+
+ if (firmware_list) {
+ GPtrArray *aux;
+ GList *l;
+
+ aux = g_ptr_array_new ();
+ for (l = firmware_list; l; l = g_list_next (l)) {
+ MMFirmwareProperties *props = (MMFirmwareProperties *)(l->data);
+ gboolean current_selected;
+
+ current_selected = (selected &&
+ g_str_equal (mm_firmware_properties_get_unique_id (props),
+ mm_firmware_properties_get_unique_id (selected)));
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN)
+ build_firmware_info_human (aux, props, current_selected);
+ else
+ build_firmware_info_keyvalue (aux, props, current_selected);
+ }
+ g_ptr_array_add (aux, NULL);
+ firmwares = (gchar **) g_ptr_array_free (aux, FALSE);
+ }
+
+ /* When printing human result, we want to show some result even if no firmwares
+ * are found, so we force a explicit string result. */
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !firmwares)
+ output_item_new_take_single (MMC_F_FIRMWARE_LIST, g_strdup ("n/a"));
+ else
+ output_item_new_take_multiple (MMC_F_FIRMWARE_LIST, firmwares, TRUE);
+}
+
+/******************************************************************************/
+/* (Custom) PCO list output */
+
+void
+mmcli_output_pco_list (GList *pco_list)
+{
+ GPtrArray *aux;
+ GList *l;
+
+ if (!pco_list) {
+ output_item_new_take_single (MMC_F_3GPP_PCO, NULL);
+ return;
+ }
+
+ aux = g_ptr_array_new ();
+ for (l = pco_list; l; l = g_list_next (l)) {
+ MMPco *pco;
+ gchar *pco_data_hex;
+ const guint8 *pco_data;
+ gsize pco_data_size;
+
+ pco = MM_PCO (l->data);
+ pco_data = mm_pco_get_data (pco, &pco_data_size);
+ pco_data_hex = (pco_data ? mm_utils_bin2hexstr (pco_data, pco_data_size) : NULL);
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN)
+ g_ptr_array_add (aux, g_strdup_printf ("%u: (%s) '%s'\n",
+ mm_pco_get_session_id (pco),
+ mm_pco_is_complete (pco) ? "complete" : "partial",
+ pco_data_hex ? pco_data_hex : ""));
+ else
+ g_ptr_array_add (aux, g_strdup_printf ("session-id: %u, complete: %s, data: %s\n",
+ mm_pco_get_session_id (pco),
+ mm_pco_is_complete (pco) ? "yes" : "no",
+ pco_data_hex ? pco_data_hex : ""));
+ g_free (pco_data_hex);
+ }
+ g_ptr_array_add (aux, NULL);
+
+ output_item_new_take_multiple (MMC_F_3GPP_PCO, (gchar **) g_ptr_array_free (aux, FALSE), TRUE);
+}
+
+/******************************************************************************/
+/* (Custom) Preferred networks output */
+
+void
+mmcli_output_preferred_networks (GList *preferred_nets_list)
+{
+ if (preferred_nets_list) {
+ GPtrArray *aux;
+
+ aux = g_ptr_array_new ();
+ for (;preferred_nets_list; preferred_nets_list = g_list_next (preferred_nets_list)) {
+ const MMSimPreferredNetwork *preferred_net;
+ gchar *access_technologies;
+ gchar *out;
+ const gchar *operator_code;
+
+ preferred_net = (const MMSimPreferredNetwork *)preferred_nets_list->data;
+ operator_code = mm_sim_preferred_network_get_operator_code (preferred_net);
+ access_technologies = mm_modem_access_technology_build_string_from_mask (mm_sim_preferred_network_get_access_technology (preferred_net));
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN)
+ out = g_strdup_printf ("%s (%s)",
+ operator_code,
+ access_technologies);
+ else
+ out = g_strdup_printf ("operator-code: %s, access-technologies: %s",
+ operator_code,
+ access_technologies);
+
+ g_ptr_array_add (aux, out);
+ g_free (access_technologies);
+ }
+ g_ptr_array_add (aux, NULL);
+
+ mmcli_output_string_array_take (MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS, (gchar **) g_ptr_array_free (aux, FALSE), TRUE);
+ }
+}
+
+/******************************************************************************/
+/* (Custom) Profile list output */
+
+static void
+build_profile_human (GPtrArray *array,
+ MM3gppProfile *profile)
+{
+ const gchar *aux;
+ MMBearerAllowedAuth allowed_auth;
+ MMBearerIpFamily ip_type;
+ MMBearerApnType apn_type;
+
+ g_ptr_array_add (array, g_strdup_printf ("profile-id: %u", mm_3gpp_profile_get_profile_id (profile)));
+
+ if ((aux = mm_3gpp_profile_get_profile_name (profile)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" profile name: %s", aux));
+
+ if ((aux = mm_3gpp_profile_get_apn (profile)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" apn: %s", aux));
+
+ allowed_auth = mm_3gpp_profile_get_allowed_auth (profile);
+ if (allowed_auth != MM_BEARER_ALLOWED_AUTH_NONE) {
+ g_autofree gchar *allowed_auth_str = NULL;
+
+ allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
+ g_ptr_array_add (array, g_strdup_printf (" allowed-auth: %s", allowed_auth_str));
+ }
+
+ if ((aux = mm_3gpp_profile_get_user (profile)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" user: %s", aux));
+
+ if ((aux = mm_3gpp_profile_get_password (profile)) != NULL)
+ g_ptr_array_add (array, g_strdup_printf (" password: %s", aux));
+
+ ip_type = mm_3gpp_profile_get_ip_type (profile);
+ if (ip_type != MM_BEARER_IP_FAMILY_NONE) {
+ g_autofree gchar *ip_type_str = NULL;
+
+ ip_type_str = mm_bearer_ip_family_build_string_from_mask (ip_type);
+ g_ptr_array_add (array, g_strdup_printf (" ip-type: %s", ip_type_str));
+ }
+
+ apn_type = mm_3gpp_profile_get_apn_type (profile);
+ if (apn_type != MM_BEARER_APN_TYPE_NONE) {
+ g_autofree gchar *apn_type_str = NULL;
+
+ apn_type_str = mm_bearer_apn_type_build_string_from_mask (apn_type);
+ g_ptr_array_add (array, g_strdup_printf (" apn-type: %s", apn_type_str));
+ }
+}
+
+static void
+build_profile_keyvalue (GPtrArray *array,
+ MM3gppProfile *profile)
+{
+ GString *str;
+ const gchar *aux;
+ MMBearerAllowedAuth allowed_auth;
+ MMBearerIpFamily ip_type;
+ MMBearerApnType apn_type;
+
+ str = g_string_new ("");
+ g_string_append_printf (str, "profile-id: %u", mm_3gpp_profile_get_profile_id (profile));
+
+ if ((aux = mm_3gpp_profile_get_profile_name (profile)) != NULL)
+ g_string_append_printf (str, ", profile-name: %s", aux);
+
+ if ((aux = mm_3gpp_profile_get_apn (profile)) != NULL)
+ g_string_append_printf (str, ", apn: %s", aux);
+
+ allowed_auth = mm_3gpp_profile_get_allowed_auth (profile);
+ if (allowed_auth != MM_BEARER_ALLOWED_AUTH_NONE) {
+ g_autofree gchar *allowed_auth_str = NULL;
+
+ allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
+ g_string_append_printf (str, ", allowed-auth: %s", allowed_auth_str);
+ }
+
+ if ((aux = mm_3gpp_profile_get_user (profile)) != NULL)
+ g_string_append_printf (str, ", user: %s", aux);
+
+ if ((aux = mm_3gpp_profile_get_password (profile)) != NULL)
+ g_string_append_printf (str, ", password: %s", aux);
+
+ ip_type = mm_3gpp_profile_get_ip_type (profile);
+ if (ip_type != MM_BEARER_IP_FAMILY_NONE) {
+ g_autofree gchar *ip_type_str = NULL;
+
+ ip_type_str = mm_bearer_ip_family_build_string_from_mask (ip_type);
+ g_string_append_printf (str, ", ip-type: %s", ip_type_str);
+ }
+
+ apn_type = mm_3gpp_profile_get_apn_type (profile);
+ if (apn_type != MM_BEARER_APN_TYPE_NONE) {
+ g_autofree gchar *apn_type_str = NULL;
+
+ apn_type_str = mm_bearer_apn_type_build_string_from_mask (apn_type);
+ g_string_append_printf (str, ", apn-type: %s", apn_type_str);
+ }
+
+ g_ptr_array_add (array, g_string_free (str, FALSE));
+}
+
+static void
+output_profile_list (MmcF field,
+ GList *profile_list)
+{
+ gchar **profiles = NULL;
+
+ if (profile_list) {
+ GPtrArray *aux;
+ GList *l;
+
+ aux = g_ptr_array_new ();
+ for (l = profile_list; l; l = g_list_next (l)) {
+ MM3gppProfile *profile = (MM3gppProfile *)(l->data);
+
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN)
+ build_profile_human (aux, profile);
+ else
+ build_profile_keyvalue (aux, profile);
+ }
+ g_ptr_array_add (aux, NULL);
+ profiles = (gchar **) g_ptr_array_free (aux, FALSE);
+ }
+
+ /* When printing human result, we want to show some result even if no profiles
+ * are found, so we force a explicit string result. */
+ if (selected_type == MMC_OUTPUT_TYPE_HUMAN && !profiles)
+ output_item_new_take_single (field, g_strdup ("n/a"));
+ else
+ output_item_new_take_multiple (field, profiles, TRUE);
+}
+
+void
+mmcli_output_profile_list (GList *profile_list)
+{
+ output_profile_list (MMC_F_3GPP_PROFILE_MANAGER_LIST, profile_list);
+}
+
+void
+mmcli_output_profile_set (MM3gppProfile *profile)
+{
+ GList *profile_list = NULL;
+
+ profile_list = g_list_append (profile_list, profile);
+ output_profile_list (MMC_F_3GPP_PROFILE_MANAGER_SET, profile_list);
+ g_list_free (profile_list);
+}
+
+/******************************************************************************/
+/* Human-friendly output */
+
+#define HUMAN_MAX_VALUE_LENGTH 60
+
+static gint
+list_sort_human (const OutputItem *item_a,
+ const OutputItem *item_b)
+{
+ if (field_infos[item_a->field].section < field_infos[item_b->field].section)
+ return -1;
+ if (field_infos[item_a->field].section > field_infos[item_b->field].section)
+ return 1;
+ return item_a->field - item_b->field;
+}
+
+static void
+dump_output_human (void)
+{
+ GList *l;
+ MmcS current_section = MMC_S_UNKNOWN;
+ guint longest_section_name = 0;
+ guint longest_field_name = 0;
+
+ output_items = g_list_sort (output_items, (GCompareFunc) list_sort_human);
+
+ /* First pass to process */
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l;
+ guint aux;
+ gboolean ignore = FALSE;
+
+ item_l = (OutputItem *)(l->data);
+
+ /* Post-process values */
+ if (item_l->type == VALUE_TYPE_SINGLE) {
+ OutputItemSingle *single = (OutputItemSingle *)item_l;
+
+ if (!single->value)
+ ignore = TRUE;
+ } else if (item_l->type == VALUE_TYPE_MULTIPLE) {
+ OutputItemMultiple *multiple = (OutputItemMultiple *)item_l;
+
+ if (!multiple->values)
+ ignore = TRUE;
+ }
+
+ /* Compute max lengths */
+ if (!ignore) {
+ aux = strlen (section_infos[field_infos[item_l->field].section].name);
+ if (aux > longest_section_name)
+ longest_section_name = aux;
+ aux = strlen (field_infos[item_l->field].name);
+ if (aux > longest_field_name)
+ longest_field_name = aux;
+ }
+ }
+
+ /* Second pass to print */
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l;
+ OutputItemSingle *single = NULL;
+ OutputItemMultiple *multiple = NULL;
+
+ item_l = (OutputItem *)(l->data);
+ if (item_l->type == VALUE_TYPE_SINGLE)
+ single = (OutputItemSingle *)item_l;
+ else if (item_l->type == VALUE_TYPE_MULTIPLE)
+ multiple = (OutputItemMultiple *)item_l;
+ else
+ g_assert_not_reached ();
+
+ /* Ignore items without a value set */
+ if ((single && (!single->value || !single->value[0])) ||
+ (multiple && (!multiple->values || !g_strv_length (multiple->values))))
+ continue;
+
+ /* Section change? */
+ if (field_infos[item_l->field].section != current_section) {
+ current_section = field_infos[item_l->field].section;
+ g_print (" %.*s\n",
+ longest_section_name + longest_field_name + 4,
+ "------------------------------------------------------------");
+ g_print (" %-*.*s | ",
+ longest_section_name,
+ longest_section_name,
+ section_infos[field_infos[item_l->field].section].name);
+ } else
+ g_print (" %*.*s | ",
+ longest_section_name,
+ longest_section_name,
+ "");
+
+ g_print ("%*.*s: ",
+ longest_field_name, longest_field_name, field_infos[item_l->field].name);
+
+ if (single) {
+ gchar **split_lines;
+ guint i;
+
+ split_lines = g_strsplit (single->value, "\n", -1);
+ for (i = 0; split_lines[i]; i++) {
+ if (i != 0) {
+ g_print (" %*.*s | %*.*s ",
+ longest_section_name, longest_section_name, "",
+ longest_field_name, longest_field_name, "");
+ }
+ g_print ("%s\n", split_lines[i]);
+ }
+ g_strfreev (split_lines);
+ } else if (multiple) {
+ guint line_length = 0;
+ guint n, i;
+
+ n = multiple->values ? g_strv_length (multiple->values) : 0;
+ for (i = 0; i < n; i++) {
+ const gchar *value;
+ guint value_length;
+
+ value = multiple->values[i];
+ value_length = strlen (value) + ((i < (n - 1)) ? 2 : 0);
+ if ((multiple->multiline && i != 0) || ((line_length + value_length) > HUMAN_MAX_VALUE_LENGTH)) {
+ line_length = 0;
+ g_print ("\n"
+ " %*.*s | %*.*s ",
+ longest_section_name, longest_section_name, "",
+ longest_field_name, longest_field_name, "");
+ } else
+ line_length += value_length;
+ g_print ("%s%s", value, (!multiple->multiline && i < (n - 1)) ? ", " : "");
+ }
+ g_print ("\n");
+ }
+ }
+}
+
+static void
+dump_output_list_human (MmcF field)
+{
+ GList *l;
+ guint n;
+
+ g_assert (field != MMC_F_UNKNOWN);
+
+ /* First pass to process */
+ for (n = 0, l = output_items; l; l = g_list_next (l), n++) {
+ OutputItem *item_l;
+ OutputItemListitem *listitem;
+
+ item_l = (OutputItem *)(l->data);
+ g_assert (item_l->type == VALUE_TYPE_LISTITEM);
+ listitem = (OutputItemListitem *)item_l;
+ g_assert (listitem->value);
+
+ /* All items must be of same type */
+ g_assert_cmpint (item_l->field, ==, field);
+ }
+
+ /* Second pass to print */
+ if (n == 0) {
+ g_print ("No %s were found\n", field_infos[field].name);
+ return;
+ }
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItemListitem *listitem;
+
+ listitem = (OutputItemListitem *)(l->data);
+ g_print ("%s%s %s\n", listitem->prefix, listitem->value, listitem->extra);
+ }
+}
+
+/******************************************************************************/
+/* Key-value output */
+
+#define KEY_ARRAY_LENGTH_SUFFIX ".length"
+#define KEY_ARRAY_VALUE_SUFFIX ".value"
+
+static gint
+list_sort_keyvalue (const OutputItem *item_a,
+ const OutputItem *item_b)
+{
+ return item_a->field - item_b->field;
+}
+
+static void
+dump_output_keyvalue (void)
+{
+ GList *l;
+ guint longest_field_key = 0;
+
+ output_items = g_list_sort (output_items, (GCompareFunc) list_sort_keyvalue);
+
+ /* First pass to process */
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l;
+ OutputItemMultiple *multiple = NULL;
+ guint key_length;
+
+ item_l = (OutputItem *)(l->data);
+ if (item_l->type == VALUE_TYPE_MULTIPLE)
+ multiple = (OutputItemMultiple *)item_l;
+
+ key_length = strlen (field_infos[item_l->field].key);
+
+ /* when printing array contents, each item is given with an index,
+ * e.g.: something.value[1]
+ * The max length of the field will need to consider the array length
+ * in order to accommodate the length of the index.
+ */
+ if (multiple) {
+ guint n;
+
+ n = multiple->values ? g_strv_length (multiple->values) : 0;
+ if (n > 0) {
+ guint aux = n;
+
+ key_length += ((strlen (KEY_ARRAY_VALUE_SUFFIX)) + 3);
+ while ((aux /= 10) > 0)
+ key_length++;
+ }
+ }
+
+ if (key_length > longest_field_key)
+ longest_field_key = key_length;
+ }
+
+ /* Second pass to print */
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l;
+ OutputItemSingle *single = NULL;
+ OutputItemMultiple *multiple = NULL;
+
+ item_l = (OutputItem *)(l->data);
+ if (item_l->type == VALUE_TYPE_SINGLE)
+ single = (OutputItemSingle *)item_l;
+ else if (item_l->type == VALUE_TYPE_MULTIPLE)
+ multiple = (OutputItemMultiple *)item_l;
+ else
+ g_assert_not_reached ();
+
+ if (single) {
+ gchar *escaped = NULL;
+
+ if (single->value)
+ escaped = g_strescape (single->value, NULL);
+ g_print ("%-*.*s : %s\n",
+ longest_field_key, longest_field_key, field_infos[item_l->field].key,
+ escaped ? escaped : "--");
+ g_free (escaped);
+ } else if (multiple) {
+ guint n;
+
+ n = multiple->values ? g_strv_length (multiple->values) : 0;
+ if (n > 0) {
+ guint i;
+ gchar *new_key;
+
+ new_key = g_strdup_printf ("%s" KEY_ARRAY_LENGTH_SUFFIX, field_infos[item_l->field].key);
+ g_print ("%-*.*s : %u\n", longest_field_key, longest_field_key, new_key, n);
+ g_free (new_key);
+
+ for (i = 0; i < n; i++) {
+ gchar *escaped = NULL;
+
+ /* Printed indices start at 1 */
+ new_key = g_strdup_printf ("%s" KEY_ARRAY_VALUE_SUFFIX "[%u]", field_infos[item_l->field].key, i + 1);
+ escaped = g_strescape (multiple->values[i], NULL);
+ g_print ("%-*.*s : %s\n", longest_field_key, longest_field_key, new_key, escaped);
+ g_free (escaped);
+ g_free (new_key);
+ }
+ } else
+ g_print ("%-*.*s : --\n",
+ longest_field_key, longest_field_key, field_infos[item_l->field].key);
+ }
+ }
+}
+
+static void
+dump_output_list_keyvalue (MmcF field)
+{
+ GList *l;
+ guint key_length;
+ guint n;
+ gchar *new_key;
+
+ g_assert (field != MMC_F_UNKNOWN);
+ key_length = strlen (field_infos[field].key);
+
+ /* First pass to process */
+ for (n = 0, l = output_items; l; l = g_list_next (l), n++) {
+ OutputItem *item_l;
+ OutputItemListitem *listitem;
+
+ item_l = (OutputItem *)(l->data);
+ g_assert (item_l->type == VALUE_TYPE_LISTITEM);
+ listitem = (OutputItemListitem *)item_l;
+ g_assert (listitem->value);
+
+ /* All items must be of same type */
+ g_assert_cmpint (item_l->field, ==, field);
+ }
+
+ if (n > 0) {
+ key_length += ((strlen (KEY_ARRAY_VALUE_SUFFIX)) + 3);
+ if (n > 10)
+ key_length++;
+ }
+
+ new_key = g_strdup_printf ("%s" KEY_ARRAY_LENGTH_SUFFIX, field_infos[field].key);
+ g_print ("%-*.*s : %u\n", key_length, key_length, new_key, n);
+ g_free (new_key);
+
+ /* Second pass to print */
+ for (n = 0, l = output_items; l; l = g_list_next (l), n++) {
+ OutputItemListitem *listitem;
+
+ listitem = (OutputItemListitem *)(l->data);
+ new_key = g_strdup_printf ("%s" KEY_ARRAY_VALUE_SUFFIX "[%u]", field_infos[field].key, n + 1);
+ g_print ("%-*.*s : %s\n",
+ key_length, key_length, new_key,
+ listitem->value);
+ g_free (new_key);
+ }
+}
+
+/******************************************************************************/
+/* JSON-friendly output */
+
+static gchar *
+json_strescape (const gchar *str)
+{
+ const gchar *p;
+ const gchar *end;
+ GString *output;
+ gsize len;
+
+ len = strlen (str);
+ end = str + len;
+ output = g_string_sized_new (len);
+
+ for (p = str; p < end; p++) {
+ if (*p == '\\' || *p == '"') {
+ g_string_append_c (output, '\\');
+ g_string_append_c (output, *p);
+ } else if ((*p > 0 && *p < 0x1f) || *p == 0x7f) {
+ switch (*p) {
+ case '\b':
+ g_string_append (output, "\\b");
+ break;
+ case '\f':
+ g_string_append (output, "\\f");
+ break;
+ case '\n':
+ g_string_append (output, "\\n");
+ break;
+ case '\r':
+ g_string_append (output, "\\r");
+ break;
+ case '\t':
+ g_string_append (output, "\\t");
+ break;
+ default:
+ g_string_append_printf (output, "\\u00%02x", (guint)*p);
+ break;
+ }
+ } else
+ g_string_append_c (output, *p);
+ }
+ return g_string_free (output, FALSE);
+}
+
+static gint
+list_sort_by_keys (const OutputItem *item_a,
+ const OutputItem *item_b)
+{
+ return g_strcmp0 (field_infos[item_a->field].key, field_infos[item_b->field].key);
+}
+
+static void
+dump_output_json (void)
+{
+ GList *l;
+ MmcF current_field = MMC_F_UNKNOWN;
+ gchar **current_path = NULL;
+ guint cur_dlen = 0;
+
+ output_items = g_list_sort (output_items, (GCompareFunc) list_sort_by_keys);
+
+ g_print("{");
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l = (OutputItem *)(l->data);
+
+ if (current_field != item_l->field) {
+ guint new_dlen;
+ guint iter = 0;
+ gchar **new_path;
+
+ new_path = g_strsplit (field_infos[item_l->field].key, ".", -1);
+ new_dlen = g_strv_length (new_path) - 1;
+ if (current_path) {
+ guint min_dlen;
+
+ min_dlen = MIN (cur_dlen, new_dlen);
+ while (iter < min_dlen && g_strcmp0 (current_path[iter], new_path[iter]) == 0)
+ iter++;
+
+ g_strfreev (current_path);
+
+ if (iter < min_dlen || new_dlen < cur_dlen)
+ for (min_dlen = iter; min_dlen < cur_dlen; min_dlen++)
+ g_print ("}");
+
+ g_print (",");
+ }
+
+ while (iter < new_dlen)
+ g_print ("\"%s\":{", new_path[iter++]);
+
+ cur_dlen = new_dlen;
+ current_path = new_path;
+ current_field = item_l->field;
+ } else {
+ g_print (",");
+ }
+
+ if (item_l->type == VALUE_TYPE_SINGLE) {
+ OutputItemSingle *single = (OutputItemSingle *) item_l;
+ gchar *escaped = NULL;
+
+ if (single->value)
+ escaped = json_strescape (single->value);
+
+ g_print ("\"%s\":\"%s\"", current_path[cur_dlen], escaped ? escaped : "--");
+ g_free (escaped);
+ } else if (item_l->type == VALUE_TYPE_MULTIPLE) {
+ OutputItemMultiple *multiple = (OutputItemMultiple *) item_l;
+ guint i, n;
+
+ n = multiple->values ? g_strv_length (multiple->values) : 0;
+
+ g_print ("\"%s\":[", current_path[cur_dlen]);
+ for (i = 0; i < n; i++) {
+ gchar *escaped;
+
+ escaped = json_strescape (multiple->values[i]);
+ g_print("\"%s\"", escaped);
+ if (i < n - 1)
+ g_print(",");
+ g_free (escaped);
+ }
+ g_print("]");
+ } else
+ g_assert_not_reached ();
+ }
+
+ while (cur_dlen--)
+ g_print ("}");
+ g_print("}\n");
+
+ g_strfreev (current_path);
+}
+
+static void
+dump_output_list_json (MmcF field)
+{
+ GList *l;
+
+ g_assert (field != MMC_F_UNKNOWN);
+
+ g_print("{\"%s\":[", field_infos[field].key);
+
+ for (l = output_items; l; l = g_list_next (l)) {
+ OutputItem *item_l;
+ OutputItemListitem *listitem;
+
+ item_l = (OutputItem *)(l->data);
+ g_assert (item_l->type == VALUE_TYPE_LISTITEM);
+ listitem = (OutputItemListitem *)item_l;
+ g_assert (listitem->value);
+
+ /* All items must be of same type */
+ g_assert_cmpint (item_l->field, ==, field);
+ g_print("\"%s\"", listitem->value);
+
+ if (g_list_next (l))
+ g_print(",");
+ }
+
+ g_print("]}\n");
+}
+
+/******************************************************************************/
+/* Dump output */
+
+void
+mmcli_output_dump (void)
+{
+ switch (selected_type) {
+ case MMC_OUTPUT_TYPE_NONE:
+ break;
+ case MMC_OUTPUT_TYPE_HUMAN:
+ dump_output_human ();
+ break;
+ case MMC_OUTPUT_TYPE_KEYVALUE:
+ dump_output_keyvalue ();
+ break;
+ case MMC_OUTPUT_TYPE_JSON:
+ dump_output_json ();
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_list_free_full (output_items, (GDestroyNotify) output_item_free);
+ output_items = NULL;
+
+ fflush (stdout);
+}
+
+void
+mmcli_output_list_dump (MmcF field)
+{
+ switch (selected_type) {
+ case MMC_OUTPUT_TYPE_NONE:
+ break;
+ case MMC_OUTPUT_TYPE_HUMAN:
+ dump_output_list_human (field);
+ break;
+ case MMC_OUTPUT_TYPE_KEYVALUE:
+ dump_output_list_keyvalue (field);
+ break;
+ case MMC_OUTPUT_TYPE_JSON:
+ dump_output_list_json (field);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_list_free_full (output_items, (GDestroyNotify) output_item_free);
+ output_items = NULL;
+
+ fflush (stdout);
+}
diff --git a/cli/mmcli-output.h b/cli/mmcli-output.h
new file mode 100644
index 00000000..cc34d536
--- /dev/null
+++ b/cli/mmcli-output.h
@@ -0,0 +1,393 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * mmcli -- Control modem status & access information from the command line
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MMCLI_OUTPUT_H
+#define MMCLI_OUTPUT_H
+
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+/******************************************************************************/
+/* List of sections (grouped fields) displayed in the human-friendly output */
+
+typedef enum {
+ MMC_S_UNKNOWN = -1,
+ /* Modem object related sections */
+ MMC_S_MODEM_GENERAL = 0,
+ MMC_S_MODEM_HARDWARE,
+ MMC_S_MODEM_SYSTEM,
+ MMC_S_MODEM_NUMBERS,
+ MMC_S_MODEM_STATUS,
+ MMC_S_MODEM_MODES,
+ MMC_S_MODEM_BANDS,
+ MMC_S_MODEM_IP,
+ MMC_S_MODEM_3GPP,
+ MMC_S_MODEM_3GPP_EPS,
+ MMC_S_MODEM_3GPP_SCAN,
+ MMC_S_MODEM_3GPP_USSD,
+ MMC_S_MODEM_3GPP_PROFILE_MANAGER,
+ MMC_S_MODEM_CDMA,
+ MMC_S_MODEM_SIM,
+ MMC_S_MODEM_BEARER,
+ MMC_S_MODEM_TIME,
+ MMC_S_MODEM_TIMEZONE,
+ MMC_S_MODEM_MESSAGING,
+ MMC_S_MODEM_SIGNAL,
+ MMC_S_MODEM_SIGNAL_CDMA1X,
+ MMC_S_MODEM_SIGNAL_EVDO,
+ MMC_S_MODEM_SIGNAL_GSM,
+ MMC_S_MODEM_SIGNAL_UMTS,
+ MMC_S_MODEM_SIGNAL_LTE,
+ MMC_S_MODEM_SIGNAL_5G,
+ MMC_S_MODEM_OMA,
+ MMC_S_MODEM_OMA_CURRENT,
+ MMC_S_MODEM_OMA_PENDING,
+ MMC_S_MODEM_LOCATION,
+ MMC_S_MODEM_LOCATION_3GPP,
+ MMC_S_MODEM_LOCATION_GPS,
+ MMC_S_MODEM_LOCATION_CDMABS,
+ MMC_S_MODEM_FIRMWARE,
+ MMC_S_MODEM_FIRMWARE_FASTBOOT,
+ MMC_S_MODEM_VOICE,
+ MMC_S_MODEM_SAR,
+ MMC_S_BEARER_GENERAL,
+ MMC_S_BEARER_STATUS,
+ MMC_S_BEARER_PROPERTIES,
+ MMC_S_BEARER_IPV4_CONFIG,
+ MMC_S_BEARER_IPV6_CONFIG,
+ MMC_S_BEARER_STATS,
+ MMC_S_CALL_GENERAL,
+ MMC_S_CALL_PROPERTIES,
+ MMC_S_CALL_AUDIO_FORMAT,
+ MMC_S_SMS_GENERAL,
+ MMC_S_SMS_CONTENT,
+ MMC_S_SMS_PROPERTIES,
+ MMC_S_SIM_GENERAL,
+ MMC_S_SIM_PROPERTIES,
+} MmcS;
+
+/******************************************************************************/
+/* List of fields */
+
+typedef enum {
+ MMC_F_UNKNOWN = -1,
+ /* General section */
+ MMC_F_GENERAL_DBUS_PATH = 0,
+ MMC_F_GENERAL_DEVICE_ID,
+ /* Hardware section */
+ MMC_F_HARDWARE_MANUFACTURER,
+ MMC_F_HARDWARE_MODEL,
+ MMC_F_HARDWARE_REVISION,
+ MMC_F_HARDWARE_CARRIER_CONF,
+ MMC_F_HARDWARE_CARRIER_CONF_REV,
+ MMC_F_HARDWARE_HW_REVISION,
+ MMC_F_HARDWARE_SUPPORTED_CAPABILITIES,
+ MMC_F_HARDWARE_CURRENT_CAPABILITIES,
+ MMC_F_HARDWARE_EQUIPMENT_ID,
+ /* System section */
+ MMC_F_SYSTEM_DEVICE,
+ MMC_F_SYSTEM_DRIVERS,
+ MMC_F_SYSTEM_PLUGIN,
+ MMC_F_SYSTEM_PRIMARY_PORT,
+ MMC_F_SYSTEM_PORTS,
+ /* Numbers section */
+ MMC_F_NUMBERS_OWN,
+ /* Status section */
+ MMC_F_STATUS_LOCK,
+ MMC_F_STATUS_UNLOCK_RETRIES,
+ MMC_F_STATUS_STATE,
+ MMC_F_STATUS_FAILED_REASON,
+ MMC_F_STATUS_POWER_STATE,
+ MMC_F_STATUS_ACCESS_TECH,
+ MMC_F_STATUS_SIGNAL_QUALITY_VALUE,
+ MMC_F_STATUS_SIGNAL_QUALITY_RECENT,
+ /* Modes section */
+ MMC_F_MODES_SUPPORTED,
+ MMC_F_MODES_CURRENT,
+ /* Bands section */
+ MMC_F_BANDS_SUPPORTED,
+ MMC_F_BANDS_CURRENT,
+ /* IP section */
+ MMC_F_IP_SUPPORTED,
+ /* 3GPP section */
+ MMC_F_3GPP_IMEI,
+ MMC_F_3GPP_ENABLED_LOCKS,
+ MMC_F_3GPP_OPERATOR_ID,
+ MMC_F_3GPP_OPERATOR_NAME,
+ MMC_F_3GPP_REGISTRATION,
+ MMC_F_3GPP_PACKET_SERVICE_STATE,
+ MMC_F_3GPP_PCO,
+ /* 3GPP EPS section */
+ MMC_F_3GPP_EPS_UE_MODE,
+ MMC_F_3GPP_EPS_INITIAL_BEARER_PATH,
+ MMC_F_3GPP_EPS_BEARER_SETTINGS_APN,
+ MMC_F_3GPP_EPS_BEARER_SETTINGS_IP_TYPE,
+ MMC_F_3GPP_EPS_BEARER_SETTINGS_USER,
+ MMC_F_3GPP_EPS_BEARER_SETTINGS_PASSWORD,
+ /* 3GPP scan section */
+ MMC_F_3GPP_SCAN_NETWORKS,
+ /* 3GPP profile management section */
+ MMC_F_3GPP_PROFILE_MANAGER_LIST,
+ MMC_F_3GPP_PROFILE_MANAGER_SET,
+ /* USSD section */
+ MMC_F_3GPP_USSD_STATUS,
+ MMC_F_3GPP_USSD_NETWORK_REQUEST,
+ MMC_F_3GPP_USSD_NETWORK_NOTIFICATION,
+ /* CDMA section */
+ MMC_F_CDMA_MEID,
+ MMC_F_CDMA_ESN,
+ MMC_F_CDMA_SID,
+ MMC_F_CDMA_NID,
+ MMC_F_CDMA_REGISTRATION_CDMA1X,
+ MMC_F_CDMA_REGISTRATION_EVDO,
+ MMC_F_CDMA_ACTIVATION,
+ /* SIM section */
+ MMC_F_SIM_PATH,
+ MMC_F_SIM_PRIMARY_SLOT,
+ MMC_F_SIM_SLOT_PATHS,
+ /* Bearer section */
+ MMC_F_BEARER_PATHS,
+ /* Time section */
+ MMC_F_TIME_CURRENT,
+ MMC_F_TIMEZONE_CURRENT,
+ MMC_F_TIMEZONE_DST_OFFSET,
+ MMC_F_TIMEZONE_LEAP_SECONDS,
+ /* Messaging section */
+ MMC_F_MESSAGING_SUPPORTED_STORAGES,
+ MMC_F_MESSAGING_DEFAULT_STORAGES,
+ /* Signal section */
+ MMC_F_SIGNAL_REFRESH_RATE,
+ MMC_F_SIGNAL_RSSI_THRESHOLD,
+ MMC_F_SIGNAL_ERROR_RATE_THRESHOLD,
+ MMC_F_SIGNAL_CDMA1X_RSSI,
+ MMC_F_SIGNAL_CDMA1X_ECIO,
+ MMC_F_SIGNAL_CDMA1X_ERROR_RATE,
+ MMC_F_SIGNAL_EVDO_RSSI,
+ MMC_F_SIGNAL_EVDO_ECIO,
+ MMC_F_SIGNAL_EVDO_SINR,
+ MMC_F_SIGNAL_EVDO_IO,
+ MMC_F_SIGNAL_EVDO_ERROR_RATE,
+ MMC_F_SIGNAL_GSM_RSSI,
+ MMC_F_SIGNAL_GSM_ERROR_RATE,
+ MMC_F_SIGNAL_UMTS_RSSI,
+ MMC_F_SIGNAL_UMTS_RSCP,
+ MMC_F_SIGNAL_UMTS_ECIO,
+ MMC_F_SIGNAL_UMTS_ERROR_RATE,
+ MMC_F_SIGNAL_LTE_RSSI,
+ MMC_F_SIGNAL_LTE_RSRQ,
+ MMC_F_SIGNAL_LTE_RSRP,
+ MMC_F_SIGNAL_LTE_SNR,
+ MMC_F_SIGNAL_LTE_ERROR_RATE,
+ MMC_F_SIGNAL_5G_RSRQ,
+ MMC_F_SIGNAL_5G_RSRP,
+ MMC_F_SIGNAL_5G_SNR,
+ MMC_F_SIGNAL_5G_ERROR_RATE,
+ /* OMA section */
+ MMC_F_OMA_FEATURES,
+ MMC_F_OMA_CURRENT_TYPE,
+ MMC_F_OMA_CURRENT_STATE,
+ MMC_F_OMA_PENDING_SESSIONS,
+ /* Location status section */
+ MMC_F_LOCATION_CAPABILITIES,
+ MMC_F_LOCATION_ENABLED,
+ MMC_F_LOCATION_SIGNALS,
+ MMC_F_LOCATION_GPS_REFRESH_RATE,
+ MMC_F_LOCATION_GPS_SUPL_SERVER,
+ MMC_F_LOCATION_GPS_ASSISTANCE,
+ MMC_F_LOCATION_GPS_ASSISTANCE_SERVERS,
+ MMC_F_LOCATION_3GPP_MCC,
+ MMC_F_LOCATION_3GPP_MNC,
+ MMC_F_LOCATION_3GPP_LAC,
+ MMC_F_LOCATION_3GPP_TAC,
+ MMC_F_LOCATION_3GPP_CID,
+ MMC_F_LOCATION_GPS_NMEA,
+ MMC_F_LOCATION_GPS_UTC,
+ MMC_F_LOCATION_GPS_LONG,
+ MMC_F_LOCATION_GPS_LAT,
+ MMC_F_LOCATION_GPS_ALT,
+ MMC_F_LOCATION_CDMABS_LONG,
+ MMC_F_LOCATION_CDMABS_LAT,
+ /* Firmware status section */
+ MMC_F_FIRMWARE_LIST,
+ MMC_F_FIRMWARE_METHOD,
+ MMC_F_FIRMWARE_DEVICE_IDS,
+ MMC_F_FIRMWARE_VERSION,
+ MMC_F_FIRMWARE_FASTBOOT_AT,
+ /* Voice section */
+ MMC_F_VOICE_EMERGENCY_ONLY,
+ /* Bearer general section */
+ MMC_F_BEARER_GENERAL_DBUS_PATH,
+ MMC_F_BEARER_GENERAL_TYPE,
+ /* Bearer status section */
+ MMC_F_BEARER_STATUS_CONNECTED,
+ MMC_F_BEARER_STATUS_CONNECTION_ERROR_NAME,
+ MMC_F_BEARER_STATUS_CONNECTION_ERROR_MESSAGE,
+ MMC_F_BEARER_STATUS_SUSPENDED,
+ MMC_F_BEARER_STATUS_MULTIPLEXED,
+ MMC_F_BEARER_STATUS_INTERFACE,
+ MMC_F_BEARER_STATUS_IP_TIMEOUT,
+ MMC_F_BEARER_STATUS_PROFILE_ID,
+ /* Bearer properties section */
+ MMC_F_BEARER_PROPERTIES_APN,
+ MMC_F_BEARER_PROPERTIES_APN_TYPE,
+ MMC_F_BEARER_PROPERTIES_ROAMING,
+ MMC_F_BEARER_PROPERTIES_IP_TYPE,
+ MMC_F_BEARER_PROPERTIES_ALLOWED_AUTH,
+ MMC_F_BEARER_PROPERTIES_USER,
+ MMC_F_BEARER_PROPERTIES_PASSWORD,
+ MMC_F_BEARER_PROPERTIES_PROFILE_ID,
+ MMC_F_BEARER_PROPERTIES_NUMBER,
+ MMC_F_BEARER_PROPERTIES_RM_PROTOCOL,
+ MMC_F_BEARER_IPV4_CONFIG_METHOD,
+ MMC_F_BEARER_IPV4_CONFIG_ADDRESS,
+ MMC_F_BEARER_IPV4_CONFIG_PREFIX,
+ MMC_F_BEARER_IPV4_CONFIG_GATEWAY,
+ MMC_F_BEARER_IPV4_CONFIG_DNS,
+ MMC_F_BEARER_IPV4_CONFIG_MTU,
+ MMC_F_BEARER_IPV6_CONFIG_METHOD,
+ MMC_F_BEARER_IPV6_CONFIG_ADDRESS,
+ MMC_F_BEARER_IPV6_CONFIG_PREFIX,
+ MMC_F_BEARER_IPV6_CONFIG_GATEWAY,
+ MMC_F_BEARER_IPV6_CONFIG_DNS,
+ MMC_F_BEARER_IPV6_CONFIG_MTU,
+ MMC_F_BEARER_STATS_START_DATE,
+ MMC_F_BEARER_STATS_DURATION,
+ MMC_F_BEARER_STATS_UPLINK_SPEED,
+ MMC_F_BEARER_STATS_DOWNLINK_SPEED,
+ MMC_F_BEARER_STATS_BYTES_RX,
+ MMC_F_BEARER_STATS_BYTES_TX,
+ MMC_F_BEARER_STATS_ATTEMPTS,
+ MMC_F_BEARER_STATS_FAILED_ATTEMPTS,
+ MMC_F_BEARER_STATS_TOTAL_DURATION,
+ MMC_F_BEARER_STATS_TOTAL_BYTES_RX,
+ MMC_F_BEARER_STATS_TOTAL_BYTES_TX,
+ MMC_F_CALL_GENERAL_DBUS_PATH,
+ MMC_F_CALL_PROPERTIES_NUMBER,
+ MMC_F_CALL_PROPERTIES_DIRECTION,
+ MMC_F_CALL_PROPERTIES_MULTIPARTY,
+ MMC_F_CALL_PROPERTIES_STATE,
+ MMC_F_CALL_PROPERTIES_STATE_REASON,
+ MMC_F_CALL_PROPERTIES_AUDIO_PORT,
+ MMC_F_CALL_AUDIO_FORMAT_ENCODING,
+ MMC_F_CALL_AUDIO_FORMAT_RESOLUTION,
+ MMC_F_CALL_AUDIO_FORMAT_RATE,
+ MMC_F_SMS_GENERAL_DBUS_PATH,
+ MMC_F_SMS_CONTENT_NUMBER,
+ MMC_F_SMS_CONTENT_TEXT,
+ MMC_F_SMS_CONTENT_DATA,
+ MMC_F_SMS_PROPERTIES_PDU_TYPE,
+ MMC_F_SMS_PROPERTIES_STATE,
+ MMC_F_SMS_PROPERTIES_VALIDITY,
+ MMC_F_SMS_PROPERTIES_STORAGE,
+ MMC_F_SMS_PROPERTIES_SMSC,
+ MMC_F_SMS_PROPERTIES_CLASS,
+ MMC_F_SMS_PROPERTIES_TELESERVICE_ID,
+ MMC_F_SMS_PROPERTIES_SERVICE_CATEGORY,
+ MMC_F_SMS_PROPERTIES_DELIVERY_REPORT,
+ MMC_F_SMS_PROPERTIES_MSG_REFERENCE,
+ MMC_F_SMS_PROPERTIES_TIMESTAMP,
+ MMC_F_SMS_PROPERTIES_DELIVERY_STATE,
+ MMC_F_SMS_PROPERTIES_DISCH_TIMESTAMP,
+ MMC_F_SIM_GENERAL_DBUS_PATH,
+ MMC_F_SIM_PROPERTIES_ACTIVE,
+ MMC_F_SIM_PROPERTIES_IMSI,
+ MMC_F_SIM_PROPERTIES_ICCID,
+ MMC_F_SIM_PROPERTIES_EID,
+ MMC_F_SIM_PROPERTIES_OPERATOR_ID,
+ MMC_F_SIM_PROPERTIES_OPERATOR_NAME,
+ MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS,
+ MMC_F_SIM_PROPERTIES_PREFERRED_NETWORKS,
+ MMC_F_SAR_STATE,
+ MMC_F_SAR_POWER_LEVEL,
+ /* Lists */
+ MMC_F_MODEM_LIST_DBUS_PATH,
+ MMC_F_SMS_LIST_DBUS_PATH,
+ MMC_F_CALL_LIST_DBUS_PATH,
+} MmcF;
+
+/******************************************************************************/
+/* Output type selection */
+
+typedef enum {
+ MMC_OUTPUT_TYPE_NONE,
+ MMC_OUTPUT_TYPE_HUMAN,
+ MMC_OUTPUT_TYPE_KEYVALUE,
+ MMC_OUTPUT_TYPE_JSON
+} MmcOutputType;
+
+void mmcli_output_set (MmcOutputType type);
+MmcOutputType mmcli_output_get (void);
+
+/******************************************************************************/
+/* Generic output management */
+
+void mmcli_output_string (MmcF field,
+ const gchar *str);
+void mmcli_output_string_take (MmcF field,
+ gchar *str);
+void mmcli_output_string_list (MmcF field,
+ const gchar *str);
+void mmcli_output_string_list_take (MmcF field,
+ gchar *str);
+void mmcli_output_string_multiline (MmcF field,
+ const gchar *str);
+void mmcli_output_string_multiline_take (MmcF field,
+ gchar *str);
+void mmcli_output_string_array (MmcF field,
+ const gchar **strv,
+ gboolean multiline);
+void mmcli_output_string_array_take (MmcF field,
+ gchar **strv,
+ gboolean multiline);
+void mmcli_output_string_array_multiline_take (MmcF field,
+ gchar **strv);
+void mmcli_output_string_take_typed (MmcF field,
+ gchar *value,
+ const gchar *type);
+void mmcli_output_listitem (MmcF field,
+ const gchar *prefix,
+ const gchar *value,
+ const gchar *extra);
+
+/******************************************************************************/
+/* Custom output management */
+
+void mmcli_output_signal_quality (guint value,
+ gboolean recent);
+void mmcli_output_start_date (guint64 value);
+void mmcli_output_state (MMModemState state,
+ MMModemStateFailedReason reason);
+void mmcli_output_sim_slots (gchar **sim_slot_paths,
+ guint primary_sim_slot);
+void mmcli_output_scan_networks (GList *network_list);
+void mmcli_output_firmware_list (GList *firmware_list,
+ MMFirmwareProperties *selected);
+void mmcli_output_pco_list (GList *pco_list);
+void mmcli_output_preferred_networks (GList *preferred_nets_list);
+void mmcli_output_profile_list (GList *profile_list);
+void mmcli_output_profile_set (MM3gppProfile *profile);
+
+/******************************************************************************/
+/* Dump output */
+
+void mmcli_output_dump (void);
+void mmcli_output_list_dump (MmcF field);
+
+#endif /* MMCLI_OUTPUT_H */
diff --git a/cli/mmcli-sim.c b/cli/mmcli-sim.c
index 65993392..3056783b 100644
--- a/cli/mmcli-sim.c
+++ b/cli/mmcli-sim.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -15,7 +15,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2011-2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -50,6 +51,7 @@ static gchar *puk_str;
static gboolean enable_pin_flag;
static gboolean disable_pin_flag;
static gchar *change_pin_str;
+static gchar *set_preferred_networks_str;
static GOptionEntry entries[] = {
{ "pin", 0, 0, G_OPTION_ARG_STRING, &pin_str,
@@ -72,6 +74,10 @@ static GOptionEntry entries[] = {
"Change the PIN in a given SIM (must send the current PIN with --pin).",
"[New PIN]"
},
+ { "sim-set-preferred-networks", 0, 0, G_OPTION_ARG_STRING, &set_preferred_networks_str,
+ "Set preferred network list stored in a given SIM.",
+ "[[MCCMNC[,access_tech]],...]"
+ },
{ NULL }
};
@@ -82,7 +88,7 @@ mmcli_sim_get_option_group (void)
/* Status options */
group = g_option_group_new ("sim",
- "SIM options",
+ "SIM options:",
"Show SIM options",
NULL,
NULL);
@@ -103,10 +109,11 @@ mmcli_sim_options_enabled (void)
n_actions = (!!puk_str +
enable_pin_flag +
disable_pin_flag +
- !!change_pin_str);
+ !!change_pin_str +
+ !!set_preferred_networks_str);
if (n_actions == 1) {
- if (!pin_str) {
+ if (!pin_str && !set_preferred_networks_str) {
g_printerr ("error: action requires also the PIN code\n");
exit (EXIT_FAILURE);
}
@@ -124,12 +131,15 @@ mmcli_sim_options_enabled (void)
exit (EXIT_FAILURE);
}
+ if (info_flag)
+ mmcli_force_sync_operation ();
+
checked = TRUE;
return !!n_actions;
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -148,28 +158,26 @@ context_free (Context *ctx)
void
mmcli_sim_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
print_sim_info (MMSim *sim)
{
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE
-#define VALIDATE(str) (str ? str : "unknown")
-
- g_print ("SIM '%s'\n",
- mm_sim_get_path (sim));
- g_print (" -------------------------\n"
- " Properties | imsi : '%s'\n"
- " | id : '%s'\n"
- " | operator id : '%s'\n"
- " | operator name : '%s'\n",
- VALIDATE (mm_sim_get_imsi (sim)),
- VALIDATE (mm_sim_get_identifier (sim)),
- VALIDATE (mm_sim_get_operator_identifier (sim)),
- VALIDATE (mm_sim_get_operator_name (sim)));
+ GList *preferred_nets_list;
+
+ mmcli_output_string (MMC_F_SIM_GENERAL_DBUS_PATH, mm_sim_get_path (sim));
+ mmcli_output_string (MMC_F_SIM_PROPERTIES_ACTIVE, mm_sim_get_active (sim) ? "yes" : "no");
+ mmcli_output_string (MMC_F_SIM_PROPERTIES_IMSI, mm_sim_get_imsi (sim));
+ mmcli_output_string (MMC_F_SIM_PROPERTIES_ICCID, mm_sim_get_identifier (sim));
+ mmcli_output_string (MMC_F_SIM_PROPERTIES_EID, mm_sim_get_eid (sim));
+ mmcli_output_string (MMC_F_SIM_PROPERTIES_OPERATOR_ID, mm_sim_get_operator_identifier (sim));
+ mmcli_output_string (MMC_F_SIM_PROPERTIES_OPERATOR_NAME, mm_sim_get_operator_name (sim));
+ mmcli_output_string_array (MMC_F_SIM_PROPERTIES_EMERGENCY_NUMBERS, (const gchar **) mm_sim_get_emergency_numbers (sim), FALSE);
+ preferred_nets_list = mm_sim_get_preferred_networks (sim);
+ mmcli_output_preferred_networks (preferred_nets_list);
+ g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
+ mmcli_output_dump ();
}
static void
@@ -308,6 +316,77 @@ change_pin_ready (MMSim *sim,
}
static void
+parse_preferred_networks (GList **preferred_networks)
+{
+ gchar **parts;
+ GList *preferred_nets_list = NULL;
+ GError *error = NULL;
+
+ parts = g_strsplit (set_preferred_networks_str, ",", -1);
+ if (parts) {
+ guint i;
+
+ for (i = 0; parts[i]; i++) {
+ MMModemAccessTechnology access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ MMSimPreferredNetwork *preferred_net;
+ const gchar *mccmnc;
+
+ mccmnc = parts[i];
+ if (!mm_is_string_mccmnc (mccmnc)) {
+ g_printerr ("error: couldn't parse MCCMNC for preferred network: '%s'\n",
+ mccmnc);
+ exit (EXIT_FAILURE);
+ }
+ /* if the next item is MCCMNC or is missing, omit the access technology */
+ if (parts[i + 1] && !mm_is_string_mccmnc (parts[i + 1])) {
+ i++;
+ access_tech = mm_common_get_access_technology_from_string (parts[i], &error);
+ if (error) {
+ g_printerr ("error: %s\n", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ preferred_net = mm_sim_preferred_network_new ();
+ mm_sim_preferred_network_set_operator_code (preferred_net, mccmnc);
+ mm_sim_preferred_network_set_access_technology (preferred_net, access_tech);
+ preferred_nets_list = g_list_append (preferred_nets_list, preferred_net);
+ }
+ }
+ g_strfreev (parts);
+
+ *preferred_networks = preferred_nets_list;
+}
+
+static void
+set_preferred_networks_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't set preferred networks: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully set preferred networks\n");
+}
+
+static void
+set_preferred_networks_ready (MMSim *sim,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_sim_set_preferred_networks_finish (sim, result, &error);
+ set_preferred_networks_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
get_sim_ready (GObject *source,
GAsyncResult *result,
gpointer none)
@@ -364,6 +443,20 @@ get_sim_ready (GObject *source,
return;
}
+ /* Requesting to set preferred networks? */
+ if (set_preferred_networks_str) {
+ GList *preferred_networks = NULL;
+
+ parse_preferred_networks (&preferred_networks);
+ mm_sim_set_preferred_networks (ctx->sim,
+ preferred_networks,
+ ctx->cancellable,
+ (GAsyncReadyCallback)set_preferred_networks_ready,
+ NULL);
+ g_list_free_full (preferred_networks, (GDestroyNotify) mm_sim_preferred_network_free);
+ return;
+ }
+
/* Requesting to send PIN? (always LAST check!) */
if (pin_str) {
mm_sim_send_pin (ctx->sim,
@@ -466,6 +559,21 @@ mmcli_sim_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Requesting to set preferred networks? */
+ if (set_preferred_networks_str) {
+ gboolean operation_result;
+ GList *preferred_networks = NULL;
+
+ parse_preferred_networks (&preferred_networks);
+ operation_result = mm_sim_set_preferred_networks_sync (ctx->sim,
+ preferred_networks,
+ NULL,
+ &error);
+ g_list_free_full (preferred_networks, (GDestroyNotify) mm_sim_preferred_network_free);
+ set_preferred_networks_process_reply (operation_result, error);
+ return;
+ }
+
/* Requesting to send PIN? (always LAST check!) */
if (pin_str) {
gboolean operation_result;
diff --git a/cli/mmcli-sms.c b/cli/mmcli-sms.c
index 9c54ef76..c468ed60 100644
--- a/cli/mmcli-sms.c
+++ b/cli/mmcli-sms.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -33,6 +33,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
/* Context */
typedef struct {
@@ -77,7 +78,7 @@ mmcli_sms_get_option_group (void)
/* Status options */
group = g_option_group_new ("sms",
- "SMS options",
+ "SMS options:",
"Show SMS options",
NULL,
NULL);
@@ -122,7 +123,7 @@ mmcli_sms_options_enabled (void)
}
static void
-context_free (Context *ctx)
+context_free (void)
{
if (!ctx)
return;
@@ -141,95 +142,55 @@ context_free (Context *ctx)
void
mmcli_sms_shutdown (void)
{
- context_free (ctx);
+ context_free ();
}
static void
print_sms_info (MMSms *sms)
{
- MMSmsPduType pdu_type;
- const guint8 *data;
- gsize data_size;
-
- /* Not the best thing to do, as we may be doing _get() calls twice, but
- * easiest to maintain */
-#undef VALIDATE
-#define VALIDATE(str) (str ? str : "unknown")
-
- pdu_type = mm_sms_get_pdu_type (sms);
-
- g_print ("SMS '%s'\n",
- mm_sms_get_path (sms));
- g_print (" -----------------------------------\n"
- " Content | number: '%s'\n",
- VALIDATE (mm_sms_get_number (sms)));
-
- if (mm_sms_get_text (sms))
- g_print (" | text: '%s'\n",
- VALIDATE (mm_sms_get_text (sms)));
-
- data = mm_sms_get_data (sms, &data_size);
- if (data) {
- gchar *data_hex;
-
- data_hex = mm_utils_bin2hexstr (data, data_size);
- g_print (" | data: '%s'\n",
- VALIDATE (data_hex));
- g_free (data_hex);
- }
-
- g_print (" -----------------------------------\n"
- " Properties | PDU type: '%s'\n"
- " | state: '%s'\n",
- mm_sms_pdu_type_get_string (pdu_type),
- mm_sms_state_get_string (mm_sms_get_state (sms)));
-
+ MMSmsPduType pdu_type;
+ gchar *data = NULL;
+ const guint8 *databin;
+ gsize databin_size;
+ gchar *validity = NULL;
+ gchar *class = NULL;
+ const gchar *delivery_report = NULL;
+ gchar *message_reference = NULL;
+ const gchar *delivery_state = NULL;
+
+ databin = mm_sms_get_data (sms, &databin_size);
+ if (databin)
+ data = mm_utils_bin2hexstr (databin, databin_size);
if (mm_sms_get_validity_type (sms) == MM_SMS_VALIDITY_TYPE_RELATIVE)
- g_print (" | validity (relative): '%u'\n",
- mm_sms_get_validity_relative (sms));
-
- g_print (" | storage: '%s'\n",
- mm_sms_storage_get_string (mm_sms_get_storage (sms)));
-
- /* Print properties which are set, regardless of the pdu type */
-
- if (mm_sms_get_smsc (sms))
- g_print (" | smsc: '%s'\n",
- mm_sms_get_smsc (sms));
-
+ validity = g_strdup_printf ("%u", mm_sms_get_validity_relative (sms));
if (mm_sms_get_class (sms) >= 0)
- g_print (" | class: '%d'\n",
- mm_sms_get_class (sms));
-
- if (mm_sms_get_teleservice_id (sms) != MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN)
- g_print (" | teleservice id: '%s'\n",
- mm_sms_cdma_teleservice_id_get_string (mm_sms_get_teleservice_id (sms)));
-
- if (mm_sms_get_service_category (sms) != MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN)
- g_print (" | service category: '%s'\n",
- mm_sms_cdma_service_category_get_string (mm_sms_get_service_category (sms)));
-
- /* Delivery report request just in 3GPP submit PDUs */
+ class = g_strdup_printf ("%d", mm_sms_get_class (sms));
+ pdu_type = mm_sms_get_pdu_type (sms);
if (pdu_type == MM_SMS_PDU_TYPE_SUBMIT)
- g_print (" | delivery report: '%s'\n",
- mm_sms_get_delivery_report_request (sms) ? "requested" : "not requested");
-
+ delivery_report = mm_sms_get_delivery_report_request (sms) ? "requested" : "not requested";
if (mm_sms_get_message_reference (sms) != 0)
- g_print (" | message reference: '%u'\n",
- mm_sms_get_message_reference (sms));
-
- if (mm_sms_get_timestamp (sms))
- g_print (" | timestamp: '%s'\n",
- mm_sms_get_timestamp (sms));
-
+ message_reference = g_strdup_printf ("%u", mm_sms_get_message_reference (sms));
if (mm_sms_get_delivery_state (sms) != MM_SMS_DELIVERY_STATE_UNKNOWN)
- g_print (" | delivery state: '%s' (0x%X)\n",
- VALIDATE (mm_sms_delivery_state_get_string_extended (mm_sms_get_delivery_state (sms))),
- mm_sms_get_delivery_state (sms));
-
- if (mm_sms_get_discharge_timestamp (sms))
- g_print (" | discharge timestamp: '%s'\n",
- mm_sms_get_discharge_timestamp (sms));
+ delivery_state = mm_sms_delivery_state_get_string_extended (mm_sms_get_delivery_state (sms));
+
+ mmcli_output_string (MMC_F_SMS_GENERAL_DBUS_PATH, mm_sms_get_path (sms));
+ mmcli_output_string (MMC_F_SMS_CONTENT_NUMBER, mm_sms_get_number (sms));
+ mmcli_output_string (MMC_F_SMS_CONTENT_TEXT, mm_sms_get_text (sms));
+ mmcli_output_string_take (MMC_F_SMS_CONTENT_DATA, data);
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_PDU_TYPE, mm_sms_pdu_type_get_string (pdu_type));
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_STATE, mm_sms_state_get_string (mm_sms_get_state (sms)));
+ mmcli_output_string_take (MMC_F_SMS_PROPERTIES_VALIDITY, validity);
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_STORAGE, mm_sms_storage_get_string (mm_sms_get_storage (sms)));
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_SMSC, mm_sms_get_smsc (sms));
+ mmcli_output_string_take (MMC_F_SMS_PROPERTIES_CLASS, class);
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_TELESERVICE_ID, mm_sms_cdma_teleservice_id_get_string (mm_sms_get_teleservice_id (sms)));
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_SERVICE_CATEGORY, mm_sms_cdma_service_category_get_string (mm_sms_get_service_category (sms)));
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_DELIVERY_REPORT, delivery_report);
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_MSG_REFERENCE, message_reference);
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_TIMESTAMP, mm_sms_get_timestamp (sms));
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_DELIVERY_STATE, delivery_state);
+ mmcli_output_string (MMC_F_SMS_PROPERTIES_DISCH_TIMESTAMP, mm_sms_get_discharge_timestamp (sms));
+ mmcli_output_dump ();
}
static void
@@ -450,7 +411,6 @@ mmcli_sms_run_synchronous (GDBusConnection *connection)
if (store_in_storage_str) {
gboolean operation_result;
MMSmsStorage storage;
- GError *error = NULL;
storage = mm_common_get_sms_storage_from_string (store_in_storage_str, &error);
if (error) {
diff --git a/cli/mmcli.c b/cli/mmcli.c
index a4e6a4f5..e10267a3 100644
--- a/cli/mmcli.c
+++ b/cli/mmcli.c
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -34,6 +34,7 @@
#include "mmcli.h"
#include "mmcli-common.h"
+#include "mmcli-output.h"
#define PROGRAM_NAME "mmcli"
#define PROGRAM_VERSION PACKAGE_VERSION
@@ -43,12 +44,22 @@ static GMainLoop *loop;
static GCancellable *cancellable;
/* Context */
+static gboolean output_keyvalue_flag;
+static gboolean output_json_flag;
static gboolean verbose_flag;
static gboolean version_flag;
static gboolean async_flag;
static gint timeout = 30; /* by default, use 30s for all operations */
static GOptionEntry main_entries[] = {
+ { "output-keyvalue", 'K', 0, G_OPTION_ARG_NONE, &output_keyvalue_flag,
+ "Run action with machine-friendly key-value output",
+ NULL
+ },
+ { "output-json", 'J', 0, G_OPTION_ARG_NONE, &output_json_flag,
+ "Run action with machine-friendly json output",
+ NULL
+ },
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
"Run action with verbose logs",
NULL
@@ -110,7 +121,6 @@ log_handler (const gchar *log_domain,
break;
case G_LOG_LEVEL_CRITICAL:
- case G_LOG_FLAG_FATAL:
case G_LOG_LEVEL_ERROR:
log_level_str = "-Error **";
break;
@@ -119,9 +129,16 @@ log_handler (const gchar *log_domain,
log_level_str = "[Debug]";
break;
- default:
+ case G_LOG_LEVEL_MESSAGE:
+ case G_LOG_LEVEL_INFO:
log_level_str = "";
break;
+
+ case G_LOG_FLAG_FATAL:
+ case G_LOG_LEVEL_MASK:
+ case G_LOG_FLAG_RECURSION:
+ default:
+ g_assert_not_reached ();
}
g_print ("[%s] %s %s\n", time_str, log_level_str, message);
@@ -130,9 +147,8 @@ log_handler (const gchar *log_domain,
static void
print_version_and_exit (void)
{
- g_print ("\n"
- PROGRAM_NAME " " PROGRAM_VERSION "\n"
- "Copyright (2011) Aleksander Morgado\n"
+ g_print (PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (2011 - 2021) Aleksander Morgado\n"
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
@@ -172,7 +188,8 @@ mmcli_force_sync_operation (void)
void
mmcli_force_operation_timeout (GDBusProxy *proxy)
{
- g_dbus_proxy_set_default_timeout (proxy, timeout * 1000);
+ if (proxy)
+ g_dbus_proxy_set_default_timeout (proxy, timeout * 1000);
}
gint
@@ -184,8 +201,6 @@ main (gint argc, gchar **argv)
setlocale (LC_ALL, "");
- g_type_init ();
-
/* Setup option context, process it and destroy it */
context = g_option_context_new ("- Control and monitor the ModemManager");
g_option_context_add_group (context,
@@ -197,6 +212,10 @@ main (gint argc, gchar **argv)
g_option_context_add_group (context,
mmcli_modem_3gpp_get_option_group ());
g_option_context_add_group (context,
+ mmcli_modem_3gpp_profile_manager_get_option_group ());
+ g_option_context_add_group (context,
+ mmcli_modem_3gpp_ussd_get_option_group ());
+ g_option_context_add_group (context,
mmcli_modem_cdma_get_option_group ());
g_option_context_add_group (context,
mmcli_modem_simple_get_option_group ());
@@ -205,10 +224,14 @@ main (gint argc, gchar **argv)
g_option_context_add_group (context,
mmcli_modem_messaging_get_option_group ());
g_option_context_add_group (context,
+ mmcli_modem_voice_get_option_group ());
+ g_option_context_add_group (context,
mmcli_modem_time_get_option_group ());
g_option_context_add_group (context,
mmcli_modem_firmware_get_option_group ());
g_option_context_add_group (context,
+ mmcli_modem_sar_get_option_group ());
+ g_option_context_add_group (context,
mmcli_modem_signal_get_option_group ());
g_option_context_add_group (context,
mmcli_modem_oma_get_option_group ());
@@ -218,6 +241,8 @@ main (gint argc, gchar **argv)
mmcli_bearer_get_option_group ());
g_option_context_add_group (context,
mmcli_sms_get_option_group ());
+ g_option_context_add_group (context,
+ mmcli_call_get_option_group ());
g_option_context_add_main_entries (context, main_entries, NULL);
g_option_context_parse (context, &argc, &argv, NULL);
g_option_context_free (context);
@@ -228,6 +253,28 @@ main (gint argc, gchar **argv)
if (verbose_flag)
g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, log_handler, NULL);
+ /* Setup output */
+ if (output_keyvalue_flag && output_json_flag) {
+ g_printerr ("error: only one output type supported at the same time\n");
+ exit (EXIT_FAILURE);
+ }
+ if (output_keyvalue_flag) {
+ if (verbose_flag) {
+ g_printerr ("error: cannot set verbose output in keyvalue output type\n");
+ exit (EXIT_FAILURE);
+ }
+ mmcli_output_set (MMC_OUTPUT_TYPE_KEYVALUE);
+ }
+ else if (output_json_flag) {
+ if (verbose_flag) {
+ g_printerr ("error: cannot set verbose output in JSON output type\n");
+ exit (EXIT_FAILURE);
+ }
+ mmcli_output_set (MMC_OUTPUT_TYPE_JSON);
+ } else {
+ mmcli_output_set (MMC_OUTPUT_TYPE_HUMAN);
+ }
+
/* Setup signals */
signal (SIGINT, signals_handler);
signal (SIGHUP, signals_handler);
@@ -280,6 +327,13 @@ main (gint argc, gchar **argv)
else
mmcli_sms_run_synchronous (connection);
}
+ /* Call options? */
+ else if (mmcli_call_options_enabled ()) {
+ if (async_flag)
+ mmcli_call_run_asynchronous (connection, cancellable);
+ else
+ mmcli_call_run_synchronous (connection);
+ }
/* Modem 3GPP options? */
else if (mmcli_modem_3gpp_options_enabled ()) {
if (async_flag)
@@ -287,6 +341,20 @@ main (gint argc, gchar **argv)
else
mmcli_modem_3gpp_run_synchronous (connection);
}
+ /* Modem 3GPP profile manager options? */
+ else if (mmcli_modem_3gpp_profile_manager_options_enabled ()) {
+ if (async_flag)
+ mmcli_modem_3gpp_profile_manager_run_asynchronous (connection, cancellable);
+ else
+ mmcli_modem_3gpp_profile_manager_run_synchronous (connection);
+ }
+ /* Modem 3GPP USSD options? */
+ else if (mmcli_modem_3gpp_ussd_options_enabled ()) {
+ if (async_flag)
+ mmcli_modem_3gpp_ussd_run_asynchronous (connection, cancellable);
+ else
+ mmcli_modem_3gpp_ussd_run_synchronous (connection);
+ }
/* Modem CDMA options? */
else if (mmcli_modem_cdma_options_enabled ()) {
if (async_flag)
@@ -315,6 +383,13 @@ main (gint argc, gchar **argv)
else
mmcli_modem_messaging_run_synchronous (connection);
}
+ /* Voice options? */
+ else if (mmcli_modem_voice_options_enabled ()) {
+ if (async_flag)
+ mmcli_modem_voice_run_asynchronous (connection, cancellable);
+ else
+ mmcli_modem_voice_run_synchronous (connection);
+ }
/* Modem Time options? */
else if (mmcli_modem_time_options_enabled ()) {
if (async_flag)
@@ -329,6 +404,13 @@ main (gint argc, gchar **argv)
else
mmcli_modem_firmware_run_synchronous (connection);
}
+ /* Modem SAR options? */
+ else if (mmcli_modem_sar_options_enabled ()) {
+ if (async_flag)
+ mmcli_modem_sar_run_asynchronous (connection, cancellable);
+ else
+ mmcli_modem_sar_run_synchronous (connection);
+ }
/* Modem Signal options? */
else if (mmcli_modem_signal_options_enabled ()) {
if (async_flag)
@@ -367,6 +449,10 @@ main (gint argc, gchar **argv)
mmcli_manager_shutdown ();
} else if (mmcli_modem_3gpp_options_enabled ()) {
mmcli_modem_3gpp_shutdown ();
+ } else if (mmcli_modem_3gpp_profile_manager_options_enabled ()) {
+ mmcli_modem_3gpp_profile_manager_shutdown ();
+ } else if (mmcli_modem_3gpp_ussd_options_enabled ()) {
+ mmcli_modem_3gpp_ussd_shutdown ();
} else if (mmcli_modem_cdma_options_enabled ()) {
mmcli_modem_cdma_shutdown ();
} else if (mmcli_modem_simple_options_enabled ()) {
@@ -375,10 +461,14 @@ main (gint argc, gchar **argv)
mmcli_modem_location_shutdown ();
} else if (mmcli_modem_messaging_options_enabled ()) {
mmcli_modem_messaging_shutdown ();
+ } else if (mmcli_modem_voice_options_enabled ()) {
+ mmcli_modem_voice_shutdown ();
} else if (mmcli_modem_time_options_enabled ()) {
mmcli_modem_time_shutdown ();
} else if (mmcli_modem_firmware_options_enabled ()) {
mmcli_modem_firmware_shutdown ();
+ } else if (mmcli_modem_sar_options_enabled ()) {
+ mmcli_modem_sar_shutdown ();
} else if (mmcli_modem_signal_options_enabled ()) {
mmcli_modem_signal_shutdown ();
} else if (mmcli_modem_oma_options_enabled ()) {
@@ -389,6 +479,8 @@ main (gint argc, gchar **argv)
mmcli_bearer_shutdown ();
} else if (mmcli_sms_options_enabled ()) {
mmcli_sms_shutdown ();
+ } else if (mmcli_call_options_enabled ()) {
+ mmcli_call_shutdown ();
} else if (mmcli_modem_options_enabled ()) {
mmcli_modem_shutdown ();
}
diff --git a/cli/mmcli.h b/cli/mmcli.h
index 6b7b5536..de5cc804 100644
--- a/cli/mmcli.h
+++ b/cli/mmcli.h
@@ -4,7 +4,7 @@
*
* 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 3 of the License, or
+ * 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,
@@ -53,6 +53,22 @@ void mmcli_modem_3gpp_run_asynchronous (GDBusConnection *connection,
void mmcli_modem_3gpp_run_synchronous (GDBusConnection *connection);
void mmcli_modem_3gpp_shutdown (void);
+/* 3GPP USSD group */
+GOptionGroup *mmcli_modem_3gpp_ussd_get_option_group (void);
+gboolean mmcli_modem_3gpp_ussd_options_enabled (void);
+void mmcli_modem_3gpp_ussd_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable);
+void mmcli_modem_3gpp_ussd_run_synchronous (GDBusConnection *connection);
+void mmcli_modem_3gpp_ussd_shutdown (void);
+
+/* 3GPP profile manager group */
+GOptionGroup *mmcli_modem_3gpp_profile_manager_get_option_group (void);
+gboolean mmcli_modem_3gpp_profile_manager_options_enabled (void);
+void mmcli_modem_3gpp_profile_manager_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable);
+void mmcli_modem_3gpp_profile_manager_run_synchronous (GDBusConnection *connection);
+void mmcli_modem_3gpp_profile_manager_shutdown (void);
+
/* CDMA group */
GOptionGroup *mmcli_modem_cdma_get_option_group (void);
gboolean mmcli_modem_cdma_options_enabled (void);
@@ -85,6 +101,14 @@ void mmcli_modem_messaging_run_asynchronous (GDBusConnection *connect
void mmcli_modem_messaging_run_synchronous (GDBusConnection *connection);
void mmcli_modem_messaging_shutdown (void);
+/* Voice group */
+GOptionGroup *mmcli_modem_voice_get_option_group (void);
+gboolean mmcli_modem_voice_options_enabled (void);
+void mmcli_modem_voice_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable);
+void mmcli_modem_voice_run_synchronous (GDBusConnection *connection);
+void mmcli_modem_voice_shutdown (void);
+
/* Time group */
GOptionGroup *mmcli_modem_time_get_option_group (void);
gboolean mmcli_modem_time_options_enabled (void);
@@ -101,6 +125,15 @@ void mmcli_modem_firmware_run_asynchronous (GDBusConnection *connecti
void mmcli_modem_firmware_run_synchronous (GDBusConnection *connection);
void mmcli_modem_firmware_shutdown (void);
+/* SAR group */
+GOptionGroup *mmcli_modem_sar_get_option_group (void);
+gboolean mmcli_modem_sar_options_enabled (void);
+void mmcli_modem_sar_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable);
+void mmcli_modem_sar_run_synchronous (GDBusConnection *connection);
+void mmcli_modem_sar_shutdown (void);
+
+
/* Signal group */
GOptionGroup *mmcli_modem_signal_get_option_group (void);
gboolean mmcli_modem_signal_options_enabled (void);
@@ -141,4 +174,12 @@ void mmcli_sms_run_asynchronous (GDBusConnection *connection,
void mmcli_sms_run_synchronous (GDBusConnection *connection);
void mmcli_sms_shutdown (void);
+/* Call group */
+GOptionGroup *mmcli_call_get_option_group (void);
+gboolean mmcli_call_options_enabled (void);
+void mmcli_call_run_asynchronous (GDBusConnection *connection,
+ GCancellable *cancellable);
+void mmcli_call_run_synchronous (GDBusConnection *connection);
+void mmcli_call_shutdown (void);
+
#endif /* __MMCLI_H__ */
diff --git a/configure.ac b/configure.ac
index c0064d21..fba9df98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,8 +1,11 @@
AC_PREREQ([2.63])
-dnl The MM version number
+dnl-----------------------------------------------------------------------------
+dnl Package and library versioning support
+dnl
+
m4_define([mm_major_version], [1])
-m4_define([mm_minor_version], [5])
+m4_define([mm_minor_version], [19])
m4_define([mm_micro_version], [0])
m4_define([mm_version],
[mm_major_version.mm_minor_version.mm_micro_version])
@@ -15,16 +18,24 @@ dnl If the interface has grown (that is, the new library is compatible
dnl with old code), increment a.
dnl If the interface has changed in an incompatible way (that is,
dnl functions have changed or been removed), then zero a.
-m4_define([mm_glib_lt_current], [2])
+m4_define([mm_glib_lt_current], [8])
m4_define([mm_glib_lt_revision], [0])
-m4_define([mm_glib_lt_age], [2])
+m4_define([mm_glib_lt_age], [8])
-
-AC_INIT([ModemManager],[mm_version],[modemmanager-devel@lists.freedesktop.org],[ModemManager])
-AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-xz -Wno-portability])
+dnl-----------------------------------------------------------------------------
+dnl autoconf, automake, libtool initialization
+dnl
+AC_INIT([ModemManager],
+ [mm_version],
+ [https://gitlab.freedesktop.org/mobile-broadband/ModemManager/issues],
+ [ModemManager])
+AM_INIT_AUTOMAKE([1.11.2 subdir-objects tar-ustar no-dist-gzip dist-xz -Wno-portability])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
+
AM_MAINTAINER_MODE([enable])
+AM_SILENT_RULES([yes])
+
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS(config.h)
@@ -36,10 +47,42 @@ dnl Required programs
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_INSTALL
+AC_PROG_MKDIR_P
dnl Initialize libtool
LT_PREREQ([2.2])
-LT_INIT
+LT_INIT([disable-static])
+
+dnl-----------------------------------------------------------------------------
+dnl Compiler warnings
+dnl
+
+dnl Make sure autoconf-archive is available
+m4_pattern_forbid([^AX_(COMPILER_FLAGS|COMPILER_FLAGS_(CFLAGS|GIR|LDFLAGS))\b],
+ [Unexpanded AX_ macro found. Please install GNU autoconf-archive])
+
+dnl Detect git or release builds
+AX_IS_RELEASE([git-directory])
+
+dnl Function type cast disabled: used throughout the code especially to
+dnl cast GAsyncReadyCallbacks with the real object type instead of GObject
+DISABLED_WARNINGS="${DISABLED_WARNINGS} -Wno-cast-function-type"
+
+dnl All message protocol structs are packed, never complain about it
+DISABLED_WARNINGS="${DISABLED_WARNINGS} -Wno-packed"
+
+dnl Setup compiler checks
+AX_COMPILER_FLAGS()
+AX_COMPILER_FLAGS_CFLAGS(,,,[${DISABLED_WARNINGS}])
+
+dnl Specify gnu89 mode
+if test "$GCC" = "yes"; then
+ CFLAGS="$CFLAGS -std=gnu89"
+fi
+
+dnl-----------------------------------------------------------------------------
+dnl Version definitions
+dnl
dnl Version stuff
MM_MAJOR_VERSION=mm_major_version
@@ -59,69 +102,95 @@ AC_SUBST(MM_GLIB_LT_CURRENT)
AC_SUBST(MM_GLIB_LT_REVISION)
AC_SUBST(MM_GLIB_LT_AGE)
-dnl
+dnl-----------------------------------------------------------------------------
dnl Documentation
dnl
+
GTK_DOC_CHECK(1.0)
+dnl-----------------------------------------------------------------------------
+dnl i18n
dnl
-dnl translation support
-dnl
-IT_PROG_INTLTOOL([0.40.0])
AM_GNU_GETTEXT([external])
-AM_GNU_GETTEXT_VERSION([0.17])
+AM_GNU_GETTEXT_VERSION([0.19.8])
GETTEXT_PACKAGE=ModemManager
AC_SUBST(GETTEXT_PACKAGE)
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE", [Gettext package])
+dnl-----------------------------------------------------------------------------
+dnl Build dependencies
+dnl
+
+GLIB_MIN_VERSION=2.56.0
+GLIB_BUILD_SYMBOLS="-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_56 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_56 -DGLIB_DISABLE_DEPRECATION_WARNINGS"
+
PKG_CHECK_MODULES(MM,
- glib-2.0 >= 2.32
+ glib-2.0 >= $GLIB_MIN_VERSION
gmodule-2.0
gobject-2.0
gio-2.0
gio-unix-2.0)
+MM_CFLAGS="$MM_CFLAGS $GLIB_BUILD_SYMBOLS"
AC_SUBST(MM_CFLAGS)
AC_SUBST(MM_LIBS)
PKG_CHECK_MODULES(LIBMM_GLIB,
- glib-2.0 >= 2.32
+ glib-2.0 >= $GLIB_MIN_VERSION
gobject-2.0
gio-2.0
gio-unix-2.0)
+LIBMM_GLIB_CFLAGS="$LIBMM_GLIB_CFLAGS $GLIB_BUILD_SYMBOLS"
AC_SUBST(LIBMM_GLIB_CFLAGS)
AC_SUBST(LIBMM_GLIB_LIBS)
PKG_CHECK_MODULES(MMCLI,
- glib-2.0 >= 2.32
+ glib-2.0 >= $GLIB_MIN_VERSION
gobject-2.0
gio-2.0)
+MMCLI_CFLAGS="$MMCLI_CFLAGS $GLIB_BUILD_SYMBOLS"
AC_SUBST(MMCLI_CFLAGS)
-AC_SUBST(LIBMM_LIBS)
+AC_SUBST(MMCLI_LIBS)
-PKG_CHECK_MODULES(GUDEV, gudev-1.0 >= 147)
-AC_SUBST(GUDEV_CFLAGS)
-AC_SUBST(GUDEV_LIBS)
-
-# Some required utilities
+dnl Some required utilities
GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
AC_SUBST(GLIB_MKENUMS)
+
GDBUS_CODEGEN=`$PKG_CONFIG --variable=gdbus_codegen gio-2.0`
AC_SUBST(GDBUS_CODEGEN)
-# GObject Introspection
+dnl xsltproc required in git builds only
+AC_CHECK_PROG(XSLTPROC_CHECK,xsltproc,yes)
+if test "x$ax_is_release" != "xyes" -a "x$XSLTPROC_CHECK" != "xyes"; then
+ AC_MSG_ERROR([Please install xsltproc before configuring.])
+fi
+
+dnl-----------------------------------------------------------------------------
+dnl Testing support
+dnl
+
+dnl Code coverage (disabled by default)
+AX_CODE_COVERAGE
+
+dnl-----------------------------------------------------------------------------
+dnl Introspection and bindings
+dnl
+
+dnl GObject Introspection
GOBJECT_INTROSPECTION_CHECK([0.9.6])
-# Vala bindings
+dnl Vala bindings
VAPIGEN_CHECK(0.18)
-
-# Sanity check
if test "x$enable_vala" = "xyes" -a ! -f "$VAPIGEN_MAKEFILE"; then
AC_MSG_ERROR([Vala bindings enabled but Makefile.vapigen not found. Install vala-devel, or pass --disable-vala])
fi
-# DBus system directory
+dnl-----------------------------------------------------------------------------
+dnl System paths
+dnl
+
+dnl DBus system directory
AC_ARG_WITH(dbus-sys-dir, AS_HELP_STRING([--with-dbus-sys-dir=DIR], [where D-BUS system.d directory is]))
if test -n "$with_dbus_sys_dir" ; then
DBUS_SYS_DIR="$with_dbus_sys_dir"
@@ -130,7 +199,7 @@ else
fi
AC_SUBST(DBUS_SYS_DIR)
-# udev base directory
+dnl udev base directory
AC_ARG_WITH(udev-base-dir, AS_HELP_STRING([--with-udev-base-dir=DIR], [where udev base directory is]))
if test -n "$with_udev_base_dir" ; then
UDEV_BASE_DIR="$with_udev_base_dir"
@@ -139,7 +208,7 @@ else
fi
AC_SUBST(UDEV_BASE_DIR)
-# systemd system unit directory
+dnl systemd system unit directory
AC_ARG_WITH([systemdsystemunitdir], AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [where systemd service files are]),
[], [with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)])
if test "x$with_systemdsystemunitdir" != xno; then
@@ -147,29 +216,117 @@ if test "x$with_systemdsystemunitdir" != xno; then
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$SYSTEMD_UNIT_DIR" -a "$SYSTEMD_UNIT_DIR" != xno ])
-# PolicyKit
+dnl-----------------------------------------------------------------------------
+dnl udev support (enabled by default)
+dnl
+
+GUDEV_VERSION=232
+
+AC_ARG_WITH(udev, AS_HELP_STRING([--without-udev], [Build without udev support]), [], [with_udev=yes])
+AM_CONDITIONAL(WITH_UDEV, test "x$with_udev" = "xyes")
+case $with_udev in
+ yes)
+ PKG_CHECK_MODULES(GUDEV, [gudev-1.0 >= $GUDEV_VERSION], [have_gudev=yes],[have_gudev=no])
+ if test "x$have_gudev" = "xno"; then
+ AC_MSG_ERROR([Couldn't find gudev >= $GUDEV_VERSION. Install it, or otherwise configure using --without-udev to disable udev support.])
+ else
+ AC_DEFINE(WITH_UDEV, 1, [Define if you want udev support])
+ AC_SUBST(GUDEV_CFLAGS)
+ AC_SUBST(GUDEV_LIBS)
+ fi
+ ;;
+ *)
+ with_udev=no
+ ;;
+esac
+
+dnl-----------------------------------------------------------------------------
+dnl Suspend/resume support
+dnl
+
+PKG_CHECK_MODULES(LIBSYSTEMD, [libsystemd >= 209],[have_libsystemd=yes],[have_libsystemd=no])
+PKG_CHECK_MODULES(LIBSYSTEMD_LOGIN, [libsystemd-login >= 183],[have_libsystemd_login=yes],[have_libsystemd_login=no])
+PKG_CHECK_MODULES(LIBELOGIND, [libelogind >= 209], [have_elogind=yes], [have_elogind=no])
+AC_ARG_WITH(systemd-suspend-resume,
+ AS_HELP_STRING([--with-systemd-suspend-resume=no|yes],
+ [Enable systemd suspend/resume support [[default=auto]]]),,
+ [with_systemd_suspend_resume=auto])
+
+if test "x$with_systemd_suspend_resume" = "xauto"; then
+ if test "x$have_libsystemd" = "xyes" || test "x$have_libsystemd_login" = "xyes" || test "x$have_elogind" = "xyes"; then
+ with_systemd_suspend_resume=yes
+ else
+ with_systemd_suspend_resume=no
+ fi
+fi
+
+case $with_systemd_suspend_resume in
+ yes)
+ if test "x$have_libsystemd" = "xno" && test "x$have_libsystemd_login" = "xno" && test "x$have_elogind" = "xno"; then
+ AC_MSG_WARN(libsystemd, libsystemd-login or elogind must be available at runtime for suspend/resume support)
+ fi
+ AC_DEFINE(WITH_SYSTEMD_SUSPEND_RESUME, 1, [Define if you have systemd suspend-resume support])
+ ;;
+ *)
+ with_systemd_suspend_resume=no
+ ;;
+esac
+
+AM_CONDITIONAL(WITH_SYSTEMD_SUSPEND_RESUME, test "x$with_systemd_suspend_resume" = "xyes")
+
+dnl-----------------------------------------------------------------------------
+dnl systemd journal support
+dnl
+
+AC_ARG_WITH(systemd-journal,
+ AS_HELP_STRING([--with-systemd-journal=no|yes|auto],
+ [Enable systemd journal support [[default=auto]]]),,
+ [with_systemd_journal=auto])
+
+if test "x$with_systemd_journal" = "xauto"; then
+ if test "x$have_libsystemd" = "xyes"; then
+ with_systemd_journal=yes
+ else
+ with_systemd_journal=no
+ fi
+fi
+
+case $with_systemd_journal in
+ yes)
+ if test "x$have_libsystemd" = "xno"; then
+ AC_MSG_ERROR(libsystemd development headers are required)
+ fi
+ AC_DEFINE(WITH_SYSTEMD_JOURNAL, 1, [Define if you want systemd journal support])
+ ;;
+ *)
+ with_systemd_journal=no
+ ;;
+esac
+
+AM_CONDITIONAL(WITH_SYSTEMD_JOURNAL, test "x$with_systemd_journal" = "xyes")
+
+dnl-----------------------------------------------------------------------------
+dnl PolicyKit
+dnl
+
PKG_CHECK_MODULES(POLKIT, [polkit-gobject-1 >= 0.97], [have_polkit=yes],[have_polkit=no])
AC_ARG_WITH(polkit,
- AS_HELP_STRING([--with-polkit=(strict|permissive|none)],
+ AS_HELP_STRING([--with-polkit=(strict|permissive|no)],
[Enable PolicyKit support [[default=auto]]]),,
[with_polkit=auto])
-# Handle 'auto' ('strict' if polkit found, 'none' otherwise),
-# 'yes' ('strict') and 'no' ('none')
+
if test "x$with_polkit" = "xauto"; then
if test "x$have_polkit" = "xno"; then
- with_polkit="none"
+ with_polkit="no"
else
with_polkit="strict"
fi
-elif test "x$with_polkit" = "xno"; then
- with_polkit=none
elif test "x$with_polkit" = "xyes"; then
with_polkit=strict
fi
-# Build policies context
-if test "x$with_polkit" = "xnone"; then
- AC_DEFINE(WITH_POLKIT, 0, [Define if you have PolicyKit support])
-else
+
+MM_POLKIT_SERVICE=""
+if test "x$with_polkit" != "xno"; then
if test "x$have_polkit" = "xno"; then
AC_MSG_ERROR(PolicyKit development headers are required)
fi
@@ -177,9 +334,11 @@ else
case "x$with_polkit" in
"xpermissive")
MM_DEFAULT_USER_POLICY="yes"
+ MM_POLKIT_SERVICE="polkit.service"
;;
"xstrict")
MM_DEFAULT_USER_POLICY="auth_self_keep"
+ MM_POLKIT_SERVICE="polkit.service"
;;
*)
AC_MSG_ERROR([Wrong value for --with-polkit: $with_polkit])
@@ -192,18 +351,39 @@ else
AC_SUBST(MM_DEFAULT_USER_POLICY)
fi
-AM_CONDITIONAL(WITH_POLKIT, [test "x$with_polkit" != "xnone" ])
+AC_SUBST(MM_POLKIT_SERVICE)
+AM_CONDITIONAL(WITH_POLKIT, [test "x$with_polkit" != "xno"])
+dnl-----------------------------------------------------------------------------
+dnl AT command via DBus support (disabled by default unless running in --debug)
dnl
+dnl It is suggested that this option is only enabled in custom built systems and
+dnl only if truly required.
+dnl
+
+AC_ARG_WITH(at_command_via_dbus,
+ AS_HELP_STRING([--with-at-command-via-dbus],
+ [Build with Modem.Command() interface enabled always]),
+ [],
+ [with_at_command_via_dbus=no])
+
+if test "x$with_at_command_via_dbus" = "xyes"; then
+ AC_DEFINE(WITH_AT_COMMAND_VIA_DBUS, 1, [Define if you want to enable AT commands via DBus])
+fi
+
+dnl-----------------------------------------------------------------------------
dnl MBIM support (enabled by default)
dnl
+
+LIBMBIM_VERSION=1.27.3
+
AC_ARG_WITH(mbim, AS_HELP_STRING([--without-mbim], [Build without MBIM support]), [], [with_mbim=yes])
AM_CONDITIONAL(WITH_MBIM, test "x$with_mbim" = "xyes")
case $with_mbim in
yes)
- PKG_CHECK_MODULES(MBIM, [mbim-glib >= 1.10], [have_mbim=yes],[have_mbim=no])
+ PKG_CHECK_MODULES(MBIM, [mbim-glib >= $LIBMBIM_VERSION], [have_mbim=yes],[have_mbim=no])
if test "x$have_mbim" = "xno"; then
- AC_MSG_ERROR([Couldn't find libmbim-glib. Install it, or otherwise configure using --without-mbim to disable MBIM support.])
+ AC_MSG_ERROR([Couldn't find libmbim-glib >= $LIBMBIM_VERSION. Install it, or otherwise configure using --without-mbim to disable MBIM support.])
else
AC_DEFINE(WITH_MBIM, 1, [Define if you want MBIM support])
AC_SUBST(MBIM_CFLAGS)
@@ -215,16 +395,19 @@ case $with_mbim in
;;
esac
-dnl
+dnl-----------------------------------------------------------------------------
dnl QMI support (enabled by default)
dnl
+
+LIBQMI_VERSION=1.31.1
+
AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes])
AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes")
case $with_qmi in
yes)
- PKG_CHECK_MODULES(QMI, [qmi-glib >= 1.11.1], [have_qmi=yes],[have_qmi=no])
+ PKG_CHECK_MODULES(QMI, [qmi-glib >= $LIBQMI_VERSION], [have_qmi=yes],[have_qmi=no])
if test "x$have_qmi" = "xno"; then
- AC_MSG_ERROR([Couldn't find libqmi-glib. Install it, or otherwise configure using --without-qmi to disable QMI support.])
+ AC_MSG_ERROR([Couldn't find libqmi-glib >= $LIBQMI_VERSION. Install it, or otherwise configure using --without-qmi to disable QMI support.])
else
AC_DEFINE(WITH_QMI, 1, [Define if you want QMI support])
AC_SUBST(QMI_CFLAGS)
@@ -236,10 +419,43 @@ case $with_qmi in
;;
esac
-NM_COMPILER_WARNINGS
+dnl-----------------------------------------------------------------------------
+dnl QRTR support (both as libqrtr-glib and libqmi-glib apis)
+dnl
+LIBQRTR_VERSION=1.0.0
+PKG_CHECK_MODULES(QRTR, [qrtr-glib >= $LIBQRTR_VERSION],[have_qrtr=yes],[have_qrtr=no])
+qmi_qrtr_supported=$($PKG_CONFIG --variable=qmi_qrtr_supported qmi-glib)
-dnl
+AC_ARG_WITH(qrtr, AS_HELP_STRING([--without-qrtr], [Build without QRTR support]), [], [with_qrtr=auto])
+if test "x$with_qrtr" = "xauto"; then
+ if test "x$qmi_qrtr_supported" = "x1" && test "x$with_qmi" = "xyes" && test "x$have_qrtr" = "xyes"; then
+ with_qrtr=yes
+ else
+ with_qrtr=no
+ fi
+fi
+case $with_qrtr in
+ yes)
+ if test "x$with_qmi" = "xno"; then
+ AC_MSG_ERROR([QRTR support requires QMI enabled. Configure using --with-qmi, or otherwise configure using --without-qrtr to disable QRTR support.])
+ elif test "x$have_qrtr" = "xno"; then
+ AC_MSG_ERROR([Couldn't find libqrtr-glib >= $LIBQRTR_VERSION. Install it, or otherwise configure using --without-qrtr to disable QRTR support.])
+ elif test "x$qmi_qrtr_supported" != "x1"; then
+ AC_MSG_ERROR([Couldn't find QRTR support in libqmi-glib. Install it, or otherwise configure using --without-qrtr to disable QRTR support.])
+ else
+ AC_DEFINE(WITH_QRTR, 1, [Define if you want QRTR support])
+ AC_SUBST(QRTR_CFLAGS)
+ AC_SUBST(QRTR_LIBS)
+ fi
+ ;;
+ *)
+ with_qrtr=no
+ ;;
+esac
+AM_CONDITIONAL(WITH_QRTR, test "x$with_qrtr" = "xyes")
+
+dnl-----------------------------------------------------------------------------
dnl Distribution version string
dnl
AC_ARG_WITH(dist-version, AS_HELP_STRING([--with-dist-version=<mm-dist-version>], [Define the custom version (like distribution package name and revision)]), ac_distver=$withval, ac_distver="")
@@ -247,16 +463,106 @@ if ! test x"$ac_distver" = x""; then
AC_DEFINE_UNQUOTED(MM_DIST_VERSION, "$ac_distver", [Define the distribution version string])
fi
-# Not building protocol libs standalone
+dnl-----------------------------------------------------------------------------
+dnl Protocol libs
+dnl
+
AM_CONDITIONAL(QCDM_STANDALONE, test "yes" = "no")
-AM_CONDITIONAL(WMC_STANDALONE, test "yes" = "no")
+
+dnl-----------------------------------------------------------------------------
+dnl Plugins
+dnl
+dnl By default all plugins are built and installed. If the user wants to build only
+dnl some specific plugins, this can be done by disabling all first and then enabling
+dnl only the ones required, e.g.:
+dnl $ ./configure \
+dnl --disable-all-plugins \
+dnl --enable-plugin-generic \
+dnl --enable-plugin-zte ...
+dnl
+dnl If the user wants all plugins except for some specific ones, those can be
+dnl explicitly disabled, e.g.:
+dnl $ ./configure \
+dnl --disable-plugin-generic \
+dnl --disable-plugin-zte ...
+dnl
+
+MM_ENABLE_ALL_PLUGINS
+
+MM_ENABLE_PLUGIN([generic])
+MM_ENABLE_PLUGIN([altair-lte])
+MM_ENABLE_PLUGIN([anydata])
+MM_ENABLE_PLUGIN([broadmobi])
+MM_ENABLE_PLUGIN([cinterion])
+MM_ENABLE_PLUGIN([dell],
+ [with_shared_sierra,
+ with_shared_novatel,
+ with_shared_xmm,
+ with_shared_telit,
+ with_shared_foxconn])
+MM_ENABLE_PLUGIN([dlink])
+MM_ENABLE_PLUGIN([fibocom],
+ [with_shared_xmm])
+MM_ENABLE_PLUGIN([foxconn],
+ [with_shared_foxconn])
+MM_ENABLE_PLUGIN([gosuncn])
+MM_ENABLE_PLUGIN([haier])
+MM_ENABLE_PLUGIN([huawei])
+MM_ENABLE_PLUGIN([iridium])
+MM_ENABLE_PLUGIN([linktop])
+MM_ENABLE_PLUGIN([longcheer])
+MM_ENABLE_PLUGIN([mbm])
+MM_ENABLE_PLUGIN([motorola])
+MM_ENABLE_PLUGIN([mtk])
+MM_ENABLE_PLUGIN([nokia])
+MM_ENABLE_PLUGIN([nokia-icera],
+ [with_shared_icera])
+MM_ENABLE_PLUGIN([novatel],
+ [with_shared_novatel])
+MM_ENABLE_PLUGIN([novatel-lte])
+MM_ENABLE_PLUGIN([option],
+ [with_shared_option])
+MM_ENABLE_PLUGIN([option-hso],
+ [with_shared_option])
+MM_ENABLE_PLUGIN([pantech])
+MM_ENABLE_PLUGIN([qcom-soc])
+MM_ENABLE_PLUGIN([quectel])
+MM_ENABLE_PLUGIN([samsung],
+ [with_shared_icera])
+MM_ENABLE_PLUGIN([sierra-legacy],
+ [with_shared_icera,
+ with_shared_sierra])
+MM_ENABLE_PLUGIN([sierra],
+ [with_shared_xmm])
+MM_ENABLE_PLUGIN([simtech])
+MM_ENABLE_PLUGIN([telit],
+ [with_shared_telit])
+MM_ENABLE_PLUGIN([thuraya])
+MM_ENABLE_PLUGIN([tplink])
+MM_ENABLE_PLUGIN([ublox])
+MM_ENABLE_PLUGIN([via])
+MM_ENABLE_PLUGIN([wavecom])
+MM_ENABLE_PLUGIN([x22x])
+MM_ENABLE_PLUGIN([zte],
+ [with_shared_icera])
+
+MM_BUILD_SHARED([icera])
+MM_BUILD_SHARED([sierra])
+MM_BUILD_SHARED([option])
+MM_BUILD_SHARED([novatel])
+MM_BUILD_SHARED([xmm])
+MM_BUILD_SHARED([telit])
+MM_BUILD_SHARED([foxconn])
+
+dnl-----------------------------------------------------------------------------
+dnl Output
+dnl
AC_CONFIG_FILES([
Makefile
data/Makefile
data/ModemManager.pc
data/mm-glib.pc
-data/org.freedesktop.ModemManager1.policy.in
data/tests/Makefile
data/tests/org.freedesktop.ModemManager1.service
include/Makefile
@@ -265,14 +571,13 @@ build-aux/Makefile
libqcdm/Makefile
libqcdm/src/Makefile
libqcdm/tests/Makefile
-libwmc/Makefile
-libwmc/src/Makefile
-libwmc/tests/Makefile
src/Makefile
src/tests/Makefile
plugins/Makefile
-uml290/Makefile
test/Makefile
+tools/Makefile
+tools/tests/Makefile
+tools/tests/services/org.freedesktop.ModemManager1.service
introspection/Makefile
introspection/tests/Makefile
po/Makefile.in
@@ -292,6 +597,9 @@ cli/Makefile
examples/Makefile
examples/modem-watcher-python/Makefile
examples/modem-watcher-javascript/Makefile
+examples/sms-python/Makefile
+examples/network-scan-python/Makefile
+examples/sms-c/Makefile
])
AC_OUTPUT
@@ -299,18 +607,88 @@ echo "
ModemManager $VERSION
==============================================
- compiler: ${CC}
- cflags: ${CFLAGS}
- Maintainer mode: ${USE_MAINTAINER_MODE}
-
- D-Bus system directory: ${DBUS_SYS_DIR}
- udev base directory: ${UDEV_BASE_DIR}
- systemd unit directory: ${with_systemdsystemunitdir}
-
- PolicyKit support: ${with_polkit}
- GObject Introspection: ${found_introspection}
- Vala Bindings: ${enable_vala}
- Documentation: ${enable_gtk_doc}
- MBIM support: ${with_mbim}
- QMI support: ${with_qmi}
-"
+ Build:
+ compiler: ${CC}
+ cflags: ${CFLAGS}
+ ldflags: ${LDFLAGS}
+ warn cflags: ${WARN_CFLAGS}
+ warn ldflags: ${WARN_LDFLAGS}
+ maintainer mode: ${USE_MAINTAINER_MODE}
+ release: ${ax_is_release}
+
+ System paths:
+ prefix: ${prefix}
+ D-Bus system directory: ${DBUS_SYS_DIR}
+ udev base directory: ${UDEV_BASE_DIR}
+ systemd unit directory: ${with_systemdsystemunitdir}
+
+ Features:
+ udev: ${with_udev}
+ policykit: ${with_polkit}
+ mbim: ${with_mbim}
+ qmi: ${with_qmi}
+ qrtr: ${with_qrtr}
+ systemd suspend/resume: ${with_systemd_suspend_resume}
+ systemd journal: ${with_systemd_journal}
+ at command via dbus: ${with_at_command_via_dbus}
+
+ Shared utils:
+ icera: ${with_shared_icera}
+ sierra: ${with_shared_sierra}
+ option: ${with_shared_option}
+ novatel: ${with_shared_novatel}
+ xmm: ${with_shared_xmm}
+ telit: ${with_shared_telit}
+ foxconn: ${with_shared_foxconn}
+
+ Plugins:
+ generic: ${enable_plugin_generic}
+ altair lte: ${enable_plugin_altair_lte}
+ anydata: ${enable_plugin_anydata}
+ broadmobi: ${enable_plugin_broadmobi}
+ cinterion: ${enable_plugin_cinterion}
+ dell: ${enable_plugin_dell}
+ dlink: ${enable_plugin_dlink}
+ fibocom: ${enable_plugin_fibocom}
+ foxconn: ${enable_plugin_foxconn}
+ gosuncn: ${enable_plugin_gosuncn}
+ haier: ${enable_plugin_haier}
+ huawei: ${enable_plugin_huawei}
+ iridium: ${enable_plugin_iridium}
+ linktop: ${enable_plugin_linktop}
+ longcheer: ${enable_plugin_longcheer}
+ mbm: ${enable_plugin_mbm}
+ motorola: ${enable_plugin_motorola}
+ mtk: ${enable_plugin_mtk}
+ nokia: ${enable_plugin_nokia}
+ nokia icera: ${enable_plugin_nokia_icera}
+ novatel: ${enable_plugin_novatel}
+ novatel lte: ${enable_plugin_novatel_lte}
+ option: ${enable_plugin_option}
+ option hso: ${enable_plugin_option_hso}
+ pantech: ${enable_plugin_pantech}
+ qcom-soc: ${enable_plugin_qcom_soc}
+ quectel: ${enable_plugin_quectel}
+ samsung: ${enable_plugin_samsung}
+ sierra legacy: ${enable_plugin_sierra_legacy}
+ sierra: ${enable_plugin_sierra}
+ simtech: ${enable_plugin_simtech}
+ telit: ${enable_plugin_telit}
+ thuraya: ${enable_plugin_thuraya}
+ tplink: ${enable_plugin_tplink}
+ ublox: ${enable_plugin_ublox}
+ via: ${enable_plugin_via}
+ wavecom: ${enable_plugin_wavecom}
+ x22x: ${enable_plugin_x22x}
+ zte: ${enable_plugin_zte}
+
+ Miscellaneous:
+ gobject introspection: ${found_introspection}
+ vala bindings: ${enable_vala}
+ documentation: ${enable_gtk_doc}
+ code coverage: ${CODE_COVERAGE_ENABLED}"
+if test "x${CODE_COVERAGE_ENABLED}" = "xyes"; then
+ echo " code coverage cflags: ${CODE_COVERAGE_CFLAGS}"
+ echo " code coverage ldflags: ${CODE_COVERAGE_LDFLAGS}"
+fi
+echo ""
diff --git a/data/Makefile.am b/data/Makefile.am
index 168895d9..447cb3d0 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -5,7 +5,8 @@ edit = @sed \
-e 's|@sbindir[@]|$(sbindir)|g' \
-e 's|@sysconfdir[@]|$(sysconfdir)|g' \
-e 's|@localstatedir[@]|$(localstatedir)|g' \
- -e 's|@libexecdir[@]|$(libexecdir)|g'
+ -e 's|@libexecdir[@]|$(libexecdir)|g' \
+ -e 's|@MM_POLKIT_SERVICE[@]|$(MM_POLKIT_SERVICE)|g'
# DBus Service file
@@ -59,15 +60,22 @@ diagrams = \
ModemManager-interface-initialization-sequence.png \
ModemManager-interface-initialization-sequence-subclassed.png
-
# Polkit
-polkit_policy_in_in_files = org.freedesktop.ModemManager1.policy.in.in
+
+# build file with translations, which we will include in dist
+org.freedesktop.ModemManager1.policy.in: org.freedesktop.ModemManager1.policy.in.in
+ $(AM_V_GEN) GETTEXTDATADIR=$(top_srcdir)/data $(MSGFMT) --xml -d $(top_srcdir)/po/ -o $@ --template $<
+
if WITH_POLKIT
+
+# build with requested user policy
+org.freedesktop.ModemManager1.policy: org.freedesktop.ModemManager1.policy.in
+ $(AM_V_GEN) sed -e s,@MM_DEFAULT_USER_POLICY\@,$(MM_DEFAULT_USER_POLICY), $< > $@.tmp && mv $@.tmp $@
+
polkit_policydir = $(datadir)/polkit-1/actions
-polkit_policy_DATA = $(polkit_policy_in_in_files:.policy.in.in=.policy)
-@INTLTOOL_POLICY_RULE@
-endif
+polkit_policy_DATA = org.freedesktop.ModemManager1.policy
+endif
# Set up pkg-config .pc files for exported libraries
pkgconfigdir = $(libdir)/pkgconfig
@@ -75,23 +83,26 @@ pkgconfig_DATA = \
ModemManager.pc \
mm-glib.pc
+MAINTAINERCLEANFILES = \
+ org.freedesktop.ModemManager1.policy.in
DISTCLEANFILES = \
+ org.freedesktop.ModemManager1.policy \
$(dbusactivation_DATA) \
- $(dbusservice_DATA) \
- $(polkit_policy_DATA)
+ $(dbusservice_DATA)
if HAVE_SYSTEMD
DISTCLEANFILES += $(systemdsystemunit_DATA)
endif
-
EXTRA_DIST = \
+ its \
+ org.freedesktop.ModemManager1.policy.in.in \
+ org.freedesktop.ModemManager1.policy.in \
$(systemdsystemunit_in_files) \
$(dbusactivation_in_files) \
$(dbusservice_file_polkit) \
$(dbusservice_file_nopolkit) \
$(icon_DATA) \
- $(polkit_policy_in_in_files) \
$(logos) \
$(diagrams)
diff --git a/data/ModemManager-icon.svg b/data/ModemManager-icon.svg
index c1c5d4db..60bd9ce0 100644
--- a/data/ModemManager-icon.svg
+++ b/data/ModemManager-icon.svg
@@ -1,6 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
@@ -10,12 +8,12 @@
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="744.09448819"
- height="1052.3622047"
+ width="83.788681"
+ height="62.479412"
id="svg2"
version="1.1"
- inkscape:version="0.48.4 r9939"
- sodipodi:docname="ModemManager-logo-wide.svg"
+ inkscape:version="1.0.2 (e86c870879, 2021-01-15, custom)"
+ sodipodi:docname="ModemManager-icon.svg"
inkscape:export-filename="/home/aleksander/Pictures/ModeManager-logo-10.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
@@ -110,20 +108,23 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.98994949"
- inkscape:cx="359.87747"
- inkscape:cy="691.32398"
+ inkscape:cx="-79.11342"
+ inkscape:cy="176.6279"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
- inkscape:window-width="1600"
- inkscape:window-height="834"
+ inkscape:window-width="2560"
+ inkscape:window-height="1363"
inkscape:window-x="0"
- inkscape:window-y="27"
+ inkscape:window-y="40"
inkscape:window-maximized="1"
- showguides="false">
+ showguides="false"
+ inkscape:document-rotation="0">
<inkscape:grid
type="xygrid"
- id="grid3780" />
+ id="grid3780"
+ originx="-210.19134"
+ originy="-353.07168" />
</sodipodi:namedview>
<metadata
id="metadata7">
@@ -140,58 +141,71 @@
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
- id="layer1">
+ id="layer1"
+ transform="translate(-210.19134,-353.07168)">
<path
sodipodi:type="arc"
- style="opacity:0.5;fill:none;stroke:#ff0000;stroke-width:6;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="opacity:0.5;fill:none;stroke:#ff0000;stroke-width:6;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5800-8"
sodipodi:cx="-382.34274"
sodipodi:cy="326.56757"
sodipodi:rx="42.93148"
sodipodi:ry="40.406101"
- d="m -396.33913,288.36909 a 42.93148,40.406101 0 0 1 56.92787,38.19848"
+ d="m -396.33913,288.36909 a 42.93148,40.406101 0 0 1 38.91853,5.29767 42.93148,40.406101 0 0 1 18.00934,32.90081"
sodipodi:start="4.3803018"
sodipodi:end="6.2831853"
transform="translate(614.67785,85.610455)"
- sodipodi:open="true" />
+ sodipodi:open="true"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ sodipodi:arc-type="arc" />
<path
sodipodi:type="arc"
- style="fill:none;stroke:#ff0000;stroke-width:28.72183228;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="fill:none;stroke:#ff0000;stroke-width:28.7218;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5800-1-5"
sodipodi:cx="-382.34274"
sodipodi:cy="326.56757"
sodipodi:rx="42.93148"
sodipodi:ry="40.406101"
- d="m -394.32583,287.76737 a 42.93148,40.406101 0 0 1 54.91457,38.8002"
+ d="m -394.32583,287.76737 a 42.93148,40.406101 0 0 1 37.75769,6.48641 42.93148,40.406101 0 0 1 17.15688,32.31379"
sodipodi:start="4.4295101"
sodipodi:end="6.2831853"
transform="matrix(0.24371704,0,0,0.24371704,324.88787,332.46101)"
- sodipodi:open="true" />
+ sodipodi:open="true"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ sodipodi:arc-type="arc" />
<path
sodipodi:type="arc"
- style="opacity:0.75;fill:none;stroke:#ff0000;stroke-width:11.44055462;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="opacity:0.75;fill:none;stroke:#ff0000;stroke-width:11.4406;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5800-9-8"
sodipodi:cx="-382.34274"
sodipodi:cy="326.56757"
sodipodi:rx="42.93148"
sodipodi:ry="40.406101"
- d="m -396.17542,288.31631 a 42.93148,40.406101 0 0 1 56.76416,38.25126"
+ d="m -396.17542,288.31631 a 42.93148,40.406101 0 0 1 38.82522,5.3978 42.93148,40.406101 0 0 1 17.93894,32.85346"
sodipodi:start="4.3843327"
sodipodi:end="6.2831853"
transform="matrix(0.61185844,0,0,0.61185844,465.81116,211.88657)"
- sodipodi:open="true" />
+ sodipodi:open="true"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ sodipodi:arc-type="arc" />
<path
sodipodi:type="arc"
- style="opacity:0.25;fill:none;stroke:#ff0000;stroke-width:3.62445903;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ style="opacity:0.25;fill:none;stroke:#ff0000;stroke-width:3.62446;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path5800-95-9"
sodipodi:cx="-382.34274"
sodipodi:cy="326.56757"
sodipodi:rx="42.93148"
sodipodi:ry="40.406101"
- d="m -396.52453,288.42972 a 42.93148,40.406101 0 0 1 57.11327,38.13785"
+ d="m -396.52453,288.42972 a 42.93148,40.406101 0 0 1 39.02396,5.18351 42.93148,40.406101 0 0 1 18.08931,32.95434"
sodipodi:start="4.3757301"
sodipodi:end="6.2831853"
transform="matrix(1.3795162,0,0,1.3795162,759.70335,-39.189369)"
- sodipodi:open="true" />
+ sodipodi:open="true"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96"
+ sodipodi:arc-type="arc" />
</g>
</svg>
diff --git a/data/ModemManager.service.in b/data/ModemManager.service.in
index 9fe3a3bc..5c963065 100644
--- a/data/ModemManager.service.in
+++ b/data/ModemManager.service.in
@@ -1,6 +1,7 @@
[Unit]
Description=Modem Manager
-After=syslog.target
+After=@MM_POLKIT_SERVICE@
+Requires=@MM_POLKIT_SERVICE@
[Service]
Type=dbus
@@ -8,6 +9,13 @@ BusName=org.freedesktop.ModemManager1
ExecStart=@sbindir@/ModemManager
StandardError=null
Restart=on-abort
+CapabilityBoundingSet=CAP_SYS_ADMIN CAP_NET_ADMIN
+ProtectSystem=true
+ProtectHome=true
+PrivateTmp=true
+RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_QIPCRTR
+NoNewPrivileges=true
+User=root
[Install]
WantedBy=multi-user.target
diff --git a/data/its/polkit.its b/data/its/polkit.its
new file mode 100644
index 00000000..1c37e6be
--- /dev/null
+++ b/data/its/polkit.its
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<its:rules xmlns:its="http://www.w3.org/2005/11/its"
+ version="2.0">
+ <its:translateRule selector="//*" translate="no"/>
+ <its:translateRule selector="//action/description |
+ //action/message"
+ translate="yes"/>
+</its:rules>
diff --git a/data/its/polkit.loc b/data/its/polkit.loc
new file mode 100644
index 00000000..c7427ec6
--- /dev/null
+++ b/data/its/polkit.loc
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<locatingRules>
+ <locatingRule name="polkit policy" pattern="*.policy">
+ <documentRule localName="policyconfig" target="polkit.its"/>
+ </locatingRule>
+</locatingRules>
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 00000000..bfa60053
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+service_conf = {
+ 'sbindir': mm_prefix / mm_sbindir,
+ 'MM_POLKIT_SERVICE': (enable_polkit ? 'polkit.service' : ''),
+}
+
+# DBus Service file
+configure_file(
+ input: 'org.freedesktop.ModemManager1.conf.@0@polkit'.format(enable_polkit ? '' : 'no'),
+ output: '@BASENAME@',
+ copy: true,
+ install_dir: dbus_policy_dir,
+)
+
+# DBus Activation file
+configure_file(
+ input: 'org.freedesktop.ModemManager1.service.in',
+ output: '@BASENAME@',
+ configuration: service_conf,
+ install_dir: dbus_system_bus_services_dir,
+)
+
+# systemd unit file
+if install_systemdunitdir
+ configure_file(
+ input: 'ModemManager.service.in',
+ output: '@BASENAME@',
+ configuration: service_conf,
+ install_dir: systemd_systemdsystemunitdir,
+ )
+endif
+
+# Polkit
+if enable_polkit
+ policy = 'org.freedesktop.ModemManager1.policy'
+
+ # build file with translations, which we will include in dist
+ i18n.merge_file(
+ policy,
+ input: configure_file(
+ input: policy + '.in.in',
+ output: '@BASENAME@',
+ configuration: policy_conf,
+ ),
+ output: '@BASENAME@',
+ po_dir: po_dir,
+ install: true,
+ install_dir: polkit_gobject_policydir,
+ )
+endif
+
+if enable_gtk_doc
+ # Logos
+ logos_pngs = files(
+ 'ModemManager-logo-square.png',
+ 'ModemManager-logo-wide.png',
+ 'ModemManager-logo-wide-text.png',
+ )
+
+ # Diagrams
+ diagrams_pngs = files(
+ 'ModemManager-interface-initialization-sequence.png',
+ 'ModemManager-interface-initialization-sequence-subclassed.png',
+ 'ModemManager-states.png',
+ )
+endif
+
+# Icon
+install_data(
+ 'ModemManager.png',
+ install_dir: mm_datadir / 'icons/hicolor/22x22/apps',
+)
diff --git a/data/org.freedesktop.ModemManager1.conf.polkit b/data/org.freedesktop.ModemManager1.conf.polkit
index d1ea1669..ab0e9e5c 100644
--- a/data/org.freedesktop.ModemManager1.conf.polkit
+++ b/data/org.freedesktop.ModemManager1.conf.polkit
@@ -3,7 +3,8 @@
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
<policy context="default">
- <deny send_destination="org.freedesktop.ModemManager1"/>
+ <deny send_destination="org.freedesktop.ModemManager1"
+ send_type="method_call"/>
<!-- Methods listed here are explicitly allowed or PolicyKit protected.
The rest are restricted to root for security.
@@ -52,6 +53,10 @@
<allow send_destination="org.freedesktop.ModemManager1"
send_interface="org.freedesktop.ModemManager1.Modem"
+ send_member="SetPowerState"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem"
send_member="Reset"/>
<allow send_destination="org.freedesktop.ModemManager1"
@@ -60,16 +65,30 @@
<allow send_destination="org.freedesktop.ModemManager1"
send_interface="org.freedesktop.ModemManager1.Modem"
- send_member="SetAllowedModes"/>
+ send_member="SetCurrentCapabilities"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem"
+ send_member="SetCurrentModes"/>
<allow send_destination="org.freedesktop.ModemManager1"
send_interface="org.freedesktop.ModemManager1.Modem"
- send_member="SetBands"/>
+ send_member="SetCurrentBands"/>
<allow send_destination="org.freedesktop.ModemManager1"
send_interface="org.freedesktop.ModemManager1.Modem"
send_member="Command"/>
+ <!-- org.freedesktop.ModemManager1.Modem.Firmware.xml -->
+
+ <!-- Protected by the Device.Control policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Firmware"
+ send_member="List"/>
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Firmware"
+ send_member="Select"/>
+
<!-- org.freedesktop.ModemManager1.Modem.Simple.xml -->
<!-- Allowed for everyone -->
@@ -97,6 +116,18 @@
send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp"
send_member="Scan"/>
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp"
+ send_member="SetEpsUeModeOperation"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp"
+ send_member="SetInitialEpsBearerSettings"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp"
+ send_member="DisableFacilityLock"/>
+
<!-- org.freedesktop.ModemManager1.Modem.ModemCdma.xml -->
<!-- Protected by the Device.Control policy rule -->
@@ -108,6 +139,25 @@
send_interface="org.freedesktop.ModemManager1.Modem.ModemCdma"
send_member="ActivateManual"/>
+ <!-- org.freedesktop.ModemManager1.Modem.Oma.xml -->
+
+ <!-- Protected by the Device.Control policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Oma"
+ send_member="Setup"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Oma"
+ send_member="StartClientInitiatedSession"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Oma"
+ send_member="AcceptNetworkInitiatedSession"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Oma"
+ send_member="CancelSession"/>
+
<!-- org.freedesktop.ModemManager1.Sim.xml -->
<!-- Protected by the Device.Control policy rule -->
@@ -138,6 +188,21 @@
send_interface="org.freedesktop.ModemManager1.Bearer"
send_member="Disconnect"/>
+ <!-- org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml -->
+
+ <!-- Protected by the Device.Control policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager"
+ send_member="List"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager"
+ send_member="Set"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager"
+ send_member="Delete"/>
+
<!-- org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml -->
<!-- Protected by the USSD policy rule -->
@@ -160,6 +225,18 @@
send_interface="org.freedesktop.ModemManager1.Modem.Location"
send_member="Setup"/>
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Location"
+ send_member="SetSuplServer"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Location"
+ send_member="InjectAssistanceData"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Location"
+ send_member="SetGpsRefreshRate"/>
+
<!-- Protected by the Location policy rule -->
<allow send_destination="org.freedesktop.ModemManager1"
send_interface="org.freedesktop.ModemManager1.Modem.Location"
@@ -192,13 +269,91 @@
send_interface="org.freedesktop.ModemManager1.Sms"
send_member="Send"/>
- <!-- org.freedesktop.ModemManager1.Modem.Signal.xml -->
+ <!-- org.freedesktop.ModemManager1.Modem.Voice.xml -->
<!-- Allowed for everyone -->
<allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="ListCalls"/>
+
+ <!-- Protected by the Voice policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="CreateCall"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="DeleteCall"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="HoldAndAccept"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="HangupAndAccept"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="HangupAll"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="Transfer"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="CallWaitingSetup"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Voice"
+ send_member="CallWaitingQuery"/>
+
+ <!-- org.freedesktop.ModemManager1.Call.xml -->
+
+ <!-- Protected by the Voice policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="Start"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="Accept"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="Deflect"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="JoinMultiparty"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="LeaveMultiparty"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="Hangup"/>
+
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Call"
+ send_member="SendDtmf"/>
+
+ <!-- org.freedesktop.ModemManager1.Modem.Signal.xml -->
+
+ <!-- Protected by the Device.Control policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
send_interface="org.freedesktop.ModemManager1.Modem.Signal"
send_member="Setup"/>
+ <!-- org.freedesktop.ModemManager1.Modem.Time.xml -->
+
+ <!-- Protected by the Time policy rule -->
+ <allow send_destination="org.freedesktop.ModemManager1"
+ send_interface="org.freedesktop.ModemManager1.Modem.Time"
+ send_member="GetNetworkTime"/>
+
</policy>
<policy user="root">
diff --git a/data/org.freedesktop.ModemManager1.policy.in.in b/data/org.freedesktop.ModemManager1.policy.in.in
index 7b3a22a3..301b6e94 100644
--- a/data/org.freedesktop.ModemManager1.policy.in.in
+++ b/data/org.freedesktop.ModemManager1.policy.in.in
@@ -10,8 +10,8 @@
<icon_name>ModemManager</icon_name>
<action id="org.freedesktop.ModemManager1.Control">
- <_description>Control the Modem Manager daemon</_description>
- <_message>System policy prevents controlling the Modem Manager.</_message>
+ <description>Control the Modem Manager daemon</description>
+ <message>System policy prevents controlling the Modem Manager.</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin</allow_active>
@@ -19,8 +19,8 @@
</action>
<action id="org.freedesktop.ModemManager1.Device.Control">
- <_description>Unlock and control a mobile broadband device</_description>
- <_message>System policy prevents unlocking or controlling the mobile broadband device.</_message>
+ <description>Unlock and control a mobile broadband device</description>
+ <message>System policy prevents unlocking or controlling the mobile broadband device.</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
@@ -28,8 +28,8 @@
</action>
<action id="org.freedesktop.ModemManager1.Contacts">
- <_description>Add, modify, and delete mobile broadband contacts</_description>
- <_message>System policy prevents adding, modifying, or deleting this device's contacts.</_message>
+ <description>Add, modify, and delete mobile broadband contacts</description>
+ <message>System policy prevents adding, modifying, or deleting this device's contacts.</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
@@ -37,8 +37,26 @@
</action>
<action id="org.freedesktop.ModemManager1.Messaging">
- <_description>Send, save, modify, and delete text messages</_description>
- <_message>System policy prevents sending or maniuplating this device's text messages.</_message>
+ <description>Send, save, modify, and delete text messages</description>
+ <message>System policy prevents sending or manipulating this device's text messages.</message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.ModemManager1.Voice">
+ <description>Accept incoming voice calls or start outgoing voice calls.</description>
+ <message>System policy prevents voice calls.</message>
+ <defaults>
+ <allow_inactive>no</allow_inactive>
+ <allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.freedesktop.ModemManager1.Time">
+ <description>Query network time and timezone information</description>
+ <message>System policy prevents querying network time information.</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
@@ -46,8 +64,8 @@
</action>
<action id="org.freedesktop.ModemManager1.Location">
- <_description>Enable and view geographic location and positioning information</_description>
- <_message>System policy prevents enabling or viewing geographic location information.</_message>
+ <description>Enable and view geographic location and positioning information</description>
+ <message>System policy prevents enabling or viewing geographic location information.</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
@@ -55,17 +73,17 @@
</action>
<action id="org.freedesktop.ModemManager1.USSD">
- <_description>Query and utilize network information and services</_description>
- <_message>System policy prevents querying or utilizing network information and services.</_message>
+ <description>Query and utilize network information and services</description>
+ <message>System policy prevents querying or utilizing network information and services.</message>
<defaults>
<allow_inactive>no</allow_inactive>
- <allow_active>yes</allow_active>
+ <allow_active>@MM_DEFAULT_USER_POLICY@</allow_active>
</defaults>
</action>
<action id="org.freedesktop.ModemManager1.Firmware">
- <_description>Query and manage firmware on a mobile broadband device</_description>
- <_message>System policy prevents querying or managing this device's firmware.</_message>
+ <description>Query and manage firmware on a mobile broadband device</description>
+ <message>System policy prevents querying or managing this device's firmware.</message>
<defaults>
<allow_inactive>no</allow_inactive>
<allow_active>auth_admin</allow_active>
diff --git a/data/tests/org.freedesktop.ModemManager1.service.in b/data/tests/org.freedesktop.ModemManager1.service.in
index ee8bdead..d7c1a007 100644
--- a/data/tests/org.freedesktop.ModemManager1.service.in
+++ b/data/tests/org.freedesktop.ModemManager1.service.in
@@ -2,4 +2,4 @@
[D-BUS Service]
Name=org.freedesktop.ModemManager1
-Exec=@abs_top_builddir@/src/ModemManager --test-session --test-no-auto-scan --test-enable --test-plugin-dir="@abs_top_builddir@/plugins/.libs" --debug
+Exec=@abs_top_builddir@/src/ModemManager --test-session --no-auto-scan --test-enable --test-plugin-dir="@abs_top_builddir@/plugins/.libs" --debug
diff --git a/data/valgrind.suppressions b/data/valgrind.suppressions
new file mode 100644
index 00000000..9b340ac3
--- /dev/null
+++ b/data/valgrind.suppressions
@@ -0,0 +1,487 @@
+#
+# Originally imported from the NetworkManager project
+#
+# IMPORTANT: these suppressions strongly depend on the used library version.
+# They probably don't work out-of-the-box on anything but Fedora, where they
+# are mainly tested.
+#
+# Make sure to install debug information, otherwise the suppression trace might
+# not match. On Fedora, try 'dnf debuginfo-install glib2'.
+{
+ NSS_NoDB_Init
+ Memcheck:Leak
+ ...
+ fun:NSS_NoDB_Init
+ ...
+}
+{
+ g_type_init_with_debug_flags
+ Memcheck:Leak
+ ...
+ fun:g_type_init_with_debug_flags
+ ...
+}
+{
+ g_type_register_static
+ Memcheck:Leak
+ ...
+ fun:g_type_register_static
+ ...
+}
+{
+ g_param_spec_boxed
+ Memcheck:Leak
+ ...
+ fun:g_param_spec_boxed
+ ...
+}
+{
+ g_type_add_interface_static
+ Memcheck:Leak
+ ...
+ fun:g_type_add_interface_static
+ ...
+}
+{
+ g_signal_type_cclosure_new
+ Memcheck:Leak
+ ...
+ fun:g_malloc0
+ fun:g_closure_new_simple
+ fun:g_signal_type_cclosure_new
+ fun:g_signal_new
+ ...
+}
+{
+ dbus_g_value_types_init
+ Memcheck:Leak
+ fun:realloc
+ fun:g_realloc
+ fun:g_type_set_qdata
+ fun:_dbus_g_value_types_init
+ fun:dbus_g_bus_get
+ ...
+}
+{
+ type_iface_vtable_base_init_Wm
+ Memcheck:Leak
+ fun:malloc
+ fun:g_malloc
+ fun:g_memdup
+ fun:type_iface_vtable_base_init_Wm
+ fun:g_type_class_ref
+ ...
+}
+{
+ g_type_create_instance
+ Memcheck:Leak
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_type_create_instance
+ fun:g_object_constructor
+ ...
+}
+{
+ g_signal_new_class_handler
+ Memcheck:Leak
+ ...
+ fun:g_closure_new_simple
+ fun:g_cclosure_new
+ fun:g_signal_new_class_handler
+ ...
+}
+{
+ _dl_init_g_type_register_fundamental
+ Memcheck:Leak
+ ...
+ fun:g_type_register_fundamental
+ ...
+ fun:_dl_init
+ obj:/*/ld-*.so
+}
+{
+ _dl_init_g_malloc0
+ Memcheck:Leak
+ fun:calloc
+ fun:g_malloc0
+ ...
+ fun:_dl_init
+ obj:/*/ld-*.so
+}
+{
+ all_gobject_init_ctor
+ Memcheck:Leak
+ ...
+ fun:gobject_init_ctor
+ ...
+}
+
+
+# The following suppressions were needed on fc20.armv7hl
+{
+ _fun_malloc
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:malloc
+}
+{
+ _fun_realloc
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:realloc
+}
+{
+ _fun_calloc
+ Memcheck:Leak
+ match-leak-kinds: possible
+ fun:calloc
+}
+
+
+{
+ _glib_sigaction
+ Memcheck:Param
+ rt_sigaction(act->sa_flags)
+ fun:__libc_sigaction
+ fun:unref_unix_signal_handler_unlocked
+ fun:g_child_watch_finalize
+ fun:g_source_unref_internal
+ fun:g_main_context_dispatch
+ ...
+ fun:g_main_loop_run
+ ...
+}
+
+{
+ # FIXME: dunny why this is needed. Clean up later.
+ _dispatcher_test
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_variant_new_from_bytes
+ fun:g_variant_new_from_trusted
+ fun:parse_dhcp
+ fun:get_dispatcher_file
+ ...
+ fun:g_test_run_suite_internal
+ fun:g_test_run_suite_internal
+ fun:g_test_run_suite
+}
+
+{
+ _gdbus_1
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:get_dispatch
+ fun:g_main_context_dispatch
+ ...
+ fun:g_main_loop_run
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+
+{
+ _gdbus_2
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_main_context_push_thread_default
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+
+{
+ _gdbus_3
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ fun:_g_socket_read_with_control_messages
+ fun:_g_dbus_worker_do_read_unlocked
+ fun:_g_dbus_worker_do_read_cb
+ fun:g_simple_async_result_complete
+ fun:complete_in_idle_cb
+ ...
+ fun:g_main_context_dispatch
+ ...
+ fun:g_main_loop_run
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+}
+
+{
+ _gdbus_4
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ ...
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_main_context_push_thread_default
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+
+{
+ _gdbus_5
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_dbus_message_new_from_blob
+ ...
+}
+
+{
+ _gdbus_9
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:get_dispatch
+ fun:g_main_current_source
+ fun:g_task_return
+ fun:g_task_thread_pool_thread
+ fun:g_thread_pool_thread_proxy
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+
+{
+ _gdbus_10
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_system_thread_new
+ fun:g_thread_new_internal
+ ...
+ fun:g_thread_pool_push
+ ...
+ fun:g_task_run_in_thread
+ fun:g_async_initable_real_init_async
+ fun:g_bus_get
+ ...
+}
+
+{
+ _gdbus_11
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ ...
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:get_dispatch
+ fun:g_main_current_source
+ fun:g_task_return
+ fun:g_task_thread_pool_thread
+ fun:g_thread_pool_thread_proxy
+ fun:g_thread_proxy
+ fun:start_thread
+}
+
+{
+ _gdbus_12
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_error_new_valist
+ fun:g_error_new
+ fun:g_dbus_error_new_for_dbus_error
+ fun:g_dbus_error_set_dbus_error
+ fun:g_dbus_message_to_gerror
+ fun:decode_method_reply
+ fun:g_dbus_connection_call_sync_internal
+ fun:g_dbus_proxy_call_sync_internal
+ fun:g_dbus_proxy_call_sync
+}
+
+{
+ _gdbus_15
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_hash_table_new_full
+ fun:demarshal_map
+ fun:_dbus_gvalue_demarshal
+ fun:dbus_g_proxy_end_call_internal
+ fun:dbus_g_proxy_end_call
+ fun:get_permissions_reply
+ fun:complete_pending_call_and_unlock
+ fun:dbus_connection_dispatch
+ fun:message_queue_dispatch
+}
+
+{
+ _gdbus_16
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ fun:_g_dbus_worker_send_message
+ fun:g_dbus_connection_send_message_unlocked
+ fun:unsubscribe_id_internal
+ fun:g_dbus_connection_signal_unsubscribe
+ fun:g_dbus_proxy_finalize
+ ...
+}
+
+{
+ _gdbus_17
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:calloc
+ fun:g_malloc0
+ fun:thread_memory_from_self
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_main_context_push_thread_default
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+
+{
+ _gdbus_18
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:get_dispatch
+ fun:g_main_dispatch
+ fun:g_main_context_dispatch
+ ...
+ fun:g_main_loop_run
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+{
+ _gdbus_f21_1
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:_g_dbus_worker_send_message
+ fun:g_dbus_connection_send_message_unlocked
+ ...
+ fun:g_dbus_proxy_finalize
+ ...
+}
+{
+ _gdbus_f23_1
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_type_create_instance
+ fun:g_object_new_internal
+ fun:g_object_new*
+ fun:g_object_new
+ fun:g_task_new
+ fun:_g_socket_read_with_control_messages
+ fun:_g_dbus_worker_do_read_unlocked
+ fun:_g_dbus_worker_do_read_cb
+ fun:g_task_return_now
+ ...
+ fun:g_main_dispatch
+ fun:g_main_context_dispatch
+ fun:g_main_context_iterate.isra.*
+ fun:g_main_loop_run
+ fun:gdbus_shared_thread_func
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+{
+ _gdbus_f25_1
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_system_thread_new
+ fun:g_thread_new_internal
+ fun:g_thread_new
+ fun:g_get_worker_context
+ fun:g_task_thread_pool_init
+ fun:g_task_get_type
+ fun:ensure_required_types
+ ...
+ fun:g_bus_get_sync
+}
+{
+ _gdbus_f25_2
+ Memcheck:Leak
+ match-leak-kinds: definite
+ ...
+ fun:g_dbus_proxy_new_sync
+ fun:initable_init
+ fun:async_init_thread
+ fun:g_task_thread_pool_thread
+ fun:g_thread_pool_thread_proxy
+ fun:g_thread_proxy
+ fun:start_thread
+ fun:clone
+}
+{
+ _gdbusobjectmanager_f25_1
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:malloc
+ fun:g_malloc
+ fun:g_slice_alloc
+ fun:g_slice_alloc0
+ fun:g_system_thread_new
+ fun:g_thread_new_internal
+ fun:g_thread_pool_start_thread.part.1
+ fun:g_thread_pool_start_thread
+ fun:g_thread_pool_push
+ fun:g_task_run_in_thread
+ fun:g_async_initable_real_init_async
+ fun:g_async_initable_new_valist_async
+ fun:g_async_initable_new_async
+ fun:g_dbus_object_manager_client_new_for_bus
+}
+{
+ _btrfs_io_clone
+ Memcheck:Param
+ ioctl(generic)
+ fun:ioctl
+ fun:btrfs_reflink_with_progress
+ ...
+}
diff --git a/decode/packet.py b/decode/packet.py
index e28b62fb..97daee1e 100644
--- a/decode/packet.py
+++ b/decode/packet.py
@@ -109,7 +109,7 @@ class Packet:
def add_line(self, line):
line = line.strip()
if not len(line):
- return
+ return
self.lines.append(line)
if line[0] == '[':
diff --git a/docs/man/Makefile.am b/docs/man/Makefile.am
index 32fe6a0f..9092246a 100644
--- a/docs/man/Makefile.am
+++ b/docs/man/Makefile.am
@@ -1,3 +1,3 @@
-man_MANS = ModemManager.8 mmcli.8
+man_MANS = ModemManager.8 mmcli.1
EXTRA_DIST = $(man_MANS)
diff --git a/docs/man/ModemManager.8 b/docs/man/ModemManager.8
index d9c527c5..62a4e43f 100644
--- a/docs/man/ModemManager.8
+++ b/docs/man/ModemManager.8
@@ -19,26 +19,39 @@ actual device (Generic AT, vendor-specific AT, QCDM, QMI, MBIM...).
ModemManager is a DBus-based system daemon and is not meant to be used directly
from the command line.
-.SH HELP OPTIONS
+.SH APPLICATION OPTIONS
.TP
-.B \-V, \-\-version
-Print the ModemManager software version and exit.
+.B \-\-filter\-policy=<policy>
+Specify which ports are probed and how:
+.RS 9
.TP
-.B \-h, \-\-help
-Show application options.
+\fB'WHITELIST-ONLY'\fR
+Only devices or ports explicitly whitelisted with the 'ID_MM_DEVICE_PROCESS' udev tag are probed.
.TP
-.B \-\-help\-all
-Show application and test options.
+\fB'STRICT'\fR
+Only the TTY ports that are heuristically determined to be very likely to be modem ports are probed. Nay end up ignoring some devices.
+.RE
+.TP
+.B \-\-no\-auto\-scan
+Fully disable udev-based auto-scan looking for devices.
+.TP
+.B \-\-initial\-kernel\-events=<filename>
+Specify location of the file where the list of initial kernel events is
+available. The ModemManager daemon will process this file on startup.
.TP
-.B \-\-help\-test
-Show test options.
-
-.SH APPLICATION OPTIONS
.B \-\-debug
Runs ModemManager with "DEBUG" log level and without daemonizing. This is useful
for debugging, as it directs log output to the controlling terminal in addition to
syslog.
.TP
+.B \-V, \-\-version
+Print the ModemManager software version and exit.
+.TP
+.B \-h, \-\-help
+Show application options.
+
+.SH LOGGING OPTIONS
+.TP
.B \-\-log\-level=<level>
Sets how much information ModemManager sends to the log destination (usually
syslog's "daemon" facility). By default, only informational, warning, and error
@@ -48,10 +61,13 @@ messages are logged. Given level must be one of "ERR", "WARN", "INFO" or "DEBUG"
Specify location of the file where ModemManager will dump its log messages,
instead of syslog.
.TP
-.B \-\-timestamps
+.B \-\-log\-journal
+Output log message to the systemd journal.
+.TP
+.B \-\-log\-timestamps
Include absolute timestamps in the log output.
.TP
-.B \-\-relative-timestamps
+.B \-\-log\-relative\-timestamps
Include timestamps, relative to the start time of the daemon, in the log output.
.SH TEST OPTIONS
@@ -59,9 +75,6 @@ Include timestamps, relative to the start time of the daemon, in the log output.
.B \-\-test\-session
Run the ModemManager daemon in the Session bus instead of the System bus.
.TP
-.B \-\-test\-no\-auto\-scan
-Fully disable udev-based auto-scan looking for devices.
-.TP
.B \-\-test\-enable
Enable the Test DBus interface in the daemon.
.TP
@@ -72,4 +85,4 @@ Specify an alternate directory where the daemon should look for vendor plugins.
Aleksander Morgado <aleksander@aleksander.es>
.SH SEE ALSO
-\fBmmcli\fR(8), \fBNetworkManager\fR(8)
+\fBmmcli\fR(1), \fBNetworkManager\fR(8)
diff --git a/docs/man/meson.build b/docs/man/meson.build
new file mode 100644
index 00000000..33c4ca05
--- /dev/null
+++ b/docs/man/meson.build
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+mans = files(
+ 'mmcli.1',
+ 'ModemManager.8',
+)
+
+install_man(mans)
diff --git a/docs/man/mmcli.8 b/docs/man/mmcli.1
index 768a3172..e5728a11 100644
--- a/docs/man/mmcli.8
+++ b/docs/man/mmcli.1
@@ -1,11 +1,11 @@
-.\" mmcli(8) manual page
+.\" mmcli(1) manual page
.\"
.\" Copyright (C) 2012 Martyn Russell
.\"
.\" Comment out '.nr' or set to 0 to eliminate WIDTH fiddlin' !
.nr half_xtra 4
-.TH mmcli 8 "October 2012" GNU "User Commands"
+.TH mmcli 1 "October 2012" GNU "User Commands"
.SH NAME
mmcli \- Control and monitor the ModemManager
@@ -80,8 +80,13 @@ is related.
.SH MANAGER OPTIONS
.TP
+.B \-B, \-\-get\-daemon\-version
+Retrieve the version of the currently running ModemManager daemon.
+.TP
.B \-G, \-\-set\-logging=[ERR|WARN|INFO|DEBUG]
-Set the logging level in ModemManager daemon. For debugging information you can supply \fBDEBUG\fR. Each value above \fBDEBUG\fR provides less detail. In most cases \fBERR\fR (for displaying errors) are the important messages.
+Set the logging level in ModemManager daemon. For debugging information you can
+supply \fBDEBUG\fR. Each value above \fBDEBUG\fR provides less detail. In most
+cases \fBERR\fR (for displaying errors) are the important messages.
The default mode is \fBERR\fR.
.TP
@@ -94,6 +99,53 @@ List available modems and monitor modems added or removed.
.B \-S, \-\-scan-modems
Scan for any potential new modems. This is only useful when expecting pure
RS232 modems, as they are not notified automatically by the kernel.
+.TP
+.B \-I, \-\-inhibit\-device=[UID]
+Inhibit the specific device from being used by ModemManager. The \fBUID\fR
+that should be given is the value of the \fBDevice\fR property exposed by
+a given modem (i.e. equal to the \fBID_MM_PHYSDEV_UID\fR if one set, or
+otherwise equal to the full device sysfs path).
+
+This command will not exit right away, as that would implicitly remove the
+inhibition. The user must make sure to stop the mmcli process hitting Ctrl+C
+in order to un-inhibit the device.
+
+When a device is inhibited via this method, ModemManager will disable the modem
+(therefore stopping any ongoing connection) and will no longer use it until it
+is uninhibited.
+.TP
+.B \-\-report\-kernel\-event=['KEY1=VALUE1,KEY2=VALUE2,...']
+Manually report kernel events, instead of relying on udev (e.g. if the daemon
+is running with \fB\-\-no\-auto\-scan\fR or if the system was built without udev
+support).
+
+The supported \fBKEY\fRs are:
+.RS 9
+.TP
+\fB'action'\fR
+Action to report, one of 'add' or 'remove'. Required.
+.TP
+\fB'subsystem'\fR
+Subsystem of the specific port being reported, e.g. 'tty' (for serial ports),
+'net' (for network interfaces), or 'usbmisc' (for cdc-wdm ports)..
+.TP
+\fB'name'\fR
+Name of the port being reported, e.g. 'ttyACM0', 'wwan0' or 'cdc-wdm0'.
+.TP
+\fB'uid'\fR
+The specific UID of the device, equivalent to the \fBID_MM_PHYSDEV_UID\fR udev
+tag. All ports reported with the same 'UID' value will be considered part of the
+same device, which may be useful for e.g. modems with multiple platform TTYs.
+.RE
+
+.TP
+.B \-\-report\-kernel\-event\-auto\-scan
+When built with udev support but the daemon is running with
+\fB\-\-no\-auto\-scan\fR, this method may be used to automatically report kernel
+events based on udev.
+
+This command will not exit right away. The user must make sure to stop the mmcli
+process hitting Ctrl+C in order to stopping monitoring for new events.
.SH COMMON OPTIONS
All options below take a \fBPATH\fR or \fBINDEX\fR argument. If no action is
@@ -177,9 +229,6 @@ Send an AT \fBCOMMAND\fR to the given modem. For example,
\fBCOMMAND\fR could be 'AT+GMM' to probe for phone model information. This
operation is only available when ModemManager is run in debug mode.
.TP
-.B \-\-list\-bearers
-List packet data bearers that are available for the given modem.
-.TP
.B \-\-create\-bearer=['KEY1=VALUE1,KEY2=VALUE2,...']
Create a new packet data bearer for a given modem. The \fBKEY\fRs and
some \fBVALUE\fRs are listed below:
@@ -210,16 +259,20 @@ Protocol of the Rm interface, given as a MMModemCdmaRmProtocol value (e.g. 'asyn
Telephone number to dial. Required in POTS.
.RE
.TP
-.B \-\-delete\-bearer=PATH
-Delete bearer from a given modem. This option explicitly uses a
-\fBPATH\fR to define the bearer, you can not use an \fBINDEX\fR to be
-deleted.
+.B \-\-delete\-bearer=[PATH|INDEX]
+Delete bearer from a given modem.
.TP
.B \-\-set\-allowed\-modes=[MODE1|MODE2|...]
Set allowed modes for a given modem. For possible modes, see the
beginning of this section.
.TP
-.B \-\-set\-bands=[BAND1|BAND2|...]
+.B \-\-set\-preferred\-mode=MODE
+Set the preferred \fBMODE\fR for the given modem. The \fBMODE\fR
+\fIMUST\fR be one of the allowed modes as set with the
+\fB\-\-set\-allowed\-modes\fR option. Possible \fBMODE\fR arguments
+are detailed at the beginning of this section.
+.TP
+.B \-\-set\-current\-bands=[BAND1|BAND2|...]
Set bands to be used for a given modem. These are frequency ranges
the modem should use. There are quite a number of supported bands and
listing them all here would be quite extensive. For details, see the
@@ -228,11 +281,25 @@ MMModemBand documentation.
An example would be: 'egsm|dcs|pcs|g850' to select all the GSM
frequency bands.
.TP
-.B \-\-set\-preferred\-mode=MODE
-Set the preferred \fBMODE\fR for the given modem. The \fBMODE\fR
-\fIMUST\fR be one of the allowed modes as set with the
-\fB\-\-set\-allowed\-modes\fR option. Possible \fBMODE\fR arguments
-are detailed at the beginning of this section.
+.B \-\-set\-primary\-sim\-slot=[SLOT]
+Request to switch the primary SIM slot.
+
+The given \fBSLOT\fR must be a valid slot number in the [1,N] range, where
+N is the amount of SIM slots available in the system.
+.TP
+.B \-\-inhibit
+Inhibit the specific modem from being used by ModemManager. This method
+is completely equivalent to \fB\-\-inhibit\-device\fR, with the only
+difference being that in this case, the modem must be managed by the daemon
+at the time the inhibition is requested.
+
+This command will not exit right away, as that would implicitly remove the
+inhibition. The user must make sure to stop the mmcli process hitting Ctrl+C
+in order to un-inhibit the device.
+
+When a device is inhibited via this method, ModemManager will disable the modem
+(therefore stopping any ongoing connection) and will no longer use it until it
+is uninhibited.
.SH 3GPP OPTIONS
The 3rd Generation Partnership Project (3GPP) is a collaboration
@@ -275,6 +342,23 @@ network-originated request. This option allows for that.
.TP
.B \-\-3gpp\-ussd\-cancel
Cancel an ongoing USSD session for a given modem.
+.TP
+.B \-\-3gpp\-disable\-facility\-lock=FACILITY,CONTROL_KEY
+Disable selected facility lock using provided control key.
+.RS 9
+.TP
+\fB'FACILITY'\fR
+One of the following types of lock:
+.Bd -literal -compact
+ \fB'net-pers'\fR - network personalization
+ \fB'net-sub-pers'\fR - network subset personalization
+ \fB'provider-pers'\fR - provider personalization
+ \fB'corp-pers'\fR - corporate personalization
+.Ed
+.TP
+\fB'CONTROL_KEY'\fR
+Alphanumeric code to unlock facility.
+.RE
.SH CDMA OPTIONS
All CDMA (Code Division Multiple Access) options require the
@@ -305,9 +389,6 @@ ETSI MCC-MNC of a network to force registration.
.TP
.B \-\-simple\-disconnect
Disconnect \fIALL\fR connected bearers for a given modem.
-.TP
-.B \-\-simple\-status
-Display the status of the given modem.
.SH LOCATION OPTIONS
These options detail how to discover your location using Global
@@ -329,27 +410,48 @@ Enable location discovery using the 3GPP network.
.B \-\-location\-disable\-3gpp
Disable location discovery using the 3GPP network.
.TP
-.B \-\-location\-get\-3gpp
-Show 3GPP based location information (MCC, MNC, LAC, CI).
+.B \-\-location\-enable\-agps-msa
+Enable A-GPS (MSA) support. This command does not implicitly start the GPS
+engine, it just specifies that A-GPS should be enabled when the engine is
+started. Therefore, the user should request enabling A-GPS before the raw
+or NMEA outputs are enabled with \fB\-\-location\-enable\-gps\-raw\fR or
+\fB\-\-location\-enable\-gps\-nmea\fR.
+.TP
+.B \-\-location\-disable\-agps-msa
+Disable A-GPS (MSA) support.
+.TP
+.B \-\-location\-enable\-agps-msb
+Enable A-GPS (MSB) support. This command does not implicitly start the GPS
+engine, it just specifies that A-GPS should be enabled when the engine is
+started. Therefore, the user should request enabling A-GPS before the raw
+or NMEA outputs are enabled with \fB\-\-location\-enable\-gps\-raw\fR or
+\fB\-\-location\-enable\-gps\-nmea\fR.
+.TP
+.B \-\-location\-disable\-agps-msb
+Disable A-GPS (MSB) support.
.TP
.B \-\-location\-enable\-gps\-nmea
Enable location discovery using GPS and reported with NMEA traces.
+
+This command will start the GPS engine, if it isn't started already.
.TP
.B \-\-location\-disable\-gps\-nmea
Disable location discovery using GPS and NMEA traces.
-.TP
-.B \-\-location\-get\-gps\-nmea
-Show GPS based location with NMEA trace information.
+
+If the raw output is not enabled at the same time, the GPS engine will be
+stopped.
.TP
.B \-\-location\-enable\-gps\-raw
Enable location discovery using GPS and reported with raw (i.e.
longitude/latitude) values.
+
+This command will start the GPS engine, if it isn't started already.
.TP
.B \-\-location\-disable\-gps\-raw
Disable location discovery using GPS and raw values.
-.TP
-.B \-\-location\-get\-gps\-raw
-Show GPS based location information with raw values (e.g. latitude, longitude).
+
+If the NMEA output is not enabled at the same time, the GPS engine will be
+stopped.
.TP
.B \-\-location\-enable\-cdma-bs
Enable location discovery using the 3GPP2 network.
@@ -357,15 +459,38 @@ Enable location discovery using the 3GPP2 network.
.B \-\-location\-disable\-cdma-bs
Disable location discovery using the 3GPP2 network.
.TP
-.B \-\-location\-get\-cdma-bs
-Show 3GPP2 based location information (location of the CDMA base station).
-.TP
.B \-\-location\-enable\-gps\-unmanaged
Enable location discovery using GPS but without taking control of the NMEA tty
-port.
+port. This allows other programs, e.g. gpsd, to use the NMEA tty once the GPS
+engine has been enabled.
.TP
.B \-\-location\-disable\-gps\-unmanaged
Disable location discovery using GPS and unmanaged port.
+.TP
+.B \-\-location\-set\-gps\-refresh\-rate=SEC
+Set the location refresh rate on the DBus interface to SEC seconds. If set to
+0, the new location is published on the DBus interface as soon as ModemManager
+detects it.
+.TP
+.B \-\-location\-set\-supl\-server=[IP:PORT] or \-\-location\-set\-supl\-server=[FQDN:PORT]
+Configure the location of the A\-GPS SUPL server, either specifying the IP
+address (\fBIP:PORT\fR) or specifyng a fully qualified domain name
+(\fB[FQDN:PORT]\fR).
+.TP
+.B \-\-location\-inject\-assistance\-data=[PATH]
+Inject assistance data into the GNSS module, loaded from a local file at
+\fBPATH\fR. The assistance data should be in a format expected by the device,
+e.g. downloaded from the URLs exposed by the 'AssistanceDataServers' property.
+.TP
+.B \-\-location\-set\-enable\-signal
+Enable reporting location updates via DBus property signals. This is
+required if applications rely on listening to 'Location' property updates,
+instead of explicit queries with the policy-protected 'GetLocation' method.
+
+This DBus property signal updates are by default disabled.
+.TP
+.B \-\-location\-set\-disable\-signal
+Disable reporting location updates via DBus property signals.
.SH MESSAGING OPTIONS
All messaging options must be used with \fB\-\-modem\fR or \fB\-m\fR.
@@ -408,11 +533,11 @@ Specifies the storage where this message is kept. Storages may
be 'sm', 'me', 'mt', 'sr', 'bm', 'ta'.
.RE
.TP
-.B \-\-messaging\-create-sms-with-data=PATH
+.B \-\-messaging\-create\-sms\-with\-data=PATH
Use \fBPATH\fR to a filename as the data to create a new SMS.
.TP
-.B \-\-messaging\-delete-sms=PATH
-Delete an SMS from a given modem. \fBPATH\fR indicates the SMS path.
+.B \-\-messaging\-delete\-sms=[PATH|INDEX]
+Delete an SMS from a given modem.
.SH TIME OPTIONS
All time operations require the \fB\-\-modem\fR or \fB\-m\fR option.
@@ -422,10 +547,32 @@ All time operations require the \fB\-\-modem\fR or \fB\-m\fR option.
Display the current network time from the operator. This includes the
timezone which is usually of importance.
+.SH VOICE OPTIONS
+All voice operations require the \fB\-\-modem\fR or \fB\-m\fR option.
+
+.TP
+.B \-\-voice\-list\-calls
+List calls managed (initiated, received, ongoing) on a given modem.
+.TP
+.B \-\-voice\-create-call=['KEY1=VALUE1,...']
+Create a new outgoing call on a given modem. \fBKEY\fRs can be any of the
+following:
+.RS 9
+.TP
+\fB'number'\fR
+Number to call.
+.RE
+.TP
+.B \-\-voice\-delete\-call=[PATH|INDEX]
+Delete a call from a given modem.
+
.SH FIRMWARE OPTIONS
All firmware options require the \fB\-\-modem\fR or \fB\-m\fR option.
.TP
+.B \-\-firmware\-status
+Show firmware update specific details and properties.
+.TP
.B \-\-firmware\-list
List all the firmware images installed on a given modem.
.TP
@@ -436,6 +583,19 @@ of available firmware images can be seen using the
The \fBID\fR provided is a \fIUNIQUE\fR identifier for the firmware.
+.SH SIGNAL OPTIONS
+All signal options require the \fB\-\-modem\fR or \fB\-m\fR option.
+
+.TP
+.B \-\-signal\-setup=[Rate]
+Setup extended signal quality information retrieval at the specified rate
+(in seconds).
+
+By default this is disabled (rate set to 0).
+.TP
+.B \-\-signal\-get
+Retrieve the last extended signal quality information loaded.
+
.SH OMA OPTIONS
All OMA options require the \fB\-\-modem\fR or \fB\-m\fR option.
@@ -534,8 +694,30 @@ This option takes an SMS that has \fIDATA\fR (not \fITEXT\fR) and will
create a local file described by \fBPATH\fR and store the content of
the SMS there.
+.SH CALL OPTIONS
+.TP
+.B \-\-start
+Initiate an outgoing call.
+.TP
+.B \-\-accept
+Accept an incoming call.
+.TP
+.B \-\-hangup
+Reject an incoming call or hangup an ongoing one.
+.TP
+.B \-\-send\-dtmf=[0\-9A\-D*#]
+Send a DTMF sequence through an ongoing call.
+
.SH APPLICATION OPTIONS
.TP
+.B \-J, \-\-output\-json
+Run action with machine-friendly JSON output, to be used e.g. by
+shell scripts that rely on mmcli operations.
+.TP
+.B \-K, \-\-output\-keyvalue
+Run action with machine-friendly key-value output, to be used e.g. by
+shell scripts that rely on mmcli operations.
+.TP
.B \-v, \-\-verbose
Perform actions with more details reported and/or logged.
.TP
@@ -549,7 +731,7 @@ practical benefit to most user operations.
.B \-\-timeout=SECONDS
Use \fBSECONDS\fR for the timeout when performing operations with this
command. This option is useful when executing long running operations, like
-\-\-3gpp\-scan.
+\fB\-\-3gpp\-scan\fR.
.SH EXAMPLES
.SS Send the PIN to the SIM card
@@ -557,13 +739,13 @@ command. This option is useful when executing long running operations, like
You'll need first to know which the proper path/index is for the SIM in your
modem:
.Bd -literal -compact
- $ mmcli -m 0 | grep SIM
- SIM | path: '/org/freedesktop/ModemManager1/SIM/0'
+ $ mmcli -m 0 -K | grep "modem.generic.sim" | awk -F ": " '{ print $2 }'
+ /org/freedesktop/ModemManager1/SIM/0
.Ed
And after that, you can just use the SIM index:
.Bd -literal -compact
- $ mmcli -i 0 --pin=1234
+ $ sudo mmcli -i 0 --pin=1234
successfully sent PIN code to the SIM
.Ed
@@ -571,13 +753,13 @@ And after that, you can just use the SIM index:
You can launch the simple connection process like:
.Bd -literal -compact
- $ mmcli -m 0 --simple-connect="pin=1234,apn=internet"
+ $ sudo mmcli -m 0 --simple-connect="pin=1234,apn=internet"
successfully connected the modem
.Ed
Then, you can disconnect it like:
.Bd -literal -compact
- $ mmcli -m 0 --simple-disconnect
+ $ sudo mmcli -m 0 --simple-disconnect
successfully disconnected all bearers in the modem
.Ed
@@ -586,13 +768,12 @@ Then, you can disconnect it like:
Scanning for 3GPP networks may really take a long time, so a specific timeout
must be given:
.Bd -literal -compact
- $ mmcli -m 0 --3gpp-scan --timeout=300
-
- Found 4 networks:
- 21404 - Yoigo (umts, available)
- 21407 - Movistar (umts, current)
- 21401 - vodafone ES (umts, forbidden)
- 21403 - Orange (umts, forbidden)
+ $ sudo mmcli -m 0 --3gpp-scan --timeout=300
+ ---------------------
+ 3GPP scan | networks: 21403 - Orange SP (gprs, unknown)
+ | 21407 - Movistar (gprs, unknown)
+ | 21404 - YOIGO (gprs, unknown)
+ | 21401 - vodafone ES (gprs, unknown)
.Ed
.SS Creating a new SMS message & storing it
@@ -600,7 +781,7 @@ must be given:
Using the “sm” (SIM), you can do this using:
.Bd -literal -compact
- $ mmcli -m 0 --messaging-create-sms="text='Hello world',number='+1234567890'"
+ $ sudo mmcli -m 0 --messaging-create-sms="text='Hello world',number='+1234567890'"
Successfully created new SMS:
/org/freedesktop/ModemManager1/SMS/21 (unknown)
@@ -608,28 +789,28 @@ Using the “sm” (SIM), you can do this using:
successfully stored the SMS
$ sudo mmcli -s 21
- SMS '/org/freedesktop/ModemManager1/SMS/21'
- -----------------------------------
- Content | number: '+1234567890'
- | text: 'Hello world'
- -----------------------------------
- Properties | PDU type: 'submit'
- | state: 'stored'
- | smsc: 'unknown'
- | validity: '0'
- | class: '0'
- | storage: 'sm'
- | delivery report: 'not requested'
- | message reference: '0'
+ -------------------------------
+ General | dbus path: /org/freedesktop/ModemManager1/SMS/21
+ -------------------------------
+ Content | number: +1234567890
+ | text: Hello world
+ -------------------------------
+ Properties | PDU type: submit
+ | state: stored
+ | smsc: unknown
+ | validity: 0
+ | class: 0
+ | storage: sm
+ | delivery report: not requested
+ | message reference: 0
$ sudo mmcli -m 0 --messaging-status
- /org/freedesktop/ModemManager1/Modem/0
----------------------------
- Messaging | supported storages: 'sm, me'
- | default storage: 'me'
+ Messaging | supported storages: sm, me
+ | default storage: me
.Ed
-.SS Sending SMS messages from files
+.SS Sending binary SMS messages from files
As you can see below, the important part is the
\fB\-\-messaging\-create\-sms\-with\-data\fR and the \fBPATH\fR provided.
@@ -653,7 +834,6 @@ ModemManager setup:
.Bd -literal -compact
$> sudo mmcli -m 0 --messaging-list-sms
- Found 1 SMS messages:
/org/freedesktop/ModemManager1/SMS/0 (received)
$> sudo mmcli -s 0 --create-file-with-data=/path/to/the/output/file
@@ -668,11 +848,10 @@ proper modem index:
.Bd -literal -compact
$ mmcli -m 0 --location-status
- /org/freedesktop/ModemManager1/Modem/0
----------------------------
- Location | capabilities: '3gpp-lac-ci, gps-raw, gps-nmea'
- | enabled: 'none'
- | signals: 'no'
+ Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea
+ | enabled: none
+ | signals: no
.Ed
The output says that the modem supports 3GPP Location area code/Cell
@@ -684,16 +863,15 @@ didn’t enable the modem, which we can do issuing:
successfully enabled the modem
$ mmcli -m 0 --location-status
- /org/freedesktop/ModemManager1/Modem/0
----------------------------
- Location | capabilities: '3gpp-lac-ci, gps-raw, gps-nmea'
- | enabled: '3gpp-lac-ci'
- | signals: 'no'
+ Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea
+ | enabled: 3gpp-lac-ci
+ | signals: no
.Ed
.SS GPS location technology enabling
-We can enable the RAW and NMEA GPS location sources using:
+We can start the GPS engine by enabling the RAW or NMEA GPS location sources:
.Bd -literal -compact
$ sudo mmcli -m 0 \\
@@ -706,27 +884,25 @@ If we do check again the status, we’ll see the GPS-specific locations are enab
.Bd -literal -compact
$ mmcli -m 0 --location-status
- /org/freedesktop/ModemManager1/Modem/0
- ----------------------------
- Location | capabilities: '3gpp-lac-ci, gps-raw, gps-nmea'
- | enabled: '3gpp-lac-ci, gps-raw, gps-nmea'
- | signals: 'no'
+ --------------------------------
+ Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea
+ | enabled: 3gpp-lac-ci, gps-raw, gps-nmea
+ | signals: no
.Ed
.SS GPS location retrieval
-You can query location source specific information with
-\fB\-\-location\-get\-3gpp\fR, \fB\-\-location\-get\-gps\-nmea\fR and
-\fB\-\-location\-get\-gps\-raw\fR; but also for all at the same time:
+You can query all location information at the same time with a single command.
+If any of the specific outputs is not available, the corresponding section will
+be omitted from the output.
.Bd -literal -compact
$ sudo mmcli -m 0 --location-get
- /org/freedesktop/ModemManager1/Modem/0
-------------------------
- 3GPP location | Mobile country code: '214'
- | Mobile network code: '3'
- | Location area code: '21071'
- | Cell ID: '7033737'
+ 3GPP location | Mobile country code: 214
+ | Mobile network code: 3
+ | Location area code: 21071
+ | Cell ID: 7033737
-------------------------
GPS NMEA traces | $GPGGA,,,,,,0,,,,,,,,*66
| $GPRMC,,V,,,,,,,,,,N*53
@@ -736,26 +912,92 @@ You can query location source specific information with
| $GPGSV,4,3,16,03,,,,12,,,,30,,,,13,,,*78
| $GPGSV,4,4,16,23,,,,15,,,,27,,,,07,,,*79
| $GPVTG,,T,,M,,N,,K,N*2C
- -------------------------
- Raw GPS | Not available
- -------------------------
- CDMA BS | Not available
.Ed
-An example of RAW GPS location information:
+.SS A-GPS support
+
+If A-GPS is enabled before starting the GPS engine, and if a data connection
+is available in the modem, the configured SUPL servers may be used to obtain
+a faster initial position fix.
+
+Note that the GPS engine will not be started when just A-GPS capability is
+enabled. An explicit output (RAW or NMEA) is required to be enabled in order
+to start the GPS engine.
.Bd -literal -compact
- $ sudo mmcli -m 0 --location-get-gps-raw
- /org/freedesktop/ModemManager1/Modem/0
- -------------------------
- Raw GPS | UTC time: '155142.2'
- | Longitude: '-3.513941'
- | Latitude: '40.502603'
- | Altitude: '18.000000'
+ $ mmcli -m 0 --location-status
+ --------------------------------
+ Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea, agps-msa, agps-msb
+ | enabled: 3gpp-lac-ci
+ | signals: no
+ -----------------------------
+ GPS | refresh rate: 30 seconds
+ | a-gps supl server: supl.google.com:7276
+
+ $ sudo mmcli -m 0 --location-enable-agps-msa
+ successfully setup location gathering
+
+ $ sudo mmcli -m 0 --location-enable-gps-nmea
+ successfully setup location gathering
+
+ $ sudo mmcli -m 0 --location-enable-gps-raw
+ successfully setup location gathering
+.Ed
+
+.SS Injecting assistance data
+
+If the modem device does not have an ongoing connection (e.g. no mobile network
+coverage) but the system has other means to access the Internet (e.g. WiFi), the
+user may be able to download location assistance data and inject it in the
+module.
+
+E.g. If the device supports XTRA assistance data, the user may download it from
+one of the servers listed by ModemManager and manually inject it afterwards. The
+XTRA assistance data is usually valid for several days.
+
+.Bd -literal -compact
+ $ mmcli -m 0 --location-status
+ --------------------------------
+ Location | capabilities: 3gpp-lac-ci, gps-raw, gps-nmea, agps-msa, agps-msb
+ | enabled: 3gpp-lac-ci
+ | signals: no
+ --------------------------------
+ GPS | refresh rate: 30 seconds
+ | a-gps supl server: supl.google.com:7276
+ | supported assistance: xtra
+ | assistance servers: https://xtrapath3.izatcloud.net/xtra3grcej.bin
+ | https://xtrapath1.izatcloud.net/xtra3grcej.bin
+ | https://xtrapath2.izatcloud.net/xtra3grcej.bin
+
+ $ wget -q https://xtrapath3.izatcloud.net/xtra3grcej.bin
+
+ $ sudo mmcli -m 0 --location-inject-assistance-data=./xtra3grcej.bin
+ successfully injected assistance data
+
+ $ sudo mmcli -m 0 --location-enable-gps-nmea
+ successfully setup location gathering
+
+ $ sudo mmcli -m 0 --location-enable-gps-raw
+ successfully setup location gathering
+.Ed
+
+.SS Key-Value output
+
+Writing shell scripts that use mmcli to perform operations with the
+modem is easy when using the \fB\-\-output\-keyvalue\fR option. For
+example, you could gather all the main status information of the modem
+with a single call and then parse it to read single fields:
+
+.Bd -literal -compact
+ $ STATUS=$(mmcli -m 0 --output-keyvalue)
+ $ echo "${STATUS}" | grep "modem.generic.state " | awk -F ": " '{ print $2 }'
+ failed
+ $ echo "${STATUS}" | grep "modem.generic.state-failed-reason " | awk -F ": " '{ print $2 }'
+ sim-missing
.Ed
-.SH AUTHOR
-Martyn Russell <martyn@lanedo.com>
+.SH AUTHORS
+Written by Martyn Russell <martyn@lanedo.com> and Aleksander Morgado <aleksander@aleksander.es>
.SH SEE ALSO
\fBModemManager\fR(8), \fBNetworkManager\fR(8)
diff --git a/docs/reference/api/Makefile.am b/docs/reference/api/Makefile.am
index 68029cfb..a641eadf 100644
--- a/docs/reference/api/Makefile.am
+++ b/docs/reference/api/Makefile.am
@@ -18,7 +18,7 @@ DOC_MODULE = ModemManager
DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
# Extra options to supply to gtkdoc-scan
-SCAN_OPTIONS =
+SCAN_OPTIONS = --deprecated-guards="MM_DISABLE_DEPRECATED"
# The directory containing the source code.
DOC_SOURCE_DIR = $(top_srcdir)/include
@@ -54,12 +54,7 @@ HTML_IMAGES = \
$(LOGOS_PNG) \
$(NULL)
-# Note that PNG files are also added in content_files so that
-# the documentation is not built before the PNGs.
-content_files = \
- $(HTML_IMAGES) \
- $(NULL)
-
+content_files =
expand_content_files = \
ModemManager-overview.xml \
ModemManager-dbus-reference.xml \
@@ -76,9 +71,11 @@ expand_content_files = \
$(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml \
$(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml \
$(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml \
+ $(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml \
$(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml \
$(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml \
$(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml \
+ $(top_builddir)/libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml \
$(NULL)
extra_files = \
@@ -115,9 +112,3 @@ CLEANFILES += \
*.stamp \
-rf xml html tmpl \
$(NULL)
-
-# PNGs are removed only in dist-clean
-# Diagrams and logos are generated by dia in the current directory
-DISTCLEANFILES = \
- $(notdir $(DIAGRAMS_PNG)) \
- $(notdir $(LOGOS_PNG))
diff --git a/docs/reference/api/ModemManager-dbus-reference.xml b/docs/reference/api/ModemManager-dbus-reference.xml
index fb15f98c..6d9ef43f 100644
--- a/docs/reference/api/ModemManager-dbus-reference.xml
+++ b/docs/reference/api/ModemManager-dbus-reference.xml
@@ -130,12 +130,15 @@
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml"/>
+ <xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml"/>
+ <xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml"/>
+ <xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml"/>
<!--xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Contacts.xml"/-->
@@ -173,4 +176,14 @@
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml"/>
</chapter>
+ <chapter id="ref-dbus-object-call">
+ <title>The <literal>/org/freedesktop/ModemManager/Calls</literal> objects</title>
+ <para>
+ Modems implementing the
+ <link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Voice">Voice interface</link>
+ will export one Call object for each Call managed in the device.
+ </para>
+ <xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml"/>
+ </chapter>
+
</part>
diff --git a/docs/reference/api/ModemManager-docs.xml b/docs/reference/api/ModemManager-docs.xml
index 569e66d1..b25e8445 100644
--- a/docs/reference/api/ModemManager-docs.xml
+++ b/docs/reference/api/ModemManager-docs.xml
@@ -15,26 +15,28 @@
</subtitle>
<releaseinfo>
For ModemManager version &version;
+ The latest version of this documentation can be found on-line at
+ <ulink role="online-location" url="https://www.freedesktop.org/software/ModemManager/doc/latest/ModemManager/">https://www.freedesktop.org/software/ModemManager/doc/latest/ModemManager/</ulink>.
</releaseinfo>
<authorgroup>
<author>
- <firstname>Dan</firstname>
- <surname>Williams</surname>
- <affiliation>
- <address>
- <email>dcbw@redhat.com</email>
- </address>
- </affiliation>
+ <firstname>Dan</firstname>
+ <surname>Williams</surname>
+ <affiliation>
+ <address>
+ <email>dcbw@redhat.com</email>
+ </address>
+ </affiliation>
</author>
<author>
- <firstname>Aleksander</firstname>
- <surname>Morgado</surname>
- <affiliation>
- <address>
- <email>aleksander@aleksander.es</email>
- </address>
- </affiliation>
+ <firstname>Aleksander</firstname>
+ <surname>Morgado</surname>
+ <affiliation>
+ <address>
+ <email>aleksander@aleksander.es</email>
+ </address>
+ </affiliation>
</author>
</authorgroup>
@@ -45,6 +47,14 @@
<year>2011</year>
<year>2012</year>
<year>2013</year>
+ <year>2014</year>
+ <year>2015</year>
+ <year>2016</year>
+ <year>2017</year>
+ <year>2018</year>
+ <year>2019</year>
+ <year>2020</year>
+ <year>2021</year>
<holder>The ModemManager Authors</holder>
</copyright>
@@ -80,19 +90,72 @@
<xi:include href="xml/mm-errors.xml"/>
</part>
+ <part id="ref-udev">
+ <title>Common udev tag definitions</title>
+ <xi:include href="xml/mm-tags.xml"/>
+ </part>
+
<!-- Part 2, DBus reference -->
<xi:include href="xml/ModemManager-dbus-reference.xml"/>
<!-- Part 3, Migration reference -->
<xi:include href="xml/ModemManager-migration-reference.xml"/>
- <index>
- <title>Index</title>
- </index>
+ <part id="ref-compat">
+ <title>Compatibility with older versions</title>
+ <xi:include href="xml/mm-compat.xml"/>
+ </part>
- <!-- NOT needed in DBus API -->
- <!--chapter id="mm-hierarchy">
- <title>Object Hierarchy</title>
- <xi:include href="xml/tree_index.sgml"/>
- </chapter-->
+ <chapter id="api-index-full">
+ <title>Index</title>
+ <xi:include href="xml/api-index-full.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-deprecated" role="deprecated">
+ <title>Index of deprecated symbols</title>
+ <xi:include href="xml/api-index-deprecated.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-0" role="1.0">
+ <title>Index of new symbols in 1.0</title>
+ <xi:include href="xml/api-index-1.0.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-2" role="1.2">
+ <title>Index of new symbols in 1.2</title>
+ <xi:include href="xml/api-index-1.2.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-4" role="1.4">
+ <title>Index of new symbols in 1.4</title>
+ <xi:include href="xml/api-index-1.4.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-6" role="1.6">
+ <title>Index of new symbols in 1.6</title>
+ <xi:include href="xml/api-index-1.6.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-8" role="1.8">
+ <title>Index of new symbols in 1.8</title>
+ <xi:include href="xml/api-index-1.8.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-10" role="1.10">
+ <title>Index of new symbols in 1.10</title>
+ <xi:include href="xml/api-index-1.10.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-12" role="1.12">
+ <title>Index of new symbols in 1.12</title>
+ <xi:include href="xml/api-index-1.12.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-14" role="1.14">
+ <title>Index of new symbols in 1.14</title>
+ <xi:include href="xml/api-index-1.14.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-16" role="1.16">
+ <title>Index of new symbols in 1.16</title>
+ <xi:include href="xml/api-index-1.16.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-18" role="1.18">
+ <title>Index of new symbols in 1.18</title>
+ <xi:include href="xml/api-index-1.18.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-20" role="1.20">
+ <title>Index of new symbols in 1.20</title>
+ <xi:include href="xml/api-index-1.20.xml"></xi:include>
+ </chapter>
</book>
diff --git a/docs/reference/api/ModemManager-migration-reference.xml b/docs/reference/api/ModemManager-migration-reference.xml
index 89670b5f..5e45b89b 100644
--- a/docs/reference/api/ModemManager-migration-reference.xml
+++ b/docs/reference/api/ModemManager-migration-reference.xml
@@ -132,7 +132,7 @@
<link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Simple.Connect">
<literal>Connect()</literal>
</link>
- method will create a single bearer with the parameters specified in the call an get
+ method will create a single bearer with the parameters specified in the call and get
it connected, while the
<link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Simple.Disconnect">
<literal>Disconnect()</literal>
diff --git a/docs/reference/api/ModemManager-overview.xml b/docs/reference/api/ModemManager-overview.xml
index d55beef9..ff532fe4 100644
--- a/docs/reference/api/ModemManager-overview.xml
+++ b/docs/reference/api/ModemManager-overview.xml
@@ -51,17 +51,16 @@
</formalpara>
</chapter>
- <chapter id="ref-overview-modem-detection-and-setup">
- <title>Modem detection and setup</title>
+ <chapter id="ref-overview-modem-detection">
+ <title>Modem detection</title>
<section>
- <title>Detection mechanisms</title>
+ <title>Builds with udev support</title>
<para>
ModemManager requires <emphasis>udev</emphasis>-powered Linux kernels in order
to get notified of possible available Modems. udev will report each of the ports
- found in the device, and ModemManager will probe each of the ports marked with
- the <emphasis>ID_MM_CANDIDATE</emphasis> tag in udev. This includes both "tty"
- and "net" ports.
+ found in the device, and ModemManager will consider for probing each of the ports
+ marked with the <emphasis>ID_MM_CANDIDATE</emphasis> tag in udev.
</para>
<para>
Aditionally, users of RS232-based devices may need to request additional manual
@@ -72,283 +71,557 @@
</section>
<section>
- <title>Probing</title>
+ <title>Builds without udev support</title>
<para>
- Whenever a new device is detected by ModemManager, port probing process will
- get started, so that we can determine which kind of ports we have, and also
- which plugin we need to use for the specific device. Devices may expose one or
- more ports, and all of them will follow the same probing logic.
+ When the udev daemon isn't available in the system, the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1.ReportKernelEvent">ReportKernelEvent</link>
+ method in the
+ <link linkend="gdbus-org.freedesktop.ModemManager1">Manager interface</link>
+ may be used to notify the ModemManager daemons of device addition and removals.
</para>
<para>
- The whole probing process, including pre-probing and post-probing filters, is
- implemented in the core ModemManager daemon. Plugins will just configure their
- own special needs in the probing process, so that only the steps required by the
- given plugin are executed. For example, plugins which do not support RS232
- devices will not need AT-based vendor or product filters.
+ When udev support is disabled in the build, the <emphasis>ID_MM_CANDIDATE</emphasis>
+ tag and manual scan requests are still applicable. ModemManager has a built-in parser
+ of udev rule files that is enabled when udev itself isn't available.
</para>
+ </section>
+ </chapter>
- <section>
- <title>Pre-probing filters</title>
- <para>
- Pre-probing filters are those which control whether the probing, as
- requested by the specific plugin, takes place.
- </para>
+ <chapter id="ref-overview-modem-filter">
+ <title>Modem filter</title>
+
+ <para>
+ ModemManager will not probe all TTYs, NET and cdc-wdm ports found in the system,
+ as this may end up interfering e.g. with TTYs that have nothing to do with modem
+ devices.
+ </para>
+ <para>
+ The daemon comes with several predefined <emphasis>filter policies</emphasis>, each
+ of them composed of one or more <emphasis>filter rules</emphasis>.
+ </para>
+
+ <section>
+ <title>Filter rules</title>
+ <para>
+ The device filter in ModemManager defines the following independent filter rules. The
+ predefined filter policies are based on one or more of these predefined filter rules.
<itemizedlist>
<listitem>
- <para><emphasis>Allowed vendor IDs</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_EXPLICIT_WHITELIST</emphasis></para>
<para>
- Plugins can provide a list of udev-reported vendor IDs to be used as
- pre-probing filters. If the vendor ID reported by the device via udev
- is found in the list provided by the plugin, port probing will be
- launched as requested by the given plugin.
- </para>
- <para>
- This filter is specified by the <type>MM_PLUGIN_ALLOWED_VENDOR_IDS</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This filter allows users to manually tag devices and/or device ports with the
+ <emphasis>ID_MM_DEVICE_PROCESS</emphasis> udev tag. If the filter finds this tag,
+ the device and/or device ports will be automatically accepted and port probing
+ will be allowed.
</para>
+ <programlisting>
+$ sudo vim /lib/udev/rules.d/78-mm-whitelist-internal-modem.rules
+ ACTION!="add|change|move", GOTO="mm_whitelist_internal_modem_end"
+ ATTRS{idVendor}=="1199", ATTRS{idProduct}=="a001", ENV{ID_MM_DEVICE_PROCESS}="1"
+ LABEL="mm_whitelist_internal_modem_end"
+// Apply new rules without reboot
+$ sudo udevadm control --reload
+$ sudo udevadm trigger
+ </programlisting>
</listitem>
<listitem>
- <para><emphasis>Product IDs</emphasis></para>
- <para>
- Plugins can provide a list of udev-reported pairs of vendor and product
- IDs to be used as pre-probing filters.
- </para>
- <para>
- If the vendor ID and product ID pair reported by the device via udev is
- found in the list of 'allowed' pairs provided by the plugin, port probing
- will be launched as requested by the given plugin. This additional filter
- should be used when the plugin is expected to work only with a given
- specific product of a given vendor.
- </para>
- <para>
- If the vendor ID and product ID pair reported by the device via udev is
- found in the list of 'forbidden' pairs provided by the plugin, port probing
- will not be launched by this plugin. This additional filter
- should be used when the plugin supports all devices of a given vendor
- except for some specific ones.
- </para>
+ <para><emphasis>MM_FILTER_RULE_EXPLICIT_BLACKLIST</emphasis></para>
<para>
- These filters are specified by the <type>MM_PLUGIN_ALLOWED_PRODUCT_IDS</type>
- and <type>MM_PLUGIN_FORBIDDEN_PRODUCT_IDS</type> properties in the
- <structname>MMPlugin</structname> object provided by the plugin.
+ This filter allows users to manually tag devices and/or device ports with the
+ <emphasis>ID_MM_DEVICE_IGNORE</emphasis> udev tag. If the filter finds this tag,
+ the device and/or device ports will be automatically ignored and port probing
+ will be never run on them.
</para>
+ <programlisting>
+$ sudo vim /lib/udev/rules.d/78-mm-blacklist-internal-modem.rules
+ ACTION!="add|change|move", GOTO="mm_blacklist_internal_modem_end"
+ ATTRS{idVendor}=="1199", ATTRS{idProduct}=="a001", ENV{ID_MM_DEVICE_IGNORE}="1"
+ LABEL="mm_blacklist_internal_modem_end"
+// Apply new rules without reboot
+$ sudo udevadm control --reload
+$ sudo udevadm trigger
+ </programlisting>
</listitem>
<listitem>
- <para><emphasis>Subsystems</emphasis></para>
- <para>
- Plugins can specify which subsystems they expect, so that we filter out
- any port detected with a subsystem not listed by the plugin.
- </para>
+ <para><emphasis>MM_FILTER_RULE_PLUGIN_WHITELIST</emphasis></para>
<para>
- This filter is specified by the <type>MM_PLUGIN_ALLOWED_SUBSYSTEMS</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This filter will automatically whitelist devices that are explicitly referenced
+ by plugins, either with plugin-specific whitelist tags, with exact
+ <emphasis>vid:pid</emphasis> matches, or just with <emphasis>vid</emphasis> matches.
</para>
</listitem>
<listitem>
- <para><emphasis>Drivers</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_QRTR</emphasis></para>
<para>
- Plugins can specify which drivers they expect, so that we filter out
- any port detected being managed by a driver not listed by the plugin.
+ This filter will automatically flag as allowed all QRTR nodes that have been
+ notified as being modem management capable.
</para>
<para>
- Plugins can also specify which drivers they do not expect, so that we
- filter out any port detected being managed by a driver listed by the plugin.
- </para>
- <para>
- These filters are specified by the <type>MM_PLUGIN_ALLOWED_DRIVERS</type>
- and <type>MM_PLUGIN_FORBIDDEN_DRIVERS</type> properties in the
- <structname>MMPlugin</structname> object provided by the plugin.
+ This filter rule is available since 1.18.0.
</para>
</listitem>
<listitem>
- <para><emphasis>udev tags</emphasis></para>
- <para>
- Plugins can provide a list of udev tags, so that we filter out
- any port detected which doesn't expose any of the given tags.
- </para>
+ <para><emphasis>MM_FILTER_RULE_VIRTUAL</emphasis></para>
<para>
- This filter is specified by the <type>MM_PLUGIN_ALLOWED_UDEV_TAGS</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This filter will automatically flag as forbidden all ports exposed by virtual
+ devices, like the 'lo' network interface or the tty0, tty1... virtual terminals.
+ There is no reason to disable this filter, except for testing purposes.
</para>
</listitem>
- </itemizedlist>
- </section>
-
- <section>
- <title>Probing sequence</title>
- <para>
- Whenever all pre-probing filters of a given plugin pass, ModemManager will run
- the probing sequence as requested by the specific plugin. The main purpose of the
- probing sequence step is to determine the type of port being probed, and also
- prepare the information required in any expected post-probing filter.
- </para>
- <itemizedlist>
<listitem>
- <para><emphasis>Custom initialization</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_NET</emphasis></para>
<para>
- This property allows plugins to provide an asynchronous method which will get
- executed as soon as the AT port gets opened. This method may be used for any
- purpose, like running an early command in the ports as soon as possible, or
- querying the modem for info about the port layout.
+ This filter will automatically flag as allowed all network ports exposed by
+ devices. Unless there is a will to explicitly forbid network ports, this filter
+ should always be enabled.
</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>MM_FILTER_RULE_USBMISC</emphasis></para>
<para>
- This configuration is specified by the <type>MM_PLUGIN_CUSTOM_INIT</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This filter will automatically flag as allowed all cdc-wdm ports exposed in the
+ usbmisc subsystem. Unless there is a will to explicitly forbid the cdc-wdm ports exposed
+ by qmi_wwan, cdc_mbim or huawei-cdc-ncm kernel drivers, this filter should always
+ be enabled.
</para>
</listitem>
<listitem>
- <para><emphasis>AT allowed</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_RPMSG</emphasis></para>
<para>
- This boolean property allows plugins to specify that they expect and support
- AT serial ports.
+ This filter will automatically flag as allowed all rpmsg ports exposed in the
+ rpmsg subsystem. Unless there is a will to explicitly forbid the rpmsg ports,
+ this filter should always be enabled.
</para>
<para>
- This configuration is specified by the <type>MM_PLUGIN_ALLOWED_AT</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This filter rule is available since 1.16.0.
</para>
</listitem>
<listitem>
- <para><emphasis>Single AT expected</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_WWAN</emphasis></para>
<para>
- This boolean property allows plugins to specify that they only expect and support
- one AT serial port. Whenever the first AT port is grabbed, any remaining AT probing
- in ports of the same device will get cancelled.
+ This filter will automatically flag as allowed all wwan control ports exposed
+ in the wwan subsystem. Unless there is a will to explicitly forbid the wwan control
+ ports, this filter should always be enabled.
</para>
<para>
- This configuration is specified by the <type>MM_PLUGIN_ALLOWED_SINGLE_AT</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This filter rule is available since 1.18.0.
</para>
</listitem>
<listitem>
- <para><emphasis>Custom AT probing</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_TTY</emphasis></para>
<para>
- This property allows plugins to specify custom commands to check whether a port
- is AT or not. By default, the 'AT' command will be used if no custom one specified.
+ If the MM_FILTER_RULE_TTY filter is disabled, no TTY port will be allowed. If this
+ filter is enabled, TTY ports will only be allowed if the TTY-specific filters (defined
+ next) allow it.
</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>MM_FILTER_RULE_TTY_PLATFORM_DRIVER</emphasis></para>
<para>
- This configuration is specified by the <type>MM_PLUGIN_CUSTOM_AT_PROBE</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ If this filter is enabled, all platform TTY ports will be forbidden automatically.
</para>
</listitem>
<listitem>
- <para><emphasis>QCDM allowed</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_TTY_DRIVER</emphasis></para>
<para>
- This boolean property allows plugins to specify that they expect and support
- QCDM serial ports.
+ If this filter is enabled, all TTY ports managed by modem-specific kernel drivers will be
+ allowed automatically.
</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>FILTER_RULE_TTY_ACM_INTERFACE</emphasis></para>
<para>
- This configuration is specified by the <type>MM_PLUGIN_ALLOWED_QCDM</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ If this filter is enabled, all TTY ports managed by the cdc-acm kernel driver with
+ class=2/subclass=2/protocol=1 (AT command capable ttyACM port) will be allowed
+ automatically.
</para>
</listitem>
<listitem>
- <para><emphasis>Check Icera support</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_TTY_WITH_NET</emphasis></para>
<para>
- This boolean property allows plugins to specify that they want to have the Icera
- support checks included in the probing sequence. They can afterwards get the result
- of the support check to decide whether they want to create a Icera-based modem
- object or not.
+ If this filter is enabled, all TTY ports for devices that also expose a network
+ interface port will be allowed automatically.
</para>
+ </listitem>
+ <listitem>
+ <para><emphasis>MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN</emphasis></para>
<para>
- This configuration is specified by the <type>MM_PLUGIN_ICERA_PROBE</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ This rule is the implicit one defining what happens when a TTY port isn't explicitly
+ accepted by any of the TTY-specific filters; i.e. the TTY port will be forbidden.
</para>
</listitem>
</itemizedlist>
- </section>
+ </para>
- <section>
- <title>Post-probing filters</title>
- <para>
- Post-probing filters are required to identify whether a plugin can handle a given
- modem, in special cases where the information retrieved from udev is either not
- enough or wrong. This covers, for example, RS232 modems connected through a RS232
- to USB converter, where udev-reported vendor ID is that of the converter, not the
- one of the modem.
- </para>
+ <para>
+ The following filter rules have been deprecated and are no longer used.
<itemizedlist>
<listitem>
- <para><emphasis>Allowed vendor strings</emphasis></para>
- <para>
- Plugins can provide a list of vendor strings to be used as post-probing
- filters. If the vendor string reported by the device via AT commands
- is found in the list provided by the plugin, the plugin will report that
- it can handle this modem.
- </para>
+ <para><emphasis>MM_FILTER_RULE_TTY_BLACKLIST</emphasis></para>
<para>
- This filter is specified by the <type>MM_PLUGIN_ALLOWED_VENDOR_STRINGS</type>
- property in the <structname>MMPlugin</structname> object provided
- by the plugin.
+ Deprecated in 1.18.0.
</para>
</listitem>
<listitem>
- <para><emphasis>Product strings</emphasis></para>
+ <para><emphasis>MM_FILTER_RULE_TTY_MANUAL_SCAN_ONLY</emphasis></para>
<para>
- Plugins can provide a list of pairs of vendor and product
- strings to be used as post-probing filters.
+ Deprecated in 1.18.0.
</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </section>
+
+ <section>
+ <title>Filter policies</title>
+
+ <para>
+ The predefined filter policies are:
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>Whitelist only</emphasis></para>
<para>
- If the vendor and product string pair reported by the device via AT
- commands is found in the 'allowed' list provided by the plugin, the
- plugin will report that it can handle this modem. This additional filter
- should be used when the plugin is expected to work only with a given
- specific product of a given vendor.
+ This is a policy where only the MM_FILTER_RULE_EXPLICIT_WHITELIST rule is enabled.
</para>
+ <programlisting># /usr/sbin/ModemManager --filter-policy=WHITELIST-ONLY</programlisting>
+ </listitem>
+ <listitem>
+ <para><emphasis>Strict</emphasis></para>
<para>
- If the vendor and product string pair reported by the device via AT
- commands is found in the 'forbidden list provided by the plugin, the
- plugin will report that it cannot handle this modem. This additional filter
- should be used when the plugin supports all devices of a given vendor, except for some specific ones.
+ This is a policy where the following rules are enabled:
+ <itemizedlist>
+ <listitem>MM_FILTER_RULE_EXPLICIT_WHITELIST</listitem>
+ <listitem>MM_FILTER_RULE_EXPLICIT_BLACKLIST</listitem>
+ <listitem>MM_FILTER_RULE_PLUGIN_WHITELIST</listitem>
+ <listitem>MM_FILTER_RULE_QRTR</listitem>
+ <listitem>MM_FILTER_RULE_VIRTUAL</listitem>
+ <listitem>MM_FILTER_RULE_NET</listitem>
+ <listitem>MM_FILTER_RULE_USBMISC</listitem>
+ <listitem>MM_FILTER_RULE_RPMSG</listitem>
+ <listitem>MM_FILTER_RULE_TTY</listitem>
+ <listitem>MM_FILTER_RULE_TTY_PLATFORM_DRIVER</listitem>
+ <listitem>MM_FILTER_RULE_TTY_DRIVER</listitem>
+ <listitem>MM_FILTER_RULE_TTY_ACM_INTERFACE</listitem>
+ <listitem>MM_FILTER_RULE_TTY_WITH_NET</listitem>
+ <listitem>MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN</listitem>
+ <listitem>MM_FILTER_RULE_WWAN</listitem>
+ </itemizedlist>
</para>
<para>
- These filters are specified by the <type>MM_PLUGIN_ALLOWED_PRODUCT_STRINGS</type>
- and <type>MM_PLUGIN_FORBIDDEN_PRODUCT_STRINGS</type> properties in the
- <structname>MMPlugin</structname> object provided by the plugin.
+ This policy is the default one when a different one is not explicitly
+ selected. In this policy, all TTYs are forbidden except for the ones
+ explicitly allowed by one of the TTY-specific rules.
</para>
+ <programlisting># /usr/sbin/ModemManager --filter-policy=STRICT</programlisting>
</listitem>
<listitem>
- <para><emphasis>Icera support</emphasis></para>
+ <para><emphasis>Custom</emphasis></para>
<para>
- Plugins can specify that they only support Icera-based modems, or that they
- do not support any Icera-based modem. When either of this configurations is
- enabled, the Icera support checks will be included in the
- probing sequence, and the result of the check will help to determine whether
- the plugin supports the modem or not.
+ Any of the previously defined predefined policies may be modified rule per rule
+ by explicitly enabling or disabling rules via environment variables.
</para>
<para>
- This filter is specified by the <type>MM_PLUGIN_ALLOWED_ICERA</type> and
- <type>MM_PLUGIN_FORBIDDEN_ICERA</type> properties in the
- <structname>MMPlugin</structname> object provided by the plugin.
+ E.g. this would launch ModemManager with the Strict filter policy but with all
+ net and cdc-wdm ports forbidden completely:
+ <programlisting>
+# MM_FILTER_RULE_NET=0 \
+ MM_FILTER_RULE_USBMISC=0 \
+ /usr/sbin/ModemManager --filter-policy=STRICT</programlisting>
+ </para>
+ <para>
+ E.g. this would launch ModemManager with the Whitelist-only filter policy but also
+ explicitly allowing all net and cdc-wdm ports. Note that in this case, all virtual
+ net ports (e.g. 'lo') are also being allowed.
+ <programlisting>
+# MM_FILTER_RULE_NET=1 \
+ MM_FILTER_RULE_USBMISC=1 \
+ /usr/sbin/ModemManager --filter-policy=WHITELIST-ONLY</programlisting>
</para>
</listitem>
</itemizedlist>
+ </para>
+
+ </section>
+ </chapter>
+
+ <chapter id="ref-overview-modem-port-probing">
+ <title>Port probing</title>
+ <para>
+ Whenever a new device is detected by ModemManager, port probing process will
+ get started, so that we can determine which kind of ports we have, and also
+ which plugin we need to use for the specific device. Devices may expose one or
+ more ports, and all of them will follow the same probing logic.
+ </para>
+ <para>
+ The whole probing process, including pre-probing and post-probing filters, is
+ implemented in the core ModemManager daemon. Plugins will just configure their
+ own special needs in the probing process, so that only the steps required by the
+ given plugin are executed. For example, plugins which do not support RS232
+ devices will not need AT-based vendor or product filters.
+ </para>
+
+ <section>
+ <title>Pre-probing filters</title>
+ <para>
+ Pre-probing filters are those which control whether the probing, as
+ requested by the specific plugin, takes place.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>Allowed vendor IDs</emphasis></para>
+ <para>
+ Plugins can provide a list of udev-reported vendor IDs to be used as
+ pre-probing filters. If the vendor ID reported by the device via udev
+ is found in the list provided by the plugin, port probing will be
+ launched as requested by the given plugin.
+ </para>
+ <para>
+ This filter is specified by the <type>MM_PLUGIN_ALLOWED_VENDOR_IDS</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Product IDs</emphasis></para>
+ <para>
+ Plugins can provide a list of udev-reported pairs of vendor and product
+ IDs to be used as pre-probing filters.
+ </para>
+ <para>
+ If the vendor ID and product ID pair reported by the device via udev is
+ found in the list of 'allowed' pairs provided by the plugin, port probing
+ will be launched as requested by the given plugin. This additional filter
+ should be used when the plugin is expected to work only with a given
+ specific product of a given vendor.
+ </para>
+ <para>
+ If the vendor ID and product ID pair reported by the device via udev is
+ found in the list of 'forbidden' pairs provided by the plugin, port probing
+ will not be launched by this plugin. This additional filter
+ should be used when the plugin supports all devices of a given vendor
+ except for some specific ones.
+ </para>
+ <para>
+ These filters are specified by the <type>MM_PLUGIN_ALLOWED_PRODUCT_IDS</type>
+ and <type>MM_PLUGIN_FORBIDDEN_PRODUCT_IDS</type> properties in the
+ <structname>MMPlugin</structname> object provided by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Subsystems</emphasis></para>
+ <para>
+ Plugins can specify which subsystems they expect, so that we filter out
+ any port detected with a subsystem not listed by the plugin.
+ </para>
+ <para>
+ This filter is specified by the <type>MM_PLUGIN_ALLOWED_SUBSYSTEMS</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Drivers</emphasis></para>
+ <para>
+ Plugins can specify which drivers they expect, so that we filter out
+ any port detected being managed by a driver not listed by the plugin.
+ </para>
+ <para>
+ Plugins can also specify which drivers they do not expect, so that we
+ filter out any port detected being managed by a driver listed by the plugin.
+ </para>
+ <para>
+ These filters are specified by the <type>MM_PLUGIN_ALLOWED_DRIVERS</type>
+ and <type>MM_PLUGIN_FORBIDDEN_DRIVERS</type> properties in the
+ <structname>MMPlugin</structname> object provided by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>udev tags</emphasis></para>
+ <para>
+ Plugins can provide a list of udev tags, so that we filter out
+ any port detected which doesn't expose any of the given tags.
+ </para>
+ <para>
+ This filter is specified by the <type>MM_PLUGIN_ALLOWED_UDEV_TAGS</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </section>
- <note>
+ <section>
+ <title>Probing sequence</title>
+ <para>
+ Whenever all pre-probing filters of a given plugin pass, ModemManager will run
+ the probing sequence as requested by the specific plugin. The main purpose of the
+ probing sequence step is to determine the type of port being probed, and also
+ prepare the information required in any expected post-probing filter.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>Custom initialization</emphasis></para>
+ <para>
+ This property allows plugins to provide an asynchronous method which will get
+ executed as soon as the AT port gets opened. This method may be used for any
+ purpose, like running an early command in the ports as soon as possible, or
+ querying the modem for info about the port layout.
+ </para>
+ <para>
+ This configuration is specified by the <type>MM_PLUGIN_CUSTOM_INIT</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>AT allowed</emphasis></para>
+ <para>
+ This boolean property allows plugins to specify that they expect and support
+ AT serial ports.
+ </para>
<para>
- Plugins which require post-probing filters will always be sorted last, and
- therefore they will be the last ones being checked for pre-probing filters. This
- is due to the fact that we need to assume that these plugins aren't able to
- determine port support just with pre-probing filters, as we want to avoid
- unnecessary probing sequences launched. Also note that the Generic plugin is
- anyway always the last one in the list.
+ This configuration is specified by the <type>MM_PLUGIN_ALLOWED_AT</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
</para>
- </note>
- </section>
+ </listitem>
+ <listitem>
+ <para><emphasis>Single AT expected</emphasis></para>
+ <para>
+ This boolean property allows plugins to specify that they only expect and support
+ one AT serial port. Whenever the first AT port is grabbed, any remaining AT probing
+ in ports of the same device will get cancelled.
+ </para>
+ <para>
+ This configuration is specified by the <type>MM_PLUGIN_ALLOWED_SINGLE_AT</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Custom AT probing</emphasis></para>
+ <para>
+ This property allows plugins to specify custom commands to check whether a port
+ is AT or not. By default, the 'AT' command will be used if no custom one specified.
+ </para>
+ <para>
+ This configuration is specified by the <type>MM_PLUGIN_CUSTOM_AT_PROBE</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>QCDM allowed</emphasis></para>
+ <para>
+ This boolean property allows plugins to specify that they expect and support
+ QCDM serial ports.
+ </para>
+ <para>
+ This configuration is specified by the <type>MM_PLUGIN_ALLOWED_QCDM</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Check Icera support</emphasis></para>
+ <para>
+ This boolean property allows plugins to specify that they want to have the Icera
+ support checks included in the probing sequence. They can afterwards get the result
+ of the support check to decide whether they want to create a Icera-based modem
+ object or not.
+ </para>
+ <para>
+ This configuration is specified by the <type>MM_PLUGIN_ICERA_PROBE</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ </itemizedlist>
+ </section>
- <section>
- <title>Probing setup examples</title>
- <example>
- <title>Probing setup for a plugin requiring udev-based vendor/product checks</title>
- <programlisting>
+ <section>
+ <title>Post-probing filters</title>
+ <para>
+ Post-probing filters are required to identify whether a plugin can handle a given
+ modem, in special cases where the information retrieved from udev is either not
+ enough or wrong. This covers, for example, RS232 modems connected through a RS232
+ to USB converter, where udev-reported vendor ID is that of the converter, not the
+ one of the modem.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para><emphasis>Allowed vendor strings</emphasis></para>
+ <para>
+ Plugins can provide a list of vendor strings to be used as post-probing
+ filters. If the vendor string reported by the device via AT commands
+ is found in the list provided by the plugin, the plugin will report that
+ it can handle this modem.
+ </para>
+ <para>
+ This filter is specified by the <type>MM_PLUGIN_ALLOWED_VENDOR_STRINGS</type>
+ property in the <structname>MMPlugin</structname> object provided
+ by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Product strings</emphasis></para>
+ <para>
+ Plugins can provide a list of pairs of vendor and product
+ strings to be used as post-probing filters.
+ </para>
+ <para>
+ If the vendor and product string pair reported by the device via AT
+ commands is found in the 'allowed' list provided by the plugin, the
+ plugin will report that it can handle this modem. This additional filter
+ should be used when the plugin is expected to work only with a given
+ specific product of a given vendor.
+ </para>
+ <para>
+ If the vendor and product string pair reported by the device via AT
+ commands is found in the 'forbidden list provided by the plugin, the
+ plugin will report that it cannot handle this modem. This additional filter
+ should be used when the plugin supports all devices of a given vendor, except for some specific ones.
+ </para>
+ <para>
+ These filters are specified by the <type>MM_PLUGIN_ALLOWED_PRODUCT_STRINGS</type>
+ and <type>MM_PLUGIN_FORBIDDEN_PRODUCT_STRINGS</type> properties in the
+ <structname>MMPlugin</structname> object provided by the plugin.
+ </para>
+ </listitem>
+ <listitem>
+ <para><emphasis>Icera support</emphasis></para>
+ <para>
+ Plugins can specify that they only support Icera-based modems, or that they
+ do not support any Icera-based modem. When either of this configurations is
+ enabled, the Icera support checks will be included in the
+ probing sequence, and the result of the check will help to determine whether
+ the plugin supports the modem or not.
+ </para>
+ <para>
+ This filter is specified by the <type>MM_PLUGIN_ALLOWED_ICERA</type> and
+ <type>MM_PLUGIN_FORBIDDEN_ICERA</type> properties in the
+ <structname>MMPlugin</structname> object provided by the plugin.
+ </para>
+ </listitem>
+ </itemizedlist>
+
+ <note>
+ <para>
+ Plugins which require post-probing filters will always be sorted last, and
+ therefore they will be the last ones being checked for pre-probing filters. This
+ is due to the fact that we need to assume that these plugins aren't able to
+ determine port support just with pre-probing filters, as we want to avoid
+ unnecessary probing sequences launched. Also note that the Generic plugin is
+ anyway always the last one in the list.
+ </para>
+ </note>
+ </section>
+
+ <section>
+ <title>Probing setup examples</title>
+ <example>
+ <title>Probing setup for a plugin requiring udev-based vendor/product checks</title>
+ <programlisting>
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
@@ -374,12 +647,12 @@ mm_plugin_create (void)
/* No post-probing filters */
NULL));
}
- </programlisting>
- </example>
+ </programlisting>
+ </example>
- <example>
- <title>Probing setup for a plugin requiring AT-probed vendor/product checks</title>
- <programlisting>
+ <example>
+ <title>Probing setup for a plugin requiring AT-probed vendor/product checks</title>
+ <programlisting>
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
@@ -402,12 +675,12 @@ mm_plugin_create (void)
MM_PLUGIN_PRODUCT_STRINGS, product_strings,
NULL));
}
- </programlisting>
- </example>
+ </programlisting>
+ </example>
- <example>
- <title>Probing setup for a plugin with custom initialization requirements</title>
- <programlisting>
+ <example>
+ <title>Probing setup for a plugin with custom initialization requirements</title>
+ <programlisting>
static gboolean
parse_custom_at (const gchar *response,
const GError *error,
@@ -451,25 +724,23 @@ mm_plugin_create (void)
/* No post-probing filters */
NULL));
}
- </programlisting>
- </example>
- </section>
- </section>
-
- <section>
- <title>Port grabbing and Modem object creation</title>
- <para>
- Once a port passes all probing filters of a given plugin, the plugin will grab
- the port. When the first port of a given device is grabbed, the plugin will create
- the required Modem object.
- </para>
- <para>
- While preparing to get the Modem object grab the specific port probed, udev-based
- port type hints can be used to specify AT port flags (e.g. if a port is to be
- considered primary, secondary or for PPP).
- </para>
+ </programlisting>
+ </example>
</section>
+ </chapter>
+ <chapter id="ref-overview-modem-object-creation">
+ <title>Modem object creation</title>
+ <para>
+ Once a port passes all probing filters of a given plugin, the plugin will grab
+ the port. When the first port of a given device is grabbed, the plugin will create
+ the required Modem object.
+ </para>
+ <para>
+ While preparing to get the Modem object grab the specific port probed, udev-based
+ port type hints can be used to specify AT port flags (e.g. if a port is to be
+ considered primary, secondary or for PPP).
+ </para>
</chapter>
<chapter id="ref-overview-modem-state-machine">
diff --git a/docs/reference/api/ModemManager-sections.txt b/docs/reference/api/ModemManager-sections.txt
index fd551a29..5184503a 100644
--- a/docs/reference/api/ModemManager-sections.txt
+++ b/docs/reference/api/ModemManager-sections.txt
@@ -10,15 +10,23 @@ MM_CHECK_VERSION
<SECTION>
<FILE>mm-enums</FILE>
<TITLE>Flags and Enumerations</TITLE>
+MMBearerType
MMBearerIpFamily
MMBearerIpMethod
MMBearerAllowedAuth
+MMBearerMultiplexSupport
+MMBearerApnType
+MMCallDirection
+MMCallState
+MMCallStateReason
MMFirmwareImageType
MMModem3gppFacility
MMModem3gppNetworkAvailability
MMModem3gppSubscriptionState
MMModem3gppRegistrationState
MMModem3gppUssdSessionState
+MMModem3gppEpsUeModeOperation
+MMModem3gppPacketServiceState
MMModemAccessTechnology
MMModemBand
MMModemCapability
@@ -27,6 +35,7 @@ MMModemCdmaRegistrationState
MMModemCdmaRmProtocol
MMModemContactsStorage
MMModemLocationSource
+MMModemLocationAssistanceDataType
MMModemLock
MMModemMode
MMModemState
@@ -34,6 +43,7 @@ MMModemStateFailedReason
MMModemStateChangeReason
MMModemPowerState
MMModemPortType
+MMModemFirmwareUpdateMethod
MMOmaFeature
MMOmaSessionState
MMOmaSessionStateFailedReason
@@ -50,17 +60,158 @@ MMSmsCdmaServiceCategory
<SECTION>
<FILE>mm-errors</FILE>
<TITLE>Errors</TITLE>
-MMConnectionError
+MM_CORE_ERROR_DBUS_PREFIX
+MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX
+MM_CONNECTION_ERROR_DBUS_PREFIX
+MM_SERIAL_ERROR_DBUS_PREFIX
+MM_MESSAGE_ERROR_DBUS_PREFIX
+MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX
MMCoreError
-MMMessageError
MMMobileEquipmentError
+MMConnectionError
MMSerialError
+MMMessageError
MMCdmaActivationError
+</SECTION>
+
+<SECTION>
+<FILE>mm-compat</FILE>
+MM_MODEM_BAND_U2100
+MM_MODEM_BAND_U1900
+MM_MODEM_BAND_U1800
+MM_MODEM_BAND_U17IV
+MM_MODEM_BAND_U850
+MM_MODEM_BAND_U800
+MM_MODEM_BAND_U2600
+MM_MODEM_BAND_U900
+MM_MODEM_BAND_U17IX
+MM_MODEM_BAND_EUTRAN_I
+MM_MODEM_BAND_EUTRAN_II
+MM_MODEM_BAND_EUTRAN_III
+MM_MODEM_BAND_EUTRAN_IV
+MM_MODEM_BAND_EUTRAN_V
+MM_MODEM_BAND_EUTRAN_VI
+MM_MODEM_BAND_EUTRAN_VII
+MM_MODEM_BAND_EUTRAN_VIII
+MM_MODEM_BAND_EUTRAN_IX
+MM_MODEM_BAND_EUTRAN_X
+MM_MODEM_BAND_EUTRAN_XI
+MM_MODEM_BAND_EUTRAN_XII
+MM_MODEM_BAND_EUTRAN_XIII
+MM_MODEM_BAND_EUTRAN_XIV
+MM_MODEM_BAND_EUTRAN_XVII
+MM_MODEM_BAND_EUTRAN_XVIII
+MM_MODEM_BAND_EUTRAN_XIX
+MM_MODEM_BAND_EUTRAN_XX
+MM_MODEM_BAND_EUTRAN_XXI
+MM_MODEM_BAND_EUTRAN_XXII
+MM_MODEM_BAND_EUTRAN_XXIII
+MM_MODEM_BAND_EUTRAN_XXIV
+MM_MODEM_BAND_EUTRAN_XXV
+MM_MODEM_BAND_EUTRAN_XXVI
+MM_MODEM_BAND_EUTRAN_XXXIII
+MM_MODEM_BAND_EUTRAN_XXXIV
+MM_MODEM_BAND_EUTRAN_XXXV
+MM_MODEM_BAND_EUTRAN_XXXVI
+MM_MODEM_BAND_EUTRAN_XXXVII
+MM_MODEM_BAND_EUTRAN_XXXVIII
+MM_MODEM_BAND_EUTRAN_XXXIX
+MM_MODEM_BAND_EUTRAN_XL
+MM_MODEM_BAND_EUTRAN_XLI
+MM_MODEM_BAND_EUTRAN_XLII
+MM_MODEM_BAND_EUTRAN_XLIII
+MM_MODEM_BAND_EUTRAN_XLIV
+MM_MODEM_BAND_CDMA_BC0_CELLULAR_800
+MM_MODEM_BAND_CDMA_BC1_PCS_1900
+MM_MODEM_BAND_CDMA_BC2_TACS
+MM_MODEM_BAND_CDMA_BC3_JTACS
+MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS
+MM_MODEM_BAND_CDMA_BC5_NMT450
+MM_MODEM_BAND_CDMA_BC6_IMT2000
+MM_MODEM_BAND_CDMA_BC7_CELLULAR_700
+MM_MODEM_BAND_CDMA_BC8_1800
+MM_MODEM_BAND_CDMA_BC9_900
+MM_MODEM_BAND_CDMA_BC10_SECONDARY_800
+MM_MODEM_BAND_CDMA_BC11_PAMR_400
+MM_MODEM_BAND_CDMA_BC12_PAMR_800
+MM_MODEM_BAND_CDMA_BC13_IMT2000_2500
+MM_MODEM_BAND_CDMA_BC14_PCS2_1900
+MM_MODEM_BAND_CDMA_BC15_AWS
+MM_MODEM_BAND_CDMA_BC16_US_2500
+MM_MODEM_BAND_CDMA_BC17_US_FLO_2500
+MM_MODEM_BAND_CDMA_BC18_US_PS_700
+MM_MODEM_BAND_CDMA_BC19_US_LOWER_700
+MM_MODEM_LOCATION_SOURCE_AGPS
+MM_MODEM_CAPABILITY_LTE_ADVANCED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_BY_GGSN_OR_GW
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_UNSPECIFIED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONDITIONAL_IE_ERROR
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_FEATURE_NOT_SUPPORTED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_IE_NOT_IMPLEMENTED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_MANDATORY_IE_ERROR
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_NOT_AUTHORIZED_FOR_CSG
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_OPERATOR_DETERMINED_BARRING
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUESTED_APN_NOT_SUPPORTED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUEST_REJECTED_BCM_VIOLATION
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTICALLY_INCORRECT_MESSAGE
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERRORS_IN_PACKET_FILTER
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERROR_IN_TFT_OPERATION
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_PACKET_FILTER
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_TFT_OPERATION
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_ADDRESS_OR_TYPE
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_CONTEXT
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNSPECIFIED_PROTOCOL_ERROR
+MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED
<SUBSECTION Private>
-MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX
-MM_CONNECTION_ERROR_DBUS_PREFIX
-MM_CORE_ERROR_DBUS_PREFIX
-MM_MESSAGE_ERROR_DBUS_PREFIX
-MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX
-MM_SERIAL_ERROR_DBUS_PREFIX
+MMModemBandDeprecated
+MMModemLocationSourceDeprecated
+MMModemCapabilityDeprecated
+MMMobileEquipmentErrorDeprecated
+MM_DEPRECATED
+</SECTION>
+
+<SECTION>
+<FILE>mm-tags</FILE>
+<TITLE>Common udev tags</TITLE>
+ID_MM_CANDIDATE
+ID_MM_PHYSDEV_UID
+ID_MM_DEVICE_PROCESS
+ID_MM_DEVICE_IGNORE
+ID_MM_PORT_IGNORE
+ID_MM_PORT_TYPE_AT_PPP
+ID_MM_PORT_TYPE_AT_PRIMARY
+ID_MM_PORT_TYPE_AT_SECONDARY
+ID_MM_PORT_TYPE_GPS
+ID_MM_PORT_TYPE_QCDM
+ID_MM_PORT_TYPE_AUDIO
+ID_MM_PORT_TYPE_QMI
+ID_MM_PORT_TYPE_MBIM
+ID_MM_TTY_BAUDRATE
+ID_MM_TTY_FLOW_CONTROL
+<SUBSECTION Deprecated>
+ID_MM_TTY_BLACKLIST
+ID_MM_TTY_MANUAL_SCAN_ONLY
</SECTION>
diff --git a/docs/reference/api/meson.build b/docs/reference/api/meson.build
new file mode 100644
index 00000000..d7c420c7
--- /dev/null
+++ b/docs/reference/api/meson.build
@@ -0,0 +1,57 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+doc_module = mm_name
+
+private_headers = [
+ 'ModemManager.h',
+ 'ModemManager-names.h',
+]
+
+mm_doc_path = mm_prefix / gnome.gtkdoc_html_dir(doc_module)
+
+version_xml = configure_file(
+ input: 'version.xml.in',
+ output: '@BASENAME@',
+ configuration: version_conf,
+)
+
+expand_content_files = [
+ 'ModemManager-dbus-reference.xml',
+ 'ModemManager-migration-reference.xml',
+ 'ModemManager-overview.xml',
+ # FIXME: workaround because only strings can be included and not custom targets (gen_docs)
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml',
+ generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.xml',
+]
+
+gnome.gtkdoc(
+ doc_module,
+ main_xml: doc_module + '-docs.xml',
+ src_dir: include_inc,
+ ignore_headers: private_headers,
+ include_directories: top_inc,
+ gobject_typesfile: doc_module + '.types',
+ dependencies: glib_deps,
+ namespace: 'mm',
+ scan_args: '--deprecated-guards="MM_DISABLE_DEPRECATED"',
+ fixxref_args: '--html-dir=' + mm_doc_path,
+ html_assets: logos_pngs + diagrams_pngs,
+ content_files: version_xml,
+ expand_content_files: expand_content_files,
+ install: true,
+)
diff --git a/docs/reference/libmm-glib/Makefile.am b/docs/reference/libmm-glib/Makefile.am
index e2abdbf5..db8c12b5 100644
--- a/docs/reference/libmm-glib/Makefile.am
+++ b/docs/reference/libmm-glib/Makefile.am
@@ -12,7 +12,7 @@ DOC_MODULE = libmm-glib
DOC_MAIN_SGML_FILE = $(DOC_MODULE)-docs.xml
# Extra options to supply to gtkdoc-scan
-SCAN_OPTIONS =
+SCAN_OPTIONS = --rebuild-types --deprecated-guards="MM_DISABLE_DEPRECATED"
# The directory containing the source code.
DOC_SOURCE_DIR = \
@@ -60,7 +60,6 @@ HTML_IMAGES = \
$(NULL)
content_files = \
- $(HTML_IMAGES) \
$(NULL)
if ENABLE_GTK_DOC
@@ -87,11 +86,7 @@ CLEANFILES += \
$(DOC_MODULE).interfaces \
$(DOC_MODULE).prerequisites \
$(DOC_MODULE).signals \
+ $(DOC_MODULE).actions \
*.stamp \
-rf xml html tmpl \
$(NULL)
-
-# PNGs are removed only in maintainer-clean
-# Logos are copied to current directory by gtk-doc
-DISTCLEANFILES = \
- $(notdir $(LOGOS_PNG))
diff --git a/docs/reference/libmm-glib/libmm-glib-docs.xml b/docs/reference/libmm-glib/libmm-glib-docs.xml
index c864cbbf..942559bf 100644
--- a/docs/reference/libmm-glib/libmm-glib-docs.xml
+++ b/docs/reference/libmm-glib/libmm-glib-docs.xml
@@ -15,23 +15,34 @@
</subtitle>
<releaseinfo>
For libmm-glib version &version;
+ The latest version of this documentation can be found on-line at
+ <ulink role="online-location" url="https://www.freedesktop.org/software/ModemManager/doc/latest/libmm-glib/">https://www.freedesktop.org/software/ModemManager/doc/latest/libmm-glib/</ulink>.
</releaseinfo>
<authorgroup>
<author>
- <firstname>Aleksander</firstname>
- <surname>Morgado</surname>
- <affiliation>
- <address>
- <email>aleksander@aleksander.es</email>
- </address>
- </affiliation>
+ <firstname>Aleksander</firstname>
+ <surname>Morgado</surname>
+ <affiliation>
+ <address>
+ <email>aleksander@aleksander.es</email>
+ </address>
+ </affiliation>
</author>
</authorgroup>
<copyright>
<year>2011</year>
<year>2012</year>
+ <year>2013</year>
+ <year>2014</year>
+ <year>2015</year>
+ <year>2016</year>
+ <year>2017</year>
+ <year>2018</year>
+ <year>2019</year>
+ <year>2020</year>
+ <year>2021</year>
<holder>The ModemManager Authors</holder>
</copyright>
@@ -68,6 +79,7 @@
<chapter>
<title>The Manager object</title>
<xi:include href="xml/mm-manager.xml"/>
+ <xi:include href="xml/mm-kernel-event-properties.xml"/>
</chapter>
<chapter>
@@ -77,10 +89,10 @@
<title>Generic interfaces</title>
<xi:include href="xml/mm-modem.xml"/>
<xi:include href="xml/mm-modem-3gpp.xml"/>
- <xi:include href="xml/mm-modem-3gpp-ussd.xml"/>
<xi:include href="xml/mm-modem-cdma.xml"/>
<xi:include href="xml/mm-cdma-manual-activation-properties.xml"/>
<xi:include href="xml/mm-unlock-retries.xml"/>
+ <xi:include href="xml/mm-pco.xml"/>
</section>
<section>
<title>Simple interface support</title>
@@ -89,6 +101,15 @@
<xi:include href="xml/mm-simple-status.xml"/>
</section>
<section>
+ <title>USSD support</title>
+ <xi:include href="xml/mm-modem-3gpp-ussd.xml"/>
+ </section>
+ <section>
+ <title>Profile management support</title>
+ <xi:include href="xml/mm-modem-3gpp-profile-manager.xml"/>
+ <xi:include href="xml/mm-3gpp-profile.xml"/>
+ </section>
+ <section>
<title>Location support</title>
<xi:include href="xml/mm-modem-location.xml"/>
<xi:include href="xml/mm-location-3gpp.xml"/>
@@ -109,16 +130,26 @@
<title>Firmware support</title>
<xi:include href="xml/mm-modem-firmware.xml"/>
<xi:include href="xml/mm-firmware-properties.xml"/>
+ <xi:include href="xml/mm-firmware-update-settings.xml"/>
</section>
<section>
<title>Extended signal information</title>
<xi:include href="xml/mm-modem-signal.xml"/>
<xi:include href="xml/mm-signal.xml"/>
+ <xi:include href="xml/mm-signal-threshold-properties.xml"/>
</section>
<section>
<title>OMA support</title>
<xi:include href="xml/mm-modem-oma.xml"/>
</section>
+ <section>
+ <title>Voice support</title>
+ <xi:include href="xml/mm-modem-voice.xml"/>
+ </section>
+ <section>
+ <title>SAR support</title>
+ <xi:include href="xml/mm-modem-sar.xml"/>
+ </section>
</chapter>
<chapter>
@@ -126,11 +157,13 @@
<xi:include href="xml/mm-bearer.xml"/>
<xi:include href="xml/mm-bearer-properties.xml"/>
<xi:include href="xml/mm-bearer-ip-config.xml"/>
+ <xi:include href="xml/mm-bearer-stats.xml"/>
</chapter>
<chapter>
<title>The SIM object</title>
<xi:include href="xml/mm-sim.xml"/>
+ <xi:include href="xml/mm-sim-preferred-network.xml"/>
</chapter>
<chapter>
@@ -139,6 +172,13 @@
<xi:include href="xml/mm-sms-properties.xml"/>
</chapter>
+ <chapter>
+ <title>The Call object</title>
+ <xi:include href="xml/mm-call.xml"/>
+ <xi:include href="xml/mm-call-properties.xml"/>
+ <xi:include href="xml/mm-call-audio-format.xml"/>
+ </chapter>
+
</part>
<part>
@@ -160,6 +200,10 @@
<xi:include href="xml/MmGdbusModem3gppProxy.xml"/>
<xi:include href="xml/MmGdbusModem3gppSkeleton.xml"/>
+ <xi:include href="xml/MmGdbusModem3gppProfileManager.xml"/>
+ <xi:include href="xml/MmGdbusModem3gppProfileManagerProxy.xml"/>
+ <xi:include href="xml/MmGdbusModem3gppProfileManagerSkeleton.xml"/>
+
<xi:include href="xml/MmGdbusModem3gppUssd.xml"/>
<xi:include href="xml/MmGdbusModem3gppUssdProxy.xml"/>
<xi:include href="xml/MmGdbusModem3gppUssdSkeleton.xml"/>
@@ -192,6 +236,14 @@
<xi:include href="xml/MmGdbusModemOmaProxy.xml"/>
<xi:include href="xml/MmGdbusModemOmaSkeleton.xml"/>
+ <xi:include href="xml/MmGdbusModemVoice.xml"/>
+ <xi:include href="xml/MmGdbusModemVoiceProxy.xml"/>
+ <xi:include href="xml/MmGdbusModemVoiceSkeleton.xml"/>
+
+ <xi:include href="xml/MmGdbusModemSar.xml"/>
+ <xi:include href="xml/MmGdbusModemSarProxy.xml"/>
+ <xi:include href="xml/MmGdbusModemSarSkeleton.xml"/>
+
<!--xi:include href="xml/MmGdbusModemContacts.xml"/>
<xi:include href="xml/MmGdbusModemContactsProxy.xml"/>
<xi:include href="xml/MmGdbusModemContactsSkeleton.xml"/-->
@@ -212,22 +264,72 @@
<xi:include href="xml/MmGdbusSmsProxy.xml"/>
<xi:include href="xml/MmGdbusSmsSkeleton.xml"/>
+ <xi:include href="xml/MmGdbusCall.xml"/>
+ <xi:include href="xml/MmGdbusCallProxy.xml"/>
+ <xi:include href="xml/MmGdbusCallSkeleton.xml"/>
</part>
+ <chapter>
+ <title>Compatibility with older versions</title>
+ <xi:include href="xml/mm-compat.xml"/>
+ </chapter>
+
<chapter id="object-tree">
<title>Object Hierarchy</title>
<xi:include href="xml/tree_index.sgml"/>
</chapter>
+ <chapter id="api-index-full">
+ <title>Index</title>
+ <xi:include href="xml/api-index-full.xml"></xi:include>
+ </chapter>
+ <chapter id="deprecated-api-index" role="deprecated">
+ <title>Index of deprecated symbols</title>
+ <xi:include href="xml/api-index-deprecated.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-0" role="1.0">
+ <title>Index of new symbols in 1.0</title>
+ <xi:include href="xml/api-index-1.0.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-2" role="1.2">
+ <title>Index of new symbols in 1.2</title>
+ <xi:include href="xml/api-index-1.2.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-4" role="1.4">
+ <title>Index of new symbols in 1.4</title>
+ <xi:include href="xml/api-index-1.4.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-6" role="1.6">
+ <title>Index of new symbols in 1.6</title>
+ <xi:include href="xml/api-index-1.6.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-8" role="1.8">
+ <title>Index of new symbols in 1.8</title>
+ <xi:include href="xml/api-index-1.8.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-10" role="1.10">
+ <title>Index of new symbols in 1.10</title>
+ <xi:include href="xml/api-index-1.10.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-12" role="1.12">
+ <title>Index of new symbols in 1.12</title>
+ <xi:include href="xml/api-index-1.12.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-14" role="1.14">
+ <title>Index of new symbols in 1.14</title>
+ <xi:include href="xml/api-index-1.14.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-16" role="1.16">
+ <title>Index of new symbols in 1.16</title>
+ <xi:include href="xml/api-index-1.16.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-18" role="1.18">
+ <title>Index of new symbols in 1.18</title>
+ <xi:include href="xml/api-index-1.18.xml"></xi:include>
+ </chapter>
+ <chapter id="api-index-1-20" role="1.20">
+ <title>Index of new symbols in 1.20</title>
+ <xi:include href="xml/api-index-1.20.xml"></xi:include>
+ </chapter>
- <index id="api-index-full">
- <title>API Index</title>
- <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
- </index>
-
- <index id="deprecated-api-index" role="deprecated">
- <title>Index of deprecated API</title>
- <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
- </index>
-
- <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
+ <xi:include href="xml/annotation-glossary.xml"></xi:include>
</book>
diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt
index 7db3730a..1d95b042 100644
--- a/docs/reference/libmm-glib/libmm-glib-sections.txt
+++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
@@ -14,12 +14,22 @@ mm_manager_new
mm_manager_new_finish
mm_manager_new_sync
<SUBSECTION Methods>
+mm_manager_get_version
mm_manager_scan_devices
mm_manager_scan_devices_finish
mm_manager_scan_devices_sync
+mm_manager_inhibit_device
+mm_manager_inhibit_device_finish
+mm_manager_inhibit_device_sync
+mm_manager_uninhibit_device
+mm_manager_uninhibit_device_finish
+mm_manager_uninhibit_device_sync
mm_manager_set_logging
mm_manager_set_logging_finish
mm_manager_set_logging_sync
+mm_manager_report_kernel_event
+mm_manager_report_kernel_event_finish
+mm_manager_report_kernel_event_sync
<SUBSECTION Standard>
MMManagerClass
MMManagerPrivate
@@ -33,6 +43,37 @@ mm_manager_get_type
</SECTION>
<SECTION>
+<FILE>mm-kernel-event-properties</FILE>
+<TITLE>MMKernelEventProperties</TITLE>
+MMKernelEventProperties
+<SUBSECTION Methods>
+mm_kernel_event_properties_new
+mm_kernel_event_properties_get_action
+mm_kernel_event_properties_set_action
+mm_kernel_event_properties_get_name
+mm_kernel_event_properties_set_name
+mm_kernel_event_properties_get_subsystem
+mm_kernel_event_properties_set_subsystem
+mm_kernel_event_properties_get_uid
+mm_kernel_event_properties_set_uid
+<SUBSECTION Private>
+mm_kernel_event_properties_new_from_string
+mm_kernel_event_properties_new_from_dictionary
+mm_kernel_event_properties_dup
+mm_kernel_event_properties_get_dictionary
+<SUBSECTION Standard>
+MMKernelEventPropertiesClass
+MMKernelEventPropertiesPrivate
+MM_KERNEL_EVENT_PROPERTIES
+MM_KERNEL_EVENT_PROPERTIES_CLASS
+MM_KERNEL_EVENT_PROPERTIES_GET_CLASS
+MM_IS_KERNEL_EVENT_PROPERTIES
+MM_IS_KERNEL_EVENT_PROPERTIES_CLASS
+MM_TYPE_KERNEL_EVENT_PROPERTIES
+mm_kernel_event_properties_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-object</FILE>
<TITLE>MMObject</TITLE>
MMObject
@@ -44,6 +85,8 @@ mm_object_peek_modem
mm_object_get_modem
mm_object_peek_modem_3gpp
mm_object_get_modem_3gpp
+mm_object_peek_modem_3gpp_profile_manager
+mm_object_get_modem_3gpp_profile_manager
mm_object_peek_modem_3gpp_ussd
mm_object_get_modem_3gpp_ussd
mm_object_peek_modem_cdma
@@ -62,6 +105,10 @@ mm_object_peek_modem_simple
mm_object_get_modem_simple
mm_object_peek_modem_signal
mm_object_get_modem_signal
+mm_object_peek_modem_voice
+mm_object_get_modem_voice
+mm_object_peek_modem_sar
+mm_object_get_modem_sar
<SUBSECTION Standard>
MMObjectClass
MM_IS_OBJECT
@@ -94,6 +141,12 @@ mm_modem_get_model
mm_modem_dup_model
mm_modem_get_revision
mm_modem_dup_revision
+mm_modem_get_carrier_configuration
+mm_modem_dup_carrier_configuration
+mm_modem_get_carrier_configuration_revision
+mm_modem_dup_carrier_configuration_revision
+mm_modem_get_hardware_revision
+mm_modem_dup_hardware_revision
mm_modem_get_drivers
mm_modem_dup_drivers
mm_modem_get_plugin
@@ -113,6 +166,7 @@ mm_modem_peek_unlock_retries
mm_modem_get_unlock_retries
mm_modem_get_max_bearers
mm_modem_get_max_active_bearers
+mm_modem_get_max_active_multiplexed_bearers
mm_modem_get_bearer_paths
mm_modem_dup_bearer_paths
mm_modem_get_own_numbers
@@ -133,6 +187,15 @@ mm_modem_dup_sim_path
mm_modem_get_sim
mm_modem_get_sim_finish
mm_modem_get_sim_sync
+mm_modem_get_sim_slot_paths
+mm_modem_dup_sim_slot_paths
+mm_modem_get_primary_sim_slot
+mm_modem_list_sim_slots
+mm_modem_list_sim_slots_finish
+mm_modem_list_sim_slots_sync
+mm_modem_set_primary_sim_slot
+mm_modem_set_primary_sim_slot_finish
+mm_modem_set_primary_sim_slot_sync
<SUBSECTION Methods>
mm_modem_enable
mm_modem_enable_finish
@@ -237,7 +300,16 @@ mm_modem_3gpp_get_operator_name
mm_modem_3gpp_dup_operator_name
mm_modem_3gpp_get_enabled_facility_locks
mm_modem_3gpp_get_registration_state
-mm_modem_3gpp_get_subscription_state
+mm_modem_3gpp_get_pco
+mm_modem_3gpp_get_eps_ue_mode_operation
+mm_modem_3gpp_get_initial_eps_bearer_path
+mm_modem_3gpp_dup_initial_eps_bearer_path
+mm_modem_3gpp_get_initial_eps_bearer
+mm_modem_3gpp_get_initial_eps_bearer_finish
+mm_modem_3gpp_get_initial_eps_bearer_sync
+mm_modem_3gpp_get_initial_eps_bearer_settings
+mm_modem_3gpp_peek_initial_eps_bearer_settings
+mm_modem_3gpp_get_packet_service_state
<SUBSECTION Methods>
mm_modem_3gpp_register
mm_modem_3gpp_register_finish
@@ -245,15 +317,30 @@ mm_modem_3gpp_register_sync
mm_modem_3gpp_scan
mm_modem_3gpp_scan_finish
mm_modem_3gpp_scan_sync
+mm_modem_3gpp_set_eps_ue_mode_operation
+mm_modem_3gpp_set_eps_ue_mode_operation_finish
+mm_modem_3gpp_set_eps_ue_mode_operation_sync
+mm_modem_3gpp_set_initial_eps_bearer_settings
+mm_modem_3gpp_set_initial_eps_bearer_settings_finish
+mm_modem_3gpp_set_initial_eps_bearer_settings_sync
+mm_modem_3gpp_disable_facility_lock
+mm_modem_3gpp_disable_facility_lock_finish
+mm_modem_3gpp_disable_facility_lock_sync
+mm_modem_3gpp_set_packet_service_state
+mm_modem_3gpp_set_packet_service_state_finish
+mm_modem_3gpp_set_packet_service_state_sync
<SUBSECTION Standard>
MMModem3gppClass
+MMModem3gppPrivate
MM_IS_MODEM_3GPP
MM_IS_MODEM_3GPP_CLASS
MM_MODEM_3GPP
MM_MODEM_3GPP_CLASS
MM_MODEM_3GPP_GET_CLASS
MM_TYPE_MODEM_3GPP
+MM_TYPE_MODEM_3GPP_NETWORK
mm_modem_3gpp_get_type
+mm_modem_3gpp_network_get_type
</SECTION>
<SECTION>
@@ -377,9 +464,13 @@ mm_modem_location_get_path
mm_modem_location_dup_path
mm_modem_location_get_capabilities
mm_modem_location_get_enabled
+mm_modem_location_get_gps_refresh_rate
mm_modem_location_signals_location
mm_modem_location_dup_supl_server
mm_modem_location_get_supl_server
+mm_modem_location_get_supported_assistance_data
+mm_modem_location_dup_assistance_data_servers
+mm_modem_location_get_assistance_data_servers
<SUBSECTION Methods>
mm_modem_location_setup
mm_modem_location_setup_finish
@@ -387,23 +478,38 @@ mm_modem_location_setup_sync
mm_modem_location_set_supl_server
mm_modem_location_set_supl_server_finish
mm_modem_location_set_supl_server_sync
+mm_modem_location_inject_assistance_data
+mm_modem_location_inject_assistance_data_finish
+mm_modem_location_inject_assistance_data_sync
+mm_modem_location_set_gps_refresh_rate
+mm_modem_location_set_gps_refresh_rate_finish
+mm_modem_location_set_gps_refresh_rate_sync
mm_modem_location_get_3gpp
mm_modem_location_get_3gpp_finish
mm_modem_location_get_3gpp_sync
+mm_modem_location_peek_signaled_3gpp
+mm_modem_location_get_signaled_3gpp
mm_modem_location_get_gps_nmea
mm_modem_location_get_gps_nmea_finish
mm_modem_location_get_gps_nmea_sync
+mm_modem_location_peek_signaled_gps_nmea
+mm_modem_location_get_signaled_gps_nmea
mm_modem_location_get_gps_raw
mm_modem_location_get_gps_raw_finish
mm_modem_location_get_gps_raw_sync
+mm_modem_location_peek_signaled_gps_raw
+mm_modem_location_get_signaled_gps_raw
mm_modem_location_get_cdma_bs
mm_modem_location_get_cdma_bs_finish
mm_modem_location_get_cdma_bs_sync
+mm_modem_location_peek_signaled_cdma_bs
+mm_modem_location_get_signaled_cdma_bs
mm_modem_location_get_full
mm_modem_location_get_full_finish
mm_modem_location_get_full_sync
<SUBSECTION Standard>
MMModemLocationClass
+MMModemLocationPrivate
MM_IS_MODEM_LOCATION
MM_IS_MODEM_LOCATION_CLASS
MM_MODEM_LOCATION
@@ -420,7 +526,9 @@ MMLocation3gpp
<SUBSECTION Getters>
mm_location_3gpp_get_mobile_country_code
mm_location_3gpp_get_mobile_network_code
+mm_location_3gpp_get_operator_code
mm_location_3gpp_get_location_area_code
+mm_location_3gpp_get_tracking_area_code
mm_location_3gpp_get_cell_id
<SUBSECTION Private>
mm_location_3gpp_get_string_variant
@@ -428,8 +536,9 @@ mm_location_3gpp_new
mm_location_3gpp_new_from_string_variant
mm_location_3gpp_set_cell_id
mm_location_3gpp_set_location_area_code
-mm_location_3gpp_set_mobile_country_code
-mm_location_3gpp_set_mobile_network_code
+mm_location_3gpp_set_tracking_area_code
+mm_location_3gpp_set_operator_code
+mm_location_3gpp_reset
<SUBSECTION Standard>
MMLocation3gppClass
MMLocation3gppPrivate
@@ -448,7 +557,7 @@ mm_location_3gpp_get_type
MMLocationGpsNmea
<SUBSECTION Getters>
mm_location_gps_nmea_get_trace
-mm_location_gps_nmea_build_full
+mm_location_gps_nmea_get_traces
<SUBSECTION Private>
mm_location_gps_nmea_new
mm_location_gps_nmea_new_from_string_variant
@@ -616,7 +725,10 @@ mm_modem_firmware_list_sync
mm_modem_firmware_select
mm_modem_firmware_select_finish
mm_modem_firmware_select_sync
+mm_modem_firmware_get_update_settings
+mm_modem_firmware_peek_update_settings
<SUBSECTION Standard>
+MMModemFirmwarePrivate
MMModemFirmwareClass
MM_IS_MODEM_FIRMWARE
MM_IS_MODEM_FIRMWARE_CLASS
@@ -661,6 +773,34 @@ mm_firmware_properties_get_type
</SECTION>
<SECTION>
+<FILE>mm-firmware-update-settings</FILE>
+<TITLE>MMFirmwareUpdateSettings</TITLE>
+MMFirmwareUpdateSettings
+<SUBSECTION Getters>
+mm_firmware_update_settings_get_fastboot_at
+mm_firmware_update_settings_get_method
+mm_firmware_update_settings_get_device_ids
+mm_firmware_update_settings_get_version
+<SUBSECTION Private>
+mm_firmware_update_settings_get_variant
+mm_firmware_update_settings_new
+mm_firmware_update_settings_new_from_variant
+mm_firmware_update_settings_set_fastboot_at
+mm_firmware_update_settings_set_device_ids
+mm_firmware_update_settings_set_version
+<SUBSECTION Standard>
+MMFirmwareUpdateSettingsClass
+MMFirmwareUpdateSettingsPrivate
+MM_FIRMWARE_UPDATE_SETTINGS
+MM_FIRMWARE_UPDATE_SETTINGS_CLASS
+MM_FIRMWARE_UPDATE_SETTINGS_GET_CLASS
+MM_IS_FIRMWARE_UPDATE_SETTINGS
+MM_IS_FIRMWARE_UPDATE_SETTINGS_CLASS
+MM_TYPE_FIRMWARE_UPDATE_SETTINGS
+mm_firmware_update_settings_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-modem-oma</FILE>
<TITLE>MMModemOma</TITLE>
MMModemOma
@@ -684,8 +824,8 @@ mm_modem_oma_cancel_session_sync
mm_modem_oma_get_features
mm_modem_oma_get_session_type
mm_modem_oma_get_session_state
-mm_modem_peek_pending_network_initiated_sessions
-mm_modem_get_pending_network_initiated_sessions
+mm_modem_oma_peek_pending_network_initiated_sessions
+mm_modem_oma_get_pending_network_initiated_sessions
<SUBSECTION Standard>
MMModemOmaClass
MMModemOmaPrivate
@@ -727,6 +867,33 @@ mm_modem_simple_get_type
</SECTION>
<SECTION>
+<FILE>mm-modem-sar</FILE>
+<TITLE>MMModemSar</TITLE>
+MMModemSar
+<SUBSECTION Getters>
+mm_modem_sar_get_path
+mm_modem_sar_dup_path
+<SUBSECTION Methods>
+mm_modem_sar_enable
+mm_modem_sar_enable_finish
+mm_modem_sar_enable_sync
+mm_modem_sar_set_power_level
+mm_modem_sar_set_power_level_finish
+mm_modem_sar_set_power_level_sync
+mm_modem_sar_get_state
+mm_modem_sar_get_power_level
+<SUBSECTION Standard>
+MMModemSarClass
+MM_IS_MODEM_SAR
+MM_IS_MODEM_SAR_CLASS
+MM_MODEM_SAR
+MM_MODEM_SAR_CLASS
+MM_MODEM_SAR_GET_CLASS
+MM_TYPE_MODEM_SAR
+mm_modem_sar_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-simple-connect-properties</FILE>
<TITLE>MMSimpleConnectProperties</TITLE>
MMSimpleConnectProperties
@@ -739,6 +906,8 @@ mm_simple_connect_properties_get_operator_id
mm_simple_connect_properties_set_operator_id
mm_simple_connect_properties_get_apn
mm_simple_connect_properties_set_apn
+mm_simple_connect_properties_get_apn_type
+mm_simple_connect_properties_set_apn_type
mm_simple_connect_properties_get_allowed_auth
mm_simple_connect_properties_set_allowed_auth
mm_simple_connect_properties_get_user
@@ -747,10 +916,14 @@ mm_simple_connect_properties_get_password
mm_simple_connect_properties_set_password
mm_simple_connect_properties_get_ip_type
mm_simple_connect_properties_set_ip_type
+mm_simple_connect_properties_get_profile_id
+mm_simple_connect_properties_set_profile_id
mm_simple_connect_properties_get_allow_roaming
mm_simple_connect_properties_set_allow_roaming
-mm_simple_connect_properties_get_number
-mm_simple_connect_properties_set_number
+mm_simple_connect_properties_get_rm_protocol
+mm_simple_connect_properties_set_rm_protocol
+mm_simple_connect_properties_get_multiplex
+mm_simple_connect_properties_set_multiplex
<SUBSECTION Private>
mm_simple_connect_properties_get_bearer_properties
mm_simple_connect_properties_new_from_dictionary
@@ -780,7 +953,6 @@ mm_simple_status_get_current_bands
mm_simple_status_get_3gpp_registration_state
mm_simple_status_get_3gpp_operator_code
mm_simple_status_get_3gpp_operator_name
-mm_simple_status_get_3gpp_subscription_state
mm_simple_status_get_cdma_cdma1x_registration_state
mm_simple_status_get_cdma_evdo_registration_state
mm_simple_status_get_cdma_nid
@@ -821,6 +993,8 @@ MMModemSignal
mm_modem_signal_get_path
mm_modem_signal_dup_path
mm_modem_signal_get_rate
+mm_modem_signal_get_rssi_threshold
+mm_modem_signal_get_error_rate_threshold
mm_modem_signal_peek_cdma
mm_modem_signal_get_cdma
mm_modem_signal_peek_evdo
@@ -831,10 +1005,15 @@ mm_modem_signal_peek_umts
mm_modem_signal_get_umts
mm_modem_signal_peek_lte
mm_modem_signal_get_lte
+mm_modem_signal_peek_nr5g
+mm_modem_signal_get_nr5g
<SUBSECTION Methods>
mm_modem_signal_setup
mm_modem_signal_setup_finish
mm_modem_signal_setup_sync
+mm_modem_signal_setup_thresholds
+mm_modem_signal_setup_thresholds_finish
+mm_modem_signal_setup_thresholds_sync
<SUBSECTION Standard>
MMModemSignalPrivate
MMModemSignalClass
@@ -854,23 +1033,27 @@ MMSignal
MM_SIGNAL_UNKNOWN
<SUBSECTION Getters>
mm_signal_get_rssi
+mm_signal_get_rscp
mm_signal_get_ecio
mm_signal_get_sinr
mm_signal_get_io
mm_signal_get_rsrp
mm_signal_get_rsrq
mm_signal_get_snr
+mm_signal_get_error_rate
<SUBSECTION Private>
mm_signal_new
mm_signal_new_from_dictionary
mm_signal_get_dictionary
mm_signal_set_rssi
+mm_signal_set_rscp
mm_signal_set_ecio
mm_signal_set_sinr
mm_signal_set_io
mm_signal_set_rsrp
mm_signal_set_rsrq
mm_signal_set_snr
+mm_signal_set_error_rate
<SUBSECTION Standard>
MMSignalClass
MMSignalPrivate
@@ -884,6 +1067,82 @@ mm_signal_get_type
</SECTION>
<SECTION>
+<FILE>mm-signal-threshold-properties</FILE>
+<TITLE>MMSignalThresholdProperties</TITLE>
+MMSignalThresholdProperties
+<SUBSECTION New>
+mm_signal_threshold_properties_new
+<SUBSECTION GettersSetters>
+mm_signal_threshold_properties_get_rssi
+mm_signal_threshold_properties_set_rssi
+mm_signal_threshold_properties_get_error_rate
+mm_signal_threshold_properties_set_error_rate
+<SUBSECTION Private>
+mm_signal_threshold_properties_new_from_dictionary
+mm_signal_threshold_properties_new_from_string
+mm_signal_threshold_properties_get_dictionary
+<SUBSECTION Standard>
+MMSignalThresholdPropertiesClass
+MMSignalThresholdPropertiesPrivate
+MM_SIGNAL_THRESHOLD_PROPERTIES
+MM_SIGNAL_THRESHOLD_PROPERTIES_CLASS
+MM_SIGNAL_THRESHOLD_PROPERTIES_GET_CLASS
+MM_IS_SIGNAL_THRESHOLD_PROPERTIES
+MM_IS_SIGNAL_THRESHOLD_PROPERTIES_CLASS
+MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES
+mm_signal_threshold_properties_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mm-modem-voice</FILE>
+<TITLE>MMModemVoice</TITLE>
+MMModemVoice
+<SUBSECTION Getters>
+mm_modem_voice_get_path
+mm_modem_voice_dup_path
+mm_modem_voice_get_emergency_only
+
+<SUBSECTION Methods>
+mm_modem_voice_create_call
+mm_modem_voice_create_call_finish
+mm_modem_voice_create_call_sync
+mm_modem_voice_delete_call
+mm_modem_voice_delete_call_finish
+mm_modem_voice_delete_call_sync
+mm_modem_voice_list_calls
+mm_modem_voice_list_calls_finish
+mm_modem_voice_list_calls_sync
+mm_modem_voice_hangup_and_accept
+mm_modem_voice_hangup_and_accept_finish
+mm_modem_voice_hangup_and_accept_sync
+mm_modem_voice_hold_and_accept
+mm_modem_voice_hold_and_accept_finish
+mm_modem_voice_hold_and_accept_sync
+mm_modem_voice_hangup_all
+mm_modem_voice_hangup_all_finish
+mm_modem_voice_hangup_all_sync
+mm_modem_voice_transfer
+mm_modem_voice_transfer_finish
+mm_modem_voice_transfer_sync
+mm_modem_voice_call_waiting_query
+mm_modem_voice_call_waiting_query_finish
+mm_modem_voice_call_waiting_query_sync
+mm_modem_voice_call_waiting_setup
+mm_modem_voice_call_waiting_setup_finish
+mm_modem_voice_call_waiting_setup_sync
+<SUBSECTION Standard>
+MMModemVoiceClass
+MMModemVoicePrivate
+MM_IS_MODEM_VOICE
+MM_IS_MODEM_VOICE_CLASS
+MM_MODEM_VOICE
+MM_MODEM_VOICE_CLASS
+MM_MODEM_VOICE_GET_CLASS
+MM_TYPE_MODEM_VOICE
+mm_modem_voice_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-bearer</FILE>
<TITLE>MMBearer</TITLE>
MMBearer
@@ -894,13 +1153,20 @@ mm_bearer_get_interface
mm_bearer_dup_interface
mm_bearer_get_connected
mm_bearer_get_suspended
+mm_bearer_get_multiplexed
mm_bearer_get_ip_timeout
+mm_bearer_get_bearer_type
+mm_bearer_get_profile_id
mm_bearer_peek_ipv4_config
mm_bearer_get_ipv4_config
mm_bearer_peek_ipv6_config
mm_bearer_get_ipv6_config
mm_bearer_peek_properties
mm_bearer_get_properties
+mm_bearer_peek_stats
+mm_bearer_get_stats
+mm_bearer_get_connection_error
+mm_bearer_peek_connection_error
<SUBSECTION Methods>
mm_bearer_connect
mm_bearer_connect_finish
@@ -955,6 +1221,49 @@ mm_bearer_ip_config_get_type
</SECTION>
<SECTION>
+<FILE>mm-bearer-stats</FILE>
+<TITLE>MMBearerStats</TITLE>
+MMBearerStats
+<SUBSECTION Getters>
+mm_bearer_stats_get_duration
+mm_bearer_stats_get_start_date
+mm_bearer_stats_get_rx_bytes
+mm_bearer_stats_get_tx_bytes
+mm_bearer_stats_get_attempts
+mm_bearer_stats_get_failed_attempts
+mm_bearer_stats_get_total_duration
+mm_bearer_stats_get_total_rx_bytes
+mm_bearer_stats_get_total_tx_bytes
+mm_bearer_stats_get_uplink_speed
+mm_bearer_stats_get_downlink_speed
+<SUBSECTION Private>
+mm_bearer_stats_get_dictionary
+mm_bearer_stats_new
+mm_bearer_stats_new_from_dictionary
+mm_bearer_stats_set_duration
+mm_bearer_stats_set_start_date
+mm_bearer_stats_set_rx_bytes
+mm_bearer_stats_set_tx_bytes
+mm_bearer_stats_set_attempts
+mm_bearer_stats_set_failed_attempts
+mm_bearer_stats_set_total_duration
+mm_bearer_stats_set_total_rx_bytes
+mm_bearer_stats_set_total_tx_bytes
+mm_bearer_stats_set_uplink_speed
+mm_bearer_stats_set_downlink_speed
+<SUBSECTION Standard>
+MMBearerStatsClass
+MMBearerStatsPrivate
+MM_BEARER_STATS
+MM_BEARER_STATS_CLASS
+MM_BEARER_STATS_GET_CLASS
+MM_IS_BEARER_STATS
+MM_IS_BEARER_STATS_CLASS
+MM_TYPE_BEARER_STATS
+mm_bearer_stats_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-bearer-properties</FILE>
<TITLE>MMBearerProperties</TITLE>
MMBearerProperties
@@ -963,6 +1272,8 @@ mm_bearer_properties_new
<SUBSECTION GettersSetters>
mm_bearer_properties_get_apn
mm_bearer_properties_set_apn
+mm_bearer_properties_get_apn_type
+mm_bearer_properties_set_apn_type
mm_bearer_properties_get_allowed_auth
mm_bearer_properties_set_allowed_auth
mm_bearer_properties_get_user
@@ -971,20 +1282,27 @@ mm_bearer_properties_get_password
mm_bearer_properties_set_password
mm_bearer_properties_get_ip_type
mm_bearer_properties_set_ip_type
+mm_bearer_properties_get_profile_id
+mm_bearer_properties_set_profile_id
+mm_bearer_properties_get_profile_name
+mm_bearer_properties_set_profile_name
mm_bearer_properties_get_allow_roaming
mm_bearer_properties_set_allow_roaming
-mm_bearer_properties_get_number
-mm_bearer_properties_set_number
mm_bearer_properties_get_rm_protocol
mm_bearer_properties_set_rm_protocol
+mm_bearer_properties_get_multiplex
+mm_bearer_properties_set_multiplex
<SUBSECTION Private>
mm_bearer_properties_new_from_dictionary
mm_bearer_properties_new_from_string
+mm_bearer_properties_new_from_profile
+MMBearerPropertiesCmpFlags
mm_bearer_properties_cmp
mm_bearer_properties_consume_string
mm_bearer_properties_consume_variant
mm_bearer_properties_dup
mm_bearer_properties_get_dictionary
+mm_bearer_properties_peek_3gpp_profile
<SUBSECTION Standard>
MMBearerPropertiesClass
MMBearerPropertiesPrivate
@@ -998,20 +1316,48 @@ mm_bearer_properties_get_type
</SECTION>
<SECTION>
+<FILE>mm-sim-preferred-network</FILE>
+<TITLE>MMSimPreferredNetwork</TITLE>
+MMSimPreferredNetwork
+mm_sim_preferred_network_new
+mm_sim_preferred_network_get_operator_code
+mm_sim_preferred_network_set_operator_code
+mm_sim_preferred_network_get_access_technology
+mm_sim_preferred_network_set_access_technology
+mm_sim_preferred_network_free
+<SUBSECTION Private>
+mm_sim_preferred_network_new_from_variant
+mm_sim_preferred_network_get_tuple
+mm_sim_preferred_network_list_get_variant
+mm_sim_preferred_network_list_new_from_variant
+mm_sim_preferred_network_list_copy
+mm_sim_preferred_network_list_free
+<SUBSECTION Standard>
+MM_TYPE_SIM_PREFERRED_NETWORK
+mm_sim_preferred_network_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-sim</FILE>
<TITLE>MMSim</TITLE>
MMSim
<SUBSECTION Getters>
mm_sim_get_path
mm_sim_dup_path
+mm_sim_get_active
mm_sim_get_identifier
mm_sim_dup_identifier
mm_sim_get_imsi
mm_sim_dup_imsi
+mm_sim_get_eid
+mm_sim_dup_eid
mm_sim_get_operator_identifier
mm_sim_dup_operator_identifier
mm_sim_get_operator_name
mm_sim_dup_operator_name
+mm_sim_get_emergency_numbers
+mm_sim_dup_emergency_numbers
+mm_sim_get_preferred_networks
<SUBSECTION Methods>
mm_sim_send_pin
mm_sim_send_pin_finish
@@ -1028,6 +1374,9 @@ mm_sim_disable_pin_sync
mm_sim_change_pin
mm_sim_change_pin_finish
mm_sim_change_pin_sync
+mm_sim_set_preferred_networks
+mm_sim_set_preferred_networks_finish
+mm_sim_set_preferred_networks_sync
<SUBSECTION Standard>
MMSimClass
MM_IS_SIM
@@ -1134,11 +1483,218 @@ mm_sms_properties_get_type
</SECTION>
<SECTION>
+<FILE>mm-call</FILE>
+<TITLE>MMCall</TITLE>
+MMCall
+<SUBSECTION Getters>
+mm_call_get_path
+mm_call_dup_path
+mm_call_get_number
+mm_call_dup_number
+mm_call_get_direction
+mm_call_get_state
+mm_call_get_state_reason
+mm_call_get_audio_port
+mm_call_dup_audio_port
+mm_call_get_audio_format
+mm_call_peek_audio_format
+mm_call_get_multiparty
+<SUBSECTION Methods>
+mm_call_start
+mm_call_start_finish
+mm_call_start_sync
+mm_call_accept
+mm_call_accept_finish
+mm_call_accept_sync
+mm_call_hangup
+mm_call_hangup_finish
+mm_call_hangup_sync
+mm_call_send_dtmf
+mm_call_send_dtmf_finish
+mm_call_send_dtmf_sync
+mm_call_deflect
+mm_call_deflect_finish
+mm_call_deflect_sync
+mm_call_join_multiparty
+mm_call_join_multiparty_finish
+mm_call_join_multiparty_sync
+mm_call_leave_multiparty
+mm_call_leave_multiparty_finish
+mm_call_leave_multiparty_sync
+<SUBSECTION Standard>
+MMCallClass
+MMCallPrivate
+MM_IS_CALL
+MM_IS_CALL_CLASS
+MM_CALL
+MM_CALL_CLASS
+MM_CALL_GET_CLASS
+MM_TYPE_CALL
+mm_call_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mm-call-properties</FILE>
+<TITLE>MMCallProperties</TITLE>
+MMCallProperties
+<SUBSECTION New>
+mm_call_properties_new
+<SUBSECTION GettersSetters>
+mm_call_properties_get_number
+mm_call_properties_set_number
+<SUBSECTION Private>
+mm_call_properties_get_dictionary
+mm_call_properties_dup
+mm_call_properties_new_from_dictionary
+mm_call_properties_new_from_string
+<SUBSECTION Standard>
+MMCallPropertiesClass
+MMCallPropertiesPrivate
+MM_IS_CALL_PROPERTIES
+MM_IS_CALL_PROPERTIES_CLASS
+MM_CALL_PROPERTIES
+MM_CALL_PROPERTIES_CLASS
+MM_CALL_PROPERTIES_GET_CLASS
+MM_TYPE_CALL_PROPERTIES
+mm_call_properties_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mm-pco</FILE>
+<TITLE>MMPco</TITLE>
+MMPco
+<SUBSECTION GettersSetters>
+mm_pco_get_session_id
+mm_pco_is_complete
+mm_pco_get_data
+<SUBSECTION Private>
+mm_pco_new
+mm_pco_from_variant
+mm_pco_to_variant
+mm_pco_set_session_id
+mm_pco_set_complete
+mm_pco_set_data
+mm_pco_list_add
+<SUBSECTION Standard>
+MMPcoClass
+MMPcoPrivate
+MM_IS_PCO
+MM_IS_PCO_CLASS
+MM_PCO
+MM_PCO_CLASS
+MM_PCO_GET_CLASS
+MM_TYPE_PCO
+mm_pco_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mm-call-audio-format</FILE>
+<TITLE>MMCallAudioFormat</TITLE>
+MMCallAudioFormat
+<SUBSECTION Getters>
+mm_call_audio_format_get_encoding
+mm_call_audio_format_get_resolution
+mm_call_audio_format_get_rate
+<SUBSECTION Private>
+mm_call_audio_format_get_dictionary
+mm_call_audio_format_new
+mm_call_audio_format_new_from_dictionary
+mm_call_audio_format_set_encoding
+mm_call_audio_format_set_resolution
+mm_call_audio_format_set_rate
+mm_call_audio_format_dup
+<SUBSECTION Standard>
+MMCallAudioFormatClass
+MMCallAudioFormatPrivate
+MM_CALL_AUDIO_FORMAT
+MM_CALL_AUDIO_FORMAT_CLASS
+MM_CALL_AUDIO_FORMAT_GET_CLASS
+MM_IS_CALL_AUDIO_FORMAT
+MM_IS_CALL_AUDIO_FORMAT_CLASS
+MM_TYPE_CALL_AUDIO_FORMAT
+mm_call_audio_format_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mm-3gpp-profile</FILE>
+<TITLE>3GPP profile</TITLE>
+MM_3GPP_PROFILE_ID_UNKNOWN
+MM3gppProfile
+mm_3gpp_profile_new
+<SUBSECTION GettersSetters>
+mm_3gpp_profile_get_apn
+mm_3gpp_profile_set_apn
+mm_3gpp_profile_get_apn_type
+mm_3gpp_profile_set_apn_type
+mm_3gpp_profile_get_allowed_auth
+mm_3gpp_profile_set_allowed_auth
+mm_3gpp_profile_get_user
+mm_3gpp_profile_set_user
+mm_3gpp_profile_get_password
+mm_3gpp_profile_set_password
+mm_3gpp_profile_get_ip_type
+mm_3gpp_profile_set_ip_type
+mm_3gpp_profile_get_profile_id
+mm_3gpp_profile_set_profile_id
+mm_3gpp_profile_get_profile_name
+mm_3gpp_profile_set_profile_name
+<SUBSECTION Private>
+mm_3gpp_profile_new_from_dictionary
+mm_3gpp_profile_new_from_string
+mm_3gpp_profile_consume_string
+mm_3gpp_profile_consume_variant
+mm_3gpp_profile_get_dictionary
+MM3gppProfileCmpFlags
+mm_3gpp_profile_cmp
+<SUBSECTION Standard>
+MM3gppProfileClass
+MM3gppProfilePrivate
+MM_3GPP_PROFILE
+MM_3GPP_PROFILE_CLASS
+MM_3GPP_PROFILE_GET_CLASS
+MM_IS_3GPP_PROFILE
+MM_IS_3GPP_PROFILE_CLASS
+MM_TYPE_3GPP_PROFILE
+mm_3gpp_profile_get_type
+</SECTION>
+
+<SECTION>
+<FILE>mm-modem-3gpp-profile-manager</FILE>
+<TITLE>MMModem3gppProfileManager</TITLE>
+MMModem3gppProfileManager
+<SUBSECTION Getters>
+mm_modem_3gpp_profile_manager_get_path
+mm_modem_3gpp_profile_manager_dup_path
+<SUBSECTION Methods>
+mm_modem_3gpp_profile_manager_list
+mm_modem_3gpp_profile_manager_list_finish
+mm_modem_3gpp_profile_manager_list_sync
+mm_modem_3gpp_profile_manager_set
+mm_modem_3gpp_profile_manager_set_finish
+mm_modem_3gpp_profile_manager_set_sync
+mm_modem_3gpp_profile_manager_delete
+mm_modem_3gpp_profile_manager_delete_finish
+mm_modem_3gpp_profile_manager_delete_sync
+<SUBSECTION Standard>
+MMModem3gppProfileManagerClass
+MM_IS_MODEM_3GPP_PROFILE_MANAGER
+MM_IS_MODEM_3GPP_PROFILE_MANAGER_CLASS
+MM_MODEM_3GPP_PROFILE_MANAGER
+MM_MODEM_3GPP_PROFILE_MANAGER_CLASS
+MM_MODEM_3GPP_PROFILE_MANAGER_GET_CLASS
+MM_TYPE_MODEM_3GPP_PROFILE_MANAGER
+mm_modem_3gpp_profile_manager_get_type
+</SECTION>
+
+<SECTION>
<FILE>mm-enums-types</FILE>
<TITLE>Flags and Enumerations</TITLE>
+mm_bearer_type_get_string
mm_bearer_ip_method_get_string
mm_bearer_ip_family_get_string
mm_bearer_allowed_auth_build_string_from_mask
+mm_bearer_multiplex_support_get_string
+mm_bearer_apn_type_build_string_from_mask
mm_modem_capability_build_string_from_mask
mm_modem_state_get_string
mm_modem_state_failed_reason_get_string
@@ -1154,11 +1710,15 @@ mm_modem_3gpp_subscription_state_get_string
mm_modem_3gpp_facility_build_string_from_mask
mm_modem_3gpp_network_availability_get_string
mm_modem_3gpp_ussd_session_state_get_string
+mm_modem_3gpp_eps_ue_mode_operation_get_string
+mm_modem_3gpp_packet_service_state_get_string
mm_modem_cdma_registration_state_get_string
mm_modem_cdma_activation_state_get_string
mm_modem_cdma_rm_protocol_get_string
mm_modem_location_source_build_string_from_mask
+mm_modem_location_assistance_data_type_build_string_from_mask
mm_modem_contacts_storage_get_string
+mm_modem_firmware_update_method_build_string_from_mask
mm_sms_pdu_type_get_string
mm_sms_state_get_string
mm_sms_delivery_state_get_string
@@ -1171,6 +1731,9 @@ mm_oma_feature_build_string_from_mask
mm_oma_session_type_get_string
mm_oma_session_state_get_string
mm_oma_session_state_failed_reason_get_string
+mm_call_direction_get_string
+mm_call_state_get_string
+mm_call_state_reason_get_string
<SUBSECTION Private>
mm_modem_capability_get_string
mm_modem_lock_build_string_from_mask
@@ -1189,34 +1752,49 @@ mm_sms_validity_type_build_string_from_mask
mm_sms_cdma_teleservice_id_build_string_from_mask
mm_sms_cdma_service_category_build_string_from_mask
mm_modem_location_source_get_string
+mm_modem_location_assistance_data_type_get_string
mm_modem_contacts_storage_build_string_from_mask
+mm_bearer_type_build_string_from_mask
mm_bearer_ip_family_build_string_from_mask
mm_bearer_ip_method_build_string_from_mask
mm_bearer_allowed_auth_get_string
+mm_bearer_multiplex_support_build_string_from_mask
+mm_bearer_apn_type_get_string
mm_modem_cdma_registration_state_build_string_from_mask
mm_modem_cdma_activation_state_build_string_from_mask
mm_modem_cdma_rm_protocol_build_string_from_mask
mm_modem_3gpp_registration_state_build_string_from_mask
+mm_modem_3gpp_packet_service_state_build_string_from_mask
mm_modem_3gpp_subscription_state_build_string_from_mask
mm_modem_3gpp_facility_get_string
mm_modem_3gpp_network_availability_build_string_from_mask
mm_modem_3gpp_ussd_session_state_build_string_from_mask
+mm_modem_3gpp_eps_ue_mode_operation_build_string_from_mask
mm_firmware_image_type_build_string_from_mask
mm_modem_port_type_build_string_from_mask
mm_oma_feature_get_string
mm_oma_session_type_build_string_from_mask
mm_oma_session_state_build_string_from_mask
mm_oma_session_state_failed_reason_build_string_from_mask
+mm_call_direction_build_string_from_mask
+mm_call_state_build_string_from_mask
+mm_call_state_reason_build_string_from_mask
+mm_modem_firmware_update_method_get_string
<SUBSECTION Standard>
+MM_TYPE_BEARER_TYPE
MM_TYPE_BEARER_IP_FAMILY
MM_TYPE_BEARER_IP_METHOD
MM_TYPE_BEARER_ALLOWED_AUTH
+MM_TYPE_BEARER_MULTIPLEX_SUPPORT
+MM_TYPE_BEARER_APN_TYPE
MM_TYPE_FIRMWARE_IMAGE_TYPE
MM_TYPE_MODEM_3GPP_FACILITY
MM_TYPE_MODEM_3GPP_NETWORK_AVAILABILITY
MM_TYPE_MODEM_3GPP_REGISTRATION_STATE
MM_TYPE_MODEM_3GPP_SUBSCRIPTION_STATE
MM_TYPE_MODEM_3GPP_USSD_SESSION_STATE
+MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION
+MM_TYPE_MODEM_3GPP_PACKET_SERVICE_STATE
MM_TYPE_MODEM_ACCESS_TECHNOLOGY
MM_TYPE_MODEM_BAND
MM_TYPE_MODEM_CAPABILITY
@@ -1225,6 +1803,7 @@ MM_TYPE_MODEM_CDMA_REGISTRATION_STATE
MM_TYPE_MODEM_CDMA_RM_PROTOCOL
MM_TYPE_MODEM_CONTACTS_STORAGE
MM_TYPE_MODEM_LOCATION_SOURCE
+MM_TYPE_MODEM_LOCATION_ASSISTANCE_DATA_TYPE
MM_TYPE_MODEM_LOCK
MM_TYPE_MODEM_MODE
MM_TYPE_MODEM_STATE
@@ -1243,15 +1822,24 @@ MM_TYPE_OMA_FEATURE
MM_TYPE_OMA_SESSION_STATE
MM_TYPE_OMA_SESSION_STATE_FAILED_REASON
MM_TYPE_OMA_SESSION_TYPE
+MM_TYPE_CALL_DIRECTION
+MM_TYPE_CALL_STATE
+MM_TYPE_CALL_STATE_REASON
+MM_TYPE_MODEM_FIRMWARE_UPDATE_METHOD
+mm_bearer_type_get_type
mm_bearer_ip_family_get_type
mm_bearer_ip_method_get_type
mm_bearer_allowed_auth_get_type
+mm_bearer_multiplex_support_get_type
+mm_bearer_apn_type_get_type
mm_firmware_image_type_get_type
mm_modem_3gpp_facility_get_type
mm_modem_3gpp_network_availability_get_type
mm_modem_3gpp_registration_state_get_type
mm_modem_3gpp_subscription_state_get_type
mm_modem_3gpp_ussd_session_state_get_type
+mm_modem_3gpp_eps_ue_mode_operation_get_type
+mm_modem_3gpp_packet_service_state_get_type
mm_modem_access_technology_get_type
mm_modem_band_get_type
mm_modem_capability_get_type
@@ -1260,6 +1848,7 @@ mm_modem_cdma_registration_state_get_type
mm_modem_cdma_rm_protocol_get_type
mm_modem_contacts_storage_get_type
mm_modem_location_source_get_type
+mm_modem_location_assistance_data_type_get_type
mm_modem_lock_get_type
mm_modem_mode_get_type
mm_modem_state_change_reason_get_type
@@ -1278,6 +1867,10 @@ mm_oma_feature_get_type
mm_oma_session_state_failed_reason_get_type
mm_oma_session_state_get_type
mm_oma_session_type_get_type
+mm_call_direction_get_type
+mm_call_state_get_type
+mm_call_state_reason_get_type
+mm_modem_firmware_update_method_get_type
</SECTION>
<SECTION>
@@ -1311,6 +1904,35 @@ mm_serial_error_get_type
</SECTION>
<SECTION>
+<FILE>mm-compat</FILE>
+<TITLE>Deprecated Interface</TITLE>
+<SUBSECTION SimpleConnectProperties>
+mm_simple_connect_properties_get_number
+mm_simple_connect_properties_set_number
+<SUBSECTION BearerProperties>
+mm_bearer_properties_get_number
+mm_bearer_properties_set_number
+<SUBSECTION CallProperties>
+mm_call_properties_get_direction
+mm_call_properties_set_direction
+mm_call_properties_get_state
+mm_call_properties_set_state
+mm_call_properties_get_state_reason
+mm_call_properties_set_state_reason
+<SUBSECTION LocationGpsNmea>
+mm_location_gps_nmea_build_full
+<SUBSECTION Pco>
+mm_pco_list_free
+<SUBSECTION SimpleStatus>
+mm_simple_status_get_3gpp_subscription_state
+<SUBSECTION Modem3gpp>
+mm_modem_3gpp_get_subscription_state
+<SUBSECTION ModemOma>
+mm_modem_peek_pending_network_initiated_sessions
+mm_modem_get_pending_network_initiated_sessions
+</SECTION>
+
+<SECTION>
<FILE>MmGdbusBearer</FILE>
<TITLE>MmGdbusBearer</TITLE>
MmGdbusBearer
@@ -1326,7 +1948,14 @@ mm_gdbus_bearer_get_ip_timeout
mm_gdbus_bearer_get_properties
mm_gdbus_bearer_dup_properties
mm_gdbus_bearer_get_connected
+mm_gdbus_bearer_get_connection_error
+mm_gdbus_bearer_dup_connection_error
mm_gdbus_bearer_get_suspended
+mm_gdbus_bearer_get_multiplexed
+mm_gdbus_bearer_get_bearer_type
+mm_gdbus_bearer_get_profile_id
+mm_gdbus_bearer_get_stats
+mm_gdbus_bearer_dup_stats
<SUBSECTION Methods>
mm_gdbus_bearer_call_connect
mm_gdbus_bearer_call_connect_finish
@@ -1337,12 +1966,17 @@ mm_gdbus_bearer_call_disconnect_sync
<SUBSECTION Private>
mm_gdbus_bearer_interface_info
mm_gdbus_bearer_set_connected
+mm_gdbus_bearer_set_connection_error
mm_gdbus_bearer_set_interface
mm_gdbus_bearer_set_ip4_config
mm_gdbus_bearer_set_ip6_config
mm_gdbus_bearer_set_ip_timeout
mm_gdbus_bearer_set_properties
mm_gdbus_bearer_set_suspended
+mm_gdbus_bearer_set_bearer_type
+mm_gdbus_bearer_set_profile_id
+mm_gdbus_bearer_set_stats
+mm_gdbus_bearer_set_multiplexed
mm_gdbus_bearer_override_properties
mm_gdbus_bearer_complete_connect
mm_gdbus_bearer_complete_disconnect
@@ -1401,16 +2035,27 @@ mm_gdbus_bearer_skeleton_get_type
MmGdbusOrgFreedesktopModemManager1
MmGdbusOrgFreedesktopModemManager1Iface
<SUBSECTION Methods>
+mm_gdbus_org_freedesktop_modem_manager1_dup_version
+mm_gdbus_org_freedesktop_modem_manager1_get_version
mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices
mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish
mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_sync
+mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device
+mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_finish
+mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_sync
mm_gdbus_org_freedesktop_modem_manager1_call_set_logging
mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish
mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_sync
+mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event
+mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_finish
+mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync
<SUBSECTION Private>
+mm_gdbus_org_freedesktop_modem_manager1_set_version
mm_gdbus_org_freedesktop_modem_manager1_override_properties
+mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device
mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices
mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging
+mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event
mm_gdbus_org_freedesktop_modem_manager1_interface_info
<SUBSECTION Standard>
MM_GDBUS_IS_ORG_FREEDESKTOP_MODEM_MANAGER1
@@ -1476,6 +2121,14 @@ mm_gdbus_modem3gpp_get_operator_name
mm_gdbus_modem3gpp_dup_operator_name
mm_gdbus_modem3gpp_get_enabled_facility_locks
mm_gdbus_modem3gpp_get_subscription_state
+mm_gdbus_modem3gpp_get_eps_ue_mode_operation
+mm_gdbus_modem3gpp_get_pco
+mm_gdbus_modem3gpp_dup_pco
+mm_gdbus_modem3gpp_get_initial_eps_bearer
+mm_gdbus_modem3gpp_dup_initial_eps_bearer
+mm_gdbus_modem3gpp_get_initial_eps_bearer_settings
+mm_gdbus_modem3gpp_dup_initial_eps_bearer_settings
+mm_gdbus_modem3gpp_get_packet_service_state
<SUBSECTION Methods>
mm_gdbus_modem3gpp_call_register
mm_gdbus_modem3gpp_call_register_finish
@@ -1483,9 +2136,25 @@ mm_gdbus_modem3gpp_call_register_sync
mm_gdbus_modem3gpp_call_scan
mm_gdbus_modem3gpp_call_scan_finish
mm_gdbus_modem3gpp_call_scan_sync
+mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation
+mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_finish
+mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_sync
+mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings
+mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_finish
+mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_sync
+mm_gdbus_modem3gpp_call_disable_facility_lock
+mm_gdbus_modem3gpp_call_disable_facility_lock_finish
+mm_gdbus_modem3gpp_call_disable_facility_lock_sync
+mm_gdbus_modem3gpp_call_set_packet_service_state
+mm_gdbus_modem3gpp_call_set_packet_service_state_finish
+mm_gdbus_modem3gpp_call_set_packet_service_state_sync
<SUBSECTION Private>
mm_gdbus_modem3gpp_complete_register
mm_gdbus_modem3gpp_complete_scan
+mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation
+mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings
+mm_gdbus_modem3gpp_complete_disable_facility_lock
+mm_gdbus_modem3gpp_complete_set_packet_service_state
mm_gdbus_modem3gpp_interface_info
mm_gdbus_modem3gpp_override_properties
mm_gdbus_modem3gpp_set_enabled_facility_locks
@@ -1494,6 +2163,11 @@ mm_gdbus_modem3gpp_set_operator_code
mm_gdbus_modem3gpp_set_operator_name
mm_gdbus_modem3gpp_set_registration_state
mm_gdbus_modem3gpp_set_subscription_state
+mm_gdbus_modem3gpp_set_eps_ue_mode_operation
+mm_gdbus_modem3gpp_set_pco
+mm_gdbus_modem3gpp_set_initial_eps_bearer
+mm_gdbus_modem3gpp_set_initial_eps_bearer_settings
+mm_gdbus_modem3gpp_set_packet_service_state
<SUBSECTION Standard>
MM_GDBUS_IS_MODEM3GPP
MM_GDBUS_MODEM3GPP
@@ -1623,6 +2297,77 @@ mm_gdbus_modem3gpp_ussd_skeleton_get_type
</SECTION>
<SECTION>
+<FILE>MmGdbusModem3gppProfileManager</FILE>
+<TITLE>MmGdbusModem3gppProfileManager</TITLE>
+MmGdbusModem3gppProfileManager
+MmGdbusModem3gppProfileManagerIface
+<SUBSECTION Methods>
+mm_gdbus_modem3gpp_profile_manager_call_delete
+mm_gdbus_modem3gpp_profile_manager_call_delete_finish
+mm_gdbus_modem3gpp_profile_manager_call_delete_sync
+mm_gdbus_modem3gpp_profile_manager_call_list
+mm_gdbus_modem3gpp_profile_manager_call_list_finish
+mm_gdbus_modem3gpp_profile_manager_call_list_sync
+mm_gdbus_modem3gpp_profile_manager_call_set
+mm_gdbus_modem3gpp_profile_manager_call_set_finish
+mm_gdbus_modem3gpp_profile_manager_call_set_sync
+<SUBSECTION Private>
+mm_gdbus_modem3gpp_profile_manager_emit_updated
+mm_gdbus_modem3gpp_profile_manager_complete_delete
+mm_gdbus_modem3gpp_profile_manager_complete_list
+mm_gdbus_modem3gpp_profile_manager_complete_set
+mm_gdbus_modem3gpp_profile_manager_interface_info
+mm_gdbus_modem3gpp_profile_manager_override_properties
+<SUBSECTION Standard>
+MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_GET_IFACE
+MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER
+mm_gdbus_modem3gpp_profile_manager_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModem3gppProfileManagerProxy</FILE>
+<TITLE>MmGdbusModem3gppProfileManagerProxy</TITLE>
+MmGdbusModem3gppProfileManagerProxy
+<SUBSECTION New>
+mm_gdbus_modem3gpp_profile_manager_proxy_new
+mm_gdbus_modem3gpp_profile_manager_proxy_new_finish
+mm_gdbus_modem3gpp_profile_manager_proxy_new_for_bus
+mm_gdbus_modem3gpp_profile_manager_proxy_new_for_bus_finish
+mm_gdbus_modem3gpp_profile_manager_proxy_new_for_bus_sync
+mm_gdbus_modem3gpp_profile_manager_proxy_new_sync
+<SUBSECTION Standard>
+MmGdbusModem3gppProfileManagerProxyClass
+MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_PROXY
+MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_PROXY_CLASS
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_PROXY
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_PROXY_CLASS
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_PROXY_GET_CLASS
+MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_PROXY
+MmGdbusModem3gppProfileManagerProxyPrivate
+mm_gdbus_modem3gpp_profile_manager_proxy_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModem3gppProfileManagerSkeleton</FILE>
+<TITLE>MmGdbusModem3gppProfileManagerSkeleton</TITLE>
+MmGdbusModem3gppProfileManagerSkeleton
+<SUBSECTION New>
+mm_gdbus_modem3gpp_profile_manager_skeleton_new
+<SUBSECTION Standard>
+MmGdbusModem3gppProfileManagerSkeletonClass
+MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_SKELETON
+MM_GDBUS_IS_MODEM3GPP_PROFILE_MANAGER_SKELETON_CLASS
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_SKELETON
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_SKELETON_CLASS
+MM_GDBUS_MODEM3GPP_PROFILE_MANAGER_SKELETON_GET_CLASS
+MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_SKELETON
+MmGdbusModem3gppProfileManagerSkeletonPrivate
+mm_gdbus_modem3gpp_profile_manager_skeleton_get_type
+</SECTION>
+
+<SECTION>
<FILE>MmGdbusModem</FILE>
<TITLE>MmGdbusModem</TITLE>
MmGdbusModem
@@ -1647,6 +2392,7 @@ mm_gdbus_modem_dup_equipment_identifier
mm_gdbus_modem_get_manufacturer
mm_gdbus_modem_dup_manufacturer
mm_gdbus_modem_get_max_active_bearers
+mm_gdbus_modem_get_max_active_multiplexed_bearers
mm_gdbus_modem_get_max_bearers
mm_gdbus_modem_get_model
mm_gdbus_modem_dup_model
@@ -1661,10 +2407,19 @@ mm_gdbus_modem_get_ports
mm_gdbus_modem_dup_ports
mm_gdbus_modem_get_revision
mm_gdbus_modem_dup_revision
+mm_gdbus_modem_get_carrier_configuration
+mm_gdbus_modem_dup_carrier_configuration
+mm_gdbus_modem_get_carrier_configuration_revision
+mm_gdbus_modem_dup_carrier_configuration_revision
+mm_gdbus_modem_get_hardware_revision
+mm_gdbus_modem_dup_hardware_revision
mm_gdbus_modem_get_signal_quality
mm_gdbus_modem_dup_signal_quality
mm_gdbus_modem_get_sim
mm_gdbus_modem_dup_sim
+mm_gdbus_modem_dup_sim_slots
+mm_gdbus_modem_get_sim_slots
+mm_gdbus_modem_get_primary_sim_slot
mm_gdbus_modem_get_supported_capabilities
mm_gdbus_modem_dup_supported_capabilities
mm_gdbus_modem_get_state
@@ -1708,6 +2463,9 @@ mm_gdbus_modem_call_set_current_bands_sync
mm_gdbus_modem_call_set_current_capabilities
mm_gdbus_modem_call_set_current_capabilities_finish
mm_gdbus_modem_call_set_current_capabilities_sync
+mm_gdbus_modem_call_set_primary_sim_slot
+mm_gdbus_modem_call_set_primary_sim_slot_finish
+mm_gdbus_modem_call_set_primary_sim_slot_sync
mm_gdbus_modem_call_command
mm_gdbus_modem_call_command_finish
mm_gdbus_modem_call_command_sync
@@ -1723,6 +2481,7 @@ mm_gdbus_modem_set_drivers
mm_gdbus_modem_set_equipment_identifier
mm_gdbus_modem_set_manufacturer
mm_gdbus_modem_set_max_active_bearers
+mm_gdbus_modem_set_max_active_multiplexed_bearers
mm_gdbus_modem_set_max_bearers
mm_gdbus_modem_set_model
mm_gdbus_modem_set_own_numbers
@@ -1730,8 +2489,13 @@ mm_gdbus_modem_set_plugin
mm_gdbus_modem_set_primary_port
mm_gdbus_modem_set_ports
mm_gdbus_modem_set_revision
+mm_gdbus_modem_set_carrier_configuration
+mm_gdbus_modem_set_carrier_configuration_revision
+mm_gdbus_modem_set_hardware_revision
mm_gdbus_modem_set_signal_quality
mm_gdbus_modem_set_sim
+mm_gdbus_modem_set_sim_slots
+mm_gdbus_modem_set_primary_sim_slot
mm_gdbus_modem_set_supported_capabilities
mm_gdbus_modem_set_state
mm_gdbus_modem_set_state_failed_reason
@@ -1753,6 +2517,7 @@ mm_gdbus_modem_complete_reset
mm_gdbus_modem_complete_set_current_modes
mm_gdbus_modem_complete_set_current_bands
mm_gdbus_modem_complete_set_current_capabilities
+mm_gdbus_modem_complete_set_primary_sim_slot
mm_gdbus_modem_interface_info
mm_gdbus_modem_override_properties
<SUBSECTION Standard>
@@ -1902,6 +2667,10 @@ mm_gdbus_modem_location_get_location
mm_gdbus_modem_location_dup_location
mm_gdbus_modem_location_dup_supl_server
mm_gdbus_modem_location_get_supl_server
+mm_gdbus_modem_location_get_gps_refresh_rate
+mm_gdbus_modem_location_get_supported_assistance_data
+mm_gdbus_modem_location_dup_assistance_data_servers
+mm_gdbus_modem_location_get_assistance_data_servers
<SUBSECTION Methods>
mm_gdbus_modem_location_call_get_location
mm_gdbus_modem_location_call_get_location_finish
@@ -1912,15 +2681,26 @@ mm_gdbus_modem_location_call_setup_sync
mm_gdbus_modem_location_call_set_supl_server
mm_gdbus_modem_location_call_set_supl_server_finish
mm_gdbus_modem_location_call_set_supl_server_sync
+mm_gdbus_modem_location_call_inject_assistance_data
+mm_gdbus_modem_location_call_inject_assistance_data_finish
+mm_gdbus_modem_location_call_inject_assistance_data_sync
+mm_gdbus_modem_location_call_set_gps_refresh_rate
+mm_gdbus_modem_location_call_set_gps_refresh_rate_finish
+mm_gdbus_modem_location_call_set_gps_refresh_rate_sync
<SUBSECTION Private>
mm_gdbus_modem_location_set_capabilities
mm_gdbus_modem_location_set_enabled
mm_gdbus_modem_location_set_location
mm_gdbus_modem_location_set_signals_location
mm_gdbus_modem_location_set_supl_server
+mm_gdbus_modem_location_set_supported_assistance_data
+mm_gdbus_modem_location_set_gps_refresh_rate
+mm_gdbus_modem_location_set_assistance_data_servers
mm_gdbus_modem_location_complete_get_location
mm_gdbus_modem_location_complete_setup
mm_gdbus_modem_location_complete_set_supl_server
+mm_gdbus_modem_location_complete_inject_assistance_data
+mm_gdbus_modem_location_complete_set_gps_refresh_rate
mm_gdbus_modem_location_interface_info
mm_gdbus_modem_location_override_properties
<SUBSECTION Standard>
@@ -2129,6 +2909,8 @@ mm_gdbus_modem_time_skeleton_get_type
MmGdbusModemFirmware
MmGdbusModemFirmwareIface
<SUBSECTION Getters>
+mm_gdbus_modem_firmware_dup_update_settings
+mm_gdbus_modem_firmware_get_update_settings
<SUBSECTION Methods>
mm_gdbus_modem_firmware_call_list
mm_gdbus_modem_firmware_call_list_finish
@@ -2137,6 +2919,7 @@ mm_gdbus_modem_firmware_call_select
mm_gdbus_modem_firmware_call_select_finish
mm_gdbus_modem_firmware_call_select_sync
<SUBSECTION Private>
+mm_gdbus_modem_firmware_set_update_settings
mm_gdbus_modem_firmware_complete_list
mm_gdbus_modem_firmware_complete_select
mm_gdbus_modem_firmware_interface_info
@@ -2352,28 +3135,39 @@ MmGdbusModemSignal
MmGdbusModemSignalIface
<SUBSECTION Getters>
mm_gdbus_modem_signal_get_rate
+mm_gdbus_modem_signal_get_error_rate_threshold
+mm_gdbus_modem_signal_get_rssi_threshold
mm_gdbus_modem_signal_get_cdma
mm_gdbus_modem_signal_get_evdo
mm_gdbus_modem_signal_get_gsm
mm_gdbus_modem_signal_get_umts
mm_gdbus_modem_signal_get_lte
+mm_gdbus_modem_signal_get_nr5g
mm_gdbus_modem_signal_dup_cdma
mm_gdbus_modem_signal_dup_evdo
mm_gdbus_modem_signal_dup_gsm
mm_gdbus_modem_signal_dup_umts
mm_gdbus_modem_signal_dup_lte
+mm_gdbus_modem_signal_dup_nr5g
<SUBSECTION Methods>
mm_gdbus_modem_signal_call_setup
mm_gdbus_modem_signal_call_setup_finish
mm_gdbus_modem_signal_call_setup_sync
+mm_gdbus_modem_signal_call_setup_thresholds
+mm_gdbus_modem_signal_call_setup_thresholds_finish
+mm_gdbus_modem_signal_call_setup_thresholds_sync
<SUBSECTION Private>
mm_gdbus_modem_signal_set_cdma
mm_gdbus_modem_signal_set_evdo
mm_gdbus_modem_signal_set_gsm
mm_gdbus_modem_signal_set_lte
+mm_gdbus_modem_signal_set_nr5g
mm_gdbus_modem_signal_set_rate
mm_gdbus_modem_signal_set_umts
+mm_gdbus_modem_signal_set_error_rate_threshold
+mm_gdbus_modem_signal_set_rssi_threshold
mm_gdbus_modem_signal_complete_setup
+mm_gdbus_modem_signal_complete_setup_thresholds
mm_gdbus_modem_signal_interface_info
mm_gdbus_modem_signal_override_properties
<SUBSECTION Standard>
@@ -2426,6 +3220,179 @@ mm_gdbus_modem_signal_skeleton_get_type
</SECTION>
<SECTION>
+<FILE>MmGdbusModemVoice</FILE>
+<TITLE>MmGdbusModemVoice</TITLE>
+MmGdbusModemVoice
+MmGdbusModemVoiceIface
+<SUBSECTION Getters>
+mm_gdbus_modem_voice_get_calls
+mm_gdbus_modem_voice_dup_calls
+mm_gdbus_modem_voice_get_emergency_only
+<SUBSECTION Methods>
+mm_gdbus_modem_voice_call_create_call
+mm_gdbus_modem_voice_call_create_call_finish
+mm_gdbus_modem_voice_call_create_call_sync
+mm_gdbus_modem_voice_call_delete_call
+mm_gdbus_modem_voice_call_delete_call_finish
+mm_gdbus_modem_voice_call_delete_call_sync
+mm_gdbus_modem_voice_call_list_calls
+mm_gdbus_modem_voice_call_list_calls_finish
+mm_gdbus_modem_voice_call_list_calls_sync
+mm_gdbus_modem_voice_call_hangup_and_accept
+mm_gdbus_modem_voice_call_hangup_and_accept_finish
+mm_gdbus_modem_voice_call_hangup_and_accept_sync
+mm_gdbus_modem_voice_call_hold_and_accept
+mm_gdbus_modem_voice_call_hold_and_accept_finish
+mm_gdbus_modem_voice_call_hold_and_accept_sync
+mm_gdbus_modem_voice_call_hangup_all
+mm_gdbus_modem_voice_call_hangup_all_finish
+mm_gdbus_modem_voice_call_hangup_all_sync
+mm_gdbus_modem_voice_call_transfer
+mm_gdbus_modem_voice_call_transfer_finish
+mm_gdbus_modem_voice_call_transfer_sync
+mm_gdbus_modem_voice_call_call_waiting_query
+mm_gdbus_modem_voice_call_call_waiting_query_finish
+mm_gdbus_modem_voice_call_call_waiting_query_sync
+mm_gdbus_modem_voice_call_call_waiting_setup
+mm_gdbus_modem_voice_call_call_waiting_setup_finish
+mm_gdbus_modem_voice_call_call_waiting_setup_sync
+<SUBSECTION Private>
+mm_gdbus_modem_voice_set_calls
+mm_gdbus_modem_voice_set_emergency_only
+mm_gdbus_modem_voice_emit_call_added
+mm_gdbus_modem_voice_emit_call_deleted
+mm_gdbus_modem_voice_complete_create_call
+mm_gdbus_modem_voice_complete_delete_call
+mm_gdbus_modem_voice_complete_list_calls
+mm_gdbus_modem_voice_complete_hangup_and_accept
+mm_gdbus_modem_voice_complete_hold_and_accept
+mm_gdbus_modem_voice_complete_hangup_all
+mm_gdbus_modem_voice_complete_transfer
+mm_gdbus_modem_voice_complete_call_waiting_query
+mm_gdbus_modem_voice_complete_call_waiting_setup
+mm_gdbus_modem_voice_interface_info
+mm_gdbus_modem_voice_override_properties
+<SUBSECTION Standard>
+MM_GDBUS_IS_MODEM_VOICE
+MM_GDBUS_MODEM_VOICE
+MM_GDBUS_MODEM_VOICE_GET_IFACE
+MM_GDBUS_TYPE_MODEM_VOICE
+mm_gdbus_modem_voice_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModemVoiceProxy</FILE>
+<TITLE>MmGdbusModemVoiceProxy</TITLE>
+MmGdbusModemVoiceProxy
+<SUBSECTION New>
+mm_gdbus_modem_voice_proxy_new
+mm_gdbus_modem_voice_proxy_new_finish
+mm_gdbus_modem_voice_proxy_new_for_bus
+mm_gdbus_modem_voice_proxy_new_for_bus_finish
+mm_gdbus_modem_voice_proxy_new_for_bus_sync
+mm_gdbus_modem_voice_proxy_new_sync
+<SUBSECTION Standard>
+MmGdbusModemVoiceProxyClass
+MM_GDBUS_IS_MODEM_VOICE_PROXY
+MM_GDBUS_IS_MODEM_VOICE_PROXY_CLASS
+MM_GDBUS_MODEM_VOICE_PROXY
+MM_GDBUS_MODEM_VOICE_PROXY_CLASS
+MM_GDBUS_MODEM_VOICE_PROXY_GET_CLASS
+MM_GDBUS_TYPE_MODEM_VOICE_PROXY
+MmGdbusModemVoiceProxyPrivate
+mm_gdbus_modem_voice_proxy_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModemVoiceSkeleton</FILE>
+<TITLE>MmGdbusModemVoiceSkeleton</TITLE>
+MmGdbusModemVoiceSkeleton
+<SUBSECTION New>
+mm_gdbus_modem_voice_skeleton_new
+<SUBSECTION Standard>
+MmGdbusModemVoiceSkeletonClass
+MM_GDBUS_IS_MODEM_VOICE_SKELETON
+MM_GDBUS_IS_MODEM_VOICE_SKELETON_CLASS
+MM_GDBUS_MODEM_VOICE_SKELETON
+MM_GDBUS_MODEM_VOICE_SKELETON_CLASS
+MM_GDBUS_MODEM_VOICE_SKELETON_GET_CLASS
+MM_GDBUS_TYPE_MODEM_VOICE_SKELETON
+MmGdbusModemVoiceSkeletonPrivate
+mm_gdbus_modem_voice_skeleton_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModemSar</FILE>
+<TITLE>MmGdbusModemSar</TITLE>
+MmGdbusModemSar
+MmGdbusModemSarIface
+<SUBSECTION Getters>
+mm_gdbus_modem_sar_get_power_level
+mm_gdbus_modem_sar_get_state
+<SUBSECTION Methods>
+mm_gdbus_modem_sar_call_enable
+mm_gdbus_modem_sar_call_enable_finish
+mm_gdbus_modem_sar_call_enable_sync
+mm_gdbus_modem_sar_call_set_power_level
+mm_gdbus_modem_sar_call_set_power_level_finish
+mm_gdbus_modem_sar_call_set_power_level_sync
+<SUBSECTION Private>
+mm_gdbus_modem_sar_set_power_level
+mm_gdbus_modem_sar_set_state
+mm_gdbus_modem_sar_interface_info
+mm_gdbus_modem_sar_override_properties
+mm_gdbus_modem_sar_complete_enable
+mm_gdbus_modem_sar_complete_set_power_level
+<SUBSECTION Standard>
+MM_GDBUS_IS_MODEM_SAR
+MM_GDBUS_MODEM_SAR
+MM_GDBUS_MODEM_SAR_GET_IFACE
+MM_GDBUS_TYPE_MODEM_SAR
+mm_gdbus_modem_sar_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModemSarProxy</FILE>
+<TITLE>MmGdbusModemSarProxy</TITLE>
+MmGdbusModemSarProxy
+<SUBSECTION New>
+mm_gdbus_modem_sar_proxy_new
+mm_gdbus_modem_sar_proxy_new_finish
+mm_gdbus_modem_sar_proxy_new_for_bus
+mm_gdbus_modem_sar_proxy_new_for_bus_finish
+mm_gdbus_modem_sar_proxy_new_for_bus_sync
+mm_gdbus_modem_sar_proxy_new_sync
+<SUBSECTION Standard>
+MmGdbusModemSarProxyClass
+MM_GDBUS_IS_MODEM_SAR_PROXY
+MM_GDBUS_IS_MODEM_SAR_PROXY_CLASS
+MM_GDBUS_MODEM_SAR_PROXY
+MM_GDBUS_MODEM_SAR_PROXY_CLASS
+MM_GDBUS_MODEM_SAR_PROXY_GET_CLASS
+MM_GDBUS_TYPE_MODEM_SAR_PROXY
+MmGdbusModemSarProxyPrivate
+mm_gdbus_modem_sar_proxy_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusModemSarSkeleton</FILE>
+<TITLE>MmGdbusModemSarSkeleton</TITLE>
+MmGdbusModemSarSkeleton
+<SUBSECTION New>
+mm_gdbus_modem_sar_skeleton_new
+<SUBSECTION Standard>
+MmGdbusModemSarSkeletonClass
+MM_GDBUS_IS_MODEM_SAR_SKELETON
+MM_GDBUS_IS_MODEM_SAR_SKELETON_CLASS
+MM_GDBUS_MODEM_SAR_SKELETON
+MM_GDBUS_MODEM_SAR_SKELETON_CLASS
+MM_GDBUS_MODEM_SAR_SKELETON_GET_CLASS
+MM_GDBUS_TYPE_MODEM_SAR_SKELETON
+MmGdbusModemSarSkeletonPrivate
+mm_gdbus_modem_sar_skeleton_get_type
+</SECTION>
+
+<SECTION>
<FILE>MmGdbusObject</FILE>
<TITLE>MmGdbusObject</TITLE>
MmGdbusObject
@@ -2437,6 +3404,8 @@ mm_gdbus_object_peek_modem3gpp
mm_gdbus_object_get_modem3gpp
mm_gdbus_object_peek_modem3gpp_ussd
mm_gdbus_object_get_modem3gpp_ussd
+mm_gdbus_object_peek_modem3gpp_profile_manager
+mm_gdbus_object_get_modem3gpp_profile_manager
mm_gdbus_object_peek_modem_cdma
mm_gdbus_object_get_modem_cdma
mm_gdbus_object_peek_modem_location
@@ -2453,6 +3422,10 @@ mm_gdbus_object_peek_modem_simple
mm_gdbus_object_get_modem_simple
mm_gdbus_object_peek_modem_signal
mm_gdbus_object_get_modem_signal
+mm_gdbus_object_peek_modem_voice
+mm_gdbus_object_get_modem_voice
+mm_gdbus_object_peek_modem_sar
+mm_gdbus_object_get_modem_sar
<SUBSECTION Methods>
<SUBSECTION Private>
<SUBSECTION Standard>
@@ -2491,6 +3464,7 @@ mm_gdbus_object_skeleton_new
mm_gdbus_object_skeleton_set_modem
mm_gdbus_object_skeleton_set_modem3gpp
mm_gdbus_object_skeleton_set_modem3gpp_ussd
+mm_gdbus_object_skeleton_set_modem3gpp_profile_manager
mm_gdbus_object_skeleton_set_modem_cdma
mm_gdbus_object_skeleton_set_modem_firmware
mm_gdbus_object_skeleton_set_modem_oma
@@ -2499,6 +3473,8 @@ mm_gdbus_object_skeleton_set_modem_messaging
mm_gdbus_object_skeleton_set_modem_simple
mm_gdbus_object_skeleton_set_modem_time
mm_gdbus_object_skeleton_set_modem_signal
+mm_gdbus_object_skeleton_set_modem_voice
+mm_gdbus_object_skeleton_set_modem_sar
<SUBSECTION Standard>
MmGdbusObjectSkeletonClass
MM_GDBUS_IS_OBJECT_SKELETON
@@ -2542,14 +3518,21 @@ mm_gdbus_object_manager_client_get_type
MmGdbusSim
MmGdbusSimIface
<SUBSECTION Getters>
+mm_gdbus_sim_get_active
mm_gdbus_sim_get_imsi
mm_gdbus_sim_dup_imsi
+mm_gdbus_sim_get_eid
+mm_gdbus_sim_dup_eid
mm_gdbus_sim_get_sim_identifier
mm_gdbus_sim_dup_sim_identifier
mm_gdbus_sim_get_operator_identifier
mm_gdbus_sim_dup_operator_identifier
mm_gdbus_sim_get_operator_name
mm_gdbus_sim_dup_operator_name
+mm_gdbus_sim_get_emergency_numbers
+mm_gdbus_sim_dup_emergency_numbers
+mm_gdbus_sim_dup_preferred_networks
+mm_gdbus_sim_get_preferred_networks
<SUBSECTION Methods>
mm_gdbus_sim_call_send_pin
mm_gdbus_sim_call_send_pin_finish
@@ -2563,15 +3546,23 @@ mm_gdbus_sim_call_enable_pin_sync
mm_gdbus_sim_call_change_pin
mm_gdbus_sim_call_change_pin_finish
mm_gdbus_sim_call_change_pin_sync
+mm_gdbus_sim_call_set_preferred_networks
+mm_gdbus_sim_call_set_preferred_networks_finish
+mm_gdbus_sim_call_set_preferred_networks_sync
<SUBSECTION Private>
+mm_gdbus_sim_set_active
mm_gdbus_sim_set_imsi
+mm_gdbus_sim_set_eid
mm_gdbus_sim_set_operator_identifier
mm_gdbus_sim_set_operator_name
mm_gdbus_sim_set_sim_identifier
+mm_gdbus_sim_set_emergency_numbers
+mm_gdbus_sim_set_preferred_networks
mm_gdbus_sim_complete_change_pin
mm_gdbus_sim_complete_enable_pin
mm_gdbus_sim_complete_send_pin
mm_gdbus_sim_complete_send_puk
+mm_gdbus_sim_complete_set_preferred_networks
mm_gdbus_sim_interface_info
mm_gdbus_sim_override_properties
<SUBSECTION Standard>
@@ -2728,3 +3719,109 @@ MM_GDBUS_TYPE_SMS_SKELETON
MmGdbusSmsSkeletonPrivate
mm_gdbus_sms_skeleton_get_type
</SECTION>
+
+<SECTION>
+<FILE>MmGdbusCall</FILE>
+<TITLE>MmGdbusCall</TITLE>
+MmGdbusCall
+MmGdbusCallIface
+<SUBSECTION Getters>
+mm_gdbus_call_get_direction
+mm_gdbus_call_get_number
+mm_gdbus_call_dup_number
+mm_gdbus_call_get_state
+mm_gdbus_call_get_state_reason
+mm_gdbus_call_dup_audio_format
+mm_gdbus_call_dup_audio_port
+mm_gdbus_call_get_audio_format
+mm_gdbus_call_get_audio_port
+mm_gdbus_call_get_multiparty
+<SUBSECTION Methods>
+mm_gdbus_call_call_accept
+mm_gdbus_call_call_accept_finish
+mm_gdbus_call_call_accept_sync
+mm_gdbus_call_call_start
+mm_gdbus_call_call_start_finish
+mm_gdbus_call_call_start_sync
+mm_gdbus_call_call_hangup
+mm_gdbus_call_call_hangup_finish
+mm_gdbus_call_call_hangup_sync
+mm_gdbus_call_call_send_dtmf
+mm_gdbus_call_call_send_dtmf_finish
+mm_gdbus_call_call_send_dtmf_sync
+mm_gdbus_call_call_deflect
+mm_gdbus_call_call_deflect_finish
+mm_gdbus_call_call_deflect_sync
+mm_gdbus_call_call_join_multiparty
+mm_gdbus_call_call_join_multiparty_finish
+mm_gdbus_call_call_join_multiparty_sync
+mm_gdbus_call_call_leave_multiparty
+mm_gdbus_call_call_leave_multiparty_finish
+mm_gdbus_call_call_leave_multiparty_sync
+<SUBSECTION Private>
+mm_gdbus_call_set_direction
+mm_gdbus_call_set_number
+mm_gdbus_call_set_state
+mm_gdbus_call_set_state_reason
+mm_gdbus_call_set_audio_format
+mm_gdbus_call_set_audio_port
+mm_gdbus_call_set_multiparty
+mm_gdbus_call_complete_accept
+mm_gdbus_call_complete_hangup
+mm_gdbus_call_complete_send_dtmf
+mm_gdbus_call_complete_start
+mm_gdbus_call_complete_deflect
+mm_gdbus_call_complete_join_multiparty
+mm_gdbus_call_complete_leave_multiparty
+mm_gdbus_call_interface_info
+mm_gdbus_call_override_properties
+mm_gdbus_call_emit_dtmf_received
+mm_gdbus_call_emit_state_changed
+<SUBSECTION Standard>
+MM_GDBUS_IS_CALL
+MM_GDBUS_CALL
+MM_GDBUS_CALL_GET_IFACE
+MM_GDBUS_TYPE_CALL
+mm_gdbus_call_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusCallProxy</FILE>
+<TITLE>MmGdbusCallProxy</TITLE>
+MmGdbusCallProxy
+<SUBSECTION New>
+mm_gdbus_call_proxy_new
+mm_gdbus_call_proxy_new_finish
+mm_gdbus_call_proxy_new_for_bus
+mm_gdbus_call_proxy_new_for_bus_finish
+mm_gdbus_call_proxy_new_for_bus_sync
+mm_gdbus_call_proxy_new_sync
+<SUBSECTION Standard>
+MmGdbusCallProxyClass
+MM_GDBUS_IS_CALL_PROXY
+MM_GDBUS_IS_CALL_PROXY_CLASS
+MM_GDBUS_CALL_PROXY
+MM_GDBUS_CALL_PROXY_CLASS
+MM_GDBUS_CALL_PROXY_GET_CLASS
+MM_GDBUS_TYPE_CALL_PROXY
+MmGdbusCallProxyPrivate
+mm_gdbus_call_proxy_get_type
+</SECTION>
+
+<SECTION>
+<FILE>MmGdbusCallSkeleton</FILE>
+<TITLE>MmGdbusCallSkeleton</TITLE>
+MmGdbusCallSkeleton
+<SUBSECTION New>
+mm_gdbus_call_skeleton_new
+<SUBSECTION Standard>
+MmGdbusCallSkeletonClass
+MM_GDBUS_IS_CALL_SKELETON
+MM_GDBUS_IS_CALL_SKELETON_CLASS
+MM_GDBUS_CALL_SKELETON
+MM_GDBUS_CALL_SKELETON_CLASS
+MM_GDBUS_CALL_SKELETON_GET_CLASS
+MM_GDBUS_TYPE_CALL_SKELETON
+MmGdbusCallSkeletonPrivate
+mm_gdbus_call_skeleton_get_type
+</SECTION>
diff --git a/docs/reference/libmm-glib/libmm-glib.types b/docs/reference/libmm-glib/libmm-glib.types
deleted file mode 100644
index 19961564..00000000
--- a/docs/reference/libmm-glib/libmm-glib.types
+++ /dev/null
@@ -1,118 +0,0 @@
-mm_bearer_allowed_auth_get_type
-mm_bearer_get_type
-mm_bearer_ip_config_get_type
-mm_bearer_ip_family_get_type
-mm_bearer_ip_method_get_type
-mm_bearer_properties_get_type
-mm_cdma_activation_error_get_type
-mm_cdma_manual_activation_properties_get_type
-mm_connection_error_get_type
-mm_core_error_get_type
-mm_firmware_image_type_get_type
-mm_firmware_properties_get_type
-mm_signal_get_type
-mm_gdbus_bearer_get_type
-mm_gdbus_bearer_proxy_get_type
-mm_gdbus_bearer_skeleton_get_type
-mm_gdbus_modem3gpp_get_type
-mm_gdbus_modem3gpp_proxy_get_type
-mm_gdbus_modem3gpp_skeleton_get_type
-mm_gdbus_modem3gpp_ussd_get_type
-mm_gdbus_modem3gpp_ussd_proxy_get_type
-mm_gdbus_modem3gpp_ussd_skeleton_get_type
-mm_gdbus_modem_cdma_get_type
-mm_gdbus_modem_cdma_proxy_get_type
-mm_gdbus_modem_cdma_skeleton_get_type
-mm_gdbus_modem_firmware_get_type
-mm_gdbus_modem_firmware_proxy_get_type
-mm_gdbus_modem_firmware_skeleton_get_type
-mm_gdbus_modem_get_type
-mm_gdbus_modem_location_get_type
-mm_gdbus_modem_location_proxy_get_type
-mm_gdbus_modem_location_skeleton_get_type
-mm_gdbus_modem_messaging_get_type
-mm_gdbus_modem_messaging_proxy_get_type
-mm_gdbus_modem_messaging_skeleton_get_type
-mm_gdbus_modem_oma_get_type
-mm_gdbus_modem_oma_proxy_get_type
-mm_gdbus_modem_oma_skeleton_get_type
-mm_gdbus_modem_proxy_get_type
-mm_gdbus_modem_signal_get_type
-mm_gdbus_modem_signal_proxy_get_type
-mm_gdbus_modem_signal_skeleton_get_type
-mm_gdbus_modem_simple_get_type
-mm_gdbus_modem_simple_proxy_get_type
-mm_gdbus_modem_simple_skeleton_get_type
-mm_gdbus_modem_skeleton_get_type
-mm_gdbus_modem_time_get_type
-mm_gdbus_modem_time_proxy_get_type
-mm_gdbus_modem_time_skeleton_get_type
-mm_gdbus_object_get_type
-mm_gdbus_object_manager_client_get_type
-mm_gdbus_object_proxy_get_type
-mm_gdbus_object_skeleton_get_type
-mm_gdbus_org_freedesktop_modem_manager1_get_type
-mm_gdbus_org_freedesktop_modem_manager1_proxy_get_type
-mm_gdbus_org_freedesktop_modem_manager1_skeleton_get_type
-mm_gdbus_sim_get_type
-mm_gdbus_sim_proxy_get_type
-mm_gdbus_sim_skeleton_get_type
-mm_gdbus_sms_get_type
-mm_gdbus_sms_proxy_get_type
-mm_gdbus_sms_skeleton_get_type
-mm_location_3gpp_get_type
-mm_location_cdma_bs_get_type
-mm_location_gps_nmea_get_type
-mm_location_gps_raw_get_type
-mm_manager_get_type
-mm_message_error_get_type
-mm_mobile_equipment_error_get_type
-mm_modem_3gpp_facility_get_type
-mm_modem_3gpp_get_type
-mm_modem_3gpp_network_availability_get_type
-mm_modem_3gpp_registration_state_get_type
-mm_modem_3gpp_subscription_state_get_type
-mm_modem_3gpp_ussd_get_type
-mm_modem_3gpp_ussd_session_state_get_type
-mm_modem_access_technology_get_type
-mm_modem_band_get_type
-mm_modem_capability_get_type
-mm_modem_cdma_activation_state_get_type
-mm_modem_cdma_get_type
-mm_modem_cdma_registration_state_get_type
-mm_modem_cdma_rm_protocol_get_type
-mm_modem_contacts_storage_get_type
-mm_modem_firmware_get_type
-mm_modem_get_type
-mm_modem_location_get_type
-mm_modem_location_source_get_type
-mm_modem_lock_get_type
-mm_modem_messaging_get_type
-mm_modem_mode_get_type
-mm_modem_oma_get_type
-mm_modem_port_type_get_type
-mm_modem_power_state_get_type
-mm_modem_signal_get_type
-mm_modem_simple_get_type
-mm_modem_state_change_reason_get_type
-mm_modem_state_failed_reason_get_type
-mm_modem_state_get_type
-mm_modem_time_get_type
-mm_network_timezone_get_type
-mm_object_get_type
-mm_oma_feature_get_type
-mm_oma_session_state_failed_reason_get_type
-mm_oma_session_state_get_type
-mm_oma_session_type_get_type
-mm_serial_error_get_type
-mm_sim_get_type
-mm_simple_connect_properties_get_type
-mm_simple_status_get_type
-mm_sms_delivery_state_get_type
-mm_sms_get_type
-mm_sms_pdu_type_get_type
-mm_sms_properties_get_type
-mm_sms_state_get_type
-mm_sms_storage_get_type
-mm_sms_validity_type_get_type
-mm_unlock_retries_get_type
diff --git a/docs/reference/libmm-glib/meson.build b/docs/reference/libmm-glib/meson.build
new file mode 100644
index 00000000..2f1eb137
--- /dev/null
+++ b/docs/reference/libmm-glib/meson.build
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+doc_module = mm_glib_name
+
+src_dirs = [
+ generated_inc,
+ libmm_glib_inc,
+]
+
+private_headers = [
+ 'mm-common-helpers.h',
+ 'mm-gdbus-test.h',
+ 'mm-helpers.h',
+]
+
+scan_args = [
+ '--deprecated-guards="MM_DISABLE_DEPRECATED"',
+ '--rebuild-types',
+]
+
+mkdb_args = [
+ '--ignore-files=mm-gdbus-test.h',
+ '--ignore-files=mm-gdbus-test.c',
+]
+
+fixxref_args = [
+ '--html-dir=' + (mm_prefix / gnome.gtkdoc_html_dir(doc_module)),
+ '--extra-dir=' + mm_doc_path,
+]
+
+version_xml = configure_file(
+ input: 'version.xml.in',
+ output: '@BASENAME@',
+ configuration: version_conf,
+)
+
+gnome.gtkdoc(
+ doc_module,
+ main_xml: doc_module + '-docs.xml',
+ src_dir: src_dirs,
+ ignore_headers: private_headers,
+ include_directories: top_inc,
+ dependencies: libmm_glib_dep,
+ namespace: 'mm',
+ scan_args: scan_args,
+ mkdb_args: mkdb_args,
+ fixxref_args: fixxref_args,
+ html_assets: logos_pngs,
+ content_files: version_xml,
+ install: true,
+)
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 53b0b70e..927d3a9c 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -1 +1,7 @@
-SUBDIRS = modem-watcher-python modem-watcher-javascript
+SUBDIRS = \
+ modem-watcher-python \
+ modem-watcher-javascript \
+ sms-python \
+ network-scan-python \
+ sms-c \
+ $(NULL)
diff --git a/examples/modem-watcher-javascript/modemWatcher.js b/examples/modem-watcher-javascript/modemWatcher.js
index f97ff483..278e335d 100644
--- a/examples/modem-watcher-javascript/modemWatcher.js
+++ b/examples/modem-watcher-javascript/modemWatcher.js
@@ -49,7 +49,7 @@ const ModemWatcher = new Lang.Class({
_ModemManagerNameOwnerChanged: function() {
if (this._manager.name_owner)
- print('[ModemWatcher] ModemManager service is available in bus');
+ print('[ModemWatcher] ModemManager ' + this._manager.get_version() + ' service is available in bus');
else
print('[ModemWatcher] ModemManager service not available in bus');
},
diff --git a/examples/modem-watcher-python/ModemWatcher.py b/examples/modem-watcher-python/ModemWatcher.py
index 2a9d7390..b7cf4e7c 100644
--- a/examples/modem-watcher-python/ModemWatcher.py
+++ b/examples/modem-watcher-python/ModemWatcher.py
@@ -18,27 +18,25 @@
# Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
#
-import os
-import sys
+import gi
+gi.require_version('ModemManager', '1.0')
+from gi.repository import Gio, GLib, GObject, ModemManager
-from gi.repository import GLib, GObject, Gio, ModemManager
-"""
-The ModemWatcher class is responsible for monitoring ModemManager
-"""
class ModemWatcher:
-
"""
- Constructor
+ The ModemWatcher class is responsible for monitoring ModemManager.
"""
+
def __init__(self):
# Flag for initial logs
self.initializing = True
# Setup DBus monitoring
- self.connection = Gio.bus_get_sync (Gio.BusType.SYSTEM, None)
- self.manager = ModemManager.Manager.new_sync (self.connection,
- Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START,
- None)
+ self.connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ self.manager = ModemManager.Manager.new_sync(
+ self.connection,
+ Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START,
+ None)
# IDs for added/removed signals
self.object_added_id = 0
self.object_removed_id = 0
@@ -49,25 +47,25 @@ class ModemWatcher:
# Finish initialization
self.initializing = False
- """
- ModemManager is now available
- """
def set_available(self):
- if self.available == False or self.initializing == True:
- print('[ModemWatcher] ModemManager service is available in bus')
+ """
+ ModemManager is now available.
+ """
+ if not self.available or self.initializing:
+ print('[ModemWatcher] ModemManager %s service is available in bus' % self.manager.get_version())
self.object_added_id = self.manager.connect('object-added', self.on_object_added)
self.object_removed_id = self.manager.connect('object-removed', self.on_object_removed)
self.available = True
# Initial scan
- if self.initializing == True:
+ if self.initializing:
for obj in self.manager.get_objects():
self.on_object_added(self.manager, obj)
- """
- ModemManager is now unavailable
- """
def set_unavailable(self):
- if self.available == True or self.initializing == True:
+ """
+ ModemManager is now unavailable.
+ """
+ if self.available or self.initializing:
print('[ModemWatcher] ModemManager service not available in bus')
if self.object_added_id:
self.manager.disconnect(self.object_added_id)
@@ -77,32 +75,44 @@ class ModemWatcher:
self.object_removed_id = 0
self.available = False
- """
- Name owner updates
- """
def on_name_owner(self, manager, prop):
+ """
+ Name owner updates.
+ """
if self.manager.get_name_owner():
self.set_available()
else:
self.set_unavailable()
- """
- Object added
- """
+ def on_modem_state_updated(self, modem, old, new, reason):
+ """
+ Modem state updated
+ """
+ print('[ModemWatcher] %s: modem state updated: %s -> %s (%s) ' %
+ (modem.get_object_path(),
+ ModemManager.ModemState.get_string (old),
+ ModemManager.ModemState.get_string (new),
+ ModemManager.ModemStateChangeReason.get_string (reason)))
+
def on_object_added(self, manager, obj):
+ """
+ Object added.
+ """
modem = obj.get_modem()
- print('[ModemWatcher] %s (%s) modem managed by ModemManager [%s]: %s' %
- (modem.get_manufacturer(),
- modem.get_model(),
+ print('[ModemWatcher] %s: modem managed by ModemManager [%s]: %s (%s)' %
+ (obj.get_object_path(),
modem.get_equipment_identifier(),
- obj.get_object_path()))
+ modem.get_manufacturer(),
+ modem.get_model()))
if modem.get_state() == ModemManager.ModemState.FAILED:
- print('[ModemWatcher,%s] ignoring failed modem' %
- modem_index(obj.get_object_path()))
+ print('[ModemWatcher] %s: ignoring failed modem' %
+ obj.get_object_path())
+ else:
+ modem.connect('state-changed', self.on_modem_state_updated)
- """
- Object removed
- """
def on_object_removed(self, manager, obj):
- print('[ModemWatcher] modem unmanaged by ModemManager: %s' %
+ """
+ Object removed.
+ """
+ print('[ModemWatcher] %s: modem unmanaged by ModemManager' %
obj.get_object_path())
diff --git a/examples/modem-watcher-python/modem-watcher-python b/examples/modem-watcher-python/modem-watcher-python
index 40998588..f7dd1fc6 100755
--- a/examples/modem-watcher-python/modem-watcher-python
+++ b/examples/modem-watcher-python/modem-watcher-python
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
#
# This program is free software; you can redistribute it and/or modify it under
@@ -18,25 +18,34 @@
# Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
#
-import sys, signal
-import ModemWatcher
+import signal
+
from gi.repository import GLib
-main_loop = None
-watcher = None
+import ModemWatcher
-def signal_handler(data):
- main_loop.quit()
-if __name__ == "__main__":
+def signal_handler(loop):
+ """SIGHUP and SIGINT handler."""
+ loop.quit()
+
+
+def main():
+ """Main routine."""
# Create modem watcher
- watcher = ModemWatcher.ModemWatcher()
+ ModemWatcher.ModemWatcher()
# Main loop
main_loop = GLib.MainLoop()
- GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGHUP, signal_handler, None)
- GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, signal_handler, None)
+ GLib.unix_signal_add(
+ GLib.PRIORITY_HIGH, signal.SIGHUP, signal_handler, main_loop)
+ GLib.unix_signal_add(
+ GLib.PRIORITY_HIGH, signal.SIGTERM, signal_handler, main_loop)
try:
main_loop.run()
except KeyboardInterrupt:
pass
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/network-scan-python/Makefile.am b/examples/network-scan-python/Makefile.am
new file mode 100644
index 00000000..70bdfb2b
--- /dev/null
+++ b/examples/network-scan-python/Makefile.am
@@ -0,0 +1,4 @@
+
+EXTRA_DIST = \
+ network-scan-python \
+ $(NULL)
diff --git a/examples/network-scan-python/README b/examples/network-scan-python/README
new file mode 100644
index 00000000..08e5e212
--- /dev/null
+++ b/examples/network-scan-python/README
@@ -0,0 +1,17 @@
+
+The network-scan-python program makes use of the 'libmm-glib' library through
+GObject Introspection to talk to ModemManager.
+
+The program will:
+ * Detect whether ModemManager is found in the bus
+ * Loop through each modem found in the system, running a network scan for each
+
+The output will look like this:
+
+$ ./network-scan-python
+
+
+Note that the program requires ModemManager and libmm-glib to be installed in
+the system and the introspection typelibs available in the standard paths.
+
+Have fun! \ No newline at end of file
diff --git a/examples/network-scan-python/network-scan-python b/examples/network-scan-python/network-scan-python
new file mode 100755
index 00000000..21dd89f4
--- /dev/null
+++ b/examples/network-scan-python/network-scan-python
@@ -0,0 +1,74 @@
+#!/usr/bin/env python3
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+#
+
+import sys
+import time
+
+import gi
+gi.require_version('ModemManager', '1.0')
+from gi.repository import Gio, GLib, GObject, ModemManager
+
+
+def main():
+ """Main routine."""
+
+ # Process input arguments
+ if len(sys.argv) != 1:
+ sys.stderr.write('error: wrong number of arguments\n')
+ sys.stdout.write('usage: network-scan-python\n')
+ sys.exit(1)
+
+ # Connection to ModemManager
+ connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ manager = ModemManager.Manager.new_sync(
+ connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None)
+ if not manager.get_name_owner():
+ sys.stderr.write('ModemManager not found in bus\n')
+ sys.exit(2)
+
+ # Iterate modems and scan network with each one by one
+ for obj in manager.get_objects():
+ modem = obj.get_modem()
+ modem.enable()
+ time.sleep(1)
+ modem3gpp = obj.get_modem_3gpp()
+ if not modem3gpp:
+ sys.stderr.write('%s: skipping unusable modem...\n' %
+ obj.get_object_path())
+ continue
+ modem3gpp.set_default_timeout(300000)
+ print('%s: starting network scan...' % modem3gpp.get_object_path())
+ networks = modem3gpp.scan_sync()
+ if networks:
+ for network in networks:
+ print('%s: %s - %s (%s, %s)' % (
+ network.get_operator_code(),
+ network.get_operator_short(),
+ network.get_operator_short(),
+ ModemManager.modem_access_technology_build_string_from_mask(
+ network.get_access_technology()),
+ ModemManager.Modem3gppNetworkAvailability.get_string(
+ network.get_availability())))
+ else:
+ print('no networks found')
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/sms-c/Makefile.am b/examples/sms-c/Makefile.am
new file mode 100644
index 00000000..c8c3c905
--- /dev/null
+++ b/examples/sms-c/Makefile.am
@@ -0,0 +1,49 @@
+noinst_PROGRAMS = sms-c-sync sms-c-async
+
+sms_c_sync_CPPFLAGS = \
+ $(WARN_CFLAGS) \
+ $(MMCLI_CFLAGS) \
+ -I$(top_srcdir) \
+ -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 \
+ $(NULL)
+
+sms_c_sync_SOURCES = \
+ sms-c-sync.c \
+ $(NULL)
+
+sms_c_sync_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+sms_c_sync_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(MMCLI_LIBS) \
+ $(NULL)
+
+sms_c_async_CPPFLAGS = \
+ $(WARN_CFLAGS) \
+ $(MMCLI_CFLAGS) \
+ -I$(top_srcdir) \
+ -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 \
+ $(NULL)
+
+sms_c_async_SOURCES = \
+ sms-c-async.c \
+ $(NULL)
+
+sms_c_async_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+sms_c_async_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(MMCLI_LIBS) \
+ $(NULL)
diff --git a/examples/sms-c/meson.build b/examples/sms-c/meson.build
new file mode 100644
index 00000000..9e2a0d3c
--- /dev/null
+++ b/examples/sms-c/meson.build
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+
+example_units = [
+ 'sms-c-async',
+ 'sms-c-sync',
+]
+
+foreach example_unit: example_units
+ executable(
+ example_unit,
+ example_unit + '.c',
+ include_directories: top_inc,
+ dependencies: libmm_glib_dep,
+ )
+endforeach
diff --git a/examples/sms-c/sms-c-async.c b/examples/sms-c/sms-c-async.c
new file mode 100644
index 00000000..fd2d47ea
--- /dev/null
+++ b/examples/sms-c/sms-c-async.c
@@ -0,0 +1,197 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libmm-glib.h>
+
+typedef struct {
+ GMainLoop *loop;
+ GDBusConnection *connection;
+ MMManager *manager;
+ GList *objects;
+ MMSmsProperties *properties;
+
+ MMObject *current_obj;
+ MMModemMessaging *current_messaging;
+ MMSms *current_sms;
+} Context;
+
+static void send_sms_next (Context *context);
+
+static void
+sms_send_ready (MMSms *sms,
+ GAsyncResult *res,
+ Context *context)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_sms_send_finish (sms, res, &error))
+ g_printerr ("error: couldn't send sms in modem %s: %s\n",
+ mm_object_get_path (context->current_obj),
+ error->message);
+ else
+ g_print ("successfully sent sms in modem %s\n",
+ mm_object_get_path (context->current_obj));
+
+ send_sms_next (context);
+}
+
+static void
+messaging_create_ready (MMModemMessaging *messaging,
+ GAsyncResult *res,
+ Context *context)
+{
+ g_autoptr(GError) error = NULL;
+
+ context->current_sms = mm_modem_messaging_create_finish (messaging, res, &error);
+ if (!context->current_sms) {
+ g_printerr ("error: couldn't create sms in modem %s: %s\n",
+ mm_object_get_path (context->current_obj),
+ error->message);
+ send_sms_next (context);
+ return;
+ }
+
+ mm_sms_send (context->current_sms,
+ NULL,
+ (GAsyncReadyCallback) sms_send_ready,
+ context);
+}
+
+static void
+send_sms_next (Context *context)
+{
+ g_clear_object (&context->current_sms);
+ g_clear_object (&context->current_messaging);
+ g_clear_object (&context->current_obj);
+
+ if (!context->objects) {
+ g_main_loop_quit (context->loop);
+ return;
+ }
+
+ context->current_obj = context->objects->data;
+ context->objects = g_list_delete_link (context->objects, context->objects);
+
+ context->current_messaging = mm_object_get_modem_messaging (context->current_obj);
+ if (!context->current_messaging) {
+ g_printerr ("error: modem %s does not have messaging capabilities\n",
+ mm_object_get_path (context->current_obj));
+ send_sms_next (context);
+ return;
+ }
+
+ mm_modem_messaging_create (context->current_messaging,
+ context->properties,
+ NULL,
+ (GAsyncReadyCallback)messaging_create_ready,
+ context);
+}
+
+static void
+manager_new_ready (GObject *source,
+ GAsyncResult *res,
+ Context *context)
+{
+ g_autofree gchar *name_owner = NULL;
+ g_autoptr(GError) error = NULL;
+
+ context->manager = mm_manager_new_finish (res, &error);
+ if (!context->connection) {
+ g_printerr ("error: couldn't get manager: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (context->manager));
+ if (!name_owner) {
+ g_printerr ("error: ModemManager not found in the system bus\n");
+ exit (EXIT_FAILURE);
+ }
+
+ context->objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (context->manager));
+ if (!context->objects) {
+ g_printerr ("error: no modems found\n");
+ exit (EXIT_FAILURE);
+ }
+
+ send_sms_next (context);
+}
+
+static void
+bus_get_ready (GObject *source,
+ GAsyncResult *res,
+ Context *context)
+{
+ g_autoptr(GError) error = NULL;
+
+ context->connection = g_bus_get_finish (res, &error);
+ if (!context->connection) {
+ g_printerr ("error: couldn't get bus: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ mm_manager_new (context->connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ (GAsyncReadyCallback) manager_new_ready,
+ context);
+}
+
+int main (int argc, char **argv)
+{
+ Context context = { 0 };
+
+ if (argc < 3) {
+ g_printerr ("error: missing arguments\n");
+ g_printerr ("usage: %s <NUMBER> <TEXT>\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+
+ context.properties = mm_sms_properties_new ();
+ mm_sms_properties_set_number (context.properties, argv[1]);
+ mm_sms_properties_set_text (context.properties, argv[2]);
+
+ g_bus_get (G_BUS_TYPE_SYSTEM,
+ NULL,
+ (GAsyncReadyCallback) bus_get_ready,
+ &context);
+
+ context.loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (context.loop);
+
+ g_assert (!context.current_obj);
+ g_assert (!context.current_messaging);
+ g_assert (!context.current_sms);
+
+ g_main_loop_unref (context.loop);
+ g_clear_object (&context.connection);
+ g_clear_object (&context.manager);
+ g_clear_object (&context.properties);
+ g_list_free_full (g_steal_pointer (&context.objects), g_object_unref);
+
+ return 0;
+}
diff --git a/examples/sms-c/sms-c-sync.c b/examples/sms-c/sms-c-sync.c
new file mode 100644
index 00000000..5a4a4a59
--- /dev/null
+++ b/examples/sms-c/sms-c-sync.c
@@ -0,0 +1,143 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libmm-glib.h>
+
+static gboolean
+loop_ready (GMainLoop *loop)
+{
+ g_main_loop_quit (loop);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+send_sms (MMObject *obj,
+ MMSmsProperties *properties)
+{
+ g_autoptr(MMModemMessaging) messaging = NULL;
+ g_autoptr(MMSms) sms = NULL;
+ g_autoptr(GError) error = NULL;
+
+ messaging = mm_object_get_modem_messaging (obj);
+ if (!messaging) {
+ g_printerr ("error: modem %s does not have messaging capabilities\n",
+ mm_object_get_path (obj));
+ return;
+ }
+
+ sms = mm_modem_messaging_create_sync (messaging, properties, NULL, &error);
+ if (!sms) {
+ g_printerr ("error: couldn't create sms in modem %s: %s\n",
+ mm_object_get_path (obj),
+ error->message);
+ return;
+ }
+
+ if (!mm_sms_send_sync (sms, NULL, &error)) {
+ g_printerr ("error: couldn't send sms in modem %s: %s\n",
+ mm_object_get_path (obj),
+ error->message);
+ return;
+ }
+
+ g_print ("successfully sent sms in modem %s\n",
+ mm_object_get_path (obj));
+}
+
+int main (int argc, char **argv)
+{
+ g_autoptr(GDBusConnection) connection = NULL;
+ g_autoptr(MMManager) manager = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autolist(MMObject) objects = NULL;
+ g_autoptr(MMSmsProperties) properties = NULL;
+ g_autofree gchar *name_owner = NULL;
+
+ if (argc < 3) {
+ g_printerr ("error: missing arguments\n");
+ g_printerr ("usage: %s <NUMBER> <TEXT>\n", argv[0]);
+ exit (EXIT_FAILURE);
+ }
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!connection) {
+ g_printerr ("error: couldn't get bus: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ manager = mm_manager_new_sync (connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ &error);
+ if (!manager) {
+ g_printerr ("error: couldn't get manager: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
+ if (!name_owner) {
+ g_printerr ("error: ModemManager not found in the system bus\n");
+ exit (EXIT_FAILURE);
+ }
+
+ objects = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
+ if (!objects) {
+ g_printerr ("error: no modems found\n");
+ exit (EXIT_FAILURE);
+ }
+
+ properties = mm_sms_properties_new ();
+ mm_sms_properties_set_number (properties, argv[1]);
+ mm_sms_properties_set_text (properties, argv[2]);
+
+ g_list_foreach (objects, (GFunc)send_sms, properties);
+
+ g_clear_object (&connection);
+ g_clear_object (&manager);
+ g_list_free_full (g_steal_pointer (&objects), g_object_unref);
+
+ /* This main loop is responsible for processing all async events that may
+ * have been scheduled in the previous sync operations, e.g. when disposing
+ * some of the GDBus related objects created before. Without this loop,
+ * the events scheduled in the main context would never be fully released.
+ * This is obviously not a problem in this example as it will just exit, but
+ * it can serve as guideline for others integrating sync-only libmm-glib
+ * opertions in other programs. By creating an ephimeral main loop here and
+ * appending an idle task (that will be run after all the other idle tasks
+ * that may have been already scheduled), we make sure all are processed and
+ * removed before exiting.*/
+ {
+ g_autoptr(GMainLoop) loop = NULL;
+
+ loop = g_main_loop_new (NULL, FALSE);
+ g_idle_add ((GSourceFunc)loop_ready, loop);
+ g_main_loop_run (loop);
+ }
+
+ return 0;
+}
diff --git a/examples/sms-python/Makefile.am b/examples/sms-python/Makefile.am
new file mode 100644
index 00000000..8ecca80b
--- /dev/null
+++ b/examples/sms-python/Makefile.am
@@ -0,0 +1,4 @@
+
+EXTRA_DIST = \
+ sms-python \
+ $(NULL)
diff --git a/examples/sms-python/README b/examples/sms-python/README
new file mode 100644
index 00000000..50a1cc55
--- /dev/null
+++ b/examples/sms-python/README
@@ -0,0 +1,20 @@
+
+The sms-python program makes use of the 'libmm-glib' library through
+GObject Introspection to talk to ModemManager.
+
+The program will:
+ * Detect whether ModemManager is found in the bus
+ * Prepare SMS properties object with the provided Number and Text.
+ * Loop through each modem found in the system, and for each:
+ ** Create a SMS
+ ** Send the SMS
+
+The output will look like this:
+
+$ ./sms-python "+1234567890" "hello there, how are you?"
+/org/freedesktop/ModemManager1/Modem/0: sms sent
+
+Note that the program requires ModemManager and libmm-glib to be installed in
+the system and the introspection typelibs available in the standard paths.
+
+Have fun! \ No newline at end of file
diff --git a/examples/sms-python/sms-python b/examples/sms-python/sms-python
new file mode 100755
index 00000000..46d5e692
--- /dev/null
+++ b/examples/sms-python/sms-python
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation; either version 2 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc., 51
+# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
+#
+
+import sys
+
+import gi
+gi.require_version('ModemManager', '1.0')
+from gi.repository import Gio, GLib, GObject, ModemManager
+
+
+def main():
+ """Main routine."""
+
+ # Process input arguments
+ if len(sys.argv) != 3:
+ sys.stderr.write('error: wrong number of arguments\n')
+ sys.stdout.write('usage: sms-python <NUMBER> <TEXT>\n')
+ sys.exit(1)
+
+ # Prepare SMS properties
+ sms_properties = ModemManager.SmsProperties.new()
+ sms_properties.set_number(sys.argv[1])
+ sms_properties.set_text(sys.argv[2])
+
+ # Connection to ModemManager
+ connection = Gio.bus_get_sync(Gio.BusType.SYSTEM, None)
+ manager = ModemManager.Manager.new_sync(
+ connection, Gio.DBusObjectManagerClientFlags.DO_NOT_AUTO_START, None)
+ if not manager.get_name_owner():
+ sys.stderr.write('ModemManager not found in bus')
+ sys.exit(2)
+
+ # Iterate modems and send SMS with each
+ for obj in manager.get_objects():
+ messaging = obj.get_modem_messaging()
+ sms = messaging.create_sync(sms_properties)
+ sms.send_sync()
+ print('%s: sms sent' % messaging.get_object_path())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/gtester.make b/gtester.make
index 40348dcd..f9cb8161 100644
--- a/gtester.make
+++ b/gtester.make
@@ -67,25 +67,5 @@ test-report perf-report full-report: ${TEST_PROGS}
}
.PHONY: test test-report perf-report full-report test-nonrecursive
-.PHONY: lcov genlcov lcov-clean
-# use recursive makes in order to ignore errors during check
-lcov:
- -$(MAKE) $(AM_MAKEFLAGS) -k check
- $(MAKE) $(AM_MAKEFLAGS) genlcov
-
-# we have to massage the lcov.info file slightly to hide the effect of libtool
-# placing the objects files in the .libs/ directory separate from the *.c
-# we also have to delete tests/.libs/libmoduletestplugin_*.gcda
-genlcov:
- rm -f $(top_builddir)/tests/.libs/libmoduletestplugin_*.gcda
- $(LTP) --directory $(top_builddir) --capture --output-file glib-lcov.info --test-name GLIB_PERF --no-checksum --compat-libtool
- LANG=C $(LTP_GENHTML) --prefix $(top_builddir) --output-directory glib-lcov --title "GLib Code Coverage" --legend --show-details glib-lcov.info
- @echo "file://$(abs_top_builddir)/glib-lcov/index.html"
-
-lcov-clean:
- -$(LTP) --directory $(top_builddir) -z
- -rm -rf glib-lcov.info glib-lcov
- -find -name '*.gcda' -print | xargs rm
-
# run tests in cwd as part of make check
check-local: test-nonrecursive
diff --git a/gtk-doc.make b/gtk-doc.make
new file mode 100644
index 00000000..913aa4f7
--- /dev/null
+++ b/gtk-doc.make
@@ -0,0 +1,320 @@
+# -*- mode: makefile -*-
+#
+# gtk-doc.make - make rules for gtk-doc
+# Copyright (C) 2003 James Henstridge
+# 2004-2007 Damon Chaplin
+# 2007-2017 Stefan Sauer
+#
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+####################################
+# Everything below here is generic #
+####################################
+
+if GTK_DOC_USE_LIBTOOL
+GTKDOC_CC = $(LIBTOOL) --tag=CC --mode=compile $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(LIBTOOL) --tag=CC --mode=link $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+GTKDOC_RUN = $(LIBTOOL) --mode=execute
+else
+GTKDOC_CC = $(CC) $(INCLUDES) $(GTKDOC_DEPS_CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+GTKDOC_LD = $(CC) $(GTKDOC_DEPS_LIBS) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS)
+GTKDOC_RUN =
+endif
+
+# We set GPATH here; this gives us semantics for GNU make
+# which are more like other make's VPATH, when it comes to
+# whether a source that is a target of one rule is then
+# searched for in VPATH/GPATH.
+#
+GPATH = $(srcdir)
+
+TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE)
+
+SETUP_FILES = \
+ $(content_files) \
+ $(expand_content_files) \
+ $(DOC_MAIN_SGML_FILE) \
+ $(DOC_MODULE)-sections.txt \
+ $(DOC_MODULE)-overrides.txt
+
+EXTRA_DIST = \
+ $(HTML_IMAGES) \
+ $(SETUP_FILES)
+
+DOC_STAMPS=setup-build.stamp scan-build.stamp sgml-build.stamp \
+ html-build.stamp pdf-build.stamp \
+ sgml.stamp html.stamp pdf.stamp
+
+SCANOBJ_FILES = \
+ $(DOC_MODULE).args \
+ $(DOC_MODULE).hierarchy \
+ $(DOC_MODULE).interfaces \
+ $(DOC_MODULE).prerequisites \
+ $(DOC_MODULE).signals
+
+REPORT_FILES = \
+ $(DOC_MODULE)-undocumented.txt \
+ $(DOC_MODULE)-undeclared.txt \
+ $(DOC_MODULE)-unused.txt
+
+gtkdoc-check.test: Makefile
+ $(AM_V_GEN)echo "#!/bin/sh -e" > $@; \
+ echo "$(GTKDOC_CHECK_PATH) || exit 1" >> $@; \
+ chmod +x $@
+
+CLEANFILES = $(SCANOBJ_FILES) $(REPORT_FILES) $(DOC_STAMPS) gtkdoc-check.test
+
+if GTK_DOC_BUILD_HTML
+HTML_BUILD_STAMP=html-build.stamp
+else
+HTML_BUILD_STAMP=
+endif
+if GTK_DOC_BUILD_PDF
+PDF_BUILD_STAMP=pdf-build.stamp
+else
+PDF_BUILD_STAMP=
+endif
+
+all-gtk-doc: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP)
+.PHONY: all-gtk-doc
+
+if ENABLE_GTK_DOC
+all-local: all-gtk-doc
+endif
+
+docs: $(HTML_BUILD_STAMP) $(PDF_BUILD_STAMP)
+
+$(REPORT_FILES): sgml-build.stamp
+
+#### setup ####
+
+GTK_DOC_V_SETUP=$(GTK_DOC_V_SETUP_@AM_V@)
+GTK_DOC_V_SETUP_=$(GTK_DOC_V_SETUP_@AM_DEFAULT_V@)
+GTK_DOC_V_SETUP_0=@echo " DOC Preparing build";
+
+setup-build.stamp:
+ -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
+ files=`echo $(SETUP_FILES) $(DOC_MODULE).types`; \
+ if test "x$$files" != "x" ; then \
+ for file in $$files ; do \
+ destdir=`dirname $(abs_builddir)/$$file`; \
+ test -d "$$destdir" || mkdir -p "$$destdir"; \
+ test -f $(abs_srcdir)/$$file && \
+ cp -pf $(abs_srcdir)/$$file $(abs_builddir)/$$file || true; \
+ done; \
+ fi; \
+ fi
+ $(AM_V_at)touch setup-build.stamp
+
+#### scan ####
+
+GTK_DOC_V_SCAN=$(GTK_DOC_V_SCAN_@AM_V@)
+GTK_DOC_V_SCAN_=$(GTK_DOC_V_SCAN_@AM_DEFAULT_V@)
+GTK_DOC_V_SCAN_0=@echo " DOC Scanning header files";
+
+GTK_DOC_V_INTROSPECT=$(GTK_DOC_V_INTROSPECT_@AM_V@)
+GTK_DOC_V_INTROSPECT_=$(GTK_DOC_V_INTROSPECT_@AM_DEFAULT_V@)
+GTK_DOC_V_INTROSPECT_0=@echo " DOC Introspecting gobjects";
+
+scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB)
+ $(GTK_DOC_V_SCAN)_source_dir='' ; \
+ for i in $(DOC_SOURCE_DIR) ; do \
+ _source_dir="$${_source_dir} --source-dir=$$i" ; \
+ done ; \
+ gtkdoc-scan --module=$(DOC_MODULE) --ignore-headers="$(IGNORE_HFILES)" $${_source_dir} $(SCAN_OPTIONS) $(EXTRA_HFILES)
+ $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \
+ scanobj_options=""; \
+ gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \
+ if test "$$?" = "0"; then \
+ if test "x$(V)" = "x1"; then \
+ scanobj_options="--verbose"; \
+ fi; \
+ fi; \
+ CC="$(GTKDOC_CC)" LD="$(GTKDOC_LD)" RUN="$(GTKDOC_RUN)" CFLAGS="$(GTKDOC_CFLAGS) $(CFLAGS)" LDFLAGS="$(GTKDOC_LIBS) $(LDFLAGS)" \
+ gtkdoc-scangobj $(SCANGOBJ_OPTIONS) $$scanobj_options --module=$(DOC_MODULE); \
+ else \
+ for i in $(SCANOBJ_FILES) ; do \
+ test -f $$i || touch $$i ; \
+ done \
+ fi
+ $(AM_V_at)touch scan-build.stamp
+
+$(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt: scan-build.stamp
+ @true
+
+#### xml ####
+
+GTK_DOC_V_XML=$(GTK_DOC_V_XML_@AM_V@)
+GTK_DOC_V_XML_=$(GTK_DOC_V_XML_@AM_DEFAULT_V@)
+GTK_DOC_V_XML_0=@echo " DOC Building XML";
+
+sgml-build.stamp: setup-build.stamp $(DOC_MODULE)-decl.txt $(SCANOBJ_FILES) $(HFILE_GLOB) $(CFILE_GLOB) $(DOC_MODULE)-sections.txt $(DOC_MODULE)-overrides.txt $(expand_content_files) xml/gtkdocentities.ent
+ $(GTK_DOC_V_XML)_source_dir='' ; \
+ for i in $(DOC_SOURCE_DIR) ; do \
+ _source_dir="$${_source_dir} --source-dir=$$i" ; \
+ done ; \
+ gtkdoc-mkdb --module=$(DOC_MODULE) --output-format=xml --expand-content-files="$(expand_content_files)" --main-sgml-file=$(DOC_MAIN_SGML_FILE) $${_source_dir} $(MKDB_OPTIONS)
+ $(AM_V_at)touch sgml-build.stamp
+
+sgml.stamp: sgml-build.stamp
+ @true
+
+$(DOC_MAIN_SGML_FILE): sgml-build.stamp
+ @true
+
+xml/gtkdocentities.ent: Makefile
+ $(GTK_DOC_V_XML)$(MKDIR_P) $(@D) && ( \
+ echo "<!ENTITY package \"$(PACKAGE)\">"; \
+ echo "<!ENTITY package_bugreport \"$(PACKAGE_BUGREPORT)\">"; \
+ echo "<!ENTITY package_name \"$(PACKAGE_NAME)\">"; \
+ echo "<!ENTITY package_string \"$(PACKAGE_STRING)\">"; \
+ echo "<!ENTITY package_tarname \"$(PACKAGE_TARNAME)\">"; \
+ echo "<!ENTITY package_url \"$(PACKAGE_URL)\">"; \
+ echo "<!ENTITY package_version \"$(PACKAGE_VERSION)\">"; \
+ ) > $@
+
+#### html ####
+
+GTK_DOC_V_HTML=$(GTK_DOC_V_HTML_@AM_V@)
+GTK_DOC_V_HTML_=$(GTK_DOC_V_HTML_@AM_DEFAULT_V@)
+GTK_DOC_V_HTML_0=@echo " DOC Building HTML";
+
+GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_@AM_V@)
+GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_@AM_DEFAULT_V@)
+GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references";
+
+html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files)
+ $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \
+ mkhtml_options=""; \
+ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \
+ if test "$$?" = "0"; then \
+ if test "x$(V)" = "x1"; then \
+ mkhtml_options="$$mkhtml_options --verbose"; \
+ fi; \
+ fi; \
+ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \
+ if test "$$?" = "0"; then \
+ mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \
+ fi; \
+ cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE)
+ -@test "x$(HTML_IMAGES)" = "x" || \
+ for file in $(HTML_IMAGES) ; do \
+ test -f $(abs_srcdir)/$$file && cp $(abs_srcdir)/$$file $(abs_builddir)/html; \
+ test -f $(abs_builddir)/$$file && cp $(abs_builddir)/$$file $(abs_builddir)/html; \
+ test -f $$file && cp $$file $(abs_builddir)/html; \
+ done;
+ $(GTK_DOC_V_XREF)gtkdoc-fixxref --module=$(DOC_MODULE) --module-dir=html --html-dir=$(HTML_DIR) $(FIXXREF_OPTIONS)
+ $(AM_V_at)touch html-build.stamp
+
+#### pdf ####
+
+GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_@AM_V@)
+GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_@AM_DEFAULT_V@)
+GTK_DOC_V_PDF_0=@echo " DOC Building PDF";
+
+pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files)
+ $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \
+ mkpdf_options=""; \
+ gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \
+ if test "$$?" = "0"; then \
+ if test "x$(V)" = "x1"; then \
+ mkpdf_options="$$mkpdf_options --verbose"; \
+ fi; \
+ fi; \
+ if test "x$(HTML_IMAGES)" != "x"; then \
+ for img in $(HTML_IMAGES); do \
+ part=`dirname $$img`; \
+ echo $$mkpdf_options | grep >/dev/null "\-\-imgdir=$$part "; \
+ if test $$? != 0; then \
+ mkpdf_options="$$mkpdf_options --imgdir=$$part"; \
+ fi; \
+ done; \
+ fi; \
+ gtkdoc-mkpdf --path="$(abs_srcdir)" $$mkpdf_options $(DOC_MODULE) $(DOC_MAIN_SGML_FILE) $(MKPDF_OPTIONS)
+ $(AM_V_at)touch pdf-build.stamp
+
+##############
+
+clean-local:
+ @rm -f *~ *.bak
+ @rm -rf .libs
+ @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \
+ rm -f $(DOC_MODULE).types; \
+ fi
+ @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-sections" ; then \
+ rm -f $(DOC_MODULE)-sections.txt; \
+ fi
+
+distclean-local:
+ @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \
+ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt
+ @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \
+ rm -f $(SETUP_FILES) $(DOC_MODULE).types; \
+ fi
+
+maintainer-clean-local:
+ @rm -rf xml html
+
+install-data-local:
+ @installfiles=`echo $(builddir)/html/*`; \
+ if test "$$installfiles" = '$(builddir)/html/*'; \
+ then echo 1>&2 'Nothing to install' ; \
+ else \
+ if test -n "$(DOC_MODULE_VERSION)"; then \
+ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
+ else \
+ installdir="$(DESTDIR)$(TARGET_DIR)"; \
+ fi; \
+ $(mkinstalldirs) $${installdir} ; \
+ for i in $$installfiles; do \
+ echo ' $(INSTALL_DATA) '$$i ; \
+ $(INSTALL_DATA) $$i $${installdir}; \
+ done; \
+ if test -n "$(DOC_MODULE_VERSION)"; then \
+ mv -f $${installdir}/$(DOC_MODULE).devhelp2 \
+ $${installdir}/$(DOC_MODULE)-$(DOC_MODULE_VERSION).devhelp2; \
+ fi; \
+ fi
+
+uninstall-local:
+ @if test -n "$(DOC_MODULE_VERSION)"; then \
+ installdir="$(DESTDIR)$(TARGET_DIR)-$(DOC_MODULE_VERSION)"; \
+ else \
+ installdir="$(DESTDIR)$(TARGET_DIR)"; \
+ fi; \
+ rm -rf $${installdir}
+
+#
+# Require gtk-doc when making dist
+#
+if HAVE_GTK_DOC
+dist-check-gtkdoc: docs
+else
+dist-check-gtkdoc:
+ @echo "*** gtk-doc is needed to run 'make dist'. ***"
+ @echo "*** gtk-doc was not found when 'configure' ran. ***"
+ @echo "*** please install gtk-doc and rerun 'configure'. ***"
+ @false
+endif
+
+dist-hook: dist-check-gtkdoc all-gtk-doc dist-hook-local
+ @mkdir $(distdir)/html
+ @cp ./html/* $(distdir)/html
+ @-cp ./$(DOC_MODULE).pdf $(distdir)/
+ @-cp ./$(DOC_MODULE).types $(distdir)/
+ @-cp ./$(DOC_MODULE)-sections.txt $(distdir)/
+ @cd $(distdir) && rm -f $(DISTCLEANFILES)
+ @$(GTKDOC_REBASE) --online --relative --html-dir=$(distdir)/html
+
+.PHONY : dist-hook-local docs
diff --git a/include/Makefile.am b/include/Makefile.am
index 424b51c3..cd44da5f 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -8,14 +8,16 @@ include_HEADERS = \
ModemManager-names.h \
ModemManager-enums.h \
ModemManager-errors.h \
+ ModemManager-compat.h \
ModemManager-version.h \
ModemManager.h
ModemManager-names.h: $(XMLS) $(top_srcdir)/build-aux/header-generator.xsl
$(AM_V_GEN) $(XSLTPROC) $(top_srcdir)/build-aux/header-generator.xsl $(top_srcdir)/introspection/all.xml > $@
-CLEANFILES = \
+MAINTAINERCLEANFILES = \
ModemManager-names.h
EXTRA_DIST = \
+ ModemManager-tags.h \
ModemManager-version.h.in
diff --git a/include/ModemManager-compat.h b/include/ModemManager-compat.h
new file mode 100644
index 00000000..048ac7d9
--- /dev/null
+++ b/include/ModemManager-compat.h
@@ -0,0 +1,1071 @@
+/* -*- 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) 2017 Google, Inc.
+ */
+
+#ifndef _MODEMMANAGER_COMPAT_H_
+#define _MODEMMANAGER_COMPAT_H_
+
+#if !defined (__MODEM_MANAGER_H_INSIDE__)
+#error "Only <ModemManager.h> can be included directly."
+#endif
+
+#include <ModemManager-enums.h>
+
+/**
+ * SECTION:mm-compat
+ * @title: API break replacements
+ * @short_description: List of deprecated methods, types and symbols.
+ *
+ * These compatibility types and methods are flagged as deprecated and
+ * therefore shouldn't be used in newly written code. They are provided to
+ * avoid unnecessary API/ABI breaks.
+ */
+
+/* deprecated attribute support since gcc 3.1 */
+#if defined __GNUC__ && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+# define MM_DEPRECATED __attribute__((__deprecated__))
+#else
+# define MM_DEPRECATED
+#endif
+
+#ifndef MM_DISABLE_DEPRECATED
+
+/* The following type exists just so that we can get deprecation warnings */
+MM_DEPRECATED
+typedef int MMModemBandDeprecated;
+
+/**
+ * MM_MODEM_BAND_U2100:
+ *
+ * WCDMA 2100 MHz (UTRAN band 1).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_1 instead.
+ */
+#define MM_MODEM_BAND_U2100 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_1)
+
+/**
+ * MM_MODEM_BAND_U1900:
+ *
+ * WCDMA 1900 MHz (UTRAN band 2).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_2 instead.
+ */
+#define MM_MODEM_BAND_U1900 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_2)
+
+/**
+ * MM_MODEM_BAND_U1800:
+ *
+ * WCDMA 1800 MHz (UTRAN band 3).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_3 instead.
+ */
+#define MM_MODEM_BAND_U1800 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_3)
+
+/**
+ * MM_MODEM_BAND_U17IV:
+ *
+ * AWS 1700/2100 MHz (UTRAN band 4).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_4 instead.
+ */
+#define MM_MODEM_BAND_U17IV ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_4)
+
+/**
+ * MM_MODEM_BAND_U850:
+ *
+ * UMTS 850 MHz (UTRAN band 5).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_5 instead.
+ */
+#define MM_MODEM_BAND_U850 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_5)
+
+/**
+ * MM_MODEM_BAND_U800:
+ *
+ * UMTS 800 MHz (UTRAN band 6).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_6 instead.
+ */
+#define MM_MODEM_BAND_U800 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_6)
+
+/**
+ * MM_MODEM_BAND_U2600:
+ *
+ * UMTS 2600 MHz (UTRAN band 7).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_7 instead.
+ */
+#define MM_MODEM_BAND_U2600 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_7)
+
+/**
+ * MM_MODEM_BAND_U900:
+ *
+ * UMTS 900 MHz (UTRAN band 8).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_8 instead.
+ */
+#define MM_MODEM_BAND_U900 ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_8)
+
+/**
+ * MM_MODEM_BAND_U17IX:
+ *
+ * UMTS 1700 MHz (UTRAN band 9).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_UTRAN_9 instead.
+ */
+#define MM_MODEM_BAND_U17IX ((MMModemBandDeprecated)MM_MODEM_BAND_UTRAN_9)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_I:
+ *
+ * E-UTRAN band 1.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_1 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_I ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_1)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_II:
+ *
+ * E-UTRAN band 2.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_2 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_II ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_2)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_III:
+ *
+ * E-UTRAN band 3.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_3 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_III ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_3)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_IV:
+ *
+ * E-UTRAN band 4.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_4 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_IV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_4)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_V:
+ *
+ * E-UTRAN band 5.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_5 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_V ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_5)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_VI:
+ *
+ * E-UTRAN band 6.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_6 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_VI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_6)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_VII:
+ *
+ * E-UTRAN band 7.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_7 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_VII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_7)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_VIII:
+ *
+ * E-UTRAN band 8.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_8 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_VIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_8)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_IX:
+ *
+ * E-UTRAN band 9.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_9 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_IX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_9)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_X:
+ *
+ * E-UTRAN band 10.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_10 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_X ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_10)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XI:
+ *
+ * E-UTRAN band 11.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_11 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_11)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XII:
+ *
+ * E-UTRAN band 12.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_12 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_12)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XIII:
+ *
+ * E-UTRAN band 13.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_13 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_13)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XIV:
+ *
+ * E-UTRAN band 14.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_14 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_14)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XVII:
+ *
+ * E-UTRAN band 17.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_17 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XVII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_17)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XVIII:
+ *
+ * E-UTRAN band 18.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_18 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XVIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_18)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XIX:
+ *
+ * E-UTRAN band 19.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_19 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XIX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_19)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XX:
+ *
+ * E-UTRAN band 20.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_20 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_20)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXI:
+ *
+ * E-UTRAN band 21.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_21 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_21)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXII:
+ *
+ * E-UTRAN band 22.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_22 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_22)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXIII:
+ *
+ * E-UTRAN band 23.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_23 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_23)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXIV:
+ *
+ * E-UTRAN band 24.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_24 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_24)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXV:
+ *
+ * E-UTRAN band 25.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_25 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_25)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXVI:
+ *
+ * E-UTRAN band 26.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_26 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXVI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_26)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXIII:
+ *
+ * E-UTRAN band 33.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_33 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_33)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXIV:
+ *
+ * E-UTRAN band 34.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_34 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_34)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXV:
+ *
+ * E-UTRAN band 35.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_35 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_35)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXVI:
+ *
+ * E-UTRAN band 36.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_36 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXVI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_36)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXVII:
+ *
+ * E-UTRAN band 37.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_37 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXVII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_37)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXVIII:
+ *
+ * E-UTRAN band 38.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_38 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXVIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_38)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XXXIX:
+ *
+ * E-UTRAN band 39.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_39 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XXXIX ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_39)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XL:
+ *
+ * E-UTRAN band 40.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_40 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XL ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_40)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XLI:
+ *
+ * E-UTRAN band 41.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_41 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XLI ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_41)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XLII:
+ *
+ * E-UTRAN band 42.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_42 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XLII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_42)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XLIII:
+ *
+ * E-UTRAN band 43.
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_43 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XLIII ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_43)
+
+/**
+ * MM_MODEM_BAND_EUTRAN_XLIV:
+ *
+ * E-UTRAN band 44.
+ *
+ * Since: 1.4
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_EUTRAN_44 instead.
+ */
+#define MM_MODEM_BAND_EUTRAN_XLIV ((MMModemBandDeprecated)MM_MODEM_BAND_EUTRAN_44)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC0_CELLULAR_800:
+ *
+ * CDMA Band Class 0 (US Cellular 850MHz)
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC0 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC0_CELLULAR_800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC0)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC1_PCS_1900:
+ *
+ * CDMA Band Class 1 (US PCS 1900MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC1 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC1_PCS_1900 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC1)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC2_TACS:
+ *
+ * CDMA Band Class 2 (UK TACS 900MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC2 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC2_TACS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC2)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC3_JTACS:
+ *
+ * CDMA Band Class 3 (Japanese TACS).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC3 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC3_JTACS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC3)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS:
+ *
+ * CDMA Band Class 4 (Korean PCS).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC4 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC4)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC5_NMT450:
+ *
+ * CDMA Band Class 5 (NMT 450MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC5 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC5_NMT450 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC5)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC6_IMT2000:
+ *
+ * CDMA Band Class 6 (IMT2000 2100MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC6 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC6_IMT2000 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC6)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC7_CELLULAR_700:
+ *
+ * CDMA Band Class 7 (Cellular 700MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC7 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC7)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC8_1800:
+ *
+ * CDMA Band Class 8 (1800MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC8 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC8_1800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC8)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC9_900:
+ *
+ * CDMA Band Class 9 (900MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC9 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC9_900 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC9)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC10_SECONDARY_800:
+ *
+ * CDMA Band Class 10 (US Secondary 800).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC10 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC10)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC11_PAMR_400:
+ *
+ * CDMA Band Class 11 (European PAMR 400MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC11 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC11_PAMR_400 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC11)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC12_PAMR_800:
+ *
+ * CDMA Band Class 12 (PAMR 800MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC12 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC12_PAMR_800 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC12)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC13_IMT2000_2500:
+ *
+ * CDMA Band Class 13 (IMT2000 2500MHz Expansion).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC13 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC13_IMT2000_2500 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC13)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC14_PCS2_1900:
+ *
+ * CDMA Band Class 14 (More US PCS 1900MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC14 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC14_PCS2_1900 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC14)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC15_AWS:
+ *
+ * CDMA Band Class 15 (AWS 1700MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC15 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC15_AWS ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC15)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC16_US_2500:
+ *
+ * CDMA Band Class 16 (US 2500MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC16 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC16_US_2500 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC16)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC17_US_FLO_2500:
+ *
+ * CDMA Band Class 17 (US 2500MHz Forward Link Only).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC17 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC17)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC18_US_PS_700:
+ *
+ * CDMA Band Class 18 (US 700MHz Public Safety).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC18 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC18_US_PS_700 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC18)
+
+/**
+ * MM_MODEM_BAND_CDMA_BC19_US_LOWER_700:
+ *
+ * CDMA Band Class 19 (US Lower 700MHz).
+ *
+ * Since: 1.0
+ * Deprecated: 1.8: Use #MM_MODEM_BAND_CDMA_BC19 instead.
+ */
+#define MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 ((MMModemBandDeprecated)MM_MODEM_BAND_CDMA_BC19)
+
+/* The following type exists just so that we can get deprecation warnings */
+MM_DEPRECATED
+typedef int MMModemLocationSourceDeprecated;
+
+/**
+ * MM_MODEM_LOCATION_SOURCE_AGPS:
+ *
+ * A-GPS location requested.
+ *
+ * Since: 1.6
+ * Deprecated: 1.12.0: Use #MM_MODEM_LOCATION_SOURCE_AGPS_MSA instead.
+ */
+#define MM_MODEM_LOCATION_SOURCE_AGPS ((MMModemLocationSourceDeprecated)MM_MODEM_LOCATION_SOURCE_AGPS_MSA)
+
+/* The following type exists just so that we can get deprecation warnings */
+MM_DEPRECATED
+typedef int MMModemCapabilityDeprecated;
+
+/**
+ * MM_MODEM_CAPABILITY_LTE_ADVANCED:
+ *
+ * Modem has LTE Advanced data capability.
+ *
+ * This value is deprecated because it is not used anywhere. LTE Advanced
+ * capable devices are reported as LTE capable.
+ *
+ * Since: 1.0
+ * Deprecated: 1.14.0.
+ */
+#define MM_MODEM_CAPABILITY_LTE_ADVANCED ((MMModemCapabilityDeprecated)(1 << 4))
+
+/* The following type exists just so that we can get deprecation warnings */
+MM_DEPRECATED
+typedef int MMMobileEquipmentErrorDeprecated;
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_AND_NON_GPRS_SERVICES_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_CONGESTION instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_CONGESTION
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_NOT_AUTHORIZED_FOR_CSG:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_NOT_AUTHORIZED_FOR_CSG (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES:
+ *
+ * Since: 1.4
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN:
+ *
+ * Since: 1.4
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_ADDRESS_OR_TYPE:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_ADDRESS_OR_TYPE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED:
+ *
+ * Since: 1.4
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_BY_GGSN_OR_GW:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_BY_GGSN_OR_GW (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_UNSPECIFIED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_ACTIVATION_REJECTED_UNSPECIFIED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_FEATURE_NOT_SUPPORTED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_FEATURE_NOT_SUPPORTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERROR_IN_TFT_OPERATION:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERROR_IN_TFT_OPERATION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_TFT_OPERATION:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_TFT_OPERATION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_CONTEXT:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN_PDP_CONTEXT (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERRORS_IN_PACKET_FILTER:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTIC_ERRORS_IN_PACKET_FILTER (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_PACKET_FILTER:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SYNTACTICAL_ERROR_IN_PACKET_FILTER (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS:
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY:
+ *
+ * Since: 1.14
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_LAST_PDN_DISCONNECTION_NOT_ALLOWED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTICALLY_INCORRECT_MESSAGE:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_SEMANTICALLY_INCORRECT_MESSAGE (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_MANDATORY_IE_ERROR:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_MANDATORY_IE_ERROR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_IE_NOT_IMPLEMENTED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_IE_NOT_IMPLEMENTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONDITIONAL_IE_ERROR:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONDITIONAL_IE_ERROR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNSPECIFIED_PROTOCOL_ERROR:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNSPECIFIED_PROTOCOL_ERROR (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_OPERATOR_DETERMINED_BARRING:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_OPERATOR_DETERMINED_BARRING (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUESTED_APN_NOT_SUPPORTED:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUESTED_APN_NOT_SUPPORTED (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUEST_REJECTED_BCM_VIOLATION:
+ *
+ * Since: 1.8
+ * Deprecated: 1.18.0: Use #MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION instead.
+ */
+#define MM_MOBILE_EQUIPMENT_ERROR_GPRS_REQUEST_REJECTED_BCM_VIOLATION (MMMobileEquipmentErrorDeprecated)MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION
+
+#endif /* MM_DISABLE_DEPRECATED */
+
+#endif /* _MODEMMANAGER_COMPAT_H_ */
diff --git a/include/ModemManager-enums.h b/include/ModemManager-enums.h
index 57a2b277..09caf45b 100644
--- a/include/ModemManager-enums.h
+++ b/include/ModemManager-enums.h
@@ -36,12 +36,15 @@
* @MM_MODEM_CAPABILITY_CDMA_EVDO: Modem supports at least one of CDMA 1xRTT, EVDO revision 0, EVDO revision A, or EVDO revision B.
* @MM_MODEM_CAPABILITY_GSM_UMTS: Modem supports at least one of GSM, GPRS, EDGE, UMTS, HSDPA, HSUPA, or HSPA+ packet switched data capability.
* @MM_MODEM_CAPABILITY_LTE: Modem has LTE data capability.
- * @MM_MODEM_CAPABILITY_LTE_ADVANCED: Modem has LTE Advanced data capability.
* @MM_MODEM_CAPABILITY_IRIDIUM: Modem has Iridium capabilities.
+ * @MM_MODEM_CAPABILITY_5GNR: Modem has 5GNR capabilities. Since 1.14.
+ * @MM_MODEM_CAPABILITY_TDS: Modem has TDS capabilties. Since 1.20.
* @MM_MODEM_CAPABILITY_ANY: Mask specifying all capabilities.
*
* Flags describing one or more of the general access technology families that a
* modem supports.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_capability >*/
MM_MODEM_CAPABILITY_NONE = 0,
@@ -49,8 +52,10 @@ typedef enum { /*< underscore_name=mm_modem_capability >*/
MM_MODEM_CAPABILITY_CDMA_EVDO = 1 << 1,
MM_MODEM_CAPABILITY_GSM_UMTS = 1 << 2,
MM_MODEM_CAPABILITY_LTE = 1 << 3,
- MM_MODEM_CAPABILITY_LTE_ADVANCED = 1 << 4,
+ /* MM_MODEM_CAPABILITY_LTE_ADVANCED deprecated */
MM_MODEM_CAPABILITY_IRIDIUM = 1 << 5,
+ MM_MODEM_CAPABILITY_5GNR = 1 << 6,
+ MM_MODEM_CAPABILITY_TDS = 1 << 7,
MM_MODEM_CAPABILITY_ANY = 0xFFFFFFFF
} MMModemCapability;
@@ -75,6 +80,8 @@ typedef enum { /*< underscore_name=mm_modem_capability >*/
* @MM_MODEM_LOCK_PH_NETSUB_PUK: Modem requires the network subset PUK code.
*
* Enumeration of possible lock reasons.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_lock >*/
MM_MODEM_LOCK_UNKNOWN = 0,
@@ -113,6 +120,8 @@ typedef enum { /*< underscore_name=mm_modem_lock >*/
* @MM_MODEM_STATE_CONNECTED: One or more packet data bearers is active and connected.
*
* Enumeration of possible modem states.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_state >*/
MM_MODEM_STATE_FAILED = -1,
@@ -138,6 +147,8 @@ typedef enum { /*< underscore_name=mm_modem_state >*/
* @MM_MODEM_STATE_FAILED_REASON_SIM_ERROR: SIM is available, but unusable (e.g. permanently locked).
*
* Enumeration of possible errors when the modem is in @MM_MODEM_STATE_FAILED.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_state_failed_reason >*/
MM_MODEM_STATE_FAILED_REASON_NONE = 0,
@@ -154,6 +165,8 @@ typedef enum { /*< underscore_name=mm_modem_state_failed_reason >*/
* @MM_MODEM_POWER_STATE_ON: Full power mode.
*
* Power state of the modem.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_power_state >*/
MM_MODEM_POWER_STATE_UNKNOWN = 0,
@@ -170,6 +183,8 @@ typedef enum { /*< underscore_name=mm_modem_power_state >*/
* @MM_MODEM_STATE_CHANGE_REASON_FAILURE: State change was caused by an unrecoverable error.
*
* Enumeration of possible reasons to have changed the modem state.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_state_change_reason >*/
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN = 0,
@@ -196,10 +211,13 @@ typedef enum { /*< underscore_name=mm_modem_state_change_reason >*/
* @MM_MODEM_ACCESS_TECHNOLOGY_EVDOA: CDMA2000 EVDO revision A.
* @MM_MODEM_ACCESS_TECHNOLOGY_EVDOB: CDMA2000 EVDO revision B.
* @MM_MODEM_ACCESS_TECHNOLOGY_LTE: LTE (ETSI 27.007: "E-UTRAN")
+ * @MM_MODEM_ACCESS_TECHNOLOGY_5GNR: 5GNR (ETSI 27.007: "NG-RAN"). Since 1.14.
* @MM_MODEM_ACCESS_TECHNOLOGY_ANY: Mask specifying all access technologies.
*
* Describes various access technologies that a device uses when registered with
* or connected to a network.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_access_technology >*/
MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN = 0,
@@ -218,6 +236,7 @@ typedef enum { /*< underscore_name=mm_modem_access_technology >*/
MM_MODEM_ACCESS_TECHNOLOGY_EVDOA = 1 << 12,
MM_MODEM_ACCESS_TECHNOLOGY_EVDOB = 1 << 13,
MM_MODEM_ACCESS_TECHNOLOGY_LTE = 1 << 14,
+ MM_MODEM_ACCESS_TECHNOLOGY_5GNR = 1 << 15,
MM_MODEM_ACCESS_TECHNOLOGY_ANY = 0xFFFFFFFF,
} MMModemAccessTechnology;
@@ -228,10 +247,13 @@ typedef enum { /*< underscore_name=mm_modem_access_technology >*/
* @MM_MODEM_MODE_2G: GPRS, EDGE.
* @MM_MODEM_MODE_3G: UMTS, HSxPA.
* @MM_MODEM_MODE_4G: LTE.
+ * @MM_MODEM_MODE_5G: 5GNR. Since 1.14.
* @MM_MODEM_MODE_ANY: Any mode can be used (only this value allowed for POTS modems).
*
* Bitfield to indicate which access modes are supported, allowed or
* preferred in a given device.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_mode >*/
MM_MODEM_MODE_NONE = 0,
@@ -239,6 +261,7 @@ typedef enum { /*< underscore_name=mm_modem_mode >*/
MM_MODEM_MODE_2G = 1 << 1,
MM_MODEM_MODE_3G = 1 << 2,
MM_MODEM_MODE_4G = 1 << 3,
+ MM_MODEM_MODE_5G = 1 << 4,
MM_MODEM_MODE_ANY = 0xFFFFFFFF
} MMModemMode;
@@ -249,149 +272,260 @@ typedef enum { /*< underscore_name=mm_modem_mode >*/
* @MM_MODEM_BAND_DCS: GSM/GPRS/EDGE 1800 MHz.
* @MM_MODEM_BAND_PCS: GSM/GPRS/EDGE 1900 MHz.
* @MM_MODEM_BAND_G850: GSM/GPRS/EDGE 850 MHz.
- * @MM_MODEM_BAND_U2100: WCDMA 2100 MHz (Class I).
- * @MM_MODEM_BAND_U1800: WCDMA 3GPP 1800 MHz (Class III).
- * @MM_MODEM_BAND_U17IV: WCDMA 3GPP AWS 1700/2100 MHz (Class IV).
- * @MM_MODEM_BAND_U800: WCDMA 3GPP UMTS 800 MHz (Class VI).
- * @MM_MODEM_BAND_U850: WCDMA 3GPP UMTS 850 MHz (Class V).
- * @MM_MODEM_BAND_U900: WCDMA 3GPP UMTS 900 MHz (Class VIII).
- * @MM_MODEM_BAND_U17IX: WCDMA 3GPP UMTS 1700 MHz (Class IX).
- * @MM_MODEM_BAND_U1900: WCDMA 3GPP UMTS 1900 MHz (Class II).
- * @MM_MODEM_BAND_U2600: WCDMA 3GPP UMTS 2600 MHz (Class VII, internal).
- * @MM_MODEM_BAND_EUTRAN_I: E-UTRAN band I.
- * @MM_MODEM_BAND_EUTRAN_II: E-UTRAN band II.
- * @MM_MODEM_BAND_EUTRAN_III: E-UTRAN band III.
- * @MM_MODEM_BAND_EUTRAN_IV: E-UTRAN band IV.
- * @MM_MODEM_BAND_EUTRAN_V: E-UTRAN band V.
- * @MM_MODEM_BAND_EUTRAN_VI: E-UTRAN band VI.
- * @MM_MODEM_BAND_EUTRAN_VII: E-UTRAN band VII.
- * @MM_MODEM_BAND_EUTRAN_VIII: E-UTRAN band VIII.
- * @MM_MODEM_BAND_EUTRAN_IX: E-UTRAN band IX.
- * @MM_MODEM_BAND_EUTRAN_X: E-UTRAN band X.
- * @MM_MODEM_BAND_EUTRAN_XI: E-UTRAN band XI.
- * @MM_MODEM_BAND_EUTRAN_XII: E-UTRAN band XII.
- * @MM_MODEM_BAND_EUTRAN_XIII: E-UTRAN band XIII.
- * @MM_MODEM_BAND_EUTRAN_XIV: E-UTRAN band XIV.
- * @MM_MODEM_BAND_EUTRAN_XVII: E-UTRAN band XVII.
- * @MM_MODEM_BAND_EUTRAN_XVIII: E-UTRAN band XVIII.
- * @MM_MODEM_BAND_EUTRAN_XIX: E-UTRAN band XIX.
- * @MM_MODEM_BAND_EUTRAN_XX: E-UTRAN band XX.
- * @MM_MODEM_BAND_EUTRAN_XXI: E-UTRAN band XXI.
- * @MM_MODEM_BAND_EUTRAN_XXII: E-UTRAN band XXII.
- * @MM_MODEM_BAND_EUTRAN_XXIII: E-UTRAN band XXIII.
- * @MM_MODEM_BAND_EUTRAN_XXIV: E-UTRAN band XXIV.
- * @MM_MODEM_BAND_EUTRAN_XXV: E-UTRAN band XXV.
- * @MM_MODEM_BAND_EUTRAN_XXVI: E-UTRAN band XXVI.
- * @MM_MODEM_BAND_EUTRAN_XXXIII: E-UTRAN band XXXIII.
- * @MM_MODEM_BAND_EUTRAN_XXXIV: E-UTRAN band XXXIV.
- * @MM_MODEM_BAND_EUTRAN_XXXV: E-UTRAN band XXXV.
- * @MM_MODEM_BAND_EUTRAN_XXXVI: E-UTRAN band XXXVI.
- * @MM_MODEM_BAND_EUTRAN_XXXVII: E-UTRAN band XXXVII.
- * @MM_MODEM_BAND_EUTRAN_XXXVIII: E-UTRAN band XXXVIII.
- * @MM_MODEM_BAND_EUTRAN_XXXIX: E-UTRAN band XXXIX.
- * @MM_MODEM_BAND_EUTRAN_XL: E-UTRAN band XL.
- * @MM_MODEM_BAND_EUTRAN_XLI: E-UTRAN band XLI.
- * @MM_MODEM_BAND_EUTRAN_XLII: E-UTRAN band XLII.
- * @MM_MODEM_BAND_EUTRAN_XLIII: E-UTRAN band XLIII.
- * @MM_MODEM_BAND_EUTRAN_XLIV: E-UTRAN band XLIV.
- * @MM_MODEM_BAND_CDMA_BC0_CELLULAR_800: CDMA Band Class 0 (US Cellular 850MHz).
- * @MM_MODEM_BAND_CDMA_BC1_PCS_1900: CDMA Band Class 1 (US PCS 1900MHz).
- * @MM_MODEM_BAND_CDMA_BC2_TACS: CDMA Band Class 2 (UK TACS 900MHz).
- * @MM_MODEM_BAND_CDMA_BC3_JTACS: CDMA Band Class 3 (Japanese TACS).
- * @MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS: CDMA Band Class 4 (Korean PCS).
- * @MM_MODEM_BAND_CDMA_BC5_NMT450: CDMA Band Class 5 (NMT 450MHz).
- * @MM_MODEM_BAND_CDMA_BC6_IMT2000: CDMA Band Class 6 (IMT2000 2100MHz).
- * @MM_MODEM_BAND_CDMA_BC7_CELLULAR_700: CDMA Band Class 7 (Cellular 700MHz).
- * @MM_MODEM_BAND_CDMA_BC8_1800: CDMA Band Class 8 (1800MHz).
- * @MM_MODEM_BAND_CDMA_BC9_900: CDMA Band Class 9 (900MHz).
- * @MM_MODEM_BAND_CDMA_BC10_SECONDARY_800: CDMA Band Class 10 (US Secondary 800).
- * @MM_MODEM_BAND_CDMA_BC11_PAMR_400: CDMA Band Class 11 (European PAMR 400MHz).
- * @MM_MODEM_BAND_CDMA_BC12_PAMR_800: CDMA Band Class 12 (PAMR 800MHz).
- * @MM_MODEM_BAND_CDMA_BC13_IMT2000_2500: CDMA Band Class 13 (IMT2000 2500MHz Expansion).
- * @MM_MODEM_BAND_CDMA_BC14_PCS2_1900: CDMA Band Class 14 (More US PCS 1900MHz).
- * @MM_MODEM_BAND_CDMA_BC15_AWS: CDMA Band Class 15 (AWS 1700MHz).
- * @MM_MODEM_BAND_CDMA_BC16_US_2500: CDMA Band Class 16 (US 2500MHz).
- * @MM_MODEM_BAND_CDMA_BC17_US_FLO_2500: CDMA Band Class 17 (US 2500MHz Forward Link Only).
- * @MM_MODEM_BAND_CDMA_BC18_US_PS_700: CDMA Band Class 18 (US 700MHz Public Safety).
- * @MM_MODEM_BAND_CDMA_BC19_US_LOWER_700: CDMA Band Class 19 (US Lower 700MHz).
+ * @MM_MODEM_BAND_G450: GSM/GPRS/EDGE 450 MHz.
+ * @MM_MODEM_BAND_G480: GSM/GPRS/EDGE 480 MHz.
+ * @MM_MODEM_BAND_G750: GSM/GPRS/EDGE 750 MHz.
+ * @MM_MODEM_BAND_G380: GSM/GPRS/EDGE 380 MHz.
+ * @MM_MODEM_BAND_G410: GSM/GPRS/EDGE 410 MHz.
+ * @MM_MODEM_BAND_G710: GSM/GPRS/EDGE 710 MHz.
+ * @MM_MODEM_BAND_G810: GSM/GPRS/EDGE 810 MHz.
+ * @MM_MODEM_BAND_UTRAN_1: UMTS 2100 MHz (IMT, UTRAN band 1). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_2: UMTS 1900 MHz (PCS A-F, UTRAN band 2). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_3: UMTS 1800 MHz (DCS, UTRAN band 3). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_4: UMTS 1700 MHz (AWS A-F, UTRAN band 4). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_5: UMTS 850 MHz (CLR, UTRAN band 5). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_6: UMTS 800 MHz (UTRAN band 6). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_7: UMTS 2600 MHz (IMT-E, UTRAN band 7). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_8: UMTS 900 MHz (E-GSM, UTRAN band 8). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_9: UMTS 1700 MHz (UTRAN band 9). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_10: UMTS 1700 MHz (EAWS A-G, UTRAN band 10). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_11: UMTS 1500 MHz (LPDC, UTRAN band 11). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_12: UMTS 700 MHz (LSMH A/B/C, UTRAN band 12). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_13: UMTS 700 MHz (USMH C, UTRAN band 13). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_14: UMTS 700 MHz (USMH D, UTRAN band 14). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_19: UMTS 800 MHz (UTRAN band 19). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_20: UMTS 800 MHz (EUDD, UTRAN band 20). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_21: UMTS 1500 MHz (UPDC, UTRAN band 21). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_22: UMTS 3500 MHz (UTRAN band 22). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_25: UMTS 1900 MHz (EPCS A-G, UTRAN band 25). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_26: UMTS 850 MHz (ECLR, UTRAN band 26). Since 1.8.
+ * @MM_MODEM_BAND_UTRAN_32: UMTS 1500 MHz (L-band, UTRAN band 32). Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_1: E-UTRAN band 1. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_2: E-UTRAN band 2. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_3: E-UTRAN band 3. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_4: E-UTRAN band 4. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_5: E-UTRAN band 5. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_6: E-UTRAN band 6. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_7: E-UTRAN band 7. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_8: E-UTRAN band 8. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_9: E-UTRAN band 9. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_10: E-UTRAN band 10. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_11: E-UTRAN band 11. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_12: E-UTRAN band 12. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_13: E-UTRAN band 13. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_14: E-UTRAN band 14. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_17: E-UTRAN band 17. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_18: E-UTRAN band 18. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_19: E-UTRAN band 19. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_20: E-UTRAN band 20. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_21: E-UTRAN band 21. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_22: E-UTRAN band 22. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_23: E-UTRAN band 23. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_24: E-UTRAN band 24. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_25: E-UTRAN band 25. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_26: E-UTRAN band 26. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_27: E-UTRAN band 27. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_28: E-UTRAN band 28. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_29: E-UTRAN band 29. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_30: E-UTRAN band 30. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_31: E-UTRAN band 31. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_32: E-UTRAN band 32. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_33: E-UTRAN band 33. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_34: E-UTRAN band 34. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_35: E-UTRAN band 35. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_36: E-UTRAN band 36. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_37: E-UTRAN band 37. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_38: E-UTRAN band 38. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_39: E-UTRAN band 39. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_40: E-UTRAN band 40. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_41: E-UTRAN band 41. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_42: E-UTRAN band 42. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_43: E-UTRAN band 43. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_44: E-UTRAN band 44. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_45: E-UTRAN band 45. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_46: E-UTRAN band 46. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_47: E-UTRAN band 47. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_48: E-UTRAN band 48. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_49: E-UTRAN band 49. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_50: E-UTRAN band 50. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_51: E-UTRAN band 51. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_52: E-UTRAN band 52. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_53: E-UTRAN band 53. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_54: E-UTRAN band 54. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_55: E-UTRAN band 55. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_56: E-UTRAN band 56. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_57: E-UTRAN band 57. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_58: E-UTRAN band 58. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_59: E-UTRAN band 59. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_60: E-UTRAN band 60. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_61: E-UTRAN band 61. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_62: E-UTRAN band 62. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_63: E-UTRAN band 63. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_64: E-UTRAN band 64. Since 1.10.
+ * @MM_MODEM_BAND_EUTRAN_65: E-UTRAN band 65. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_66: E-UTRAN band 66. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_67: E-UTRAN band 67. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_68: E-UTRAN band 68. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_69: E-UTRAN band 69. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_70: E-UTRAN band 70. Since 1.8.
+ * @MM_MODEM_BAND_EUTRAN_71: E-UTRAN band 71. Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC0: CDMA Band Class 0 (US Cellular 850MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC1: CDMA Band Class 1 (US PCS 1900MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC2: CDMA Band Class 2 (UK TACS 900MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC3: CDMA Band Class 3 (Japanese TACS). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC4: CDMA Band Class 4 (Korean PCS). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC5: CDMA Band Class 5 (NMT 450MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC6: CDMA Band Class 6 (IMT2000 2100MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC7: CDMA Band Class 7 (Cellular 700MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC8: CDMA Band Class 8 (1800MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC9: CDMA Band Class 9 (900MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC10: CDMA Band Class 10 (US Secondary 800). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC11: CDMA Band Class 11 (European PAMR 400MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC12: CDMA Band Class 12 (PAMR 800MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC13: CDMA Band Class 13 (IMT2000 2500MHz Expansion). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC14: CDMA Band Class 14 (More US PCS 1900MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC15: CDMA Band Class 15 (AWS 1700MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC16: CDMA Band Class 16 (US 2500MHz). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC17: CDMA Band Class 17 (US 2500MHz Forward Link Only). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC18: CDMA Band Class 18 (US 700MHz Public Safety). Since 1.8.
+ * @MM_MODEM_BAND_CDMA_BC19: CDMA Band Class 19 (US Lower 700MHz). Since 1.8.
* @MM_MODEM_BAND_ANY: For certain operations, allow the modem to select a band automatically.
*
* Radio bands supported by the device when connecting to a mobile network.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_band >*/
MM_MODEM_BAND_UNKNOWN = 0,
/* GSM/UMTS bands */
- MM_MODEM_BAND_EGSM = 1,
- MM_MODEM_BAND_DCS = 2,
- MM_MODEM_BAND_PCS = 3,
- MM_MODEM_BAND_G850 = 4,
- MM_MODEM_BAND_U2100 = 5,
- MM_MODEM_BAND_U1800 = 6,
- MM_MODEM_BAND_U17IV = 7,
- MM_MODEM_BAND_U800 = 8,
- MM_MODEM_BAND_U850 = 9,
- MM_MODEM_BAND_U900 = 10,
- MM_MODEM_BAND_U17IX = 11,
- MM_MODEM_BAND_U1900 = 12,
- MM_MODEM_BAND_U2600 = 13,
+ MM_MODEM_BAND_EGSM = 1,
+ MM_MODEM_BAND_DCS = 2,
+ MM_MODEM_BAND_PCS = 3,
+ MM_MODEM_BAND_G850 = 4,
+ MM_MODEM_BAND_UTRAN_1 = 5,
+ MM_MODEM_BAND_UTRAN_3 = 6,
+ MM_MODEM_BAND_UTRAN_4 = 7,
+ MM_MODEM_BAND_UTRAN_6 = 8,
+ MM_MODEM_BAND_UTRAN_5 = 9,
+ MM_MODEM_BAND_UTRAN_8 = 10,
+ MM_MODEM_BAND_UTRAN_9 = 11,
+ MM_MODEM_BAND_UTRAN_2 = 12,
+ MM_MODEM_BAND_UTRAN_7 = 13,
+ MM_MODEM_BAND_G450 = 14,
+ MM_MODEM_BAND_G480 = 15,
+ MM_MODEM_BAND_G750 = 16,
+ MM_MODEM_BAND_G380 = 17,
+ MM_MODEM_BAND_G410 = 18,
+ MM_MODEM_BAND_G710 = 19,
+ MM_MODEM_BAND_G810 = 20,
/* LTE bands */
- MM_MODEM_BAND_EUTRAN_I = 31,
- MM_MODEM_BAND_EUTRAN_II = 32,
- MM_MODEM_BAND_EUTRAN_III = 33,
- MM_MODEM_BAND_EUTRAN_IV = 34,
- MM_MODEM_BAND_EUTRAN_V = 35,
- MM_MODEM_BAND_EUTRAN_VI = 36,
- MM_MODEM_BAND_EUTRAN_VII = 37,
- MM_MODEM_BAND_EUTRAN_VIII = 38,
- MM_MODEM_BAND_EUTRAN_IX = 39,
- MM_MODEM_BAND_EUTRAN_X = 40,
- MM_MODEM_BAND_EUTRAN_XI = 41,
- MM_MODEM_BAND_EUTRAN_XII = 42,
- MM_MODEM_BAND_EUTRAN_XIII = 43,
- MM_MODEM_BAND_EUTRAN_XIV = 44,
- MM_MODEM_BAND_EUTRAN_XVII = 47,
- MM_MODEM_BAND_EUTRAN_XVIII = 48,
- MM_MODEM_BAND_EUTRAN_XIX = 49,
- MM_MODEM_BAND_EUTRAN_XX = 50,
- MM_MODEM_BAND_EUTRAN_XXI = 51,
- MM_MODEM_BAND_EUTRAN_XXII = 52,
- MM_MODEM_BAND_EUTRAN_XXIII = 53,
- MM_MODEM_BAND_EUTRAN_XXIV = 54,
- MM_MODEM_BAND_EUTRAN_XXV = 55,
- MM_MODEM_BAND_EUTRAN_XXVI = 56,
- MM_MODEM_BAND_EUTRAN_XXXIII = 63,
- MM_MODEM_BAND_EUTRAN_XXXIV = 64,
- MM_MODEM_BAND_EUTRAN_XXXV = 65,
- MM_MODEM_BAND_EUTRAN_XXXVI = 66,
- MM_MODEM_BAND_EUTRAN_XXXVII = 67,
- MM_MODEM_BAND_EUTRAN_XXXVIII = 68,
- MM_MODEM_BAND_EUTRAN_XXXIX = 69,
- MM_MODEM_BAND_EUTRAN_XL = 70,
- MM_MODEM_BAND_EUTRAN_XLI = 71,
- MM_MODEM_BAND_EUTRAN_XLII = 72,
- MM_MODEM_BAND_EUTRAN_XLIII = 73,
- MM_MODEM_BAND_EUTRAN_XLIV = 74,
+ MM_MODEM_BAND_EUTRAN_1 = 31,
+ MM_MODEM_BAND_EUTRAN_2 = 32,
+ MM_MODEM_BAND_EUTRAN_3 = 33,
+ MM_MODEM_BAND_EUTRAN_4 = 34,
+ MM_MODEM_BAND_EUTRAN_5 = 35,
+ MM_MODEM_BAND_EUTRAN_6 = 36,
+ MM_MODEM_BAND_EUTRAN_7 = 37,
+ MM_MODEM_BAND_EUTRAN_8 = 38,
+ MM_MODEM_BAND_EUTRAN_9 = 39,
+ MM_MODEM_BAND_EUTRAN_10 = 40,
+ MM_MODEM_BAND_EUTRAN_11 = 41,
+ MM_MODEM_BAND_EUTRAN_12 = 42,
+ MM_MODEM_BAND_EUTRAN_13 = 43,
+ MM_MODEM_BAND_EUTRAN_14 = 44,
+ MM_MODEM_BAND_EUTRAN_17 = 47,
+ MM_MODEM_BAND_EUTRAN_18 = 48,
+ MM_MODEM_BAND_EUTRAN_19 = 49,
+ MM_MODEM_BAND_EUTRAN_20 = 50,
+ MM_MODEM_BAND_EUTRAN_21 = 51,
+ MM_MODEM_BAND_EUTRAN_22 = 52,
+ MM_MODEM_BAND_EUTRAN_23 = 53,
+ MM_MODEM_BAND_EUTRAN_24 = 54,
+ MM_MODEM_BAND_EUTRAN_25 = 55,
+ MM_MODEM_BAND_EUTRAN_26 = 56,
+ MM_MODEM_BAND_EUTRAN_27 = 57,
+ MM_MODEM_BAND_EUTRAN_28 = 58,
+ MM_MODEM_BAND_EUTRAN_29 = 59,
+ MM_MODEM_BAND_EUTRAN_30 = 60,
+ MM_MODEM_BAND_EUTRAN_31 = 61,
+ MM_MODEM_BAND_EUTRAN_32 = 62,
+ MM_MODEM_BAND_EUTRAN_33 = 63,
+ MM_MODEM_BAND_EUTRAN_34 = 64,
+ MM_MODEM_BAND_EUTRAN_35 = 65,
+ MM_MODEM_BAND_EUTRAN_36 = 66,
+ MM_MODEM_BAND_EUTRAN_37 = 67,
+ MM_MODEM_BAND_EUTRAN_38 = 68,
+ MM_MODEM_BAND_EUTRAN_39 = 69,
+ MM_MODEM_BAND_EUTRAN_40 = 70,
+ MM_MODEM_BAND_EUTRAN_41 = 71,
+ MM_MODEM_BAND_EUTRAN_42 = 72,
+ MM_MODEM_BAND_EUTRAN_43 = 73,
+ MM_MODEM_BAND_EUTRAN_44 = 74,
+ MM_MODEM_BAND_EUTRAN_45 = 75,
+ MM_MODEM_BAND_EUTRAN_46 = 76,
+ MM_MODEM_BAND_EUTRAN_47 = 77,
+ MM_MODEM_BAND_EUTRAN_48 = 78,
+ MM_MODEM_BAND_EUTRAN_49 = 79,
+ MM_MODEM_BAND_EUTRAN_50 = 80,
+ MM_MODEM_BAND_EUTRAN_51 = 81,
+ MM_MODEM_BAND_EUTRAN_52 = 82,
+ MM_MODEM_BAND_EUTRAN_53 = 83,
+ MM_MODEM_BAND_EUTRAN_54 = 84,
+ MM_MODEM_BAND_EUTRAN_55 = 85,
+ MM_MODEM_BAND_EUTRAN_56 = 86,
+ MM_MODEM_BAND_EUTRAN_57 = 87,
+ MM_MODEM_BAND_EUTRAN_58 = 88,
+ MM_MODEM_BAND_EUTRAN_59 = 89,
+ MM_MODEM_BAND_EUTRAN_60 = 90,
+ MM_MODEM_BAND_EUTRAN_61 = 91,
+ MM_MODEM_BAND_EUTRAN_62 = 92,
+ MM_MODEM_BAND_EUTRAN_63 = 93,
+ MM_MODEM_BAND_EUTRAN_64 = 94,
+ MM_MODEM_BAND_EUTRAN_65 = 95,
+ MM_MODEM_BAND_EUTRAN_66 = 96,
+ MM_MODEM_BAND_EUTRAN_67 = 97,
+ MM_MODEM_BAND_EUTRAN_68 = 98,
+ MM_MODEM_BAND_EUTRAN_69 = 99,
+ MM_MODEM_BAND_EUTRAN_70 = 100,
+ MM_MODEM_BAND_EUTRAN_71 = 101,
/* CDMA Band Classes (see 3GPP2 C.S0057-C) */
- MM_MODEM_BAND_CDMA_BC0_CELLULAR_800 = 128,
- MM_MODEM_BAND_CDMA_BC1_PCS_1900 = 129,
- MM_MODEM_BAND_CDMA_BC2_TACS = 130,
- MM_MODEM_BAND_CDMA_BC3_JTACS = 131,
- MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS = 132,
- MM_MODEM_BAND_CDMA_BC5_NMT450 = 134,
- MM_MODEM_BAND_CDMA_BC6_IMT2000 = 135,
- MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 = 136,
- MM_MODEM_BAND_CDMA_BC8_1800 = 137,
- MM_MODEM_BAND_CDMA_BC9_900 = 138,
- MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 = 139,
- MM_MODEM_BAND_CDMA_BC11_PAMR_400 = 140,
- MM_MODEM_BAND_CDMA_BC12_PAMR_800 = 141,
- MM_MODEM_BAND_CDMA_BC13_IMT2000_2500 = 142,
- MM_MODEM_BAND_CDMA_BC14_PCS2_1900 = 143,
- MM_MODEM_BAND_CDMA_BC15_AWS = 144,
- MM_MODEM_BAND_CDMA_BC16_US_2500 = 145,
- MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 = 146,
- MM_MODEM_BAND_CDMA_BC18_US_PS_700 = 147,
- MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 = 148,
+ MM_MODEM_BAND_CDMA_BC0 = 128,
+ MM_MODEM_BAND_CDMA_BC1 = 129,
+ MM_MODEM_BAND_CDMA_BC2 = 130,
+ MM_MODEM_BAND_CDMA_BC3 = 131,
+ MM_MODEM_BAND_CDMA_BC4 = 132,
+ MM_MODEM_BAND_CDMA_BC5 = 134,
+ MM_MODEM_BAND_CDMA_BC6 = 135,
+ MM_MODEM_BAND_CDMA_BC7 = 136,
+ MM_MODEM_BAND_CDMA_BC8 = 137,
+ MM_MODEM_BAND_CDMA_BC9 = 138,
+ MM_MODEM_BAND_CDMA_BC10 = 139,
+ MM_MODEM_BAND_CDMA_BC11 = 140,
+ MM_MODEM_BAND_CDMA_BC12 = 141,
+ MM_MODEM_BAND_CDMA_BC13 = 142,
+ MM_MODEM_BAND_CDMA_BC14 = 143,
+ MM_MODEM_BAND_CDMA_BC15 = 144,
+ MM_MODEM_BAND_CDMA_BC16 = 145,
+ MM_MODEM_BAND_CDMA_BC17 = 146,
+ MM_MODEM_BAND_CDMA_BC18 = 147,
+ MM_MODEM_BAND_CDMA_BC19 = 148,
+ /* Additional UMTS bands:
+ * 15-18 reserved
+ * 23-24 reserved
+ * 27-31 reserved
+ */
+ MM_MODEM_BAND_UTRAN_10 = 210,
+ MM_MODEM_BAND_UTRAN_11 = 211,
+ MM_MODEM_BAND_UTRAN_12 = 212,
+ MM_MODEM_BAND_UTRAN_13 = 213,
+ MM_MODEM_BAND_UTRAN_14 = 214,
+ MM_MODEM_BAND_UTRAN_19 = 219,
+ MM_MODEM_BAND_UTRAN_20 = 220,
+ MM_MODEM_BAND_UTRAN_21 = 221,
+ MM_MODEM_BAND_UTRAN_22 = 222,
+ MM_MODEM_BAND_UTRAN_25 = 225,
+ MM_MODEM_BAND_UTRAN_26 = 226,
+ MM_MODEM_BAND_UTRAN_32 = 232,
/* All/Any */
MM_MODEM_BAND_ANY = 256
} MMModemBand;
@@ -405,8 +539,12 @@ typedef enum { /*< underscore_name=mm_modem_band >*/
* @MM_MODEM_PORT_TYPE_GPS: GPS port.
* @MM_MODEM_PORT_TYPE_QMI: QMI port.
* @MM_MODEM_PORT_TYPE_MBIM: MBIM port.
+ * @MM_MODEM_PORT_TYPE_AUDIO: Audio port. Since 1.12.
+ * @MM_MODEM_PORT_TYPE_IGNORED: Ignored port. Since 1.16.
*
* Type of modem port.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_port_type >*/
MM_MODEM_PORT_TYPE_UNKNOWN = 1,
@@ -415,7 +553,9 @@ typedef enum { /*< underscore_name=mm_modem_port_type >*/
MM_MODEM_PORT_TYPE_QCDM = 4,
MM_MODEM_PORT_TYPE_GPS = 5,
MM_MODEM_PORT_TYPE_QMI = 6,
- MM_MODEM_PORT_TYPE_MBIM = 7
+ MM_MODEM_PORT_TYPE_MBIM = 7,
+ MM_MODEM_PORT_TYPE_AUDIO = 8,
+ MM_MODEM_PORT_TYPE_IGNORED = 9,
} MMModemPortType;
/**
@@ -424,14 +564,16 @@ typedef enum { /*< underscore_name=mm_modem_port_type >*/
* @MM_SMS_PDU_TYPE_DELIVER: 3GPP Mobile-Terminated (MT) message.
* @MM_SMS_PDU_TYPE_SUBMIT: 3GPP Mobile-Originated (MO) message.
* @MM_SMS_PDU_TYPE_STATUS_REPORT: 3GPP status report (MT).
- * @MM_SMS_PDU_TYPE_CDMA_DELIVER: 3GPP2 Mobile-Terminated (MT) message.
- * @MM_SMS_PDU_TYPE_CDMA_SUBMIT: 3GPP2 Mobile-Originated (MO) message.
- * @MM_SMS_PDU_TYPE_CDMA_CANCELLATION: 3GPP2 Cancellation (MO) message.
- * @MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT: 3GPP2 Delivery Acknowledgement (MT) message.
- * @MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT: 3GPP2 User Acknowledgement (MT or MO) message.
- * @MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT: 3GPP2 Read Acknowledgement (MT or MO) message.
+ * @MM_SMS_PDU_TYPE_CDMA_DELIVER: 3GPP2 Mobile-Terminated (MT) message. Since 1.2.
+ * @MM_SMS_PDU_TYPE_CDMA_SUBMIT: 3GPP2 Mobile-Originated (MO) message. Since 1.2.
+ * @MM_SMS_PDU_TYPE_CDMA_CANCELLATION: 3GPP2 Cancellation (MO) message. Since 1.2.
+ * @MM_SMS_PDU_TYPE_CDMA_DELIVERY_ACKNOWLEDGEMENT: 3GPP2 Delivery Acknowledgement (MT) message. Since 1.2.
+ * @MM_SMS_PDU_TYPE_CDMA_USER_ACKNOWLEDGEMENT: 3GPP2 User Acknowledgement (MT or MO) message. Since 1.2.
+ * @MM_SMS_PDU_TYPE_CDMA_READ_ACKNOWLEDGEMENT: 3GPP2 Read Acknowledgement (MT or MO) message. Since 1.2.
*
* Type of PDUs used in the SMS.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_sms_pdu_type >*/
MM_SMS_PDU_TYPE_UNKNOWN = 0,
@@ -456,6 +598,8 @@ typedef enum { /*< underscore_name=mm_sms_pdu_type >*/
* @MM_SMS_STATE_SENT: The message was successfully sent.
*
* State of a given SMS.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_sms_state >*/
MM_SMS_STATE_UNKNOWN = 0,
@@ -494,67 +638,69 @@ typedef enum { /*< underscore_name=mm_sms_state >*/
* @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_QOS_NOT_AVAILABLE: Permanent error, QoS not available.
* @MM_SMS_DELIVERY_STATE_TEMPORARY_FATAL_ERROR_IN_SME: Permanent error in SME.
* @MM_SMS_DELIVERY_STATE_UNKNOWN: Unknown state.
- * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_VACANT: Permanent error in network, address vacant.
- * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE: Permanent error in network, address translation failure.
- * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE: Permanent error in network, network resource outage.
- * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_FAILURE: Permanent error in network, network failure.
- * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_INVALID_TELESERVICE_ID: Permanent error in network, invalid teleservice id.
- * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_OTHER: Permanent error, other network problem.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_PAGE_RESPONSE: Permanent error in terminal, no page response.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_BUSY: Permanent error in terminal, destination busy.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT: Permanent error in terminal, no acknowledgement.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE: Permanent error in terminal, destination resource shortage.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED: Permanent error in terminal, SMS delivery postponed.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE: Permanent error in terminal, destination out of service.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS: Permanent error in terminal, destination no longer at this address.
- * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_OTHER: Permanent error, other terminal problem.
- * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE: Permanent error in radio interface, resource shortage.
- * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY: Permanent error in radio interface, problem incompatibility.
- * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_OTHER: Permanent error, other radio interface problem.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_ENCODING: Permanent error, encoding.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED: Permanent error, SMS origination denied.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_TERMINATION_DENIED: Permanent error, SMS termination denied.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED: Permanent error, supplementary service not supported.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_NOT_SUPPORTED: Permanent error, SMS not supported.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER: Permanent error, missing expected parameter.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER: Permanent error, missing mandatory parameter.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE: Permanent error, unrecognized parameter value.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE: Permanent error, unexpected parameter value.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR: Permanent error, user data size error.
- * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_OTHER: Permanent error, other general problem.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_VACANT: Temporary error in network, address vacant.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE: Temporary error in network, address translation failure.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE: Temporary error in network, network resource outage.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_FAILURE: Temporary error in network, network failure.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_INVALID_TELESERVICE_ID: Temporary error in network, invalid teleservice id.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_OTHER: Temporary error, other network problem.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_PAGE_RESPONSE: Temporary error in terminal, no page response.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_BUSY: Temporary error in terminal, destination busy.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT: Temporary error in terminal, no acknowledgement.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE: Temporary error in terminal, destination resource shortage.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED: Temporary error in terminal, SMS delivery postponed.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE: Temporary error in terminal, destination out of service.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS: Temporary error in terminal, destination no longer at this address.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_OTHER: Temporary error, other terminal problem.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE: Temporary error in radio interface, resource shortage.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY: Temporary error in radio interface, problem incompatibility.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_OTHER: Temporary error, other radio interface problem.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_ENCODING: Temporary error, encoding.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED: Temporary error, SMS origination denied.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_TERMINATION_DENIED: Temporary error, SMS termination denied.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED: Temporary error, supplementary service not supported.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_NOT_SUPPORTED: Temporary error, SMS not supported.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER: Temporary error, missing expected parameter.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER: Temporary error, missing mandatory parameter.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE: Temporary error, unrecognized parameter value.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE: Temporary error, unexpected parameter value.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR: Temporary error, user data size error.
- * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_OTHER: Temporary error, other general problem.
+ * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_VACANT: Permanent error in network, address vacant. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE: Permanent error in network, address translation failure. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE: Permanent error in network, network resource outage. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_NETWORK_FAILURE: Permanent error in network, network failure. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_INVALID_TELESERVICE_ID: Permanent error in network, invalid teleservice id. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_NETWORK_PROBLEM_OTHER: Permanent error, other network problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_PAGE_RESPONSE: Permanent error in terminal, no page response. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_BUSY: Permanent error in terminal, destination busy. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT: Permanent error in terminal, no acknowledgement. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE: Permanent error in terminal, destination resource shortage. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED: Permanent error in terminal, SMS delivery postponed. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE: Permanent error in terminal, destination out of service. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS: Permanent error in terminal, destination no longer at this address. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TERMINAL_PROBLEM_OTHER: Permanent error, other terminal problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE: Permanent error in radio interface, resource shortage. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY: Permanent error in radio interface, problem incompatibility. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_RADIO_INTERFACE_PROBLEM_OTHER: Permanent error, other radio interface problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_ENCODING: Permanent error, encoding. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED: Permanent error, SMS origination denied. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_TERMINATION_DENIED: Permanent error, SMS termination denied. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED: Permanent error, supplementary service not supported. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_SMS_NOT_SUPPORTED: Permanent error, SMS not supported. Since 1.22.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER: Permanent error, missing expected parameter. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER: Permanent error, missing mandatory parameter. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE: Permanent error, unrecognized parameter value. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE: Permanent error, unexpected parameter value. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR: Permanent error, user data size error. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_GENERAL_PROBLEM_OTHER: Permanent error, other general problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_VACANT: Temporary error in network, address vacant. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_ADDRESS_TRANSLATION_FAILURE: Temporary error in network, address translation failure. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_RESOURCE_OUTAGE: Temporary error in network, network resource outage. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_NETWORK_FAILURE: Temporary error in network, network failure. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_INVALID_TELESERVICE_ID: Temporary error in network, invalid teleservice id. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_NETWORK_PROBLEM_OTHER: Temporary error, other network problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_PAGE_RESPONSE: Temporary error in terminal, no page response. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_BUSY: Temporary error in terminal, destination busy. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_NO_ACKNOWLEDGMENT: Temporary error in terminal, no acknowledgement. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_RESOURCE_SHORTAGE: Temporary error in terminal, destination resource shortage. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_SMS_DELIVERY_POSTPONED: Temporary error in terminal, SMS delivery postponed. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_OUT_OF_SERVICE: Temporary error in terminal, destination out of service. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_DESTINATION_NO_LONGER_AT_THIS_ADDRESS: Temporary error in terminal, destination no longer at this address. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_TERMINAL_PROBLEM_OTHER: Temporary error, other terminal problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_RESOURCE_SHORTAGE: Temporary error in radio interface, resource shortage. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_INCOMPATIBILITY: Temporary error in radio interface, problem incompatibility. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_RADIO_INTERFACE_PROBLEM_OTHER: Temporary error, other radio interface problem. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_ENCODING: Temporary error, encoding. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_ORIGINATION_DENIED: Temporary error, SMS origination denied. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_TERMINATION_DENIED: Temporary error, SMS termination denied. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SUPPLEMENTARY_SERVICE_NOT_SUPPORTED: Temporary error, supplementary service not supported. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_SMS_NOT_SUPPORTED: Temporary error, SMS not supported. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_EXPECTED_PARAMETER: Temporary error, missing expected parameter. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_MISSING_MANDATORY_PARAMETER: Temporary error, missing mandatory parameter. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNRECOGNIZED_PARAMETER_VALUE: Temporary error, unrecognized parameter value. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_UNEXPECTED_PARAMETER_VALUE: Temporary error, unexpected parameter value. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_USER_DATA_SIZE_ERROR: Temporary error, user data size error. Since 1.2.
+ * @MM_SMS_DELIVERY_STATE_TEMPORARY_GENERAL_PROBLEM_OTHER: Temporary error, other general problem. Since 1.2.
*
* Enumeration of known SMS delivery states as defined in 3GPP TS 03.40 and
* 3GPP2 N.S0005-O, section 6.5.2.125.
*
* States out of the known ranges may also be valid (either reserved or SC-specific).
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_sms_delivery_state >*/
/* --------------- 3GPP specific errors ---------------------- */
@@ -675,6 +821,8 @@ typedef enum { /*< underscore_name=mm_sms_delivery_state >*/
* @MM_SMS_STORAGE_TA: Terminal adaptor message storage area.
*
* Storage for SMS messages.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_sms_storage >*/
MM_SMS_STORAGE_UNKNOWN = 0,
@@ -694,6 +842,8 @@ typedef enum { /*< underscore_name=mm_sms_storage >*/
* @MM_SMS_VALIDITY_TYPE_ENHANCED: Enhanced validity.
*
* Type of SMS validity value.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_sms_validity_type >*/
MM_SMS_VALIDITY_TYPE_UNKNOWN = 0,
@@ -716,6 +866,8 @@ typedef enum { /*< underscore_name=mm_sms_validity_type >*/
*
* Teleservice IDs supported for CDMA SMS, as defined in 3GPP2 X.S0004-550-E
* (section 2.256) and 3GPP2 C.S0015-B (section 3.4.3.1).
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_sms_cdma_teleservice_id >*/
MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN = 0x0000,
@@ -770,6 +922,8 @@ typedef enum { /*< underscore_name=mm_sms_cdma_teleservice_id >*/
* @MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST: CMAS test.
*
* Service category for CDMA SMS, as defined in 3GPP2 C.R1001-D (section 9.3).
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_sms_cdma_service_category >*/
MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN = 0x0000,
@@ -818,10 +972,13 @@ typedef enum { /*< underscore_name=mm_sms_cdma_service_category >*/
* @MM_MODEM_LOCATION_SOURCE_GPS_RAW: GPS location given by predefined keys.
* @MM_MODEM_LOCATION_SOURCE_GPS_NMEA: GPS location given as NMEA traces.
* @MM_MODEM_LOCATION_SOURCE_CDMA_BS: CDMA base station position.
- * @MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: No location given, just GPS module setup.
- * @MM_MODEM_LOCATION_SOURCE_AGPS: A-GPS location requested.
+ * @MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED: No location given, just GPS module setup. Since 1.4.
+ * @MM_MODEM_LOCATION_SOURCE_AGPS_MSA: Mobile Station Assisted A-GPS location requested. Since 1.12.
+ * @MM_MODEM_LOCATION_SOURCE_AGPS_MSB: Mobile Station Based A-GPS location requested. Since 1.12.
*
* Sources of location information supported by the modem.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_location_source >*/
MM_MODEM_LOCATION_SOURCE_NONE = 0,
@@ -830,10 +987,29 @@ typedef enum { /*< underscore_name=mm_modem_location_source >*/
MM_MODEM_LOCATION_SOURCE_GPS_NMEA = 1 << 2,
MM_MODEM_LOCATION_SOURCE_CDMA_BS = 1 << 3,
MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED = 1 << 4,
- MM_MODEM_LOCATION_SOURCE_AGPS = 1 << 5,
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA = 1 << 5,
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB = 1 << 6,
+#if defined (MM_COMPILATION) /*< private >*/
+ MM_MODEM_LOCATION_SOURCE_FIRST = MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI, /*< skip >*/
+ MM_MODEM_LOCATION_SOURCE_LAST = MM_MODEM_LOCATION_SOURCE_AGPS_MSB, /*< skip >*/
+#endif
} MMModemLocationSource;
/**
+ * MMModemLocationAssistanceDataType:
+ * @MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE: None.
+ * @MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA: Qualcomm gpsOneXTRA.
+ *
+ * Type of assistance data that may be injected to the GNSS module.
+ *
+ * Since: 1.10
+ */
+typedef enum { /*< underscore_name=mm_modem_location_assistance_data_type >*/
+ MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE = 0,
+ MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA = 1 << 0,
+} MMModemLocationAssistanceDataType;
+
+/**
* MMModemContactsStorage:
* @MM_MODEM_CONTACTS_STORAGE_UNKNOWN: Unknown location.
* @MM_MODEM_CONTACTS_STORAGE_ME: Device's local memory.
@@ -841,6 +1017,8 @@ typedef enum { /*< underscore_name=mm_modem_location_source >*/
* @MM_MODEM_CONTACTS_STORAGE_MT: Combined device/ME and SIM/SM phonebook.
*
* Specifies different storage locations for contact information.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_contacts_storage >*/
MM_MODEM_CONTACTS_STORAGE_UNKNOWN = 0,
@@ -850,17 +1028,49 @@ typedef enum { /*< underscore_name=mm_modem_contacts_storage >*/
} MMModemContactsStorage;
/**
+ * MMBearerType:
+ * @MM_BEARER_TYPE_UNKNOWN: Unknown bearer.
+ * @MM_BEARER_TYPE_DEFAULT: Primary context (2G/3G) or default bearer (4G),
+ * defined by the user of the API.
+ * @MM_BEARER_TYPE_DEFAULT_ATTACH: The initial default bearer established
+ * during LTE attach procedure, automatically connected as long as the device is
+ * regitered in the LTE network.
+ * @MM_BEARER_TYPE_DEDICATED: Secondary context (2G/3G) or dedicated bearer
+ * (4G), defined by the user of the API. These bearers use the same IP address
+ * used by a primary context or default bearer and provide a dedicated flow for
+ * specific traffic with different QoS settings.
+ *
+ * Type of context (2G/3G) or bearer (4G).
+ *
+ * Since: 1.10
+ */
+typedef enum { /*< underscore_name=mm_bearer_type >*/
+ MM_BEARER_TYPE_UNKNOWN = 0,
+ MM_BEARER_TYPE_DEFAULT = 1,
+ MM_BEARER_TYPE_DEFAULT_ATTACH = 2,
+ MM_BEARER_TYPE_DEDICATED = 3,
+} MMBearerType;
+
+/**
* MMBearerIpMethod:
* @MM_BEARER_IP_METHOD_UNKNOWN: Unknown method.
- * @MM_BEARER_IP_METHOD_PPP: Use PPP to get the address.
+ * @MM_BEARER_IP_METHOD_PPP: Use PPP to get IP addresses and DNS information.
+ * For IPv6, use PPP to retrieve the 64-bit Interface Identifier, use the IID to
+ * construct an IPv6 link-local address by following RFC 5072, and then run
+ * DHCP over the PPP link to retrieve DNS settings.
* @MM_BEARER_IP_METHOD_STATIC: Use the provided static IP configuration given
- * by the modem to configure the IP data interface.
+ * by the modem to configure the IP data interface. Note that DNS servers may
+ * not be provided by the network or modem firmware.
* @MM_BEARER_IP_METHOD_DHCP: Begin DHCP or IPv6 SLAAC on the data interface to
- * obtain necessary IP configuration details. For IPv4 bearers DHCP should
- * be used. For IPv6 bearers SLAAC should be used to determine the prefix and
- * any additional details.
+ * obtain any necessary IP configuration details that are not already provided
+ * by the IP configuration. For IPv4 bearers DHCP should be used. For IPv6
+ * bearers SLAAC should be used, and the IP configuration may already contain
+ * a link-local address that should be assigned to the interface before SLAAC
+ * is started to obtain the rest of the configuration.
*
* Type of IP method configuration to be used in a given Bearer.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_bearer_ip_method >*/
MM_BEARER_IP_METHOD_UNKNOWN = 0,
@@ -878,6 +1088,8 @@ typedef enum { /*< underscore_name=mm_bearer_ip_method >*/
* @MM_BEARER_IP_FAMILY_ANY: Mask specifying all IP families.
*
* Type of IP family to be used in a given Bearer.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_bearer_ip_family >*/
MM_BEARER_IP_FAMILY_NONE = 0,
@@ -898,6 +1110,8 @@ typedef enum { /*< underscore_name=mm_bearer_ip_family >*/
* @MM_BEARER_ALLOWED_AUTH_EAP: EAP.
*
* Allowed authentication methods when authenticating with the network.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_bearer_allowed_auth >*/
MM_BEARER_ALLOWED_AUTH_UNKNOWN = 0,
@@ -918,6 +1132,8 @@ typedef enum { /*< underscore_name=mm_bearer_allowed_auth >*/
* @MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING: Currently registered on a roaming network.
*
* Registration state of a CDMA modem.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_cdma_registration_state >*/
MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN = 0,
@@ -935,6 +1151,8 @@ typedef enum { /*< underscore_name=mm_modem_cdma_registration_state >*/
* @MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED: Device is ready for use.
*
* Activation state of a CDMA modem.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_cdma_activation_state >*/
MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN = 0,
@@ -954,6 +1172,8 @@ typedef enum { /*< underscore_name=mm_modem_cdma_activation_state >*/
* @MM_MODEM_CDMA_RM_PROTOCOL_STU_III: STU-III service.
*
* Protocol of the Rm interface in modems with CDMA capabilities.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_cdma_rm_protocol >*/
MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN = 0,
@@ -972,16 +1192,30 @@ typedef enum { /*< underscore_name=mm_modem_cdma_rm_protocol >*/
* @MM_MODEM_3GPP_REGISTRATION_STATE_DENIED: Registration denied.
* @MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN: Unknown registration status.
* @MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING: Registered on a roaming network.
+ * @MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY: Registered for "SMS only", home network (applicable only when on LTE). Since 1.8.
+ * @MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY: Registered for "SMS only", roaming network (applicable only when on LTE). Since 1.8.
+ * @MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY: Emergency services only. Since 1.8.
+ * @MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED: Registered for "CSFB not preferred", home network (applicable only when on LTE). Since 1.8.
+ * @MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED: Registered for "CSFB not preferred", roaming network (applicable only when on LTE). Since 1.8.
+ * @MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS: Attached for access to Restricted Local Operator Services (applicable only when on LTE). Since 1.14.
+ *
+ * GSM registration code as defined in 3GPP TS 27.007.
*
- * GSM registration code as defined in 3GPP TS 27.007 section 10.1.19.
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_3gpp_registration_state >*/
- MM_MODEM_3GPP_REGISTRATION_STATE_IDLE = 0,
- MM_MODEM_3GPP_REGISTRATION_STATE_HOME = 1,
- MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING = 2,
- MM_MODEM_3GPP_REGISTRATION_STATE_DENIED = 3,
- MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN = 4,
- MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING = 5,
+ MM_MODEM_3GPP_REGISTRATION_STATE_IDLE = 0,
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME = 1,
+ MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING = 2,
+ MM_MODEM_3GPP_REGISTRATION_STATE_DENIED = 3,
+ MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN = 4,
+ MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING = 5,
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY = 6,
+ MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY = 7,
+ MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY = 8,
+ MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED = 9,
+ MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED = 10,
+ MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS = 11,
} MMModem3gppRegistrationState;
/**
@@ -1000,6 +1234,8 @@ typedef enum { /*< underscore_name=mm_modem_3gpp_registration_state >*/
* requires a pin or unlock code. The facilities include the
* personalizations (device locks) described in 3GPP spec TS 22.022,
* and the PIN and PIN2 locks, which are SIM locks.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_3gpp_facility >*/
MM_MODEM_3GPP_FACILITY_NONE = 0,
@@ -1020,7 +1256,9 @@ typedef enum { /*< underscore_name=mm_modem_3gpp_facility >*/
* @MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT: Network is the current one.
* @MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN: Network is forbidden.
*
- * Network availability status as defined in 3GPP TS 27.007 section 7.3
+ * Network availability status as defined in 3GPP TS 27.007 section 7.3.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_3gpp_network_availability >*/
MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN = 0,
@@ -1038,6 +1276,8 @@ typedef enum { /*< underscore_name=mm_modem_3gpp_network_availability >*/
*
* Describes the current subscription status of the SIM. This value is only available after the
* modem attempts to register with the network.
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_modem_3gpp_subscription_state >*/
MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN = 0,
@@ -1054,6 +1294,8 @@ typedef enum { /*< underscore_name=mm_modem_3gpp_subscription_state >*/
* @MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: The network is waiting for the client's response.
*
* State of a USSD session.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_modem_3gpp_ussd_session_state >*/
MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN = 0,
@@ -1063,12 +1305,34 @@ typedef enum { /*< underscore_name=mm_modem_3gpp_ussd_session_state >*/
} MMModem3gppUssdSessionState;
/**
+ * MMModem3gppEpsUeModeOperation:
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN: Unknown or not applicable.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1: PS mode 1 of operation: EPS only, voice-centric.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2: PS mode 2 of operation: EPS only, data-centric.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1: CS/PS mode 1 of operation: EPS and non-EPS, voice-centric.
+ * @MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2: CS/PS mode 2 of operation: EPS and non-EPS, data-centric.
+ *
+ * UE mode of operation for EPS, as per 3GPP TS 24.301.
+ *
+ * Since: 1.8
+ */
+typedef enum { /*< underscore_name=mm_modem_3gpp_eps_ue_mode_operation >*/
+ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN = 0,
+ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1 = 1,
+ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2 = 2,
+ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1 = 3,
+ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2 = 4,
+} MMModem3gppEpsUeModeOperation;
+
+/**
* MMFirmwareImageType:
* @MM_FIRMWARE_IMAGE_TYPE_UNKNOWN: Unknown firmware type.
* @MM_FIRMWARE_IMAGE_TYPE_GENERIC: Generic firmware image.
* @MM_FIRMWARE_IMAGE_TYPE_GOBI: Firmware image of Gobi devices.
*
* Type of firmware image.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_firmware_image_type >*/
MM_FIRMWARE_IMAGE_TYPE_UNKNOWN = 0,
@@ -1084,6 +1348,8 @@ typedef enum { /*< underscore_name=mm_firmware_image_type >*/
* @MM_OMA_FEATURE_HANDS_FREE_ACTIVATION: Hands free activation service.
*
* Features that can be enabled or disabled in the OMA device management support.
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_oma_feature >*/
MM_OMA_FEATURE_NONE = 0,
@@ -1104,6 +1370,8 @@ typedef enum { /*< underscore_name=mm_oma_feature >*/
* @MM_OMA_SESSION_TYPE_DEVICE_INITIATED_HANDS_FREE_ACTIVATION: Device-initiated hands free activation.
*
* Type of OMA device management session.
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_oma_session_type >*/
MM_OMA_SESSION_TYPE_UNKNOWN = 0,
@@ -1132,6 +1400,8 @@ typedef enum { /*< underscore_name=mm_oma_session_type >*/
* @MM_OMA_SESSION_STATE_COMPLETED: Session completed.
*
* State of the OMA device management session.
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_oma_session_state >*/
MM_OMA_SESSION_STATE_FAILED = -1,
@@ -1158,6 +1428,8 @@ typedef enum { /*< underscore_name=mm_oma_session_state >*/
* @MM_OMA_SESSION_STATE_FAILED_REASON_SESSION_CANCELLED: Session cancelled.
*
* Reason of failure in the OMA device management session.
+ *
+ * Since: 1.2
*/
typedef enum { /*< underscore_name=mm_oma_session_state_failed_reason >*/
MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN = 0,
@@ -1168,4 +1440,193 @@ typedef enum { /*< underscore_name=mm_oma_session_state_failed_reason >*/
MM_OMA_SESSION_STATE_FAILED_REASON_SESSION_CANCELLED = 5
} MMOmaSessionStateFailedReason;
+/**
+ * MMCallState:
+ * @MM_CALL_STATE_UNKNOWN: default state for a new outgoing call.
+ * @MM_CALL_STATE_DIALING: outgoing call started. Wait for free channel.
+ * @MM_CALL_STATE_RINGING_IN: incoming call is waiting for an answer.
+ * @MM_CALL_STATE_RINGING_OUT: outgoing call attached to GSM network, waiting for an answer.
+ * @MM_CALL_STATE_ACTIVE: call is active between two peers.
+ * @MM_CALL_STATE_HELD: held call (by +CHLD AT command).
+ * @MM_CALL_STATE_WAITING: waiting call (by +CCWA AT command).
+ * @MM_CALL_STATE_TERMINATED: call is terminated.
+ *
+ * State of Call.
+ *
+ * Since: 1.6
+ */
+typedef enum { /*< underscore_name=mm_call_state >*/
+ MM_CALL_STATE_UNKNOWN = 0,
+ MM_CALL_STATE_DIALING = 1,
+ MM_CALL_STATE_RINGING_OUT = 2,
+ MM_CALL_STATE_RINGING_IN = 3,
+ MM_CALL_STATE_ACTIVE = 4,
+ MM_CALL_STATE_HELD = 5,
+ MM_CALL_STATE_WAITING = 6,
+ MM_CALL_STATE_TERMINATED = 7
+} MMCallState;
+
+/**
+ * MMCallStateReason:
+ * @MM_CALL_STATE_REASON_UNKNOWN: Default value for a new outgoing call.
+ * @MM_CALL_STATE_REASON_OUTGOING_STARTED: Outgoing call is started.
+ * @MM_CALL_STATE_REASON_INCOMING_NEW: Received a new incoming call.
+ * @MM_CALL_STATE_REASON_ACCEPTED: Dialing or Ringing call is accepted.
+ * @MM_CALL_STATE_REASON_TERMINATED: Call is correctly terminated.
+ * @MM_CALL_STATE_REASON_REFUSED_OR_BUSY: Remote peer is busy or refused call.
+ * @MM_CALL_STATE_REASON_ERROR: Wrong number or generic network error.
+ * @MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED: Error setting up audio channel. Since 1.10.
+ * @MM_CALL_STATE_REASON_TRANSFERRED: Call has been transferred. Since 1.12.
+ * @MM_CALL_STATE_REASON_DEFLECTED: Call has been deflected to a new number. Since 1.12.
+ *
+ * Reason for the state change in the call.
+ *
+ * Since: 1.6
+ */
+typedef enum { /*< underscore_name=mm_call_state_reason >*/
+ MM_CALL_STATE_REASON_UNKNOWN = 0,
+ MM_CALL_STATE_REASON_OUTGOING_STARTED = 1,
+ MM_CALL_STATE_REASON_INCOMING_NEW = 2,
+ MM_CALL_STATE_REASON_ACCEPTED = 3,
+ MM_CALL_STATE_REASON_TERMINATED = 4,
+ MM_CALL_STATE_REASON_REFUSED_OR_BUSY = 5,
+ MM_CALL_STATE_REASON_ERROR = 6,
+ MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED = 7,
+ MM_CALL_STATE_REASON_TRANSFERRED = 8,
+ MM_CALL_STATE_REASON_DEFLECTED = 9,
+} MMCallStateReason;
+
+/**
+ * MMCallDirection:
+ * @MM_CALL_DIRECTION_UNKNOWN: unknown.
+ * @MM_CALL_DIRECTION_INCOMING: call from network.
+ * @MM_CALL_DIRECTION_OUTGOING: call to network.
+ *
+ * Direction of the call.
+ *
+ * Since: 1.6
+ */
+typedef enum { /*< underscore_name=mm_call_direction >*/
+ MM_CALL_DIRECTION_UNKNOWN = 0,
+ MM_CALL_DIRECTION_INCOMING = 1,
+ MM_CALL_DIRECTION_OUTGOING = 2
+} MMCallDirection;
+
+/**
+ * MMModemFirmwareUpdateMethod:
+ * @MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE: No method specified.
+ * @MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT: Device supports fastboot-based update.
+ * @MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC: Device supports QMI PDC based update.
+ * @MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU: Device supports MBIM QDU based update. Since 1.18.
+ * @MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE: Device supports Firehose based update. Since 1.18.
+ *
+ * Type of firmware update method supported by the module.
+ *
+ * Since: 1.10
+ */
+typedef enum { /*< underscore_name=mm_modem_firmware_update_method >*/
+ MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE = 0,
+ MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT = 1 << 0,
+ MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC = 1 << 1,
+ MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU = 1 << 2,
+ MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE = 1 << 3,
+} MMModemFirmwareUpdateMethod;
+
+/**
+ * MMBearerMultiplexSupport:
+ * @MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN: Unknown.
+ * @MM_BEARER_MULTIPLEX_SUPPORT_NONE: No multiplex support should be used.
+ * @MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED: If available, multiplex support should be used.
+ * @MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED: Multiplex support must be used or otherwise the connection attempt will fail.
+ *
+ * Multiplex support requested by the user.
+ *
+ * Since: 1.18
+ */
+typedef enum { /*< underscore_name=mm_bearer_multiplex_support >*/
+ MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN = 0,
+ MM_BEARER_MULTIPLEX_SUPPORT_NONE = 1,
+ MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED = 2,
+ MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED = 3,
+} MMBearerMultiplexSupport;
+
+/**
+ * MMBearerApnType:
+ * @MM_BEARER_APN_TYPE_NONE: Unknown or unsupported.
+ * @MM_BEARER_APN_TYPE_INITIAL: APN used for the initial attach procedure.
+ * @MM_BEARER_APN_TYPE_DEFAULT: Default connection APN providing access to the Internet.
+ * @MM_BEARER_APN_TYPE_IMS: APN providing access to IMS services.
+ * @MM_BEARER_APN_TYPE_MMS: APN providing access to MMS services.
+ * @MM_BEARER_APN_TYPE_MANAGEMENT: APN providing access to over-the-air device management procedures.
+ * @MM_BEARER_APN_TYPE_VOICE: APN providing access to voice-over-IP services.
+ * @MM_BEARER_APN_TYPE_EMERGENCY: APN providing access to emergency services.
+ * @MM_BEARER_APN_TYPE_PRIVATE: APN providing access to private networks.
+ * @MM_BEARER_APN_TYPE_PURCHASE: APN providing access to over-the-air activation sites. Since 1.20.
+ * @MM_BEARER_APN_TYPE_VIDEO_SHARE: APN providing access to video sharing service. Since 1.20.
+ * @MM_BEARER_APN_TYPE_LOCAL: APN providing access to a local connection with the device. Since 1.20.
+ * @MM_BEARER_APN_TYPE_APP: APN providing access to certain applications allowed by mobile operators. Since 1.20.
+ * @MM_BEARER_APN_TYPE_XCAP: APN providing access to XCAP provisioning on IMS services. Since 1.20.
+ * @MM_BEARER_APN_TYPE_TETHERING: APN providing access to mobile hotspot tethering. Since 1.20.
+ *
+ * Purpose of the APN used in a given Bearer.
+ *
+ * This information may be stored in the device configuration (e.g. if carrier
+ * specific configurations have been enabled for the SIM in use), or provided
+ * explicitly by the user.
+ *
+ * If the mask of types includes %MM_BEARER_APN_TYPE_DEFAULT, it is expected
+ * that the connection manager will include a default route through the specific
+ * bearer connection to the public Internet.
+ *
+ * For any other mask type, it is expected that the connection manager will
+ * not setup a default route and will therefore require additional custom
+ * routing rules to provide access to the different services. E.g. a bearer
+ * connected with %MM_BEARER_APN_TYPE_MMS will probably require an explicit
+ * additional route in the host to access the MMSC server at the address
+ * specified by the operator. If this address relies on a domain name instead
+ * of a fixed IP address, the name resolution should be performed using the
+ * DNS servers specified in the corresponding bearer connection settings.
+ *
+ * If not explicitly specified during a connection attempt, the connection
+ * manager should be free to treat it with its own logic. E.g. a good default
+ * could be to treat the first connection as %MM_BEARER_APN_TYPE_DEFAULT (with
+ * a default route) and any other additional connection as
+ * %MM_BEARER_APN_TYPE_PRIVATE (without a default route).
+ *
+ * Since: 1.18
+ */
+typedef enum { /*< underscore_name=mm_bearer_apn_type >*/
+ MM_BEARER_APN_TYPE_NONE = 0,
+ MM_BEARER_APN_TYPE_INITIAL = 1 << 0,
+ MM_BEARER_APN_TYPE_DEFAULT = 1 << 1,
+ MM_BEARER_APN_TYPE_IMS = 1 << 2,
+ MM_BEARER_APN_TYPE_MMS = 1 << 3,
+ MM_BEARER_APN_TYPE_MANAGEMENT = 1 << 4,
+ MM_BEARER_APN_TYPE_VOICE = 1 << 5,
+ MM_BEARER_APN_TYPE_EMERGENCY = 1 << 6,
+ MM_BEARER_APN_TYPE_PRIVATE = 1 << 7,
+ MM_BEARER_APN_TYPE_PURCHASE = 1 << 8,
+ MM_BEARER_APN_TYPE_VIDEO_SHARE = 1 << 9,
+ MM_BEARER_APN_TYPE_LOCAL = 1 << 10,
+ MM_BEARER_APN_TYPE_APP = 1 << 11,
+ MM_BEARER_APN_TYPE_XCAP = 1 << 12,
+ MM_BEARER_APN_TYPE_TETHERING = 1 << 13,
+} MMBearerApnType;
+
+/**
+ * MMModem3gppPacketServiceState:
+ * @MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN: Unknown.
+ * @MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED: Detached.
+ * @MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED: Attached.
+ *
+ * The packet domain service state.
+ *
+ * Since: 1.20
+ */
+typedef enum { /*< underscore_name=mm_modem_3gpp_packet_service_state >*/
+ MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN = 0,
+ MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED = 1,
+ MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED = 2,
+} MMModem3gppPacketServiceState;
+
#endif /* _MODEMMANAGER_ENUMS_H_ */
diff --git a/include/ModemManager-errors.h b/include/ModemManager-errors.h
index f2dc0b62..854e83ac 100644
--- a/include/ModemManager-errors.h
+++ b/include/ModemManager-errors.h
@@ -31,12 +31,59 @@
* ModemManager interface.
**/
-#define MM_CORE_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Core"
+/**
+ * MM_CORE_ERROR_DBUS_PREFIX:
+ *
+ * DBus prefix for #MMCoreError errors.
+ *
+ * Since: 1.0
+ */
+#define MM_CORE_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Core"
+
+/**
+ * MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX:
+ *
+ * DBus prefix for #MMMobileEquipmentError errors.
+ *
+ * Since: 1.0
+ */
#define MM_MOBILE_EQUIPMENT_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".MobileEquipment"
-#define MM_CONNECTION_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Connection"
-#define MM_SERIAL_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Serial"
-#define MM_MESSAGE_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Message"
-#define MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".CdmaActivation"
+
+/**
+ * MM_CONNECTION_ERROR_DBUS_PREFIX:
+ *
+ * DBus prefix for #MMConnectionError errors.
+ *
+ * Since: 1.0
+ */
+#define MM_CONNECTION_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Connection"
+
+/**
+ * MM_SERIAL_ERROR_DBUS_PREFIX:
+ *
+ * DBus prefix for #MMSerialError errors.
+ *
+ * Since: 1.0
+ */
+#define MM_SERIAL_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Serial"
+
+/**
+ * MM_MESSAGE_ERROR_DBUS_PREFIX:
+ *
+ * DBus prefix for #MMMessageError errors.
+ *
+ * Since: 1.0
+ */
+#define MM_MESSAGE_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".Message"
+
+/**
+ * MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX:
+ *
+ * DBus prefix for #MMCdmaActivationError errors.
+ *
+ * Since: 1.0
+ */
+#define MM_CDMA_ACTIVATION_ERROR_DBUS_PREFIX MM_DBUS_ERROR_PREFIX ".CdmaActivation"
/**
* MMCoreError:
@@ -56,6 +103,8 @@
* @MM_CORE_ERROR_EXISTS: Item already exists.
*
* Common errors that may be reported by ModemManager.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_core_error >*/
MM_CORE_ERROR_FAILED = 0, /*< nick=Failed >*/
@@ -112,90 +161,303 @@ typedef enum { /*< underscore_name=mm_core_error >*/
* @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK: Service provider personalisation PUK required.
* @MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN: Corporate personalisation PIN required.
* @MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK: Corporate personalisation PUK required.
+ * @MM_MOBILE_EQUIPMENT_ERROR_HIDDEN_KEY_REQUIRED: Hidden key required. Since: 1.8.
+ * @MM_MOBILE_EQUIPMENT_ERROR_EAP_METHOD_NOT_SUPPORTED: EAP method not supported. Since: 1.8.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS: Incorrect parameters. Since: 1.8.
+ * @MM_MOBILE_EQUIPMENT_ERROR_COMMAND_DISABLED: Command implemented but currently disabled. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_COMMAND_ABORTED: Command aborted by user. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ATTACHED_RESTRICTED: Not attached to network due to MT functionality restrictions. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_EMERGENCY_ONLY: Modem not allowed, MT restricted to emergency calls only. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_RESTRICTED: Operation not allowed because of MT functionality restrictions. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_FIXED_DIAL_NUMBER_ONLY: Fixed dial number only allowed; called number is not a fixed dial number. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_OUT_OF_SERVICE: Temporarily out of service due to other MT usage. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_LANGUAGE_OR_ALPHABET_NOT_SUPPORTED: Language or alphabet not supported. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNEXPECTED_DATA_VALUE: Unexpected data value. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SYSTEM_FAILURE: System failure. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_DATA_MISSING: Data missing. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_CALL_BARRED: Call barred. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_WAITING_INDICATION_SUBSCRIPTION_FAILURE: Message waiting indication subscription failure. Since 1.18.
* @MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN: Unknown.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR: IMSI unknown in HLR.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR: IMSI unknown in VLR.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS: Illegal MS.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME: Illegal ME.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED: GPRS service not allowed.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED: PLMN not allowed.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED: Location area not allowed.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED: Roaming not allowed in this location area.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA: No cells in this location area.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE: Network failure.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION: Congestion.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES: Insufficient resources.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN: Missing or unknown APN.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED: User authentication failed.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED: Service option not supported.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED: Requested service option not subscribed.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER: Service option temporarily out of order.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN: Unspecified GPRS error.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE: PDP authentication failure.
- * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS: Invalid mobile class.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS: IMSI unknown in HLR (CS, GPRS, UMTS); IMSI unknown in HSS (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE: Illegal MS (CS, GPRS, UMTS); Illegal UE (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR: IMSI unknown in VLR. Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED: IMEI not accepted (CS, GPRS, UMTS, EPS); PEI not accepted (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME: Illegal ME (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED: GPRS services not allowed (CS, GPRS, UMTS); EPS services not allowed (EPS); 5GS services not allowed (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED: GPRS and non-GPRS services not allowed (CS, GPRS, UMTS); EPS and non-EPS services not allowed (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK: MS identity cannot be derived from network (CS, GPRS, UMTS; UE identity cannot be derived from network (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED: Implicitly detached (CS, GPRS, UMTS, EPS); implicitly degistered (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED: PLMN not allowed (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED: Location area not allowed (CS, GPRS, UMTS); Tracking area not allowed (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA: Roaming not allowed in this location area (CS, GPRS, UMTS); Roaming not allowed in this tracking area (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN: GPRS services not allowed in this PLMN (CS, GPRS, UMTS); EPS services not allowed in this PLMN (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA: No suitable cells in this location area (CS, GPRS, UMTS); no suitable cells in this tracking area (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE: MSC temporarily not reachable (CS, GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH: Network failure during attach (CS, GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_CS_DOMAIN_UNAVAILABLE: CS domain not available (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ESM_FAILURE: ESM failure (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_CONGESTION: Congestion (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MBMS_BEARER_CAPABILITIES_INSUFFICIENT_FOR_SERVICE: MBMS bearer capabilities insufficient for service (GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG: Not authorized for this CSG (CS, GPRS, UMTS, EPS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES: Insufficient resources (GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN: Missing or unknown APN (GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE: Unknown PDP address or PDP type (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED: User authentication or authorization failed (GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW: Activation rejected by GGSN, Serving GW or PDN GW (GPRS, UMTS); activation rejected by Serving GW or PDN GW (EPS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED: Activation rejected, unspecified (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED: Service option not supported (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED: Requested service option not subscribed (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER: Service option temporarily out of order (CS, GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE: NSAPI out of order (GPRS, UMTS); PTI out of order (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION: Regular deactivation (GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED: EPS Qos not accepted (EPS); 5GS QoS not accepted (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_CALL_CANNOT_BE_IDENTIFIED: Call cannot be identified (CS, GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_CS_SERVICE_TEMPORARILY_UNAVAILABLE: CS service temporarily unavailable (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED: Feature not supported (GPRS, UMTS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION: Semantic error in TFT operation (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION: Syntactical error in TFT operation (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT: Unknown PDP context (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER: Semantic errors in packet filter (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER: Syntactical error in packet filter (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED: PDP context without TFT already activated (GPRS, UMTS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MULTICAST_GROUP_MEMBERSHIP_TIMEOUT: Multicast group membership timeout (GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN: Unspecified GPRS error (CS, GPRS, UMTS).
+ * @MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE: PDP authentication failure (GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS: Invalid mobile class (CS, GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY: Last PDN disconnection not allowed, legacy value defined before 3GPP Rel-11 (EPS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED: Last PDN disconnection not allowed (EPS). Since: 1.8.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE: Semantically incorrect message (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION: Invalid mandatory information (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED: Message type non-existent or not implemented (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR: Conditional IE error (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR: Unspecified protocol error (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING: Operator determined barring (GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED: Maximum number of PDP contexts reached (GPRS, UMTS); maximum number of EPS bearers reached (EPS); maximum number of PDU sessions reached (5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED: Requested APN not supported in current RAT and PLMN combination (GPRS, UMTS, EPS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION: Request rejected, bearer control mode violation (GPRS, UMTS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_QCI_OR_5QI_VALUE: Unsupported QCI value (EPS); unsupported 5QI value (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_USER_DATA_VIA_CONTROL_PLANE_CONGESTED: User data transmission via control plane is congested (GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SMS_PROVIDED_VIA_GPRS_IN_ROUTING_AREA: SMS provided via GPRS in routing area (CS, GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_PTI_VALUE: Invalid PTI value (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED: No PDP context activated (CS, GPRS, UMTS); no bearer context activated (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE: Message not compatible with protocol state (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_RECOVERY_ON_TIMER_EXPIRY: Recovery on timer expiry (CS, GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE: Invalid transaction identifier value (GPRS, UMTS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_AUTHORIZED_IN_PLMN: Requested service option is not authorized in this PLMN (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ACTIVATION: Network failure during context activation (GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_REACTIVATION_REQUESTED: Reactivation requested (GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED: PDP type IPv4 only allowed (GPRS, UMTS); PDN type IPv4 only allowed (EPS); PDU session type IPv4 only allowed (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED: PDP type IPv6 only allowed (GPRS, UMTS); PDN type IPv6 only allowed (EPS); PDU session type IPv6 only allowed (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED: Single address bearers only allowed (GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_COLLISION_WITH_NETWORK_INITIATED_REQUEST: Collision with network initiated request (GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IPV4V6_ONLY_ALLOWED: PDP type IPv4v6 only allowed (GPRS, UMTS); PDN type IPv4v6 only allowed (EPS); PDU session type IPv4v6 only allowed (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NON_IP_ONLY_ALLOWED: PDP type non-IP only allowed (GPRS, UMTS); PDN type non-IP only allowed (EPS); PDU session type unstructured only allowed (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_BEARER_HANDLING_UNSUPPORTED: Bearer handling not supported (GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE: APN restriction value incompatible with active PDP context (GPRS, UMTS); APN restriction value incompatible with active EPS bearer context (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED: Multiple accesses to PDN connection not allowed (GPRS, UMTS, EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED: ESM information not received (EPS).Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT: PDN connection does not exist (EPS); PDU session does not exist (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED: Multiple PDN connections for a given APN not allowed (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SEVERE_NETWORK_FAILURE: Severe network failure (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN: Insufficient resources for specific slice and DNN (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_SSC_MODE: Not supported SSC mode (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE: Insufficient resources for specific slice (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE: Message type not compatible with protocol state (CS, GPRS, UMTS, EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED: Information element non-existent or not implemented (CS, GPRS, UMTS, EPS, 5GS). Since: 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_N1_MODE_NOT_ALLOWED: N1 mode not allowed. (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_RESTRICTED_SERVICE_AREA: Restricted service area (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_LADN_UNAVAILABLE: LADN not available (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_DNN_IN_SLICE: Missing or unknown DNN in a slice (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NGKSI_ALREADY_IN_USE: ngKSI already in use (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PAYLOAD_NOT_FORWARDED: Payload was not forwarded (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NON_3GPP_ACCESS_TO_5GCN_NOT_ALLOWED: Non-3GPP access to 5GCN not allowed (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SERVING_NETWORK_NOT_AUTHORIZED: Serving network not authorized (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_DNN_NOT_SUPPORTED_IN_SLICE: DNN not supported or not subscribed in the slice (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_USER_PLANE_RESOURCES_FOR_PDU_SESSION: Insufficient user plane resources for PDU session (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_OUT_OF_LADN_SERVICE_AREA: Out of LADN service area (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PTI_MISMATCH: PTI mismatch (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_MAX_DATA_RATE_FOR_USER_PLANE_INTEGRITY_TOO_LOW: Maximum data rate per UE for user-plane integrity protection is too low (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_QOS_OPERATION: Semantic error in QoS operation (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_QOS_OPERATION: Semantic error in QoS operation (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_INVALID_MAPPED_EPS_BEARER_IDENTITY: Invalid mapped EPS bearer identity (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_5GCN_REQUIRED: Redirection to 5GCN required (EPS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_EPC_REQUIRED: Redirection to EPC required (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_UNAUTHORIZED_FOR_SNPN: Temporarily not authorized for this SNPN (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_PERMANENTLY_UNAUTHORIZED_FOR_SNPN: Permanently not authorized for this SNPN (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_ETHERNET_ONLY_ALLOWED: PDN type Ethernet only allowed (EPS, 5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_UNAUTHORIZED_FOR_CAG: Not authorized for this CAG or authorized for CAG cells only (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK_SLICES_AVAILABLE: No network slices available (5GS). Since 1.18.
+ * @MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED: Wireline access area not allowed (5GS). Since 1.18.
+ *
+ * Enumeration of Mobile Equipment errors, as defined in 3GPP TS 27.007 v17.1.0,
+ * section 9.2 (Mobile termination error result code +CME ERROR).
*
- * Enumeration of Mobile Equipment errors, as defined in 3GPP TS 07.07 version 7.8.0.
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_mobile_equipment_error >*/
- /* General errors */
- MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE = 0, /*< nick=PhoneFailure >*/
- MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION = 1, /*< nick=NoConnection >*/
- MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED = 2, /*< nick=LinkReserved >*/
- MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED = 3, /*< nick=NotAllowed >*/
- MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED = 4, /*< nick=NotSupported >*/
- MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN = 5, /*< nick=PhSimPin >*/
- MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN = 6, /*< nick=PhFsimPin >*/
- MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK = 7, /*< nick=PhFsimPuk >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED = 10, /*< nick=SimNotInserted >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN = 11, /*< nick=SimPin >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK = 12, /*< nick=SimPuk >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE = 13, /*< nick=SimFailure >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY = 14, /*< nick=SimBusy >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG = 15, /*< nick=SimWrong >*/
- MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD = 16, /*< nick=IncorrectPassword >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2 = 17, /*< nick=SimPin2 >*/
- MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2 = 18, /*< nick=SimPuk2 >*/
- MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL = 20, /*< nick=MemoryFull >*/
- MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX = 21, /*< nick=InvalidIndex >*/
- MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND = 22, /*< nick=NotFound >*/
- MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE = 23, /*< nick=MemoryFailure >*/
- MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG = 24, /*< nick=TextTooLong >*/
- MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS = 25, /*< nick=InvalidChars >*/
- MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG = 26, /*< nick=DialStringTooLong >*/
- MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID = 27, /*< nick=DialStringInvalid >*/
- MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK = 30, /*< nick=NoNetwork >*/
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT = 31, /*< nick=NetworkTimeout >*/
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED = 32, /*< nick=NetworkNotAllowed >*/
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN = 40, /*< nick=NetworkPin >*/
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK = 41, /*< nick=NetworkPuk >*/
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN = 42, /*< nick=NetworkSubsetPin >*/
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK = 43, /*< nick=NetworkSubsetPuk >*/
- MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN = 44, /*< nick=ServicePin >*/
- MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK = 45, /*< nick=ServicePuk >*/
- MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN = 46, /*< nick=CorpPin >*/
- MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK = 47, /*< nick=CorpPuk >*/
- MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN = 100, /*< nick=Unknown >*/
- /* GPRS related errors */
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR = 102, /*< nick=GprsImsiUnknownInHlr >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS = 103, /*< nick=GprsIllegalMs >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR = 104, /*< nick=GprsImsiUnknownInVlr >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME = 106, /*< nick=GprsIllegalMe >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED = 107, /*< nick=GprsServiceNotAllowed >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED = 111, /*< nick=GprsPlmnNotAllowed >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED = 112, /*< nick=GprsLocationNotAllowed >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED = 113, /*< nick=GprsRomaingNotAllowed >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA = 115, /*< nick=GprsNoCellsInLocationArea >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE = 117, /*< nick=GprsNetworkFailure >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION = 122, /*< nick=GprsCongestion >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_INSUFFICIENT_RESOURCES = 126, /*< nick=GprsInsufficientResources >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN = 127, /*< nick=GprsMissingOrUnknownApn >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED = 129, /*< nick=GprsUserAuthenticationFailed >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED = 132, /*< nick=GprsServiceOptionNotSupported >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED = 133, /*< nick=GprsServiceOptionNotSubscribed >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER = 134, /*< nick=GprsServiceOptionOutOfOrder >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN = 148, /*< nick=GprsUnknown >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE = 149, /*< nick=GprsPdpAuthFailure >*/
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS = 150, /*< nick=GprsInvalidMobileClass >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE = 0, /*< nick=PhoneFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION = 1, /*< nick=NoConnection >*/
+ MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED = 2, /*< nick=LinkReserved >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED = 3, /*< nick=NotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED = 4, /*< nick=NotSupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN = 5, /*< nick=PhSimPin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN = 6, /*< nick=PhFsimPin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK = 7, /*< nick=PhFsimPuk >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED = 10, /*< nick=SimNotInserted >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN = 11, /*< nick=SimPin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK = 12, /*< nick=SimPuk >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE = 13, /*< nick=SimFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY = 14, /*< nick=SimBusy >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG = 15, /*< nick=SimWrong >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD = 16, /*< nick=IncorrectPassword >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2 = 17, /*< nick=SimPin2 >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2 = 18, /*< nick=SimPuk2 >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL = 20, /*< nick=MemoryFull >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX = 21, /*< nick=InvalidIndex >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND = 22, /*< nick=NotFound >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE = 23, /*< nick=MemoryFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG = 24, /*< nick=TextTooLong >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS = 25, /*< nick=InvalidChars >*/
+ MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG = 26, /*< nick=DialStringTooLong >*/
+ MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID = 27, /*< nick=DialStringInvalid >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK = 30, /*< nick=NoNetwork >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT = 31, /*< nick=NetworkTimeout >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED = 32, /*< nick=NetworkNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN = 40, /*< nick=NetworkPin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK = 41, /*< nick=NetworkPuk >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN = 42, /*< nick=NetworkSubsetPin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK = 43, /*< nick=NetworkSubsetPuk >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN = 44, /*< nick=ServicePin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK = 45, /*< nick=ServicePuk >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN = 46, /*< nick=CorpPin >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK = 47, /*< nick=CorpPuk >*/
+ MM_MOBILE_EQUIPMENT_ERROR_HIDDEN_KEY_REQUIRED = 48, /*< nick=HiddenKeyRequired >*/
+ MM_MOBILE_EQUIPMENT_ERROR_EAP_METHOD_NOT_SUPPORTED = 49, /*< nick=EapMethodNotSupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS = 50, /*< nick=IncorrectParameters >*/
+ MM_MOBILE_EQUIPMENT_ERROR_COMMAND_DISABLED = 51, /*< nick=CommandDisabled >*/
+ MM_MOBILE_EQUIPMENT_ERROR_COMMAND_ABORTED = 52, /*< nick=CommandAborted >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_ATTACHED_RESTRICTED = 53, /*< nick=NotAttachedRestricted >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_EMERGENCY_ONLY = 54, /*< nick=NotAllowedEmergencyOnly >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_RESTRICTED = 55, /*< nick=NotAllowedRestricted >*/
+ MM_MOBILE_EQUIPMENT_ERROR_FIXED_DIAL_NUMBER_ONLY = 56, /*< nick=FixedDialNumberOnly >*/
+ MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_OUT_OF_SERVICE = 57, /*< nick=TemporarilyOutOfService >*/
+ MM_MOBILE_EQUIPMENT_ERROR_LANGUAGE_OR_ALPHABET_NOT_SUPPORTED = 58, /*< nick=LanguageOrAlphabetNotSupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNEXPECTED_DATA_VALUE = 59, /*< nick=UnexpectedDataValue >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SYSTEM_FAILURE = 60, /*< nick=SystemFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_DATA_MISSING = 61, /*< nick=DataMissing >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CALL_BARRED = 62, /*< nick=CallBarred >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_WAITING_INDICATION_SUBSCRIPTION_FAILURE = 63, /*< nick=MessageWaitingIndicationSubscriptionFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN = 100, /*< nick=Unknown >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS = 102, /*< nick=ImsiUnknownInHss >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE = 103, /*< nick=IllegalUe >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR = 104, /*< nick=ImsiUnknownInVlr >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED = 105, /*< nick=ImeiNotAccepted >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME = 106, /*< nick=IllegalMe >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED = 107, /*< nick=PsServicesNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED = 108, /*< nick=PsAndNonPsServicesNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK = 109, /*< nick=UeIdentityNotDerivedFromNetwork >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED = 110, /*< nick=ImplicitlyDetached >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED = 111, /*< nick=PlmnNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED = 112, /*< nick=AreaNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA = 113, /*< nick=RoamingNotAllowedInArea >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN = 114, /*< nick=PsServicesNotAllowedInPlmn >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA = 115, /*< nick=NoCellsInArea >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE = 116, /*< nick=MscTemporarilyNotReachable >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH = 117, /*< nick=NetworkFailureAttach >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CS_DOMAIN_UNAVAILABLE = 118, /*< nick=CsDomainUnavailable >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ESM_FAILURE = 119, /*< nick=EsmFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CONGESTION = 122, /*< nick=Congestion >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MBMS_BEARER_CAPABILITIES_INSUFFICIENT_FOR_SERVICE = 124, /*< nick=MbmsBearerCapabilitiesInsufficientForService >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG = 125, /*< nick=NotAuthorizedForCsg >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES = 126, /*< nick=InsufficientResources >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN = 127, /*< nick=MissingOrUnknownApn >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE = 128, /*< nick=UnknownPdpAddressOrType >*/
+ MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED = 129, /*< nick=UserAuthenticationFailed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW = 130, /*< nick=ActivationRejectedByGgsnOrGw >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED = 131, /*< nick=ActivationRejectedUnspecified >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED = 132, /*< nick=ServiceOptionNotSupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED = 133, /*< nick=ServiceOptionNotSubscribed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER = 134, /*< nick=ServiceOptionOutOfOrder >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE = 135, /*< nick=NsapiOrPtiAlreadyInUse >*/
+ MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION = 136, /*< nick=RegularDeactivation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED = 137, /*< nick=QosNotAccepted >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CALL_CANNOT_BE_IDENTIFIED = 138, /*< nick=CallCannotBeIdentified >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CS_SERVICE_TEMPORARILY_UNAVAILABLE = 139, /*< nick=CsServiceTemporarilyUnavailable >*/
+ MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED = 140, /*< nick=FeatureNotSupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION = 141, /*< nick=SemanticErrorInTftOperation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION = 142, /*< nick=SyntacticalErrorInTftOperation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT = 143, /*< nick=UnknownPdpContext >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER = 144, /*< nick=SemanticErrorsInPacketFilter >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER = 145, /*< nick=SyntacticalErrorsInPacketFilter >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED = 146, /*< nick=PdpContextWithoutTftAlreadyActivated >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MULTICAST_GROUP_MEMBERSHIP_TIMEOUT = 147, /*< nick=MulticastGroupMembershipTimeout >*/
+ MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN = 148, /*< nick=GprsUnknown >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE = 149, /*< nick=PdpAuthFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS = 150, /*< nick=InvalidMobileClass >*/
+ MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY = 151, /*< nick=LastPdnDisconnectionNotAllowedLegacy >*/
+ MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED = 171, /*< nick=LastPdnDisconnectionNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE = 172, /*< nick=SemanticallyIncorrectMessage >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION = 173, /*< nick=InvalidMandatoryInformation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED = 174, /*< nick=MessageTypeNotImplemented >*/
+ MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR = 175, /*< nick=ConditionalIeError >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR = 176, /*< nick=UnspecifiedProtocolError >*/
+ MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING = 177, /*< nick=OperatorDeterminedBarring >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED = 178, /*< nick=MaximumNumberOfBearersReached >*/
+ MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED = 179, /*< nick=RequestedApnNotSupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION = 180, /*< nick=RequestRejectedBcmViolation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_QCI_OR_5QI_VALUE = 181, /*< nick=UnsupportedQciOr5qiValue >*/
+ MM_MOBILE_EQUIPMENT_ERROR_USER_DATA_VIA_CONTROL_PLANE_CONGESTED = 182, /*< nick=UserDataViaControlPlaneCongested >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SMS_PROVIDED_VIA_GPRS_IN_ROUTING_AREA = 183, /*< nick=SmsProvidedViaGprsInRoutingArea >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_PTI_VALUE = 184, /*< nick=InvalidPtiValue >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED = 185, /*< nick=NoBearerActivated >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 186, /*< nick=MessageNotCompatibleWithProtocolState >*/
+ MM_MOBILE_EQUIPMENT_ERROR_RECOVERY_ON_TIMER_EXPIRY = 187, /*< nick=RecoveryOnTimerExpiry >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE = 188, /*< nick=InvalidTransactionIdValue >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_AUTHORIZED_IN_PLMN = 189, /*< nick=ServiceOptionNotAuthorizedInPlmn >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ACTIVATION = 190, /*< nick=NetworkFailureActivation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_REACTIVATION_REQUESTED = 191, /*< nick=ReactivationRequested >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED = 192, /*< nick=Ipv4OnlyAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED = 193, /*< nick=Ipv6OnlyAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED = 194, /*< nick=SingleAddressBearersOnlyAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_COLLISION_WITH_NETWORK_INITIATED_REQUEST = 195, /*< nick=CollisionWithNetworkInitiatedRequest >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IPV4V6_ONLY_ALLOWED = 196, /*< nick=Ipv4v6OnlyAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NON_IP_ONLY_ALLOWED = 197, /*< nick=NonIpOnlyAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_BEARER_HANDLING_UNSUPPORTED = 198, /*< nick=BearerHandlingUnsupported >*/
+ MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE = 199, /*< nick=ApnRestrictionIncompatible >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED = 200, /*< nick=MultipleAccessToPdnConnectionNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED = 201, /*< nick=EsmInformationNotReceived >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT = 202, /*< nick=PdnConnectionNonexistent >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED = 203, /*< nick=MultiplePdnConnectionSameApnNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SEVERE_NETWORK_FAILURE = 204, /*< nick=SevereNetworkFailure >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN = 205, /*< nick=InsufficientResourcesForSliceAndDnn >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_SSC_MODE = 206, /*< nick=UnsupportedSscMode >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE = 207, /*< nick=InsufficientResourcesForSlice >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 208, /*< nick=MessageTypeNotCompatibleWithProtocolState >*/
+ MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED = 209, /*< nick=IeNotImplemented >*/
+ MM_MOBILE_EQUIPMENT_ERROR_N1_MODE_NOT_ALLOWED = 210, /*< nick=N1ModeNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_RESTRICTED_SERVICE_AREA = 211, /*< nick=RestrictedServiceArea >*/
+ MM_MOBILE_EQUIPMENT_ERROR_LADN_UNAVAILABLE = 212, /*< nick=LadnUnavailable >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_DNN_IN_SLICE = 213, /*< nick=MissingOrUnknownDnnInSlice >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NGKSI_ALREADY_IN_USE = 214, /*< nick=NkgsiAlreadyInUse >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PAYLOAD_NOT_FORWARDED = 215, /*< nick=PayloadNotForwarded >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NON_3GPP_ACCESS_TO_5GCN_NOT_ALLOWED = 216, /*< nick=Non3gppAccessTo5gcnNotAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SERVING_NETWORK_NOT_AUTHORIZED = 217, /*< nick=ServingNetworkNotAuthorized >*/
+ MM_MOBILE_EQUIPMENT_ERROR_DNN_NOT_SUPPORTED_IN_SLICE = 218, /*< nick=DnnNotSupportedInSlice >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_USER_PLANE_RESOURCES_FOR_PDU_SESSION = 219, /*< nick=InsufficientUserPlaneResourcesForPduSession >*/
+ MM_MOBILE_EQUIPMENT_ERROR_OUT_OF_LADN_SERVICE_AREA = 220, /*< nick=OutOfLadnServiceArea >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PTI_MISMATCH = 221, /*< nick=PtiMismatch >*/
+ MM_MOBILE_EQUIPMENT_ERROR_MAX_DATA_RATE_FOR_USER_PLANE_INTEGRITY_TOO_LOW = 222, /*< nick=MaxDataRateForUserPlaneIntegrityTooLow >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_QOS_OPERATION = 223, /*< nick=SemanticErrorInQosOperation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_QOS_OPERATION = 224, /*< nick=SyntacticalErrorInQosOperation >*/
+ MM_MOBILE_EQUIPMENT_ERROR_INVALID_MAPPED_EPS_BEARER_IDENTITY = 225, /*< nick=InvalidMappedEpsBearerIdentity >*/
+ MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_5GCN_REQUIRED = 226, /*< nick=RedirectionTo5gcnRequired >*/
+ MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_EPC_REQUIRED = 227, /*< nick=RedirectionToEpcRequired >*/
+ MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_UNAUTHORIZED_FOR_SNPN = 228, /*< nick=TemporarilyUnauthorizedForSnpn >*/
+ MM_MOBILE_EQUIPMENT_ERROR_PERMANENTLY_UNAUTHORIZED_FOR_SNPN = 229, /*< nick=PermanentlyUnauthorizedForSnpn >*/
+ MM_MOBILE_EQUIPMENT_ERROR_ETHERNET_ONLY_ALLOWED = 230, /*< nick=EthernetOnlyAllowed >*/
+ MM_MOBILE_EQUIPMENT_ERROR_UNAUTHORIZED_FOR_CAG = 231, /*< nick=UnauthorizedForCag >*/
+ MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK_SLICES_AVAILABLE = 232, /*< nick=NoNetworkSlicesAvailable >*/
+ MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED = 233, /*< nick=WirelineAccessAreaNotAllowed >*/
} MMMobileEquipmentError;
/**
@@ -207,6 +469,8 @@ typedef enum { /*< underscore_name=mm_mobile_equipment_error >*/
* @MM_CONNECTION_ERROR_NO_ANSWER: No answer.
*
* Connection errors that may be reported by ModemManager.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_connection_error >*/
MM_CONNECTION_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/
@@ -226,9 +490,11 @@ typedef enum { /*< underscore_name=mm_connection_error >*/
* @MM_SERIAL_ERROR_FLASH_FAILED: Could not flash the device.
* @MM_SERIAL_ERROR_NOT_OPEN: The serial port is not open.
* @MM_SERIAL_ERROR_PARSE_FAILED: The serial port specific parsing failed.
- * @MM_SERIAL_ERROR_FRAME_NOT_FOUND: The serial port reported that the frame marker wasn't found (e.g. for QCDM).
+ * @MM_SERIAL_ERROR_FRAME_NOT_FOUND: The serial port reported that the frame marker wasn't found (e.g. for QCDM). Since 1.6.
*
* Serial errors that may be reported by ModemManager.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_serial_error >*/
MM_SERIAL_ERROR_UNKNOWN = 0, /*< nick=Unknown >*/
@@ -269,6 +535,8 @@ typedef enum { /*< underscore_name=mm_serial_error >*/
* @MM_MESSAGE_ERROR_UNKNOWN: Unknown error.
*
* Enumeration of message errors, as defined in 3GPP TS 27.005 version 10 section 3.2.5.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_message_error >*/
/* 0 -> 127 per 3GPP TS 24.011 [6] clause E.2 */
@@ -312,6 +580,8 @@ typedef enum { /*< underscore_name=mm_message_error >*/
* @MM_CDMA_ACTIVATION_ERROR_START_FAILED: API call for initial activation failed.
*
* CDMA Activation errors.
+ *
+ * Since: 1.0
*/
typedef enum { /*< underscore_name=mm_cdma_activation_error >*/
MM_CDMA_ACTIVATION_ERROR_NONE = 0, /*< nick=None >*/
diff --git a/include/ModemManager-tags.h b/include/ModemManager-tags.h
new file mode 100644
index 00000000..decb4527
--- /dev/null
+++ b/include/ModemManager-tags.h
@@ -0,0 +1,282 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+/*
+ * NOTE! this file is NOT part of the installed ModemManager API.
+ *
+ * We keep this file under include/ because we want to build and
+ * expose the associated documentation.
+ */
+
+#ifndef MM_TAGS_H
+#define MM_TAGS_H
+
+/**
+ * SECTION:mm-tags
+ * @short_description: generic udev tags supported
+ *
+ * This section defines generic udev tags that are used by ModemManager,
+ * associated to full devices or to specific ports in a given device.
+ */
+
+/**
+ * ID_MM_CANDIDATE:
+ *
+ * This is a port-specific tag added automatically when all other
+ * ModemManager related tags have already been set.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_CANDIDATE "ID_MM_CANDIDATE"
+
+/**
+ * ID_MM_PHYSDEV_UID:
+ *
+ * This is a device-specific tag that allows users to 'name' modem
+ * devices with a predefined 'unique ID' string.
+ *
+ * When this tag is given per-port, the daemon will consider that all
+ * ports with the same UID value are associated to the same device.
+ * This is useful for e.g. modems that expose multiple RS232 ports
+ * connected to the system via different platform ports (or USB to
+ * RS232 adapters).
+ *
+ * This UID is exposed in
+ * the '<link linkend="gdbus-property-org-freedesktop-ModemManager1-Modem.Device">Device</link>'
+ * property and can then be used in mmcli calls to refer unequivocally
+ * to a specific device, regardless of its modem index, e.g.:
+ * $ mmcli --modem=UID ...
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PHYSDEV_UID "ID_MM_PHYSDEV_UID"
+
+/**
+ * ID_MM_DEVICE_PROCESS:
+ *
+ * This is a device-specific tag that allows explicitly requesting the
+ * processing of all ports exposed by the device. This tag is usually
+ * used by users when the daemon runs with WHITELIST-ONLY filter policy
+ * type, and is associated to the MM_FILTER_RULE_EXPLICIT_WHITELIST rule.
+ *
+ * This tag may also be specified in specific ports, e.g. when the modem
+ * exposes a single platform port without any parent device.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_DEVICE_PROCESS "ID_MM_DEVICE_PROCESS"
+
+/**
+ * ID_MM_DEVICE_IGNORE:
+ *
+ * This is a device-specific tag that allows explicitly requesting to
+ * ignore all ports exposed by the device.
+ *
+ * This tag was originally applicable to TTY ports and only when running
+ * in certain filter policy types. Since 1.12, this tag applies to all
+ * filter types and to all port types (not only TTYs), and is associated
+ * to the MM_FILTER_RULE_EXPLICIT_BLACKLIST rule.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_DEVICE_IGNORE "ID_MM_DEVICE_IGNORE"
+
+/**
+ * ID_MM_PORT_IGNORE:
+ *
+ * This is a port-specific tag that allows explicitly ignoring a given port
+ * in a device.
+ *
+ * This tag applies to all types of ports.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PORT_IGNORE "ID_MM_PORT_IGNORE"
+
+/**
+ * ID_MM_PORT_TYPE_AT_PRIMARY:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are AT ports to be used as primary control ports.
+ *
+ * This tag will also prevent QCDM probing on the port.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PORT_TYPE_AT_PRIMARY "ID_MM_PORT_TYPE_AT_PRIMARY"
+
+/**
+ * ID_MM_PORT_TYPE_AT_SECONDARY:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are AT ports to be used as secondary control ports.
+ *
+ * This tag will also prevent QCDM probing on the port.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PORT_TYPE_AT_SECONDARY "ID_MM_PORT_TYPE_AT_SECONDARY"
+
+/**
+ * ID_MM_PORT_TYPE_AT_PPP:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are AT ports to be used as data ports exclusively.
+ *
+ * This tag will also prevent QCDM probing on the port.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PORT_TYPE_AT_PPP "ID_MM_PORT_TYPE_AT_PPP"
+
+/**
+ * ID_MM_PORT_TYPE_QCDM:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are QCDM ports.
+ *
+ * The only purpose of this tag is to prevent AT probing in the port.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PORT_TYPE_QCDM "ID_MM_PORT_TYPE_QCDM"
+
+/**
+ * ID_MM_PORT_TYPE_GPS:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are GPS data ports where we expect to receive NMEA traces.
+ *
+ * This tag also prevents AT and QCDM probing in the port.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_PORT_TYPE_GPS "ID_MM_PORT_TYPE_GPS"
+
+/**
+ * ID_MM_PORT_TYPE_AUDIO:
+ *
+ * This is a port-specific tag applied to TTYs that we know in advance
+ * are audio ports.
+ *
+ * This tag also prevents AT and QCDM probing in the port.
+ *
+ * Since: 1.12
+ */
+#define ID_MM_PORT_TYPE_AUDIO "ID_MM_PORT_TYPE_AUDIO"
+
+/**
+ * ID_MM_PORT_TYPE_QMI:
+ *
+ * This is a port-specific tag applied to generic ports that we know in advance
+ * are QMI ports.
+ *
+ * This tag will also prevent other types of probing (e.g. AT, MBIM) on the
+ * port.
+ *
+ * This tag is not required for QMI ports exposed by the qmi_wwan driver.
+ *
+ * Since: 1.16
+ */
+#define ID_MM_PORT_TYPE_QMI "ID_MM_PORT_TYPE_QMI"
+
+/**
+ * ID_MM_PORT_TYPE_MBIM:
+ *
+ * This is a port-specific tag applied to generic ports that we know in advance
+ * are MBIM ports.
+ *
+ * This tag will also prevent other types of probing (e.g. AT, QMI) on the
+ * port.
+ *
+ * This tag is not required for MBIM ports exposed by the cdc_mbim driver.
+ *
+ * Since: 1.16
+ */
+#define ID_MM_PORT_TYPE_MBIM "ID_MM_PORT_TYPE_MBIM"
+
+/**
+ * ID_MM_TTY_BAUDRATE:
+ *
+ * This is a port-specific tag applied to TTYs that require a specific
+ * baudrate to work. USB modems will usually allow auto-bauding
+ * configuration, so this tag is really only meaningful to true RS232
+ * devices.
+ *
+ * The value of the tag should be the number of bauds per second to
+ * use when talking to the port, e.g. "115200". If not given, the
+ * default of 57600bps is assumed.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_TTY_BAUDRATE "ID_MM_TTY_BAUDRATE"
+
+/**
+ * ID_MM_TTY_FLOW_CONTROL:
+ *
+ * This is a port-specific tag applied to TTYs that require a specific
+ * flow control mechanism to work not only in data mode but also in
+ * control mode.
+ *
+ * The value of the tag should be either 'none', 'xon-xoff' or
+ * 'rts-cts', and must be a flow control value supported by the device
+ * where it's configured. If not given, it is assumed that the TTYs
+ * don't require any specific flow control setting in command mode.
+ *
+ * Since: 1.10
+ */
+#define ID_MM_TTY_FLOW_CONTROL "ID_MM_TTY_FLOW_CONTROL"
+
+/*
+ * The following symbols are deprecated. We don't add them to -compat
+ * because this -tags file is not really part of the installed API.
+ */
+
+#ifndef MM_DISABLE_DEPRECATED
+
+ /**
+ * ID_MM_TTY_BLACKLIST:
+ *
+ * This was a device-specific tag that allowed explicitly blacklisting
+ * devices that exposed TTY devices so that they were never probed.
+ *
+ * This tag was applicable only when running in certain filter policy types,
+ * and is no longer used since 1.18.
+ *
+ * Since: 1.12
+ * Deprecated: 1.18.0
+ */
+#define ID_MM_TTY_BLACKLIST "ID_MM_TTY_BLACKLIST"
+
+/**
+ * ID_MM_TTY_MANUAL_SCAN_ONLY:
+ *
+ * This was a device-specific tag that allowed explicitly greylisting
+ * devices that exposed TTY devices so that they were never probed
+ * automatically. Instead, an explicit manual scan request could
+ * be sent to the daemon so that the TTY ports exposed by the device
+ * were probed.
+ *
+ * This tag was applicable only when running in certain filter policy types,
+ * and is no longer used since 1.18.
+ *
+ * Since: 1.12
+ * Deprecated: 1.18.0
+ */
+#define ID_MM_TTY_MANUAL_SCAN_ONLY "ID_MM_TTY_MANUAL_SCAN_ONLY"
+
+#endif
+
+#endif /* MM_TAGS_H */
diff --git a/include/ModemManager-version.h.in b/include/ModemManager-version.h.in
index 0cb4341e..b3b20003 100644
--- a/include/ModemManager-version.h.in
+++ b/include/ModemManager-version.h.in
@@ -33,6 +33,8 @@
*
* Evaluates to the major version number of ModemManager which this source
* is compiled against.
+ *
+ * Since: 1.0
*/
#define MM_MAJOR_VERSION (@MM_MAJOR_VERSION@)
@@ -41,6 +43,8 @@
*
* Evaluates to the minor version number of ModemManager which this source
* is compiled against.
+ *
+ * Since: 1.0
*/
#define MM_MINOR_VERSION (@MM_MINOR_VERSION@)
@@ -49,6 +53,8 @@
*
* Evaluates to the micro version number of ModemManager which this source
* compiled against.
+ *
+ * Since: 1.0
*/
#define MM_MICRO_VERSION (@MM_MICRO_VERSION@)
@@ -58,8 +64,12 @@
* @minor: minor version (e.g. 2 for version 1.2.5)
* @micro: micro version (e.g. 5 for version 1.2.5)
*
+ * Checks the version of ModemManager at compile time.
+ *
* Returns: %TRUE if the version of the ModemManager header files
* is the same as or newer than the passed-in version.
+ *
+ * Since: 1.0
*/
#define MM_CHECK_VERSION(major,minor,micro) \
(MM_MAJOR_VERSION > (major) || \
diff --git a/include/ModemManager.h b/include/ModemManager.h
index 3a6eb43c..530bd210 100644
--- a/include/ModemManager.h
+++ b/include/ModemManager.h
@@ -1,6 +1,6 @@
/*
* ModemManager Interface Specification
- * version 0.8
+ * version 1.x
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -37,6 +37,9 @@
/* Public header with errors */
#include <ModemManager-errors.h>
+/* Public header with compatibility types and methods */
+#include <ModemManager-compat.h>
+
/* Public header with version info */
#include <ModemManager-version.h>
diff --git a/include/meson.build b/include/meson.build
new file mode 100644
index 00000000..c69d3ff1
--- /dev/null
+++ b/include/meson.build
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+include_inc = include_directories('.')
+
+mm_enums_header = files('ModemManager-enums.h')
+mm_errors_header = files('ModemManager-errors.h')
+
+headers = files(
+ 'ModemManager-compat.h',
+ 'ModemManager.h',
+)
+
+install_headers(
+ headers + mm_enums_header + mm_errors_header,
+ install_dir: mm_pkgincludedir,
+)
+
+header = 'ModemManager-names.h'
+
+mm_names_header = custom_target(
+ header,
+ input: [build_aux_dir / 'header-generator.xsl', mm_ifaces_all],
+ output: header,
+ command: [find_program('xsltproc'), '--xinclude', '--nonet', '--output', '@OUTPUT@', '@INPUT@'],
+ install: true,
+ install_dir: mm_pkgincludedir,
+)
+
+mm_version_header = configure_file(
+ input: 'ModemManager-version.h.in',
+ output: '@BASENAME@',
+ configuration: version_conf,
+ install: true,
+ install_dir: mm_pkgincludedir,
+)
+
+include_dep = declare_dependency(
+ sources: [mm_names_header, mm_version_header],
+ include_directories: include_inc,
+)
diff --git a/introspection/Makefile.am b/introspection/Makefile.am
index d09c55c6..c7515773 100644
--- a/introspection/Makefile.am
+++ b/introspection/Makefile.am
@@ -2,8 +2,30 @@
SUBDIRS = . tests
# DBus Introspection files
-XMLS = $(wildcard *.xml)
xmldir = $(datadir)/dbus-1/interfaces
-xml_DATA = $(filter-out all.xml wip-*, $(XMLS))
+xml_DATA = \
+ org.freedesktop.ModemManager1.xml \
+ org.freedesktop.ModemManager1.Modem.xml \
+ org.freedesktop.ModemManager1.Modem.Modem3gpp.xml \
+ org.freedesktop.ModemManager1.Modem.ModemCdma.xml \
+ org.freedesktop.ModemManager1.Modem.Simple.xml \
+ org.freedesktop.ModemManager1.Sim.xml \
+ org.freedesktop.ModemManager1.Bearer.xml \
+ org.freedesktop.ModemManager1.Modem.Location.xml \
+ org.freedesktop.ModemManager1.Modem.Messaging.xml \
+ org.freedesktop.ModemManager1.Sms.xml \
+ org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml \
+ org.freedesktop.ModemManager1.Modem.Firmware.xml \
+ org.freedesktop.ModemManager1.Modem.Oma.xml \
+ org.freedesktop.ModemManager1.Modem.Signal.xml \
+ org.freedesktop.ModemManager1.Modem.Time.xml \
+ org.freedesktop.ModemManager1.Modem.Voice.xml \
+ org.freedesktop.ModemManager1.Call.xml \
+ org.freedesktop.ModemManager1.Modem.Sar.xml \
+ org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml \
+ $(NULL)
-EXTRA_DIST = $(XMLS)
+EXTRA_DIST = \
+ $(xml_DATA) \
+ all.xml \
+ $(NULL)
diff --git a/introspection/all.xml b/introspection/all.xml
index 4d277e36..dda64dd5 100644
--- a/introspection/all.xml
+++ b/introspection/all.xml
@@ -6,14 +6,18 @@
<xi:include href="org.freedesktop.ModemManager1.Sim.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Bearer.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Sms.xml"/>
+ <xi:include href="org.freedesktop.ModemManager1.Call.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.xml"/>
+ <xi:include href="org.freedesktop.ModemManager1.Modem.Voice.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.xml"/>
+ <xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.ModemCdma.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Messaging.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Location.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Time.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Firmware.xml"/>
+ <xi:include href="org.freedesktop.ModemManager1.Modem.Sar.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Signal.xml"/>
<xi:include href="org.freedesktop.ModemManager1.Modem.Oma.xml"/>
diff --git a/introspection/meson.build b/introspection/meson.build
new file mode 100644
index 00000000..b1da7ee5
--- /dev/null
+++ b/introspection/meson.build
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+# DBus Introspection files
+mm_ifaces_all = files('all.xml')
+mm_ifaces_test = files('tests/org.freedesktop.ModemManager1.Test.xml')
+
+mm_ifaces = files('org.freedesktop.ModemManager1.xml')
+
+mm_ifaces_bearer = files('org.freedesktop.ModemManager1.Bearer.xml')
+mm_ifaces_call = files('org.freedesktop.ModemManager1.Call.xml')
+
+mm_ifaces_modem = files(
+ 'org.freedesktop.ModemManager1.Modem.Firmware.xml',
+ 'org.freedesktop.ModemManager1.Modem.Location.xml',
+ 'org.freedesktop.ModemManager1.Modem.Messaging.xml',
+ 'org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml',
+ 'org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml',
+ 'org.freedesktop.ModemManager1.Modem.Modem3gpp.xml',
+ 'org.freedesktop.ModemManager1.Modem.ModemCdma.xml',
+ 'org.freedesktop.ModemManager1.Modem.Oma.xml',
+ 'org.freedesktop.ModemManager1.Modem.Sar.xml',
+ 'org.freedesktop.ModemManager1.Modem.Signal.xml',
+ 'org.freedesktop.ModemManager1.Modem.Simple.xml',
+ 'org.freedesktop.ModemManager1.Modem.Time.xml',
+ 'org.freedesktop.ModemManager1.Modem.Voice.xml',
+ 'org.freedesktop.ModemManager1.Modem.xml',
+)
+
+mm_ifaces_sim = files('org.freedesktop.ModemManager1.Sim.xml')
+mm_ifaces_sms = files('org.freedesktop.ModemManager1.Sms.xml',)
+
+install_data(
+ mm_ifaces + mm_ifaces_bearer + mm_ifaces_call + mm_ifaces_modem + mm_ifaces_sim + mm_ifaces_sms,
+ install_dir: dbus_interfaces_dir,
+)
diff --git a/introspection/org.freedesktop.ModemManager1.Bearer.xml b/introspection/org.freedesktop.ModemManager1.Bearer.xml
index e1463a56..b822bf98 100644
--- a/introspection/org.freedesktop.ModemManager1.Bearer.xml
+++ b/introspection/org.freedesktop.ModemManager1.Bearer.xml
@@ -33,6 +33,8 @@
#org.freedesktop.ModemManager1.Bearer:Ip6Config
properties become valid and may contain IP configuration information for
the data interface associated with this bearer.
+
+ Since: 1.0
-->
<method name="Connect" />
@@ -43,6 +45,8 @@
Any ongoing data session will be terminated and IP addresses become
invalid when this method is called.
+
+ Since: 1.0
-->
<method name="Disconnect" />
@@ -82,6 +86,8 @@
<link linkend="MM-BEARER-IP-METHOD-PPP:CAPS">MM_BEARER_IP_METHOD_PPP</link>
method is given, the interface
will be a serial TTY which must then have PPP run over it.
+
+ Since: 1.0
-->
<property name="Interface" type="s" access="read" />
@@ -90,19 +96,47 @@
Indicates whether or not the bearer is connected and thus whether
packet data communication using this bearer is possible.
+
+ Since: 1.0
-->
<property name="Connected" type="b" access="read" />
<!--
+ ConnectionError:
+
+ Provides additional information specifying the reason why the modem is
+ not connected (either due to a failed connection attempt, or due to a
+ a network initiated disconnection).
+
+ The value is composed of two strings: the registered DBus error name,
+ and an optional error message.
+
+ Since: 1.18
+ -->
+ <property name="ConnectionError" type="(ss)" access="read" />
+
+ <!--
Suspended:
In some devices, packet data service will be suspended while the device
is handling other communication, like a voice call. If packet data
service is suspended (but not deactivated) this property will be %TRUE.
+
+ Since: 1.0
-->
<property name="Suspended" type="b" access="read" />
<!--
+ Multiplexed:
+
+ This property will be %TRUE if the bearer is connected through a
+ multiplexed network link.
+
+ Since: 1.18
+ -->
+ <property name="Multiplexed" type="b" access="read" />
+
+ <!--
Ip4Config:
If the bearer was configured for IPv4 addressing, upon activation this
@@ -120,7 +154,7 @@
</variablelist>
If the bearer specifies configuration via PPP or DHCP, only the
- <literal>"method"</literal> item will be present.
+ <literal>"method"</literal> item is guaranteed to be present.
Additional items which are only applicable when using the
<link linkend="MM-BEARER-IP-METHOD-STATIC:CAPS">MM_BEARER_IP_METHOD_STATIC</link>
@@ -166,6 +200,8 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<property name="Ip4Config" type="a{sv}" access="read" />
@@ -234,24 +270,270 @@
<variablelist>
<varlistentry><term><literal>"mtu"</literal></term>
<listitem>
- Maximum transmission unit (MTU), given as an unsigned integer value (signature <literal>"u"</literal>).
+ Maximum transmission unit (MTU), given as an unsigned integer value (signature <literal>"u"</literal>). Since 1.4.
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<property name="Ip6Config" type="a{sv}" access="read" />
<!--
+ Stats:
+
+ If the modem supports it, this property will show statistics associated
+ to the bearer.
+
+ There are two main different statistic types reported: either applicable
+ to the ongoing connection, or otherwise compiled for all connections
+ that have been done on this bearer object.
+
+ When the connection is disconnected automatically or explicitly by the
+ user, the values applicable to the ongoing connection will show the last
+ values cached.
+
+ The following items may appear in the list of statistics:
+ <variablelist>
+ <varlistentry><term><literal>"rx-bytes"</literal></term>
+ <listitem>
+ Number of bytes received without error in the ongoing connection,
+ given as an unsigned 64-bit integer value (signature
+ <literal>"t"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"tx-bytes"</literal></term>
+ <listitem>
+ Number of bytes transmitted without error in the ongoing
+ connection, given as an unsigned 64-bit integer value (signature
+ <literal>"t"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"start-date"</literal></term>
+ <listitem>
+ Timestamp indicating when the ongoing connection started, given
+ as an unsigned 64-bit integer value representing seconds
+ since the epoch (signature <literal>"t"</literal>).
+ Since 1.20.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"duration"</literal></term>
+ <listitem>
+ Duration of the ongoing connection, in seconds, given as an
+ unsigned integer value (signature <literal>"u"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"attempts"</literal></term>
+ <listitem>
+ Total number of connection attempts done with this bearer, given
+ as an unsigned integer value (signature <literal>"u"</literal>).
+ Since 1.14.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"failed-attempts"</literal></term>
+ <listitem>
+ Number of failed connection attempts done with this bearer,
+ given as an unsigned integer value (signature
+ <literal>"u"</literal>). Since 1.14.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"total-rx-bytes"</literal></term>
+ <listitem>
+ Total number of bytes received without error in all the successful
+ connection establishments, given as an unsigned 64-bit integer
+ value (signature <literal>"t"</literal>). Since 1.14.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"total-tx-bytes"</literal></term>
+ <listitem>
+ Total number of bytes transmitted without error in all the
+ successful connection establishments, given as an unsigned 64-bit
+ integer value (signature <literal>"t"</literal>). Since 1.14.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"total-duration"</literal></term>
+ <listitem>
+ Total duration of all the successful connection establishments, in
+ seconds, given as an unsigned integer value (signature
+ <literal>"u"</literal>). Since 1.14.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"uplink-speed"</literal></term>
+ <listitem>
+ Uplink bit rate negotiated with network, in bits per second, given
+ as an unsigned 64-bit integer value (signature <literal>"t"</literal>).
+ Since 1.20.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"downlink-speed"</literal></term>
+ <listitem>
+ Downlink bit rate negotiated with network, in bits per second, given
+ as an unsigned 64-bit integer value (signature <literal>"t"</literal>).
+ Since 1.20.
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.6
+ -->
+ <property name="Stats" type="a{sv}" access="read" />
+
+ <!--
IpTimeout:
Maximum time to wait for a successful IP establishment, when PPP is used.
+
+ Since: 1.0
-->
<property name="IpTimeout" type="u" access="read" />
<!--
+ BearerType:
+
+ A <link linkend="MMBearerType">MMBearerType</link>
+
+ Since: 1.10
+ -->
+ <property name="BearerType" type="u" access="read" />
+
+ <!--
+ ProfileId:
+
+ The profile ID this bearer object is associated with, only applicable if
+ the modem supports profile management operations, and if the bearer is
+ connected.
+
+ If the bearer is disconnected, or if profile management operations are
+ not supported, -1 will be reported.
+
+ Since: 1.18
+ -->
+ <property name="ProfileId" type="i" access="read" />
+
+ <!--
Properties:
- List of properties used when creating the bearer.
+ List of settings used to create the bearer.
+
+ Bearers may be implicitly created (e.g. the default initial EPS bearer
+ created during the network registration process in 4G and 5G networks)
+ or explicitly created by the user (e.g. via the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem.CreateBearer">CreateBearer()</link>
+ or <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Simple.Connect">Connect()</link>
+ calls).
+
+ The following settings apply to 3GPP (GSM/UMTS/LTE/5GNR) devices:
+
+ <variablelist>
+ <varlistentry><term><literal>"apn"</literal></term>
+ <listitem><para>
+ The Access Point Name to use in the connection, given as a string
+ value (signature <literal>"s"</literal>). For 5G NGC, this field
+ contains the Data Network Name (DNN).
+ </para></listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"ip-type"</literal></term>
+ <listitem>
+ The IP addressing type to use, given as a
+ <link linkend="MMBearerIpFamily">MMBearerIpFamily</link>
+ value (signature <literal>"u"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"apn-type"</literal></term>
+ <listitem>
+ The purposes of the specified APN, given as a
+ <link linkend="MMBearerApnType">MMBearerApnType</link>
+ value (signature <literal>"u"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"allowed-auth"</literal></term>
+ <listitem>
+ The authentication method to use, given as a
+ <link linkend="MMBearerAllowedAuth">MMBearerAllowedAuth</link>
+ value (signature <literal>"u"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"user"</literal></term>
+ <listitem>
+ The user name (if any) required by the network, given as a string
+ value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"password"</literal></term>
+ <listitem>
+ The password (if any) required by the network, given as a string
+ value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"profile-id"</literal></term>
+ <listitem>
+ The ID of the 3GPP profile to connect to (signature
+ <literal>"i"</literal>), as given in the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Modem3gpp-ProfileManager.List">profile list</link>.
+ In this case, if additional profile settings are given in the properties
+ and they already exist in the profile (e.g. <literal>"apn"</literal>),
+ the new settings will be explicitly ignored; the settings stored in the
+ profile itself always take preference. The value -1 is used to
+ indicate an invalid or uninitialized profile id. Since 1.18.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"profile-name"</literal></term>
+ <listitem><para>
+ The name of the profile, given as a string
+ value (signature <literal>"s"</literal>).
+ This value has no effect on the connection, but can be used by the host
+ to identify the profiles. This setting only applies on profile management
+ operations, it should not be used as part of the settings of an explicit
+ connection attempt. Since 1.20.
+ </para></listitem>
+ </varlistentry>
+ </variablelist>
+
+ The following settings apply to 3GPP2 (CDMA/EVDO) devices:
+
+ <variablelist>
+ <varlistentry><term><literal>"rm-protocol"</literal></term>
+ <listitem>
+ The protocol of the Rm interface, given as a
+ <link linkend="MMModemCdmaRmProtocol">MMModemCdmaRmProtocol</link>
+ value (signature <literal>"u"</literal>).
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ The following settings apply to all devices types:
+
+ <variablelist>
+ <varlistentry><term><literal>"allow-roaming"</literal></term>
+ <listitem>
+ Specifies whether the connections are allowed even when the device
+ is registered in a roaming network, given as a boolean value (signature
+ <literal>"b"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"multiplex"</literal></term>
+ <listitem>
+ The multiplex support requested by the user, given as a
+ <link linkend="MMBearerMultiplexSupport">MMBearerMultiplexSupport</link>
+ value (signature <literal>"u"</literal>). Since 1.18.
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ The following settings are no longer supported, but they are kept on the
+ interface for compatibility purposes:
+
+ <variablelist>
+ <varlistentry><term><literal>"number"</literal></term>
+ <listitem>
+ Number to dial for the data connection, given as a string value
+ (signature <literal>"s"</literal>).
+ Deprecated since version 1.10.0.
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.0
-->
<property name="Properties" type="a{sv}" access="read" />
diff --git a/introspection/org.freedesktop.ModemManager1.Call.xml b/introspection/org.freedesktop.ModemManager1.Call.xml
new file mode 100644
index 00000000..3aa6a643
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager1.Call.xml
@@ -0,0 +1,236 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ ModemManager 1.0 Interface Specification
+
+ Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ Copyright (C) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ Copyright (C) 2019 Purism SPC
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <!--
+ org.freedesktop.ModemManager1.Call:
+ @short_description: The ModemManager Call interface.
+
+ The Call interface Defines operations and properties of a single Call.
+ -->
+ <interface name="org.freedesktop.ModemManager1.Call">
+
+ <!--
+ Start:
+
+ If the outgoing call has not yet been started, start it.
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-UNKNOWN:CAPS"><constant>MM_CALL_STATE_UNKNOWN</constant></link> and direction is
+ <link linkend="MM-CALL-DIRECTION-OUTGOING:CAPS"><constant>MM_CALL_DIRECTION_OUTGOING</constant></link>.
+
+ Since: 1.6
+ -->
+ <method name="Start" />
+
+ <!--
+ Accept:
+
+ Accept incoming call (answer).
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-RINGING-IN:CAPS"><constant>MM_CALL_STATE_RINGING_IN</constant></link> and direction is
+ <link linkend="MM-CALL-DIRECTION-INCOMING:CAPS"><constant>MM_CALL_DIRECTION_INCOMING</constant></link>.
+
+ Since: 1.6
+ -->
+ <method name="Accept" />
+
+ <!--
+ Deflect:
+ @number: new number where the call will be deflected.
+
+ Deflect an incoming or waiting call to a new number. This call will be
+ considered terminated once the deflection is performed.
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-RINGING-IN:CAPS"><constant>MM_CALL_STATE_RINGING_IN</constant></link> or
+ <link linkend="MM-CALL-STATE-WAITING:CAPS"><constant>MM_CALL_STATE_WAITING</constant></link> and direction is
+ <link linkend="MM-CALL-DIRECTION-INCOMING:CAPS"><constant>MM_CALL_DIRECTION_INCOMING</constant></link>.
+
+ Since: 1.12
+ -->
+ <method name="Deflect">
+ <arg name="number" type="s" />
+ </method>
+
+ <!--
+ JoinMultiparty:
+
+ Join the currently held call into a single multiparty call with another
+ already active call.
+
+ The calls will be flagged with the
+ '<link linkend="gdbus-property-org-freedesktop-ModemManager1-Call.Multiparty">Multiparty</link>'
+ property while they are part of the multiparty call.
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-HELD:CAPS"><constant>MM_CALL_STATE_HELD</constant></link>.
+
+ Since: 1.12
+ -->
+ <method name="JoinMultiparty" />
+
+ <!--
+ LeaveMultiparty:
+
+ If this call is part of an ongoing multiparty call, detach it from the multiparty call,
+ put the multiparty call on hold, and activate this one alone. This operation makes this
+ call private again between both ends of the call.
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-ACTIVE:CAPS"><constant>MM_CALL_STATE_ACTIVE</constant></link> or
+ <link linkend="MM-CALL-STATE-HELD:CAPS"><constant>MM_CALL_STATE_HELD</constant></link> and
+ the call is a multiparty call.
+
+ Since: 1.12
+ -->
+ <method name="LeaveMultiparty"/>
+
+ <!--
+ Hangup:
+
+ Hangup the active call.
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-UNKNOWN:CAPS"><constant>MM_CALL_STATE_UNKNOWN</constant></link>.
+
+ Since: 1.6
+ -->
+ <method name="Hangup"/>
+
+ <!--
+ SendDtmf:
+ @dtmf: DTMF tone identifier [0-9A-D*#].
+
+ Send a DTMF tone (Dual Tone Multi-Frequency) (only on supported modem).
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-ACTIVE:CAPS"><constant>MM_CALL_STATE_ACTIVE</constant></link>.
+
+ Since: 1.6
+ -->
+ <method name="SendDtmf">
+ <arg name="dtmf" type="s" direction="in"/>
+ </method>
+
+ <!--
+ DtmfReceived:
+ @dtmf: DTMF tone identifier [0-9A-D*#].
+
+ Emitted when a DTMF tone is received (only on supported modem)
+
+ Since: 1.6
+ -->
+ <signal name="DtmfReceived">
+ <arg name="dtmf" type="s" />
+ </signal>
+
+ <!--
+ StateChanged:
+ @old: Old state MMCallState
+ @new: New state MMCallState
+ @reason: A <link linkend="MMCallStateReason">MMCallStateReason</link> value, specifying the reason for this state change.
+
+ Emitted when call changes state
+
+ Since: 1.6
+ -->
+ <signal name="StateChanged">
+ <arg name="old" type="i" />
+ <arg name="new" type="i" />
+ <arg name="reason" type="u" />
+ </signal>
+
+ <!--
+ State:
+
+ A <link linkend="MMCallState">MMCallState</link> value,
+ describing the state of the call.
+
+ Since: 1.6
+ -->
+ <property name="State" type="i" access="read" />
+
+ <!--
+ StateReason:
+
+ A <link linkend="MMCallStateReason">MMCallStateReason</link> value, describing why the state is changed.
+
+ Since: 1.6
+ -->
+ <property name="StateReason" type="i" access="read" />
+
+ <!--
+ Direction:
+
+ A <link linkend="MMCallDirection">MMCallDirection</link> value,
+ describing the direction of the call.
+
+ Since: 1.6
+ -->
+ <property name="Direction" type="i" access="read" />
+
+ <!--
+ Number:
+
+ The remote phone number.
+
+ Since: 1.6
+ -->
+ <property name="Number" type="s" access="read" />
+
+ <!--
+ Multiparty:
+
+ Whether the call is currently part of a multiparty conference call.
+
+ Since: 1.12
+ -->
+ <property name="Multiparty" type="b" access="read" />
+
+ <!--
+ AudioPort:
+
+ If call audio is routed via the host, the name of the kernel device that
+ provides the audio. For example, with certain Huawei USB modems, this
+ property might be "ttyUSB2" indicating audio is available via ttyUSB2 in
+ the format described by the AudioFormat property.
+
+ Since: 1.10
+ -->
+ <property name="AudioPort" type="s" access="read" />
+
+ <!--
+ AudioFormat:
+
+ If call audio is routed via the host, a description of the audio format
+ supported by the audio port.
+
+ This property may include the following items:
+ <variablelist>
+ <varlistentry><term><literal>"encoding"</literal></term>
+ <listitem>
+ The audio encoding format. For example, "pcm" for PCM audio.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"resolution"</literal></term>
+ <listitem>
+ The sampling precision and its encoding format. For example,
+ "s16le" for signed 16-bit little-endian samples.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"rate"</literal></term>
+ <listitem>
+ The sampling rate as an unsigned integer. For example, 8000 for
+ 8000hz.
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.10
+ -->
+ <property name="AudioFormat" type="a{sv}" access="read" />
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml b/introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml
index 6030a378..d8464bf5 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml
@@ -6,6 +6,7 @@
Copyright (C) 2011-2013 Red Hat, Inc.
Copyright (C) 2011-2013 Google, Inc.
Copyright (C) 2011-2013 Lanedo GmbH
+ Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
-->
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
@@ -14,67 +15,18 @@
org.freedesktop.ModemManager1.Modem.Firmware:
@short_description: The ModemManager Firmware interface.
- This interface allows clients to select or install firmware images on
- modems.
-
- Firmware slots and firmware images are identified by arbitrary opaque
- strings.
-
- Firmware images are represented as dictionaries of properties.
- Certain properties are pre-defined, and some are required:
-
- <variablelist>
- <varlistentry><term><literal>"image-type"</literal></term>
- <listitem>
- (Required) Type of the firmware image, given as a
- <link linkend="MMFirmwareImageType">MMFirmwareImageType</link> value
- (signature <literal>"u"</literal>). Firmware images of type
- <link linkend="MM-FIRMWARE-IMAGE-TYPE-GENERIC:CAPS">MM_FIRMWARE_IMAGE_TYPE_GENERIC</link>
- will only expose only the mandatory properties.
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"unique-id"</literal></term>
- <listitem>
- (Required) A user-readable unique ID for the firmware image, given as a
- string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"gobi-pri-version"</literal></term>
- <listitem>
- (Optional) The version of the PRI firmware image, in images of type
- <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"gobi-pri-info"</literal></term>
- <listitem>
- (Optional) Additional information of the PRI image, in images of type
- <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"gobi-boot-version"</literal></term>
- <listitem>
- (Optional) The boot version of the PRI firmware image, in images of type
- <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"gobi-pri-unique-id"</literal></term>
- <listitem>
- (Optional) The unique ID of the PRI firmware image, in images of type
- <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"gobi-modem-unique-id"</literal></term>
- <listitem>
- (Optional) The unique ID of the Modem firmware image, in images of type
- <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- </variablelist>
+ This interface provides access to perform different firmware-related operations
+ in the modem, including listing the available firmware images in the module and
+ selecting which of them to use.
+
+ This interface does not provide direct access to perform firmware updates in
+ the device. Instead, it exposes information about the expected firmware update
+ method as well as method-specific details required for the upgrade to happen.
+ The actual firmware upgrade may be performed via the Linux Vendor Firmware Service
+ and the <ulink url="https://fwupd.org">fwupd</ulink> daemon.
+
+ This interface will always be available as long a the modem is considered
+ valid.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Firmware">
@@ -85,13 +37,69 @@
List installed firmware images.
- Depending on the type of modem, installed images may be stored on the
- host or the modem.
-
- Installed images can be selected non-destructively.
+ Firmware slots and firmware images are identified by arbitrary opaque
+ strings.
+
+ Firmware images are represented as dictionaries of properties.
+ Certain properties are pre-defined, and some are required:
+
+ <variablelist>
+ <varlistentry><term><literal>"image-type"</literal></term>
+ <listitem>
+ (Required) Type of the firmware image, given as a
+ <link linkend="MMFirmwareImageType">MMFirmwareImageType</link> value
+ (signature <literal>"u"</literal>). Firmware images of type
+ <link linkend="MM-FIRMWARE-IMAGE-TYPE-GENERIC:CAPS">MM_FIRMWARE_IMAGE_TYPE_GENERIC</link>
+ will only expose only the mandatory properties.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"unique-id"</literal></term>
+ <listitem>
+ (Required) A user-readable unique ID for the firmware image, given as a
+ string value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"gobi-pri-version"</literal></term>
+ <listitem>
+ (Optional) The version of the PRI firmware image, in images of type
+ <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
+ given as a string value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"gobi-pri-info"</literal></term>
+ <listitem>
+ (Optional) Additional information of the PRI image, in images of type
+ <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
+ given as a string value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"gobi-boot-version"</literal></term>
+ <listitem>
+ (Optional) The boot version of the PRI firmware image, in images of type
+ <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
+ given as a string value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"gobi-pri-unique-id"</literal></term>
+ <listitem>
+ (Optional) The unique ID of the PRI firmware image, in images of type
+ <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
+ given as a string value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"gobi-modem-unique-id"</literal></term>
+ <listitem>
+ (Optional) The unique ID of the Modem firmware image, in images of type
+ <link linkend="MM-FIRMWARE-IMAGE-TYPE-GOBI:CAPS">MM_FIRMWARE_IMAGE_TYPE_GOBI</link>,
+ given as a string value (signature <literal>"s"</literal>).
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.0
-->
<method name="List">
- <arg name="selected" type="s" direction="out" />
+ <arg name="selected" type="s" direction="out" />
<arg name="installed" type="aa{sv}" direction="out" />
</method>
@@ -106,30 +114,77 @@
returned by
<link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Firmware.List">List()</link>,
or if the image could not be selected for some reason.
+
+ Installed images can be selected non-destructively.
+
+ Since: 1.0
-->
<method name="Select">
<arg name="uniqueid" type="s" direction="in" />
</method>
<!--
- Install:
- @image: The identifier of the firmware image to install.
- @slot: The identifier of the slot into which the firmware should be installed.
-
- Install an available firmware image into a slot.
-
- It does not guarantee that the image will be installed into the
- specified slot, but does guarantee that, if the slot is empty, no
- image will be overwritten, and if the slot is not empty, no image
- other than the one in that slot will be overwritten.
-
- The method will fail if either of the identifiers is invalid, or if the
- image could not be installed into the slot for some reason.
-
- <method name="Install">
- <arg name="image" type="s" direction="in" />
- <arg name="slot" type="s" direction="in" />
- </method-->
+ UpdateSettings:
+
+ Detailed settings that provide information about how the module should be
+ updated.
+
+ The settings are given as a bitmask of <link linkend="MMModemFirmwareUpdateMethod">MMModemFirmwareUpdateMethod</link>
+ values specifying the type of firmware update procedures expected followed by a
+ dictionary that includes other parameters applicable to the specific methods reported.
+
+ <variablelist>
+ <varlistentry>
+ <listitem>
+ <para>
+ The following settings are mandatory as long as the reported update method is not
+ <link linkend="MM-MODEM-FIRMWARE-UPDATE-METHOD-NONE:CAPS">MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE</link>.
+ </para>
+ <variablelist>
+ <varlistentry><term><literal>"device-ids"</literal></term>
+ <listitem>
+ (Required) This property exposes the list of device IDs associated to a given
+ device, from most specific to least specific. (signature <literal>'as'</literal>).
+ E.g. a list containing: <literal>"USB\VID_413C&amp;PID_81D7&amp;REV_0001"</literal>,
+ <literal>"USB\VID_413C&amp;PID_81D7"</literal> and <literal>"USB\VID_413C"</literal>.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"version"</literal></term>
+ <listitem>
+ (Required) This property exposes the current firmware version string of the module.
+ If the module uses separate version numbers for firmware version and carrier configuration,
+ this version string will be a combination of both, and so it may be different to the
+ version string showed in the #org.freedesktop.ModemManager1.Modem:Revision property.
+ (signature <literal>'s'</literal>)
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ <variablelist>
+ <varlistentry><term><link linkend="MM-MODEM-FIRMWARE-UPDATE-METHOD-FASTBOOT:CAPS">MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT</link></term>
+ <listitem>
+ <para>
+ Devices supporting the fastboot firmware update method require exposing the
+ following additional settings:
+ </para>
+ <variablelist>
+ <varlistentry><term><literal>"fastboot-at"</literal></term>
+ <listitem>
+ (Required) This property exposes the AT command that should be sent to the
+ module to trigger a reset into fastboot mode (signature <literal>'s'</literal>)
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.10
+ -->
+ <property name="UpdateSettings" type="(ua{sv})" access="read" />
</interface>
</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Location.xml b/introspection/org.freedesktop.ModemManager1.Modem.Location.xml
index 93cf6406..6cb67330 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Location.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Location.xml
@@ -19,6 +19,11 @@
client applications. Not all devices can provide this information, or even
if they do, they may not be able to provide it while a data session is
active.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used (including GNSS module management).
-->
<interface name="org.freedesktop.ModemManager1.Modem.Location">
@@ -28,26 +33,43 @@
@signal_location: Flag to control whether the device emits signals with the new location information. This argument is ignored when disabling location information gathering.
Configure the location sources to use when gathering location
- information. Also enable or disable location information gathering.
+ information. Adding new location sources may require to enable them
+ in the device (e.g. the GNSS engine will need to be started explicitly
+ if a GPS source is requested by the user). In the same way, removing
+ location sources may require to disable them in the device (e.g. when
+ no GPS sources are requested by the user, the GNSS engine will need
+ to be stopped explicitly).
+
This method may require the client to authenticate itself.
- When signals are emitted, any client application (including malicious
- ones!) can listen for location updates unless D-Bus permissions restrict
- these signals from certain users. If further security is desired, the
+ When location signaling is enabled by the user, any client application (including
+ malicious ones!) would be able to use the #org.freedesktop.ModemManager1.Modem.Location:Location
+ property to receive location updates. If further security is desired, the
@signal_location argument can be set to %FALSE to disable location
updates via D-Bus signals and require applications to call
- authenticated APIs (like
- <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Location.GetLocation">GetLocation()</link>
- ) to get
- location information.
+ authenticated APIs (like <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Location.GetLocation">GetLocation()</link>)
+ to get the location information.
+
+ By default location signaling is disabled, and therefore the
+ #org.freedesktop.ModemManager1.Modem.Location:Location property will not
+ be usable until explicitly enabled by the user.
The optional
- <link linkend="MM-MODEM-LOCATION-SOURCE-AGPS:CAPS">MM_MODEM_LOCATION_SOURCE_AGPS</link>
- allows to request A-GPS operation, and it must be given along with either
+ <link linkend="MM-MODEM-LOCATION-SOURCE-AGPS-MSA:CAPS">MM_MODEM_LOCATION_SOURCE_AGPS_MSA</link>
+ and
+ <link linkend="MM-MODEM-LOCATION-SOURCE-AGPS-MSB:CAPS">MM_MODEM_LOCATION_SOURCE_AGPS_MSB</link>
+ allow to request MSA/MSB A-GPS operation, and they must be given along with either
<link linkend="MM-MODEM-LOCATION-SOURCE-GPS-RAW:CAPS">MM_MODEM_LOCATION_SOURCE_GPS_RAW</link>
or
<link linkend="MM-MODEM-LOCATION-SOURCE-GPS-NMEA:CAPS">MM_MODEM_LOCATION_SOURCE_GPS_NMEA</link>.
+ Both
+ <link linkend="MM-MODEM-LOCATION-SOURCE-AGPS-MSA:CAPS">MM_MODEM_LOCATION_SOURCE_AGPS_MSA</link>
+ and
+ <link linkend="MM-MODEM-LOCATION-SOURCE-AGPS-MSB:CAPS">MM_MODEM_LOCATION_SOURCE_AGPS_MSB</link>
+ cannot be given at the same time, and if none given, standalone GPS is assumed.
+
+ Since: 1.0
-->
<method name="Setup">
<arg name="sources" type="u" direction="in" />
@@ -64,6 +86,8 @@
for more information on the dictionary returned at @location.
This method may require the client to authenticate itself.
+
+ Since: 1.0
-->
<method name="GetLocation">
<arg name="Location" type="a{uv}" direction="out" />
@@ -71,28 +95,86 @@
<!--
SetSuplServer:
- @supl: SUPL server configuration, given either as IP:PORT or with a full URL.
+ @supl: SUPL server configuration, given either as IP:PORT or as FQDN:PORT.
Configure the SUPL server for A-GPS.
+
+ Since: 1.6
-->
<method name="SetSuplServer">
<arg name="supl" type="s" direction="in" />
</method>
<!--
+ InjectAssistanceData:
+ @data: assistance data to be injected to the GNSS module.
+
+ Inject assistance data to the GNSS module, which will allow it to have a more
+ accurate positioning information.
+
+ The data files should be downloaded using external means from the URLs specified in
+ the <link linkend="AssistanceDataServers">AssistanceDataServers</link> property. The
+ user does not need to specify the assistance data type being given.
+
+ There is no maximum @data size limit specified, default DBus system bus limits apply.
+
+ This method may be used when the device does not have a mobile network connection by
+ itself, and therefore it cannot use any A-GPS server to improve the accuracy of the
+ position. In this case, the user can instead download the assistance data files using
+ a WiFi or LAN network, and inject them to the GNSS engine manually.
+
+ Since: 1.10
+ -->
+ <method name="InjectAssistanceData">
+ <arg name="data" type="ay" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true"/>
+ </arg>
+ </method>
+
+ <!--
+ SetGpsRefreshRate:
+ @rate: Rate, in seconds.
+
+ Set the refresh rate of the GPS information in the API. If not explicitly
+ set, a default of 30s will be used.
+
+ The refresh rate can be set to 0 to disable it, so that every update reported by
+ the modem is published in the interface.
+
+ Since: 1.6
+ -->
+ <method name="SetGpsRefreshRate">
+ <arg name="rate" type="u" direction="in" />
+ </method>
+
+ <!--
Capabilities:
Bitmask of <link linkend="MMModemLocationSource">MMModemLocationSource</link>
values, specifying the supported location sources.
+
+ Since: 1.0
-->
<property name="Capabilities" type="u" access="read" />
<!--
+ SupportedAssistanceData:
+
+ Bitmask of <link linkend="MMModemLocationAssistanceDataType">MMModemLocationAssistanceDataType</link>
+ values, specifying the supported types of assistance data.
+
+ Since: 1.10
+ -->
+ <property name="SupportedAssistanceData" type="u" access="read" />
+
+ <!--
Enabled:
Bitmask specifying which of the supported
<link linkend="MMModemLocationSource">MMModemLocationSource</link>
location sources is currently enabled in the device.
+
+ Since: 1.0
-->
<property name="Enabled" type="u" access="read" />
@@ -105,6 +187,8 @@
See the
<link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Location.Setup">Setup()</link>
method for more information.
+
+ Since: 1.0
-->
<property name="SignalsLocation" type="b" access="read" />
@@ -115,9 +199,16 @@
gathering is enabled. If the modem supports multiple location types it
may return more than one here.
- Note that if the device was told not to emit updated location
- information when location information gathering was initially enabled,
- this property may not return any location information for security reasons.
+ For security reasons, the location information updates via this
+ property are disabled by default. Users can use this property to monitor
+ location updates only if the location signals are enabled with
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Location.Setup">Setup()</link>,
+ but considering that enabling the location signals would allow all users
+ to receive property updates as well, not just the process that enabled them.
+ For a finer grained access control, the user can use the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Location.GetLocation">GetLocation()</link>
+ method instead, which may require the client to authenticate itself on every
+ call.
This dictionary is composed of a
<link linkend="MMModemLocationSource">MMModemLocationSource</link>
@@ -129,7 +220,7 @@
<listitem>
<para>
Devices supporting this
- capability return a string in the format <literal>"MCC,MNC,LAC,CI"</literal> (without the
+ capability return a string in the format <literal>"MCC,MNC,LAC,CI,TAC"</literal> (without the
quotes of course) where the following applies:
</para>
<variablelist>
@@ -149,10 +240,10 @@
</varlistentry>
<varlistentry><term><literal>LAC</literal></term>
<listitem>
- This is the two-byte Location Area Code of the base station with
- which the mobile is registered, in upper-case hexadecimal format
- without leading zeros, as specified in 3GPP TS 27.007 section
- 10.1.19. e.g. <literal>"84CD"</literal>.
+ This is the two-byte Location Area Code of the GSM/UMTS base
+ station with which the mobile is registered, in upper-case
+ hexadecimal format without leading zeros, as specified in
+ 3GPP TS 27.007. E.g. <literal>"84CD"</literal>.
</listitem>
</varlistentry>
<varlistentry><term><literal>CI</literal></term>
@@ -163,6 +254,14 @@
e.g. <literal>"2BAF"</literal> or <literal>"D30156"</literal>.
</listitem>
</varlistentry>
+ <varlistentry><term><literal>TAC</literal></term>
+ <listitem>
+ This is the two- or three-byte Tracking Area Code of the LTE/5GNR
+ base station with which the mobile is registered, in upper-case
+ hexadecimal format without leading zeros, as specified in
+ 3GPP TS 27.007. E.g. <literal>"6FFE"</literal>. Since 1.10.
+ </listitem>
+ </varlistentry>
</variablelist>
<para>
The entire string may only be composed of the ASCII digits <literal>[0-9]</literal>,
@@ -280,15 +379,38 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<property name="Location" type="a{uv}" access="read" />
<!--
SuplServer:
- SUPL server configuration for A-GPS, given either as IP:PORT or with a full URL.
+ SUPL server configuration for A-GPS, given either as IP:PORT or FQDN:PORT.
+
+ Since: 1.6
-->
<property name="SuplServer" type="s" access="read" />
+ <!--
+ AssistanceDataServers:
+
+ URLs from where the user can download assistance data files to inject with
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Location.InjectAssistanceData">InjectAssistanceData()</link>.
+
+ Since: 1.10
+ -->
+ <property name="AssistanceDataServers" type="as" access="read" />
+
+ <!--
+ GpsRefreshRate:
+
+ Rate of refresh of the GPS information in the interface.
+
+ Since: 1.6
+ -->
+ <property name="GpsRefreshRate" type="u" access="read" />
+
</interface>
</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml b/introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml
index 514a745f..9117eb14 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml
@@ -17,6 +17,11 @@
The Messaging interface handles sending SMS messages and notification of new
incoming messages.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used (including listing stored messages).
-->
<interface name="org.freedesktop.ModemManager1.Modem.Messaging">
@@ -30,6 +35,8 @@
retrieved either by listening for the
#org.freedesktop.ModemManager1.Modem.Messaging::Added signal,
or by querying the specific SMS object of interest.
+
+ Since: 1.0
-->
<method name="List">
<arg name="result" type="ao" direction="out" />
@@ -40,6 +47,8 @@
@path: The object path of the SMS to delete.
Delete an SMS message.
+
+ Since: 1.0
-->
<method name="Delete">
<arg name="path" type="o" direction="in" />
@@ -60,6 +69,8 @@
If the SMSC is not specified and one is required, the default SMSC is
used.
+
+ Since: 1.0
-->
<method name="Create">
<arg name="properties" type="a{sv}" direction="in" />
@@ -79,6 +90,8 @@
Check the
'<link linkend="gdbus-property-org-freedesktop-ModemManager1-Sms.State">State</link>'
property to determine if the message is complete.
+
+ Since: 1.0
-->
<signal name="Added">
<arg name="path" type="o" />
@@ -90,6 +103,8 @@
@path: Object path of the now deleted SMS.
Emitted when a message has been deleted.
+
+ Since: 1.0
-->
<signal name="Deleted">
<arg name="path" type="o" />
@@ -99,6 +114,8 @@
Messages:
The list of SMS object paths.
+
+ Since: 1.2
-->
<property name="Messages" type="ao" access="read" />
@@ -108,6 +125,8 @@
A list of <link linkend="MMSmsStorage">MMSmsStorage</link> values,
specifying the storages supported by this modem for storing and
receiving SMS.
+
+ Since: 1.0
-->
<property name="SupportedStorages" type="au" access="read" />
@@ -116,6 +135,8 @@
A <link linkend="MMSmsStorage">MMSmsStorage</link> value,
specifying the storage to be used when receiving or storing SMS.
+
+ Since: 1.0
-->
<property name="DefaultStorage" type="u" access="read" />
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml
new file mode 100644
index 00000000..f3086757
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ ModemManager 1.0 Interface Specification
+
+ Copyright (C) 2021 Google Inc.
+ Copyright (C) 2021 Aleksander Morgado
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <!--
+ org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager:
+ @short_description: The ModemManager 3GPP profile management interface.
+
+ This interface provides access to actions with connection profiles.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
+
+ The user of the interface can optionally choose to use the new profile
+ management methods to manage the connection setup, e.g by using the new
+ <literal>"profile-id"</literal> setting in either the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem.CreateBearer">CreateBearer</link>
+ or the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Simple.Connect">Connect</link>
+ methods. If that's the case, it is suggested that the legacy approach of
+ not using the profiles is completely avoided. If both approaches are
+ used at the same time, it may happen that a connection attempt not using
+ the <literal>"profile-id"</literal> implicitly updates a given profile
+ (without emitting
+ <link linkend="gdbus-signal-org-freedesktop-ModemManager1-Modem-Modem3gpp-ProfileManager.Updated">Updated</link>),
+ as the amount of profiles implemented in modems may be fixed.
+ -->
+ <interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager">
+
+ <!--
+ List:
+ @profiles: An array of dictionaries containing the properties of the provisioned profiles.
+
+ Lists the available profiles or contexts provisioned on the modem.
+
+ Profiles are represented as dictionaries of properties, and any of the
+ 3GPP-specific properties defined in the
+ <link linkend="gdbus-property-org-freedesktop-ModemManager1-Bearer.Properties">bearer properties</link>
+ are allowed.
+
+ Depending on the implementation, the settings applicable to the initial
+ EPS bearer given in
+ <link linkend="gdbus-property-org-freedesktop-ModemManager1-Modem-Modem3gpp.InitialEpsBearerSettings">bearer properties</link>
+ may also be reported as an item in the returned list, identified by the
+ %MM_BEARER_APN_TYPE_INITIAL <literal>"apn-type"</literal> flag.
+
+ Since: 1.18
+ -->
+ <method name="List">
+ <arg name="profiles" type="aa{sv}" direction="out" />
+ </method>
+
+ <!--
+ Set:
+ @requested_properties: the requested profile properties.
+ @stored_properties: the stored profile properties.
+
+ Creates or updates a connection profile on this modem. If
+ <literal>"profile-id"</literal> is not given, a new profile will be
+ created; otherwise, the profile with the given ID will be updated.
+
+ Profiles are represented as dictionaries of properties, and any of the
+ 3GPP-specific properties defined in the
+ <link linkend="gdbus-property-org-freedesktop-ModemManager1-Bearer.Properties">bearer properties</link>
+ are allowed. The real list of supported properties really depends on the
+ underlying protocol and implementation, though; e.g. in AT-based modems
+ setting <literal>"apn-type"</literal> won't be supported, and instead the
+ user should give that setting explicitly when creating the bearer object.
+
+ The operation may fail if it is attempting to update an existing
+ profile for which connected bearer objects already exist. In this case,
+ the user should make sure these bearer objects are already disconnected
+ before attempting to change the profile settings.
+
+ The operation may also fail if it is attempting to update the profile
+ associated to the settings of the initial EPS bearer, identified by the
+ %MM_BEARER_APN_TYPE_INITIAL <literal>"apn-type"</literal> flag. In this
+ case, <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Modem3gpp.SetInitialEpsBearerSettings">SetInitialEpsBearerSettings()</link>
+ should be used instead.
+
+ The output @stored_properties will contain the settings that were
+ successfully stored, including the new <literal>"profile-id"</literal>
+ if the operation was creating a new profile.
+
+ Since: 1.18
+ -->
+ <method name="Set">
+ <arg name="requested_properties" type="a{sv}" direction="in" />
+ <arg name="stored_properties" type="a{sv}" direction="out" />
+ </method>
+
+ <!--
+ Delete:
+ @properties: the profile properties.
+
+ Deletes the profile with the <literal>"profile-id"</literal> given in @properties.
+
+ If additional settings are given in @properties they are ignored. This
+ allows the user to easily request the deletion of a profile that has been
+ provided in the List() operation.
+
+ This method may just clear the existing profiles (i.e. reseting all the
+ properties to defaults) instead of fully removing them if the profiles
+ cannot be fully removed. In this case, the method will succeed, but the
+ size of the list of profiles will not change.
+
+ This method will fail if <literal>"profile-id"</literal> is not given.
+
+ The operation may fail if it is attempting to delete a profile
+ for which connected bearer objects already exist. In this case,
+ the user should make sure these bearer objects are already disconnected
+ before attempting to delete the profile.
+
+ The operation may also fail if it is attempting to delete the profile
+ associated to the settings of the initial EPS bearer, identified by the
+ %MM_BEARER_APN_TYPE_INITIAL <literal>"apn-type"</literal> flag. In this
+ case, <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Modem3gpp.SetInitialEpsBearerSettings">SetInitialEpsBearerSettings()</link>
+ may be used instead to clear these settings.
+
+ Since: 1.18
+ -->
+ <method name="Delete">
+ <arg name="properties" type="a{sv}" direction="in" />
+ </method>
+
+ <!--
+ Updated:
+
+ Emitted when the profiles are updated by the network through OTA
+ procedures.
+
+ Since: 1.18
+ -->
+ <signal name="Updated" />
+
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml
index 56160195..fc07bc03 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml
@@ -16,6 +16,11 @@
@short_description: The ModemManager 3GPP USSD interface.
This interface provides access to actions based on the USSD protocol.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd">
@@ -30,6 +35,8 @@
response or an appropriate error. The network may be awaiting further
response from the ME after returning from this method and no new command
can be initiated until this one is cancelled or ended.
+
+ Since: 1.0
-->
<method name="Initiate">
<arg name="command" type="s" direction="in" />
@@ -45,6 +52,8 @@
or that is awaiting further input after
<link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Modem3gpp-Ussd.Initiate">Initiate()</link>
was called.
+
+ Since: 1.0
-->
<method name="Respond">
<arg name="response" type="s" direction="in" />
@@ -55,6 +64,8 @@
Cancel:
Cancel an ongoing USSD session, either mobile or network initiated.
+
+ Since: 1.0
-->
<method name="Cancel" />
@@ -63,6 +74,8 @@
A <link linkend="MMModem3gppUssdSessionState">MMModem3gppUssdSessionState</link> value,
indicating the state of any ongoing USSD session.
+
+ Since: 1.0
-->
<property name="State" type="u" access="read" />
@@ -74,6 +87,8 @@
When no USSD session is active, or when there is no network-
initiated request, this property will be a zero-length string.
+
+ Since: 1.0
-->
<property name="NetworkNotification" type="s" access="read" />
@@ -87,6 +102,8 @@
When no USSD session is active, or when there is no pending
network-initiated request, this property will be a zero-length string.
+
+ Since: 1.0
-->
<property name="NetworkRequest" type="s" access="read" />
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
index 6d082f70..0b494010 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml
@@ -17,6 +17,11 @@
This interface provides access to specific actions that may be performed
in modems with 3GPP capabilities.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp">
@@ -25,6 +30,8 @@
@operator_id: The operator ID (ie, <literal>"MCCMNC"</literal>, like <literal>"310260"</literal>) to register. An empty string can be used to register to the home network.
Request registration with a given mobile network.
+
+ Since: 1.0
-->
<method name="Register">
<arg name="operator_id" type="s" direction="in" />
@@ -80,15 +87,49 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<method name="Scan">
<arg name="results" type="aa{sv}" direction="out" />
</method>
<!--
+ SetEpsUeModeOperation:
+ @mode: a <link linkend="MMModem3gppEpsUeModeOperation">MMModem3gppEpsUeModeOperation</link>.
+
+ Sets the UE mode of operation for EPS.
+
+ Since: 1.10
+ -->
+ <method name="SetEpsUeModeOperation">
+ <arg name="mode" type="u" direction="in" />
+ </method>
+
+ <!--
+ SetInitialEpsBearerSettings:
+ @settings: List of properties to use when requesting the LTE attach procedure.
+
+ Updates the default settings to be used in the initial default EPS bearer when registering to the LTE network.
+
+ The allowed properties in this method are all the 3GPP-specific ones specified
+ in the <link linkend="gdbus-property-org-freedesktop-ModemManager1-Bearer.Properties">bearer properties</link>;
+ i.e.: <literal>"apn"</literal>, <literal>"ip-type"</literal>,
+ <literal>"allowed-auth"</literal>, <literal>"user"</literal>, and
+ <literal>"password"</literal>.
+
+ Since: 1.10
+ -->
+ <method name="SetInitialEpsBearerSettings">
+ <arg name="settings" type="a{sv}" direction="in" />
+ </method>
+
+ <!--
Imei:
The <ulink url="http://en.wikipedia.org/wiki/Imei">IMEI</ulink> of the device.
+
+ Since: 1.0
-->
<property name="Imei" type="s" access="read" />
@@ -98,6 +139,8 @@
A <link linkend="MMModem3gppRegistrationState">MMModem3gppRegistrationState</link>
value specifying the mobile registration status as defined in 3GPP TS 27.007
section 10.1.19.
+
+ Since: 1.0
-->
<property name="RegistrationState" type="u" access="read" />
@@ -114,6 +157,8 @@
If the <literal>MCC</literal> and <literal>MNC</literal> are not known
or the mobile is not registered to a mobile network, this property will
be a zero-length (blank) string.
+
+ Since: 1.0
-->
<property name="OperatorCode" type="s" access="read" />
@@ -125,6 +170,8 @@
If the operator name is not known or the mobile is not
registered to a mobile network, this property will be a zero-length
(blank) string.
+
+ Since: 1.0
-->
<property name="OperatorName" type="s" access="read" />
@@ -133,17 +180,142 @@
Bitmask of <link linkend="MMModem3gppFacility">MMModem3gppFacility</link> values
for which PIN locking is enabled.
+
+ Since: 1.0
-->
<property name="EnabledFacilityLocks" type="u" access="read" />
<!--
+ DisableFacilityLock:
+ @properties: A tuple of facility type and control key.
+
+ Sends control key to modem to disable selected facility lock
+
+ <variablelist>
+ <varlistentry><term>"facility"</term>
+ <listitem>
+ <para>
+ A <link linkend="MMModem3gppFacility">MMModem3gppFacility</link> value
+ representing the type of the facility lock to disable.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term>"control key"</term>
+ <listitem>
+ <para>
+ Alphanumeric key required to unlock facility.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ -->
+ <method name="DisableFacilityLock">
+ <arg name="properties" type="(us)" direction="in" />
+ </method>
+
+ <!--
+ SetPacketServiceState:
+ @state: a <link linkend="MMModem3gppPacketServiceState">MMModem3gppPacketServiceState</link>.
+
+ Explicitly attach or detach packet service on the current registered network.
+
+ Since: 1.20
+ -->
+ <method name="SetPacketServiceState">
+ <arg name="state" type="u" direction="in" />
+ </method>
+
+ <!--
SubscriptionState:
A <link linkend="MMModem3gppSubscriptionState">MMModem3gppSubscriptionState</link>
value representing the subscription status of the account and whether there
is any data remaining, given as an unsigned integer (signature <literal>"u"</literal>).
+
+ Since: 1.2
+
+ Deprecated: 1.10.0. The value of this property can only be obtained with operator
+ specific logic (e.g. processing specific PCO info), and therefore it doesn't make sense
+ to expose it in the ModemManager interface.
-->
<property name="SubscriptionState" type="u" access="read" />
+ <!--
+ EpsUeModeOperation:
+
+ A <link linkend="MMModem3gppEpsUeModeOperation">MMModem3gppEpsUeModeOperation</link>
+ value representing the UE mode of operation for EPS, given as an unsigned integer
+ (signature <literal>"u"</literal>).
+
+ Since: 1.10
+ -->
+ <property name="EpsUeModeOperation" type="u" access="read" />
+
+ <!--
+ Pco:
+
+ The raw PCOs received from the network, given as array of PCO
+ elements (signature <literal>"a(ubay)"</literal>).
+
+ Each PCO is defined as a sequence of 3 fields:
+ <orderedlist>
+ <listitem>
+ The session ID associated with the PCO, given as an
+ unsigned integer value (signature <literal>"u"</literal>).
+ </listitem>
+ <listitem>
+ The flag that indicates whether the PCO data contains the
+ complete PCO structure received from the network, given as
+ a boolean value (signature <literal>"b"</literal>).
+ </listitem>
+ <listitem>
+ The raw PCO data, given as an array of bytes (signature
+ <literal>"ay"</literal>).
+ </listitem>
+ </orderedlist>
+
+ Since: 1.10
+ -->
+ <property name="Pco" type="a(ubay)" access="read" />
+
+ <!--
+ InitialEpsBearer:
+
+ The object path for the initial default EPS bearer.
+
+ Since: 1.10
+ -->
+ <property name="InitialEpsBearer" type="o" access="read" />
+
+ <!--
+ InitialEpsBearerSettings:
+
+ List of properties requested by the device for the initial EPS bearer during
+ LTE network attach procedure.
+
+ The network may decide to use different settings during the actual device attach
+ procedure, e.g. if the device is roaming or no explicit settings were requested,
+ so the values shown in the
+ #org.freedesktop.ModemManager1.Modem.Modem3gpp:InitialEpsBearer
+ bearer object may be totally different.
+
+ This is a read-only property, updating these settings should be done using the
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem-Modem3gpp.SetInitialEpsBearerSettings">SetInitialEpsBearerSettings()</link>
+ method.
+
+ Since: 1.10
+ -->
+ <property name="InitialEpsBearerSettings" type="a{sv}" access="read" />
+
+ <!--
+ PacketServiceState:
+
+ A <link linkend="MMModem3gppPacketServiceState">MMModem3gppPacketServiceState</link>
+ value specifying the packet domain service state.
+
+ Since: 1.20
+ -->
+ <property name="PacketServiceState" type="u" access="read" />
+
</interface>
</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml b/introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml
index 28e4e161..6d04cc5b 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml
@@ -17,6 +17,11 @@
This interface provides access to specific actions that may be performed
in modems with CDMA capabilities.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. Mixed 3GPP+3GPP2 devices will require
+ a valid unlocked SIM card before any of the features in the interface can
+ be used.
-->
<interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
@@ -29,6 +34,8 @@
activation functionality, if any.
Some modems will reboot after this call is made.
+
+ Since: 1.0
-->
<method name="Activate">
<arg name="carrier_code" type="s" direction="in" />
@@ -107,9 +114,11 @@
ActivationStateChanged:
@activation_state: Current activation state, given as a <link linkend="MMModemCdmaActivationState">MMModemCdmaActivationState</link>.
@activation_error: Carrier-specific error code, given as a <link linkend="MMCdmaActivationError">MMCdmaActivationError</link>.
- @status_changes: Properties that have changed as a result of this activation state chage, including <literal>"mdn"</literal> and <literal>"min"</literal>. The dictionary may be empty if the changed properties are unknown.
+ @status_changes: Properties that have changed as a result of this activation state change, including <literal>"mdn"</literal> and <literal>"min"</literal>. The dictionary may be empty if the changed properties are unknown.
The device activation state changed.
+
+ Since: 1.0
-->
<signal name="ActivationStateChanged">
<arg name="activation_state" type="u" />
@@ -122,6 +131,8 @@
A <link linkend="MMModemCdmaActivationState">MMModemCdmaActivationState</link>
value specifying the state of the activation in the 3GPP2 network.
+
+ Since: 1.0
-->
<property name="ActivationState" type="u" access="read" />
@@ -129,6 +140,8 @@
Meid:
The modem's <ulink url="http://en.wikipedia.org/wiki/MEID">Mobile Equipment Identifier</ulink>.
+
+ Since: 1.0
-->
<property name="Meid" type="s" access="read" />
@@ -139,6 +152,8 @@
The modem's
<ulink url="http://en.wikipedia.org/wiki/Electronic_serial_number">Electronic Serial Number</ulink>
(superceded by MEID but still used by older devices).
+
+ Since: 1.0
-->
<property name="Esn" type="s" access="read" />
@@ -152,6 +167,8 @@
See <ulink url="http://ifast.org">ifast.org</ulink> or the mobile
broadband provider database for mappings of SIDs to network providers.
+
+ Since: 1.0
-->
<property name="Sid" type="u" access="read" />
@@ -162,6 +179,8 @@
<ulink url="http://en.wikipedia.org/wiki/Network_Identification_Number">Network Identifier</ulink>
of the serving CDMA 1x network, if known, and
if the modem is registered with a CDMA 1x network.
+
+ Since: 1.0
-->
<property name="Nid" type="u" access="read" />
@@ -170,6 +189,8 @@
A <link linkend="MMModemCdmaRegistrationState">MMModemCdmaRegistrationState</link>
value specifying the CDMA 1x registration state.
+
+ Since: 1.0
-->
<property name="Cdma1xRegistrationState" type="u" access="read" />
@@ -178,6 +199,8 @@
A <link linkend="MMModemCdmaRegistrationState">MMModemCdmaRegistrationState</link>
value specifying the EVDO registration state.
+
+ Since: 1.0
-->
<property name="EvdoRegistrationState" type="u" access="read" />
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Oma.xml b/introspection/org.freedesktop.ModemManager1.Modem.Oma.xml
index 351ff518..d697265e 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Oma.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Oma.xml
@@ -17,6 +17,11 @@
Device management sessions are either on-demand (client-initiated), or
automatically initiated by either the device itself or the network.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Oma">
@@ -25,6 +30,8 @@
@features: Bitmask of <link linkend="MMModemOmaFeature">MMModemOmaFeature</link> flags, specifying which device management features should get enabled or disabled. <link linkend="MM-OMA-FEATURE-NONE:CAPS">MM_OMA_FEATURE_NONE</link> will disable all features.
Configures which OMA device management features should be enabled.
+
+ Since: 1.2
-->
<method name="Setup">
<arg name="features" type="u" direction="in" />
@@ -35,6 +42,8 @@
@session_type: Type of client-initiated device management session,given as a <link linkend="MMModemOmaSessionType">MMModemOmaSessionType</link>
Starts a client-initiated device management session.
+
+ Since: 1.2
-->
<method name="StartClientInitiatedSession">
<arg name="session_type" type="u" direction="in" />
@@ -46,6 +55,8 @@
@accept: Boolean specifying whether the session is accepted or rejected.
Accepts or rejects a network-initiated device management session.
+
+ Since: 1.2
-->
<method name="AcceptNetworkInitiatedSession">
<arg name="session_id" type="u" direction="in" />
@@ -56,6 +67,8 @@
CancelSession:
Cancels the current on-going device management session.
+
+ Since: 1.2
-->
<method name="CancelSession" />
@@ -65,6 +78,8 @@
Bitmask of <link linkend="MMModemOmaFeature">MMModemOmaFeature</link>
flags, specifying which device management features are enabled or
disabled.
+
+ Since: 1.2
-->
<property name="Features" type="u" access="read" />
@@ -86,6 +101,8 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.2
-->
<property name="PendingNetworkInitiatedSessions" type="a(uu)" access="read" />
@@ -94,6 +111,8 @@
Type of the current on-going device management session, given as a
<link linkend="MMOmaSessionType">MMOmaSessionType</link>.
+
+ Since: 1.2
-->
<property name="SessionType" type="u" access="read" />
@@ -102,6 +121,8 @@
State of the current on-going device management session, given as a
<link linkend="MMOmaSessionState">MMOmaSessionState</link>.
+
+ Since: 1.2
-->
<property name="SessionState" type="i" access="read" />
@@ -112,6 +133,8 @@
@session_state_failed_reason: Reason of failure, given as a <link linkend="MMOmaSessionStateFailedReason">MMOmaSessionStateFailedReason</link>, if @session_state is <link linkend="MM-OMA-SESSION-STATE-FAILED:CAPS">MM_OMA_SESSION_STATE_FAILED</link>.
The session state changed.
+
+ Since: 1.2
-->
<signal name="SessionStateChanged">
<arg name="old_session_state" type="i" />
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Sar.xml b/introspection/org.freedesktop.ModemManager1.Modem.Sar.xml
new file mode 100644
index 00000000..53623a99
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Sar.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ ModemManager 1.0 Interface Specification
+
+ Copyright (C) 2020 Google, Inc.
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <!--
+ org.freedesktop.ModemManager1.Modem.Sar:
+ @short_description: The ModemManager dynamic SAR interface.
+
+ The SAR interface defines operations and properties for dynamic SAR, so
+ that user applications can control the output power level dynamically.
+
+ Using the SAR interface is not trivial, and it requires prior knowledge of
+ the internal SAR mapping table configured in each device.
+
+ The SAR mapping table is defined by the manufacturer of the device, and is
+ usually setup to match a specific hardware setup (e.g. a specific laptop or
+ hardware configuration). This table may provide different power levels for
+ different frequency bands and/or antennas.
+
+ An example SAR mapping table, where all bands and antennas share the same
+ level, could look like this:
+ <table>
+ <tr><td>Power Level</td><td>LTE B3</td> <td>LTE B7</td> <td>LTE B20</td></tr>
+ <tr><td>0</td> <td>26.0 dBm</td> <td>26.5 dBm</td> <td>27.0 dBm</td></tr>
+ <tr><td>1</td> <td>26.5 dBm</td> <td>27.0 dBm</td> <td>27.5 dBm</td></tr>
+ <tr><td>2</td> <td>27.0 dBm</td> <td>27.5 dBm</td> <td>28.0 dBm</td></tr>
+ <tr><td>3</td> <td>27.5 dBm</td> <td>28.0 dBm</td> <td>28.5 dBm</td></tr>
+ <tr><td>4</td> <td>28.0 dBm</td> <td>28.5 dBm</td> <td>28.5 dBm</td></tr>
+ <tr><td>5</td> <td>28.5 dBm</td> <td>28.5 dBm</td> <td>28.5 dBm</td></tr>
+ </table>
+
+ In this example, the SAR mapping table has 6 different levels (0 to 5),
+ and it's applied exclusively for LTE bands B3, B7 and B20 in all available
+ antennas. None of this information is exposed to the user in the SAR
+ interface, not even the amount of levels available.
+ -->
+ <interface name="org.freedesktop.ModemManager1.Modem.Sar">
+
+ <!--
+ Enable:
+ @enable: %TRUE to enable dynamic SAR and %FALSE to disable it.
+
+ Enable or disable dynamic SAR.
+
+ When enabled, the modem's output power level can be dynamically updated
+ by the host.
+
+ When disabled, the modem's output power level is dynamically updated
+ exclusively by the device.
+
+ Since: 1.20
+ -->
+ <method name="Enable">
+ <arg name="enable" type="b" direction="in" />
+ </method>
+
+ <!--
+ SetPowerLevel:
+ @level: Index of the SAR power level mapping table.
+
+ Set current dynamic SAR power level for all antennas on the device.
+
+ Please check with your modem vendor for detailed description on the
+ number of levels and corresponding power output for each level.
+
+ The index set here will apply to all antennas on the system.
+
+ Since: 1.20
+ -->
+ <method name="SetPowerLevel">
+ <arg name="level" type="u" direction="in" />
+ </method>
+
+ <!--
+ State:
+
+ Boolean indicating whether dynamic SAR is currently enabled.
+
+ Since: 1.20
+ -->
+ <property name="State" type="b" access="read" />
+
+ <!--
+ PowerLevel:
+
+ Current index of the SAR power level mapping table that is being used
+ for setting the output power of all antennas on the system.
+
+ This value is only applicable when dynamic SAR is enabled.
+
+ Since: 1.20
+ -->
+ <property name="PowerLevel" type="u" access="read" />
+
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml b/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml
index 094cbcf8..93b2996f 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml
@@ -3,7 +3,8 @@
<!--
ModemManager 0.8 Interface Specification
- Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ Copyright (C) 2021 Intel Corporation
-->
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
@@ -13,28 +14,123 @@
@short_description: The ModemManager Signal interface.
This interface provides access to extended signal quality information.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
+
+ There are two different ways defined to use the interface: either enabling
+ peridic polling (with the Setup() method), or configuring the device with
+ thresholds so that it reports the value updates automatically (with the
+ SetupThresholds() method, since 1.20).
+
+ Both Setup() and SetupThresholds() can also be used at the same time if
+ required, e.g. if they report different signal quality measurement types.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Signal">
<!--
Setup:
- @rate: refresh rate to set, in seconds. 0 to disable retrieval.
+ @rate: refresh rate to set, in seconds. Use 0 to disable periodic polling.
+
+ Enable or disable the extended signal quality information retrieval via
+ periodic polling.
- Setup extended signal quality information retrieval.
+ Polling is less than optimal; a better way to be notified of extended
+ signal quality updates is to configure the modem to trigger the reports
+ when the signal changes, i.e. with SetupThresholds().
+
+ Since: 1.2
-->
<method name="Setup">
<arg name="rate" type="u" direction="in" />
</method>
<!--
+ SetupThresholds:
+ @settings: threshold values to set.
+
+ Setup thresholds so that the device itself decides when to report the
+ extended signal quality information updates.
+
+ The thresholds configured via this method specify the delta between
+ specific signal quality measurements that would trigger a report by the
+ modem. For example, the user may want to be notified every time the
+ signal RSSI changes more than 10dBm, so a value of 10 would be
+ configured as <literal>"rssi-threshold"</literal>.
+
+ The device may not support this kind of threshold setting, and instead
+ support fixed signal levels as thresholds (e.g. trigger reports when
+ signal RSSI crosses -90dBm). On these devices, the threshold configured
+ by the user as a difference between measurements is converted to fixed
+ signal levels automatically, depending on the expected range for each of
+ the configured values. E.g. if the user configures 10dBm as
+ <literal>"rssi-threshold"</literal>, the fixed signal levels could be
+ automatically set to -100dBm, -90dBm, -80dBm, -70dBm and -60dBm.
+
+ <variablelist>
+ <varlistentry><term><literal>"rssi-threshold"</literal></term>
+ <listitem>
+ The difference of signal RSSI measurements, in dBm, that should
+ trigger a signal quality report update, given as an unsigned
+ integer (signature <literal>"u"</literal>). Use 0 to disable this
+ threshold.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"error-rate-threshold"</literal></term>
+ <listitem>
+ A boolean value, indicating whether signal quality report updates
+ should be triggered when error rate measurements change (signature
+ <literal>"b"</literal>).
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ If any of the settings is not given as input, the corresponding threshold
+ will be considered disabled, and device defaults will apply.
+
+ Since: 1.20
+ -->
+ <method name="SetupThresholds">
+ <arg name="settings" type="a{sv}" direction="in" />
+ </method>
+
+ <!--
Rate:
- Refresh rate for the extended signal quality information updates,
- in seconds. A value of 0 disables the retrieval of the values.
+ Refresh rate, in seconds, for the extended signal quality information
+ periodic polling, as configured via the Setup() method.
+
+ A value of 0 indicates the periodic polling is disabled.
+
+ Since: 1.2
-->
<property name="Rate" type="u" access="read" />
<!--
+ RssiThreshold:
+
+ The difference of signal RSSI measurements, in dBm, that should trigger
+ a signal quality report update.
+
+ A value of 0 indicates the threshold is disabled.
+
+ Since: 1.20
+ -->
+ <property name="RssiThreshold" type="u" access="read" />
+
+ <!--
+ ErrorRateThreshold:
+
+ Flag indicating whether signal quality report updates should be triggered
+ on error rate measurement changes.
+
+ Since: 1.20
+ -->
+ <property name="ErrorRateThreshold" type="b" access="read" />
+
+ <!--
Cdma:
Dictionary of available signal information for the CDMA1x access
@@ -60,7 +156,18 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry><term><literal>"error-rate"</literal></term>
+ <listitem>
+ <para>
+ Frame error rate, in percentage value, given as a
+ floating point value (signature <literal>"d"</literal>).
+ Since: 1.20.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
+
+ Since: 1.2
-->
<property name="Cdma" type="a{sv}" access="read" />
@@ -106,7 +213,18 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry><term><literal>"error-rate"</literal></term>
+ <listitem>
+ <para>
+ Packet error rate, in percentage value, given as a
+ floating point value (signature <literal>"d"</literal>).
+ Since: 1.20.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
+
+ Since: 1.2
-->
<property name="Evdo" type="a{sv}" access="read" />
@@ -128,7 +246,18 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry><term><literal>"error-rate"</literal></term>
+ <listitem>
+ <para>
+ Bit error rate (BER), in percentage value, given as a
+ floating point value (signature <literal>"d"</literal>).
+ Since: 1.20.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
+
+ Since: 1.2
-->
<property name="Gsm" type="a{sv}" access="read" />
@@ -149,23 +278,42 @@
given as a floating point value (signature <literal>"d"</literal>).
</para>
</listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"rscp"</literal></term>
+ <listitem>
+ <para>
+ The UMTS RSCP (Received Signal Code Power), in dBm, given as a
+ floating point value (signature <literal>"d"</literal>).
+ </para>
+ </listitem>
</varlistentry>
<varlistentry><term><literal>"ecio"</literal></term>
<listitem>
<para>
- The UMTS Ec/Io, in dBm, given as a floating point value
+ The UMTS Ec/Io, in dB, given as a floating point value
(signature <literal>"d"</literal>).
</para>
</listitem>
</varlistentry>
+ <varlistentry><term><literal>"error-rate"</literal></term>
+ <listitem>
+ <para>
+ Block error rate (BLER), in percentage value, given as a
+ floating point value (signature <literal>"d"</literal>).
+ Since: 1.20.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
+
+ Since: 1.2
-->
<property name="Umts" type="a{sv}" access="read" />
<!--
Lte:
- Dictionary of available signal information for the UMTS (WCDMA) access
+ Dictionary of available signal information for the LTE access
technology.
This dictionary is composed of a string key, with an associated data
@@ -204,9 +352,69 @@
</para>
</listitem>
</varlistentry>
+ <varlistentry><term><literal>"error-rate"</literal></term>
+ <listitem>
+ <para>
+ Block error rate (BLER), in percentage value, given as a
+ floating point value (signature <literal>"d"</literal>).
+ Since: 1.20.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
+
+ Since: 1.2
-->
<property name="Lte" type="a{sv}" access="read" />
+ <!--
+ Nr5g:
+
+ Dictionary of available signal information for the 5G access
+ technology.
+
+ This dictionary is composed of a string key, with an associated data
+ which contains type-specific information.
+
+ <variablelist>
+ <varlistentry><term><literal>"rsrq"</literal></term>
+ <listitem>
+ <para>
+ The 5G RSRQ (Reference Signal Received Quality), in dB, given as
+ a floating point value (signature <literal>"d"</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"rsrp"</literal></term>
+ <listitem>
+ <para>
+ The 5G RSRP (Reference Signal Received Power), in dBm, given as
+ a floating point value (signature <literal>"d"</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"snr"</literal></term>
+ <listitem>
+ <para>
+ The 5G S/R ratio, in dB, given as
+ a floating point value (signature <literal>"d"</literal>).
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>"error-rate"</literal></term>
+ <listitem>
+ <para>
+ Block error rate (BLER), in percentage value, given as a
+ floating point value (signature <literal>"d"</literal>).
+ Since: 1.20.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.16
+ -->
+ <property name="Nr5g" type="a{sv}" access="read" />
+
</interface>
</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Simple.xml b/introspection/org.freedesktop.ModemManager1.Modem.Simple.xml
index 937664b2..9a023816 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Simple.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Simple.xml
@@ -16,6 +16,11 @@
@short_description: The ModemManager Simple interface.
The Simple interface allows controlling and querying the status of Modems.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Simple">
@@ -34,16 +39,20 @@
This call may make a large number of changes to modem configuration
based on properties passed in. For example, given a PIN-locked, disabled
- GSM/UMTS modem, this call may unlock the SIM PIN, alter the access
- technology preference, wait for network registration (or force
- registration to a specific provider), create a new packet data bearer
- using the given "apn", and connect that bearer.
+ GSM/UMTS modem, this call may unlock the SIM PIN, wait for network
+ registration (or force registration to a specific provider), create a
+ new packet data bearer using the given "apn", and connect that bearer.
+
+ The list of allowed properties includes all the ones defined in the
+ <link linkend="gdbus-property-org-freedesktop-ModemManager1-Bearer.Properties">bearer properties</link>
+ plus these additional ones that are only applicable to this method,
+ and only to 3GPP (GSM/UMTS/LTE/5GNR) devices:
- Allowed key/value pairs in @properties are:
<variablelist>
<varlistentry><term><literal>"pin"</literal></term>
<listitem>
- SIM-PIN unlock code, given as a string value (signature <literal>"s"</literal>).
+ SIM-PIN unlock code, given as a string value (signature
+ <literal>"s"</literal>).
</listitem>
</varlistentry>
<varlistentry><term><literal>"operator-id"</literal></term>
@@ -52,58 +61,12 @@
given as a string value (signature <literal>"s"</literal>).
</listitem>
</varlistentry>
- <varlistentry><term><literal>"apn"</literal></term>
- <listitem>
- For GSM/UMTS and LTE devices the APN to use,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"ip-type"</literal></term>
- <listitem>
- For GSM/UMTS and LTE devices the IP addressing type to use,
- given as a <link linkend="MMBearerIpFamily">MMBearerIpFamily</link>
- value (signature <literal>"u"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"allowed-auth"</literal></term>
- <listitem>
- The authentication method to use, given as a
- <link linkend="MMBearerAllowedAuth">MMBearerAllowedAuth</link>
- value (signature <literal>"u"</literal>). Optional in 3GPP.
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"user"</literal></term>
- <listitem>
- User name (if any) required by the network, given as a string
- value (signature <literal>"s"</literal>). Optional in 3GPP.
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"password"</literal></term>
- <listitem>
- Password (if any) required by the network, given as a string value
- (signature <literal>"s"</literal>). Optional in 3GPP.
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"number"</literal></term>
- <listitem>
- For POTS devices the number to dial,,
- given as a string value (signature <literal>"s"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"allow-roaming"</literal></term>
- <listitem>
- %FALSE to allow only connections to home networks,
- given as a boolean value (signature <literal>"b"</literal>).
- </listitem>
- </varlistentry>
- <varlistentry><term><literal>"rm-protocol"</literal></term>
- <listitem>
- For CDMA devices, the protocol of the Rm interface, given as a
- <link linkend="MMModemCdmaRmProtocol">MMModemCdmaRmProtocol</link>
- value (signature <literal>"u"</literal>).
- </listitem>
- </varlistentry>
</variablelist>
+
+ There are no settings specific to this call that would apply to 3GPP2
+ (CDMA/EVDO) devices.
+
+ Since: 1.0
-->
<method name="Connect">
<arg name="properties" type="a{sv}" direction="in" />
@@ -117,6 +80,8 @@
disconnect all active packet data bearers.
Disconnect an active packet data connection.
+
+ Since: 1.0
-->
<method name="Disconnect">
<arg name="bearer" type="o" direction="in" />
@@ -140,8 +105,9 @@
<varlistentry><term><literal>"signal-quality"</literal></term>
<listitem>
Signal quality value, given only when registered,
- as an unsigned integer value
- (signature <literal>"u"</literal>).
+ as unsigned integer value and an additional boolean value
+ indicating if the value was recently taken.
+ (signature <literal>"(ub)"</literal>).
</listitem>
</varlistentry>
<varlistentry><term><literal>"current-bands"</literal></term>
@@ -151,7 +117,7 @@
unsigned integer values (signature <literal>"au"</literal>).
</listitem>
</varlistentry>
- <varlistentry><term><literal>"access-technology"</literal></term>
+ <varlistentry><term><literal>"access-technologies"</literal></term>
<listitem>
A <link linkend="MMModemAccessTechnology">MMModemAccessTechnology</link> value,
given only when registered, as an unsigned integer value
@@ -207,6 +173,8 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<method name="GetStatus">
<arg name="properties" type="a{sv}" direction="out" />
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Time.xml b/introspection/org.freedesktop.ModemManager1.Modem.Time.xml
index d96f9b70..7dc17e2e 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.Time.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Time.xml
@@ -16,6 +16,11 @@
This interface allows clients to receive network time and timezone
updates broadcast by mobile networks.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
-->
<interface name="org.freedesktop.ModemManager1.Modem.Time">
@@ -30,6 +35,8 @@
This method will only work if the modem tracks, or can request, the
current network time; it will not attempt to use previously-received
network time updates on the host to guess the current network time.
+
+ Since: 1.0
-->
<method name="GetNetworkTime">
<arg name="time" type="s" direction="out" />
@@ -61,6 +68,8 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<property name="NetworkTimezone" type="a{sv}" access="read" />
@@ -69,6 +78,8 @@
@time: A string containing date and time in ISO 8601 format.
Sent when the network time is updated.
+
+ Since: 1.0
-->
<signal name="NetworkTimeChanged">
<arg name="time" type="s" />
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml b/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml
new file mode 100644
index 00000000..74f755d9
--- /dev/null
+++ b/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!--
+ ModemManager 1.0 Interface Specification
+
+ Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ Copyright (C) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ Copyright (C) 2019 Purism SPC
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+
+ <!--
+ org.freedesktop.ModemManager1.Modem.Voice:
+ @short_description: The ModemManager Voice interface.
+
+ The Voice interface handles Calls.
+
+ This interface will only be available once the modem is ready to be
+ registered in the cellular network. 3GPP devices will require a valid
+ unlocked SIM card before any of the features in the interface can be
+ used.
+ -->
+ <interface name="org.freedesktop.ModemManager1.Modem.Voice">
+
+ <!--
+ ListCalls:
+ @result: The list of call object paths.
+
+ Retrieve all Calls.
+
+ This method should only be used once and subsequent information
+ retrieved either by listening for the
+ #org.freedesktop.ModemManager1.Modem.Voice::Added signal,
+ or by querying the specific Call object of interest.
+
+ Since: 1.6
+ -->
+ <method name="ListCalls">
+ <arg name="result" type="ao" direction="out" />
+ </method>
+
+ <!--
+ DeleteCall:
+ @path: The object path of the Call to delete.
+
+ Delete a Call from the list of calls.
+
+ The call will be hangup if it is still active.
+
+ Since: 1.6
+ -->
+ <method name="DeleteCall">
+ <arg name="path" type="o" direction="in" />
+ </method>
+
+ <!--
+ CreateCall:
+ @properties: Call properties from the <link linkend="gdbus-org.freedesktop.ModemManager1.Call">Call D-Bus interface</link>.
+ @path: The object path of the new call object.
+
+ Creates a new call object for a new outgoing call.
+
+ The '<link linkend="gdbus-property-org-freedesktop-ModemManager1-Call.Number">Number</link>' is the only
+ expected property to set by the user.
+
+ Since: 1.6
+ -->
+ <method name="CreateCall">
+ <arg name="properties" type="a{sv}" direction="in" />
+ <arg name="path" type="o" direction="out" />
+ </method>
+
+ <!--
+ HoldAndAccept:
+
+ Place all active calls on hold, if any, and accept the next
+ call.
+
+ Waiting calls have preference over held calls, so the next
+ call being active will be any waiting call, or otherwise,
+ any held call.
+
+ The user should monitor the state of all available ongoing
+ calls to be reported of which one becomes active.
+
+ No error is returned if there are no waiting or held calls.
+
+ Since: 1.12
+ -->
+ <method name="HoldAndAccept" />
+
+ <!--
+ HangupAndAccept:
+
+ Hangup all active calls, if any, and accept the next call.
+
+ Waiting calls have preference over held calls, so the next
+ call being active will be any waiting call, or otherwise,
+ any held call.
+
+ The user should monitor the state of all available ongoing
+ calls to be reported of which one becomes active.
+
+ No error is returned if there are no waiting or held calls.
+ In this case, this method would be equivalent to calling
+ <link linkend="gdbus-method-org-freedesktop-ModemManager1-Call.Hangup">Hangup()</link>
+ on the active call.
+
+ Since: 1.12
+ -->
+ <method name="HangupAndAccept" />
+
+ <!--
+ HangupAll:
+
+ Hangup all active calls.
+
+ Depending on how the device implements the action, calls on
+ hold or in waiting state may also be terminated.
+
+ No error is returned if there are no ongoing calls.
+
+ Since: 1.12
+ -->
+ <method name="HangupAll" />
+
+ <!--
+ Transfer:
+
+ Join the currently active and held calls together into a single
+ multiparty call, but disconnects from them.
+
+ The affected calls will be considered terminated from the point of
+ view of the subscriber.
+
+ Since: 1.12
+ -->
+ <method name="Transfer" />
+
+ <!--
+ CallWaitingSetup:
+
+ Activates or deactivates the call waiting network service, as per
+ 3GPP TS 22.083.
+
+ This operation requires communication with the network in order to
+ complete, so the modem must be successfully registered.
+
+ Since: 1.12
+ -->
+ <method name="CallWaitingSetup">
+ <arg name="enable" type="b" direction="in" />
+ </method>
+
+ <!--
+ CallWaitingQuery:
+
+ Queries the status of the call waiting network service, as per
+ 3GPP TS 22.083.
+
+ This operation requires communication with the network in order to
+ complete, so the modem must be successfully registered.
+
+ Since: 1.12
+ -->
+ <method name="CallWaitingQuery">
+ <arg name="status" type="b" direction="out" />
+ </method>
+
+ <!--
+ CallAdded:
+ @path: Object path of the new call.
+
+ Emitted when a call has been added.
+
+ Since: 1.6
+ -->
+ <signal name="CallAdded">
+ <arg name="path" type="o" />
+ </signal>
+
+ <!--
+ CallDeleted:
+ @path: Object path of the now deleted Call.
+
+ Emitted when a call has been deleted.
+
+ Since: 1.6
+ -->
+ <signal name="CallDeleted">
+ <arg name="path" type="o" />
+ </signal>
+
+ <!--
+ Calls:
+
+ The list of calls object paths.
+
+ Since: 1.6
+ -->
+ <property name="Calls" type="ao" access="read" />
+
+ <!--
+ EmergencyOnly:
+
+ A flag indicating whether emergency calls are the only allowed ones.
+
+ If this flag is set, users should only attempt voice calls to
+ emergency numbers, as standard voice calls will likely fail.
+
+ Since: 1.12
+ -->
+ <property name="EmergencyOnly" type="b" access="read" />
+
+ </interface>
+</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.xml b/introspection/org.freedesktop.ModemManager1.Modem.xml
index a5a236c5..80da06ed 100644
--- a/introspection/org.freedesktop.ModemManager1.Modem.xml
+++ b/introspection/org.freedesktop.ModemManager1.Modem.xml
@@ -17,6 +17,9 @@
The Modem interface controls the status and actions in a given modem
object.
+
+ This interface will always be available as long a the modem is considered
+ valid.
-->
<interface name="org.freedesktop.ModemManager1.Modem">
@@ -31,6 +34,8 @@
When disabled, the modem enters low-power state and no network-related
operations are available.
+
+ Since: 1.0
-->
<method name="Enable">
<arg name="enable" type="b" direction="in" />
@@ -42,6 +47,11 @@
List configured packet data bearers (EPS Bearers, PDP Contexts, or
CDMA2000 Packet Data Sessions).
+
+ Since: 1.0
+
+ Deprecated: 1.10.0. Use #org.freedesktop.ModemManager1.Modem:Bearers
+ property instead.
-->
<method name="ListBearers">
<arg name="bearers" type="ao" direction="out" />
@@ -49,7 +59,7 @@
<!--
CreateBearer:
- @properties: List of properties to assign to the bearer after creating it.
+ @properties: Dictionary of properties needed to get the bearer connected.
@path: On success, the object path of the newly created bearer.
Create a new packet data bearer using the given characteristics.
@@ -57,29 +67,10 @@
This request may fail if the modem does not support additional bearers,
if too many bearers are already defined, or if properties are invalid.
- Allowed properties are:
- <variablelist>
- <varlistentry><term><literal>"apn"</literal></term>
- <listitem><para>Access Point Name, given as a string value (signature <literal>"s"</literal>). Required in 3GPP.</para></listitem></varlistentry>
- <varlistentry><term><literal>"ip-type"</literal></term>
- <listitem><para>Addressing type, given as a <link linkend="MMBearerIpFamily">MMBearerIpFamily</link> value (signature <literal>"u"</literal>). Optional in 3GPP and CDMA.</para></listitem></varlistentry>
- <varlistentry><term><literal>"allowed-auth"</literal></term>
- <listitem><para>The authentication method to use, given as a <link linkend="MMBearerAllowedAuth">MMBearerAllowedAuth</link> value (signature <literal>"u"</literal>). Optional in 3GPP.</para></listitem></varlistentry>
- <varlistentry><term><literal>"user"</literal></term>
- <listitem><para>User name (if any) required by the network, given as a string value (signature <literal>"s"</literal>). Optional in 3GPP.</para></listitem></varlistentry>
- <varlistentry><term><literal>"password"</literal></term>
- <listitem><para>Password (if any) required by the network, given as a string value (signature <literal>"s"</literal>). Optional in 3GPP.</para></listitem></varlistentry>
- <varlistentry><term><literal>"allow-roaming"</literal></term>
- <listitem><para>Flag to tell whether connection is allowed during roaming, given as a boolean value (signature <literal>"b"</literal>). Optional in 3GPP.</para></listitem></varlistentry>
- <varlistentry><term><literal>"rm-protocol"</literal></term>
- <listitem><para>Protocol of the Rm interface, given as a <link linkend="MMModemCdmaRmProtocol">MMModemCdmaRmProtocol</link> value (signature <literal>"u"</literal>). Optional in CDMA.</para></listitem></varlistentry>
- <varlistentry><term><literal>"number"</literal></term>
- <listitem><para>Telephone number to dial, given as a string value (signature <literal>"s"</literal>). Required in POTS.</para></listitem></varlistentry>
- </variablelist>
+ The properties allowed are any of the ones defined in the
+ <link linkend="gdbus-property-org-freedesktop-ModemManager1-Bearer.Properties">bearer properties</link>.
- Some properties are only applicable to a bearer of certain access
- technologies, for example the <literal>"apn"</literal> property is not
- applicable to CDMA2000 Packet Data Session bearers.
+ Since: 1.0
-->
<method name="CreateBearer">
<arg name="properties" type="a{sv}" direction="in" />
@@ -94,6 +85,8 @@
If the bearer is currently active and providing packet data server, it
will be disconnected and that packet data service will terminate.
+
+ Since: 1.0
-->
<method name="DeleteBearer">
<arg name="bearer" type="o" direction="in" />
@@ -106,6 +99,8 @@
a newly-powered-on state.
This command may power-cycle the device.
+
+ Since: 1.0
-->
<method name="Reset" />
@@ -119,6 +114,8 @@
If not required by the modem, @code may be ignored.
This command may or may not power-cycle the device.
+
+ Since: 1.0
-->
<method name="FactoryReset">
<arg name="code" type="s" direction="in" />
@@ -126,11 +123,13 @@
<!--
SetPowerState:
- @state: A <link linkend="MMModemPowerState">MMModemPowerState</link> value, to specify the desired power state.
+ @state: A <link linkend="MMModemPowerState">MMModemPowerState</link> value, to specify the desired power state.
Set the power state of the modem. This action can only be run when the
- modem is in <link linkend="MM-MODEM-STATE-DISABLED:CAPS"><constant>MM_MODEM_STATE_DISABLED</constant></link>
- state.
+ modem is in <link linkend="MM-MODEM-STATE-DISABLED:CAPS"><constant>MM_MODEM_STATE_DISABLED</constant></link>
+ state.
+
+ Since: 1.0
-->
<method name="SetPowerState">
<arg name="state" type="u" direction="in" />
@@ -140,7 +139,14 @@
SetCurrentCapabilities:
@capabilities: Bitmask of <link linkend="MMModemCapability">MMModemCapability</link> values, to specify the capabilities to use.
- Set the capabilities of the device. A restart of the modem may be required.
+ Set the capabilities of the device.
+
+ The given bitmask should be supported by the modem, as specified in the
+ #org.freedesktop.ModemManager1.Modem:SupportedCapabilities property.
+
+ This command may power-cycle the device.
+
+ Since: 1.0
-->
<method name="SetCurrentCapabilities">
<arg name="capabilities" type="u" direction="in" />
@@ -155,6 +161,8 @@
The given combination should be supported by the modem, as specified in the
#org.freedesktop.ModemManager1.Modem:SupportedModes property.
+
+ Since: 1.0
-->
<method name="SetCurrentModes">
<arg name="modes" type="(uu)" direction="in" />
@@ -166,21 +174,46 @@
Set the radio frequency and technology bands the device is currently
allowed to use when connecting to a network.
+
+ Since: 1.0
-->
<method name="SetCurrentBands">
<arg name="bands" type="au" direction="in" />
</method>
<!--
- Command
- @cmd The command string, e.g. "AT+GCAP" or "+GCAP" (leading AT is inserted if necessary).
- @timeout The number of seconds to wait for a response.
- @response The modem's response.
+ SetPrimarySimSlot:
+ @sim_slot: SIM slot number to set as primary.
+
+ Selects which SIM slot to be considered as primary, on devices that expose
+ multiple slots in the #org.freedesktop.ModemManager1.Modem:SimSlots property.
+
+ When the switch happens the modem may require a full device reprobe, so the modem
+ object in DBus will get removed, and recreated once the selected SIM slot is in
+ use.
+
+ There is no limitation on which SIM slot to select, so the user may also set as
+ primary a slot that doesn't currently have any valid SIM card inserted.
+
+ Since: 1.16
+ -->
+ <method name="SetPrimarySimSlot">
+ <arg name="sim_slot" type="u" direction="in" />
+ </method>
+
+ <!--
+ Command:
+ @cmd: The command string, e.g. "AT+GCAP" or "+GCAP" (leading AT is inserted if necessary).
+ @timeout: The number of seconds to wait for a response.
+ @response: The modem's response.
+
+ Send an arbitrary AT command to a modem and get the response.
- Send an arbitrary AT command to a modem and get the response.
+ Note that using this interface call is only allowed when running
+ ModemManager in debug mode or if the project was built using
+ the <literal>with-at-command-via-dbus</literal> configure option.
- Note that using this interface call is only allowed when running
- ModemManager in debug mode.
+ Since: 1.0
-->
<method name="Command">
<arg name="cmd" type="s" direction="in" />
@@ -195,6 +228,8 @@
@reason: A <link linkend="MMModemStateChangeReason">MMModemStateChangeReason</link> value, specifying the reason for this state change.
The modem's state (see #org.freedesktop.ModemManager1.Modem:State) changed.
+
+ Since: 1.0
-->
<signal name="StateChanged">
<arg name="old" type="i" />
@@ -205,15 +240,71 @@
<!--
Sim:
- The path of the SIM object available in this device, if any.
+ The path of the primary active SIM object available in this device,
+ if any.
+
+ This SIM object is the one used for network registration and data
+ connection setup.
+
+ If multiple #org.freedesktop.ModemManager1.Modem.SimSlots are
+ supported, the #org.freedesktop.ModemManager1.Modem.PrimarySimSlot
+ index value specifies which is the slot number where this SIM card
+ is available.
+
+ Since: 1.0
-->
<property name="Sim" type="o" access="read" />
<!--
+ SimSlots:
+
+ The list of SIM slots available in the system, including the SIM object
+ paths if the cards are present. If a given SIM slot at a given index
+ doesn't have a SIM card available, an empty object path will be given.
+
+ The length of this array of objects will be equal to the amount of
+ available SIM slots in the system, and the index in the array is the
+ slot index.
+
+ This list includes the SIM object considered as primary active SIM slot
+ (#org.freedesktop.ModemManager1.Modem.Sim) at index
+ #org.freedesktop.ModemManager1.Modem.ActiveSimSlot.
+
+ Since: 1.16
+ -->
+ <property name="SimSlots" type="ao" access="read" />
+
+ <!--
+ PrimarySimSlot:
+
+ The index of the primary active SIM slot in the
+ #org.freedesktop.ModemManager1.Modem.SimSlots array, given in the [1,N]
+ range.
+
+ If multiple SIM slots aren't supported, this property will report
+ value 0.
+
+ In a Multi SIM Single Standby setup, this index identifies the only SIM
+ that is currently active. All the remaining slots will be inactive.
+
+ In a Multi SIM Multi Standby setup, this index identifies the active SIM
+ that is considered primary, i.e. the one that will be used when a data
+ connection is setup.
+
+ Since: 1.16
+ -->
+ <property name="PrimarySimSlot" type="u" access="read" />
+
+ <!--
Bearers:
The list of bearer object paths (EPS Bearers, PDP Contexts, or
- CDMA2000 Packet Data Sessions).
+ CDMA2000 Packet Data Sessions) as requested by the user.
+
+ This list does not include the initial EPS bearer details (see
+ #org.freedesktop.ModemManager1.Modem.Modem3gpp:InitialEpsBearer).
+
+ Since: 1.2
-->
<property name="Bearers" type="ao" access="read" />
@@ -221,12 +312,18 @@
SupportedCapabilities:
List of <link linkend="MMModemCapability">MMModemCapability</link>
- values, specifying the combinations of generic family of access
+ bitmasks, specifying the combinations of generic family of access
technologies the modem supports.
- If the modem doesn't allow changing the current capabilities, a single entry with
- <link linkend="MM-MODEM-CAPABILITY-ANY:CAPS"><constant>MM_MODEM_CAPABILITY_ANY</constant></link>
- will be given.
+ If the modem doesn't allow changing the current capabilities, the
+ list will report one single entry with the same bitmask as in
+ #org.freedesktop.ModemManager1.Modem:CurrentCapabilities.
+
+ Only multimode devices implementing both 3GPP (GSM/UMTS/LTE/5GNR) and
+ 3GPP2 (CDMA/EVDO) specs will report more than one combination of
+ capabilities.
+
+ Since: 1.0
-->
<property name="SupportedCapabilities" type="au" access="read" />
@@ -234,8 +331,13 @@
CurrentCapabilities:
Bitmask of <link linkend="MMModemCapability">MMModemCapability</link>
- values, specifying the generic family of access technologies the modem
- currently supports without a firmware reload or reinitialization.
+ values, specifying the currently used generic family of access
+ technologies.
+
+ This bitmask will be one of the ones listed in
+ #org.freedesktop.ModemManager1.Modem:SupportedCapabilities.
+
+ Since: 1.0
-->
<property name="CurrentCapabilities" type="u" access="read" />
@@ -250,24 +352,54 @@
while GSM/UMTS devices typically support three or more, and any
LTE-capable device (whether LTE-only, GSM/UMTS-capable, and/or
CDMA2000-capable) also typically support three or more.
+
+ Deprecated: 1.18.0. There is no way to query the modem how many bearers
+ it supports, so the value exposed in this property in all the different
+ implementations is always equal to the value in
+ #org.freedesktop.ModemManager1.Modem:MaxActiveBearers, so there is no
+ point in using this property.
+
+ Since: 1.0
-->
<property name="MaxBearers" type="u" access="read" />
<!--
MaxActiveBearers:
- The maximum number of active packet data bearers the modem supports.
+ The maximum number of active
+ <link linkend="MM-BEARER-TYPE-DEFAULT:CAPS"><constant>MM_BEARER_TYPE_DEFAULT</constant></link>
+ bearers that may be explicitly enabled by the user without multiplexing support.
POTS and CDMA2000-only devices support one active bearer, while GSM/UMTS
- and LTE-capable devices (including LTE/CDMA devices) typically support
- at least two active bearers.
+ and LTE/5GNR capable devices (including 3GPP+3GPP3 multimode devices) may support
+ one or more active bearers, depending on the amount of physical ports exposed
+ by the device.
+
+ Since: 1.0
-->
<property name="MaxActiveBearers" type="u" access="read" />
<!--
+ MaxActiveMultiplexedBearers:
+
+ The maximum number of active
+ <link linkend="MM-BEARER-TYPE-DEFAULT:CAPS"><constant>MM_BEARER_TYPE_DEFAULT</constant></link>
+ bearers that may be explicitly enabled by the user with multiplexing support
+ on one single network interface.
+
+ If the modem doesn't support multiplexing of data sessiones, a value of 0 will
+ be reported.
+
+ Since: 1.18
+ -->
+ <property name="MaxActiveMultiplexedBearers" type="u" access="read" />
+
+ <!--
Manufacturer:
The equipment manufacturer, as reported by the modem.
+
+ Since: 1.0
-->
<property name="Manufacturer" type="s" access="read" />
@@ -275,6 +407,8 @@
Model:
The equipment model, as reported by the modem.
+
+ Since: 1.0
-->
<property name="Model" type="s" access="read" />
@@ -282,10 +416,39 @@
Revision:
The revision identification of the software, as reported by the modem.
+
+ Since: 1.0
-->
<property name="Revision" type="s" access="read" />
<!--
+ CarrierConfiguration:
+
+ The description of the carrier-specific configuration (MCFG) in use by the modem.
+
+ Since: 1.12
+ -->
+ <property name="CarrierConfiguration" type="s" access="read" />
+
+ <!--
+ CarrierConfigurationRevision:
+
+ The revision identification of the carrier-specific configuration (MCFG) in use by the modem.
+
+ Since: 1.12
+ -->
+ <property name="CarrierConfigurationRevision" type="s" access="read" />
+
+ <!--
+ HardwareRevision:
+
+ The revision identification of the hardware, as reported by the modem.
+
+ Since: 1.8
+ -->
+ <property name="HardwareRevision" type="s" access="read" />
+
+ <!--
DeviceIdentifier:
A best-effort device identifier based on various device information like
@@ -298,6 +461,8 @@
This is not the device's IMEI or ESN since those may not be available
before unlocking the device via a PIN.
+
+ Since: 1.0
-->
<property name="DeviceIdentifier" type="s" access="read" />
@@ -309,6 +474,11 @@
In Linux for example, this points to a sysfs path of the usb_device
object.
+
+ This value may also be set by the user using the MM_ID_PHYSDEV_UID udev
+ tag (e.g. binding the tag to a specific sysfs path).
+
+ Since: 1.0
-->
<property name="Device" type="s" access="read" />
@@ -317,6 +487,8 @@
The Operating System device drivers handling communication with the modem
hardware.
+
+ Since: 1.0
-->
<property name="Drivers" type="as" access="read" />
@@ -324,6 +496,8 @@
Plugin:
The name of the plugin handling this modem.
+
+ Since: 1.0
-->
<property name="Plugin" type="s" access="read" />
@@ -331,6 +505,8 @@
PrimaryPort:
The name of the primary port using to control the modem.
+
+ Since: 1.0
-->
<property name="PrimaryPort" type="s" access="read" />
@@ -341,6 +517,8 @@
integer pairs. The string is the port name or path, and the integer is
the port type given as a
<link linkend="MMModemPortType">MMModemPortType</link> value.
+
+ Since: 1.0
-->
<property name="Ports" type="a(su)" access="read" />
@@ -351,6 +529,8 @@
This will be the IMEI number for GSM devices and the hex-format ESN/MEID
for CDMA devices.
+
+ Since: 1.0
-->
<property name="EquipmentIdentifier" type="s" access="read" />
@@ -359,6 +539,8 @@
Current lock state of the device, given as a
<link linkend="MMModemLock">MMModemLock</link> value.
+
+ Since: 1.0
-->
<property name="UnlockRequired" type="u" access="read" />
@@ -370,6 +552,8 @@
before the code becomes blocked (requiring a PUK) or permanently blocked. Dictionary
entries exist only for the codes for which the modem is able to report retry
counts.
+
+ Since: 1.0
-->
<property name="UnlockRetries" type="a{uu}" access="read" />
@@ -382,6 +566,8 @@
If the device's state cannot be determined,
<link linkend="MM-MODEM-STATE-UNKNOWN:CAPS"><constant>MM_MODEM_STATE_UNKNOWN</constant></link>
will be reported.
+
+ Since: 1.0
-->
<property name="State" type="i" access="read" />
@@ -389,9 +575,11 @@
StateFailedReason:
Error specifying why the modem is in
- <link linkend="MM-MODEM-STATE-FAILED:CAPS"><constant>MM_MODEM_STATE_FAILED</constant></link>
- state, given as a
- <link linkend="MMModemStateFailedReason">MMModemStateFailedReason</link> value.
+ <link linkend="MM-MODEM-STATE-FAILED:CAPS"><constant>MM_MODEM_STATE_FAILED</constant></link>
+ state, given as a
+ <link linkend="MMModemStateFailedReason">MMModemStateFailedReason</link> value.
+
+ Since: 1.0
-->
<property name="StateFailedReason" type="u" access="read" />
@@ -405,6 +593,8 @@
If the device's access technology cannot be determined,
<link linkend="MM-MODEM-ACCESS-TECHNOLOGY-UNKNOWN:CAPS"><constant>MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN</constant></link>
will be reported.
+
+ Since: 1.0
-->
<property name="AccessTechnologies" type="u" access="read" />
@@ -417,6 +607,8 @@
The additional boolean value indicates if the quality value given was
recently taken.
+
+ Since: 1.0
-->
<property name="SignalQuality" type="(ub)" access="read" />
@@ -425,14 +617,18 @@
List of numbers (e.g. MSISDN in 3GPP) being currently handled by this
modem.
+
+ Since: 1.0
-->
<property name="OwnNumbers" type="as" access="read" />
<!--
PowerState:
- A <link linkend="MMModemPowerState">MMModemPowerState</link> value
- specifying the current power state of the modem.
+ A <link linkend="MMModemPowerState">MMModemPowerState</link> value
+ specifying the current power state of the modem.
+
+ Since: 1.0
-->
<property name="PowerState" type="u" access="read" />
@@ -456,6 +652,8 @@
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<property name="SupportedModes" type="a(uu)" access="read" />
@@ -469,6 +667,8 @@
The pair must be one of those specified in
#org.freedesktop.ModemManager1.Modem:SupportedModes.
+
+ Since: 1.0
-->
<property name="CurrentModes" type="(uu)" access="read" />
@@ -482,6 +682,8 @@
For POTS devices, only the
<link linkend="MM-MODEM-BAND-ANY:CAPS"><constant>MM_MODEM_BAND_ANY</constant></link>
mode will be returned.
+
+ Since: 1.0
-->
<property name="SupportedBands" type="au" access="read" />
@@ -493,6 +695,8 @@
currently using when connecting to a network.
It must be a subset of #org.freedesktop.ModemManager1.Modem:SupportedBands.
+
+ Since: 1.0
-->
<property name="CurrentBands" type="au" access="read" />
@@ -501,6 +705,8 @@
Bitmask of <link linkend="MMBearerIpFamily">MMBearerIpFamily</link> values,
specifying the IP families supported by the device.
+
+ Since: 1.0
-->
<property name="SupportedIpFamilies" type="u" access="read" />
diff --git a/introspection/org.freedesktop.ModemManager1.Sim.xml b/introspection/org.freedesktop.ModemManager1.Sim.xml
index 99794722..3008aaf2 100644
--- a/introspection/org.freedesktop.ModemManager1.Sim.xml
+++ b/introspection/org.freedesktop.ModemManager1.Sim.xml
@@ -24,6 +24,8 @@
@pin: A string containing the PIN code.
Send the PIN to unlock the SIM card.
+
+ Since: 1.0
-->
<method name="SendPin">
<arg name="pin" type="s" direction="in" />
@@ -35,6 +37,8 @@
@pin: A string containing the PIN code.
Send the PUK and a new PIN to unlock the SIM card.
+
+ Since: 1.0
-->
<method name="SendPuk">
<arg name="puk" type="s" direction="in" />
@@ -47,6 +51,8 @@
@enabled: %TRUE to enable PIN checking, %FALSE otherwise.
Enable or disable the PIN checking.
+
+ Since: 1.0
-->
<method name="EnablePin">
<arg name="pin" type="s" direction="in" />
@@ -59,6 +65,8 @@
@new_pin: A string containing the new PIN code.
Change the PIN code.
+
+ Since: 1.0
-->
<method name="ChangePin">
<arg name="old_pin" type="s" direction="in" />
@@ -66,13 +74,49 @@
</method>
<!--
+ SetPreferredNetworks:
+ @preferred_plmns: List of preferred networks.
+
+ Stores the provided preferred network list to the SIM card. Each entry contains
+ an operator id string (<literal>"MCCMNC"</literal>) consisting of 5 or 6 digits,
+ and an <link linkend="MMModemAccessTechnology">MMModemAccessTechnology</link> mask
+ to store to SIM card if supported.
+
+ This method removes any pre-existing entries of the preferred network list. Note
+ that even if this operation fails, the preferred network list on the SIM card may
+ have changed. Read the <link linkend="gdbus-property-org-freedesktop-ModemManager1-Sim.PreferredNetworks">
+ PreferredNetworks</link> property to get the up-to-date list.
+
+ Since: 1.18
+ -->
+ <method name="SetPreferredNetworks">
+ <arg name="preferred_networks" type="a(su)" direction="in" />
+ </method>
+
+ <!--
+ Active:
+
+ Boolean indicating whether the SIM is currently active.
+
+ On systems that support Multi SIM Single Standby, only one SIM may be
+ active at any given time, which will be the one considered primary.
+
+ On systems that support Multi SIM Multi Standby, more than one SIM may
+ be active at any given time, but only one of them is considered primary.
+
+ Since: 1.16
+ -->
+ <property name="Active" type="b" access="read" />
+
+ <!--
SimIdentifier:
- An obfuscated SIM identifier based on the IMSI or the
- ICCID.
+ The ICCID of the SIM card.
This may be available before the PIN has been entered depending
on the device itself.
+
+ Since: 1.0
-->
<property name="SimIdentifier" type="s" access="read" />
@@ -80,14 +124,27 @@
Imsi:
The IMSI of the SIM card, if any.
+
+ Since: 1.0
-->
<property name="Imsi" type="s" access="read" />
<!--
+ Eid:
+
+ The EID of the SIM card, if any.
+
+ Since: 1.16
+ -->
+ <property name="Eid" type="s" access="read" />
+
+ <!--
OperatorId:
The ID of the network operator that issued the SIM card,
formatted as a 5 or 6-digit MCC/MNC code (e.g. <literal>"310410"</literal>).
+
+ Since: 1.0
-->
<property name="OperatorIdentifier" type="s" access="read" />
@@ -95,8 +152,38 @@
OperatorName:
The name of the network operator, as given by the SIM card, if known.
+
+ Since: 1.0
-->
<property name="OperatorName" type="s" access="read" />
+ <!--
+ EmergencyNumbers:
+
+ List of emergency numbers programmed in the SIM card.
+
+ These numbers should be treated as numbers for emergency calls in
+ addition to 112 and 911.
+
+ Since: 1.12
+ -->
+ <property name="EmergencyNumbers" type="as" access="read" />
+
+ <!--
+ PreferredNetworks:
+
+ List of preferred networks with access technologies configured in the SIM card.
+
+ Each entry contains an operator id string (<literal>"MCCMNC"</literal>)
+ consisting of 5 or 6 digits, and an
+ <link linkend="MMModemAccessTechnology">MMModemAccessTechnology</link> mask.
+ If the SIM card does not support access technology storage, the mask will be
+ set to <link linkend="MM-MODEM-ACCESS-TECHNOLOGY-UNKNOWN:CAPS">
+ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN</link>.
+
+ Since: 1.18
+ -->
+ <property name="PreferredNetworks" type="a(su)" access="read" />
+
</interface>
</node>
diff --git a/introspection/org.freedesktop.ModemManager1.Sms.xml b/introspection/org.freedesktop.ModemManager1.Sms.xml
index 343e6111..c615d566 100644
--- a/introspection/org.freedesktop.ModemManager1.Sms.xml
+++ b/introspection/org.freedesktop.ModemManager1.Sms.xml
@@ -22,6 +22,8 @@
Send:
If the message has not yet been sent, queue it for delivery.
+
+ Since: 1.0
-->
<method name="Send" />
@@ -34,6 +36,8 @@
value, describing the storage where this message is to be kept; or
<link linkend="MM-SMS-STORAGE-UNKNOWN:CAPS"><constant>MM_SMS_STORAGE_UNKNOWN</constant></link>
if the default storage should be used.
+
+ Since: 1.0
-->
<method name="Store">
<arg name="storage" type="u" direction="in" />
@@ -44,6 +48,8 @@
A <link linkend="MMSmsState">MMSmsState</link> value,
describing the state of the message.
+
+ Since: 1.0
-->
<property name="State" type="u" access="read" />
@@ -52,6 +58,8 @@
A <link linkend="MMSmsPduType">MMSmsPduType</link> value,
describing the type of PDUs used in the SMS message.
+
+ Since: 1.0
-->
<property name="PduType" type="u" access="read" />
@@ -59,6 +67,8 @@
Number:
Number to which the message is addressed.
+
+ Since: 1.0
-->
<property name="Number" type="s" access="read" />
@@ -71,6 +81,8 @@
modem, the message will be broken into multiple parts or messages.
Note that Text and Data are never given at the same time.
+
+ Since: 1.0
-->
<property name="Text" type="s" access="read" />
@@ -83,6 +95,8 @@
modem, the message will be broken into multiple parts or messages.
Note that Text and Data are never given at the same time.
+
+ Since: 1.0
-->
<property name="Data" type="ay" access="read" />
@@ -92,6 +106,8 @@
Indicates the SMS service center number.
Always empty for 3GPP2/CDMA.
+
+ Since: 1.0
-->
<property name="SMSC" type="s" access="read" />
@@ -101,20 +117,22 @@
Indicates when the SMS expires in the SMSC.
This value is composed of a
- <link linkend="MMSmsValidityType">MMSmsValidityType</link>
- key, with an associated data which contains type-specific validity
- information:
+ <link linkend="MMSmsValidityType">MMSmsValidityType</link>
+ key, with an associated data which contains type-specific validity
+ information:
- <variablelist>
- <varlistentry><term><link linkend="MM-SMS-VALIDITY-TYPE-RELATIVE:CAPS">MM_SMS_VALIDITY_TYPE_RELATIVE</link></term>
+ <variablelist>
+ <varlistentry><term><link linkend="MM-SMS-VALIDITY-TYPE-RELATIVE:CAPS">MM_SMS_VALIDITY_TYPE_RELATIVE</link></term>
<listitem>
<para>
- The value is the length of the validity period in minutes, given
- as an unsigned integer (D-Bus signature <literal>'u'</literal>).
+ The value is the length of the validity period in minutes, given
+ as an unsigned integer (D-Bus signature <literal>'u'</literal>).
</para>
</listitem>
</varlistentry>
</variablelist>
+
+ Since: 1.0
-->
<property name="Validity" type="(uv)" access="read" />
@@ -125,6 +143,8 @@
is not used for this message, otherwise the 3GPP SMS message class.
Always -1 for 3GPP2/CDMA.
+
+ Since: 1.0
-->
<property name="Class" type="i" access="read" />
@@ -134,6 +154,8 @@
A <link linkend="MMSmsCdmaTeleserviceId">MMSmsCdmaTeleserviceId</link> value.
Always <link linkend="MM-SMS-CDMA-TELESERVICE-ID-UNKNOWN:CAPS">MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN</link> for 3GPP.
+
+ Since: 1.2
-->
<property name="TeleserviceId" type="u" access="read" />
@@ -143,6 +165,8 @@
A <link linkend="MMSmsCdmaServiceCategory">MMSmsCdmaServiceCategory</link> value.
Always <link linkend="MM-SMS-CDMA-SERVICE-CATEGORY-UNKNOWN:CAPS">MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN</link> for 3GPP.
+
+ Since: 1.2
-->
<property name="ServiceCategory" type="u" access="read" />
@@ -150,6 +174,8 @@
DeliveryReportRequest:
#TRUE if delivery report request is required, #FALSE otherwise.
+
+ Since: 1.0
-->
<property name="DeliveryReportRequest" type="b" access="read" />
@@ -161,6 +187,8 @@
If the PDU type is
<link linkend="MM-SMS-PDU-TYPE-STATUS-REPORT:CAPS"><constant>MM_SMS_PDU_TYPE_STATUS_REPORT</constant></link>,
this field identifies the Message Reference of the PDU associated to the status report.
+
+ Since: 1.0
-->
<property name="MessageReference" type="u" access="read" />
@@ -174,6 +202,8 @@
<link linkend="MM-SMS-PDU-TYPE-DELIVER:CAPS"><constant>MM_SMS_PDU_TYPE_DELIVER</constant></link>.
or
<link linkend="MM-SMS-PDU-TYPE-STATUS-REPORT:CAPS"><constant>MM_SMS_PDU_TYPE_STATUS_REPORT</constant></link>.
+
+ Since: 1.0
-->
<property name="Timestamp" type="s" access="read" />
@@ -186,6 +216,8 @@
This field is only applicable if the PDU type is
<link linkend="MM-SMS-PDU-TYPE-STATUS-REPORT:CAPS"><constant>MM_SMS_PDU_TYPE_STATUS_REPORT</constant></link>.
+
+ Since: 1.0
-->
<property name="DischargeTimestamp" type="s" access="read" />
@@ -197,6 +229,8 @@
This field is only applicable if the PDU type is
<link linkend="MM-SMS-PDU-TYPE-STATUS-REPORT:CAPS"><constant>MM_SMS_PDU_TYPE_STATUS_REPORT</constant></link>.
+
+ Since: 1.0
-->
<property name="DeliveryState" type="u" access="read" />
@@ -205,6 +239,8 @@
A <link linkend="MMSmsStorage">MMSmsStorage</link> value,
describing the storage where this message is kept.
+
+ Since: 1.0
-->
<property name="Storage" type="u" access="read" />
diff --git a/introspection/org.freedesktop.ModemManager1.xml b/introspection/org.freedesktop.ModemManager1.xml
index 2ecd026e..e80e0cd3 100644
--- a/introspection/org.freedesktop.ModemManager1.xml
+++ b/introspection/org.freedesktop.ModemManager1.xml
@@ -9,7 +9,7 @@
Copyright (C) 2011-2013 Lanedo GmbH
-->
-<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+<node name="/org/freedesktop/ModemManager1" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
<!--
org.freedesktop.ModemManager1:
@@ -24,6 +24,8 @@
ScanDevices:
Start a new scan for connected modem devices.
+
+ Since: 1.0
-->
<method name="ScanDevices" />
@@ -32,10 +34,115 @@
@level: One of <literal>"ERR"</literal>, <literal>"WARN"</literal>, <literal>"INFO"</literal>, <literal>"DEBUG"</literal>.
Set logging verbosity.
+
+ Since: 1.0
-->
<method name="SetLogging">
<arg name="level" type="s" direction="in" />
</method>
+ <!--
+ ReportKernelEvent:
+ @properties: event properties.
+
+ Reports a kernel event to ModemManager.
+
+ This method is only available if udev is not being used to report kernel
+ events.
+
+ The @properties dictionary is composed of key/value string pairs. The
+ possible keys are:
+
+ <variablelist>
+ <varlistentry><term><literal>action</literal></term>
+ <listitem>
+ <para>
+ The type of action, given as a string value (signature
+ <literal>"s"</literal>).
+ This parameter is MANDATORY.
+ </para>
+ <variablelist>
+ <varlistentry><term><literal>add</literal></term>
+ <listitem>
+ A new kernel device has been added.
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>remove</literal></term>
+ <listitem>
+ An existing kernel device has been removed.
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>name</literal></term>
+ <listitem>
+ <para>
+ The device name, given as a string value (signature
+ <literal>"s"</literal>).
+ This parameter is MANDATORY.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>subsystem</literal></term>
+ <listitem>
+ <para>
+ The device subsystem, given as a string value (signature
+ <literal>"s"</literal>).
+ This parameter is MANDATORY.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry><term><literal>uid</literal></term>
+ <listitem>
+ <para>
+ The unique ID of the physical device, given as a string value
+ (signature <literal>"s"</literal>).
+ This parameter is OPTIONAL, if not given the sysfs path of the
+ physical device will be used. This parameter must be the same
+ for all devices exposed by the same physical device.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ Since: 1.8
+ -->
+ <method name="ReportKernelEvent">
+ <arg name="properties" type="a{sv}" direction="in" />
+ </method>
+
+ <!--
+ InhibitDevice:
+ @uid: the unique ID of the physical device, given in the
+ #org.freedesktop.ModemManager1.Modem:Device property.
+ @inhibit: %TRUE to inhibit the modem and %FALSE to uninhibit it.
+
+ Inhibit or uninhibit the device.
+
+ When the modem is inhibited ModemManager will close all its ports and
+ unexport it from the bus, so that users of the interface are no longer
+ able to operate with it.
+
+ This operation binds the inhibition request to the existence of the
+ caller in the DBus bus. If the caller disappears from the bus, the
+ inhibition will automatically removed.
+
+ Since: 1.10
+ -->
+ <method name="InhibitDevice">
+ <arg name="uid" type="s" direction="in" />
+ <arg name="inhibit" type="b" direction="in" />
+ </method>
+
+ <!--
+ Version:
+
+ The runtime version of the ModemManager daemon.
+
+ Since: 1.10
+ -->
+ <property name="Version" type="s" access="read" />
+
</interface>
</node>
diff --git a/libmm-glib/Makefile.am b/libmm-glib/Makefile.am
index 54e79fc9..7084f294 100644
--- a/libmm-glib/Makefile.am
+++ b/libmm-glib/Makefile.am
@@ -1,5 +1,8 @@
SUBDIRS = generated . tests
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
lib_LTLIBRARIES = libmm-glib.la
libmm_glib_la_SOURCES = \
@@ -15,6 +18,8 @@ libmm_glib_la_SOURCES = \
mm-modem.c \
mm-modem-3gpp.h \
mm-modem-3gpp.c \
+ mm-modem-3gpp-profile-manager.h \
+ mm-modem-3gpp-profile-manager.c \
mm-modem-3gpp-ussd.h \
mm-modem-3gpp-ussd.c \
mm-modem-cdma.h \
@@ -27,6 +32,8 @@ libmm_glib_la_SOURCES = \
mm-modem-time.c \
mm-modem-firmware.h \
mm-modem-firmware.c \
+ mm-modem-sar.h \
+ mm-modem-sar.c \
mm-modem-signal.h \
mm-modem-signal.c \
mm-modem-oma.h \
@@ -37,6 +44,10 @@ libmm_glib_la_SOURCES = \
mm-sms.c \
mm-modem-messaging.h \
mm-modem-messaging.c \
+ mm-modem-voice.h \
+ mm-modem-voice.c \
+ mm-call.h \
+ mm-call.c \
mm-bearer.h \
mm-bearer.c \
mm-common-helpers.h \
@@ -49,8 +60,12 @@ libmm_glib_la_SOURCES = \
mm-bearer-properties.c \
mm-sms-properties.h \
mm-sms-properties.c \
+ mm-call-properties.h \
+ mm-call-properties.c \
mm-bearer-ip-config.h \
mm-bearer-ip-config.c \
+ mm-bearer-stats.h \
+ mm-bearer-stats.c \
mm-location-common.h \
mm-location-3gpp.h \
mm-location-3gpp.c \
@@ -66,12 +81,30 @@ libmm_glib_la_SOURCES = \
mm-network-timezone.c \
mm-firmware-properties.h \
mm-firmware-properties.c \
+ mm-firmware-update-settings.h \
+ mm-firmware-update-settings.c \
mm-cdma-manual-activation-properties.h \
mm-cdma-manual-activation-properties.c \
mm-signal.h \
- mm-signal.c
+ mm-signal.c \
+ mm-kernel-event-properties.h \
+ mm-kernel-event-properties.c \
+ mm-pco.h \
+ mm-pco.c \
+ mm-call-audio-format.h \
+ mm-call-audio-format.c \
+ mm-sim-preferred-network.h \
+ mm-sim-preferred-network.c \
+ mm-3gpp-profile.h \
+ mm-3gpp-profile.c \
+ mm-signal-threshold-properties.h \
+ mm-signal-threshold-properties.c \
+ mm-compat.h \
+ mm-compat.c \
+ $(NULL)
libmm_glib_la_CPPFLAGS = \
+ $(LIBMM_GLIB_CFLAGS) \
-I$(srcdir) \
-I$(top_srcdir) \
-I$(top_builddir) \
@@ -80,18 +113,21 @@ libmm_glib_la_CPPFLAGS = \
-I${top_srcdir}/libmm-glib/generated \
-I${top_builddir}/libmm-glib/generated \
-DLIBMM_GLIB_COMPILATION \
- $(AM_CPPFLAGS)
+ $(NULL)
-libmm_glib_la_CFLAGS = \
- $(LIBMM_GLIB_CFLAGS) \
- $(AM_CFLAGS)
+libmm_glib_gla_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(NULL)
libmm_glib_la_LIBADD = \
${top_builddir}/libmm-glib/generated/libmm-generated.la \
- $(LIBMM_GLIB_LIBS)
+ $(NULL)
libmm_glib_la_LDFLAGS = \
- -version-info $(MM_GLIB_LT_CURRENT):$(MM_GLIB_LT_REVISION):$(MM_GLIB_LT_AGE)
+ -version-info $(MM_GLIB_LT_CURRENT):$(MM_GLIB_LT_REVISION):$(MM_GLIB_LT_AGE) \
+ $(WARN_LDFLAGS) \
+ $(LIBMM_GLIB_LIBS) \
+ $(NULL)
includedir = @includedir@/libmm-glib
include_HEADERS = \
@@ -101,23 +137,29 @@ include_HEADERS = \
mm-object.h \
mm-modem.h \
mm-modem-3gpp.h \
+ mm-modem-3gpp-profile-manager.h \
mm-modem-3gpp-ussd.h \
mm-modem-cdma.h \
mm-modem-messaging.h \
mm-modem-location.h \
mm-modem-time.h \
mm-modem-firmware.h \
+ mm-modem-sar.h \
mm-modem-signal.h \
mm-modem-oma.h \
mm-modem-simple.h \
mm-sim.h \
mm-sms.h \
+ mm-modem-voice.h \
+ mm-call.h \
mm-bearer.h \
mm-simple-status.h \
mm-simple-connect-properties.h \
mm-bearer-properties.h \
mm-sms-properties.h \
+ mm-call-properties.h \
mm-bearer-ip-config.h \
+ mm-bearer-stats.h \
mm-location-common.h \
mm-location-3gpp.h \
mm-location-gps-nmea.h \
@@ -126,8 +168,17 @@ include_HEADERS = \
mm-unlock-retries.h \
mm-network-timezone.h \
mm-firmware-properties.h \
+ mm-firmware-update-settings.h \
mm-cdma-manual-activation-properties.h \
- mm-signal.h
+ mm-signal.h \
+ mm-kernel-event-properties.h \
+ mm-pco.h \
+ mm-call-audio-format.h \
+ mm-sim-preferred-network.h \
+ mm-3gpp-profile.h \
+ mm-signal-threshold-properties.h \
+ mm-compat.h \
+ $(NULL)
CLEANFILES =
@@ -141,8 +192,10 @@ GENERATED_H = \
mm-gdbus-manager.h \
mm-gdbus-sim.h \
mm-gdbus-sms.h \
+ mm-gdbus-call.h \
mm-gdbus-bearer.h \
- mm-gdbus-modem.h
+ mm-gdbus-modem.h \
+ $(NULL)
GENERATED_C = \
mm-enums-types.c \
@@ -151,15 +204,19 @@ GENERATED_C = \
mm-gdbus-manager.c \
mm-gdbus-sim.c \
mm-gdbus-sms.c \
+ mm-gdbus-call.c \
mm-gdbus-bearer.c \
- mm-gdbus-modem.c
+ mm-gdbus-modem.c \
+ $(NULL)
PUBLIC_H = \
ModemManager-names.h \
ModemManager-version.h
ModemManager-enums.h \
ModemManager-errors.h \
- ModemManager.h
+ ModemManager-compat.h \
+ ModemManager.h \
+ $(NULL)
INTROSPECTION_GIRS = ModemManager-1.0.gir
INTROSPECTION_SCANNER_ARGS = --warn-all
@@ -171,10 +228,12 @@ ModemManager_1_0_gir_CFLAGS = $(libmm_glib_la_CPPFLAGS)
ModemManager_1_0_gir_LIBS = libmm-glib.la
ModemManager_1_0_gir_EXPORT_PACKAGES = libmm-glib
ModemManager_1_0_gir_SCANNERFLAGS = \
+ $(WARN_SCANNERFLAGS) \
--c-include "libmm-glib.h" \
--identifier-prefix=MM \
--identifier-prefix=Mm \
- --symbol-prefix=mm
+ --symbol-prefix=mm \
+ $(NULL)
ModemManager_1_0_gir_FILES = \
$(include_HEADERS) \
$(filter-out %.h,$(libmm_glib_la_SOURCES)) \
diff --git a/libmm-glib/generated/Makefile.am b/libmm-glib/generated/Makefile.am
index 2a608fc1..5e171238 100644
--- a/libmm-glib/generated/Makefile.am
+++ b/libmm-glib/generated/Makefile.am
@@ -1,6 +1,9 @@
SUBDIRS = . tests
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
+
noinst_LTLIBRARIES = libmm-generated.la
GENERATED_H = \
@@ -9,8 +12,10 @@ GENERATED_H = \
mm-gdbus-manager.h \
mm-gdbus-sim.h \
mm-gdbus-sms.h \
+ mm-gdbus-call.h \
mm-gdbus-bearer.h \
- mm-gdbus-modem.h
+ mm-gdbus-modem.h \
+ $(NULL)
GENERATED_C = \
mm-enums-types.c \
@@ -19,68 +24,79 @@ GENERATED_C = \
mm-gdbus-manager.c \
mm-gdbus-sim.c \
mm-gdbus-sms.c \
+ mm-gdbus-call.c \
mm-gdbus-bearer.c \
- mm-gdbus-modem.c
+ mm-gdbus-modem.c \
+ $(NULL)
GENERATED_DOC = \
mm-gdbus-doc-org.freedesktop.ModemManager1.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml \
- mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml \
+ $(NULL)
BUILT_SOURCES = $(GENERATED_H) $(GENERATED_C) $(GENERATED_DOC)
# Enum types
-mm-enums-types.h: Makefile.am $(top_srcdir)/include/ModemManager-enums.h $(top_srcdir)/build-aux/mm-enums-template.h
+mm-enums-types.h: Makefile.am $(top_srcdir)/include/ModemManager-enums.h $(top_srcdir)/build-aux/mm-enums-types.h.template
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#include <ModemManager.h>\n#ifndef __MM_ENUMS_TYPES_H__\n#define __MM_ENUMS_TYPES_H__\n" \
- --template $(top_srcdir)/build-aux/mm-enums-template.h \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
--ftail "#endif /* __MM_ENUMS_TYPES_H__ */\n" \
$(top_srcdir)/include/ModemManager-enums.h > $@
-mm-enums-types.c: Makefile.am $(top_srcdir)/include/ModemManager-enums.h $(top_srcdir)/build-aux/mm-enums-template.c mm-enums-types.h
+mm-enums-types.c: Makefile.am $(top_srcdir)/include/ModemManager-enums.h $(top_srcdir)/build-aux/mm-enums-types.c.template mm-enums-types.h
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#include \"mm-enums-types.h\"\n" \
- --template $(top_srcdir)/build-aux/mm-enums-template.c \
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
$(top_srcdir)/include/ModemManager-enums.h > $@
# Error types & quarks
-mm-errors-types.h: Makefile.am $(top_srcdir)/include/ModemManager-errors.h $(top_srcdir)/build-aux/mm-errors-template.h
+mm-errors-types.h: Makefile.am $(top_srcdir)/include/ModemManager-errors.h $(top_srcdir)/build-aux/mm-errors-types.h.template
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#ifndef __MM_ERRORS_TYPES_H__\n#define __MM_ERRORS_TYPES_H__\n" \
- --template $(top_srcdir)/build-aux/mm-errors-template.h \
+ --template $(top_srcdir)/build-aux/mm-errors-types.h.template \
--ftail "#endif /* __MM_ERRORS_TYPES_H__ */\n" \
$(top_srcdir)/include/ModemManager-errors.h > $@
-mm-errors-types.c: Makefile.am $(top_srcdir)/include/ModemManager-errors.h $(top_srcdir)/build-aux/mm-errors-template.c mm-errors-types.h
+mm-errors-types.c: Makefile.am $(top_srcdir)/include/ModemManager-errors.h $(top_srcdir)/build-aux/mm-errors-types.c.template mm-errors-types.h
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#include <ModemManager.h>\n#include \"mm-errors-types.h\"\n" \
- --template $(top_srcdir)/build-aux/mm-errors-template.c \
+ --template $(top_srcdir)/build-aux/mm-errors-types.c.template \
$(top_srcdir)/include/ModemManager-errors.h > $@
-mm-errors-quarks.c: Makefile.am $(top_srcdir)/include/ModemManager-errors.h $(top_srcdir)/build-aux/mm-errors-quarks-template.c $(top_builddir)/include/ModemManager-names.h mm-errors-types.h
+mm-errors-quarks.c: Makefile.am $(top_srcdir)/include/ModemManager-errors.h $(top_srcdir)/build-aux/mm-errors-quarks.c.template $(top_builddir)/include/ModemManager-names.h mm-errors-types.h
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#include <ModemManager.h>\n#include \"mm-errors-types.h\"\n" \
- --template $(top_srcdir)/build-aux/mm-errors-quarks-template.c \
+ --template $(top_srcdir)/build-aux/mm-errors-quarks.c.template \
$(top_srcdir)/include/ModemManager-errors.h > $@
# Manager interface
mm_gdbus_manager_generated = \
mm-gdbus-manager.h \
mm-gdbus-manager.c \
- mm-gdbus-doc-org.freedesktop.ModemManager1.xml
-$(mm_gdbus_manager_generated): $(top_srcdir)/introspection/org.freedesktop.ModemManager1.xml
+ mm-gdbus-doc-org.freedesktop.ModemManager1.xml \
+ $(NULL)
+mm_gdbus_manager_deps = \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.xml \
+ $(NULL)
+mm-gdbus-manager.c: $(mm_gdbus_manager_deps)
$(AM_V_GEN) $(GDBUS_CODEGEN) \
--interface-prefix org.freedesktop.ModemManager1. \
--c-namespace=MmGdbus \
@@ -88,6 +104,8 @@ $(mm_gdbus_manager_generated): $(top_srcdir)/introspection/org.freedesktop.Modem
--generate-c-code mm-gdbus-manager \
$< \
$(NULL)
+$(filter-out mm-gdbus-manager.c, $(mm_gdbus_manager_generated)): $(mm_gdbus_manager_deps) mm-gdbus-manager.c
+ @: # nothing to do, generated as a side-effect of the .c
# Modem interfaces
mm_gdbus_modem_generated = \
@@ -95,28 +113,36 @@ mm_gdbus_modem_generated = \
mm-gdbus-modem.c \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml \
mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml \
- mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml \
+ $(NULL)
mm_gdbus_modem_deps = \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Messaging.xml \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Location.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Time.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Firmware.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Oma.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.ModemCdma.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.xml \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Sar.xml \
$(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Simple.xml \
- $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml
-$(mm_gdbus_modem_generated): $(mm_gdbus_modem_deps)
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Modem.Signal.xml \
+ $(NULL)
+mm-gdbus-modem.c: $(mm_gdbus_modem_deps)
$(AM_V_GEN) $(GDBUS_CODEGEN) \
--interface-prefix org.freedesktop.ModemManager1. \
--c-namespace=MmGdbus \
@@ -126,15 +152,22 @@ $(mm_gdbus_modem_generated): $(mm_gdbus_modem_deps)
--annotate "org.freedesktop.ModemManager1.Modem.ModemCdma" org.gtk.GDBus.C.Name ModemCdma \
--annotate "org.freedesktop.ModemManager1.Modem.Modem3gpp" org.gtk.GDBus.C.Name Modem3gpp \
--annotate "org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd" org.gtk.GDBus.C.Name Modem3gppUssd \
+ --annotate "org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager" org.gtk.GDBus.C.Name Modem3gppProfileManager \
$^ \
$(NULL)
+$(filter-out mm-gdbus-modem.c, $(mm_gdbus_modem_generated)): $(mm_gdbus_modem_deps) mm-gdbus-modem.c
+ @: # nothing to do, generated as a side-effect of the .c
# SIM interface
mm_gdbus_sim_generated = \
mm-gdbus-sim.h \
mm-gdbus-sim.c \
- mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml
-$(mm_gdbus_sim_generated): $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Sim.xml
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml \
+ $(NULL)
+mm_gdbus_sim_deps = \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Sim.xml \
+ $(NULL)
+mm-gdbus-sim.c: $(mm_gdbus_sim_deps)
$(AM_V_GEN) $(GDBUS_CODEGEN) \
--interface-prefix org.freedesktop.ModemManager1. \
--c-namespace=MmGdbus \
@@ -142,13 +175,19 @@ $(mm_gdbus_sim_generated): $(top_srcdir)/introspection/org.freedesktop.ModemMana
--generate-c-code mm-gdbus-sim \
$< \
$(NULL)
+$(filter-out mm-gdbus-sim.c, $(mm_gdbus_sim_generated)): $(mm_gdbus_sim_deps) mm-gdbus-sim.c
+ @: # nothing to do, generated as a side-effect of the .c
# Bearer interface
mm_gdbus_bearer_generated = \
mm-gdbus-bearer.h \
mm-gdbus-bearer.c \
- mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml
-$(mm_gdbus_bearer_generated): $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Bearer.xml
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml \
+ $(NULL)
+mm_gdbus_bearer_deps = \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Bearer.xml \
+ $(NULL)
+mm-gdbus-bearer.c: $(mm_gdbus_bearer_deps)
$(AM_V_GEN) $(GDBUS_CODEGEN) \
--interface-prefix org.freedesktop.ModemManager1. \
--c-namespace=MmGdbus \
@@ -156,13 +195,19 @@ $(mm_gdbus_bearer_generated): $(top_srcdir)/introspection/org.freedesktop.ModemM
--generate-c-code mm-gdbus-bearer \
$< \
$(NULL)
+$(filter-out mm-gdbus-bearer.c, $(mm_gdbus_bearer_generated)): $(mm_gdbus_bearer_deps) mm-gdbus-bearer.c
+ @: # nothing to do, generated as a side-effect of the .c
# SMS interface
mm_gdbus_sms_generated = \
mm-gdbus-sms.h \
mm-gdbus-sms.c \
- mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml
-$(mm_gdbus_sms_generated): $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Sms.xml
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml \
+ $(NULL)
+mm_gdbus_sms_deps = \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Sms.xml \
+ $(NULL)
+mm-gdbus-sms.c: $(mm_gdbus_sms_deps)
$(AM_V_GEN) $(GDBUS_CODEGEN) \
--interface-prefix org.freedesktop.ModemManager1. \
--c-namespace=MmGdbus \
@@ -171,10 +216,30 @@ $(mm_gdbus_sms_generated): $(top_srcdir)/introspection/org.freedesktop.ModemMana
--annotate "org.freedesktop.ModemManager1.Sms:Data" org.gtk.GDBus.C.ForceGVariant True \
$< \
$(NULL)
+$(filter-out mm-gdbus-sms.c, $(mm_gdbus_sms_generated)): $(mm_gdbus_sms_deps) mm-gdbus-sms.c
+ @: # nothing to do, generated as a side-effect of the .c
+
+# Call interface
+mm_gdbus_call_generated = \
+ mm-gdbus-call.h \
+ mm-gdbus-call.c \
+ mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml \
+ $(NULL)
+mm_gdbus_call_deps = \
+ $(top_srcdir)/introspection/org.freedesktop.ModemManager1.Call.xml \
+ $(NULL)
+mm-gdbus-call.c: $(mm_gdbus_call_deps)
+ $(AM_V_GEN) $(GDBUS_CODEGEN) \
+ --interface-prefix org.freedesktop.ModemManager1. \
+ --c-namespace=MmGdbus \
+ --generate-docbook mm-gdbus-doc \
+ --generate-c-code mm-gdbus-call \
+ $< \
+ $(NULL)
+$(filter-out mm-gdbus-call.c, $(mm_gdbus_call_generated)): $(mm_gdbus_call_deps) mm-gdbus-call.c
+ @: # nothing to do, generated as a side-effect of the .c
-nodist_libmm_generated_la_SOURCES = \
- $(GENERATED_H) \
- $(GENERATED_C)
+nodist_libmm_generated_la_SOURCES = $(GENERATED_H) $(GENERATED_C)
libmm_generated_la_CPPFLAGS = \
$(LIBMM_GLIB_CFLAGS) \
@@ -183,12 +248,19 @@ libmm_generated_la_CPPFLAGS = \
-I$(top_builddir)/include \
-Wno-unused-function \
-Wno-float-equal \
- -Wno-shadow
+ -Wno-shadow \
+ $(NULL)
+
+libmm_generated_la_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(NULL)
-libmm_generated_la_LIBADD = \
- $(LIBMM_GLIB_LIBS)
+libmm_generated_la_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(LIBMM_GLIB_LIBS) \
+ $(NULL)
includedir = @includedir@/libmm-glib
-include_HEADERS = $(GENERATED_H)
+nodist_include_HEADERS = $(GENERATED_H)
CLEANFILES = $(GENERATED_H) $(GENERATED_C) $(GENERATED_DOC)
diff --git a/libmm-glib/generated/meson.build b/libmm-glib/generated/meson.build
new file mode 100644
index 00000000..a44115e5
--- /dev/null
+++ b/libmm-glib/generated/meson.build
@@ -0,0 +1,129 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+generated_inc = include_directories('.')
+generated_build_dir = meson.current_build_dir()
+
+common_c_args = cc.get_supported_arguments([
+ '-Wno-float-equal',
+ '-Wno-shadow',
+ '-Wno-unused-function',
+])
+
+gen_sources = []
+gen_headers = []
+gen_docs = []
+
+# Enum types
+enums_types = 'mm-enums-types'
+
+gen_sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: mm_enums_header,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-enums-types.h"\n',
+)
+
+gen_headers += gnome.mkenums(
+ enums_types + '.h',
+ sources: mm_enums_header,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include <ModemManager.h>\n#ifndef __MM_ENUMS_TYPES_H__\n#define __MM_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_ENUMS_TYPES_H__ */\n',
+ install_header: true,
+ install_dir: mm_glib_pkgincludedir,
+)
+
+# Error types & quarks
+errors_types = 'mm-errors-types'
+
+gen_sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: mm_errors_header,
+ c_template: build_aux_dir / errors_types + '.c.template',
+ fhead: '#include <ModemManager.h>\n#include "mm-errors-types.h"\n',
+)
+
+gen_headers += gnome.mkenums(
+ errors_types + '.h',
+ sources: mm_errors_header,
+ h_template: build_aux_dir / errors_types + '.h.template',
+ fhead: '#ifndef __MM_ERRORS_TYPES_H__\n#define __MM_ERRORS_TYPES_H__\n',
+ ftail: '#endif /* __MM_ERRORS_TYPES_H__ */\n',
+ install_header: true,
+ install_dir: mm_glib_pkgincludedir,
+)
+
+errors_quarks = 'mm-errors-quarks'
+
+gen_sources += gnome.mkenums(
+ errors_quarks + '.c',
+ sources: mm_errors_header,
+ c_template: build_aux_dir / errors_quarks + '.c.template',
+ fhead: '#include <ModemManager.h>\n#include "mm-errors-types.h"\n',
+)
+
+gdbus_ifaces = [
+ ['bearer', mm_ifaces_bearer, [], false],
+ ['call', mm_ifaces_call, [], false],
+ ['manager', mm_ifaces, [], false],
+ ['sim', mm_ifaces_sim, [], false],
+]
+
+annotations = [
+ ['org.freedesktop.ModemManager1.Modem.ModemCdma', 'org.gtk.GDBus.C.Name', 'ModemCdma'],
+ ['org.freedesktop.ModemManager1.Modem.Modem3gpp', 'org.gtk.GDBus.C.Name', 'Modem3gpp'],
+ ['org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd', 'org.gtk.GDBus.C.Name', 'Modem3gppUssd'],
+ ['org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager', 'org.gtk.GDBus.C.Name', 'Modem3gppProfileManager'],
+]
+
+gdbus_ifaces += [['modem', mm_ifaces_modem, annotations, true]]
+
+annotations = [['org.freedesktop.ModemManager1.Sms:Data', 'org.gtk.GDBus.C.ForceGVariant', 'True']]
+
+gdbus_ifaces += [['sms', mm_ifaces_sms, annotations, false]]
+
+foreach gdbus_iface: gdbus_ifaces
+ gdbus_sources = gnome.gdbus_codegen(
+ 'mm-gdbus-' + gdbus_iface[0],
+ sources: gdbus_iface[1],
+ interface_prefix: 'org.freedesktop.ModemManager1.',
+ namespace: 'MmGdbus',
+ docbook: 'mm-gdbus-doc',
+ annotations: gdbus_iface[2],
+ object_manager: gdbus_iface[3],
+ autocleanup: 'objects',
+ # FIXME: due to the lack of possibility to add `docbook targets` to the `expand_content_files`.
+ build_by_default: true,
+ install_header: true,
+ install_dir: mm_glib_pkgincludedir,
+ )
+
+ gen_sources += gdbus_sources[0]
+ gen_headers += gdbus_sources[1]
+ # FIXME: the `expand_content_files` must be strings
+ gen_docs += gdbus_sources[2]
+endforeach
+
+deps = [
+ include_dep,
+ gio_unix_dep,
+ glib_deps,
+]
+
+libmm_generated = static_library(
+ 'mm-generated',
+ sources: gen_sources + gen_headers,
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: common_c_args,
+)
+
+libmm_generated_dep = declare_dependency(
+ sources: gen_headers,
+ include_directories: generated_inc,
+ dependencies: glib_deps,
+ link_whole: libmm_generated,
+)
+
+subdir('tests')
diff --git a/libmm-glib/generated/tests/Makefile.am b/libmm-glib/generated/tests/Makefile.am
index a4bed421..0cebb720 100644
--- a/libmm-glib/generated/tests/Makefile.am
+++ b/libmm-glib/generated/tests/Makefile.am
@@ -1,4 +1,5 @@
-
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
noinst_LTLIBRARIES = libmm-test-generated.la
diff --git a/libmm-glib/generated/tests/meson.build b/libmm-glib/generated/tests/meson.build
new file mode 100644
index 00000000..d63773ca
--- /dev/null
+++ b/libmm-glib/generated/tests/meson.build
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+# Test interface
+gdbus_sources = gnome.gdbus_codegen(
+ 'mm-gdbus-test',
+ sources: mm_ifaces_test,
+ interface_prefix: 'org.freedesktop.ModemManager1.',
+ namespace: 'MmGdbus',
+ autocleanup: 'objects',
+)
+
+libmm_test_generated = static_library(
+ 'mm-test-generated',
+ sources: gdbus_sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: common_c_args,
+)
+
+libmm_test_generated_dep = declare_dependency(
+ sources: gdbus_sources[1],
+ include_directories: '.',
+ dependencies: glib_deps,
+ link_with: libmm_test_generated,
+)
diff --git a/libmm-glib/libmm-glib.h b/libmm-glib/libmm-glib.h
index 30f72d32..e8b98ed8 100644
--- a/libmm-glib/libmm-glib.h
+++ b/libmm-glib/libmm-glib.h
@@ -34,17 +34,24 @@
/* This headers are not exported within ModemManager */
# include <mm-manager.h>
# include <mm-object.h>
+# include <mm-sim.h>
+# include <mm-sms.h>
+# include <mm-call.h>
+# include <mm-bearer.h>
# include <mm-modem.h>
# include <mm-modem-3gpp.h>
+# include <mm-modem-3gpp-profile-manager.h>
# include <mm-modem-3gpp-ussd.h>
# include <mm-modem-cdma.h>
# include <mm-modem-simple.h>
# include <mm-modem-location.h>
# include <mm-modem-messaging.h>
+# include <mm-modem-voice.h>
# include <mm-modem-time.h>
# include <mm-modem-firmware.h>
# include <mm-modem-signal.h>
# include <mm-modem-oma.h>
+# include <mm-modem-sar.h>
#endif
#if defined (_LIBMM_INSIDE_MM) || \
@@ -58,8 +65,10 @@
#include <mm-simple-status.h>
#include <mm-simple-connect-properties.h>
#include <mm-sms-properties.h>
+#include <mm-call-properties.h>
#include <mm-bearer-properties.h>
#include <mm-bearer-ip-config.h>
+#include <mm-bearer-stats.h>
#include <mm-location-common.h>
#include <mm-location-3gpp.h>
#include <mm-location-gps-raw.h>
@@ -68,8 +77,15 @@
#include <mm-unlock-retries.h>
#include <mm-network-timezone.h>
#include <mm-firmware-properties.h>
+#include <mm-firmware-update-settings.h>
#include <mm-cdma-manual-activation-properties.h>
#include <mm-signal.h>
+#include <mm-kernel-event-properties.h>
+#include <mm-pco.h>
+#include <mm-sim-preferred-network.h>
+#include <mm-3gpp-profile.h>
+#include <mm-signal-threshold-properties.h>
+#include <mm-compat.h>
/* generated */
#include <mm-errors-types.h>
@@ -79,5 +95,6 @@
#include <mm-gdbus-bearer.h>
#include <mm-gdbus-sim.h>
#include <mm-gdbus-sms.h>
+#include <mm-gdbus-call.h>
#endif /* _LIBMM_GLIB_H_ */
diff --git a/libmm-glib/meson.build b/libmm-glib/meson.build
new file mode 100644
index 00000000..e2641d2c
--- /dev/null
+++ b/libmm-glib/meson.build
@@ -0,0 +1,181 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+libmm_glib_inc = include_directories('.')
+
+subdir('generated')
+
+headers = files(
+ 'libmm-glib.h',
+ 'mm-3gpp-profile.h',
+ 'mm-bearer.h',
+ 'mm-bearer-ip-config.h',
+ 'mm-bearer-properties.h',
+ 'mm-bearer-stats.h',
+ 'mm-call-audio-format.h',
+ 'mm-call.h',
+ 'mm-call-properties.h',
+ 'mm-cdma-manual-activation-properties.h',
+ 'mm-compat.h',
+ 'mm-firmware-properties.h',
+ 'mm-firmware-update-settings.h',
+ 'mm-helper-types.h',
+ 'mm-kernel-event-properties.h',
+ 'mm-location-3gpp.h',
+ 'mm-location-cdma-bs.h',
+ 'mm-location-common.h',
+ 'mm-location-gps-nmea.h',
+ 'mm-location-gps-raw.h',
+ 'mm-manager.h',
+ 'mm-modem-3gpp.h',
+ 'mm-modem-3gpp-profile-manager.h',
+ 'mm-modem-3gpp-ussd.h',
+ 'mm-modem-cdma.h',
+ 'mm-modem-firmware.h',
+ 'mm-modem.h',
+ 'mm-modem-location.h',
+ 'mm-modem-messaging.h',
+ 'mm-modem-oma.h',
+ 'mm-modem-sar.h',
+ 'mm-modem-signal.h',
+ 'mm-modem-simple.h',
+ 'mm-modem-time.h',
+ 'mm-modem-voice.h',
+ 'mm-network-timezone.h',
+ 'mm-object.h',
+ 'mm-pco.h',
+ 'mm-signal.h',
+ 'mm-signal-threshold-properties.h',
+ 'mm-sim.h',
+ 'mm-simple-connect-properties.h',
+ 'mm-simple-status.h',
+ 'mm-sim-preferred-network.h',
+ 'mm-sms.h',
+ 'mm-sms-properties.h',
+ 'mm-unlock-retries.h',
+)
+
+install_headers(
+ headers,
+ install_dir: mm_glib_pkgincludedir,
+)
+
+sources = files(
+ 'mm-3gpp-profile.c',
+ 'mm-bearer.c',
+ 'mm-bearer-ip-config.c',
+ 'mm-bearer-properties.c',
+ 'mm-bearer-stats.c',
+ 'mm-call-audio-format.c',
+ 'mm-call.c',
+ 'mm-call-properties.c',
+ 'mm-cdma-manual-activation-properties.c',
+ 'mm-common-helpers.c',
+ 'mm-compat.c',
+ 'mm-firmware-properties.c',
+ 'mm-firmware-update-settings.c',
+ 'mm-helper-types.c',
+ 'mm-kernel-event-properties.c',
+ 'mm-location-3gpp.c',
+ 'mm-location-cdma-bs.c',
+ 'mm-location-gps-nmea.c',
+ 'mm-location-gps-raw.c',
+ 'mm-manager.c',
+ 'mm-modem-3gpp.c',
+ 'mm-modem-3gpp-profile-manager.c',
+ 'mm-modem-3gpp-ussd.c',
+ 'mm-modem.c',
+ 'mm-modem-cdma.c',
+ 'mm-modem-firmware.c',
+ 'mm-modem-location.c',
+ 'mm-modem-messaging.c',
+ 'mm-modem-oma.c',
+ 'mm-modem-sar.c',
+ 'mm-modem-signal.c',
+ 'mm-modem-simple.c',
+ 'mm-modem-time.c',
+ 'mm-modem-voice.c',
+ 'mm-network-timezone.c',
+ 'mm-object.c',
+ 'mm-pco.c',
+ 'mm-signal.c',
+ 'mm-signal-threshold-properties.c',
+ 'mm-sim.c',
+ 'mm-simple-connect-properties.c',
+ 'mm-simple-status.c',
+ 'mm-sim-preferred-network.c',
+ 'mm-sms.c',
+ 'mm-sms-properties.c',
+ 'mm-unlock-retries.c',
+)
+
+deps = [include_dep]
+
+libmm_glib = shared_library(
+ 'mm-glib',
+ version: mm_glib_version,
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps + [libmm_generated_dep],
+ c_args: '-DLIBMM_GLIB_COMPILATION',
+ install: true,
+)
+
+libmm_glib_dep = declare_dependency(
+ include_directories: libmm_glib_inc,
+ # FIXME: glib_deps is included because `dependencies` parameter is not part of partial_dependency
+ dependencies: deps + [glib_deps, libmm_generated_dep.partial_dependency(sources: true, includes: true)],
+ link_with: libmm_glib,
+)
+
+pkg.generate(
+ libraries: libmm_glib,
+ version: mm_version,
+ name: 'mm-glib',
+ description: 'Library to control and monitor the ModemManager',
+ subdirs: mm_glib_name,
+ # FIXME: produced by the inhability of meson to use internal dependencies
+ requires: ['gio-2.0', 'glib-2.0', 'gobject-2.0', 'ModemManager'],
+ variables: 'exec_prefix=${prefix}',
+)
+
+if enable_gir
+ incs = [
+ 'Gio-2.0',
+ 'GLib-2.0',
+ 'GObject-2.0',
+ ]
+
+ gir_ns = 'ModemManager'
+ gir_prefix = 'Mm'
+
+ args = [
+ '-DLIBMM_GLIB_COMPILATION',
+ '--identifier-prefix=' + gir_prefix.to_upper(),
+ ]
+
+ libmm_glib_gir = gnome.generate_gir(
+ libmm_glib,
+ sources: sources + headers + gen_sources + gen_headers + [mm_names_header, mm_version_header],
+ includes: incs,
+ namespace: gir_ns,
+ nsversion: mm_gir_version,
+ identifier_prefix: gir_prefix,
+ symbol_prefix: gir_prefix.to_lower(),
+ extra_args: args,
+ header: 'libmm-glib.h',
+ export_packages: gir_ns,
+ install: true,
+ )
+
+ if enable_vapi
+ libmm_glib_vapi = gnome.generate_vapi(
+ 'libmm-glib',
+ sources: libmm_glib_gir[0],
+ packages: 'gio-2.0',
+ install: true,
+ )
+ endif
+endif
+
+subdir('tests')
diff --git a/libmm-glib/mm-3gpp-profile.c b/libmm-glib/mm-3gpp-profile.c
new file mode 100644
index 00000000..82590e2a
--- /dev/null
+++ b/libmm-glib/mm-3gpp-profile.c
@@ -0,0 +1,753 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+
+#include "mm-errors-types.h"
+#include "mm-common-helpers.h"
+#include "mm-3gpp-profile.h"
+
+/**
+ * SECTION: mm-3gpp-profile
+ * @title: MM3gppProfile
+ * @short_description: Helper object to handle 3GPP profile settings.
+ *
+ * The #MM3gppProfile is an object handling the settings associated
+ * to a connection profile stored in the device.
+ */
+
+G_DEFINE_TYPE (MM3gppProfile, mm_3gpp_profile, G_TYPE_OBJECT)
+
+#define PROPERTY_ID "profile-id"
+#define PROPERTY_NAME "profile-name"
+#define PROPERTY_APN "apn"
+#define PROPERTY_ALLOWED_AUTH "allowed-auth"
+#define PROPERTY_USER "user"
+#define PROPERTY_PASSWORD "password"
+#define PROPERTY_IP_TYPE "ip-type"
+#define PROPERTY_APN_TYPE "apn-type"
+
+struct _MM3gppProfilePrivate {
+ gint profile_id;
+ gchar *profile_name;
+ gchar *apn;
+ MMBearerIpFamily ip_type;
+ MMBearerApnType apn_type;
+
+ /* Optional authentication settings */
+ MMBearerAllowedAuth allowed_auth;
+ gchar *user;
+ gchar *password;
+};
+
+/*****************************************************************************/
+
+static gboolean
+cmp_str (const gchar *a,
+ const gchar *b)
+{
+ /* Strict match */
+ if ((!a && !b) || (a && b && g_strcmp0 (a, b) == 0))
+ return TRUE;
+ /* Additional match, consider NULL and EMPTY string equal */
+ if ((!a && !b[0]) || (!b && !a[0]))
+ return TRUE;
+ return FALSE;
+}
+
+/**
+ * mm_3gpp_profile_cmp: (skip)
+ */
+gboolean
+mm_3gpp_profile_cmp (MM3gppProfile *a,
+ MM3gppProfile *b,
+ GEqualFunc cmp_apn,
+ MM3gppProfileCmpFlags flags)
+{
+ /* When an input cmp_apn() methods is provided to compare the APNs, we must
+ * run it twice, with the input arguments switched, as e.g. the mm_3gpp_cmp_apn_name()
+ * method that may be given here treats both input arguments differently. */
+ if (cmp_apn && !cmp_apn (a->priv->apn, b->priv->apn) && !cmp_apn (b->priv->apn, a->priv->apn))
+ return FALSE;
+ if (!cmp_apn && !cmp_str (a->priv->apn, b->priv->apn))
+ return FALSE;
+ if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_IP_TYPE) &&
+ (a->priv->ip_type != b->priv->ip_type))
+ return FALSE;
+ if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID) &&
+ (a->priv->profile_id != b->priv->profile_id))
+ return FALSE;
+ if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH) &&
+ ((a->priv->allowed_auth != b->priv->allowed_auth) ||
+ (!cmp_str (a->priv->user, b->priv->user)) ||
+ (!cmp_str (a->priv->password, b->priv->password))))
+ return FALSE;
+ if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE) &&
+ (a->priv->apn_type != b->priv->apn_type))
+ return FALSE;
+ if (!(flags & MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_NAME) &&
+ (a->priv->profile_name != b->priv->profile_name))
+ return FALSE;
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_profile_id:
+ * @self: a #MM3gppProfile.
+ * @profile_id: Numeric profile id to use, or #MM_3GPP_PROFILE_ID_UNKNOWN.
+ *
+ * Sets the profile id to use.
+ *
+ * If none specified explicitly, #MM_3GPP_PROFILE_ID_UNKNOWN is assumed.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_profile_id (MM3gppProfile *self,
+ gint profile_id)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ self->priv->profile_id = profile_id;
+}
+
+/**
+ * mm_3gpp_profile_get_profile_id:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the profile id.
+ *
+ * Returns: the profile id..
+ *
+ * Since: 1.18
+ */
+gint
+mm_3gpp_profile_get_profile_id (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ return self->priv->profile_id;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_apn:
+ * @self: a #MM3gppProfile.
+ * @apn: Name of the access point.
+ *
+ * Sets the name of the access point to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_apn (MM3gppProfile *self,
+ const gchar *apn)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ g_free (self->priv->apn);
+ self->priv->apn = g_strdup (apn);
+}
+
+/**
+ * mm_3gpp_profile_get_apn:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the name of the access point.
+ *
+ * Returns: (transfer none): the access point, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_3gpp_profile_get_apn (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL);
+
+ return self->priv->apn;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_profile_name:
+ * @self: a #MM3gppProfile.
+ * @profile_name: Name of the profile.
+ *
+ * Sets the name of the profile.
+ *
+ * Since: 1.20
+ */
+void
+mm_3gpp_profile_set_profile_name (MM3gppProfile *self,
+ const gchar *profile_name)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ g_free (self->priv->profile_name);
+ self->priv->profile_name = g_strdup (profile_name);
+}
+
+/**
+ * mm_3gpp_profile_get_profile_name:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the name of the profile.
+ *
+ * Returns: (transfer none): the profile name, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.20
+ */
+const gchar *
+mm_3gpp_profile_get_profile_name (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL);
+
+ return self->priv->profile_name;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_allowed_auth:
+ * @self: a #MM3gppProfile.
+ * @allowed_auth: a bitmask of #MMBearerAllowedAuth values.
+ * %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default
+ * method.
+ *
+ * Sets the method to use when authenticating with the access point.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_allowed_auth (MM3gppProfile *self,
+ MMBearerAllowedAuth allowed_auth)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ self->priv->allowed_auth = allowed_auth;
+}
+
+/**
+ * mm_3gpp_profile_get_allowed_auth:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the methods allowed to use when authenticating with the access point.
+ *
+ * Returns: a bitmask of #MMBearerAllowedAuth values, or
+ * %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method.
+ *
+ * Since: 1.18
+ */
+MMBearerAllowedAuth
+mm_3gpp_profile_get_allowed_auth (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_ALLOWED_AUTH_UNKNOWN);
+
+ return self->priv->allowed_auth;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_user:
+ * @self: a #MM3gppProfile.
+ * @user: the username
+ *
+ * Sets the username used to authenticate with the access point.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_user (MM3gppProfile *self,
+ const gchar *user)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ g_free (self->priv->user);
+ self->priv->user = g_strdup (user);
+}
+
+/**
+ * mm_3gpp_profile_get_user:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the username used to authenticate with the access point.
+ *
+ * Returns: (transfer none): the username, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_3gpp_profile_get_user (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL);
+
+ return self->priv->user;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_password:
+ * @self: a #MM3gppProfile.
+ * @password: the password
+ *
+ * Sets the password used to authenticate with the access point.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_password (MM3gppProfile *self,
+ const gchar *password)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ g_free (self->priv->password);
+ self->priv->password = g_strdup (password);
+}
+
+/**
+ * mm_3gpp_profile_get_password:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the password used to authenticate with the access point.
+ *
+ * Returns: (transfer none): the password, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_3gpp_profile_get_password (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL);
+
+ return self->priv->password;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_ip_type:
+ * @self: a #MM3gppProfile.
+ * @ip_type: a #MMBearerIpFamily.
+ *
+ * Sets the IP type to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_ip_type (MM3gppProfile *self,
+ MMBearerIpFamily ip_type)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ self->priv->ip_type = ip_type;
+}
+
+/**
+ * mm_3gpp_profile_get_ip_type:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the IP type to use.
+ *
+ * Returns: a #MMBearerIpFamily.
+ *
+ * Since: 1.18
+ */
+MMBearerIpFamily
+mm_3gpp_profile_get_ip_type (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_IP_FAMILY_NONE);
+
+ return self->priv->ip_type;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_set_apn_type:
+ * @self: a #MM3gppProfile.
+ * @apn_type: a mask of #MMBearerApnType values.
+ *
+ * Sets the APN types to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_3gpp_profile_set_apn_type (MM3gppProfile *self,
+ MMBearerApnType apn_type)
+{
+ g_return_if_fail (MM_IS_3GPP_PROFILE (self));
+
+ self->priv->apn_type = apn_type;
+}
+
+/**
+ * mm_3gpp_profile_get_apn_type:
+ * @self: a #MM3gppProfile.
+ *
+ * Gets the APN types to use.
+ *
+ * Returns: a mask of #MMBearerApnType values.
+ *
+ * Since: 1.18
+ */
+MMBearerApnType
+mm_3gpp_profile_get_apn_type (MM3gppProfile *self)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), MM_BEARER_APN_TYPE_NONE);
+
+ return self->priv->apn_type;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_get_dictionary: (skip)
+ */
+GVariant *
+mm_3gpp_profile_get_dictionary (MM3gppProfile *self)
+{
+ GVariantBuilder builder;
+
+ /* We do allow NULL */
+ if (!self)
+ return NULL;
+
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), NULL);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ID,
+ g_variant_new_int32 (self->priv->profile_id));
+
+ if (self->priv->profile_name)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_NAME,
+ g_variant_new_string (self->priv->profile_name));
+
+ if (self->priv->apn)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_APN,
+ g_variant_new_string (self->priv->apn));
+
+ if (self->priv->allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ALLOWED_AUTH,
+ g_variant_new_uint32 (self->priv->allowed_auth));
+
+ if (self->priv->user)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_USER,
+ g_variant_new_string (self->priv->user));
+
+ if (self->priv->password)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_PASSWORD,
+ g_variant_new_string (self->priv->password));
+
+ if (self->priv->ip_type != MM_BEARER_IP_FAMILY_NONE)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_IP_TYPE,
+ g_variant_new_uint32 (self->priv->ip_type));
+
+ if (self->priv->apn_type != MM_BEARER_APN_TYPE_NONE)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_APN_TYPE,
+ g_variant_new_uint32 (self->priv->apn_type));
+
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_3gpp_profile_consume_string (MM3gppProfile *self,
+ const gchar *key,
+ const gchar *value,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), FALSE);
+
+ if (g_str_equal (key, PROPERTY_ID)) {
+ gint profile_id;
+
+ if (!mm_get_int_from_str (value, &profile_id)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "invalid profile id value given: %s", value);
+ return FALSE;
+ }
+ mm_3gpp_profile_set_profile_id (self, profile_id);
+ } else if (g_str_equal (key, PROPERTY_NAME))
+ mm_3gpp_profile_set_profile_name (self, value);
+ else if (g_str_equal (key, PROPERTY_APN))
+ mm_3gpp_profile_set_apn (self, value);
+ else if (g_str_equal (key, PROPERTY_ALLOWED_AUTH)) {
+ GError *inner_error = NULL;
+ MMBearerAllowedAuth allowed_auth;
+
+ allowed_auth = mm_common_get_allowed_auth_from_string (value, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ mm_3gpp_profile_set_allowed_auth (self, allowed_auth);
+ } else if (g_str_equal (key, PROPERTY_USER))
+ mm_3gpp_profile_set_user (self, value);
+ else if (g_str_equal (key, PROPERTY_PASSWORD))
+ mm_3gpp_profile_set_password (self, value);
+ else if (g_str_equal (key, PROPERTY_IP_TYPE)) {
+ GError *inner_error = NULL;
+ MMBearerIpFamily ip_type;
+
+ ip_type = mm_common_get_ip_type_from_string (value, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ mm_3gpp_profile_set_ip_type (self, ip_type);
+ } else if (g_str_equal (key, PROPERTY_APN_TYPE)) {
+ GError *inner_error = NULL;
+ MMBearerApnType apn_type;
+
+ apn_type = mm_common_get_apn_type_from_string (value, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ mm_3gpp_profile_set_apn_type (self, apn_type);
+ } else {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Invalid properties string, unsupported key '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct {
+ MM3gppProfile *properties;
+ GError *error;
+} ParseKeyValueContext;
+
+static gboolean
+key_value_foreach (const gchar *key,
+ const gchar *value,
+ ParseKeyValueContext *ctx)
+{
+ return mm_3gpp_profile_consume_string (ctx->properties,
+ key,
+ value,
+ &ctx->error);
+}
+
+/**
+ * mm_3gpp_profile_new_from_string: (skip)
+ */
+MM3gppProfile *
+mm_3gpp_profile_new_from_string (const gchar *str,
+ GError **error)
+{
+ ParseKeyValueContext ctx;
+
+ ctx.error = NULL;
+ ctx.properties = mm_3gpp_profile_new ();
+
+ mm_common_parse_key_value_string (str,
+ &ctx.error,
+ (MMParseKeyValueForeachFn)key_value_foreach,
+ &ctx);
+ /* If error, destroy the object */
+ if (ctx.error) {
+ g_propagate_error (error, ctx.error);
+ g_object_unref (ctx.properties);
+ ctx.properties = NULL;
+ }
+
+ return ctx.properties;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_3gpp_profile_consume_variant (MM3gppProfile *self,
+ const gchar *key,
+ GVariant *value,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_3GPP_PROFILE (self), FALSE);
+
+ if (g_str_equal (key, PROPERTY_ID))
+ mm_3gpp_profile_set_profile_id (
+ self,
+ g_variant_get_int32 (value));
+ else if (g_str_equal (key, PROPERTY_NAME))
+ mm_3gpp_profile_set_profile_name (
+ self,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_APN))
+ mm_3gpp_profile_set_apn (
+ self,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_ALLOWED_AUTH))
+ mm_3gpp_profile_set_allowed_auth (
+ self,
+ g_variant_get_uint32 (value));
+ else if (g_str_equal (key, PROPERTY_USER))
+ mm_3gpp_profile_set_user (
+ self,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_PASSWORD))
+ mm_3gpp_profile_set_password (
+ self,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_IP_TYPE))
+ mm_3gpp_profile_set_ip_type (
+ self,
+ g_variant_get_uint32 (value));
+ else if (g_str_equal (key, PROPERTY_APN_TYPE))
+ mm_3gpp_profile_set_apn_type (
+ self,
+ g_variant_get_uint32 (value));
+ else {
+ /* Set error */
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid self dictionary, unexpected key '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * mm_3gpp_profile_new_from_dictionary: (skip)
+ */
+MM3gppProfile *
+mm_3gpp_profile_new_from_dictionary (GVariant *dictionary,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ MM3gppProfile *properties;
+
+ properties = mm_3gpp_profile_new ();
+ if (!dictionary)
+ return properties;
+
+ if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create Bearer properties from dictionary: "
+ "invalid variant type received");
+ g_object_unref (properties);
+ return NULL;
+ }
+
+ g_variant_iter_init (&iter, dictionary);
+ while (!inner_error &&
+ g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ mm_3gpp_profile_consume_variant (properties, key, value, &inner_error);
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ /* If error, destroy the object */
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (properties);
+ properties = NULL;
+ }
+
+ return properties;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_3gpp_profile_new:
+ *
+ * Creates a new empty #MM3gppProfile.
+ *
+ * Returns: (transfer full): a #MM3gppProfile. The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.18
+ */
+MM3gppProfile *
+mm_3gpp_profile_new (void)
+{
+ return MM_3GPP_PROFILE (g_object_new (MM_TYPE_3GPP_PROFILE, NULL));
+}
+
+static void
+mm_3gpp_profile_init (MM3gppProfile *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_3GPP_PROFILE, MM3gppProfilePrivate);
+
+ /* Some defaults */
+ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN;
+ self->priv->allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ self->priv->ip_type = MM_BEARER_IP_FAMILY_NONE;
+ self->priv->apn_type = MM_BEARER_APN_TYPE_NONE;
+}
+
+static void
+finalize (GObject *object)
+{
+ MM3gppProfile *self = MM_3GPP_PROFILE (object);
+
+ g_free (self->priv->profile_name);
+ g_free (self->priv->apn);
+ g_free (self->priv->user);
+ g_free (self->priv->password);
+
+ G_OBJECT_CLASS (mm_3gpp_profile_parent_class)->finalize (object);
+}
+
+static void
+mm_3gpp_profile_class_init (MM3gppProfileClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MM3gppProfilePrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-3gpp-profile.h b/libmm-glib/mm-3gpp-profile.h
new file mode 100644
index 00000000..c0d0206a
--- /dev/null
+++ b/libmm-glib/mm-3gpp-profile.h
@@ -0,0 +1,135 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_3GPP_PROFILE_H
+#define MM_3GPP_PROFILE_H
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+# error "Only <libmm-glib.h> can be included directly."
+#endif
+
+G_BEGIN_DECLS
+
+/**
+ * MM_3GPP_PROFILE_ID_UNKNOWN:
+ *
+ * This value may be specified in the 'profile-id' property When the user
+ * creates a new #MM3gppProfile, to indicate that the real profile id should
+ * be assigned by the device.
+ */
+#define MM_3GPP_PROFILE_ID_UNKNOWN -1
+
+#define MM_TYPE_3GPP_PROFILE (mm_3gpp_profile_get_type ())
+#define MM_3GPP_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_3GPP_PROFILE, MM3gppProfile))
+#define MM_3GPP_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_3GPP_PROFILE, MM3gppProfileClass))
+#define MM_IS_3GPP_PROFILE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_3GPP_PROFILE))
+#define MM_IS_3GPP_PROFILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_3GPP_PROFILE))
+#define MM_3GPP_PROFILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_3GPP_PROFILE, MM3gppProfileClass))
+
+typedef struct _MM3gppProfile MM3gppProfile;
+typedef struct _MM3gppProfileClass MM3gppProfileClass;
+typedef struct _MM3gppProfilePrivate MM3gppProfilePrivate;
+
+/**
+ * MM3gppProfile:
+ *
+ * The #MM3gppProfile structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _MM3gppProfile {
+ /*< private >*/
+ GObject parent;
+ MM3gppProfilePrivate *priv;
+};
+
+struct _MM3gppProfileClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_3gpp_profile_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MM3gppProfile, g_object_unref)
+
+MM3gppProfile *mm_3gpp_profile_new (void);
+
+void mm_3gpp_profile_set_profile_id (MM3gppProfile *self,
+ gint profile_id);
+void mm_3gpp_profile_set_profile_name (MM3gppProfile *self,
+ const gchar *profile_name);
+void mm_3gpp_profile_set_apn (MM3gppProfile *self,
+ const gchar *apn);
+void mm_3gpp_profile_set_allowed_auth (MM3gppProfile *self,
+ MMBearerAllowedAuth allowed_auth);
+void mm_3gpp_profile_set_user (MM3gppProfile *self,
+ const gchar *user);
+void mm_3gpp_profile_set_password (MM3gppProfile *self,
+ const gchar *password);
+void mm_3gpp_profile_set_ip_type (MM3gppProfile *self,
+ MMBearerIpFamily ip_type);
+void mm_3gpp_profile_set_apn_type (MM3gppProfile *self,
+ MMBearerApnType apn_type);
+
+gint mm_3gpp_profile_get_profile_id (MM3gppProfile *self);
+const gchar *mm_3gpp_profile_get_profile_name (MM3gppProfile *self);
+const gchar *mm_3gpp_profile_get_apn (MM3gppProfile *self);
+MMBearerAllowedAuth mm_3gpp_profile_get_allowed_auth (MM3gppProfile *self);
+const gchar *mm_3gpp_profile_get_user (MM3gppProfile *self);
+const gchar *mm_3gpp_profile_get_password (MM3gppProfile *self);
+MMBearerIpFamily mm_3gpp_profile_get_ip_type (MM3gppProfile *self);
+MMBearerApnType mm_3gpp_profile_get_apn_type (MM3gppProfile *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MM3gppProfile *mm_3gpp_profile_new_from_string (const gchar *str,
+ GError **error);
+MM3gppProfile *mm_3gpp_profile_new_from_dictionary (GVariant *dictionary,
+ GError **error);
+GVariant *mm_3gpp_profile_get_dictionary (MM3gppProfile *self);
+gboolean mm_3gpp_profile_consume_string (MM3gppProfile *self,
+ const gchar *key,
+ const gchar *value,
+ GError **error);
+gboolean mm_3gpp_profile_consume_variant (MM3gppProfile *self,
+ const gchar *key,
+ GVariant *value,
+ GError **error);
+
+typedef enum {
+ MM_3GPP_PROFILE_CMP_FLAGS_NONE = 0,
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID = 1 << 1,
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_NAME = 1 << 2,
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH = 1 << 3,
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE = 1 << 4,
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_IP_TYPE = 1 << 5,
+} MM3gppProfileCmpFlags;
+
+gboolean mm_3gpp_profile_cmp (MM3gppProfile *a,
+ MM3gppProfile *b,
+ GEqualFunc cmp_apn,
+ MM3gppProfileCmpFlags flags);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_3GPP_PROFILE_H */
diff --git a/libmm-glib/mm-bearer-ip-config.c b/libmm-glib/mm-bearer-ip-config.c
index 8be59ac6..99e4f3a3 100644
--- a/libmm-glib/mm-bearer-ip-config.c
+++ b/libmm-glib/mm-bearer-ip-config.c
@@ -60,6 +60,8 @@ struct _MMBearerIpConfigPrivate {
* Gets the IP method to be used with this bearer.
*
* Returns: a #MMBearerIpMethod.
+ *
+ * Since: 1.0
*/
MMBearerIpMethod
mm_bearer_ip_config_get_method (MMBearerIpConfig *self)
@@ -69,6 +71,9 @@ mm_bearer_ip_config_get_method (MMBearerIpConfig *self)
return self->priv->method;
}
+/**
+ * mm_bearer_ip_config_set_method: (skip)
+ */
void
mm_bearer_ip_config_set_method (MMBearerIpConfig *self,
MMBearerIpMethod method)
@@ -86,7 +91,10 @@ mm_bearer_ip_config_set_method (MMBearerIpConfig *self,
*
* Gets the IP address to be used with this bearer.
*
- * Returns: a string with the IP address, or #NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: a string with the IP address, or #NULL if unknown. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_ip_config_get_address (MMBearerIpConfig *self)
@@ -96,6 +104,9 @@ mm_bearer_ip_config_get_address (MMBearerIpConfig *self)
return self->priv->address;
}
+/**
+ * mm_bearer_ip_config_set_address: (skip)
+ */
void
mm_bearer_ip_config_set_address (MMBearerIpConfig *self,
const gchar *address)
@@ -115,6 +126,8 @@ mm_bearer_ip_config_set_address (MMBearerIpConfig *self,
* Gets the network prefix to be used with this bearer.
*
* Returns: the network prefix.
+ *
+ * Since: 1.0
*/
guint
mm_bearer_ip_config_get_prefix (MMBearerIpConfig *self)
@@ -124,6 +137,9 @@ mm_bearer_ip_config_get_prefix (MMBearerIpConfig *self)
return self->priv->prefix;
}
+/**
+ * mm_bearer_ip_config_set_prefix: (skip)
+ */
void
mm_bearer_ip_config_set_prefix (MMBearerIpConfig *self,
guint prefix)
@@ -141,7 +157,10 @@ mm_bearer_ip_config_set_prefix (MMBearerIpConfig *self,
*
* Gets the list of IP addresses of DNS servers to be used with this bearer.
*
- * Returns: (transfer none) (array zero-terminated=1): a NULL-terminated array of strings. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none) (array zero-terminated=1): a %NULL-terminated array
+ * of strings. Do not free the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar **
mm_bearer_ip_config_get_dns (MMBearerIpConfig *self)
@@ -151,6 +170,9 @@ mm_bearer_ip_config_get_dns (MMBearerIpConfig *self)
return (const gchar **)self->priv->dns;
}
+/**
+ * mm_bearer_ip_config_set_dns: (skip)
+ */
void
mm_bearer_ip_config_set_dns (MMBearerIpConfig *self,
const gchar **dns)
@@ -169,7 +191,10 @@ mm_bearer_ip_config_set_dns (MMBearerIpConfig *self,
*
* Gets the IP address of the gateway to be used with this bearer.
*
- * Returns: a string with the IP address, or #NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: a string with the IP address, or #NULL if unknown. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_ip_config_get_gateway (MMBearerIpConfig *self)
@@ -179,6 +204,9 @@ mm_bearer_ip_config_get_gateway (MMBearerIpConfig *self)
return self->priv->gateway;
}
+/**
+ * mm_bearer_ip_config_set_gateway: (skip)
+ */
void
mm_bearer_ip_config_set_gateway (MMBearerIpConfig *self,
const gchar *gateway)
@@ -198,6 +226,8 @@ mm_bearer_ip_config_set_gateway (MMBearerIpConfig *self,
* Gets the MTU to be used with this bearer.
*
* Returns: the MTU.
+ *
+ * Since: 1.4
*/
guint
mm_bearer_ip_config_get_mtu (MMBearerIpConfig *self)
@@ -207,6 +237,9 @@ mm_bearer_ip_config_get_mtu (MMBearerIpConfig *self)
return self->priv->mtu;
}
+/**
+ * mm_bearer_ip_config_set_mtu: (skip)
+ */
void
mm_bearer_ip_config_set_mtu (MMBearerIpConfig *self,
guint mtu)
@@ -218,6 +251,9 @@ mm_bearer_ip_config_set_mtu (MMBearerIpConfig *self,
/*****************************************************************************/
+/**
+ * mm_bearer_ip_config_get_dictionary: (skip)
+ */
GVariant *
mm_bearer_ip_config_get_dictionary (MMBearerIpConfig *self)
{
@@ -285,6 +321,9 @@ mm_bearer_ip_config_get_dictionary (MMBearerIpConfig *self)
/*****************************************************************************/
+/**
+ * mm_bearer_ip_config_new_from_dictionary: (skip)
+ */
MMBearerIpConfig *
mm_bearer_ip_config_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -368,33 +407,9 @@ mm_bearer_ip_config_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
/**
- * mm_bearer_ip_config_dup:
- * @orig: a #MMBearerIpConfig
- *
- * Creates a copy of @orig.
- *
- * Returns: (transfer full): a newly created #MMBearerIpConfig
+ * mm_bearer_ip_config_new: (skip)
*/
MMBearerIpConfig *
-mm_bearer_ip_config_dup (MMBearerIpConfig *orig)
-{
- GVariant *dict;
- MMBearerIpConfig *copy;
- GError *error = NULL;
-
- g_return_val_if_fail (MM_IS_BEARER_IP_CONFIG (orig), NULL);
-
- dict = mm_bearer_ip_config_get_dictionary (orig);
- copy = mm_bearer_ip_config_new_from_dictionary (dict, &error);
- g_assert_no_error (error);
- g_variant_unref (dict);
-
- return copy;
-}
-
-/*****************************************************************************/
-
-MMBearerIpConfig *
mm_bearer_ip_config_new (void)
{
return (MM_BEARER_IP_CONFIG (
diff --git a/libmm-glib/mm-bearer-ip-config.h b/libmm-glib/mm-bearer-ip-config.h
index 898a405d..4b88790c 100644
--- a/libmm-glib/mm-bearer-ip-config.h
+++ b/libmm-glib/mm-bearer-ip-config.h
@@ -54,6 +54,7 @@ struct _MMBearerIpConfigClass {
};
GType mm_bearer_ip_config_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerIpConfig, g_object_unref)
MMBearerIpMethod mm_bearer_ip_config_get_method (MMBearerIpConfig *self);
const gchar *mm_bearer_ip_config_get_address (MMBearerIpConfig *self);
@@ -73,8 +74,6 @@ MMBearerIpConfig *mm_bearer_ip_config_new (void);
MMBearerIpConfig *mm_bearer_ip_config_new_from_dictionary (GVariant *dictionary,
GError **error);
-MMBearerIpConfig *mm_bearer_ip_config_dup (MMBearerIpConfig *orig);
-
void mm_bearer_ip_config_set_method (MMBearerIpConfig *self,
MMBearerIpMethod ip_method);
void mm_bearer_ip_config_set_address (MMBearerIpConfig *self,
diff --git a/libmm-glib/mm-bearer-properties.c b/libmm-glib/mm-bearer-properties.c
index c864f8e8..4e3d7730 100644
--- a/libmm-glib/mm-bearer-properties.c
+++ b/libmm-glib/mm-bearer-properties.c
@@ -31,54 +31,84 @@
* mm_modem_create_bearer() or mm_modem_create_bearer_sync().
*/
-G_DEFINE_TYPE (MMBearerProperties, mm_bearer_properties, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMBearerProperties, mm_bearer_properties, G_TYPE_OBJECT)
-#define PROPERTY_APN "apn"
-#define PROPERTY_ALLOWED_AUTH "allowed-auth"
-#define PROPERTY_USER "user"
-#define PROPERTY_PASSWORD "password"
-#define PROPERTY_IP_TYPE "ip-type"
-#define PROPERTY_NUMBER "number"
-#define PROPERTY_ALLOW_ROAMING "allow-roaming"
-#define PROPERTY_RM_PROTOCOL "rm-protocol"
+#define PROPERTY_ALLOW_ROAMING "allow-roaming"
+#define PROPERTY_RM_PROTOCOL "rm-protocol"
+#define PROPERTY_MULTIPLEX "multiplex"
+
+/* no longer used properties */
+#define DEPRECATED_PROPERTY_NUMBER "number"
struct _MMBearerPropertiesPrivate {
- /* APN */
- gchar *apn;
- /* IP type */
- MMBearerIpFamily ip_type;
- /* Allowed auth */
- MMBearerAllowedAuth allowed_auth;
- /* Number */
- gchar *number;
- /* User */
- gchar *user;
- /* Password */
- gchar *password;
+ /* The 3GPP profile is a subset of the bearer properties */
+ MM3gppProfile *profile;
+
/* Roaming allowance */
gboolean allow_roaming_set;
gboolean allow_roaming;
/* Protocol of the Rm interface */
MMModemCdmaRmProtocol rm_protocol;
+ /* Multiplex support */
+ MMBearerMultiplexSupport multiplex;
};
/*****************************************************************************/
/**
+ * mm_bearer_properties_set_profile_name:
+ * @self: a #MMBearerProperties.
+ * @profile_name: Name of the profile.
+ *
+ * Sets the name of the profile to use when connecting.
+ *
+ * Since: 1.20
+ */
+void
+mm_bearer_properties_set_profile_name (MMBearerProperties *self,
+ const gchar *profile_name)
+{
+ g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
+
+ mm_3gpp_profile_set_profile_name (self->priv->profile, profile_name);
+}
+
+/**
+ * mm_bearer_properties_get_profile_name:
+ * @self: a #MMBearerProperties.
+ *
+ * Gets the name of the profile to use when connecting.
+ *
+ * Returns: (transfer none): the profile name, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.20
+ */
+const gchar *
+mm_bearer_properties_get_profile_name (MMBearerProperties *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
+
+ return mm_3gpp_profile_get_profile_name (self->priv->profile);
+}
+/*****************************************************************************/
+
+/**
* mm_bearer_properties_set_apn:
* @self: a #MMBearerProperties.
* @apn: Name of the access point.
*
* Sets the name of the access point to use when connecting.
+ *
+ * Since: 1.0
*/
void
mm_bearer_properties_set_apn (MMBearerProperties *self,
- const gchar *apn)
+ const gchar *apn)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- g_free (self->priv->apn);
- self->priv->apn = g_strdup (apn);
+ mm_3gpp_profile_set_apn (self->priv->profile, apn);
}
/**
@@ -87,14 +117,17 @@ mm_bearer_properties_set_apn (MMBearerProperties *self,
*
* Gets the name of the access point to use when connecting.
*
- * Returns: (transfer none): the access point, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the access point, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_properties_get_apn (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
- return self->priv->apn;
+ return mm_3gpp_profile_get_apn (self->priv->profile);
}
/*****************************************************************************/
@@ -102,17 +135,21 @@ mm_bearer_properties_get_apn (MMBearerProperties *self)
/**
* mm_bearer_properties_set_allowed_auth:
* @self: a #MMBearerProperties.
- * @allowed_auth: a bitmask of #MMBearerAllowedAuth values. %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default method.
+ * @allowed_auth: a bitmask of #MMBearerAllowedAuth values.
+ * %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default
+ * method.
*
* Sets the authentication method to use.
+ *
+ * Since: 1.0
*/
void
-mm_bearer_properties_set_allowed_auth (MMBearerProperties *self,
- MMBearerAllowedAuth allowed_auth)
+mm_bearer_properties_set_allowed_auth (MMBearerProperties *self,
+ MMBearerAllowedAuth allowed_auth)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- self->priv->allowed_auth = allowed_auth;
+ mm_3gpp_profile_set_allowed_auth (self->priv->profile, allowed_auth);
}
/**
@@ -121,14 +158,17 @@ mm_bearer_properties_set_allowed_auth (MMBearerProperties *self,
*
* Gets the authentication methods allowed in the connection.
*
- * Returns: a bitmask of #MMBearerAllowedAuth values, or %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method.
+ * Returns: a bitmask of #MMBearerAllowedAuth values, or
+ * %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method.
+ *
+ * Since: 1.0
*/
MMBearerAllowedAuth
mm_bearer_properties_get_allowed_auth (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_ALLOWED_AUTH_UNKNOWN);
- return self->priv->allowed_auth;
+ return mm_3gpp_profile_get_allowed_auth (self->priv->profile);
}
/*****************************************************************************/
@@ -139,15 +179,16 @@ mm_bearer_properties_get_allowed_auth (MMBearerProperties *self)
* @user: the username
*
* Sets the username used to authenticate with the access point.
+ *
+ * Since: 1.0
*/
void
mm_bearer_properties_set_user (MMBearerProperties *self,
- const gchar *user)
+ const gchar *user)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- g_free (self->priv->user);
- self->priv->user = g_strdup (user);
+ mm_3gpp_profile_set_user (self->priv->profile, user);
}
/**
@@ -156,14 +197,17 @@ mm_bearer_properties_set_user (MMBearerProperties *self,
*
* Gets the username used to authenticate with the access point.
*
- * Returns: (transfer none): the username, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the username, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_properties_get_user (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
- return self->priv->user;
+ return mm_3gpp_profile_get_user (self->priv->profile);
}
/*****************************************************************************/
@@ -174,15 +218,16 @@ mm_bearer_properties_get_user (MMBearerProperties *self)
* @password: the password
*
* Sets the password used to authenticate with the access point.
+ *
+ * Since: 1.0
*/
void
mm_bearer_properties_set_password (MMBearerProperties *self,
- const gchar *password)
+ const gchar *password)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- g_free (self->priv->password);
- self->priv->password = g_strdup (password);
+ mm_3gpp_profile_set_password (self->priv->profile, password);
}
/**
@@ -191,14 +236,17 @@ mm_bearer_properties_set_password (MMBearerProperties *self,
*
* Gets the password used to authenticate with the access point.
*
- * Returns: (transfer none): the password, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the password, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_properties_get_password (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
- return self->priv->password;
+ return mm_3gpp_profile_get_password (self->priv->profile);
}
/*****************************************************************************/
@@ -209,14 +257,16 @@ mm_bearer_properties_get_password (MMBearerProperties *self)
* @ip_type: a #MMBearerIpFamily.
*
* Sets the IP type to use.
+ *
+ * Since: 1.0
*/
void
mm_bearer_properties_set_ip_type (MMBearerProperties *self,
- MMBearerIpFamily ip_type)
+ MMBearerIpFamily ip_type)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- self->priv->ip_type = ip_type;
+ mm_3gpp_profile_set_ip_type (self->priv->profile, ip_type);
}
/**
@@ -226,13 +276,91 @@ mm_bearer_properties_set_ip_type (MMBearerProperties *self,
* Sets the IP type to use.
*
* Returns: a #MMBearerIpFamily.
+ *
+ * Since: 1.0
*/
MMBearerIpFamily
mm_bearer_properties_get_ip_type (MMBearerProperties *self)
{
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_IP_FAMILY_NONE);
- return self->priv->ip_type;
+ return mm_3gpp_profile_get_ip_type (self->priv->profile);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_properties_set_apn_type:
+ * @self: a #MMBearerProperties.
+ * @apn_type: a mask of #MMBearerApnType values.
+ *
+ * Sets the APN types to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_bearer_properties_set_apn_type (MMBearerProperties *self,
+ MMBearerApnType apn_type)
+{
+ g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
+
+ mm_3gpp_profile_set_apn_type (self->priv->profile, apn_type);
+}
+
+/**
+ * mm_bearer_properties_get_apn_type:
+ * @self: a #MMBearerProperties.
+ *
+ * Gets the APN types to use.
+ *
+ * Returns: a mask of #MMBearerApnType values.
+ *
+ * Since: 1.18
+ */
+MMBearerApnType
+mm_bearer_properties_get_apn_type (MMBearerProperties *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_APN_TYPE_NONE);
+
+ return mm_3gpp_profile_get_apn_type (self->priv->profile);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_properties_set_profile_id:
+ * @self: a #MMBearerProperties.
+ * @profile_id: a profile id.
+ *
+ * Sets the profile ID to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_bearer_properties_set_profile_id (MMBearerProperties *self,
+ gint profile_id)
+{
+ g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
+
+ mm_3gpp_profile_set_profile_id (self->priv->profile, profile_id);
+}
+
+/**
+ * mm_bearer_properties_get_profile_id:
+ * @self: a #MMBearerProperties.
+ *
+ * Gets the profile ID to use.
+ *
+ * Returns: the profile id.
+ *
+ * Since: 1.18
+ */
+gint
+mm_bearer_properties_get_profile_id (MMBearerProperties *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ return mm_3gpp_profile_get_profile_id (self->priv->profile);
}
/*****************************************************************************/
@@ -244,6 +372,8 @@ mm_bearer_properties_get_ip_type (MMBearerProperties *self)
*
* Sets the flag to indicate whether roaming is allowed or not in the
* connection.
+ *
+ * Since: 1.0
*/
void
mm_bearer_properties_set_allow_roaming (MMBearerProperties *self,
@@ -261,7 +391,9 @@ mm_bearer_properties_set_allow_roaming (MMBearerProperties *self,
*
* Checks whether roaming is allowed in the connection.
*
- * Returns: %TRUE if roaming is allowed, %FALSE otherwise..
+ * Returns: %TRUE if roaming is allowed, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_bearer_properties_get_allow_roaming (MMBearerProperties *self)
@@ -274,78 +406,103 @@ mm_bearer_properties_get_allow_roaming (MMBearerProperties *self)
/*****************************************************************************/
/**
- * mm_bearer_properties_set_number:
+ * mm_bearer_properties_set_rm_protocol:
* @self: a #MMBearerProperties.
- * @number: the number.
+ * @protocol: a #MMModemCdmaRmProtocol.
*
- * Sets the number to use when performing the connection.
+ * Sets the RM protocol to use in the CDMA connection.
+ *
+ * Since: 1.0
*/
void
-mm_bearer_properties_set_number (MMBearerProperties *self,
- const gchar *number)
+mm_bearer_properties_set_rm_protocol (MMBearerProperties *self,
+ MMModemCdmaRmProtocol protocol)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- g_free (self->priv->number);
- self->priv->number = g_strdup (number);
+ self->priv->rm_protocol = protocol;
}
/**
- * mm_bearer_properties_get_number:
+ * mm_bearer_properties_get_rm_protocol:
* @self: a #MMBearerProperties.
*
- * Gets the number to use when performing the connection.
+ * Gets the RM protocol requested to use in the CDMA connection.
+ *
+ * Returns: a #MMModemCdmaRmProtocol.
*
- * Returns: (transfer none): the number, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Since: 1.0
*/
-const gchar *
-mm_bearer_properties_get_number (MMBearerProperties *self)
+MMModemCdmaRmProtocol
+mm_bearer_properties_get_rm_protocol (MMBearerProperties *self)
{
- g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), NULL);
+ g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN);
- return self->priv->number;
+ return self->priv->rm_protocol;
}
/*****************************************************************************/
/**
- * mm_simple_connect_properties_set_rm_protocol:
+ * mm_bearer_properties_set_multiplex:
* @self: a #MMBearerProperties.
- * @protocol: a #MMModemCdmaRmProtocol.
+ * @multiplex: a #MMBearerMultiplexSupport.
*
- * Sets the RM protocol to use in the CDMA connection.
+ * Gets the type of multiplex support requested by the user.
+ *
+ * Since: 1.18
*/
void
-mm_bearer_properties_set_rm_protocol (MMBearerProperties *self,
- MMModemCdmaRmProtocol protocol)
+mm_bearer_properties_set_multiplex (MMBearerProperties *self,
+ MMBearerMultiplexSupport multiplex)
{
g_return_if_fail (MM_IS_BEARER_PROPERTIES (self));
- self->priv->rm_protocol = protocol;
+ self->priv->multiplex = multiplex;
}
/**
- * mm_bearer_properties_get_rm_protocol:
+ * mm_bearer_properties_get_multiplex:
* @self: a #MMBearerProperties.
*
- * Gets the RM protocol requested to use in the CDMA connection.
+ * Gets the type of multiplex support requested by the user.
*
- * Returns: a #MMModemCdmaRmProtocol.
+ * Returns: a #MMBearerMultiplexSupport.
+ *
+ * Since: 1.18
*/
-MMModemCdmaRmProtocol
-mm_bearer_properties_get_rm_protocol (MMBearerProperties *self)
+MMBearerMultiplexSupport
+mm_bearer_properties_get_multiplex (MMBearerProperties *self)
{
- g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN);
+ g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN);
- return self->priv->rm_protocol;
+ return self->priv->multiplex;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_properties_peek_3gpp_profile: (skip)
+ */
+MM3gppProfile *
+mm_bearer_properties_peek_3gpp_profile (MMBearerProperties *self)
+{
+ return self->priv->profile;
}
/*****************************************************************************/
+/**
+ * mm_bearer_properties_get_dictionary: (skip)
+ */
GVariant *
mm_bearer_properties_get_dictionary (MMBearerProperties *self)
{
- GVariantBuilder builder;
+ GVariantBuilder builder;
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ GVariant *profile_dictionary;
/* We do allow NULL */
if (!self)
@@ -355,42 +512,6 @@ mm_bearer_properties_get_dictionary (MMBearerProperties *self)
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
- if (self->priv->apn)
- g_variant_builder_add (&builder,
- "{sv}",
- PROPERTY_APN,
- g_variant_new_string (self->priv->apn));
-
- if (self->priv->allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN)
- g_variant_builder_add (&builder,
- "{sv}",
- PROPERTY_ALLOWED_AUTH,
- g_variant_new_uint32 (self->priv->allowed_auth));
-
- if (self->priv->user)
- g_variant_builder_add (&builder,
- "{sv}",
- PROPERTY_USER,
- g_variant_new_string (self->priv->user));
-
- if (self->priv->password)
- g_variant_builder_add (&builder,
- "{sv}",
- PROPERTY_PASSWORD,
- g_variant_new_string (self->priv->password));
-
- if (self->priv->ip_type != MM_BEARER_IP_FAMILY_NONE)
- g_variant_builder_add (&builder,
- "{sv}",
- PROPERTY_IP_TYPE,
- g_variant_new_uint32 (self->priv->ip_type));
-
- if (self->priv->number)
- g_variant_builder_add (&builder,
- "{sv}",
- PROPERTY_NUMBER,
- g_variant_new_string (self->priv->number));
-
if (self->priv->allow_roaming_set)
g_variant_builder_add (&builder,
"{sv}",
@@ -403,83 +524,81 @@ mm_bearer_properties_get_dictionary (MMBearerProperties *self)
PROPERTY_RM_PROTOCOL,
g_variant_new_uint32 (self->priv->rm_protocol));
+ if (self->priv->multiplex)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_MULTIPLEX,
+ g_variant_new_uint32 (self->priv->multiplex));
+
+ /* Merge dictionaries */
+ profile_dictionary = mm_3gpp_profile_get_dictionary (self->priv->profile);
+ g_variant_iter_init (&iter, profile_dictionary);
+ while (g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ g_variant_builder_add (&builder, "{sv}", key, value);
+ g_variant_unref (value);
+ g_free (key);
+ }
+ g_variant_unref (profile_dictionary);
+
return g_variant_ref_sink (g_variant_builder_end (&builder));
}
/*****************************************************************************/
-
/**
- * mm_bearer_properties_consume_string:
- * @self: a #MMBearerProperties
- * @key:
- * @value:
- * @error: (allow-none): Return location for error or %NULL.
- *
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * mm_bearer_properties_consume_string: (skip)
*/
gboolean
-mm_bearer_properties_consume_string (MMBearerProperties *self,
- const gchar *key,
- const gchar *value,
- GError **error)
+mm_bearer_properties_consume_string (MMBearerProperties *self,
+ const gchar *key,
+ const gchar *value,
+ GError **error)
{
+ GError *inner_error = NULL;
+
g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE);
- if (g_str_equal (key, PROPERTY_APN))
- mm_bearer_properties_set_apn (self, value);
- else if (g_str_equal (key, PROPERTY_ALLOWED_AUTH)) {
- GError *inner_error = NULL;
- MMBearerAllowedAuth allowed_auth;
+ /* First, check if we can consume this as bearer properties */
+ if (mm_3gpp_profile_consume_string (self->priv->profile, key, value, &inner_error))
+ return TRUE;
- allowed_auth = mm_common_get_allowed_auth_from_string (value, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- mm_bearer_properties_set_allowed_auth (self, allowed_auth);
- } else if (g_str_equal (key, PROPERTY_USER))
- mm_bearer_properties_set_user (self, value);
- else if (g_str_equal (key, PROPERTY_PASSWORD))
- mm_bearer_properties_set_password (self, value);
- else if (g_str_equal (key, PROPERTY_IP_TYPE)) {
- GError *inner_error = NULL;
- MMBearerIpFamily ip_type;
-
- ip_type = mm_common_get_ip_type_from_string (value, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- mm_bearer_properties_set_ip_type (self, ip_type);
- } else if (g_str_equal (key, PROPERTY_ALLOW_ROAMING)) {
- GError *inner_error = NULL;
+ /* Unknown keys are reported as unsupported. Any other error is right away
+ * fatal (e.g. an invalid value given to a known profile property) */
+ if (!g_error_matches (inner_error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ /* On unsupported errors, try with the bearer specific properties */
+ g_clear_error (&inner_error);
+
+ if (g_str_equal (key, PROPERTY_ALLOW_ROAMING)) {
gboolean allow_roaming;
allow_roaming = mm_common_get_boolean_from_string (value, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- mm_bearer_properties_set_allow_roaming (self, allow_roaming);
- } else if (g_str_equal (key, PROPERTY_NUMBER))
- mm_bearer_properties_set_number (self, value);
- else if (g_str_equal (key, PROPERTY_RM_PROTOCOL)) {
- GError *inner_error = NULL;
+ if (!inner_error)
+ mm_bearer_properties_set_allow_roaming (self, allow_roaming);
+ } else if (g_str_equal (key, PROPERTY_RM_PROTOCOL)) {
MMModemCdmaRmProtocol protocol;
protocol = mm_common_get_rm_protocol_from_string (value, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
- mm_bearer_properties_set_rm_protocol (self, protocol);
+ if (!inner_error)
+ mm_bearer_properties_set_rm_protocol (self, protocol);
+ } else if (g_str_equal (key, PROPERTY_MULTIPLEX)) {
+ MMBearerMultiplexSupport multiplex;
+
+ multiplex = mm_common_get_multiplex_support_from_string (value, &inner_error);
+ if (!inner_error)
+ mm_bearer_properties_set_multiplex (self, multiplex);
+ } else if (g_str_equal (key, DEPRECATED_PROPERTY_NUMBER)) {
+ /* NO-OP */
} else {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Invalid properties string, unexpected key '%s'",
- key);
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Invalid properties string, unsupported key '%s'", key);
+ }
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
}
@@ -488,12 +607,12 @@ mm_bearer_properties_consume_string (MMBearerProperties *self,
typedef struct {
MMBearerProperties *properties;
- GError *error;
+ GError *error;
} ParseKeyValueContext;
static gboolean
-key_value_foreach (const gchar *key,
- const gchar *value,
+key_value_foreach (const gchar *key,
+ const gchar *value,
ParseKeyValueContext *ctx)
{
return mm_bearer_properties_consume_string (ctx->properties,
@@ -502,6 +621,9 @@ key_value_foreach (const gchar *key,
&ctx->error);
}
+/**
+ * mm_bearer_properties_new_from_string: (skip)
+ */
MMBearerProperties *
mm_bearer_properties_new_from_string (const gchar *str,
GError **error)
@@ -528,63 +650,41 @@ mm_bearer_properties_new_from_string (const gchar *str,
/*****************************************************************************/
/**
- * mm_bearer_properties_consume_variant:
- * @properties: a #MMBearerProperties
- * @key:
- * @value: a #GVariant
- * @error: (allow-none): Return location for error or %NULL.
- *
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * mm_bearer_properties_consume_variant: (skip)
*/
gboolean
-mm_bearer_properties_consume_variant (MMBearerProperties *properties,
- const gchar *key,
- GVariant *value,
- GError **error)
+mm_bearer_properties_consume_variant (MMBearerProperties *self,
+ const gchar *key,
+ GVariant *value,
+ GError **error)
{
- g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (properties), FALSE);
-
- if (g_str_equal (key, PROPERTY_APN))
- mm_bearer_properties_set_apn (
- properties,
- g_variant_get_string (value, NULL));
- else if (g_str_equal (key, PROPERTY_ALLOWED_AUTH))
- mm_bearer_properties_set_allowed_auth (
- properties,
- g_variant_get_uint32 (value));
- else if (g_str_equal (key, PROPERTY_USER))
- mm_bearer_properties_set_user (
- properties,
- g_variant_get_string (value, NULL));
- else if (g_str_equal (key, PROPERTY_PASSWORD))
- mm_bearer_properties_set_password (
- properties,
- g_variant_get_string (value, NULL));
- else if (g_str_equal (key, PROPERTY_IP_TYPE))
- mm_bearer_properties_set_ip_type (
- properties,
- g_variant_get_uint32 (value));
- else if (g_str_equal (key, PROPERTY_NUMBER))
- mm_bearer_properties_set_number (
- properties,
- g_variant_get_string (value, NULL));
- else if (g_str_equal (key, PROPERTY_ALLOW_ROAMING))
- mm_bearer_properties_set_allow_roaming (
- properties,
- g_variant_get_boolean (value));
- else {
+ g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (self), FALSE);
+
+ /* First, check if we can consume this as profile properties */
+ if (mm_3gpp_profile_consume_variant (self->priv->profile, key, value, NULL))
+ return TRUE;
+
+ if (g_str_equal (key, PROPERTY_ALLOW_ROAMING))
+ mm_bearer_properties_set_allow_roaming (self, g_variant_get_boolean (value));
+ else if (g_str_equal (key, PROPERTY_RM_PROTOCOL))
+ mm_bearer_properties_set_rm_protocol (self, g_variant_get_uint32 (value));
+ else if (g_str_equal (key, PROPERTY_MULTIPLEX))
+ mm_bearer_properties_set_multiplex (self, g_variant_get_uint32 (value));
+ else if (g_str_equal (key, DEPRECATED_PROPERTY_NUMBER)) {
+ /* NO-OP */
+ } else {
/* Set error */
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Invalid properties dictionary, unexpected key '%s'",
- key);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid properties dictionary, unexpected key '%s'", key);
return FALSE;
}
return TRUE;
}
+/**
+ * mm_bearer_properties_new_from_dictionary: (skip)
+ */
MMBearerProperties *
mm_bearer_properties_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -612,10 +712,7 @@ mm_bearer_properties_new_from_dictionary (GVariant *dictionary,
g_variant_iter_init (&iter, dictionary);
while (!inner_error &&
g_variant_iter_next (&iter, "{sv}", &key, &value)) {
- mm_bearer_properties_consume_variant (properties,
- key,
- value,
- &inner_error);
+ mm_bearer_properties_consume_variant (properties, key, value, &inner_error);
g_free (key);
g_variant_unref (value);
}
@@ -632,46 +729,133 @@ mm_bearer_properties_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
-/**
- * mm_bearer_properties_dup:
- * @orig: a #MMBearerProperties
- *
- * Creates a copy of @orig.
- *
- * Returns: (transfer full): a newly created #MMBearerProperties
- */
-MMBearerProperties *
-mm_bearer_properties_dup (MMBearerProperties *orig)
+static gboolean
+cmp_str (const gchar *a,
+ const gchar *b,
+ MMBearerPropertiesCmpFlags flags)
{
- GVariant *dict;
- MMBearerProperties *copy;
- GError *error = NULL;
+ /* Strict match */
+ if ((!a && !b) || (a && b && g_strcmp0 (a, b) == 0))
+ return TRUE;
+ /* Additional loose match, consider NULL and EMPTY string equal */
+ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) {
+ if ((!a && !b[0]) || (!b && !a[0]))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+cmp_ip_type (MMBearerIpFamily a,
+ MMBearerIpFamily b,
+ MMBearerPropertiesCmpFlags flags)
+{
+ /* Strict match */
+ if (a == b)
+ return TRUE;
+ /* Additional loose match NONE == IPV4 */
+ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) {
+ if ((a == MM_BEARER_IP_FAMILY_NONE && b == MM_BEARER_IP_FAMILY_IPV4) ||
+ (b == MM_BEARER_IP_FAMILY_NONE && a == MM_BEARER_IP_FAMILY_IPV4))
+ return TRUE;
+ }
+ return FALSE;
+}
- g_return_val_if_fail (MM_IS_BEARER_PROPERTIES (orig), NULL);
+static gboolean
+cmp_apn_type (MMBearerApnType a,
+ MMBearerApnType b,
+ MMBearerPropertiesCmpFlags flags)
+{
+ /* Strict match */
+ if (a == b)
+ return TRUE;
+ /* Additional loose match NONE == DEFAULT */
+ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) {
+ if ((a == MM_BEARER_APN_TYPE_NONE && b == MM_BEARER_APN_TYPE_DEFAULT) ||
+ (b == MM_BEARER_APN_TYPE_NONE && a == MM_BEARER_APN_TYPE_DEFAULT))
+ return TRUE;
+ }
+ return FALSE;
+}
- dict = mm_bearer_properties_get_dictionary (orig);
- copy = mm_bearer_properties_new_from_dictionary (dict, &error);
- g_assert_no_error (error);
- g_variant_unref (dict);
+static gboolean
+cmp_allowed_auth (MMBearerAllowedAuth a,
+ MMBearerAllowedAuth b,
+ MMBearerPropertiesCmpFlags flags)
+{
+ /* Strict match */
+ if (a == b)
+ return TRUE;
+ /* Additional loose match UNKNOWN == NONE */
+ if (flags & MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE) {
+ if ((a == MM_BEARER_ALLOWED_AUTH_UNKNOWN && b == MM_BEARER_ALLOWED_AUTH_NONE) ||
+ (b == MM_BEARER_ALLOWED_AUTH_UNKNOWN && a == MM_BEARER_ALLOWED_AUTH_NONE))
+ return TRUE;
+ }
+ return FALSE;
+}
- return copy;
+/**
+ * mm_bearer_properties_cmp: (skip)
+ */
+gboolean
+mm_bearer_properties_cmp (MMBearerProperties *a,
+ MMBearerProperties *b,
+ MMBearerPropertiesCmpFlags flags)
+{
+ /* we don't have any other need to compare profiles, so just compare the properties here */
+ if (!cmp_str (mm_3gpp_profile_get_apn (a->priv->profile), mm_3gpp_profile_get_apn (b->priv->profile), flags))
+ return FALSE;
+ if (!cmp_ip_type (mm_3gpp_profile_get_ip_type (a->priv->profile), mm_3gpp_profile_get_ip_type (b->priv->profile), flags))
+ return FALSE;
+ if (!cmp_allowed_auth (mm_3gpp_profile_get_allowed_auth (a->priv->profile), mm_3gpp_profile_get_allowed_auth (b->priv->profile), flags))
+ return FALSE;
+ if (!cmp_str (mm_3gpp_profile_get_user (a->priv->profile), mm_3gpp_profile_get_user (b->priv->profile), flags))
+ return FALSE;
+ if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD) &&
+ !cmp_str (mm_3gpp_profile_get_password (a->priv->profile), mm_3gpp_profile_get_password (b->priv->profile), flags))
+ return FALSE;
+ if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE) &&
+ !cmp_apn_type (mm_3gpp_profile_get_apn_type (a->priv->profile), mm_3gpp_profile_get_apn_type (b->priv->profile), flags))
+ return FALSE;
+ if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID) &&
+ (mm_3gpp_profile_get_profile_id (a->priv->profile) != mm_3gpp_profile_get_profile_id (b->priv->profile)))
+ return FALSE;
+ if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_NAME) &&
+ !cmp_str (mm_3gpp_profile_get_profile_name (a->priv->profile), mm_3gpp_profile_get_profile_name (b->priv->profile), flags))
+ return FALSE;
+ if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING)) {
+ if (a->priv->allow_roaming != b->priv->allow_roaming)
+ return FALSE;
+ if (a->priv->allow_roaming_set != b->priv->allow_roaming_set)
+ return FALSE;
+ }
+ if (!(flags & MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL)) {
+ if (a->priv->rm_protocol != b->priv->rm_protocol)
+ return FALSE;
+ }
+ if (a->priv->multiplex != b->priv->multiplex)
+ return FALSE;
+ return TRUE;
}
/*****************************************************************************/
-gboolean
-mm_bearer_properties_cmp (MMBearerProperties *a,
- MMBearerProperties *b)
+/**
+ * mm_bearer_properties_new_from_profile: (skip)
+ */
+MMBearerProperties *
+mm_bearer_properties_new_from_profile (MM3gppProfile *profile,
+ GError **error)
{
- return ((!g_strcmp0 (a->priv->apn, b->priv->apn)) &&
- (a->priv->ip_type == b->priv->ip_type) &&
- (!g_strcmp0 (a->priv->number, b->priv->number)) &&
- (a->priv->allowed_auth == b->priv->allowed_auth) &&
- (!g_strcmp0 (a->priv->user, b->priv->user)) &&
- (!g_strcmp0 (a->priv->password, b->priv->password)) &&
- (a->priv->allow_roaming == b->priv->allow_roaming) &&
- (a->priv->allow_roaming_set == b->priv->allow_roaming_set) &&
- (a->priv->rm_protocol == b->priv->rm_protocol));
+ MMBearerProperties *self;
+
+ self = mm_bearer_properties_new ();
+ g_clear_object (&self->priv->profile);
+ self->priv->profile = g_object_ref (profile);
+
+ return self;
}
/*****************************************************************************/
@@ -682,6 +866,8 @@ mm_bearer_properties_cmp (MMBearerProperties *a,
* Creates a new empty #MMBearerProperties.
*
* Returns: (transfer full): a #MMBearerProperties. The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMBearerProperties *
mm_bearer_properties_new (void)
@@ -698,10 +884,10 @@ mm_bearer_properties_init (MMBearerProperties *self)
MMBearerPropertiesPrivate);
/* Some defaults */
+ self->priv->profile = mm_3gpp_profile_new ();
self->priv->allow_roaming = TRUE;
self->priv->rm_protocol = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
- self->priv->allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
- self->priv->ip_type = MM_BEARER_IP_FAMILY_NONE;
+ self->priv->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN;
}
static void
@@ -709,10 +895,7 @@ finalize (GObject *object)
{
MMBearerProperties *self = MM_BEARER_PROPERTIES (object);
- g_free (self->priv->apn);
- g_free (self->priv->user);
- g_free (self->priv->password);
- g_free (self->priv->number);
+ g_object_unref (self->priv->profile);
G_OBJECT_CLASS (mm_bearer_properties_parent_class)->finalize (object);
}
diff --git a/libmm-glib/mm-bearer-properties.h b/libmm-glib/mm-bearer-properties.h
index 361c8678..14b351a6 100644
--- a/libmm-glib/mm-bearer-properties.h
+++ b/libmm-glib/mm-bearer-properties.h
@@ -18,6 +18,7 @@
#include <ModemManager.h>
#include <glib-object.h>
+#include <mm-3gpp-profile.h>
#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
#error "Only <libmm-glib.h> can be included directly."
@@ -54,6 +55,7 @@ struct _MMBearerPropertiesClass {
};
GType mm_bearer_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerProperties, g_object_unref)
MMBearerProperties *mm_bearer_properties_new (void);
@@ -67,21 +69,30 @@ void mm_bearer_properties_set_password (MMBearerProperties *self,
const gchar *password);
void mm_bearer_properties_set_ip_type (MMBearerProperties *self,
MMBearerIpFamily ip_type);
+void mm_bearer_properties_set_apn_type (MMBearerProperties *self,
+ MMBearerApnType apn_type);
+void mm_bearer_properties_set_profile_id (MMBearerProperties *self,
+ gint profile_id);
+void mm_bearer_properties_set_profile_name (MMBearerProperties *self,
+ const gchar *profile_name);
void mm_bearer_properties_set_allow_roaming (MMBearerProperties *self,
gboolean allow_roaming);
-void mm_bearer_properties_set_number (MMBearerProperties *self,
- const gchar *number);
void mm_bearer_properties_set_rm_protocol (MMBearerProperties *self,
MMModemCdmaRmProtocol protocol);
-
-const gchar *mm_bearer_properties_get_apn (MMBearerProperties *self);
-MMBearerAllowedAuth mm_bearer_properties_get_allowed_auth (MMBearerProperties *self);
-const gchar *mm_bearer_properties_get_user (MMBearerProperties *self);
-const gchar *mm_bearer_properties_get_password (MMBearerProperties *self);
-MMBearerIpFamily mm_bearer_properties_get_ip_type (MMBearerProperties *self);
-gboolean mm_bearer_properties_get_allow_roaming (MMBearerProperties *self);
-const gchar *mm_bearer_properties_get_number (MMBearerProperties *self);
-MMModemCdmaRmProtocol mm_bearer_properties_get_rm_protocol (MMBearerProperties *self);
+void mm_bearer_properties_set_multiplex (MMBearerProperties *self,
+ MMBearerMultiplexSupport multiplex);
+
+const gchar *mm_bearer_properties_get_apn (MMBearerProperties *self);
+MMBearerAllowedAuth mm_bearer_properties_get_allowed_auth (MMBearerProperties *self);
+const gchar *mm_bearer_properties_get_user (MMBearerProperties *self);
+const gchar *mm_bearer_properties_get_password (MMBearerProperties *self);
+MMBearerIpFamily mm_bearer_properties_get_ip_type (MMBearerProperties *self);
+MMBearerApnType mm_bearer_properties_get_apn_type (MMBearerProperties *self);
+gint mm_bearer_properties_get_profile_id (MMBearerProperties *self);
+const gchar *mm_bearer_properties_get_profile_name (MMBearerProperties *self);
+gboolean mm_bearer_properties_get_allow_roaming (MMBearerProperties *self);
+MMModemCdmaRmProtocol mm_bearer_properties_get_rm_protocol (MMBearerProperties *self);
+MMBearerMultiplexSupport mm_bearer_properties_get_multiplex (MMBearerProperties *self);
/*****************************************************************************/
/* ModemManager/libmm-glib/mmcli specific methods */
@@ -94,8 +105,8 @@ MMBearerProperties *mm_bearer_properties_new_from_string (const gchar *str,
GError **error);
MMBearerProperties *mm_bearer_properties_new_from_dictionary (GVariant *dictionary,
GError **error);
-
-MMBearerProperties *mm_bearer_properties_dup (MMBearerProperties *orig);
+MMBearerProperties *mm_bearer_properties_new_from_profile (MM3gppProfile *profile,
+ GError **error);
gboolean mm_bearer_properties_consume_string (MMBearerProperties *self,
const gchar *key,
@@ -107,10 +118,23 @@ gboolean mm_bearer_properties_consume_variant (MMBearerProperties *properties,
GVariant *value,
GError **error);
-GVariant *mm_bearer_properties_get_dictionary (MMBearerProperties *self);
-
-gboolean mm_bearer_properties_cmp (MMBearerProperties *a,
- MMBearerProperties *b);
+GVariant *mm_bearer_properties_get_dictionary (MMBearerProperties *self);
+MM3gppProfile *mm_bearer_properties_peek_3gpp_profile (MMBearerProperties *self);
+
+typedef enum {
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NONE = 0,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE = 1 << 0,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD = 1 << 1,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING = 1 << 2,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL = 1 << 3,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE = 1 << 4,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID = 1 << 5,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_NAME = 1 << 6,
+} MMBearerPropertiesCmpFlags;
+
+gboolean mm_bearer_properties_cmp (MMBearerProperties *a,
+ MMBearerProperties *b,
+ MMBearerPropertiesCmpFlags flags);
#endif
diff --git a/libmm-glib/mm-bearer-stats.c b/libmm-glib/mm-bearer-stats.c
new file mode 100644
index 00000000..61b24de3
--- /dev/null
+++ b/libmm-glib/mm-bearer-stats.c
@@ -0,0 +1,586 @@
+/* -*- 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) 2015-2021 Azimut Electronics
+ * Copyright (C) 2015-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#include <string.h>
+
+#include "mm-errors-types.h"
+#include "mm-bearer-stats.h"
+
+/**
+ * SECTION: mm-bearer-stats
+ * @title: MMBearerStats
+ * @short_description: Helper object to handle bearer stats.
+ *
+ * The #MMBearerStats is an object handling the statistics reported by the
+ * bearer object during a connection.
+ *
+ * This object is retrieved with either mm_bearer_get_stats() or
+ * mm_bearer_peek_stats().
+ */
+
+G_DEFINE_TYPE (MMBearerStats, mm_bearer_stats, G_TYPE_OBJECT)
+
+#define PROPERTY_DURATION "duration"
+#define PROPERTY_RX_BYTES "rx-bytes"
+#define PROPERTY_TX_BYTES "tx-bytes"
+#define PROPERTY_START_DATE "start-date"
+#define PROPERTY_ATTEMPTS "attempts"
+#define PROPERTY_FAILED_ATTEMPTS "failed-attempts"
+#define PROPERTY_TOTAL_DURATION "total-duration"
+#define PROPERTY_TOTAL_RX_BYTES "total-rx-bytes"
+#define PROPERTY_TOTAL_TX_BYTES "total-tx-bytes"
+#define PROPERTY_UPLINK_SPEED "uplink-speed"
+#define PROPERTY_DOWNLINK_SPEED "downlink-speed"
+
+struct _MMBearerStatsPrivate {
+ guint duration;
+ guint64 rx_bytes;
+ guint64 tx_bytes;
+ guint64 start_date;
+ guint attempts;
+ guint failed_attempts;
+ guint total_duration;
+ guint64 total_rx_bytes;
+ guint64 total_tx_bytes;
+ guint64 uplink_speed;
+ guint64 downlink_speed;
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_duration:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the duration of the current connection, in seconds.
+ *
+ * Returns: a #guint.
+ *
+ * Since: 1.6
+ */
+guint
+mm_bearer_stats_get_duration (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->duration;
+}
+
+/**
+ * mm_bearer_stats_set_duration: (skip)
+ */
+void
+mm_bearer_stats_set_duration (MMBearerStats *self,
+ guint duration)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->duration = duration;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_rx_bytes:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the number of bytes received without error in the connection.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.6
+ */
+guint64
+mm_bearer_stats_get_rx_bytes (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->rx_bytes;
+}
+
+/**
+ * mm_bearer_stats_set_rx_bytes: (skip)
+ */
+void
+mm_bearer_stats_set_rx_bytes (MMBearerStats *self,
+ guint64 bytes)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->rx_bytes = bytes;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_tx_bytes:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the number of bytes transmitted without error in the connection.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.6
+ */
+guint64
+mm_bearer_stats_get_tx_bytes (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->tx_bytes;
+}
+
+/**
+ * mm_bearer_stats_set_tx_bytes: (skip)
+ */
+void
+mm_bearer_stats_set_tx_bytes (MMBearerStats *self,
+ guint64 bytes)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->tx_bytes = bytes;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_start_date:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the start date of the current connection as a timestamp in seconds
+ * since the epoch.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.20
+ */
+guint64
+mm_bearer_stats_get_start_date (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->start_date;
+}
+
+/**
+ * mm_bearer_stats_set_start_date: (skip)
+ */
+void
+mm_bearer_stats_set_start_date (MMBearerStats *self,
+ guint64 start_date)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->start_date = start_date;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_attempts:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the number of connection attempts done with this bearer.
+ *
+ * Returns: a #guint.
+ *
+ * Since: 1.14
+ */
+guint
+mm_bearer_stats_get_attempts (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->attempts;
+}
+
+/**
+ * mm_bearer_stats_set_attempts: (skip)
+ */
+void
+mm_bearer_stats_set_attempts (MMBearerStats *self,
+ guint attempts)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->attempts = attempts;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_failed_attempts:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the number of failed connection attempts done with this bearer.
+ *
+ * Returns: a #guint.
+ *
+ * Since: 1.14
+ */
+guint
+mm_bearer_stats_get_failed_attempts (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->failed_attempts;
+}
+
+/**
+ * mm_bearer_stats_set_failed_attempts: (skip)
+ */
+void
+mm_bearer_stats_set_failed_attempts (MMBearerStats *self,
+ guint failed_attempts)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->failed_attempts = failed_attempts;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_total_duration:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the total duration of all the connections of this bearer.
+ *
+ * Returns: a #guint.
+ *
+ * Since: 1.14
+ */
+guint
+mm_bearer_stats_get_total_duration (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->total_duration;
+}
+
+/**
+ * mm_bearer_stats_set_total_duration: (skip)
+ */
+void
+mm_bearer_stats_set_total_duration (MMBearerStats *self,
+ guint total_duration)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->total_duration = total_duration;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_total_rx_bytes:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the total number of bytes received without error during all the
+ * connections of this bearer.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.14
+ */
+guint64
+mm_bearer_stats_get_total_rx_bytes (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->total_rx_bytes;
+}
+
+/**
+ * mm_bearer_stats_set_total_rx_bytes: (skip)
+ */
+void
+mm_bearer_stats_set_total_rx_bytes (MMBearerStats *self,
+ guint64 total_bytes)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->total_rx_bytes = total_bytes;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_total_tx_bytes:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the total number of bytes transmitted without error during all the
+ * connections of this bearer.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.14
+ */
+guint64
+mm_bearer_stats_get_total_tx_bytes (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->total_tx_bytes;
+}
+
+/**
+ * mm_bearer_stats_set_total_tx_bytes: (skip)
+ */
+void
+mm_bearer_stats_set_total_tx_bytes (MMBearerStats *self,
+ guint64 total_bytes)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->total_tx_bytes = total_bytes;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_uplink_speed:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the speed of the uplink, in bits per second.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.20
+ */
+guint64
+mm_bearer_stats_get_uplink_speed (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->uplink_speed;
+}
+
+/**
+ * mm_bearer_stats_set_uplink_speed: (skip)
+ */
+void
+mm_bearer_stats_set_uplink_speed (MMBearerStats *self,
+ guint64 speed)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->uplink_speed = speed;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_downlink_speed:
+ * @self: a #MMBearerStats.
+ *
+ * Gets the speed of the downlink, in bits per second.
+ *
+ * Returns: a #guint64.
+ *
+ * Since: 1.20
+ */
+guint64
+mm_bearer_stats_get_downlink_speed (MMBearerStats *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER_STATS (self), 0);
+
+ return self->priv->downlink_speed;
+}
+
+/**
+ * mm_bearer_stats_set_downlink_speed: (skip)
+ */
+void
+mm_bearer_stats_set_downlink_speed (MMBearerStats *self,
+ guint64 speed)
+{
+ g_return_if_fail (MM_IS_BEARER_STATS (self));
+
+ self->priv->downlink_speed = speed;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_get_dictionary: (skip)
+ */
+GVariant *
+mm_bearer_stats_get_dictionary (MMBearerStats *self)
+{
+ GVariantBuilder builder;
+
+ /* We do allow self==NULL. We'll just report NULL. */
+ if (!self)
+ return NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_DURATION,
+ g_variant_new_uint32 (self->priv->duration));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_RX_BYTES,
+ g_variant_new_uint64 (self->priv->rx_bytes));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_TX_BYTES,
+ g_variant_new_uint64 (self->priv->tx_bytes));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_START_DATE,
+ g_variant_new_uint64 (self->priv->start_date));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ATTEMPTS,
+ g_variant_new_uint32 (self->priv->attempts));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_FAILED_ATTEMPTS,
+ g_variant_new_uint32 (self->priv->failed_attempts));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_TOTAL_DURATION,
+ g_variant_new_uint32 (self->priv->total_duration));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_TOTAL_RX_BYTES,
+ g_variant_new_uint64 (self->priv->total_rx_bytes));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_TOTAL_TX_BYTES,
+ g_variant_new_uint64 (self->priv->total_tx_bytes));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_UPLINK_SPEED,
+ g_variant_new_uint64 (self->priv->uplink_speed));
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_DOWNLINK_SPEED,
+ g_variant_new_uint64 (self->priv->downlink_speed));
+ return g_variant_builder_end (&builder);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_new_from_dictionary: (skip)
+ */
+MMBearerStats *
+mm_bearer_stats_new_from_dictionary (GVariant *dictionary,
+ GError **error)
+{
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ MMBearerStats *self;
+
+ self = mm_bearer_stats_new ();
+ if (!dictionary)
+ return self;
+
+ if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create Stats from dictionary: "
+ "invalid variant type received");
+ g_object_unref (self);
+ return NULL;
+ }
+
+ g_variant_iter_init (&iter, dictionary);
+ while (g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ if (g_str_equal (key, PROPERTY_DURATION)) {
+ mm_bearer_stats_set_duration (
+ self,
+ g_variant_get_uint32 (value));
+ } else if (g_str_equal (key, PROPERTY_RX_BYTES)) {
+ mm_bearer_stats_set_rx_bytes (
+ self,
+ g_variant_get_uint64 (value));
+ } else if (g_str_equal (key, PROPERTY_TX_BYTES)) {
+ mm_bearer_stats_set_tx_bytes (
+ self,
+ g_variant_get_uint64 (value));
+ } else if (g_str_equal (key, PROPERTY_START_DATE)) {
+ mm_bearer_stats_set_start_date (
+ self,
+ g_variant_get_uint64 (value));
+ } else if (g_str_equal (key, PROPERTY_ATTEMPTS)) {
+ mm_bearer_stats_set_attempts (
+ self,
+ g_variant_get_uint32 (value));
+ } else if (g_str_equal (key, PROPERTY_FAILED_ATTEMPTS)) {
+ mm_bearer_stats_set_failed_attempts (
+ self,
+ g_variant_get_uint32 (value));
+ } else if (g_str_equal (key, PROPERTY_TOTAL_DURATION)) {
+ mm_bearer_stats_set_total_duration (
+ self,
+ g_variant_get_uint32 (value));
+ } else if (g_str_equal (key, PROPERTY_TOTAL_RX_BYTES)) {
+ mm_bearer_stats_set_total_rx_bytes (
+ self,
+ g_variant_get_uint64 (value));
+ } else if (g_str_equal (key, PROPERTY_TOTAL_TX_BYTES)) {
+ mm_bearer_stats_set_total_tx_bytes (
+ self,
+ g_variant_get_uint64 (value));
+ } else if (g_str_equal (key, PROPERTY_UPLINK_SPEED)) {
+ mm_bearer_stats_set_uplink_speed (
+ self,
+ g_variant_get_uint64 (value));
+ } else if (g_str_equal (key, PROPERTY_DOWNLINK_SPEED)) {
+ mm_bearer_stats_set_downlink_speed (
+ self,
+ g_variant_get_uint64 (value));
+ }
+
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ return self;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_stats_new: (skip)
+ */
+MMBearerStats *
+mm_bearer_stats_new (void)
+{
+ return (MM_BEARER_STATS (g_object_new (MM_TYPE_BEARER_STATS, NULL)));
+}
+
+static void
+mm_bearer_stats_init (MMBearerStats *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_STATS, MMBearerStatsPrivate);
+}
+
+static void
+mm_bearer_stats_class_init (MMBearerStatsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBearerStatsPrivate));
+}
diff --git a/libmm-glib/mm-bearer-stats.h b/libmm-glib/mm-bearer-stats.h
new file mode 100644
index 00000000..960dc359
--- /dev/null
+++ b/libmm-glib/mm-bearer-stats.h
@@ -0,0 +1,102 @@
+/* -*- 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) 2015-2021 Azimut Electronics
+ * Copyright (C) 2015-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#ifndef MM_BEARER_STATS_H
+#define MM_BEARER_STATS_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_BEARER_STATS (mm_bearer_stats_get_type ())
+#define MM_BEARER_STATS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BEARER_STATS, MMBearerStats))
+#define MM_BEARER_STATS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BEARER_STATS, MMBearerStatsClass))
+#define MM_IS_BEARER_STATS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BEARER_STATS))
+#define MM_IS_BEARER_STATS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BEARER_STATS))
+#define MM_BEARER_STATS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BEARER_STATS, MMBearerStatsClass))
+
+typedef struct _MMBearerStats MMBearerStats;
+typedef struct _MMBearerStatsClass MMBearerStatsClass;
+typedef struct _MMBearerStatsPrivate MMBearerStatsPrivate;
+
+/**
+ * MMBearerStats:
+ *
+ * The #MMBearerStats structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _MMBearerStats {
+ /*< private >*/
+ GObject parent;
+ MMBearerStatsPrivate *priv;
+};
+
+struct _MMBearerStatsClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_bearer_stats_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerStats, g_object_unref)
+
+guint mm_bearer_stats_get_duration (MMBearerStats *self);
+guint64 mm_bearer_stats_get_rx_bytes (MMBearerStats *self);
+guint64 mm_bearer_stats_get_tx_bytes (MMBearerStats *self);
+guint64 mm_bearer_stats_get_start_date (MMBearerStats *self);
+guint mm_bearer_stats_get_attempts (MMBearerStats *self);
+guint mm_bearer_stats_get_failed_attempts (MMBearerStats *self);
+guint mm_bearer_stats_get_total_duration (MMBearerStats *self);
+guint64 mm_bearer_stats_get_total_rx_bytes (MMBearerStats *self);
+guint64 mm_bearer_stats_get_total_tx_bytes (MMBearerStats *self);
+guint64 mm_bearer_stats_get_uplink_speed (MMBearerStats *self);
+guint64 mm_bearer_stats_get_downlink_speed (MMBearerStats *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMBearerStats *mm_bearer_stats_new (void);
+MMBearerStats *mm_bearer_stats_new_from_dictionary (GVariant *dictionary,
+ GError **error);
+
+void mm_bearer_stats_set_duration (MMBearerStats *self, guint duration);
+void mm_bearer_stats_set_rx_bytes (MMBearerStats *self, guint64 rx_bytes);
+void mm_bearer_stats_set_tx_bytes (MMBearerStats *self, guint64 tx_bytes);
+void mm_bearer_stats_set_start_date (MMBearerStats *self, guint64 stats);
+void mm_bearer_stats_set_attempts (MMBearerStats *self, guint attempts);
+void mm_bearer_stats_set_failed_attempts (MMBearerStats *self, guint failed_attempts);
+void mm_bearer_stats_set_total_duration (MMBearerStats *self, guint duration);
+void mm_bearer_stats_set_total_rx_bytes (MMBearerStats *self, guint64 rx_bytes);
+void mm_bearer_stats_set_total_tx_bytes (MMBearerStats *self, guint64 tx_bytes);
+void mm_bearer_stats_set_uplink_speed (MMBearerStats *self, guint64 speed);
+void mm_bearer_stats_set_downlink_speed (MMBearerStats *self, guint64 speed);
+
+GVariant *mm_bearer_stats_get_dictionary (MMBearerStats *self);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_BEARER_STATS_H */
diff --git a/libmm-glib/mm-bearer.c b/libmm-glib/mm-bearer.c
index 4694cbeb..63c61ac0 100644
--- a/libmm-glib/mm-bearer.c
+++ b/libmm-glib/mm-bearer.c
@@ -22,6 +22,7 @@
*/
#include "mm-helpers.h"
+#include "mm-common-helpers.h"
#include "mm-bearer.h"
/**
@@ -39,20 +40,15 @@
G_DEFINE_TYPE (MMBearer, mm_bearer, MM_GDBUS_TYPE_BEARER_PROXY)
struct _MMBearerPrivate {
- /* IPv4 config */
- GMutex ipv4_config_mutex;
- guint ipv4_config_id;
- MMBearerIpConfig *ipv4_config;
-
- /* IPv6 config */
- GMutex ipv6_config_mutex;
- guint ipv6_config_id;
- MMBearerIpConfig *ipv6_config;
-
- /* Properties */
- GMutex properties_mutex;
- guint properties_id;
- MMBearerProperties *properties;
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_OBJECT_DECLARE (ipv4_config, MMBearerIpConfig)
+ PROPERTY_OBJECT_DECLARE (ipv6_config, MMBearerIpConfig)
+ PROPERTY_OBJECT_DECLARE (properties, MMBearerProperties)
+ PROPERTY_OBJECT_DECLARE (stats, MMBearerStats)
+
+ PROPERTY_ERROR_DECLARE (connection_error)
};
/*****************************************************************************/
@@ -64,6 +60,8 @@ struct _MMBearerPrivate {
* Gets the DBus path of the #MMBearer object.
*
* Returns: (transfer none): The DBus path of the #MMBearer object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_get_path (MMBearer *self)
@@ -80,7 +78,10 @@ mm_bearer_get_path (MMBearer *self)
*
* Gets a copy of the DBus path of the #MMBearer object.
*
- * Returns: (transfer full): The DBus path of the #MMBearer object. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMBearer object. The returned
+ * value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_bearer_dup_path (MMBearer *self)
@@ -111,7 +112,10 @@ mm_bearer_dup_path (MMBearer *self)
* @self was constructed. Use mm_bearer_dup_interface() if on another
* thread.</warning>
*
- * Returns: (transfer none): The name of the interface, or %NULL if it couldn't be retrieved.
+ * Returns: (transfer none): The name of the interface, or %NULL if it couldn't
+ * be retrieved.
+ *
+ * Since: 1.0
*/
const gchar *
mm_bearer_get_interface (MMBearer *self)
@@ -126,11 +130,14 @@ mm_bearer_get_interface (MMBearer *self)
* mm_bearer_dup_interface:
* @self: A #MMBearer.
*
- * Gets a copy of the operating system name for the network data interface that provides
- * packet data using this #MMBearer. This will only be available once the #MMBearer
- * is in connected state.
+ * Gets a copy of the operating system name for the network data interface that
+ * provides packet data using this #MMBearer. This will only be available once
+ * the #MMBearer is in connected state.
+ *
+ * Returns: (transfer full): The name of the interface, or %NULL if it couldn't
+ * be retrieved. The returned value should be freed with g_free().
*
- * Returns: (transfer full): The name of the interface, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_bearer_dup_interface (MMBearer *self)
@@ -151,6 +158,8 @@ mm_bearer_dup_interface (MMBearer *self)
* communication is possible.
*
* Returns: %TRUE if the #MMBearer is connected, #FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_bearer_get_connected (MMBearer *self)
@@ -166,10 +175,13 @@ mm_bearer_get_connected (MMBearer *self)
* mm_bearer_get_suspended:
* @self: A #MMBearer.
*
- * Checks whether or not the #MMBearer is suspended (but not deactivated) while the
- * device is handling other communications, like a voice call.
+ * Checks whether or not the #MMBearer is suspended (but not deactivated) while
+ * the device is handling other communications, like a voice call.
+ *
+ * Returns: %TRUE if packet data service is suspended in the #MMBearer, #FALSE
+ * otherwise.
*
- * Returns: %TRUE if packet data service is suspended in the #MMBearer, #FALSE otherwise.
+ * Since: 1.0
*/
gboolean
mm_bearer_get_suspended (MMBearer *self)
@@ -182,12 +194,36 @@ mm_bearer_get_suspended (MMBearer *self)
/*****************************************************************************/
/**
+ * mm_bearer_get_multiplexed:
+ * @self: A #MMBearer.
+ *
+ * Checks whether or not the #MMBearer is connected through a multiplexed
+ * network likn.
+ *
+ * Returns: %TRUE if packet data service is connected via a multiplexed network
+ * link in the #MMBearer, #FALSE otherwise.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_bearer_get_multiplexed (MMBearer *self)
+{
+ g_return_val_if_fail (MM_IS_BEARER (self), FALSE);
+
+ return mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (self));
+}
+
+/*****************************************************************************/
+
+/**
* mm_bearer_get_ip_timeout:
* @self: A #MMBearer.
*
* Gets the maximum time to wait for the bearer to retrieve a valid IP address.
*
* Returns: The IP timeout, or 0 if no specific one given.
+ *
+ * Since: 1.0
*/
guint
mm_bearer_get_ip_timeout (MMBearer *self)
@@ -199,67 +235,47 @@ mm_bearer_get_ip_timeout (MMBearer *self)
/*****************************************************************************/
-static void
-ipv4_config_updated (MMBearer *self,
- GParamSpec *pspec)
+/**
+ * mm_bearer_get_bearer_type:
+ * @self: A #MMBearer.
+ *
+ * Gets the type of bearer.
+ *
+ * Returns: a #MMBearerType.
+ *
+ * Since: 1.0
+ */
+MMBearerType
+mm_bearer_get_bearer_type (MMBearer *self)
{
- g_mutex_lock (&self->priv->ipv4_config_mutex);
- {
- GVariant *dictionary;
-
- g_clear_object (&self->priv->ipv4_config);
-
- /* TODO: update existing object instead of re-creating? */
- dictionary = mm_gdbus_bearer_get_ip4_config (MM_GDBUS_BEARER (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->ipv4_config = mm_bearer_ip_config_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid IPv4 configuration update received: %s", error->message);
- g_error_free (error);
- }
- }
- }
- g_mutex_unlock (&self->priv->ipv4_config_mutex);
+ g_return_val_if_fail (MM_IS_BEARER (self), MM_BEARER_TYPE_UNKNOWN);
+
+ return mm_gdbus_bearer_get_bearer_type (MM_GDBUS_BEARER (self));
}
-static void
-ensure_internal_ipv4_config (MMBearer *self,
- MMBearerIpConfig **dup)
+/*****************************************************************************/
+
+/**
+ * mm_bearer_get_profile_id:
+ * @self: A #MMBearer.
+ *
+ * Gets profile ID associated to the bearer connection, if known.
+ *
+ * If the bearer is disconnected or the modem doesn't support profile management
+ * features, %MM_3GPP_PROFILE_ID_UNKNOWN.
+ *
+ * Returns: a profile id.
+ *
+ * Since: 1.18
+ */
+gint
+mm_bearer_get_profile_id (MMBearer *self)
{
- g_mutex_lock (&self->priv->ipv4_config_mutex);
- {
- /* If this is the first time ever asking for the object, setup the
- * update listener and the initial object, if any. */
- if (!self->priv->ipv4_config_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_bearer_dup_ip4_config (MM_GDBUS_BEARER (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->ipv4_config = mm_bearer_ip_config_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid initial IPv4 configuration: %s", error->message);
- g_error_free (error);
- }
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->ipv4_config_id =
- g_signal_connect (self,
- "notify::ip4-config",
- G_CALLBACK (ipv4_config_updated),
- NULL);
- }
-
- if (dup && self->priv->ipv4_config)
- *dup = g_object_ref (self->priv->ipv4_config);
- }
- g_mutex_unlock (&self->priv->ipv4_config_mutex);
+ g_return_val_if_fail (MM_IS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ return mm_gdbus_bearer_get_profile_id (MM_GDBUS_BEARER (self));
}
+/*****************************************************************************/
/**
* mm_bearer_get_ipv4_config:
@@ -273,18 +289,12 @@ ensure_internal_ipv4_config (MMBearer *self,
* mm_bearer_get_ipv4_config() again to get a new #MMBearerIpConfig with the
* new values.</warning>
*
- * Returns: (transfer full): A #MMBearerIpConfig that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMBearerIpConfig that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.0
*/
-MMBearerIpConfig *
-mm_bearer_get_ipv4_config (MMBearer *self)
-{
- MMBearerIpConfig *config = NULL;
-
- g_return_val_if_fail (MM_IS_BEARER (self), NULL);
- ensure_internal_ipv4_config (self, &config);
- return config;
-}
/**
* mm_bearer_peek_ipv4_config:
@@ -298,80 +308,21 @@ mm_bearer_get_ipv4_config (MMBearer *self)
* @self was constructed. Use mm_bearer_get_ipv4_config() if on another
* thread.</warning>
*
- * Returns: (transfer none): A #MMBearerIpConfig. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMBearerIpConfig. Do not free the returned
+ * value, it belongs to @self.
+ *
+ * Since: 1.0
*/
-MMBearerIpConfig *
-mm_bearer_peek_ipv4_config (MMBearer *self)
-{
- g_return_val_if_fail (MM_IS_BEARER (self), NULL);
-
- ensure_internal_ipv4_config (self, NULL);
- return self->priv->ipv4_config;
-}
-/*****************************************************************************/
+/* helpers to match the property substring name with the one in our API */
+#define mm_gdbus_bearer_dup_ipv4_config mm_gdbus_bearer_dup_ip4_config
-static void
-ipv6_config_updated (MMBearer *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->ipv6_config_mutex);
- {
- GVariant *dictionary;
-
- g_clear_object (&self->priv->ipv6_config);
-
- /* TODO: update existing object instead of re-creating? */
- dictionary = mm_gdbus_bearer_get_ip6_config (MM_GDBUS_BEARER (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->ipv6_config = mm_bearer_ip_config_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid IPv6 configuration update received: %s", error->message);
- g_error_free (error);
- }
- }
- }
- g_mutex_unlock (&self->priv->ipv6_config_mutex);
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (ipv4_config,
+ Bearer, bearer, BEARER,
+ MMBearerIpConfig,
+ mm_bearer_ip_config_new_from_dictionary)
-static void
-ensure_internal_ipv6_config (MMBearer *self,
- MMBearerIpConfig **dup)
-{
- g_mutex_lock (&self->priv->ipv6_config_mutex);
- {
- /* If this is the first time ever asking for the object, setup the
- * update listener and the initial object, if any. */
- if (!self->priv->ipv6_config_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_bearer_dup_ip6_config (MM_GDBUS_BEARER (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->ipv6_config = mm_bearer_ip_config_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid initial IPv6 configuration: %s", error->message);
- g_error_free (error);
- }
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->ipv6_config_id =
- g_signal_connect (self,
- "notify::ip6-config",
- G_CALLBACK (ipv6_config_updated),
- NULL);
- }
-
- if (dup && self->priv->ipv6_config)
- *dup = g_object_ref (self->priv->ipv6_config);
- }
- g_mutex_unlock (&self->priv->ipv6_config_mutex);
-}
+/*****************************************************************************/
/**
* mm_bearer_get_ipv6_config:
@@ -385,18 +336,11 @@ ensure_internal_ipv6_config (MMBearer *self,
* mm_bearer_get_ipv6_config() again to get a new #MMBearerIpConfig with the
* new values.</warning>
*
- * Returns: (transfer full): A #MMBearerIpConfig that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMBearerIpConfig that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.0
*/
-MMBearerIpConfig *
-mm_bearer_get_ipv6_config (MMBearer *self)
-{
- MMBearerIpConfig *config = NULL;
-
- g_return_val_if_fail (MM_IS_BEARER (self), NULL);
-
- ensure_internal_ipv6_config (self, &config);
- return config;
-}
/**
* mm_bearer_peek_ipv6_config:
@@ -410,80 +354,21 @@ mm_bearer_get_ipv6_config (MMBearer *self)
* @self was constructed. Use mm_bearer_get_ipv6_config() if on another
* thread.</warning>
*
- * Returns: (transfer none): A #MMBearerIpConfig. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMBearerIpConfig. Do not free the returned
+ * value, it belongs to @self.
+ *
+ * Since: 1.0
*/
-MMBearerIpConfig *
-mm_bearer_peek_ipv6_config (MMBearer *self)
-{
- g_return_val_if_fail (MM_IS_BEARER (self), NULL);
- ensure_internal_ipv6_config (self, NULL);
- return self->priv->ipv6_config;
-}
+/* helpers to match the property substring name with the one in our API */
+#define mm_gdbus_bearer_dup_ipv6_config mm_gdbus_bearer_dup_ip6_config
-/*****************************************************************************/
-
-static void
-properties_updated (MMBearer *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->properties_mutex);
- {
- GVariant *dictionary;
-
- g_clear_object (&self->priv->properties);
-
- /* TODO: update existing object instead of re-creating? */
- dictionary = mm_gdbus_bearer_get_properties (MM_GDBUS_BEARER (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->properties = mm_bearer_properties_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid bearer properties received: %s", error->message);
- g_error_free (error);
- }
- }
- }
- g_mutex_unlock (&self->priv->properties_mutex);
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (ipv6_config,
+ Bearer, bearer, BEARER,
+ MMBearerIpConfig,
+ mm_bearer_ip_config_new_from_dictionary)
-static void
-ensure_internal_properties (MMBearer *self,
- MMBearerProperties **dup)
-{
- g_mutex_lock (&self->priv->properties_mutex);
- {
- /* If this is the first time ever asking for the object, setup the
- * update listener and the initial object, if any. */
- if (!self->priv->properties_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_bearer_dup_properties (MM_GDBUS_BEARER (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->properties = mm_bearer_properties_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid initial bearer properties: %s", error->message);
- g_error_free (error);
- }
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->properties_id =
- g_signal_connect (self,
- "notify::properties",
- G_CALLBACK (properties_updated),
- NULL);
- }
-
- if (dup && self->priv->properties)
- *dup = g_object_ref (self->priv->properties);
- }
- g_mutex_unlock (&self->priv->properties_mutex);
-}
+/*****************************************************************************/
/**
* mm_bearer_get_properties:
@@ -497,18 +382,11 @@ ensure_internal_properties (MMBearer *self,
* mm_bearer_get_properties() again to get a new #MMBearerProperties with the
* new values.</warning>
*
- * Returns: (transfer full): A #MMBearerProperties that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMBearerProperties that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.0
*/
-MMBearerProperties *
-mm_bearer_get_properties (MMBearer *self)
-{
- MMBearerProperties *props = NULL;
-
- g_return_val_if_fail (MM_IS_BEARER (self), NULL);
-
- ensure_internal_properties (self, &props);
- return props;
-}
/**
* mm_bearer_peek_properties:
@@ -522,28 +400,114 @@ mm_bearer_get_properties (MMBearer *self)
* @self was constructed. Use mm_bearer_get_properties() if on another
* thread.</warning>
*
- * Returns: (transfer none): A #MMBearerProperties. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMBearerProperties. Do not free the returned
+ * value, it belongs to @self.
+ *
+ * Since: 1.0
*/
-MMBearerProperties *
-mm_bearer_peek_properties (MMBearer *self)
-{
- g_return_val_if_fail (MM_IS_BEARER (self), NULL);
- ensure_internal_properties (self, NULL);
- return self->priv->properties;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (properties,
+ Bearer, bearer, BEARER,
+ MMBearerProperties,
+ mm_bearer_properties_new_from_dictionary)
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_get_stats:
+ * @self: A #MMBearer.
+ *
+ * Gets a #MMBearerStats object specifying the statistics of the current bearer
+ * connection.
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_bearer_get_stats() again to get a new #MMBearerStats with the
+ * new values.</warning>
+ *
+ * Returns: (transfer full): A #MMBearerStats that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.6
+ */
+
+/**
+ * mm_bearer_peek_stats:
+ * @self: A #MMBearer.
+ *
+ * Gets a #MMBearerStats object specifying the statistics of the current bearer
+ * connection.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_bearer_get_stats() if on another
+ * thread.</warning>
+ *
+ * Returns: (transfer none): A #MMBearerStats. Do not free the returned value,
+ * it belongs to @self.
+ *
+ * Since: 1.6
+ */
+
+PROPERTY_OBJECT_DEFINE_FAILABLE (stats,
+ Bearer, bearer, BEARER,
+ MMBearerStats,
+ mm_bearer_stats_new_from_dictionary)
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_get_connection_error:
+ * @self: A #MMBearer.
+ *
+ * Gets a #GError specifying the connection error details, if any.
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_bearer_get_connection_error() again to get a new #GError with the
+ * new values.</warning>
+ *
+ * Returns: (transfer full): A #GError that must be freed with
+ * g_error_free() or %NULL if none.
+ *
+ * Since: 1.18
+ */
+
+/**
+ * mm_bearer_peek_connection_error:
+ * @self: A #MMBearer.
+ *
+ * Gets a #GError specifying the connection error details, if any.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_bearer_get_connection_error() if on another
+ * thread.</warning>
+ *
+ * Returns: (transfer none): A #GError, or %NULL if none. Do not
+ * free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_ERROR_DEFINE_FAILABLE (connection_error,
+ Bearer, bearer, BEARER,
+ mm_common_error_from_tuple)
/*****************************************************************************/
/**
* mm_bearer_connect_finish:
* @self: A #MMBearer.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_bearer_connect().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_bearer_connect().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_bearer_connect().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_bearer_connect_finish (MMBearer *self,
@@ -565,10 +529,14 @@ mm_bearer_connect_finish (MMBearer *self,
* Asynchronously requests activation of a packet data connection with the
* network using this #MMBearer properties.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_bearer_connect_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_bearer_connect_finish() to get the result of the operation.
*
* See mm_bearer_connect_sync() for the synchronous, blocking version of this method.
+ *
+ * Since: 1.0
*/
void
mm_bearer_connect (MMBearer *self,
@@ -593,7 +561,9 @@ mm_bearer_connect (MMBearer *self,
* The calling thread is blocked until a reply is received.
* See mm_bearer_connect() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_bearer_connect_sync (MMBearer *self,
@@ -611,15 +581,22 @@ mm_bearer_connect_sync (MMBearer *self,
* mm_bearer_disconnect:
* @self: A #MMBearer.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Synchronously requests disconnection and deactivation of the packet data connection.
+ * Synchronously requests disconnection and deactivation of the packet data
+ * connection.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_bearer_disconnect_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_bearer_disconnect_finish() to get the result of the operation.
+ * See mm_bearer_disconnect_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_bearer_disconnect_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_bearer_disconnect (MMBearer *self,
@@ -635,12 +612,15 @@ mm_bearer_disconnect (MMBearer *self,
/**
* mm_bearer_disconnect_finish:
* @self: A #MMBearer.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_bearer_disconnect().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_bearer_disconnect().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_bearer_disconnect().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_bearer_disconnect_finish (MMBearer *self,
@@ -658,12 +638,15 @@ mm_bearer_disconnect_finish (MMBearer *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously requests disconnection and deactivation of the packet data connection.
+ * Synchronously requests disconnection and deactivation of the packet data
+ * connection.
*
* The calling thread is blocked until a reply is received.
* See mm_bearer_disconnect() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_bearer_disconnect_sync (MMBearer *self,
@@ -680,13 +663,14 @@ mm_bearer_disconnect_sync (MMBearer *self,
static void
mm_bearer_init (MMBearer *self)
{
- /* Setup private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_BEARER,
- MMBearerPrivate);
- g_mutex_init (&self->priv->ipv4_config_mutex);
- g_mutex_init (&self->priv->ipv6_config_mutex);
- g_mutex_init (&self->priv->properties_mutex);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER, MMBearerPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (ipv4_config, "ip4-config")
+ PROPERTY_INITIALIZE (ipv6_config, "ip6-config")
+ PROPERTY_INITIALIZE (properties, "properties")
+ PROPERTY_INITIALIZE (stats, "stats")
+ PROPERTY_INITIALIZE (connection_error, "connection-error")
}
static void
@@ -694,23 +678,16 @@ finalize (GObject *object)
{
MMBearer *self = MM_BEARER (object);
- g_mutex_clear (&self->priv->ipv4_config_mutex);
- g_mutex_clear (&self->priv->ipv6_config_mutex);
- g_mutex_clear (&self->priv->properties_mutex);
+ g_mutex_clear (&self->priv->mutex);
- G_OBJECT_CLASS (mm_bearer_parent_class)->finalize (object);
-}
+ PROPERTY_OBJECT_FINALIZE (ipv4_config)
+ PROPERTY_OBJECT_FINALIZE (ipv6_config)
+ PROPERTY_OBJECT_FINALIZE (properties)
+ PROPERTY_OBJECT_FINALIZE (stats)
-static void
-dispose (GObject *object)
-{
- MMBearer *self = MM_BEARER (object);
+ PROPERTY_ERROR_FINALIZE (connection_error)
- g_clear_object (&self->priv->ipv4_config);
- g_clear_object (&self->priv->ipv6_config);
- g_clear_object (&self->priv->properties);
-
- G_OBJECT_CLASS (mm_bearer_parent_class)->dispose (object);
+ G_OBJECT_CLASS (mm_bearer_parent_class)->finalize (object);
}
static void
@@ -720,7 +697,5 @@ mm_bearer_class_init (MMBearerClass *bearer_class)
g_type_class_add_private (object_class, sizeof (MMBearerPrivate));
- /* Virtual methods */
- object_class->dispose = dispose;
object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-bearer.h b/libmm-glib/mm-bearer.h
index dbaf497b..2240d7a2 100644
--- a/libmm-glib/mm-bearer.h
+++ b/libmm-glib/mm-bearer.h
@@ -33,6 +33,7 @@
#include "mm-gdbus-bearer.h"
#include "mm-bearer-properties.h"
#include "mm-bearer-ip-config.h"
+#include "mm-bearer-stats.h"
G_BEGIN_DECLS
@@ -65,18 +66,25 @@ struct _MMBearerClass {
};
GType mm_bearer_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearer, g_object_unref)
-const gchar *mm_bearer_get_path (MMBearer *self);
-gchar *mm_bearer_dup_path (MMBearer *self);
+const gchar *mm_bearer_get_path (MMBearer *self);
+gchar *mm_bearer_dup_path (MMBearer *self);
-const gchar *mm_bearer_get_interface (MMBearer *self);
-gchar *mm_bearer_dup_interface (MMBearer *self);
+const gchar *mm_bearer_get_interface (MMBearer *self);
+gchar *mm_bearer_dup_interface (MMBearer *self);
-gboolean mm_bearer_get_connected (MMBearer *self);
+gboolean mm_bearer_get_connected (MMBearer *self);
-gboolean mm_bearer_get_suspended (MMBearer *self);
+gboolean mm_bearer_get_suspended (MMBearer *self);
-guint mm_bearer_get_ip_timeout (MMBearer *self);
+gboolean mm_bearer_get_multiplexed (MMBearer *self);
+
+guint mm_bearer_get_ip_timeout (MMBearer *self);
+
+MMBearerType mm_bearer_get_bearer_type (MMBearer *self);
+
+gint mm_bearer_get_profile_id (MMBearer *self);
void mm_bearer_connect (MMBearer *self,
GCancellable *cancellable,
@@ -109,6 +117,12 @@ MMBearerIpConfig *mm_bearer_peek_ipv4_config (MMBearer *self);
MMBearerIpConfig *mm_bearer_get_ipv6_config (MMBearer *self);
MMBearerIpConfig *mm_bearer_peek_ipv6_config (MMBearer *self);
+MMBearerStats *mm_bearer_get_stats (MMBearer *self);
+MMBearerStats *mm_bearer_peek_stats (MMBearer *self);
+
+GError *mm_bearer_get_connection_error (MMBearer *self);
+GError *mm_bearer_peek_connection_error (MMBearer *self);
+
G_END_DECLS
#endif /* _MM_BEARER_H_ */
diff --git a/libmm-glib/mm-call-audio-format.c b/libmm-glib/mm-call-audio-format.c
new file mode 100644
index 00000000..54debb67
--- /dev/null
+++ b/libmm-glib/mm-call-audio-format.c
@@ -0,0 +1,275 @@
+/* -*- 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) 2017 Red Hat, Inc.
+ */
+
+#include <string.h>
+
+#include "mm-errors-types.h"
+#include "mm-call-audio-format.h"
+
+/**
+ * SECTION: mm-call-audio-format
+ * @title: MMCallAudioFormat
+ * @short_description: Helper object to handle voice call audio formats.
+ *
+ * The #MMCallAudioFormat is an object handling the voice call audio format
+ * which describes how to send/receive voice call audio from the host.
+ *
+ * This object is retrieved with either mm_call_get_audio_format() or
+ * mm_call_peek_audio_format().
+ */
+
+G_DEFINE_TYPE (MMCallAudioFormat, mm_call_audio_format, G_TYPE_OBJECT)
+
+#define PROPERTY_ENCODING "encoding"
+#define PROPERTY_RESOLUTION "resolution"
+#define PROPERTY_RATE "rate"
+
+struct _MMCallAudioFormatPrivate {
+ gchar *encoding;
+ gchar *resolution;
+ guint rate;
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_call_audio_format_get_encoding:
+ * @self: a #MMCallAudioFormat.
+ *
+ * Gets the encoding of the audio format. For example, "pcm" for PCM-encoded
+ * audio.
+ *
+ * Returns: a string with the encoding, or #NULL if unknown. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.10
+ */
+const gchar *
+mm_call_audio_format_get_encoding (MMCallAudioFormat *self)
+{
+ g_return_val_if_fail (MM_IS_CALL_AUDIO_FORMAT (self), NULL);
+
+ return self->priv->encoding;
+}
+
+/**
+ * mm_call_audio_format_set_encoding: (skip)
+ */
+void
+mm_call_audio_format_set_encoding (MMCallAudioFormat *self,
+ const gchar *encoding)
+{
+ g_return_if_fail (MM_IS_CALL_AUDIO_FORMAT (self));
+
+ g_free (self->priv->encoding);
+ self->priv->encoding = g_strdup (encoding);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_audio_format_get_resolution:
+ * @self: a #MMCallAudioFormat.
+ *
+ * Gets the resolution of the audio format. For example, "s16le" for signed
+ * 16-bit little-endian audio sampling resolution.
+ *
+ * Returns: a string with the resolution, or #NULL if unknown. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.10
+ */
+const gchar *
+mm_call_audio_format_get_resolution (MMCallAudioFormat *self)
+{
+ g_return_val_if_fail (MM_IS_CALL_AUDIO_FORMAT (self), NULL);
+
+ return self->priv->resolution;
+}
+
+/**
+ * mm_call_audio_format_set_resolution: (skip)
+ */
+void
+mm_call_audio_format_set_resolution (MMCallAudioFormat *self,
+ const gchar *resolution)
+{
+ g_return_if_fail (MM_IS_CALL_AUDIO_FORMAT (self));
+
+ g_free (self->priv->resolution);
+ self->priv->resolution = g_strdup (resolution);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_audio_format_get_rate:
+ * @self: a #MMCallAudioFormat.
+ *
+ * Gets the sampling rate of the audio format. For example, 8000 for an 8000hz
+ * sampling rate.
+ *
+ * Returns: the sampling rate, or 0 if unknown.
+ *
+ * Since: 1.10
+ */
+guint
+mm_call_audio_format_get_rate (MMCallAudioFormat *self)
+{
+ g_return_val_if_fail (MM_IS_CALL_AUDIO_FORMAT (self), 0);
+
+ return self->priv->rate;
+}
+
+/**
+ * mm_call_audio_format_set_rate: (skip)
+ */
+void
+mm_call_audio_format_set_rate (MMCallAudioFormat *self,
+ guint rate)
+{
+ g_return_if_fail (MM_IS_CALL_AUDIO_FORMAT (self));
+
+ self->priv->rate = rate;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_audio_format_get_dictionary: (skip)
+ */
+GVariant *
+mm_call_audio_format_get_dictionary (MMCallAudioFormat *self)
+{
+ GVariantBuilder builder;
+
+ /* We do allow NULL */
+ if (!self)
+ return NULL;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+ if (self) {
+ if (self->priv->encoding)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ENCODING,
+ g_variant_new_string (self->priv->encoding));
+
+ if (self->priv->resolution)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_RESOLUTION,
+ g_variant_new_string (self->priv->resolution));
+
+ if (self->priv->rate)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_RATE,
+ g_variant_new_uint32 (self->priv->rate));
+ }
+
+ return g_variant_builder_end (&builder);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_audio_format_new_from_dictionary: (skip)
+ */
+MMCallAudioFormat *
+mm_call_audio_format_new_from_dictionary (GVariant *dictionary,
+ GError **error)
+{
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ MMCallAudioFormat *self;
+
+ self = mm_call_audio_format_new ();
+ if (!dictionary)
+ return self;
+
+ if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create call audio format from dictionary: "
+ "invalid variant type received");
+ g_object_unref (self);
+ return NULL;
+ }
+
+ g_variant_iter_init (&iter, dictionary);
+ while (g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ if (g_str_equal (key, PROPERTY_ENCODING))
+ mm_call_audio_format_set_encoding (
+ self,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_RESOLUTION))
+ mm_call_audio_format_set_resolution (
+ self,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_RATE))
+ mm_call_audio_format_set_rate (
+ self,
+ g_variant_get_uint32 (value));
+
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ return self;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_audio_format_new: (skip)
+ */
+MMCallAudioFormat *
+mm_call_audio_format_new (void)
+{
+ return (MM_CALL_AUDIO_FORMAT (
+ g_object_new (MM_TYPE_CALL_AUDIO_FORMAT, NULL)));
+}
+
+static void
+mm_call_audio_format_init (MMCallAudioFormat *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ MM_TYPE_CALL_AUDIO_FORMAT,
+ MMCallAudioFormatPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMCallAudioFormat *self = MM_CALL_AUDIO_FORMAT (object);
+
+ g_free (self->priv->encoding);
+ g_free (self->priv->resolution);
+
+ G_OBJECT_CLASS (mm_call_audio_format_parent_class)->finalize (object);
+}
+
+static void
+mm_call_audio_format_class_init (MMCallAudioFormatClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMCallAudioFormatPrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-call-audio-format.h b/libmm-glib/mm-call-audio-format.h
new file mode 100644
index 00000000..b20c4e54
--- /dev/null
+++ b/libmm-glib/mm-call-audio-format.h
@@ -0,0 +1,87 @@
+/* -*- 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) 2017 Red Hat, Inc.
+ */
+
+#ifndef MM_CALL_AUDIO_FORMAT_H
+#define MM_CALL_AUDIO_FORMAT_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_CALL_AUDIO_FORMAT (mm_call_audio_format_get_type ())
+#define MM_CALL_AUDIO_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormat))
+#define MM_CALL_AUDIO_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormatClass))
+#define MM_IS_CALL_AUDIO_FORMAT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_AUDIO_FORMAT))
+#define MM_IS_CALL_AUDIO_FORMAT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_AUDIO_FORMAT))
+#define MM_CALL_AUDIO_FORMAT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_AUDIO_FORMAT, MMCallAudioFormatClass))
+
+typedef struct _MMCallAudioFormat MMCallAudioFormat;
+typedef struct _MMCallAudioFormatClass MMCallAudioFormatClass;
+typedef struct _MMCallAudioFormatPrivate MMCallAudioFormatPrivate;
+
+/**
+ * MMCallAudioFormat:
+ *
+ * The #MMCallAudioFormat structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _MMCallAudioFormat {
+ /*< private >*/
+ GObject parent;
+ MMCallAudioFormatPrivate *priv;
+};
+
+struct _MMCallAudioFormatClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_call_audio_format_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallAudioFormat, g_object_unref)
+
+const gchar *mm_call_audio_format_get_encoding (MMCallAudioFormat *self);
+const gchar *mm_call_audio_format_get_resolution (MMCallAudioFormat *self);
+guint mm_call_audio_format_get_rate (MMCallAudioFormat *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMCallAudioFormat *mm_call_audio_format_new (void);
+MMCallAudioFormat *mm_call_audio_format_new_from_dictionary (GVariant *dictionary,
+ GError **error);
+
+void mm_call_audio_format_set_encoding (MMCallAudioFormat *self,
+ const gchar *encoding);
+void mm_call_audio_format_set_resolution (MMCallAudioFormat *self,
+ const gchar *resolution);
+void mm_call_audio_format_set_rate (MMCallAudioFormat *self,
+ guint rate);
+
+GVariant *mm_call_audio_format_get_dictionary (MMCallAudioFormat *self);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_CALL_AUDIO_FORMAT_H */
diff --git a/libmm-glib/mm-call-properties.c b/libmm-glib/mm-call-properties.c
new file mode 100644
index 00000000..10e2062d
--- /dev/null
+++ b/libmm-glib/mm-call-properties.c
@@ -0,0 +1,293 @@
+/* -*- 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) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "mm-errors-types.h"
+#include "mm-enums-types.h"
+#include "mm-common-helpers.h"
+#include "mm-call-properties.h"
+
+/**
+ * SECTION: mm-call-properties
+ * @title: MMCallProperties
+ * @short_description: Helper object to handle CALL properties.
+ *
+ * The #MMCallProperties is an object handling the properties to be set
+ * in newly created CALL objects.
+ *
+ * This object is created by the user and passed to ModemManager with either
+ * mm_modem_voice_create_call() or mm_modem_voice_create_call_sync().
+ */
+
+G_DEFINE_TYPE (MMCallProperties, mm_call_properties, G_TYPE_OBJECT)
+
+#define PROPERTY_NUMBER "number"
+
+struct _MMCallPropertiesPrivate {
+ gchar *number;
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_call_properties_set_number:
+ * @self: A #MMCallProperties.
+ * @text: The number to set, in UTF-8.
+ *
+ * Sets the call number.
+ *
+ * Since: 1.6
+ */
+void
+mm_call_properties_set_number (MMCallProperties *self,
+ const gchar *number)
+{
+ g_return_if_fail (MM_IS_CALL_PROPERTIES (self));
+
+ g_free (self->priv->number);
+ self->priv->number = g_strdup (number);
+}
+
+/**
+ * mm_call_properties_get_number:
+ * @self: A #MMCallProperties.
+ *
+ * Gets the number, in UTF-8.
+ *
+ * Returns: the call number, or %NULL if it doesn't contain any (anonymous
+ * caller). Do not free the returned value, it is owned by @self.
+ *
+ * Since: 1.6
+ */
+const gchar *
+mm_call_properties_get_number (MMCallProperties *self)
+{
+ g_return_val_if_fail (MM_IS_CALL_PROPERTIES (self), NULL);
+
+ return self->priv->number;
+}
+
+/*****************************************************************************/
+
+/*
+ * mm_call_properties_get_dictionary: (skip)
+ */
+GVariant *
+mm_call_properties_get_dictionary (MMCallProperties *self)
+{
+ GVariantBuilder builder;
+
+ /* We do allow NULL */
+ if (!self)
+ return NULL;
+
+ g_return_val_if_fail (MM_IS_CALL_PROPERTIES (self), NULL);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (self->priv->number)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_NUMBER,
+ g_variant_new_string (self->priv->number));
+
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+/*****************************************************************************/
+static gboolean
+consume_string (MMCallProperties *self,
+ const gchar *key,
+ const gchar *value,
+ GError **error)
+{
+ if (g_str_equal (key, PROPERTY_NUMBER)) {
+ mm_call_properties_set_number (self, value);
+ } else {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid properties string, unexpected key '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct {
+ MMCallProperties *properties;
+ GError *error;
+} ParseKeyValueContext;
+
+static gboolean
+key_value_foreach (const gchar *key,
+ const gchar *value,
+ ParseKeyValueContext *ctx)
+{
+ return consume_string (ctx->properties,
+ key,
+ value,
+ &ctx->error);
+}
+
+/*
+ * mm_call_properties_new_from_string: (skip)
+ */
+MMCallProperties *
+mm_call_properties_new_from_string (const gchar *str,
+ GError **error)
+{
+ ParseKeyValueContext ctx;
+
+ ctx.properties = mm_call_properties_new ();
+ ctx.error = NULL;
+
+ mm_common_parse_key_value_string (str,
+ &ctx.error,
+ (MMParseKeyValueForeachFn)key_value_foreach,
+ &ctx);
+
+ /* If error, destroy the object */
+ if (ctx.error) {
+ g_propagate_error (error, ctx.error);
+ g_object_unref (ctx.properties);
+ ctx.properties = NULL;
+ }
+
+ return ctx.properties;
+}
+
+/*****************************************************************************/
+
+static gboolean
+consume_variant (MMCallProperties *properties,
+ const gchar *key,
+ GVariant *value,
+ GError **error)
+{
+ if (g_str_equal (key, PROPERTY_NUMBER))
+ mm_call_properties_set_number (
+ properties,
+ g_variant_get_string (value, NULL));
+ else {
+ /* Set error */
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid properties dictionary, unexpected key '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * mm_call_properties_new_from_dictionary: (skip)
+ */
+MMCallProperties *
+mm_call_properties_new_from_dictionary (GVariant *dictionary,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ MMCallProperties *properties;
+
+ properties = mm_call_properties_new ();
+ if (!dictionary)
+ return properties;
+
+ if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create call properties from dictionary: "
+ "invalid variant type received");
+ g_object_unref (properties);
+ return NULL;
+ }
+
+ g_variant_iter_init (&iter, dictionary);
+ while (!inner_error &&
+ g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ consume_variant (properties,
+ key,
+ value,
+ &inner_error);
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ /* If error, destroy the object */
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (properties);
+ properties = NULL;
+ }
+
+ return properties;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_properties_new:
+ *
+ * Creates a new empty #MMCallProperties.
+ *
+ * Returns: (transfer full): a #MMCallProperties. The returned value should be
+ * freed with g_object_unref().
+ *
+ * Since: 1.6
+ */
+MMCallProperties *
+mm_call_properties_new (void)
+{
+ return (MM_CALL_PROPERTIES (g_object_new (MM_TYPE_CALL_PROPERTIES, NULL)));
+}
+
+static void
+mm_call_properties_init (MMCallProperties *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_CALL_PROPERTIES,
+ MMCallPropertiesPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMCallProperties *self = MM_CALL_PROPERTIES (object);
+
+ g_free (self->priv->number);
+
+ G_OBJECT_CLASS (mm_call_properties_parent_class)->finalize (object);
+}
+
+static void
+mm_call_properties_class_init (MMCallPropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMCallPropertiesPrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-call-properties.h b/libmm-glib/mm-call-properties.h
new file mode 100644
index 00000000..5e131573
--- /dev/null
+++ b/libmm-glib/mm-call-properties.h
@@ -0,0 +1,81 @@
+/* -*- 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) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ */
+
+#ifndef MM_CALL_PROPERTIES_H
+#define MM_CALL_PROPERTIES_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_CALL_PROPERTIES (mm_call_properties_get_type ())
+#define MM_CALL_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_PROPERTIES, MMCallProperties))
+#define MM_CALL_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_PROPERTIES, MMCallPropertiesClass))
+#define MM_IS_CALL_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_PROPERTIES))
+#define MM_IS_CALL_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_PROPERTIES))
+#define MM_CALL_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_PROPERTIES, MMCallPropertiesClass))
+
+typedef struct _MMCallProperties MMCallProperties;
+typedef struct _MMCallPropertiesClass MMCallPropertiesClass;
+typedef struct _MMCallPropertiesPrivate MMCallPropertiesPrivate;
+
+/**
+ * MMCallProperties:
+ *
+ * The #MMCallProperties structure contains private data and should only be
+ * accessed using the provided API.
+ */
+struct _MMCallProperties {
+ /*< private >*/
+ GObject parent;
+ MMCallPropertiesPrivate *priv;
+};
+
+struct _MMCallPropertiesClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_call_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallProperties, g_object_unref)
+
+MMCallProperties *mm_call_properties_new (void);
+void mm_call_properties_set_number (MMCallProperties *self,
+ const gchar *text);
+const gchar *mm_call_properties_get_number (MMCallProperties *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMCallProperties *mm_call_properties_new_from_string (const gchar *str,
+ GError **error);
+MMCallProperties *mm_call_properties_new_from_dictionary (GVariant *dictionary,
+ GError **error);
+GVariant *mm_call_properties_get_dictionary (MMCallProperties *self);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_CALL_PROPERTIES_H */
diff --git a/libmm-glib/mm-call.c b/libmm-glib/mm-call.c
new file mode 100644
index 00000000..8091ff7c
--- /dev/null
+++ b/libmm-glib/mm-call.c
@@ -0,0 +1,967 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * 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) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ * Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#include "string.h"
+
+#include "mm-helpers.h"
+#include "mm-call.h"
+#include "mm-modem.h"
+
+/**
+ * SECTION: mm-call
+ * @title: MMCall
+ * @short_description: The call interface
+ *
+ * The #MMCall is an object providing access to the methods, signals and
+ * properties of the call interface.
+ *
+ * When the call is exposed and available in the bus, it is ensured that at
+ * least this interface is also available.
+ */
+
+G_DEFINE_TYPE (MMCall, mm_call, MM_GDBUS_TYPE_CALL_PROXY)
+
+struct _MMCallPrivate {
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_OBJECT_DECLARE (audio_format, MMCallAudioFormat)
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_path:
+ * @self: A #MMCall.
+ *
+ * Gets the DBus path of the #MMCall object.
+ *
+ * Returns: (transfer none): The DBus path of the #MMCall object.
+ *
+ * Since: 1.6
+ */
+const gchar *
+mm_call_get_path (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)));
+}
+
+/**
+ * mm_call_dup_path:
+ * @self: A #MMCall.
+ *
+ * Gets a copy of the DBus path of the #MMCall object.
+ *
+ * Returns: (transfer full): The DBus path of the #MMCall object.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.6
+ */
+gchar *
+mm_call_dup_path (MMCall *self)
+{
+ gchar *value;
+
+ g_return_val_if_fail (MM_IS_CALL (self), NULL);
+
+ g_object_get (G_OBJECT (self),
+ "g-object-path", &value,
+ NULL);
+
+ RETURN_NON_EMPTY_STRING (value);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_number:
+ * @self: A #MMCall.
+ *
+ * Gets the call number. In outgoing calls contains the dialing number or
+ * the remote number in incoming calls
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_call_dup_number() if on another
+ * thread.</warning>
+ *
+ * Returns: (transfer none): The number, or %NULL if it couldn't be retrieved.
+ *
+ * Since: 1.6
+ */
+const gchar *
+mm_call_get_number (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_call_get_number (MM_GDBUS_CALL (self)));
+}
+
+/**
+ * mm_call_dup_number:
+ * @self: A #MMCall.
+ *
+ * Gets the call number. In outgoing calls contains the dialing number or
+ * the remote number in incoming calls
+ *
+ * Returns: (transfer full): The number, or %NULL if it couldn't be retrieved.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.6
+ */
+gchar *
+mm_call_dup_number (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_call_dup_number (MM_GDBUS_CALL (self)));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_direction:
+ * @self: A #MMCall.
+ *
+ * Gets the call direction.
+ *
+ * Returns: a #MMCallDirection.
+ *
+ * Since: 1.6
+ */
+MMCallDirection
+mm_call_get_direction (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), MM_CALL_DIRECTION_INCOMING);
+
+ return (MMCallDirection) mm_gdbus_call_get_direction (MM_GDBUS_CALL (self));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_multiparty:
+ * @self: A #MMCall.
+ *
+ * Gets whether the call is part of a multiparty call.
+ *
+ * Returns: %TRUE if the call is part of a multiparty call, %FALSE otherwise.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_get_multiparty (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_get_multiparty (MM_GDBUS_CALL (self));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_state:
+ * @self: A #MMCall.
+ *
+ * Gets the current state of call.
+ *
+ * Returns: a #MMCallState.
+ *
+ * Since: 1.6
+ */
+MMCallState
+mm_call_get_state (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), MM_CALL_STATE_UNKNOWN);
+
+ return (MMCallState) mm_gdbus_call_get_state (MM_GDBUS_CALL (self));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_state_reason:
+ * @self: A #MMCall.
+ *
+ * Gets the reason of why the call changes its state.
+ *
+ * Returns: a #MMCallStateReason.
+ *
+ * Since: 1.6
+ */
+MMCallStateReason
+mm_call_get_state_reason (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), MM_CALL_STATE_REASON_UNKNOWN);
+
+ return (MMCallStateReason) mm_gdbus_call_get_state_reason (MM_GDBUS_CALL (self));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_audio_port:
+ * @self: A #MMCall.
+ *
+ * Gets the kernel device used for audio (if any).
+ *
+ * Returns: (transfer none): The audio port, or %NULL if call audio is not
+ * routed via the host or couldn't be retrieved.
+ *
+ * Since: 1.10
+ */
+const gchar *
+mm_call_get_audio_port (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_call_get_audio_port (MM_GDBUS_CALL (self)));
+}
+
+/**
+ * mm_call_dup_audio_port:
+ * @self: A #MMCall.
+ *
+ * Gets the kernel device used for audio (if any).
+ *
+ * Returns: (transfer full): The audio port, or %NULL if call audio is not
+ * routed via the host or couldn't be retrieved.
+ *
+ * Since: 1.10
+ */
+gchar *
+mm_call_dup_audio_port (MMCall *self)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_call_dup_audio_port (MM_GDBUS_CALL (self)));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_get_audio_format:
+ * @self: A #MMCall.
+ *
+ * Gets a #MMCallAudioFormat object specifying the audio format used by the
+ * audio port if call audio is routed via the host.
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_call_get_audio_format() again to get a new #MMCallAudioFormat with the
+ * new values.</warning>
+ *
+ * Returns: (transfer full): A #MMCallAudioFormat that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.10
+ */
+
+/**
+ * mm_call_peek_audio_format:
+ * @self: A #MMCall.
+ *
+ * Gets a #MMCallAudioFormat object specifying the audio format used by the
+ * audio port if call audio is routed via the host.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_call_get_audio_format() if on another
+ * thread.</warning>
+ *
+ * Returns: (transfer none): A #MMCallAudioFormat. Do not free the returned
+ * value, it belongs to @self.
+ *
+ * Since: 1.10
+ */
+
+PROPERTY_OBJECT_DEFINE_FAILABLE (audio_format,
+ Call, call, CALL,
+ MMCallAudioFormat,
+ mm_call_audio_format_new_from_dictionary)
+
+/*****************************************************************************/
+
+/**
+ * mm_call_start_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_start().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_start().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_start_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_start_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_start:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to queue the call.
+ *
+ * Call objects can only be executed once.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_start_finish() to get the result of the operation.
+ *
+ * See mm_call_start_sync() for the synchronous, blocking version of this method.
+ *
+ * Since: 1.6
+ */
+void
+mm_call_start (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_start (MM_GDBUS_CALL (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_start_sync:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to queue the call for delivery.
+ *
+ * Call objects can only be sent once.
+ *
+ * The calling thread is blocked until a reply is received.
+ * See mm_call_start() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_start_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_start_sync (MM_GDBUS_CALL (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_accept_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_accept().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_accept().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_accept_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_accept_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_accept:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to accept the incoming call.
+ *
+ * Call objects can only be executed once.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_accept_finish() to get the result of the operation.
+ *
+ * See mm_call_accept_sync() for the synchronous, blocking version of this method.
+ *
+ * Since: 1.6
+ */
+void
+mm_call_accept (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_accept (MM_GDBUS_CALL (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_accept_sync:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to accept the incoming call.
+ *
+ * Call objects can only be sent once.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_accept() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_accept_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_accept_sync (MM_GDBUS_CALL (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_deflect_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_deflect().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_deflect().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_deflect_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_deflect_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_deflect:
+ * @self: A #MMCall.
+ * @number: new number where the call will be deflected.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to deflect the incoming call.
+ *
+ * This call will be considered terminated once the deflection is performed.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_deflect_finish() to get the result of the operation.
+ *
+ * See mm_call_deflect_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.12
+ */
+void
+mm_call_deflect (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_deflect (MM_GDBUS_CALL (self),
+ number,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_deflect_sync:
+ * @self: A #MMCall.
+ * @number: new number where the call will be deflected.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to deflect the incoming call.
+ *
+ * This call will be considered terminated once the deflection is performed.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_deflect() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_deflect_sync (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_deflect_sync (MM_GDBUS_CALL (self),
+ number,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_join_multiparty_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_join_multiparty().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_join_multiparty().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_join_multiparty_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_join_multiparty_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_join_multiparty:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Synchronously requests to join this call into a multiparty call.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_join_multiparty_finish() to get the result of the operation.
+ *
+ * See mm_call_join_multiparty_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_call_join_multiparty (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_join_multiparty (MM_GDBUS_CALL (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_join_multiparty_sync:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to join this call into a multiparty call.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_join_multiparty() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_join_multiparty_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_join_multiparty_sync (MM_GDBUS_CALL (self),
+ cancellable,
+ error);
+}
+
+/**
+ * mm_call_leave_multiparty_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_leave_multiparty().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_leave_multiparty().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_leave_multiparty_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_leave_multiparty_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_leave_multiparty:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Synchronously requests to make this call private again by leaving the
+ * multiparty call.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_leave_multiparty_finish() to get the result of the operation.
+ *
+ * See mm_call_leave_multiparty_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_call_leave_multiparty (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_leave_multiparty (MM_GDBUS_CALL (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_leave_multiparty_sync:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to make this call private again by leaving the
+ * multiparty call.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_leave_multiparty() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_call_leave_multiparty_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_leave_multiparty_sync (MM_GDBUS_CALL (self),
+ cancellable,
+ error);
+}
+
+ /*****************************************************************************/
+
+/**
+ * mm_call_hangup_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_hangup().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_hangup().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_hangup_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_hangup_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_hangup:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to hangup the call.
+ *
+ * Call objects can only be executed once.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_hangup_finish() to get the result of the operation.
+ *
+ * See mm_call_hangup_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.6
+ */
+void
+mm_call_hangup (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_hangup (MM_GDBUS_CALL (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_hangup_sync:
+ * @self: A #MMCall.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to hangup the call.
+ *
+ * Call objects can only be sent once.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_hangup() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_hangup_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_hangup_sync (MM_GDBUS_CALL (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_call_send_dtmf_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_call_send_dtmf().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_send_dtmf().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_send_dtmf_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_send_dtmf_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_send_dtmf:
+ * @self: A #MMCall.
+ * @dtmf: the DMTF tone.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to send a DTMF tone the call.
+ *
+ * Call objects can only be executed once.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_call_send_dtmf_finish() to get the result of the operation.
+ *
+ * See mm_call_send_dtmf_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.6
+ */
+void
+mm_call_send_dtmf (MMCall *self,
+ const gchar *dtmf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_send_dtmf (MM_GDBUS_CALL (self),
+ dtmf,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_send_dtmf_sync:
+ * @self: A #MMCall.
+ * @dtmf: the DMTF tone.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to send a DTMF tone the call.
+ *
+ * Call objects can only be sent once.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_send_dtmf() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_call_send_dtmf_sync (MMCall *self,
+ const gchar *dtmf,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_send_dtmf_sync (MM_GDBUS_CALL (self),
+ dtmf,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+static void
+mm_call_init (MMCall *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_CALL, MMCallPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (audio_format, "audio-format")
+}
+
+static void
+finalize (GObject *object)
+{
+ MMCall *self = MM_CALL (object);
+
+ g_mutex_clear (&self->priv->mutex);
+
+ PROPERTY_OBJECT_FINALIZE (audio_format)
+
+ G_OBJECT_CLASS (mm_call_parent_class)->finalize (object);
+}
+
+static void
+mm_call_class_init (MMCallClass *call_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (call_class);
+
+ g_type_class_add_private (object_class, sizeof (MMCallPrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-call.h b/libmm-glib/mm-call.h
new file mode 100644
index 00000000..ee69b030
--- /dev/null
+++ b/libmm-glib/mm-call.h
@@ -0,0 +1,184 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * 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) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ * Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#ifndef _MM_CALL_H_
+#define _MM_CALL_H_
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+
+#include "mm-gdbus-call.h"
+#include "mm-call-audio-format.h"
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_CALL (mm_call_get_type ())
+#define MM_CALL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL, MMCall))
+#define MM_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL, MMCallClass))
+#define MM_IS_CALL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL))
+#define MM_IS_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_CALL))
+#define MM_CALL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL, MMCallClass))
+
+typedef struct _MMCall MMCall;
+typedef struct _MMCallClass MMCallClass;
+typedef struct _MMCallPrivate MMCallPrivate;
+
+/**
+ * MMCall:
+ *
+ * The #MMCall structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MMCall {
+ /*< private >*/
+ MmGdbusCallProxy parent;
+ MMCallPrivate *priv;
+};
+
+struct _MMCallClass {
+ /*< private >*/
+ MmGdbusCallProxyClass parent;
+};
+
+GType mm_call_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCall, g_object_unref)
+
+const gchar *mm_call_get_path (MMCall *self);
+gchar *mm_call_dup_path (MMCall *self);
+
+const gchar *mm_call_get_number (MMCall *self);
+gchar *mm_call_dup_number (MMCall *self);
+
+MMCallState mm_call_get_state (MMCall *self);
+
+MMCallStateReason mm_call_get_state_reason (MMCall *self);
+
+MMCallDirection mm_call_get_direction (MMCall *self);
+
+gboolean mm_call_get_multiparty (MMCall *self);
+
+const gchar *mm_call_get_audio_port (MMCall *self);
+gchar *mm_call_dup_audio_port (MMCall *self);
+
+MMCallAudioFormat *mm_call_get_audio_format (MMCall *self);
+MMCallAudioFormat *mm_call_peek_audio_format(MMCall *self);
+
+
+void mm_call_start (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_start_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_start_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error);
+
+
+void mm_call_accept (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_accept_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_accept_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_call_deflect (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_deflect_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_deflect_sync (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_call_join_multiparty (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_join_multiparty_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_join_multiparty_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_call_leave_multiparty (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_leave_multiparty_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_leave_multiparty_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_call_hangup (MMCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_hangup_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_hangup_sync (MMCall *self,
+ GCancellable *cancellable,
+ GError **error);
+
+
+void mm_call_send_dtmf (MMCall *self,
+ const gchar *dtmf,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_send_dtmf_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_send_dtmf_sync (MMCall *self,
+ const gchar *dtmf,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _MM_CALL_H_ */
diff --git a/libmm-glib/mm-cdma-manual-activation-properties.c b/libmm-glib/mm-cdma-manual-activation-properties.c
index 7d017c4a..17dab443 100644
--- a/libmm-glib/mm-cdma-manual-activation-properties.c
+++ b/libmm-glib/mm-cdma-manual-activation-properties.c
@@ -42,6 +42,7 @@ struct _MMCdmaManualActivationPropertiesPrivate {
/* Mandatory parameters */
gchar *spc;
guint16 sid;
+ gboolean sid_set;
gchar *mdn;
gchar *min;
/* Optional */
@@ -58,7 +59,10 @@ struct _MMCdmaManualActivationPropertiesPrivate {
*
* Gets the Service Programming Code.
*
- * Returns: (transfer none): The SPC. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The SPC. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.2
*/
const gchar *
mm_cdma_manual_activation_properties_get_spc (MMCdmaManualActivationProperties *self)
@@ -105,6 +109,8 @@ validate_spc (const gchar *spc,
* Sets the Service Programming Code.
*
* Returns: %TRUE if the SPC was successfully set, or %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_spc (MMCdmaManualActivationProperties *self,
@@ -130,6 +136,8 @@ mm_cdma_manual_activation_properties_set_spc (MMCdmaManualActivationProperties *
* Gets the System Identification Number.
*
* Returns: The SID.
+ *
+ * Since: 1.2
*/
guint16
mm_cdma_manual_activation_properties_get_sid (MMCdmaManualActivationProperties *self)
@@ -145,6 +153,8 @@ mm_cdma_manual_activation_properties_get_sid (MMCdmaManualActivationProperties *
* @sid: The SID.
*
* Sets the Service Identification Number.
+ *
+ * Since: 1.2
*/
void
mm_cdma_manual_activation_properties_set_sid (MMCdmaManualActivationProperties *self,
@@ -152,6 +162,7 @@ mm_cdma_manual_activation_properties_set_sid (MMCdmaManualActivationProperties *
{
g_return_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self));
+ self->priv->sid_set = TRUE;
self->priv->sid = sid;
}
@@ -163,7 +174,10 @@ mm_cdma_manual_activation_properties_set_sid (MMCdmaManualActivationProperties *
*
* Gets the Mobile Directory Number.
*
- * Returns: (transfer none): The MDN. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The MDN. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.2
*/
const gchar *
mm_cdma_manual_activation_properties_get_mdn (MMCdmaManualActivationProperties *self)
@@ -198,6 +212,8 @@ validate_mdn (const gchar *mdn,
* Sets the Mobile Directory Number.
*
* Returns: %TRUE if the MDN was successfully set, or %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_mdn (MMCdmaManualActivationProperties *self,
@@ -222,7 +238,10 @@ mm_cdma_manual_activation_properties_set_mdn (MMCdmaManualActivationProperties *
*
* Gets the Mobile Indentification Number.
*
- * Returns: (transfer none): The MIN. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The MIN. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.2
*/
const gchar *
mm_cdma_manual_activation_properties_get_min (MMCdmaManualActivationProperties *self)
@@ -257,6 +276,8 @@ validate_min (const gchar *min,
* Sets the Mobile Identification Number.
*
* Returns: %TRUE if the MIN was successfully set, or %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_min (MMCdmaManualActivationProperties *self,
@@ -281,7 +302,10 @@ mm_cdma_manual_activation_properties_set_min (MMCdmaManualActivationProperties *
*
* Gets the MN-HA key.
*
- * Returns: (transfer none): The MN-HA key. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The MN-HA key. Do not free the returned value, it
+ * is owned by @self.
+ *
+ * Since: 1.2
*/
const gchar *
mm_cdma_manual_activation_properties_get_mn_ha_key (MMCdmaManualActivationProperties *self)
@@ -315,7 +339,10 @@ validate_mn_ha_key (const gchar *mn_ha_key,
*
* Sets the Mobile Identification Number.
*
- * Returns: %TRUE if the MN-HA key was successfully set, or %FALSE if @error is set.
+ * Returns: %TRUE if the MN-HA key was successfully set, or %FALSE if @error
+ * is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_mn_ha_key (MMCdmaManualActivationProperties *self,
@@ -340,7 +367,10 @@ mm_cdma_manual_activation_properties_set_mn_ha_key (MMCdmaManualActivationProper
*
* Gets the MN-AAA key.
*
- * Returns: (transfer none): The MN-AAA key. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The MN-AAA key. Do not free the returned value, it
+ * is owned by @self.
+ *
+ * Since: 1.2
*/
const gchar *
mm_cdma_manual_activation_properties_get_mn_aaa_key (MMCdmaManualActivationProperties *self)
@@ -374,7 +404,10 @@ validate_mn_aaa_key (const gchar *mn_aaa_key,
*
* Sets the Mobile Identification Number.
*
- * Returns: %TRUE if the MN-AAA key was successfully set, or %FALSE if @error is set.
+ * Returns: %TRUE if the MN-AAA key was successfully set, or %FALSE if @error is
+ * set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_mn_aaa_key (MMCdmaManualActivationProperties *self,
@@ -400,17 +433,21 @@ mm_cdma_manual_activation_properties_set_mn_aaa_key (MMCdmaManualActivationPrope
*
* Gets the Preferred Roaming List.
*
- * Returns: (transfer none): The PRL. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The PRL. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.2
*/
const guint8 *
mm_cdma_manual_activation_properties_get_prl (MMCdmaManualActivationProperties *self,
gsize *prl_len)
{
g_return_val_if_fail (MM_IS_CDMA_MANUAL_ACTIVATION_PROPERTIES (self), NULL);
- if (self->priv->prl && prl_len)
- *prl_len = self->priv->prl->len;
- return self->priv->prl->data;
+ if (prl_len)
+ *prl_len = (self->priv->prl ? self->priv->prl->len : 0);
+
+ return (self->priv->prl ? self->priv->prl->data : NULL);
}
/**
@@ -419,7 +456,10 @@ mm_cdma_manual_activation_properties_get_prl (MMCdmaManualActivationProperties *
*
* Gets the Preferred Roaming List.
*
- * Returns: (transfer none): A #GByteArray with the PRL, or %NULL if it doesn't contain any. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): A #GByteArray with the PRL, or %NULL if it doesn't
+ * contain any. Do not free the returned value, it is owned by @self.
+ *
+ * Since: 1.2
*/
GByteArray *
mm_cdma_manual_activation_properties_peek_prl_bytearray (MMCdmaManualActivationProperties *self)
@@ -435,7 +475,10 @@ mm_cdma_manual_activation_properties_peek_prl_bytearray (MMCdmaManualActivationP
*
* Gets the Preferred Roaming List.
*
- * Returns: (transfer full): A #GByteArray with the PRL, or %NULL if it doesn't contain any. The returned value should be freed with g_byte_array_unref().
+ * Returns: (transfer full): A #GByteArray with the PRL, or %NULL if it doesn't
+ * contain any. The returned value should be freed with g_byte_array_unref().
+ *
+ * Since: 1.2
*/
GByteArray *
mm_cdma_manual_activation_properties_get_prl_bytearray (MMCdmaManualActivationProperties *self)
@@ -471,6 +514,8 @@ validate_prl (const guint8 *prl,
* Sets the Preferred Roaming List.
*
* Returns: %TRUE if the PRL was successfully set, or %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_prl (MMCdmaManualActivationProperties *self,
@@ -498,12 +543,15 @@ mm_cdma_manual_activation_properties_set_prl (MMCdmaManualActivationProperties *
/**
* mm_cdma_manual_activation_properties_set_prl_bytearray:
* @self: A #MMCdmaManualActivationProperties.
- * @prl: A #GByteArray with the PRL to set. This method takes a new reference of @prl.
+ * @prl: A #GByteArray with the PRL to set. This method takes a new reference
+ * of @prl.
* @error: Return location for error or %NULL.
*
* Sets the Preferred Roaming List.
*
* Returns: %TRUE if the PRL was successfully set, or %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_cdma_manual_activation_properties_set_prl_bytearray (MMCdmaManualActivationProperties *self,
@@ -525,12 +573,7 @@ mm_cdma_manual_activation_properties_set_prl_bytearray (MMCdmaManualActivationPr
/*****************************************************************************/
/**
- * mm_cdma_manual_activation_properties_get_dictionary:
- * @self: A #MMCdmaManualActivationProperties.
- *
- * Gets a variant dictionary with the contents of @self.
- *
- * Returns: (transfer full): A dictionary with the properties. The returned value should be freed with g_variant_unref().
+ * mm_cdma_manual_activation_properties_get_dictionary: (skip)
*/
GVariant *
mm_cdma_manual_activation_properties_get_dictionary (MMCdmaManualActivationProperties *self)
@@ -552,7 +595,7 @@ mm_cdma_manual_activation_properties_get_dictionary (MMCdmaManualActivationPrope
"{sv}",
PROPERTY_SPC,
g_variant_new_string (self->priv->spc));
- if (self->priv->sid)
+ if (self->priv->sid_set)
g_variant_builder_add (&builder,
"{sv}",
PROPERTY_SID,
@@ -659,14 +702,7 @@ consume_variant (MMCdmaManualActivationProperties *self,
}
/**
- * mm_cdma_manual_activation_properties_new_from_dictionary:
- * @dictionary: A variant dictionary with the properties of the image.
- * @error: Return location for error or %NULL.
- *
- * Creates a new #MMCdmaManualActivationProperties object with the properties exposed in
- * the dictionary.
- *
- * Returns: (transfer full): A #MMCdmaManualActivationProperties or %NULL if @error is set. The returned value should be freed with g_object_unref().
+ * mm_cdma_manual_activation_properties_new_from_dictionary: (skip)
*/
MMCdmaManualActivationProperties *
mm_cdma_manual_activation_properties_new_from_dictionary (GVariant *dictionary,
@@ -717,7 +753,7 @@ mm_cdma_manual_activation_properties_new_from_dictionary (GVariant *dictionary,
/* If mandatory properties missing, destroy the object */
if (!self->priv->spc ||
- !self->priv->sid ||
+ !self->priv->sid_set ||
!self->priv->mdn ||
!self->priv->min) {
g_set_error (error,
@@ -805,6 +841,9 @@ key_value_foreach (const gchar *key,
&ctx->error);
}
+/**
+ * mm_cdma_manual_activation_properties_new_from_string: (skip)
+ */
MMCdmaManualActivationProperties *
mm_cdma_manual_activation_properties_new_from_string (const gchar *str,
GError **error)
@@ -836,7 +875,10 @@ mm_cdma_manual_activation_properties_new_from_string (const gchar *str,
*
* Creates a new #MMCdmaManualActivationProperties object.
*
- * Returns: (transfer full): A #MMCdmaManualActivationProperties. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMCdmaManualActivationProperties. The returned
+ * value should be freed with g_object_unref().
+ *
+ * Since: 1.2
*/
MMCdmaManualActivationProperties *
mm_cdma_manual_activation_properties_new (void)
diff --git a/libmm-glib/mm-cdma-manual-activation-properties.h b/libmm-glib/mm-cdma-manual-activation-properties.h
index 062f7018..066ee698 100644
--- a/libmm-glib/mm-cdma-manual-activation-properties.h
+++ b/libmm-glib/mm-cdma-manual-activation-properties.h
@@ -54,6 +54,7 @@ struct _MMCdmaManualActivationPropertiesClass {
};
GType mm_cdma_manual_activation_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCdmaManualActivationProperties, g_object_unref)
MMCdmaManualActivationProperties *mm_cdma_manual_activation_properties_new (void);
diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c
index fbaa81d1..fbb43040 100644
--- a/libmm-glib/mm-common-helpers.c
+++ b/libmm-glib/mm-common-helpers.c
@@ -25,9 +25,17 @@
#include "mm-errors-types.h"
#include "mm-common-helpers.h"
+#if (!GLIB_CHECK_VERSION (2, 58, 0))
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GEnumClass, g_type_class_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC(GFlagsClass, g_type_class_unref)
+#endif
+
+/******************************************************************************/
+/* Enums/flags to string builders */
+
gchar *
mm_common_build_capabilities_string (const MMModemCapability *capabilities,
- guint n_capabilities)
+ guint n_capabilities)
{
gboolean first = TRUE;
GString *str;
@@ -55,7 +63,7 @@ mm_common_build_capabilities_string (const MMModemCapability *capabilities,
gchar *
mm_common_build_bands_string (const MMModemBand *bands,
- guint n_bands)
+ guint n_bands)
{
gboolean first = TRUE;
GString *str;
@@ -79,7 +87,7 @@ mm_common_build_bands_string (const MMModemBand *bands,
gchar *
mm_common_build_ports_string (const MMModemPortInfo *ports,
- guint n_ports)
+ guint n_ports)
{
gboolean first = TRUE;
GString *str;
@@ -104,7 +112,7 @@ mm_common_build_ports_string (const MMModemPortInfo *ports,
gchar *
mm_common_build_sms_storages_string (const MMSmsStorage *storages,
- guint n_storages)
+ guint n_storages)
{
gboolean first = TRUE;
GString *str;
@@ -128,7 +136,7 @@ mm_common_build_sms_storages_string (const MMSmsStorage *storages,
gchar *
mm_common_build_mode_combinations_string (const MMModemModeCombination *modes,
- guint n_modes)
+ guint n_modes)
{
gboolean first = TRUE;
GString *str;
@@ -158,141 +166,74 @@ mm_common_build_mode_combinations_string (const MMModemModeCombination *modes,
return g_string_free (str, FALSE);
}
-GArray *
-mm_common_sms_storages_variant_to_garray (GVariant *variant)
-{
- GArray *array = NULL;
-
- if (variant) {
- GVariantIter iter;
- guint n;
-
- g_variant_iter_init (&iter, variant);
- n = g_variant_iter_n_children (&iter);
-
- if (n > 0) {
- guint32 storage;
-
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), n);
- while (g_variant_iter_loop (&iter, "u", &storage))
- g_array_append_val (array, storage);
- }
- }
-
- return array;
-}
-
-MMSmsStorage *
-mm_common_sms_storages_variant_to_array (GVariant *variant,
- guint *n_storages)
-{
- GArray *array;
-
- array = mm_common_sms_storages_variant_to_garray (variant);
- if (n_storages)
- *n_storages = array->len;
- return (MMSmsStorage *) g_array_free (array, FALSE);
-}
-
-GVariant *
-mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages,
- guint n_storages)
-{
- GVariantBuilder builder;
- guint i;
-
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
-
- for (i = 0; i < n_storages; i++)
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 ((guint32)storages[i]));
- return g_variant_builder_end (&builder);
-}
-
-GVariant *
-mm_common_sms_storages_garray_to_variant (GArray *array)
-{
- if (array)
- return mm_common_sms_storages_array_to_variant ((const MMSmsStorage *)array->data,
- array->len);
+/******************************************************************************/
+/* String to enums/flags parsers */
- return mm_common_sms_storages_array_to_variant (NULL, 0);
-}
-GArray *
-mm_common_ports_variant_to_garray (GVariant *variant)
+static gint
+_enum_from_string (GType type,
+ const gchar *str,
+ gint error_value,
+ GError **error)
{
- GArray *array = NULL;
-
- if (variant) {
- guint i;
- guint n;
-
- n = g_variant_n_children (variant);
+ g_autoptr(GEnumClass) enum_class = NULL;
+ gint value;
+ guint i;
- if (n > 0) {
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), n);
- for (i = 0; i < n; i++) {
- MMModemPortInfo info;
+ enum_class = G_ENUM_CLASS (g_type_class_ref (type));
- g_variant_get_child (variant, i, "(su)", &info.name, &info.type);
- g_array_append_val (array, info);
- }
+ for (i = 0; enum_class->values[i].value_nick; i++) {
+ if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick)) {
+ value = enum_class->values[i].value;
+ return value;
}
}
- return array;
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid %s value",
+ str,
+ g_type_name (type));
+ return error_value;
}
-MMModemPortInfo *
-mm_common_ports_variant_to_array (GVariant *variant,
- guint *n_ports)
+static guint
+_flags_from_string (GType type,
+ const gchar *str,
+ guint error_value,
+ GError **error)
{
- GArray *array;
+ g_autoptr(GFlagsClass) flags_class = NULL;
+ guint value;
+ guint i;
- array = mm_common_ports_variant_to_garray (variant);
- if (n_ports)
- *n_ports = array->len;
- return (MMModemPortInfo *) g_array_free (array, FALSE);
-}
-
-GVariant *
-mm_common_ports_array_to_variant (const MMModemPortInfo *ports,
- guint n_ports)
-{
- GVariantBuilder builder;
- guint i;
+ flags_class = G_FLAGS_CLASS (g_type_class_ref (type));
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)"));
-
- for (i = 0; i < n_ports; i++) {
- GVariant *tuple[2];
-
- tuple[0] = g_variant_new_string (ports[i].name);
- tuple[1] = g_variant_new_uint32 ((guint32)ports[i].type);
- g_variant_builder_add_value (&builder, g_variant_new_tuple (tuple, 2));
+ for (i = 0; flags_class->values[i].value_nick; i++) {
+ if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick)) {
+ value = flags_class->values[i].value;
+ return value;
+ }
}
- return g_variant_builder_end (&builder);
-}
-GVariant *
-mm_common_ports_garray_to_variant (GArray *array)
-{
- if (array)
- return mm_common_ports_array_to_variant ((const MMModemPortInfo *)array->data,
- array->len);
-
- return mm_common_ports_array_to_variant (NULL, 0);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid %s value",
+ str,
+ g_type_name (type));
+ return error_value;
}
MMModemCapability
-mm_common_get_capabilities_from_string (const gchar *str,
- GError **error)
+mm_common_get_capabilities_from_string (const gchar *str,
+ GError **error)
{
- GError *inner_error = NULL;
- MMModemCapability capabilities;
- gchar **capability_strings;
- GFlagsClass *flags_class;
+ GError *inner_error = NULL;
+ MMModemCapability capabilities;
+ g_auto(GStrv) capability_strings = NULL;
+ g_autoptr(GFlagsClass) flags_class = NULL;
capabilities = MM_MODEM_CAPABILITY_NONE;
@@ -329,20 +270,17 @@ mm_common_get_capabilities_from_string (const gchar *str,
g_propagate_error (error, inner_error);
capabilities = MM_MODEM_CAPABILITY_NONE;
}
-
- g_type_class_unref (flags_class);
- g_strfreev (capability_strings);
return capabilities;
}
MMModemMode
-mm_common_get_modes_from_string (const gchar *str,
- GError **error)
+mm_common_get_modes_from_string (const gchar *str,
+ GError **error)
{
- GError *inner_error = NULL;
- MMModemMode modes;
- gchar **mode_strings;
- GFlagsClass *flags_class;
+ GError *inner_error = NULL;
+ MMModemMode modes;
+ g_auto(GStrv) mode_strings = NULL;
+ g_autoptr(GFlagsClass) flags_class = NULL;
modes = MM_MODEM_MODE_NONE;
@@ -379,145 +317,249 @@ mm_common_get_modes_from_string (const gchar *str,
g_propagate_error (error, inner_error);
modes = MM_MODEM_MODE_NONE;
}
-
- g_type_class_unref (flags_class);
- g_strfreev (mode_strings);
return modes;
}
-GArray *
-mm_common_capability_combinations_variant_to_garray (GVariant *variant)
+gboolean
+mm_common_get_bands_from_string (const gchar *str,
+ MMModemBand **bands,
+ guint *n_bands,
+ GError **error)
{
- GArray *array = NULL;
+ GError *inner_error = NULL;
+ GArray *array;
+ g_auto(GStrv) band_strings = NULL;
+ g_autoptr(GEnumClass) enum_class = NULL;
- if (variant) {
- GVariantIter iter;
- guint n;
+ array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
- g_variant_iter_init (&iter, variant);
- n = g_variant_iter_n_children (&iter);
+ enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_BAND));
+ band_strings = g_strsplit (str, "|", -1);
- if (n > 0) {
- guint32 capability;
+ if (band_strings) {
+ guint i;
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), n);
- while (g_variant_iter_loop (&iter, "u", &capability))
- g_array_append_val (array, capability);
+ for (i = 0; band_strings[i]; i++) {
+ guint j;
+ gboolean found = FALSE;
+
+ for (j = 0; enum_class->values[j].value_nick; j++) {
+ if (!g_ascii_strcasecmp (band_strings[i], enum_class->values[j].value_nick)) {
+ g_array_append_val (array, enum_class->values[j].value);
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid MMModemBand value",
+ band_strings[i]);
+ break;
+ }
}
}
- /* If nothing set, fallback to default */
- if (!array) {
- guint32 capability = MM_MODEM_CAPABILITY_NONE;
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_array_free (array, TRUE);
+ *n_bands = 0;
+ *bands = NULL;
+ return FALSE;
+ }
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
- g_array_append_val (array, capability);
+ if (!array->len) {
+ GEnumValue *value;
+
+ value = g_enum_get_value (enum_class, MM_MODEM_BAND_UNKNOWN);
+ g_array_append_val (array, value->value);
}
- return array;
+ *n_bands = array->len;
+ *bands = (MMModemBand *)g_array_free (array, FALSE);
+ return TRUE;
+}
+
+gboolean
+mm_common_get_boolean_from_string (const gchar *value,
+ GError **error)
+{
+ if (!g_ascii_strcasecmp (value, "true") || g_str_equal (value, "1") || !g_ascii_strcasecmp (value, "yes"))
+ return TRUE;
+
+ if (!g_ascii_strcasecmp (value, "false") || g_str_equal (value, "0") || !g_ascii_strcasecmp (value, "no"))
+ return FALSE;
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot get boolean from string '%s'", value);
+ return FALSE;
}
-MMModemCapability *
-mm_common_capability_combinations_variant_to_array (GVariant *variant,
- guint *n_capabilities)
+MMModemCdmaRmProtocol
+mm_common_get_rm_protocol_from_string (const gchar *str,
+ GError **error)
{
- GArray *array;
+ return _enum_from_string (MM_TYPE_MODEM_CDMA_RM_PROTOCOL,
+ str,
+ MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN,
+ error);
+}
- array = mm_common_capability_combinations_variant_to_garray (variant);
- if (n_capabilities)
- *n_capabilities = array->len;
- return (MMModemCapability *) g_array_free (array, FALSE);
+MMBearerIpFamily
+mm_common_get_ip_type_from_string (const gchar *str,
+ GError **error)
+{
+ return _flags_from_string (MM_TYPE_BEARER_IP_FAMILY,
+ str,
+ MM_BEARER_IP_FAMILY_NONE,
+ error);
}
-GVariant *
-mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities,
- guint n_capabilities)
+MMBearerAllowedAuth
+mm_common_get_allowed_auth_from_string (const gchar *str,
+ GError **error)
{
- GVariantBuilder builder;
+ GError *inner_error = NULL;
+ MMBearerAllowedAuth allowed_auth;
+ g_auto(GStrv) strings = NULL;
+ g_autoptr(GFlagsClass) flags_class = NULL;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+ allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
- if (n_capabilities > 0) {
+ flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_ALLOWED_AUTH));
+ strings = g_strsplit (str, "|", -1);
+
+ if (strings) {
guint i;
- for (i = 0; i < n_capabilities; i++)
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 ((guint32)capabilities[i]));
- } else
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
+ for (i = 0; strings[i]; i++) {
+ guint j;
+ gboolean found = FALSE;
- return g_variant_builder_end (&builder);
+ for (j = 0; flags_class->values[j].value_nick; j++) {
+ if (!g_ascii_strcasecmp (strings[i], flags_class->values[j].value_nick)) {
+ allowed_auth |= flags_class->values[j].value;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ inner_error = g_error_new (
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid MMBearerAllowedAuth value",
+ strings[i]);
+ break;
+ }
+ }
+ }
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ }
+ return allowed_auth;
}
-GVariant *
-mm_common_capability_combinations_garray_to_variant (GArray *array)
+MMSmsStorage
+mm_common_get_sms_storage_from_string (const gchar *str,
+ GError **error)
{
- if (array)
- return mm_common_capability_combinations_array_to_variant ((const MMModemCapability *)array->data,
- array->len);
+ return _enum_from_string (MM_TYPE_SMS_STORAGE,
+ str,
+ MM_SMS_STORAGE_UNKNOWN,
+ error);
+}
- return mm_common_capability_combinations_array_to_variant (NULL, 0);
+MMSmsCdmaTeleserviceId
+mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_SMS_CDMA_TELESERVICE_ID,
+ str,
+ MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN,
+ error);
}
-GVariant *
-mm_common_build_capability_combinations_none (void)
+MMSmsCdmaServiceCategory
+mm_common_get_sms_cdma_service_category_from_string (const gchar *str,
+ GError **error)
{
- GVariantBuilder builder;
+ return _enum_from_string (MM_TYPE_SMS_CDMA_SERVICE_CATEGORY,
+ str,
+ MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN,
+ error);
+}
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
- return g_variant_builder_end (&builder);
+MMCallDirection
+mm_common_get_call_direction_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_CALL_DIRECTION,
+ str,
+ MM_CALL_DIRECTION_UNKNOWN,
+ error);
}
-GVariant *
-mm_common_build_capability_combinations_any (void)
+MMCallState
+mm_common_get_call_state_from_string (const gchar *str,
+ GError **error)
{
- GVariantBuilder builder;
+ return _enum_from_string (MM_TYPE_CALL_STATE,
+ str,
+ MM_CALL_STATE_UNKNOWN,
+ error);
+}
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 (MM_MODEM_CAPABILITY_ANY));
- return g_variant_builder_end (&builder);
+MMCallStateReason
+mm_common_get_call_state_reason_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_CALL_STATE_REASON,
+ str,
+ MM_CALL_STATE_REASON_UNKNOWN,
+ error);
}
-void
-mm_common_get_bands_from_string (const gchar *str,
- MMModemBand **bands,
- guint *n_bands,
- GError **error)
+MMOmaFeature
+mm_common_get_oma_features_from_string (const gchar *str,
+ GError **error)
{
- GError *inner_error = NULL;
- GArray *array;
- gchar **band_strings;
- GEnumClass *enum_class;
+ GError *inner_error = NULL;
+ MMOmaFeature features;
+ g_auto(GStrv) feature_strings = NULL;
+ g_autoptr(GFlagsClass) flags_class = NULL;
- array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+ features = MM_OMA_FEATURE_NONE;
- enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_BAND));
- band_strings = g_strsplit (str, "|", -1);
+ flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_OMA_FEATURE));
+ feature_strings = g_strsplit (str, "|", -1);
- if (band_strings) {
+ if (feature_strings) {
guint i;
- for (i = 0; band_strings[i]; i++) {
+ for (i = 0; feature_strings[i]; i++) {
guint j;
gboolean found = FALSE;
- for (j = 0; enum_class->values[j].value_nick; j++) {
- if (!g_ascii_strcasecmp (band_strings[i], enum_class->values[j].value_nick)) {
- g_array_append_val (array, enum_class->values[j].value);
+ for (j = 0; flags_class->values[j].value_nick; j++) {
+ if (!g_ascii_strcasecmp (feature_strings[i], flags_class->values[j].value_nick)) {
+ features |= flags_class->values[j].value;
found = TRUE;
break;
}
}
if (!found) {
- inner_error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMModemBand value",
- band_strings[i]);
+ inner_error = g_error_new (
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid MMOmaFeature value",
+ feature_strings[i]);
break;
}
}
@@ -525,136 +567,341 @@ mm_common_get_bands_from_string (const gchar *str,
if (inner_error) {
g_propagate_error (error, inner_error);
- g_array_free (array, TRUE);
- *n_bands = 0;
- *bands = NULL;
- } else {
- if (!array->len) {
- GEnumValue *value;
+ features = MM_OMA_FEATURE_NONE;
+ }
+ return features;
+}
+
+MMOmaSessionType
+mm_common_get_oma_session_type_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_OMA_SESSION_TYPE,
+ str,
+ MM_OMA_SESSION_TYPE_UNKNOWN,
+ error);
+}
+
+MMModem3gppEpsUeModeOperation
+mm_common_get_eps_ue_mode_operation_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_MODEM_3GPP_EPS_UE_MODE_OPERATION,
+ str,
+ MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN,
+ error);
+}
- value = g_enum_get_value (enum_class, MM_MODEM_BAND_UNKNOWN);
- g_array_append_val (array, value->value);
+MMModemAccessTechnology
+mm_common_get_access_technology_from_string (const gchar *str,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ MMModemAccessTechnology technologies;
+ g_auto(GStrv) technology_strings = NULL;
+ g_autoptr(GFlagsClass) flags_class = NULL;
+
+ technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_MODEM_ACCESS_TECHNOLOGY));
+ technology_strings = g_strsplit (str, "|", -1);
+
+ if (technology_strings) {
+ guint i;
+
+ for (i = 0; technology_strings[i]; i++) {
+ guint j;
+ gboolean found = FALSE;
+
+ for (j = 0; flags_class->values[j].value_nick; j++) {
+ if (!g_ascii_strcasecmp (technology_strings[i], flags_class->values[j].value_nick)) {
+ technologies |= flags_class->values[j].value;
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ inner_error = g_error_new (
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid MMModemAccessTechnology value",
+ technology_strings[i]);
+ break;
+ }
}
+ }
- *n_bands = array->len;
- *bands = (MMModemBand *)g_array_free (array, FALSE);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
+ return technologies;
+}
- g_type_class_unref (enum_class);
- g_strfreev (band_strings);
+MMBearerMultiplexSupport
+mm_common_get_multiplex_support_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_BEARER_MULTIPLEX_SUPPORT,
+ str,
+ MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN,
+ error);
+}
+
+MMBearerApnType
+mm_common_get_apn_type_from_string (const gchar *str,
+ GError **error)
+{
+ return _flags_from_string (MM_TYPE_BEARER_APN_TYPE,
+ str,
+ MM_BEARER_APN_TYPE_NONE,
+ error);
+}
+
+MMModem3gppFacility
+mm_common_get_3gpp_facility_from_string (const gchar *str,
+ GError **error)
+{
+ return _flags_from_string (MM_TYPE_MODEM_3GPP_FACILITY,
+ str,
+ MM_MODEM_3GPP_FACILITY_NONE,
+ error);
+}
+
+MMModem3gppPacketServiceState
+mm_common_get_3gpp_packet_service_state_from_string (const gchar *str,
+ GError **error)
+{
+ return _enum_from_string (MM_TYPE_MODEM_3GPP_PACKET_SERVICE_STATE,
+ str,
+ MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN,
+ error);
+}
+
+/******************************************************************************/
+/* MMModemPortInfo array management */
+
+static void
+clear_modem_port_info (MMModemPortInfo *info)
+{
+ g_free (info->name);
}
GArray *
-mm_common_bands_variant_to_garray (GVariant *variant)
+mm_common_ports_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
if (variant) {
- GVariantIter iter;
+ guint i;
guint n;
- g_variant_iter_init (&iter, variant);
- n = g_variant_iter_n_children (&iter);
+ n = g_variant_n_children (variant);
if (n > 0) {
- guint32 band;
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), n);
+ g_array_set_clear_func (array, (GDestroyNotify) clear_modem_port_info);
+ for (i = 0; i < n; i++) {
+ MMModemPortInfo info;
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n);
- while (g_variant_iter_loop (&iter, "u", &band))
- g_array_append_val (array, band);
+ g_variant_get_child (variant, i, "(su)", &info.name, &info.type);
+ g_array_append_val (array, info);
+ }
}
}
- /* If nothing set, fallback to default */
- if (!array) {
- guint32 band = MM_MODEM_BAND_UNKNOWN;
-
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
- g_array_append_val (array, band);
- }
-
return array;
}
-MMModemBand *
-mm_common_bands_variant_to_array (GVariant *variant,
- guint *n_bands)
+GVariant *
+mm_common_ports_array_to_variant (const MMModemPortInfo *ports,
+ guint n_ports)
{
- GArray *array;
+ GVariantBuilder builder;
+ guint i;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)"));
- array = mm_common_bands_variant_to_garray (variant);
- if (n_bands)
- *n_bands = array->len;
- return (MMModemBand *) g_array_free (array, FALSE);
+ for (i = 0; i < n_ports; i++) {
+ GVariant *tuple[2];
+
+ tuple[0] = g_variant_new_string (ports[i].name);
+ tuple[1] = g_variant_new_uint32 ((guint32)ports[i].type);
+ g_variant_builder_add_value (&builder, g_variant_new_tuple (tuple, 2));
+ }
+ return g_variant_builder_end (&builder);
}
GVariant *
-mm_common_bands_array_to_variant (const MMModemBand *bands,
- guint n_bands)
+mm_common_ports_garray_to_variant (GArray *array)
{
- if (n_bands > 0) {
- GVariantBuilder builder;
+ if (array)
+ return mm_common_ports_array_to_variant ((const MMModemPortInfo *)array->data,
+ array->len);
+
+ return mm_common_ports_array_to_variant (NULL, 0);
+}
+
+gboolean
+mm_common_ports_garray_to_array (GArray *array,
+ MMModemPortInfo **ports,
+ guint *n_ports)
+{
+ if (!array)
+ return FALSE;
+
+ *ports = NULL;
+ *n_ports = array->len;
+ if (array->len > 0) {
guint i;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+ *ports = g_malloc (sizeof (MMModemPortInfo) * array->len);
- for (i = 0; i < n_bands; i++)
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 ((guint32)bands[i]));
- return g_variant_builder_end (&builder);
+ /* Deep-copy the array */
+ for (i = 0; i < array->len; i++) {
+ MMModemPortInfo *src;
+
+ src = &g_array_index (array, MMModemPortInfo, i);
+ (*ports)[i].name = g_strdup (src->name);
+ (*ports)[i].type = src->type;
+ }
}
+ return TRUE;
+}
- return mm_common_build_bands_unknown ();
+/******************************************************************************/
+/* MMSmsStorage array management */
+
+GArray *
+mm_common_sms_storages_variant_to_garray (GVariant *variant)
+{
+ GArray *array = NULL;
+
+ if (variant) {
+ GVariantIter iter;
+ guint n;
+
+ g_variant_iter_init (&iter, variant);
+ n = g_variant_iter_n_children (&iter);
+
+ if (n > 0) {
+ guint32 storage;
+
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), n);
+ while (g_variant_iter_loop (&iter, "u", &storage))
+ g_array_append_val (array, storage);
+ }
+ }
+
+ return array;
}
GVariant *
-mm_common_bands_garray_to_variant (GArray *array)
+mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages,
+ guint n_storages)
{
- if (array)
- return mm_common_bands_array_to_variant ((const MMModemBand *)array->data,
- array->len);
+ GVariantBuilder builder;
+ guint i;
- return mm_common_bands_array_to_variant (NULL, 0);
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+
+ for (i = 0; i < n_storages; i++)
+ g_variant_builder_add_value (&builder,
+ g_variant_new_uint32 ((guint32)storages[i]));
+ return g_variant_builder_end (&builder);
}
-static guint
-cmp_band (MMModemBand *a, MMModemBand *b)
+GVariant *
+mm_common_sms_storages_garray_to_variant (GArray *array)
{
- return (*a - *b);
+ if (array)
+ return mm_common_sms_storages_array_to_variant ((const MMSmsStorage *)array->data,
+ array->len);
+
+ return mm_common_sms_storages_array_to_variant (NULL, 0);
}
-gboolean
-mm_common_bands_garray_cmp (GArray *a, GArray *b)
+/******************************************************************************/
+/* MMModemCapability array management */
+
+GArray *
+mm_common_capability_combinations_variant_to_garray (GVariant *variant)
{
- GArray *dup_a;
- GArray *dup_b;
- guint i;
- gboolean different;
+ GArray *array = NULL;
- if (a->len != b->len)
- return FALSE;
+ if (variant) {
+ GVariantIter iter;
+ guint n;
- dup_a = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), a->len);
- g_array_append_vals (dup_a, a->data, a->len);
+ g_variant_iter_init (&iter, variant);
+ n = g_variant_iter_n_children (&iter);
- dup_b = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), b->len);
- g_array_append_vals (dup_b, b->data, b->len);
+ if (n > 0) {
+ guint32 capability;
- g_array_sort (dup_a, (GCompareFunc)cmp_band);
- g_array_sort (dup_b, (GCompareFunc)cmp_band);
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), n);
+ while (g_variant_iter_loop (&iter, "u", &capability))
+ g_array_append_val (array, capability);
+ }
+ }
- different = FALSE;
- for (i = 0; !different && i < a->len; i++) {
- if (g_array_index (dup_a, MMModemBand, i) != g_array_index (dup_b, MMModemBand, i))
- different = TRUE;
+ /* If nothing set, fallback to default */
+ if (!array) {
+ guint32 capability = MM_MODEM_CAPABILITY_NONE;
+
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
+ g_array_append_val (array, capability);
}
- g_array_unref (dup_a);
- g_array_unref (dup_b);
+ return array;
+}
- return !different;
+GVariant *
+mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities,
+ guint n_capabilities)
+{
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+
+ if (n_capabilities > 0) {
+ guint i;
+
+ for (i = 0; i < n_capabilities; i++)
+ g_variant_builder_add_value (&builder,
+ g_variant_new_uint32 ((guint32)capabilities[i]));
+ } else
+ g_variant_builder_add_value (&builder,
+ g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
+
+ return g_variant_builder_end (&builder);
+}
+
+GVariant *
+mm_common_capability_combinations_garray_to_variant (GArray *array)
+{
+ if (array)
+ return mm_common_capability_combinations_array_to_variant ((const MMModemCapability *)array->data,
+ array->len);
+
+ return mm_common_capability_combinations_array_to_variant (NULL, 0);
}
+GVariant *
+mm_common_build_capability_combinations_none (void)
+{
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+ g_variant_builder_add_value (&builder,
+ g_variant_new_uint32 (MM_MODEM_CAPABILITY_NONE));
+ return g_variant_builder_end (&builder);
+}
+
+/******************************************************************************/
+/* MMModemModeCombination array management */
+
GArray *
mm_common_mode_combinations_variant_to_garray (GVariant *variant)
{
@@ -689,21 +936,9 @@ mm_common_mode_combinations_variant_to_garray (GVariant *variant)
return array;
}
-MMModemModeCombination *
-mm_common_mode_combinations_variant_to_array (GVariant *variant,
- guint *n_modes)
-{
- GArray *array;
-
- array = mm_common_mode_combinations_variant_to_garray (variant);
- if (n_modes)
- *n_modes = array->len;
- return (MMModemModeCombination *) g_array_free (array, FALSE);
-}
-
GVariant *
mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes,
- guint n_modes)
+ guint n_modes)
{
if (n_modes > 0) {
GVariantBuilder builder;
@@ -745,8 +980,11 @@ mm_common_build_mode_combinations_default (void)
return g_variant_builder_end (&builder);
}
+/******************************************************************************/
+/* MMModemBand array management */
+
GArray *
-mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant)
+mm_common_bands_variant_to_garray (GVariant *variant)
{
GArray *array = NULL;
@@ -758,343 +996,234 @@ mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *va
n = g_variant_iter_n_children (&iter);
if (n > 0) {
- MMOmaPendingNetworkInitiatedSession session;
+ guint32 band;
- array = g_array_sized_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession), n);
- while (g_variant_iter_loop (&iter, "(uu)", &session.session_type, &session.session_id))
- g_array_append_val (array, session);
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n);
+ while (g_variant_iter_loop (&iter, "u", &band))
+ g_array_append_val (array, band);
}
}
- /* If nothing set, fallback to empty */
- if (!array)
- array = g_array_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession));
-
- return array;
-}
+ /* If nothing set, fallback to default */
+ if (!array) {
+ guint32 band = MM_MODEM_BAND_UNKNOWN;
-MMOmaPendingNetworkInitiatedSession *
-mm_common_oma_pending_network_initiated_sessions_variant_to_array (GVariant *variant,
- guint *n_sessions)
-{
- GArray *array;
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (array, band);
+ }
- array = mm_common_oma_pending_network_initiated_sessions_variant_to_garray (variant);
- if (n_sessions)
- *n_sessions = array->len;
- return (MMOmaPendingNetworkInitiatedSession *) g_array_free (array, FALSE);
+ return array;
}
GVariant *
-mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *sessions,
- guint n_sessions)
+mm_common_bands_array_to_variant (const MMModemBand *bands,
+ guint n_bands)
{
- if (n_sessions > 0) {
+ if (n_bands > 0) {
GVariantBuilder builder;
guint i;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
- for (i = 0; i < n_sessions; i++)
+ for (i = 0; i < n_bands; i++)
g_variant_builder_add_value (&builder,
- g_variant_new ("(uu)",
- ((guint32)sessions[i].session_type),
- ((guint32)sessions[i].session_id)));
+ g_variant_new_uint32 ((guint32)bands[i]));
return g_variant_builder_end (&builder);
}
- return mm_common_build_oma_pending_network_initiated_sessions_default ();
+ return mm_common_build_bands_unknown ();
}
GVariant *
-mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array)
+mm_common_bands_garray_to_variant (GArray *array)
{
if (array)
- return mm_common_oma_pending_network_initiated_sessions_array_to_variant ((const MMOmaPendingNetworkInitiatedSession *)array->data,
- array->len);
+ return mm_common_bands_array_to_variant ((const MMModemBand *)array->data,
+ array->len);
- return mm_common_oma_pending_network_initiated_sessions_array_to_variant (NULL, 0);
+ return mm_common_bands_array_to_variant (NULL, 0);
}
GVariant *
-mm_common_build_oma_pending_network_initiated_sessions_default (void)
+mm_common_build_bands_unknown (void)
{
GVariantBuilder builder;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+ g_variant_builder_add_value (&builder,
+ g_variant_new_uint32 (MM_MODEM_BAND_UNKNOWN));
return g_variant_builder_end (&builder);
}
-gboolean
-mm_common_get_boolean_from_string (const gchar *value,
- GError **error)
+GVariant *
+mm_common_build_bands_any (void)
{
- if (!g_ascii_strcasecmp (value, "true") || g_str_equal (value, "1"))
- return TRUE;
-
- if (g_ascii_strcasecmp (value, "false") && g_str_equal (value, "0"))
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Cannot get boolean from string '%s'", value);
+ GVariantBuilder builder;
- return FALSE;
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
+ g_variant_builder_add_value (&builder,
+ g_variant_new_uint32 (MM_MODEM_BAND_ANY));
+ return g_variant_builder_end (&builder);
}
-MMModemCdmaRmProtocol
-mm_common_get_rm_protocol_from_string (const gchar *str,
- GError **error)
+static guint
+cmp_band (MMModemBand *a, MMModemBand *b)
{
- GEnumClass *enum_class;
- guint i;
-
- enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_MODEM_CDMA_RM_PROTOCOL));
-
- for (i = 0; enum_class->values[i].value_nick; i++) {
- if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
- return enum_class->values[i].value;
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMModemCdmaRmProtocol value",
- str);
- return MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
+ return (*a - *b);
}
-MMBearerIpFamily
-mm_common_get_ip_type_from_string (const gchar *str,
- GError **error)
+gboolean
+mm_common_bands_garray_cmp (GArray *a, GArray *b)
{
- GFlagsClass *flags_class;
+ GArray *dup_a;
+ GArray *dup_b;
guint i;
+ gboolean different;
- flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_IP_FAMILY));
-
- for (i = 0; flags_class->values[i].value_nick; i++) {
- if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick))
- return flags_class->values[i].value;
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMBearerIpFamily value",
- str);
- return MM_BEARER_IP_FAMILY_NONE;
-}
-
-MMBearerAllowedAuth
-mm_common_get_allowed_auth_from_string (const gchar *str,
- GError **error)
-{
- GError *inner_error = NULL;
- MMBearerAllowedAuth allowed_auth;
- gchar **strings;
- GFlagsClass *flags_class;
-
- allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
-
- flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_BEARER_ALLOWED_AUTH));
- strings = g_strsplit (str, "|", -1);
+ if (a->len != b->len)
+ return FALSE;
- if (strings) {
- guint i;
+ dup_a = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), a->len);
+ g_array_append_vals (dup_a, a->data, a->len);
- for (i = 0; strings[i]; i++) {
- guint j;
- gboolean found = FALSE;
+ dup_b = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), b->len);
+ g_array_append_vals (dup_b, b->data, b->len);
- for (j = 0; flags_class->values[j].value_nick; j++) {
- if (!g_ascii_strcasecmp (strings[i], flags_class->values[j].value_nick)) {
- allowed_auth |= flags_class->values[j].value;
- found = TRUE;
- break;
- }
- }
+ g_array_sort (dup_a, (GCompareFunc)cmp_band);
+ g_array_sort (dup_b, (GCompareFunc)cmp_band);
- if (!found) {
- inner_error = g_error_new (
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMBearerAllowedAuth value",
- strings[i]);
- break;
- }
- }
+ different = FALSE;
+ for (i = 0; !different && i < a->len; i++) {
+ if (g_array_index (dup_a, MMModemBand, i) != g_array_index (dup_b, MMModemBand, i))
+ different = TRUE;
}
- if (inner_error) {
- g_propagate_error (error, inner_error);
- allowed_auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
- }
+ g_array_unref (dup_a);
+ g_array_unref (dup_b);
- g_type_class_unref (flags_class);
- g_strfreev (strings);
- return allowed_auth;
+ return !different;
}
-MMSmsStorage
-mm_common_get_sms_storage_from_string (const gchar *str,
- GError **error)
+gboolean
+mm_common_bands_garray_lookup (GArray *array,
+ MMModemBand value)
{
- GEnumClass *enum_class;
guint i;
- enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_STORAGE));
-
- for (i = 0; enum_class->values[i].value_nick; i++) {
- if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
- return enum_class->values[i].value;
+ for (i = 0; i < array->len; i++) {
+ if (value == g_array_index (array, MMModemBand, i))
+ return TRUE;
}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMSmsStorage value",
- str);
- return MM_SMS_STORAGE_UNKNOWN;
+ return FALSE;
}
-MMSmsCdmaTeleserviceId
-mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str,
- GError **error)
+void
+mm_common_bands_garray_sort (GArray *array)
{
- GEnumClass *enum_class;
- guint i;
-
- enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_CDMA_TELESERVICE_ID));
-
- for (i = 0; enum_class->values[i].value_nick; i++) {
- if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
- return enum_class->values[i].value;
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMSmsCdmaTeleserviceId value",
- str);
- return MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN;
+ g_array_sort (array, (GCompareFunc) cmp_band);
}
-MMSmsCdmaServiceCategory
-mm_common_get_sms_cdma_service_category_from_string (const gchar *str,
- GError **error)
+gboolean
+mm_common_band_is_gsm (MMModemBand band)
{
- GEnumClass *enum_class;
- guint i;
-
- enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_SMS_CDMA_SERVICE_CATEGORY));
+ return ((band >= MM_MODEM_BAND_EGSM && band <= MM_MODEM_BAND_G850) ||
+ (band >= MM_MODEM_BAND_G450 && band <= MM_MODEM_BAND_G810));
+}
- for (i = 0; enum_class->values[i].value_nick; i++) {
- if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
- return enum_class->values[i].value;
- }
+gboolean
+mm_common_band_is_utran (MMModemBand band)
+{
+ return ((band >= MM_MODEM_BAND_UTRAN_1 && band <= MM_MODEM_BAND_UTRAN_7) ||
+ (band >= MM_MODEM_BAND_UTRAN_10 && band <= MM_MODEM_BAND_UTRAN_32));
+}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMSmsCdmaServiceCategory value",
- str);
- return MM_SMS_CDMA_SERVICE_CATEGORY_UNKNOWN;
+gboolean
+mm_common_band_is_eutran (MMModemBand band)
+{
+ return (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_71);
}
-MMOmaFeature
-mm_common_get_oma_features_from_string (const gchar *str,
- GError **error)
+gboolean
+mm_common_band_is_cdma (MMModemBand band)
{
- GError *inner_error = NULL;
- MMOmaFeature features;
- gchar **feature_strings;
- GFlagsClass *flags_class;
+ return (band >= MM_MODEM_BAND_CDMA_BC0 && band <= MM_MODEM_BAND_CDMA_BC19);
+}
- features = MM_OMA_FEATURE_NONE;
+/******************************************************************************/
+/* MMOmaPendingNetworkInitiatedSession array management */
- flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_OMA_FEATURE));
- feature_strings = g_strsplit (str, "|", -1);
+GArray *
+mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant)
+{
+ GArray *array = NULL;
- if (feature_strings) {
- guint i;
+ if (variant) {
+ GVariantIter iter;
+ guint n;
- for (i = 0; feature_strings[i]; i++) {
- guint j;
- gboolean found = FALSE;
+ g_variant_iter_init (&iter, variant);
+ n = g_variant_iter_n_children (&iter);
- for (j = 0; flags_class->values[j].value_nick; j++) {
- if (!g_ascii_strcasecmp (feature_strings[i], flags_class->values[j].value_nick)) {
- features |= flags_class->values[j].value;
- found = TRUE;
- break;
- }
- }
+ if (n > 0) {
+ MMOmaPendingNetworkInitiatedSession session;
- if (!found) {
- inner_error = g_error_new (
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMOmaFeature value",
- feature_strings[i]);
- break;
- }
+ array = g_array_sized_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession), n);
+ while (g_variant_iter_loop (&iter, "(uu)", &session.session_type, &session.session_id))
+ g_array_append_val (array, session);
}
}
- if (inner_error) {
- g_propagate_error (error, inner_error);
- features = MM_OMA_FEATURE_NONE;
- }
+ /* If nothing set, fallback to empty */
+ if (!array)
+ array = g_array_new (FALSE, FALSE, sizeof (MMOmaPendingNetworkInitiatedSession));
- g_type_class_unref (flags_class);
- g_strfreev (feature_strings);
- return features;
+ return array;
}
-MMOmaSessionType
-mm_common_get_oma_session_type_from_string (const gchar *str,
- GError **error)
+GVariant *
+mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *sessions,
+ guint n_sessions)
{
- GEnumClass *enum_class;
- guint i;
+ if (n_sessions > 0) {
+ GVariantBuilder builder;
+ guint i;
- enum_class = G_ENUM_CLASS (g_type_class_ref (MM_TYPE_OMA_SESSION_TYPE));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
- for (i = 0; enum_class->values[i].value_nick; i++) {
- if (!g_ascii_strcasecmp (str, enum_class->values[i].value_nick))
- return enum_class->values[i].value;
+ for (i = 0; i < n_sessions; i++)
+ g_variant_builder_add_value (&builder,
+ g_variant_new ("(uu)",
+ ((guint32)sessions[i].session_type),
+ ((guint32)sessions[i].session_id)));
+ return g_variant_builder_end (&builder);
}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't match '%s' with a valid MMOmaSessionType value",
- str);
- return MM_OMA_SESSION_TYPE_UNKNOWN;
+ return mm_common_build_oma_pending_network_initiated_sessions_default ();
}
GVariant *
-mm_common_build_bands_unknown (void)
+mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array)
{
- GVariantBuilder builder;
+ if (array)
+ return mm_common_oma_pending_network_initiated_sessions_array_to_variant ((const MMOmaPendingNetworkInitiatedSession *)array->data,
+ array->len);
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 (MM_MODEM_BAND_UNKNOWN));
- return g_variant_builder_end (&builder);
+ return mm_common_oma_pending_network_initiated_sessions_array_to_variant (NULL, 0);
}
GVariant *
-mm_common_build_bands_any (void)
+mm_common_build_oma_pending_network_initiated_sessions_default (void)
{
GVariantBuilder builder;
- g_variant_builder_init (&builder, G_VARIANT_TYPE ("au"));
- g_variant_builder_add_value (&builder,
- g_variant_new_uint32 (MM_MODEM_BAND_ANY));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
return g_variant_builder_end (&builder);
}
+/******************************************************************************/
+/* Common parsers */
+
/* Expecting input as:
* key1=string,key2=true,key3=false...
* Strings may also be passed enclosed between double or single quotes, like:
@@ -1246,17 +1375,38 @@ mm_common_parse_key_value_string (const gchar *str,
gboolean
mm_get_int_from_str (const gchar *str,
- gint *out)
+ gint *out)
{
glong num;
+ guint i;
+ guint eol = 0;
- if (!str || !str[0])
+ if (!str)
return FALSE;
- for (num = 0; str[num]; num++) {
- if (str[num] != '+' && str[num] != '-' && !g_ascii_isdigit (str[num]))
+ /* ignore all leading whitespaces */
+ while (str[0] == ' ')
+ str++;
+
+ if (!str[0])
+ return FALSE;
+
+ for (i = 0; str[i]; i++) {
+ if (str[i] != '+' && str[i] != '-' && !g_ascii_isdigit (str[i])) {
+ /* ignore \r\n at the end of the string */
+ if ((str[i] == '\r') || (str[i] == '\n')) {
+ eol++;
+ continue;
+ }
+ return FALSE;
+ }
+ /* if eol found before a valid char, the string is not parseable */
+ if (eol)
return FALSE;
}
+ /* if all characters were eol, the string is not parseable */
+ if (eol == i)
+ return FALSE;
errno = 0;
num = strtol (str, NULL, 10);
@@ -1269,50 +1419,125 @@ mm_get_int_from_str (const gchar *str,
gboolean
mm_get_int_from_match_info (GMatchInfo *match_info,
- guint32 match_index,
- gint *out)
+ guint32 match_index,
+ gint *out)
{
- gchar *s;
- gboolean ret;
+ g_autofree gchar *s = NULL;
+
+ s = mm_get_string_unquoted_from_match_info (match_info, match_index);
+ return (s ? mm_get_int_from_str (s, out) : FALSE);
+}
- s = g_match_info_fetch (match_info, match_index);
- g_return_val_if_fail (s != NULL, FALSE);
+gboolean
+mm_get_uint_from_str (const gchar *str,
+ guint *out)
+{
+ guint64 num;
- ret = mm_get_int_from_str (s, out);
- g_free (s);
+ if (!mm_get_u64_from_str (str, &num) || num > G_MAXUINT)
+ return FALSE;
- return ret;
+ *out = (guint)num;
+ return TRUE;
}
-/**
- * mm_get_uint_from_str:
- * @str: the string to convert to an unsigned int
- * @out: on success, the number
- *
- * Converts a string to an unsigned number. All characters in the string
- * MUST be valid digits (0 - 9), otherwise FALSE is returned.
- *
- * Returns: %TRUE if the string was converted, %FALSE if it was not or if it
- * did not contain only digits.
- */
gboolean
-mm_get_uint_from_str (const gchar *str,
- guint *out)
+mm_get_u64_from_str (const gchar *str,
+ guint64 *out)
{
- gulong num;
+ guint64 num;
+ guint eol = 0;
- if (!str || !str[0])
+ if (!str)
+ return FALSE;
+
+ /* ignore all leading whitespaces */
+ while (str[0] == ' ')
+ str++;
+
+ if (!str[0])
return FALSE;
for (num = 0; str[num]; num++) {
- if (!g_ascii_isdigit (str[num]))
+ if (!g_ascii_isdigit (str[num])) {
+ /* ignore \r\n at the end of the string */
+ if ((str[num] == '\r') || (str[num] == '\n')) {
+ eol++;
+ continue;
+ }
+ return FALSE;
+ }
+ /* if eol found before a valid char, the string is not parseable */
+ if (eol)
return FALSE;
}
+ /* if all characters were eol, the string is not parseable */
+ if (eol == num)
+ return FALSE;
errno = 0;
- num = strtoul (str, NULL, 10);
- if (!errno && num <= G_MAXUINT) {
- *out = (guint)num;
+ num = (guint64) strtoull (str, NULL, 10);
+ if (!errno) {
+ *out = num;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean
+mm_get_uint_from_hex_str (const gchar *str,
+ guint *out)
+{
+ guint64 num;
+
+ if (!mm_get_u64_from_hex_str (str, &num) || num > G_MAXUINT)
+ return FALSE;
+
+ *out = (guint)num;
+ return TRUE;
+}
+
+gboolean
+mm_get_u64_from_hex_str (const gchar *str,
+ guint64 *out)
+{
+ guint64 num;
+ guint eol = 0;
+
+ if (!str)
+ return FALSE;
+
+ /* ignore all leading whitespaces */
+ while (str[0] == ' ')
+ str++;
+
+ if (g_str_has_prefix (str, "0x"))
+ str = &str[2];
+
+ if (!str[0])
+ return FALSE;
+
+ for (num = 0; str[num]; num++) {
+ if (!g_ascii_isxdigit (str[num])) {
+ /* ignore \r\n at the end of the string */
+ if ((str[num] == '\r') || (str[num] == '\n')) {
+ eol++;
+ continue;
+ }
+ return FALSE;
+ }
+ /* if eol found before a valid char, the string is not parseable */
+ if (eol)
+ return FALSE;
+ }
+ /* if all characters were eol, the string is not parseable */
+ if (eol == num)
+ return FALSE;
+
+ errno = 0;
+ num = (guint64) strtoull (str, NULL, 16);
+ if (!errno) {
+ *out = num;
return TRUE;
}
return FALSE;
@@ -1320,27 +1545,61 @@ mm_get_uint_from_str (const gchar *str,
gboolean
mm_get_uint_from_match_info (GMatchInfo *match_info,
- guint32 match_index,
- guint *out)
+ guint32 match_index,
+ guint *out)
{
- gchar *s;
- gboolean ret;
+ guint64 num;
+
+ if (!mm_get_u64_from_match_info (match_info, match_index, &num) || num > G_MAXUINT)
+ return FALSE;
- s = g_match_info_fetch (match_info, match_index);
- g_return_val_if_fail (s != NULL, FALSE);
+ *out = (guint)num;
+ return TRUE;
+}
- ret = mm_get_uint_from_str (s, out);
- g_free (s);
+gboolean
+mm_get_u64_from_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint64 *out)
+{
+ g_autofree gchar *s = NULL;
- return ret;
+ s = mm_get_string_unquoted_from_match_info (match_info, match_index);
+ return (s ? mm_get_u64_from_str (s, out) : FALSE);
+}
+
+gboolean
+mm_get_uint_from_hex_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint *out)
+{
+ guint64 num;
+
+ if (!mm_get_u64_from_hex_match_info (match_info, match_index, &num) || num > G_MAXUINT)
+ return FALSE;
+
+ *out = (guint)num;
+ return TRUE;
+}
+
+gboolean
+mm_get_u64_from_hex_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint64 *out)
+{
+ g_autofree gchar *s = NULL;
+
+ s = mm_get_string_unquoted_from_match_info (match_info, match_index);
+ return (s ? mm_get_u64_from_hex_str (s, out) : FALSE);
}
gboolean
mm_get_double_from_str (const gchar *str,
- gdouble *out)
+ gdouble *out)
{
- gdouble num;
- guint i;
+ gdouble num;
+ guint i;
+ guint eol = 0;
if (!str || !str[0])
return FALSE;
@@ -1348,14 +1607,24 @@ mm_get_double_from_str (const gchar *str,
for (i = 0; str[i]; i++) {
/* we don't really expect numbers in scientific notation, so
* don't bother looking for exponents and such */
- if (str[i] != '-' &&
- str[i] != '.' &&
- !g_ascii_isdigit (str[i]))
+ if ((str[i] != '-') && (str[i] != '.') && !g_ascii_isdigit (str[i])) {
+ /* ignore \r\n at the end of the string */
+ if ((str[i] == '\r') || (str[i] == '\n')) {
+ eol++;
+ continue;
+ }
+ return FALSE;
+ }
+ /* if eol found before a valid char, the string is not parseable */
+ if (eol)
return FALSE;
}
+ /* if all characters were eol, the string is not parseable */
+ if (eol == i)
+ return FALSE;
errno = 0;
- num = strtod (str, NULL);
+ num = g_ascii_strtod (str, NULL);
if (!errno) {
*out = num;
return TRUE;
@@ -1365,24 +1634,18 @@ mm_get_double_from_str (const gchar *str,
gboolean
mm_get_double_from_match_info (GMatchInfo *match_info,
- guint32 match_index,
- gdouble *out)
+ guint32 match_index,
+ gdouble *out)
{
- gchar *s;
- gboolean ret;
-
- s = g_match_info_fetch (match_info, match_index);
- g_return_val_if_fail (s != NULL, FALSE);
+ g_autofree gchar *s = NULL;
- ret = mm_get_double_from_str (s, out);
- g_free (s);
-
- return ret;
+ s = mm_get_string_unquoted_from_match_info (match_info, match_index);
+ return (s ? mm_get_double_from_str (s, out) : FALSE);
}
gchar *
mm_get_string_unquoted_from_match_info (GMatchInfo *match_info,
- guint32 match_index)
+ guint32 match_index)
{
gchar *str;
gsize len;
@@ -1408,48 +1671,70 @@ mm_get_string_unquoted_from_match_info (GMatchInfo *match_info,
return str;
}
-/*****************************************************************************/
+/*
+ * The following implementation is taken from glib g_date_time_format_iso8601 code
+ * https://gitlab.gnome.org/GNOME/glib/-/blob/main/glib/gdatetime.c#L3490
+ */
+static gchar *
+date_time_format_iso8601 (GDateTime *dt)
+{
+#if GLIB_CHECK_VERSION (2, 62, 0)
+ return g_date_time_format_iso8601 (dt);
+#else
+ GString *outstr = NULL;
+ g_autofree gchar *main_date = NULL;
+ gint64 offset = 0;
+
+ main_date = g_date_time_format (dt, "%Y-%m-%dT%H:%M:%S");
+ outstr = g_string_new (main_date);
+
+ /* Timezone. Format it as `%:::z` unless the offset is zero, in which case
+ * we can simply use `Z`. */
+ offset = g_date_time_get_utc_offset (dt);
+ if (offset == 0) {
+ g_string_append_c (outstr, 'Z');
+ } else {
+ g_autofree gchar *time_zone = NULL;
-const gchar *
-mm_sms_delivery_state_get_string_extended (guint delivery_state)
-{
- if (delivery_state > 0x02 && delivery_state < 0x20) {
- if (delivery_state < 0x10)
- return "completed-reason-reserved";
- else
- return "completed-sc-specific-reason";
+ time_zone = g_date_time_format (dt, "%:::z");
+ g_string_append (outstr, time_zone);
}
- if (delivery_state > 0x25 && delivery_state < 0x40) {
- if (delivery_state < 0x30)
- return "temporary-error-reason-reserved";
- else
- return "temporary-error-sc-specific-reason";
- }
+ return g_string_free (outstr, FALSE);
+#endif
+}
- if (delivery_state > 0x49 && delivery_state < 0x60) {
- if (delivery_state < 0x50)
- return "error-reason-reserved";
- else
- return "error-sc-specific-reason";
- }
+gchar *
+mm_new_iso8601_time_from_unix_time (guint64 timestamp)
+{
+ g_autoptr(GDateTime) dt = NULL;
- if (delivery_state > 0x65 && delivery_state < 0x80) {
- if (delivery_state < 0x70)
- return "temporary-fatal-error-reason-reserved";
- else
- return "temporary-fatal-error-sc-specific-reason";
- }
+ dt = g_date_time_new_from_unix_utc ((gint64)timestamp);
- if (delivery_state >= 0x80 && delivery_state < 0x100)
- return "unknown-reason-reserved";
+ return date_time_format_iso8601 (dt);
+}
- if (delivery_state >= 0x100)
- return "unknown";
+gchar *
+mm_new_iso8601_time (guint year,
+ guint month,
+ guint day,
+ guint hour,
+ guint minute,
+ guint second,
+ gboolean have_offset,
+ gint offset_minutes)
+{
+ g_autoptr(GDateTime) dt = NULL;
+
+ if (have_offset) {
+ g_autoptr(GTimeZone) tz = NULL;
+
+ tz = g_time_zone_new_offset (offset_minutes * 60);
+ dt = g_date_time_new (tz, year, month, day, hour, minute, second);
+ } else
+ dt = g_date_time_new_utc (year, month, day, hour, minute, second);
- /* Otherwise, use the MMSmsDeliveryState enum as we can match the known
- * value */
- return mm_sms_delivery_state_get_string ((MMSmsDeliveryState)delivery_state);
+ return date_time_format_iso8601 (dt);
}
/*****************************************************************************/
@@ -1482,33 +1767,48 @@ mm_utils_hex2byte (const gchar *hex)
return (a << 4) | b;
}
-gchar *
-mm_utils_hexstr2bin (const gchar *hex, gsize *out_len)
+guint8 *
+mm_utils_hexstr2bin (const gchar *hex,
+ gssize len,
+ gsize *out_len,
+ GError **error)
{
const gchar *ipos = hex;
- gchar *buf = NULL;
- gsize i;
+ g_autofree guint8 *buf = NULL;
+ gssize i;
gint a;
- gchar *opos;
- gsize len;
+ guint8 *opos;
- len = strlen (hex);
+ if (len < 0)
+ len = strlen (hex);
+
+ if (len == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Hex conversion failed: empty string");
+ return NULL;
+ }
/* Length must be a multiple of 2 */
- g_return_val_if_fail ((len % 2) == 0, NULL);
+ if ((len % 2) != 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Hex conversion failed: invalid input length");
+ return NULL;
+ }
- opos = buf = g_malloc0 ((len / 2) + 1);
+ opos = buf = g_malloc0 (len / 2);
for (i = 0; i < len; i += 2) {
a = mm_utils_hex2byte (ipos);
if (a < 0) {
- g_free (buf);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Hex byte conversion from '%c%c' failed",
+ ipos[0], ipos[1]);
return NULL;
}
- *opos++ = a;
+ *opos++ = (guint8)a;
ipos += 2;
}
*out_len = len / 2;
- return buf;
+ return g_steal_pointer (&buf);
}
/* End from hostap */
@@ -1519,9 +1819,9 @@ mm_utils_ishexstr (const gchar *hex)
gsize len;
gsize i;
- /* Length not multiple of 2? */
+ /* Empty string or length not multiple of 2? */
len = strlen (hex);
- if (len % 2 != 0)
+ if (len == 0 || (len % 2) != 0)
return FALSE;
for (i = 0; i < len; i++) {
@@ -1539,7 +1839,8 @@ mm_utils_ishexstr (const gchar *hex)
}
gchar *
-mm_utils_bin2hexstr (const guint8 *bin, gsize len)
+mm_utils_bin2hexstr (const guint8 *bin,
+ gsize len)
{
GString *ret;
gsize i;
@@ -1569,3 +1870,141 @@ mm_utils_check_for_single_value (guint32 value)
return TRUE;
}
+
+/*****************************************************************************/
+
+gboolean
+mm_is_string_mccmnc (const gchar *str)
+{
+ gsize len;
+ guint i;
+
+ if (!str)
+ return FALSE;
+
+ len = strlen (str);
+ if (len < 5 || len > 6)
+ return FALSE;
+
+ for (i = 0; i < len; i++)
+ if (str[i] < '0' || str[i] > '9')
+ return FALSE;
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+const gchar *
+mm_sms_delivery_state_get_string_extended (guint delivery_state)
+{
+ if (delivery_state > 0x02 && delivery_state < 0x20) {
+ if (delivery_state < 0x10)
+ return "completed-reason-reserved";
+ else
+ return "completed-sc-specific-reason";
+ }
+
+ if (delivery_state > 0x25 && delivery_state < 0x40) {
+ if (delivery_state < 0x30)
+ return "temporary-error-reason-reserved";
+ else
+ return "temporary-error-sc-specific-reason";
+ }
+
+ if (delivery_state > 0x49 && delivery_state < 0x60) {
+ if (delivery_state < 0x50)
+ return "error-reason-reserved";
+ else
+ return "error-sc-specific-reason";
+ }
+
+ if (delivery_state > 0x65 && delivery_state < 0x80) {
+ if (delivery_state < 0x70)
+ return "temporary-fatal-error-reason-reserved";
+ else
+ return "temporary-fatal-error-sc-specific-reason";
+ }
+
+ if (delivery_state >= 0x80 && delivery_state < 0x100)
+ return "unknown-reason-reserved";
+
+ if (delivery_state >= 0x100)
+ return "unknown";
+
+ /* Otherwise, use the MMSmsDeliveryState enum as we can match the known
+ * value */
+ return mm_sms_delivery_state_get_string ((MMSmsDeliveryState)delivery_state);
+}
+
+/*****************************************************************************/
+/* DBus error handling */
+
+gboolean
+mm_common_register_errors (void)
+{
+ static volatile guint32 aux = 0;
+
+ if (G_LIKELY (aux))
+ return FALSE;
+
+ /* Register all known own errors */
+ aux |= MM_CORE_ERROR;
+ aux |= MM_MOBILE_EQUIPMENT_ERROR;
+ aux |= MM_CONNECTION_ERROR;
+ aux |= MM_SERIAL_ERROR;
+ aux |= MM_MESSAGE_ERROR;
+ aux |= MM_CDMA_ACTIVATION_ERROR;
+
+ return TRUE;
+}
+
+GError *
+mm_common_error_from_tuple (GVariant *tuple,
+ GError **error)
+{
+ g_autoptr(GError) dbus_error = NULL;
+ g_autofree gchar *error_name = NULL;
+ g_autofree gchar *error_message = NULL;
+
+ mm_common_register_errors ();
+
+ if (!g_variant_is_of_type (tuple, G_VARIANT_TYPE ("(ss)"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create error from tuple: "
+ "invalid variant type received");
+ return NULL;
+ }
+
+ g_variant_get (tuple, "(ss)", &error_name, &error_message);
+ if (!error_name || !error_name[0])
+ return NULL;
+
+ /* We convert the error name into a proper GError (domain+code), but we
+ * don't attempt to give the error message to new_for_dbus_error() as that
+ * would generate a string we don't want (e.g. instead of just "Unknown
+ * Error" we would get "GDBus.Error:org.freedesktop.ModemManager1.Error.MobileEquipment.Unknown: Unknown error"
+ */
+ dbus_error = g_dbus_error_new_for_dbus_error (error_name, "");
+
+ /* And now we build a new GError with same domain+code but with the received
+ * error message */
+ return g_error_new (dbus_error->domain, dbus_error->code, "%s", error_message);
+}
+
+GVariant *
+mm_common_error_to_tuple (const GError *error)
+{
+ g_autofree gchar *error_name = NULL;
+ GVariant *tuple[2];
+
+ mm_common_register_errors ();
+
+ error_name = g_dbus_error_encode_gerror (error);
+ tuple[0] = g_variant_new_string (error_name);
+ tuple[1] = g_variant_new_string (error->message);
+
+ return g_variant_ref_sink (g_variant_new_tuple (tuple, 2));
+}
diff --git a/libmm-glib/mm-common-helpers.h b/libmm-glib/mm-common-helpers.h
index b05a4a92..7ad8c6ad 100644
--- a/libmm-glib/mm-common-helpers.h
+++ b/libmm-glib/mm-common-helpers.h
@@ -26,133 +26,202 @@
#ifndef MM_COMMON_HELPERS_H
#define MM_COMMON_HELPERS_H
-gchar *mm_common_build_capabilities_string (const MMModemCapability *capabilities,
- guint n_capabilities);
-
-gchar *mm_common_build_bands_string (const MMModemBand *bands,
- guint n_bands);
-
-gchar *mm_common_build_ports_string (const MMModemPortInfo *ports,
- guint n_ports);
-
-gchar *mm_common_build_sms_storages_string (const MMSmsStorage *storages,
- guint n_storages);
-
+/******************************************************************************/
+/* Enums/flags to string builders */
+
+gchar *mm_common_build_capabilities_string (const MMModemCapability *capabilities,
+ guint n_capabilities);
+gchar *mm_common_build_bands_string (const MMModemBand *bands,
+ guint n_bands);
+gchar *mm_common_build_ports_string (const MMModemPortInfo *ports,
+ guint n_ports);
+gchar *mm_common_build_sms_storages_string (const MMSmsStorage *storages,
+ guint n_storages);
gchar *mm_common_build_mode_combinations_string (const MMModemModeCombination *modes,
- guint n_modes);
-
-MMModemCapability mm_common_get_capabilities_from_string (const gchar *str,
- GError **error);
-MMModemMode mm_common_get_modes_from_string (const gchar *str,
- GError **error);
-void mm_common_get_bands_from_string (const gchar *str,
- MMModemBand **bands,
- guint *n_bands,
- GError **error);
-gboolean mm_common_get_boolean_from_string (const gchar *value,
- GError **error);
-MMModemCdmaRmProtocol mm_common_get_rm_protocol_from_string (const gchar *str,
- GError **error);
-MMBearerIpFamily mm_common_get_ip_type_from_string (const gchar *str,
- GError **error);
-MMBearerAllowedAuth mm_common_get_allowed_auth_from_string (const gchar *str,
- GError **error);
-MMSmsStorage mm_common_get_sms_storage_from_string (const gchar *str,
- GError **error);
-MMSmsCdmaTeleserviceId mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str,
- GError **error);
-MMSmsCdmaServiceCategory mm_common_get_sms_cdma_service_category_from_string (const gchar *str,
- GError **error);
-MMOmaFeature mm_common_get_oma_features_from_string (const gchar *str,
- GError **error);
-MMOmaSessionType mm_common_get_oma_session_type_from_string (const gchar *str,
- GError **error);
-
-GArray *mm_common_ports_variant_to_garray (GVariant *variant);
-MMModemPortInfo *mm_common_ports_variant_to_array (GVariant *variant,
- guint *n_ports);
-GVariant *mm_common_ports_array_to_variant (const MMModemPortInfo *ports,
- guint n_ports);
-GVariant *mm_common_ports_garray_to_variant (GArray *array);
-
-GArray *mm_common_sms_storages_variant_to_garray (GVariant *variant);
-MMSmsStorage *mm_common_sms_storages_variant_to_array (GVariant *variant,
- guint *n_storages);
-GVariant *mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages,
- guint n_storages);
-GVariant *mm_common_sms_storages_garray_to_variant (GArray *array);
-
-GArray *mm_common_bands_variant_to_garray (GVariant *variant);
-MMModemBand *mm_common_bands_variant_to_array (GVariant *variant,
- guint *n_bands);
-GVariant *mm_common_bands_array_to_variant (const MMModemBand *bands,
- guint n_bands);
-GVariant *mm_common_bands_garray_to_variant (GArray *array);
-
-GVariant *mm_common_build_bands_any (void);
-GVariant *mm_common_build_bands_unknown (void);
-
-gboolean mm_common_bands_garray_cmp (GArray *a, GArray *b);
-
-GArray *mm_common_mode_combinations_variant_to_garray (GVariant *variant);
-MMModemModeCombination *mm_common_mode_combinations_variant_to_array (GVariant *variant,
- guint *n_modes);
-GVariant *mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes,
- guint n_modes);
-GVariant *mm_common_mode_combinations_garray_to_variant (GArray *array);
-GVariant *mm_common_build_mode_combinations_default (void);
-
-GArray *mm_common_capability_combinations_variant_to_garray (GVariant *variant);
-MMModemCapability *mm_common_capability_combinations_variant_to_array (GVariant *variant,
- guint *n_capabilities);
-GVariant *mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities,
- guint n_capabilities);
-GVariant *mm_common_capability_combinations_garray_to_variant (GArray *array);
-GVariant *mm_common_build_capability_combinations_any (void);
-GVariant *mm_common_build_capability_combinations_none (void);
-
-GArray *mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant);
-MMOmaPendingNetworkInitiatedSession *mm_common_oma_pending_network_initiated_sessions_variant_to_array (GVariant *variant,
- guint *n_modes);
-GVariant *mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *modes,
- guint n_modes);
-GVariant *mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array);
-GVariant *mm_common_build_oma_pending_network_initiated_sessions_default (void);
-
-typedef gboolean (*MMParseKeyValueForeachFn) (const gchar *key,
- const gchar *value,
- gpointer user_data);
-gboolean mm_common_parse_key_value_string (const gchar *str,
- GError **error,
- MMParseKeyValueForeachFn callback,
- gpointer user_data);
-
+ guint n_modes);
+
+/******************************************************************************/
+/* String to enums/flags parsers */
+
+MMModemCapability mm_common_get_capabilities_from_string (const gchar *str,
+ GError **error);
+MMModemMode mm_common_get_modes_from_string (const gchar *str,
+ GError **error);
+gboolean mm_common_get_bands_from_string (const gchar *str,
+ MMModemBand **bands,
+ guint *n_bands,
+ GError **error);
+gboolean mm_common_get_boolean_from_string (const gchar *value,
+ GError **error);
+MMModemCdmaRmProtocol mm_common_get_rm_protocol_from_string (const gchar *str,
+ GError **error);
+MMBearerIpFamily mm_common_get_ip_type_from_string (const gchar *str,
+ GError **error);
+MMBearerAllowedAuth mm_common_get_allowed_auth_from_string (const gchar *str,
+ GError **error);
+MMSmsStorage mm_common_get_sms_storage_from_string (const gchar *str,
+ GError **error);
+MMSmsCdmaTeleserviceId mm_common_get_sms_cdma_teleservice_id_from_string (const gchar *str,
+ GError **error);
+MMSmsCdmaServiceCategory mm_common_get_sms_cdma_service_category_from_string (const gchar *str,
+ GError **error);
+MMCallDirection mm_common_get_call_direction_from_string (const gchar *str,
+ GError **error);
+MMCallState mm_common_get_call_state_from_string (const gchar *str,
+ GError **error);
+MMCallStateReason mm_common_get_call_state_reason_from_string (const gchar *str,
+ GError **error);
+MMOmaFeature mm_common_get_oma_features_from_string (const gchar *str,
+ GError **error);
+MMOmaSessionType mm_common_get_oma_session_type_from_string (const gchar *str,
+ GError **error);
+MMModem3gppEpsUeModeOperation mm_common_get_eps_ue_mode_operation_from_string (const gchar *str,
+ GError **error);
+MMModemAccessTechnology mm_common_get_access_technology_from_string (const gchar *str,
+ GError **error);
+MMBearerMultiplexSupport mm_common_get_multiplex_support_from_string (const gchar *str,
+ GError **error);
+MMBearerApnType mm_common_get_apn_type_from_string (const gchar *str,
+ GError **error);
+MMModem3gppFacility mm_common_get_3gpp_facility_from_string (const gchar *str,
+ GError **error);
+MMModem3gppPacketServiceState mm_common_get_3gpp_packet_service_state_from_string (const gchar *str,
+ GError **error);
+
+/******************************************************************************/
+
+/* MMModemPortInfo array management */
+
+GArray *mm_common_ports_variant_to_garray (GVariant *variant);
+GVariant *mm_common_ports_array_to_variant (const MMModemPortInfo *ports,
+ guint n_ports);
+GVariant *mm_common_ports_garray_to_variant (GArray *array);
+gboolean mm_common_ports_garray_to_array (GArray *array,
+ MMModemPortInfo **ports,
+ guint *n_ports);
+
+/* MMSmsStorage array management */
+
+GArray *mm_common_sms_storages_variant_to_garray (GVariant *variant);
+GVariant *mm_common_sms_storages_array_to_variant (const MMSmsStorage *storages,
+ guint n_storages);
+GVariant *mm_common_sms_storages_garray_to_variant (GArray *array);
+
+/* MMModemCapability array management */
+
+GArray *mm_common_capability_combinations_variant_to_garray (GVariant *variant);
+GVariant *mm_common_capability_combinations_array_to_variant (const MMModemCapability *capabilities,
+ guint n_capabilities);
+GVariant *mm_common_capability_combinations_garray_to_variant (GArray *array);
+GVariant *mm_common_build_capability_combinations_none (void);
+
+/* MMModemModeCombination array management */
+
+GArray *mm_common_mode_combinations_variant_to_garray (GVariant *variant);
+GVariant *mm_common_mode_combinations_array_to_variant (const MMModemModeCombination *modes,
+ guint n_modes);
+GVariant *mm_common_mode_combinations_garray_to_variant (GArray *array);
+GVariant *mm_common_build_mode_combinations_default (void);
+
+/* MMModemBand array management */
+
+GArray *mm_common_bands_variant_to_garray (GVariant *variant);
+GVariant *mm_common_bands_array_to_variant (const MMModemBand *bands,
+ guint n_bands);
+GVariant *mm_common_bands_garray_to_variant (GArray *array);
+GVariant *mm_common_build_bands_any (void);
+GVariant *mm_common_build_bands_unknown (void);
+gboolean mm_common_bands_garray_cmp (GArray *a,
+ GArray *b);
+void mm_common_bands_garray_sort (GArray *array);
+gboolean mm_common_bands_garray_lookup (GArray *array,
+ MMModemBand value);
+gboolean mm_common_band_is_gsm (MMModemBand band);
+gboolean mm_common_band_is_utran (MMModemBand band);
+gboolean mm_common_band_is_eutran (MMModemBand band);
+gboolean mm_common_band_is_cdma (MMModemBand band);
+
+/* MMOmaPendingNetworkInitiatedSession array management */
+
+GArray *mm_common_oma_pending_network_initiated_sessions_variant_to_garray (GVariant *variant);
+GVariant *mm_common_oma_pending_network_initiated_sessions_array_to_variant (const MMOmaPendingNetworkInitiatedSession *modes,
+ guint n_modes);
+GVariant *mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array);
+GVariant *mm_common_build_oma_pending_network_initiated_sessions_default (void);
+
+/******************************************************************************/
/* Common parsers */
+
+typedef gboolean (* MMParseKeyValueForeachFn) (const gchar *key,
+ const gchar *value,
+ gpointer user_data);
+gboolean mm_common_parse_key_value_string (const gchar *str,
+ GError **error,
+ MMParseKeyValueForeachFn callback,
+ gpointer user_data);
+
gboolean mm_get_int_from_str (const gchar *str,
- gint *out);
-gboolean mm_get_int_from_match_info (GMatchInfo *match_info,
- guint32 match_index,
- gint *out);
+ gint *out);
+gboolean mm_get_int_from_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ gint *out);
gboolean mm_get_uint_from_str (const gchar *str,
- guint *out);
-gboolean mm_get_uint_from_match_info (GMatchInfo *match_info,
- guint32 match_index,
- guint *out);
+ guint *out);
+gboolean mm_get_u64_from_str (const gchar *str,
+ guint64 *out);
+gboolean mm_get_uint_from_hex_str (const gchar *str,
+ guint *out);
+gboolean mm_get_u64_from_hex_str (const gchar *str,
+ guint64 *out);
+gboolean mm_get_uint_from_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint *out);
+gboolean mm_get_u64_from_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint64 *out);
+gboolean mm_get_uint_from_hex_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint *out);
+gboolean mm_get_u64_from_hex_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ guint64 *out);
gboolean mm_get_double_from_str (const gchar *str,
- gdouble *out);
-gboolean mm_get_double_from_match_info (GMatchInfo *match_info,
- guint32 match_index,
- gdouble *out);
-gchar *mm_get_string_unquoted_from_match_info (GMatchInfo *match_info,
- guint32 match_index);
-
-const gchar *mm_sms_delivery_state_get_string_extended (guint delivery_state);
+ gdouble *out);
+gboolean mm_get_double_from_match_info (GMatchInfo *match_info,
+ guint32 match_index,
+ gdouble *out);
+gchar *mm_get_string_unquoted_from_match_info (GMatchInfo *match_info,
+ guint32 match_index);
+
+gchar *mm_new_iso8601_time_from_unix_time (guint64 timestamp);
+gchar *mm_new_iso8601_time (guint year,
+ guint month,
+ guint day,
+ guint hour,
+ guint minute,
+ guint second,
+ gboolean have_offset,
+ gint offset_minutes);
+
+/******************************************************************************/
+/* Type checkers and conversion utilities */
gint mm_utils_hex2byte (const gchar *hex);
-gchar *mm_utils_hexstr2bin (const gchar *hex, gsize *out_len);
+guint8 *mm_utils_hexstr2bin (const gchar *hex, gssize len, gsize *out_len, GError **error);
gchar *mm_utils_bin2hexstr (const guint8 *bin, gsize len);
gboolean mm_utils_ishexstr (const gchar *hex);
gboolean mm_utils_check_for_single_value (guint32 value);
+gboolean mm_is_string_mccmnc (const gchar *str);
+
+const gchar *mm_sms_delivery_state_get_string_extended (guint delivery_state);
+
+/******************************************************************************/
+/* DBus error handling */
+gboolean mm_common_register_errors (void);
+GError *mm_common_error_from_tuple (GVariant *tuple,
+ GError **error);
+GVariant *mm_common_error_to_tuple (const GError *error);
+
#endif /* MM_COMMON_HELPERS_H */
diff --git a/libmm-glib/mm-compat.c b/libmm-glib/mm-compat.c
new file mode 100644
index 00000000..7a5ce216
--- /dev/null
+++ b/libmm-glib/mm-compat.c
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <gio/gio.h>
+
+#include "mm-compat.h"
+
+#ifndef MM_DISABLE_DEPRECATED
+
+/*****************************************************************************/
+
+void
+mm_simple_connect_properties_set_number (MMSimpleConnectProperties *self,
+ const gchar *number)
+{
+ /* NO-OP */
+}
+
+const gchar *
+mm_simple_connect_properties_get_number (MMSimpleConnectProperties *self)
+{
+ /* NO-OP */
+ return NULL;
+}
+
+/*****************************************************************************/
+
+void
+mm_bearer_properties_set_number (MMBearerProperties *self,
+ const gchar *number)
+{
+ /* NO-OP */
+}
+
+const gchar *
+mm_bearer_properties_get_number (MMBearerProperties *self)
+{
+ /* NO-OP */
+ return NULL;
+}
+
+/*****************************************************************************/
+
+void
+mm_call_properties_set_direction (MMCallProperties *self,
+ MMCallDirection direction)
+{
+ /* NO-OP */
+}
+
+MMCallDirection
+mm_call_properties_get_direction (MMCallProperties *self)
+{
+ /* NO-OP */
+ return MM_CALL_DIRECTION_UNKNOWN;
+}
+
+
+void
+mm_call_properties_set_state (MMCallProperties *self,
+ MMCallState state)
+{
+ /* NO-OP */
+}
+
+
+MMCallState
+mm_call_properties_get_state (MMCallProperties *self)
+{
+ /* NO-OP */
+ return MM_CALL_STATE_UNKNOWN;
+}
+
+void
+mm_call_properties_set_state_reason (MMCallProperties *self,
+ MMCallStateReason state_reason)
+{
+ /* NO-OP */
+}
+
+MMCallStateReason
+mm_call_properties_get_state_reason (MMCallProperties *self)
+{
+ /* NO-OP */
+ return MM_CALL_STATE_REASON_UNKNOWN;
+}
+
+/*****************************************************************************/
+
+gchar *
+mm_location_gps_nmea_build_full (MMLocationGpsNmea *self)
+{
+ g_auto(GStrv) traces = NULL;
+
+ traces = mm_location_gps_nmea_get_traces (self);
+ return (traces ? g_strjoinv ("\r\n", traces) : g_strdup (""));
+}
+
+/*****************************************************************************/
+
+guint
+mm_location_3gpp_get_mobile_network_code (MMLocation3gpp *self)
+{
+ const gchar *operator_code;
+
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0);
+
+ operator_code = mm_location_3gpp_get_operator_code (self);
+ if (!operator_code)
+ return 0;
+ return strtol (operator_code + 3, NULL, 10);
+}
+
+/*****************************************************************************/
+
+void
+mm_pco_list_free (GList *pco_list)
+{
+ g_list_free_full (pco_list, g_object_unref);
+}
+
+/*****************************************************************************/
+
+MMModem3gppSubscriptionState
+mm_simple_status_get_3gpp_subscription_state (MMSimpleStatus *self)
+{
+ return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN;
+}
+
+/*****************************************************************************/
+
+MMModem3gppSubscriptionState
+mm_modem_3gpp_get_subscription_state (MMModem3gpp *self)
+{
+ return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_modem_get_pending_network_initiated_sessions (MMModemOma *self,
+ MMOmaPendingNetworkInitiatedSession **sessions,
+ guint *n_sessions)
+{
+ return mm_modem_oma_get_pending_network_initiated_sessions (self, sessions, n_sessions);
+}
+
+gboolean
+mm_modem_peek_pending_network_initiated_sessions (MMModemOma *self,
+ const MMOmaPendingNetworkInitiatedSession **sessions,
+ guint *n_sessions)
+{
+ return mm_modem_oma_peek_pending_network_initiated_sessions (self, sessions, n_sessions);
+}
+
+/*****************************************************************************/
+
+guint
+mm_modem_get_max_bearers (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), 0);
+
+ return mm_gdbus_modem_get_max_bearers (MM_GDBUS_MODEM (self));
+}
+
+#endif /* MM_DISABLE_DEPRECATED */
diff --git a/libmm-glib/mm-compat.h b/libmm-glib/mm-compat.h
new file mode 100644
index 00000000..e7b6e8bd
--- /dev/null
+++ b/libmm-glib/mm-compat.h
@@ -0,0 +1,385 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef _MM_COMPAT_H_
+#define _MM_COMPAT_H_
+
+#ifndef MM_DISABLE_DEPRECATED
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include "mm-simple-connect-properties.h"
+#include "mm-bearer-properties.h"
+#include "mm-call-properties.h"
+#include "mm-location-gps-nmea.h"
+#include "mm-location-3gpp.h"
+#include "mm-pco.h"
+#include "mm-simple-status.h"
+#include "mm-modem-3gpp.h"
+#include "mm-modem-oma.h"
+#include "mm-modem.h"
+
+/**
+ * SECTION:mm-compat
+ * @short_description: Deprecated types and methods.
+ *
+ * These types and methods are flagged as deprecated and therefore
+ * shouldn't be used in newly written code. They are provided to avoid
+ * innecessary API/ABI breaks, for compatibility purposes only.
+ */
+
+/*****************************************************************************/
+
+/**
+ * mm_simple_connect_properties_set_number:
+ * @self: a #MMSimpleConnectProperties.
+ * @number: the number.
+ *
+ * Sets the number to use when performing the connection.
+ *
+ * Since: 1.0
+ * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore
+ * it doesn't make sense to expose it in the ModemManager interface.
+ */
+G_DEPRECATED
+void mm_simple_connect_properties_set_number (MMSimpleConnectProperties *self,
+ const gchar *number);
+
+/**
+ * mm_simple_connect_properties_get_number:
+ * @self: a #MMSimpleConnectProperties.
+ *
+ * Gets the number to use when performing the connection.
+ *
+ * Returns: (transfer none): the number, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
+ * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore
+ * it doesn't make sense to expose it in the ModemManager interface.
+ */
+G_DEPRECATED
+const gchar *mm_simple_connect_properties_get_number (MMSimpleConnectProperties *self);
+
+/*****************************************************************************/
+
+/**
+ * mm_bearer_properties_set_number:
+ * @self: a #MMBearerProperties.
+ * @number: the number.
+ *
+ * Sets the number to use when performing the connection.
+ *
+ * Since: 1.0
+ * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore
+ * it doesn't make sense to expose it in the ModemManager interface.
+ */
+G_DEPRECATED
+void mm_bearer_properties_set_number (MMBearerProperties *self,
+ const gchar *number);
+
+/**
+ * mm_bearer_properties_get_number:
+ * @self: a #MMBearerProperties.
+ *
+ * Gets the number to use when performing the connection.
+ *
+ * Returns: (transfer none): the number, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
+ * Deprecated: 1.10.0. The number setting is not used anywhere, and therefore
+ * it doesn't make sense to expose it in the ModemManager interface.
+ */
+G_DEPRECATED
+const gchar *mm_bearer_properties_get_number (MMBearerProperties *self);
+
+/*****************************************************************************/
+
+/**
+ * mm_call_properties_set_direction:
+ * @self: A #MMCallProperties.
+ * @direction: the call direction
+ *
+ * Sets the call direction.
+ *
+ * Since: 1.6
+ * Deprecated: 1.12: the user should not specify the direction of the call, as
+ * it is implicit (outgoing always). Anyway, this parameter has always been
+ * ignored during the new call creation processing.
+ */
+G_DEPRECATED
+void mm_call_properties_set_direction (MMCallProperties *self,
+ MMCallDirection direction);
+
+/**
+ * mm_call_properties_set_state_reason:
+ * @self: A #MMCallProperties.
+ * @state_reason: the call state reason.
+ *
+ * Sets the call state reason.
+ *
+ * Since: 1.6
+ * Deprecated: 1.12: the user should not specify the state reason of the call
+ * before the call is created. This parameter has always been ignored during the
+ * new call creation processing.
+ */
+G_DEPRECATED
+void mm_call_properties_set_state_reason (MMCallProperties *self,
+ MMCallStateReason state_reason);
+
+/**
+ * mm_call_properties_set_state:
+ * @self: A #MMCallProperties.
+ * @state: the call state
+ *
+ * Sets the call state
+ *
+ * Since: 1.6
+ * Deprecated: 1.12: the user should not specify the state of the call before
+ * the call is created. This parameter has always been ignored during the new
+ * call creation processing.
+ */
+G_DEPRECATED
+void mm_call_properties_set_state (MMCallProperties *self,
+ MMCallState state);
+
+/**
+ * mm_call_properties_get_direction:
+ * @self: A #MMCallProperties.
+ *
+ * Gets the call direction.
+ *
+ * Returns: the call direction.
+ *
+ * Since: 1.6
+ * Deprecated: 1.12: the user should not specify the direction of the call, as
+ * it is implicit (outgoing always). This parameter has always been ignored
+ * during the new call creation processing.
+ */
+G_DEPRECATED
+MMCallDirection mm_call_properties_get_direction (MMCallProperties *self);
+
+/**
+ * mm_call_properties_get_state_reason:
+ * @self: A #MMCallProperties.
+ *
+ * Gets the call state reason.
+ *
+ * Returns: the call state reason.
+ *
+ * Since: 1.6
+ * Deprecated: 1.12: the user should not specify the state reason of the call
+ * before the call is created. This parameter has always been ignored during the
+ * new call creation processing.
+ */
+G_DEPRECATED
+MMCallStateReason mm_call_properties_get_state_reason (MMCallProperties *self);
+
+/**
+ * mm_call_properties_get_state:
+ * @self: A #MMCallProperties.
+ *
+ * Gets the call state.
+ *
+ * Returns: the call state.
+ *
+ * Since: 1.6
+ * Deprecated: 1.12: the user should not specify the state of the call before
+ * the call is created. This parameter has always been ignored during the new
+ * call creation processing.
+ */
+G_DEPRECATED
+MMCallState mm_call_properties_get_state (MMCallProperties *self);
+
+/*****************************************************************************/
+/**
+ * mm_location_3gpp_get_mobile_network_code:
+ * @self: a #MMLocation3gpp.
+ *
+ * Gets the Mobile Network Code of the 3GPP network.
+ *
+ * Note that 0 may actually be a valid MNC. In general, the MNC should be
+ * considered valid just if the reported MCC is valid, as MCC should never
+ * be 0.
+ *
+ * Returns: the MNC, or 0 if unknown.
+ *
+ * Since: 1.0
+ * Deprecated: 1.18.0. This function can not separate between two-digit MNCs
+ * and three-digit MNCs with a leading zero. Use mm_location_3gpp_get_operator_code()
+ * instead.
+ */
+G_DEPRECATED
+guint mm_location_3gpp_get_mobile_network_code (MMLocation3gpp *self);
+
+/*****************************************************************************/
+
+/**
+ * mm_location_gps_nmea_build_full:
+ * @self: a #MMLocationGpsNmea.
+ *
+ * Gets a compilation of all cached traces, in a single string.
+ * Traces are separated by '\r\n'.
+ *
+ * Returns: (transfer full): a string containing all traces, or #NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
+ * Deprecated: 1.14: user should use mm_location_gps_nmea_get_traces() instead,
+ * which provides a much more generic interface to the full list of traces.
+ */
+G_DEPRECATED_FOR(mm_location_gps_nmea_get_traces)
+gchar *mm_location_gps_nmea_build_full (MMLocationGpsNmea *self);
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_list_free:
+ * @pco_list: (transfer full)(element-type ModemManager.Pco): a #GList of
+ * #MMPco.
+ *
+ * Frees all of the memory used by a #GList of #MMPco.
+ *
+ * Since: 1.10
+ * Deprecated: 1.12.0: Use g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify function instead.
+ */
+G_DEPRECATED
+void mm_pco_list_free (GList *pco_list);
+
+/*****************************************************************************/
+
+/**
+ * mm_simple_status_get_3gpp_subscription_state:
+ * @self: a #MMSimpleStatus.
+ *
+ * Gets the current subscription status of the account.
+ *
+ * Returns: a #MMModem3gppSubscriptionState.
+ *
+ * Since: 1.0
+ * Deprecated: 1.12.0. The value of this property can only be obtained with
+ * operator specific logic (e.g. processing specific PCO info), and therefore
+ * it doesn't make sense to expose it in the ModemManager interface.
+ */
+MMModem3gppSubscriptionState mm_simple_status_get_3gpp_subscription_state (MMSimpleStatus *self);
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_get_subscription_state:
+ * @self: A #MMModem.
+ *
+ * Get the current subscription status of the account. This value is only
+ * available after the modem attempts to register with the network.
+ *
+ * The value of this property can only be obtained with operator specific logic
+ * (e.g. processing specific PCO info), and therefore it doesn't make sense to
+ * expose it in the ModemManager interface.
+ *
+ * Returns: A #MMModem3gppSubscriptionState value, specifying the current
+ * subscription state.
+ *
+ * Since: 1.0
+ * Deprecated: 1.10.0. The value of this property can only be obtained with
+ * operator specific logic (e.g. processing specific PCO info), and therefore
+ * it doesn't make sense to expose it in the ModemManager interface.
+ */
+G_DEPRECATED
+MMModem3gppSubscriptionState mm_modem_3gpp_get_subscription_state (MMModem3gpp *self);
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_get_pending_network_initiated_sessions:
+ * @self: A #MMModem.
+ * @sessions: (out) (array length=n_sessions): Return location for the array of
+ * #MMOmaPendingNetworkInitiatedSession structs. The returned array should be
+ * freed with g_free() when no longer needed.
+ * @n_sessions: (out): Return location for the number of values in @sessions.
+ *
+ * Gets the list of pending network-initiated OMA sessions.
+ *
+ * Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise.
+ *
+ * Since: 1.2
+ * Deprecated: 1.18: Use mm_modem_oma_get_pending_network_initiated_sessions() instead.
+ */
+G_DEPRECATED_FOR (mm_modem_oma_get_pending_network_initiated_sessions)
+gboolean mm_modem_get_pending_network_initiated_sessions (MMModemOma *self,
+ MMOmaPendingNetworkInitiatedSession **sessions,
+ guint *n_sessions);
+
+/**
+ * mm_modem_peek_pending_network_initiated_sessions:
+ * @self: A #MMModem.
+ * @sessions: (out) (array length=n_sessions): Return location for the array of
+ * #MMOmaPendingNetworkInitiatedSession values. Do not free the returned array,
+ * it is owned by @self.
+ * @n_sessions: (out): Return location for the number of values in @sessions.
+ *
+ * Gets the list of pending network-initiated OMA sessions.
+ *
+ * Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise.
+ *
+ * Since: 1.2
+ * Deprecated: 1.18: Use mm_modem_oma_peek_pending_network_initiated_sessions() instead.
+ */
+G_DEPRECATED_FOR (mm_modem_oma_peek_pending_network_initiated_sessions)
+gboolean mm_modem_peek_pending_network_initiated_sessions (MMModemOma *self,
+ const MMOmaPendingNetworkInitiatedSession **sessions,
+ guint *n_sessions);
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_get_max_bearers:
+ * @self: a #MMModem.
+ *
+ * Gets the maximum number of defined packet data bearers this #MMModem
+ * supports.
+ *
+ * This is not the number of active/connected bearers the modem supports,
+ * but simply the number of bearers that may be defined at any given time.
+ * For example, POTS and CDMA2000-only devices support only one bearer,
+ * while GSM/UMTS devices typically support three or more, and any
+ * LTE-capable device (whether LTE-only, GSM/UMTS-capable, and/or
+ * CDMA2000-capable) also typically support three or more.
+ *
+ * Returns: the maximum number of defined packet data bearers.
+ *
+ * Since: 1.0
+ * Deprecated: 1.18. There is no way to query the modem how many bearers
+ * it supports, so the value exposed in this property in all the different
+ * implementations is always equal to the one retrieved with
+ * mm_modem_get_max_active_bearers(), so there is no point in using this
+ * method.
+ */
+G_DEPRECATED
+guint mm_modem_get_max_bearers (MMModem *self);
+
+#endif /* MM_DISABLE_DEPRECATED */
+
+#endif /* _MM_COMPAT_H_ */
diff --git a/libmm-glib/mm-firmware-properties.c b/libmm-glib/mm-firmware-properties.c
index b9d95721..d96f3430 100644
--- a/libmm-glib/mm-firmware-properties.c
+++ b/libmm-glib/mm-firmware-properties.c
@@ -31,7 +31,7 @@
* or mm_modem_firmware_list_sync().
*/
-G_DEFINE_TYPE (MMFirmwareProperties, mm_firmware_properties, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMFirmwareProperties, mm_firmware_properties, G_TYPE_OBJECT)
#define PROPERTY_UNIQUE_ID "unique-id"
#define PROPERTY_IMAGE_TYPE "image-type"
@@ -64,7 +64,10 @@ static MMFirmwareProperties *firmware_properties_new_empty (void);
*
* Gets the unique ID of the firmare image.
*
- * Returns: (transfer none): The ID of the image. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The ID of the image. Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_firmware_properties_get_unique_id (MMFirmwareProperties *self)
@@ -83,6 +86,8 @@ mm_firmware_properties_get_unique_id (MMFirmwareProperties *self)
* Gets the type of the firmare image.
*
* Returns: A #MMFirmwareImageType specifying The type of the image.
+ *
+ * Since: 1.0
*/
MMFirmwareImageType
mm_firmware_properties_get_image_type (MMFirmwareProperties *self)
@@ -100,7 +105,10 @@ mm_firmware_properties_get_image_type (MMFirmwareProperties *self)
*
* Gets the PRI version of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI.
*
- * Returns: The PRI version, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: The PRI version, or %NULL if unknown. Do not free the returned value,
+ * it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_firmware_properties_get_gobi_pri_version (MMFirmwareProperties *self)
@@ -111,6 +119,9 @@ mm_firmware_properties_get_gobi_pri_version (MMFirmwareProperties *self)
return self->priv->gobi_pri_version;
}
+/*
+ * mm_firmware_properties_set_gobi_pri_version: (skip)
+ */
void
mm_firmware_properties_set_gobi_pri_version (MMFirmwareProperties *self,
const gchar *version)
@@ -130,7 +141,10 @@ mm_firmware_properties_set_gobi_pri_version (MMFirmwareProperties *self,
*
* Gets the PRI info of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI.
*
- * Returns: The PRI info, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: The PRI info, or %NULL if unknown. Do not free the returned value,
+ * it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_firmware_properties_get_gobi_pri_info (MMFirmwareProperties *self)
@@ -141,6 +155,9 @@ mm_firmware_properties_get_gobi_pri_info (MMFirmwareProperties *self)
return self->priv->gobi_pri_info;
}
+/*
+ * mm_firmware_properties_set_gobi_pri_info: (skip)
+ */
void
mm_firmware_properties_set_gobi_pri_info (MMFirmwareProperties *self,
const gchar *info)
@@ -156,9 +173,13 @@ mm_firmware_properties_set_gobi_pri_info (MMFirmwareProperties *self,
* mm_firmware_properties_get_gobi_boot_version:
* @self: a #MMFirmwareProperties.
*
- * Gets the boot version of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI.
+ * Gets the boot version of a firmware image of type
+ * %MM_FIRMWARE_IMAGE_TYPE_GOBI.
*
- * Returns: The boot version, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: The boot version, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_firmware_properties_get_gobi_boot_version (MMFirmwareProperties *self)
@@ -169,6 +190,9 @@ mm_firmware_properties_get_gobi_boot_version (MMFirmwareProperties *self)
return self->priv->gobi_boot_version;
}
+/*
+ * mm_firmware_properties_set_gobi_boot_version: (skip)
+ */
void
mm_firmware_properties_set_gobi_boot_version (MMFirmwareProperties *self,
const gchar *version)
@@ -186,9 +210,13 @@ mm_firmware_properties_set_gobi_boot_version (MMFirmwareProperties *self,
* mm_firmware_properties_get_gobi_pri_unique_id:
* @self: a #MMFirmwareProperties.
*
- * Gets the PRI unique ID of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI.
+ * Gets the PRI unique ID of a firmware image of type
+ * %MM_FIRMWARE_IMAGE_TYPE_GOBI.
+ *
+ * Returns: The PRI unique ID, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
*
- * Returns: The PRI unique ID, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Since: 1.0
*/
const gchar *
mm_firmware_properties_get_gobi_pri_unique_id (MMFirmwareProperties *self)
@@ -199,6 +227,9 @@ mm_firmware_properties_get_gobi_pri_unique_id (MMFirmwareProperties *self)
return self->priv->gobi_pri_unique_id;
}
+/*
+ * mm_firmware_properties_set_gobi_pri_unique_id: (skip)
+ */
void
mm_firmware_properties_set_gobi_pri_unique_id (MMFirmwareProperties *self,
const gchar *unique_id)
@@ -216,9 +247,13 @@ mm_firmware_properties_set_gobi_pri_unique_id (MMFirmwareProperties *self,
* mm_firmware_properties_get_gobi_modem_unique_id:
* @self: a #MMFirmwareProperties.
*
- * Gets the MODEM unique ID of a firmware image of type %MM_FIRMWARE_IMAGE_TYPE_GOBI.
+ * Gets the MODEM unique ID of a firmware image of type
+ * %MM_FIRMWARE_IMAGE_TYPE_GOBI.
+ *
+ * Returns: The PRI unique ID, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
*
- * Returns: The PRI unique ID, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Since: 1.0
*/
const gchar *
mm_firmware_properties_get_gobi_modem_unique_id (MMFirmwareProperties *self)
@@ -229,6 +264,9 @@ mm_firmware_properties_get_gobi_modem_unique_id (MMFirmwareProperties *self)
return self->priv->gobi_modem_unique_id;
}
+/*
+ * mm_firmware_properties_set_gobi_modem_unique_id: (skip)
+ */
void
mm_firmware_properties_set_gobi_modem_unique_id (MMFirmwareProperties *self,
const gchar *unique_id)
@@ -242,13 +280,8 @@ mm_firmware_properties_set_gobi_modem_unique_id (MMFirmwareProperties *self,
/*****************************************************************************/
-/**
- * mm_firmware_properties_get_dictionary:
- * @self: A #MMFirmwareProperties.
- *
- * Gets a variant dictionary with the contents of @self.
- *
- * Returns: (transfer full): A dictionary with the image properties. The returned value should be freed with g_variant_unref().
+/*
+ * mm_firmware_properties_get_dictionary: (skip)
*/
GVariant *
mm_firmware_properties_get_dictionary (MMFirmwareProperties *self)
@@ -346,15 +379,8 @@ consume_variant (MMFirmwareProperties *self,
return TRUE;
}
-/**
- * mm_firmware_properties_new_from_dictionary:
- * @dictionary: A variant dictionary with the properties of the image.
- * @error: Return location for error or %NULL.
- *
- * Creates a new #MMFirmwareProperties object with the properties exposed in
- * the dictionary.
- *
- * Returns: (transfer full): A #MMFirmwareProperties or %NULL if @error is set. The returned value should be freed with g_object_unref().
+/*
+ * mm_firmware_properties_new_from_dictionary: (skip)
*/
MMFirmwareProperties *
mm_firmware_properties_new_from_dictionary (GVariant *dictionary,
@@ -420,14 +446,8 @@ mm_firmware_properties_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
-/**
- * mm_firmware_properties_new:
- * @image_type: A #MMFirmwareImageType specifying the type of the image.
- * @unique_id: The unique ID of the image.
- *
- * Creates a new #MMFirmwareProperties object with the properties specified.
- *
- * Returns: (transfer full): A #MMFirmwareProperties or %NULL if @error is set. The returned value should be freed with g_object_unref().
+/*
+ * mm_firmware_properties_new: (skip)
*/
MMFirmwareProperties *
mm_firmware_properties_new (MMFirmwareImageType image_type,
diff --git a/libmm-glib/mm-firmware-properties.h b/libmm-glib/mm-firmware-properties.h
index 33fffc3e..200a212e 100644
--- a/libmm-glib/mm-firmware-properties.h
+++ b/libmm-glib/mm-firmware-properties.h
@@ -54,6 +54,7 @@ struct _MMFirmwarePropertiesClass {
};
GType mm_firmware_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMFirmwareProperties, g_object_unref)
const gchar *mm_firmware_properties_get_unique_id (MMFirmwareProperties *self);
MMFirmwareImageType mm_firmware_properties_get_image_type (MMFirmwareProperties *self);
diff --git a/libmm-glib/mm-firmware-update-settings.c b/libmm-glib/mm-firmware-update-settings.c
new file mode 100644
index 00000000..72ef8456
--- /dev/null
+++ b/libmm-glib/mm-firmware-update-settings.c
@@ -0,0 +1,348 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+
+#include "mm-errors-types.h"
+#include "mm-common-helpers.h"
+#include "mm-firmware-update-settings.h"
+
+/**
+ * SECTION: mm-firmware-update-settings
+ * @title: MMFirmwareUpdateSettings
+ * @short_description: Helper object to handle firmware update settings.
+ *
+ * The #MMFirmwareUpdateSettings is an object handling the settings exposed to
+ * aid in the firmware update operation.
+ */
+
+G_DEFINE_TYPE (MMFirmwareUpdateSettings, mm_firmware_update_settings, G_TYPE_OBJECT)
+
+#define PROPERTY_DEVICE_IDS "device-ids"
+#define PROPERTY_VERSION "version"
+#define PROPERTY_FASTBOOT_AT "fastboot-at"
+
+struct _MMFirmwareUpdateSettingsPrivate {
+ /* Generic */
+ MMModemFirmwareUpdateMethod method;
+ gchar **device_ids;
+ gchar *version;
+ /* Fasboot specific */
+ gchar *fastboot_at;
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_firmware_update_settings_get_method:
+ * @self: A #MMFirmwareUpdateSettings.
+ *
+ * Gets the methods to use during the firmware update operation.
+ *
+ * Returns: a bitmask of #MMModemFirmwareUpdateMethod values.
+ *
+ * Since: 1.10
+ */
+MMModemFirmwareUpdateMethod
+mm_firmware_update_settings_get_method (MMFirmwareUpdateSettings *self)
+{
+ g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE);
+
+ return self->priv->method;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_firmware_update_settings_get_device_ids:
+ * @self: a #MMFirmwareUpdateSettings.
+ *
+ * Gets the list of device ids used to identify the device during a firmware
+ * update operation.
+ *
+ * Returns: (transfer none): The list of device ids, or %NULL if unknown. Do not
+ * free the returned value, it is owned by @self.
+ *
+ * Since: 1.10
+ */
+const gchar **
+mm_firmware_update_settings_get_device_ids (MMFirmwareUpdateSettings *self)
+{
+ g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), NULL);
+
+ return (const gchar **) self->priv->device_ids;
+}
+
+/**
+ * mm_firmware_update_settings_set_device_ids: (skip)
+ */
+void
+mm_firmware_update_settings_set_device_ids (MMFirmwareUpdateSettings *self,
+ const gchar **device_ids)
+{
+ g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self));
+
+ g_strfreev (self->priv->device_ids);
+ self->priv->device_ids = g_strdupv ((gchar **)device_ids);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_firmware_update_settings_get_version:
+ * @self: a #MMFirmwareUpdateSettings.
+ *
+ * Gets firmware version string.
+ *
+ *
+ * Returns: The version string, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.10
+ */
+const gchar *
+mm_firmware_update_settings_get_version (MMFirmwareUpdateSettings *self)
+{
+ g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), NULL);
+
+ return self->priv->version;
+}
+
+/**
+ * mm_firmware_update_settings_set_version: (skip)
+ */
+void
+mm_firmware_update_settings_set_version (MMFirmwareUpdateSettings *self,
+ const gchar *version)
+{
+ g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self));
+
+ g_free (self->priv->version);
+ self->priv->version = g_strdup (version);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_firmware_update_settings_get_fastboot_at:
+ * @self: a #MMFirmwareUpdateSettings.
+ *
+ * Gets the AT command that should be sent to the module to trigger a reset
+ * into fastboot mode.
+ *
+ * Only applicable if the update method includes
+ * %MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT.
+ *
+ * Returns: The AT command string, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.10
+ */
+const gchar *
+mm_firmware_update_settings_get_fastboot_at (MMFirmwareUpdateSettings *self)
+{
+ g_return_val_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self), NULL);
+ g_return_val_if_fail (self->priv->method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, NULL);
+
+ return self->priv->fastboot_at;
+}
+
+/**
+ * mm_firmware_update_settings_set_fastboot_at: (skip)
+ */
+void
+mm_firmware_update_settings_set_fastboot_at (MMFirmwareUpdateSettings *self,
+ const gchar *fastboot_at)
+{
+ g_return_if_fail (MM_IS_FIRMWARE_UPDATE_SETTINGS (self));
+ g_return_if_fail (self->priv->method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT);
+
+ g_free (self->priv->fastboot_at);
+ self->priv->fastboot_at = g_strdup (fastboot_at);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_firmware_update_settings_get_variant: (skip)
+ */
+GVariant *
+mm_firmware_update_settings_get_variant (MMFirmwareUpdateSettings *self)
+{
+ MMModemFirmwareUpdateMethod method;
+ GVariantBuilder builder;
+
+ method = (self ? self->priv->method : MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ua{sv})"));
+ g_variant_builder_add (&builder, "u", method);
+
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+ if (self) {
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_DEVICE_IDS,
+ g_variant_new_strv ((const gchar * const *)self->priv->device_ids, -1));
+
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_VERSION,
+ g_variant_new_string (self->priv->version));
+
+ if (method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) {
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_FASTBOOT_AT,
+ g_variant_new_string (self->priv->fastboot_at));
+ }
+ }
+ g_variant_builder_close (&builder);
+
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+/*****************************************************************************/
+
+static gboolean
+consume_variant (MMFirmwareUpdateSettings *self,
+ const gchar *key,
+ GVariant *value,
+ GError **error)
+{
+ if (g_str_equal (key, PROPERTY_FASTBOOT_AT)) {
+ g_free (self->priv->fastboot_at);
+ self->priv->fastboot_at = g_variant_dup_string (value, NULL);
+ } else if (g_str_equal (key, PROPERTY_VERSION)) {
+ g_free (self->priv->version);
+ self->priv->version = g_variant_dup_string (value, NULL);
+ } else if (g_str_equal (key, PROPERTY_DEVICE_IDS)) {
+ g_strfreev (self->priv->device_ids);
+ self->priv->device_ids = g_variant_dup_strv (value, NULL);
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid settings dictionary, unexpected key '%s'", key);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/**
+ * mm_firmware_update_settings_new_from_variant: (skip)
+ */
+MMFirmwareUpdateSettings *
+mm_firmware_update_settings_new_from_variant (GVariant *variant,
+ GError **error)
+{
+ MMFirmwareUpdateSettings *self;
+ guint method = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE;
+ GVariant *dictionary = NULL;
+ GError *inner_error = NULL;
+
+ if (!variant) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "No input given");
+ return NULL;
+ }
+
+ if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(ua{sv})"))) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid input type");
+ return NULL;
+ }
+
+ g_variant_get (variant, "(u@a{sv})", &method, &dictionary);
+ self = mm_firmware_update_settings_new (method);
+
+ if ((method != MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) && dictionary) {
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+
+ g_variant_iter_init (&iter, dictionary);
+ while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ consume_variant (self, key, value, &inner_error);
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ if (!inner_error) {
+ if (!self->priv->device_ids)
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing required '" PROPERTY_DEVICE_IDS "' setting");
+ else if (!self->priv->version)
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing required '" PROPERTY_VERSION "' setting");
+ }
+
+ if (!inner_error) {
+ if ((method & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && (!self->priv->fastboot_at))
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Fastboot method requires the '" PROPERTY_FASTBOOT_AT "' setting");
+ }
+ g_variant_unref (dictionary);
+ }
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (self);
+ return NULL;
+ }
+
+ return self;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_firmware_update_settings_new: (skip)
+ */
+MMFirmwareUpdateSettings *
+mm_firmware_update_settings_new (MMModemFirmwareUpdateMethod method)
+{
+ MMFirmwareUpdateSettings *self;
+
+ self = g_object_new (MM_TYPE_FIRMWARE_UPDATE_SETTINGS, NULL);
+ self->priv->method = method;
+ return self;
+}
+
+static void
+mm_firmware_update_settings_init (MMFirmwareUpdateSettings *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettingsPrivate);
+ self->priv->method = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE;
+}
+
+static void
+finalize (GObject *object)
+{
+ MMFirmwareUpdateSettings *self = MM_FIRMWARE_UPDATE_SETTINGS (object);
+
+ g_strfreev (self->priv->device_ids);
+ g_free (self->priv->version);
+ g_free (self->priv->fastboot_at);
+
+ G_OBJECT_CLASS (mm_firmware_update_settings_parent_class)->finalize (object);
+}
+
+static void
+mm_firmware_update_settings_class_init (MMFirmwareUpdateSettingsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMFirmwareUpdateSettingsPrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-firmware-update-settings.h b/libmm-glib/mm-firmware-update-settings.h
new file mode 100644
index 00000000..40940a2e
--- /dev/null
+++ b/libmm-glib/mm-firmware-update-settings.h
@@ -0,0 +1,96 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_FIRMWARE_UPDATE_SETTINGS_H
+#define MM_FIRMWARE_UPDATE_SETTINGS_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_FIRMWARE_UPDATE_SETTINGS (mm_firmware_update_settings_get_type ())
+#define MM_FIRMWARE_UPDATE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettings))
+#define MM_FIRMWARE_UPDATE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettingsClass))
+#define MM_IS_FIRMWARE_UPDATE_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_FIRMWARE_UPDATE_SETTINGS))
+#define MM_IS_FIRMWARE_UPDATE_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_FIRMWARE_UPDATE_SETTINGS))
+#define MM_FIRMWARE_UPDATE_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_FIRMWARE_UPDATE_SETTINGS, MMFirmwareUpdateSettingsClass))
+
+typedef struct _MMFirmwareUpdateSettings MMFirmwareUpdateSettings;
+typedef struct _MMFirmwareUpdateSettingsClass MMFirmwareUpdateSettingsClass;
+typedef struct _MMFirmwareUpdateSettingsPrivate MMFirmwareUpdateSettingsPrivate;
+
+/**
+ * MMFirmwareUpdateSettings:
+ *
+ * The #MMFirmwareUpdateSettings structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MMFirmwareUpdateSettings {
+ /*< private >*/
+ GObject parent;
+ MMFirmwareUpdateSettingsPrivate *priv;
+};
+
+struct _MMFirmwareUpdateSettingsClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_firmware_update_settings_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMFirmwareUpdateSettings, g_object_unref)
+
+MMModemFirmwareUpdateMethod mm_firmware_update_settings_get_method (MMFirmwareUpdateSettings *self);
+
+/* Generic */
+const gchar **mm_firmware_update_settings_get_device_ids (MMFirmwareUpdateSettings *self);
+const gchar *mm_firmware_update_settings_get_version (MMFirmwareUpdateSettings *self);
+
+/* Fastboot specific */
+const gchar *mm_firmware_update_settings_get_fastboot_at (MMFirmwareUpdateSettings *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMFirmwareUpdateSettings *mm_firmware_update_settings_new (MMModemFirmwareUpdateMethod method);
+
+MMFirmwareUpdateSettings *mm_firmware_update_settings_new_from_variant (GVariant *variant,
+ GError **error);
+
+GVariant *mm_firmware_update_settings_get_variant (MMFirmwareUpdateSettings *self);
+
+/* Generic */
+void mm_firmware_update_settings_set_device_ids (MMFirmwareUpdateSettings *self,
+ const gchar **device_ids);
+void mm_firmware_update_settings_set_version (MMFirmwareUpdateSettings *self,
+ const gchar *version);
+
+/* Fastboot specific */
+void mm_firmware_update_settings_set_fastboot_at (MMFirmwareUpdateSettings *self,
+ const gchar *fastboot_at);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_FIRMWARE_UPDATE_SETTINGS_H */
diff --git a/libmm-glib/mm-helper-types.c b/libmm-glib/mm-helper-types.c
index 1ac36104..73162eb7 100644
--- a/libmm-glib/mm-helper-types.c
+++ b/libmm-glib/mm-helper-types.c
@@ -28,6 +28,8 @@
* @array_size: length of @array.
*
* Frees an array of #MMModemPortInfo values.
+ *
+ * Since: 1.0
*/
void
mm_modem_port_info_array_free (MMModemPortInfo *array,
diff --git a/libmm-glib/mm-helper-types.h b/libmm-glib/mm-helper-types.h
index d4e7fa9d..a2b72c9a 100644
--- a/libmm-glib/mm-helper-types.h
+++ b/libmm-glib/mm-helper-types.h
@@ -35,7 +35,10 @@
* @allowed: Mask of #MMModemMode values specifying allowed modes.
* @preferred: A single #MMModemMode value specifying the preferred mode.
*
- * #MMModemModeCombination is a simple struct holding a pair of #MMModemMode values.
+ * #MMModemModeCombination is a simple struct holding a pair of #MMModemMode
+ * values.
+ *
+ * Since: 1.0
*/
typedef struct _MMModemModeCombination MMModemModeCombination;
struct _MMModemModeCombination {
@@ -49,6 +52,8 @@ struct _MMModemModeCombination {
* @type: A #MMModemPortType value.
*
* Information of a given port.
+ *
+ * Since: 1.0
*/
typedef struct _MMModemPortInfo MMModemPortInfo;
struct _MMModemPortInfo {
@@ -66,6 +71,8 @@ void mm_modem_port_info_array_free (MMModemPortInfo *array,
*
* #MMOmaPendingNetworkInitiatedSession is a simple struct specifying the
* information available for a pending network-initiated OMA session.
+ *
+ * Since: 1.2
*/
typedef struct _MMOmaPendingNetworkInitiatedSession MMOmaPendingNetworkInitiatedSession;
struct _MMOmaPendingNetworkInitiatedSession {
diff --git a/libmm-glib/mm-helpers.h b/libmm-glib/mm-helpers.h
index 2e8cb589..e0178b55 100644
--- a/libmm-glib/mm-helpers.h
+++ b/libmm-glib/mm-helpers.h
@@ -17,12 +17,14 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2011-2021 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef _MM_HELPERS_H_
#define _MM_HELPERS_H_
+/******************************************************************************/
+
#define RETURN_NON_EMPTY_CONSTANT_STRING(input) do { \
const gchar *str; \
\
@@ -42,4 +44,289 @@
} while (0); \
return NULL
+
+/******************************************************************************/
+/* These are helper macros to work with properties that are being monitored
+ * internally by the proxy objects. This internal monitoring is used to allow
+ * maintaining 'custom' types associated to complex DBus properties like
+ * dictionaries.
+ *
+ * Basic ARRAY and OBJECT type support is given.
+ */
+
+#define PROPERTY_COMMON_DECLARE(property_name) \
+ guint property_name##_id; \
+ gboolean property_name##_refresh_required;
+
+#define PROPERTY_DECLARE(property_name,PropertyType) \
+ PropertyType *property_name; \
+ PROPERTY_COMMON_DECLARE (property_name)
+
+#define PROPERTY_ARRAY_DECLARE(property_name) PROPERTY_DECLARE (property_name, GArray)
+#define PROPERTY_OBJECT_DECLARE(property_name,ObjectType) PROPERTY_DECLARE (property_name, ObjectType)
+#define PROPERTY_ERROR_DECLARE(property_name) PROPERTY_DECLARE (property_name, GError)
+
+#define PROPERTY_INITIALIZE(property_name,signal_name) \
+ self->priv->property_name##_refresh_required = TRUE; \
+ self->priv->property_name##_id = \
+ g_signal_connect (self, \
+ "notify::" signal_name, \
+ G_CALLBACK (property_name##_updated), \
+ NULL);
+
+#define PROPERTY_ARRAY_FINALIZE(property_name) \
+ g_clear_pointer (&self->priv->property_name, g_array_unref);
+
+#define PROPERTY_OBJECT_FINALIZE(property_name) \
+ g_clear_object (&self->priv->property_name);
+
+#define PROPERTY_ERROR_FINALIZE(property_name) \
+ g_clear_error (&self->priv->property_name);
+
+
+/* This helper macro uses a GMutexLocker to lock the context
+ * in which the macro is defined (so it must always be defined at the
+ * start of the context). It also will run a given refresh method if
+ * a specific input flag is set.
+ */
+#define PROPERTY_LOCK_AND_REFRESH(property_name) \
+ g_autoptr(GMutexLocker) locker = NULL; \
+ \
+ locker = g_mutex_locker_new (&self->priv->mutex); \
+ if (self->priv->property_name##_refresh_required) { \
+ property_name##_refresh (self); \
+ self->priv->property_name##_refresh_required = FALSE; \
+ }
+
+/* This helper defines the property refresh method, and can be used for simple
+ * one-to-one property vs array transformations */
+#define PROPERTY_ARRAY_DEFINE_REFRESH(property_name,Type,type,TYPE,variant_to_garray) \
+ static void \
+ property_name##_refresh (MM##Type *self) \
+ { \
+ g_autoptr(GVariant) variant = NULL; \
+ \
+ g_clear_pointer (&self->priv->property_name, g_array_unref); \
+ \
+ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \
+ if (!variant) \
+ return; \
+ \
+ self->priv->property_name = variant_to_garray (variant); \
+ }
+
+/* This helper defines the property refresh method, and can be used for simple
+ * one-to-one property vs object transformations */
+#define PROPERTY_OBJECT_DEFINE_REFRESH(property_name,Type,type,TYPE,variant_to_object) \
+ static void \
+ property_name##_refresh (MM##Type *self) \
+ { \
+ g_autoptr(GVariant) variant = NULL; \
+ \
+ g_clear_object (&self->priv->property_name); \
+ \
+ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \
+ if (!variant) \
+ return; \
+ \
+ self->priv->property_name = variant_to_object (variant); \
+ }
+
+#define PROPERTY_OBJECT_DEFINE_REFRESH_FAILABLE(property_name,Type,type,TYPE,variant_to_object) \
+ static void \
+ property_name##_refresh (MM##Type *self) \
+ { \
+ g_autoptr(GVariant) variant = NULL; \
+ g_autoptr(GError) inner_error = NULL; \
+ \
+ g_clear_object (&self->priv->property_name); \
+ \
+ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \
+ if (!variant) \
+ return; \
+ \
+ self->priv->property_name = variant_to_object (variant, &inner_error); \
+ if (inner_error) \
+ g_warning ("Invalid object variant reported: %s", inner_error->message); \
+ }
+
+/* This helper defines the property refresh method, and can be used for simple
+ * one-to-one property vs GError transformations */
+#define PROPERTY_ERROR_DEFINE_REFRESH_FAILABLE(property_name,Type,type,TYPE,variant_to_error) \
+ static void \
+ property_name##_refresh (MM##Type *self) \
+ { \
+ g_autoptr(GVariant) variant = NULL; \
+ g_autoptr(GError) inner_error = NULL; \
+ \
+ g_clear_error (&self->priv->property_name); \
+ \
+ variant = mm_gdbus_##type##_dup_##property_name (MM_GDBUS_##TYPE (self)); \
+ if (!variant) \
+ return; \
+ \
+ self->priv->property_name = variant_to_error (variant, &inner_error); \
+ if (inner_error) \
+ g_warning ("Invalid error variant reported: %s", inner_error->message); \
+ }
+
+/* This helper defines the common generic property updated callback */
+#define PROPERTY_DEFINE_UPDATED(property_name,Type) \
+ static void \
+ property_name##_updated (MM##Type *self) \
+ { \
+ g_autoptr(GMutexLocker) locker = NULL; \
+ \
+ locker = g_mutex_locker_new (&self->priv->mutex); \
+ self->priv->property_name##_refresh_required = TRUE; \
+ }
+
+/* Getter implementation for arrays of complex types that need
+ * deep copy. */
+#define PROPERTY_ARRAY_DEFINE_GET_DEEP(property_name,Type,type,TYPE,ArrayItemType,garray_to_array) \
+ gboolean \
+ mm_##type##_get_##property_name (MM##Type *self, \
+ ArrayItemType **out, \
+ guint *n_out) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), FALSE); \
+ g_return_val_if_fail (out != NULL, FALSE); \
+ g_return_val_if_fail (n_out != NULL, FALSE); \
+ \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ return garray_to_array (self->priv->property_name, out, n_out); \
+ } \
+ }
+
+/* Getter implementation for arrays of simple types */
+#define PROPERTY_ARRAY_DEFINE_GET(property_name,Type,type,TYPE,ArrayItemType) \
+ gboolean \
+ mm_##type##_get_##property_name (MM##Type *self, \
+ ArrayItemType **out, \
+ guint *n_out) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), FALSE); \
+ g_return_val_if_fail (out != NULL, FALSE); \
+ g_return_val_if_fail (n_out != NULL, FALSE); \
+ \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ if (!self->priv->property_name) \
+ return FALSE; \
+ \
+ *out = NULL; \
+ *n_out = self->priv->property_name->len; \
+ if (self->priv->property_name->len > 0) \
+ *out = g_memdup (self->priv->property_name->data, \
+ (guint)(sizeof (ArrayItemType) * self->priv->property_name->len)); \
+ return TRUE; \
+ } \
+ }
+
+/* Peeker implementation for arrays of any type */
+#define PROPERTY_ARRAY_DEFINE_PEEK(property_name,Type,type,TYPE,ArrayItemType) \
+ gboolean \
+ mm_##type##_peek_##property_name (MM##Type *self, \
+ const ArrayItemType **out, \
+ guint *n_out) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), FALSE); \
+ g_return_val_if_fail (out != NULL, FALSE); \
+ g_return_val_if_fail (n_out != NULL, FALSE); \
+ \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ \
+ if (!self->priv->property_name) \
+ return FALSE; \
+ \
+ *n_out = self->priv->property_name->len; \
+ *out = (ArrayItemType *)self->priv->property_name->data; \
+ return TRUE; \
+ } \
+ }
+
+/* Get implementations for object properties */
+#define PROPERTY_OBJECT_DEFINE_GET(property_name,object_name,Type,type,TYPE,ObjectType) \
+ ObjectType * \
+ mm_##type##_get_##object_name (MM##Type *self) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ return (self->priv->object_name ? \
+ g_object_ref (self->priv->object_name) : \
+ NULL); \
+ } \
+ }
+
+/* Peek implementations for object properties */
+#define PROPERTY_OBJECT_DEFINE_PEEK(property_name,object_name,Type,type,TYPE,ObjectType) \
+ ObjectType * \
+ mm_##type##_peek_##object_name (MM##Type *self) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ return self->priv->object_name; \
+ } \
+ }
+
+/* Get implementations for error properties */
+#define PROPERTY_ERROR_DEFINE_GET(property_name,Type,type,TYPE) \
+ GError * \
+ mm_##type##_get_##property_name (MM##Type *self) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ return (self->priv->property_name ? \
+ g_error_copy (self->priv->property_name) : \
+ NULL); \
+ } \
+ }
+
+/* Peek implementations for error properties */
+#define PROPERTY_ERROR_DEFINE_PEEK(property_name,Type,type,TYPE) \
+ GError * \
+ mm_##type##_peek_##property_name (MM##Type *self) \
+ { \
+ g_return_val_if_fail (MM_IS_##TYPE (self), NULL); \
+ { \
+ PROPERTY_LOCK_AND_REFRESH (property_name) \
+ return self->priv->property_name; \
+ } \
+ }
+
+#define PROPERTY_ARRAY_DEFINE(property_name,Type,type,TYPE,ArrayItemType,variant_to_garray) \
+ PROPERTY_ARRAY_DEFINE_REFRESH (property_name, Type, type, TYPE, variant_to_garray) \
+ PROPERTY_DEFINE_UPDATED (property_name, Type) \
+ PROPERTY_ARRAY_DEFINE_GET (property_name, Type, type, TYPE, ArrayItemType) \
+ PROPERTY_ARRAY_DEFINE_PEEK (property_name, Type, type, TYPE, ArrayItemType)
+
+#define PROPERTY_ARRAY_DEFINE_DEEP(property_name,Type,type,TYPE,ArrayItemType,variant_to_garray,garray_to_array) \
+ PROPERTY_ARRAY_DEFINE_REFRESH (property_name, Type, type, TYPE, variant_to_garray) \
+ PROPERTY_DEFINE_UPDATED (property_name, Type) \
+ PROPERTY_ARRAY_DEFINE_GET_DEEP (property_name, Type, type, TYPE, ArrayItemType, garray_to_array) \
+ PROPERTY_ARRAY_DEFINE_PEEK (property_name, Type, type, TYPE, ArrayItemType)
+
+#define PROPERTY_OBJECT_DEFINE(property_name,Type,type,TYPE,ObjectType,variant_to_object) \
+ PROPERTY_OBJECT_DEFINE_REFRESH (property_name, Type, type, TYPE, variant_to_object) \
+ PROPERTY_DEFINE_UPDATED (property_name, Type) \
+ PROPERTY_OBJECT_DEFINE_GET (property_name, property_name, Type, type, TYPE, ObjectType) \
+ PROPERTY_OBJECT_DEFINE_PEEK (property_name, property_name, Type, type, TYPE, ObjectType)
+
+#define PROPERTY_OBJECT_DEFINE_FAILABLE(property_name,Type,type,TYPE,ObjectType,variant_to_object) \
+ PROPERTY_OBJECT_DEFINE_REFRESH_FAILABLE (property_name, Type, type, TYPE, variant_to_object) \
+ PROPERTY_DEFINE_UPDATED (property_name, Type) \
+ PROPERTY_OBJECT_DEFINE_GET (property_name, property_name, Type, type, TYPE, ObjectType) \
+ PROPERTY_OBJECT_DEFINE_PEEK (property_name, property_name, Type, type, TYPE, ObjectType)
+
+#define PROPERTY_ERROR_DEFINE_FAILABLE(property_name,Type,type,TYPE,variant_to_error) \
+ PROPERTY_ERROR_DEFINE_REFRESH_FAILABLE (property_name, Type, type, TYPE, variant_to_error) \
+ PROPERTY_DEFINE_UPDATED (property_name, Type) \
+ PROPERTY_ERROR_DEFINE_GET (property_name, Type, type, TYPE) \
+ PROPERTY_ERROR_DEFINE_PEEK (property_name, Type, type, TYPE)
+
#endif /* _MM_HELPERS_H_ */
diff --git a/libmm-glib/mm-kernel-event-properties.c b/libmm-glib/mm-kernel-event-properties.c
new file mode 100644
index 00000000..6514944d
--- /dev/null
+++ b/libmm-glib/mm-kernel-event-properties.c
@@ -0,0 +1,459 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "mm-errors-types.h"
+#include "mm-enums-types.h"
+#include "mm-common-helpers.h"
+#include "mm-kernel-event-properties.h"
+
+/**
+ * SECTION: mm-kernel-event-properties
+ * @title: MMKernelEventProperties
+ * @short_description: Helper object to handle kernel event properties.
+ *
+ * The #MMKernelEventProperties is an object handling the properties to be set
+ * in reported kernel events.
+ *
+ * This object is created by the user and passed to ModemManager with either
+ * mm_manager_report_kernel_event() or mm_manager_report_kernel_event_sync().
+ */
+
+G_DEFINE_TYPE (MMKernelEventProperties, mm_kernel_event_properties, G_TYPE_OBJECT)
+
+#define PROPERTY_ACTION "action"
+#define PROPERTY_SUBSYSTEM "subsystem"
+#define PROPERTY_NAME "name"
+#define PROPERTY_UID "uid"
+
+struct _MMKernelEventPropertiesPrivate {
+ gchar *action;
+ gchar *subsystem;
+ gchar *name;
+ gchar *uid;
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_kernel_event_properties_set_action:
+ * @self: A #MMKernelEventProperties.
+ * @action: The action to set.
+ *
+ * Sets the action.
+ *
+ * Since: 1.8
+ */
+void
+mm_kernel_event_properties_set_action (MMKernelEventProperties *self,
+ const gchar *action)
+{
+ g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
+
+ g_free (self->priv->action);
+ self->priv->action = g_strdup (action);
+}
+
+/**
+ * mm_kernel_event_properties_get_action:
+ * @self: A #MMKernelEventProperties.
+ *
+ * Gets the action.
+ *
+ * Returns: (transfer none): The action. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.8
+ */
+const gchar *
+mm_kernel_event_properties_get_action (MMKernelEventProperties *self)
+{
+ g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL);
+
+ return self->priv->action;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_kernel_event_properties_set_subsystem:
+ * @self: A #MMKernelEventProperties.
+ * @subsystem: The subsystem to set.
+ *
+ * Sets the subsystem.
+ *
+ * Since: 1.8
+ */
+void
+mm_kernel_event_properties_set_subsystem (MMKernelEventProperties *self,
+ const gchar *subsystem)
+{
+ g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
+
+ g_free (self->priv->subsystem);
+ self->priv->subsystem = g_strdup (subsystem);
+}
+
+/**
+ * mm_kernel_event_properties_get_subsystem:
+ * @self: A #MMKernelEventProperties.
+ *
+ * Gets the subsystem.
+ *
+ * Returns: (transfer none): The subsystem. Do not free the returned value, it
+ * is owned by @self.
+ *
+ * Since: 1.8
+ */
+const gchar *
+mm_kernel_event_properties_get_subsystem (MMKernelEventProperties *self)
+{
+ g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL);
+
+ return self->priv->subsystem;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_kernel_event_properties_set_name:
+ * @self: A #MMKernelEventProperties.
+ * @name: The name to set.
+ *
+ * Sets the name.
+ *
+ * Since: 1.8
+ */
+void
+mm_kernel_event_properties_set_name (MMKernelEventProperties *self,
+ const gchar *name)
+{
+ g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
+
+ g_free (self->priv->name);
+ self->priv->name = g_strdup (name);
+}
+
+/**
+ * mm_kernel_event_properties_get_name:
+ * @self: A #MMKernelEventProperties.
+ *
+ * Gets the name.
+ *
+ * Returns: (transfer none): The name. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.8
+ */
+const gchar *
+mm_kernel_event_properties_get_name (MMKernelEventProperties *self)
+{
+ g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL);
+
+ return self->priv->name;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_kernel_event_properties_set_uid:
+ * @self: A #MMKernelEventProperties.
+ * @uid: The uid to set.
+ *
+ * Sets the unique ID of the physical device.
+ *
+ * Since: 1.8
+ */
+void
+mm_kernel_event_properties_set_uid (MMKernelEventProperties *self,
+ const gchar *uid)
+{
+ g_return_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self));
+
+ g_free (self->priv->uid);
+ self->priv->uid = g_strdup (uid);
+}
+
+/**
+ * mm_kernel_event_properties_get_uid:
+ * @self: A #MMKernelEventProperties.
+ *
+ * Gets the unique ID of the physical device.
+ *
+ * Returns: (transfer none): The uid. Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.8
+ */
+const gchar *
+mm_kernel_event_properties_get_uid (MMKernelEventProperties *self)
+{
+ g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL);
+
+ return self->priv->uid;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_kernel_event_properties_get_dictionary: (skip)
+ */
+GVariant *
+mm_kernel_event_properties_get_dictionary (MMKernelEventProperties *self)
+{
+ GVariantBuilder builder;
+
+ /* We do allow NULL */
+ if (!self)
+ return NULL;
+
+ g_return_val_if_fail (MM_IS_KERNEL_EVENT_PROPERTIES (self), NULL);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (self->priv->action)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ACTION,
+ g_variant_new_string (self->priv->action));
+
+ if (self->priv->subsystem)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_SUBSYSTEM,
+ g_variant_new_string (self->priv->subsystem));
+
+ if (self->priv->name)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_NAME,
+ g_variant_new_string (self->priv->name));
+
+ if (self->priv->uid)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_UID,
+ g_variant_new_string (self->priv->uid));
+
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+/*****************************************************************************/
+
+static gboolean
+consume_string (MMKernelEventProperties *self,
+ const gchar *key,
+ const gchar *value,
+ GError **error)
+{
+ if (g_str_equal (key, PROPERTY_ACTION))
+ mm_kernel_event_properties_set_action (self, value);
+ else if (g_str_equal (key, PROPERTY_SUBSYSTEM))
+ mm_kernel_event_properties_set_subsystem (self, value);
+ else if (g_str_equal (key, PROPERTY_NAME))
+ mm_kernel_event_properties_set_name (self, value);
+ else if (g_str_equal (key, PROPERTY_UID))
+ mm_kernel_event_properties_set_uid (self, value);
+ else {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid properties string, unexpected key '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+typedef struct {
+ MMKernelEventProperties *properties;
+ GError *error;
+} ParseKeyValueContext;
+
+static gboolean
+key_value_foreach (const gchar *key,
+ const gchar *value,
+ ParseKeyValueContext *ctx)
+{
+ return consume_string (ctx->properties,
+ key,
+ value,
+ &ctx->error);
+}
+
+/**
+ * mm_kernel_event_properties_new_from_string: (skip)
+ */
+MMKernelEventProperties *
+mm_kernel_event_properties_new_from_string (const gchar *str,
+ GError **error)
+{
+ ParseKeyValueContext ctx;
+
+ ctx.properties = mm_kernel_event_properties_new ();
+ ctx.error = NULL;
+
+ mm_common_parse_key_value_string (str,
+ &ctx.error,
+ (MMParseKeyValueForeachFn) key_value_foreach,
+ &ctx);
+
+ /* If error, destroy the object */
+ if (ctx.error) {
+ g_propagate_error (error, ctx.error);
+ g_object_unref (ctx.properties);
+ ctx.properties = NULL;
+ }
+
+ return ctx.properties;
+}
+
+/*****************************************************************************/
+
+static gboolean
+consume_variant (MMKernelEventProperties *properties,
+ const gchar *key,
+ GVariant *value,
+ GError **error)
+{
+ if (g_str_equal (key, PROPERTY_ACTION))
+ mm_kernel_event_properties_set_action (
+ properties,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_SUBSYSTEM))
+ mm_kernel_event_properties_set_subsystem (
+ properties,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_NAME))
+ mm_kernel_event_properties_set_name (
+ properties,
+ g_variant_get_string (value, NULL));
+ else if (g_str_equal (key, PROPERTY_UID))
+ mm_kernel_event_properties_set_uid (
+ properties,
+ g_variant_get_string (value, NULL));
+ else {
+ /* Set error */
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid properties dictionary, unexpected key '%s'",
+ key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * mm_kernel_event_properties_new_from_dictionary: (skip)
+ */
+MMKernelEventProperties *
+mm_kernel_event_properties_new_from_dictionary (GVariant *dictionary,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ MMKernelEventProperties *properties;
+
+ properties = mm_kernel_event_properties_new ();
+ if (!dictionary)
+ return properties;
+
+ if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create kernel event properties from dictionary: "
+ "invalid variant type received");
+ g_object_unref (properties);
+ return NULL;
+ }
+
+ g_variant_iter_init (&iter, dictionary);
+ while (!inner_error &&
+ g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ consume_variant (properties,
+ key,
+ value,
+ &inner_error);
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ /* If error, destroy the object */
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (properties);
+ properties = NULL;
+ }
+
+ return properties;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_kernel_event_properties_new:
+ *
+ * Creates a new empty #MMKernelEventProperties.
+ *
+ * Returns: (transfer full): a #MMKernelEventProperties. The returned value
+ * should be freed with g_object_unref().
+ *
+ * Since: 1.8
+ */
+MMKernelEventProperties *
+mm_kernel_event_properties_new (void)
+{
+ return (MM_KERNEL_EVENT_PROPERTIES (g_object_new (MM_TYPE_KERNEL_EVENT_PROPERTIES, NULL)));
+}
+
+static void
+mm_kernel_event_properties_init (MMKernelEventProperties *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_KERNEL_EVENT_PROPERTIES,
+ MMKernelEventPropertiesPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMKernelEventProperties *self = MM_KERNEL_EVENT_PROPERTIES (object);
+
+ g_free (self->priv->action);
+ g_free (self->priv->subsystem);
+ g_free (self->priv->name);
+ g_free (self->priv->uid);
+
+ G_OBJECT_CLASS (mm_kernel_event_properties_parent_class)->finalize (object);
+}
+
+static void
+mm_kernel_event_properties_class_init (MMKernelEventPropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMKernelEventPropertiesPrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-kernel-event-properties.h b/libmm-glib/mm-kernel-event-properties.h
new file mode 100644
index 00000000..66dc551c
--- /dev/null
+++ b/libmm-glib/mm-kernel-event-properties.h
@@ -0,0 +1,96 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ */
+
+#ifndef MM_KERNEL_EVENT_PROPERTIES_H
+#define MM_KERNEL_EVENT_PROPERTIES_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_KERNEL_EVENT_PROPERTIES (mm_kernel_event_properties_get_type ())
+#define MM_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventProperties))
+#define MM_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesClass))
+#define MM_IS_KERNEL_EVENT_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES))
+#define MM_IS_KERNEL_EVENT_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_EVENT_PROPERTIES))
+#define MM_KERNEL_EVENT_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_EVENT_PROPERTIES, MMKernelEventPropertiesClass))
+
+typedef struct _MMKernelEventProperties MMKernelEventProperties;
+typedef struct _MMKernelEventPropertiesClass MMKernelEventPropertiesClass;
+typedef struct _MMKernelEventPropertiesPrivate MMKernelEventPropertiesPrivate;
+
+/**
+ * MMKernelEventProperties:
+ *
+ * The #MMKernelEventProperties structure contains private data and should only be
+ * accessed using the provided API.
+ */
+struct _MMKernelEventProperties {
+ /*< private >*/
+ GObject parent;
+ MMKernelEventPropertiesPrivate *priv;
+};
+
+struct _MMKernelEventPropertiesClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_kernel_event_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelEventProperties, g_object_unref)
+
+MMKernelEventProperties *mm_kernel_event_properties_new (void);
+
+void mm_kernel_event_properties_set_action (MMKernelEventProperties *self,
+ const gchar *action);
+const gchar *mm_kernel_event_properties_get_action (MMKernelEventProperties *self);
+
+void mm_kernel_event_properties_set_subsystem (MMKernelEventProperties *self,
+ const gchar *subsystem);
+const gchar *mm_kernel_event_properties_get_subsystem (MMKernelEventProperties *self);
+
+void mm_kernel_event_properties_set_name (MMKernelEventProperties *self,
+ const gchar *name);
+const gchar *mm_kernel_event_properties_get_name (MMKernelEventProperties *self);
+
+void mm_kernel_event_properties_set_uid (MMKernelEventProperties *self,
+ const gchar *uid);
+const gchar *mm_kernel_event_properties_get_uid (MMKernelEventProperties *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMKernelEventProperties *mm_kernel_event_properties_new_from_string (const gchar *str,
+ GError **error);
+
+MMKernelEventProperties *mm_kernel_event_properties_new_from_dictionary (GVariant *dictionary,
+ GError **error);
+
+GVariant *mm_kernel_event_properties_get_dictionary (MMKernelEventProperties *self);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_KERNEL_EVENT_PROPERTIES_H */
diff --git a/libmm-glib/mm-location-3gpp.c b/libmm-glib/mm-location-3gpp.c
index 2de2bab9..ac9a8e20 100644
--- a/libmm-glib/mm-location-3gpp.c
+++ b/libmm-glib/mm-location-3gpp.c
@@ -37,74 +37,107 @@
G_DEFINE_TYPE (MMLocation3gpp, mm_location_3gpp, G_TYPE_OBJECT);
struct _MMLocation3gppPrivate {
- guint mobile_country_code;
- guint mobile_network_code;
+ gchar *operator_code;
gulong location_area_code;
gulong cell_id;
+ gulong tracking_area_code;
};
/*****************************************************************************/
-/**
- * mm_location_3gpp_get_mobile_country_code:
- * @self: a #MMLocation3gpp.
- *
- * Gets the Mobile Country Code of the 3GPP network.
- *
- * Returns: the MCC, or 0 if unknown.
- */
-guint
-mm_location_3gpp_get_mobile_country_code (MMLocation3gpp *self)
+static gboolean
+validate_string_length (const gchar *display,
+ const gchar *str,
+ guint min_length,
+ guint max_length,
+ GError **error)
{
- g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0);
+ /* Avoid empty strings */
+ if (!str || !str[0]) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid %s: none given",
+ display);
+ return FALSE;
+ }
- return self->priv->mobile_country_code;
+ /* Check min length of the field */
+ if (strlen (str) < min_length) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid %s: shorter than the maximum expected (%u): '%s'",
+ display,
+ min_length,
+ str);
+ return FALSE;
+ }
+
+ /* Check max length of the field */
+ if (strlen (str) > max_length) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid %s: longer than the maximum expected (%u): '%s'",
+ display,
+ max_length,
+ str);
+ return FALSE;
+ }
+
+ return TRUE;
}
-gboolean
-mm_location_3gpp_set_mobile_country_code (MMLocation3gpp *self,
- guint mobile_country_code)
+static gboolean
+validate_numeric_string_content (const gchar *display,
+ const gchar *str,
+ gboolean hex,
+ GError **error)
{
- g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
+ guint i;
- /* If no change in the location info, don't do anything */
- if (self->priv->mobile_country_code == mobile_country_code)
- return FALSE;
+ for (i = 0; str[i]; i++) {
+ if ((hex && !g_ascii_isxdigit (str[i])) ||
+ (!hex && !g_ascii_isdigit (str[i]))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid %s: unexpected char (%c): '%s'",
+ display,
+ str[i],
+ str);
+ return FALSE;
+ }
+ }
- self->priv->mobile_country_code = mobile_country_code;
return TRUE;
}
/*****************************************************************************/
/**
- * mm_location_3gpp_get_mobile_network_code:
+ * mm_location_3gpp_get_mobile_country_code:
* @self: a #MMLocation3gpp.
*
- * Gets the Mobile Network Code of the 3GPP network.
+ * Gets the Mobile Country Code of the 3GPP network.
+ *
+ * Returns: the MCC, or 0 if unknown.
*
- * Returns: the MNC, or 0 if unknown.
+ * Since: 1.0
*/
guint
-mm_location_3gpp_get_mobile_network_code (MMLocation3gpp *self)
-{
- g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0);
-
- return self->priv->mobile_network_code;
-}
-
-gboolean
-mm_location_3gpp_set_mobile_network_code (MMLocation3gpp *self,
- guint mobile_network_code)
+mm_location_3gpp_get_mobile_country_code (MMLocation3gpp *self)
{
- g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
+ gchar mcc[4];
- /* If no change in the location info, don't do anything */
- if (self->priv->mobile_network_code == mobile_network_code)
- return FALSE;
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0);
- self->priv->mobile_network_code = mobile_network_code;
- return TRUE;
+ if (!self->priv->operator_code)
+ return 0;
+ memcpy (mcc, self->priv->operator_code, 3);
+ mcc[3] = '\0';
+ return strtol (mcc, NULL, 10);
}
/*****************************************************************************/
@@ -116,6 +149,8 @@ mm_location_3gpp_set_mobile_network_code (MMLocation3gpp *self,
* Gets the location area code of the 3GPP network.
*
* Returns: the location area code, or 0 if unknown.
+ *
+ * Since: 1.0
*/
gulong
mm_location_3gpp_get_location_area_code (MMLocation3gpp *self)
@@ -125,6 +160,9 @@ mm_location_3gpp_get_location_area_code (MMLocation3gpp *self)
return self->priv->location_area_code;
}
+/**
+ * mm_location_3gpp_set_location_area_code: (skip)
+ */
gboolean
mm_location_3gpp_set_location_area_code (MMLocation3gpp *self,
gulong location_area_code)
@@ -148,6 +186,8 @@ mm_location_3gpp_set_location_area_code (MMLocation3gpp *self,
* Gets the cell ID of the 3GPP network.
*
* Returns: the cell ID, or 0 if unknown.
+ *
+ * Since: 1.0
*/
gulong
mm_location_3gpp_get_cell_id (MMLocation3gpp *self)
@@ -157,6 +197,9 @@ mm_location_3gpp_get_cell_id (MMLocation3gpp *self)
return self->priv->cell_id;
}
+/**
+ * mm_location_3gpp_set_cell_id: (skip)
+ */
gboolean
mm_location_3gpp_set_cell_id (MMLocation3gpp *self,
gulong cell_id)
@@ -173,90 +216,150 @@ mm_location_3gpp_set_cell_id (MMLocation3gpp *self,
/*****************************************************************************/
-GVariant *
-mm_location_3gpp_get_string_variant (MMLocation3gpp *self)
+/**
+ * mm_location_3gpp_get_tracking_area_code:
+ * @self: a #MMLocation3gpp.
+ *
+ * Gets the location area code of the 3GPP network.
+ *
+ * Returns: the location area code, or 0 if unknown.
+ *
+ * Since: 1.10
+ */
+gulong
+mm_location_3gpp_get_tracking_area_code (MMLocation3gpp *self)
{
- GVariant *variant = NULL;
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), 0);
+
+ return self->priv->tracking_area_code;
+}
+
+/**
+ * mm_location_3gpp_set_tracking_area_code: (skip)
+ */
+gboolean
+mm_location_3gpp_set_tracking_area_code (MMLocation3gpp *self,
+ gulong tracking_area_code)
+{
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
+
+ /* If no change in the location info, don't do anything */
+ if (self->priv->tracking_area_code == tracking_area_code)
+ return FALSE;
+
+ self->priv->tracking_area_code = tracking_area_code;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/**
+ * mm_location_3gpp_get_operator_code:
+ * @self: A #MMLocation3gpp.
+ *
+ * Gets the 3GPP network Mobile Country Code and Mobile Network Code.
+ *
+ * Returned in the format <literal>"MCCMNC"</literal>, where
+ * <literal>MCC</literal> is the three-digit ITU E.212 Mobile Country Code
+ * and <literal>MNC</literal> is the two- or three-digit GSM Mobile Network
+ * Code. e.g. e<literal>"31026"</literal> or <literal>"310260"</literal>.
+ *
+ * Returns: (transfer none): The operator code, or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_location_3gpp_get_operator_code (MMLocation3gpp *self)
+{
g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), NULL);
- if (self->priv->mobile_country_code &&
- self->priv->mobile_network_code &&
- self->priv->location_area_code &&
- self->priv->cell_id) {
- gchar *str;
+ return self->priv->operator_code;
+}
- str = g_strdup_printf ("%u,%u,%lX,%lX",
- self->priv->mobile_country_code,
- self->priv->mobile_network_code,
- self->priv->location_area_code,
- self->priv->cell_id);
+/**
+ * mm_location_3gpp_set_operator_code: (skip)
+ */
+gboolean
+mm_location_3gpp_set_operator_code (MMLocation3gpp *self,
+ const gchar *operator_code)
+{
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
- variant = g_variant_new_string (str);
- g_free (str);
- }
+ /* If no change in operator code, don't do anything */
+ if (!g_strcmp0 (operator_code, self->priv->operator_code))
+ return FALSE;
- return variant;
+ /* Check the validity here, all other functions expect it's valid. */
+ if (operator_code &&
+ (!validate_string_length ("MCCMNC", operator_code, 5, 6, NULL) ||
+ !validate_numeric_string_content ("MCCMNC", operator_code, FALSE, NULL)))
+ return FALSE;
+
+ g_free (self->priv->operator_code);
+ self->priv->operator_code = g_strdup (operator_code);
+ return TRUE;
}
/*****************************************************************************/
-static gboolean
-validate_string_length (const gchar *display,
- const gchar *str,
- guint max_length,
- GError **error)
+/**
+ * mm_location_3gpp_reset: (skip)
+ */
+gboolean
+mm_location_3gpp_reset (MMLocation3gpp *self)
{
- /* Avoid empty strings */
- if (!str || !str[0]) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Invalid %s: none given",
- display);
- return FALSE;
- }
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), FALSE);
- /* Check max length of the field */
- if (strlen (str) > max_length) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Invalid %s: longer than the maximum expected (%u): '%s'",
- display,
- max_length,
- str);
+ if (self->priv->operator_code == NULL &&
+ self->priv->location_area_code == 0 &&
+ self->priv->tracking_area_code == 0 &&
+ self->priv->cell_id == 0)
return FALSE;
- }
+ g_free (self->priv->operator_code);
+ self->priv->operator_code = NULL;
+ self->priv->location_area_code = 0;
+ self->priv->tracking_area_code = 0;
+ self->priv->cell_id = 0;
return TRUE;
}
-static gboolean
-validate_numeric_string_content (const gchar *display,
- const gchar *str,
- gboolean hex,
- GError **error)
+/*****************************************************************************/
+
+/**
+ * mm_location_3gpp_get_string_variant: (skip)
+ */
+GVariant *
+mm_location_3gpp_get_string_variant (MMLocation3gpp *self)
{
- guint i;
+ GVariant *variant = NULL;
- for (i = 0; str[i]; i++) {
- if ((hex && !g_ascii_isxdigit (str[i])) ||
- (!hex && !g_ascii_isdigit (str[i]))) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Invalid %s: unexpected char (%c): '%s'",
- display,
- str[i],
- str);
- return FALSE;
- }
+ g_return_val_if_fail (MM_IS_LOCATION_3GPP (self), NULL);
+
+ if (self->priv->operator_code &&
+ (self->priv->location_area_code || self->priv->tracking_area_code) &&
+ self->priv->cell_id) {
+ gchar *str;
+
+ str = g_strdup_printf ("%.3s,%s,%lX,%lX,%lX",
+ self->priv->operator_code,
+ self->priv->operator_code + 3,
+ self->priv->location_area_code,
+ self->priv->cell_id,
+ self->priv->tracking_area_code);
+
+ variant = g_variant_ref_sink (g_variant_new_string (str));
+ g_free (str);
}
- return TRUE;
+ return variant;
}
+/*****************************************************************************/
+
+/**
+ * mm_location_3gpp_new_from_string_variant: (skip)
+ */
MMLocation3gpp *
mm_location_3gpp_new_from_string_variant (GVariant *string,
GError **error)
@@ -284,20 +387,26 @@ mm_location_3gpp_new_from_string_variant (GVariant *string,
}
/* Validate fields */
- if (validate_string_length ("MCC", split[0], 3, error) &&
+ if (validate_string_length ("MCC", split[0], 0, 3, error) &&
validate_numeric_string_content ("MCC", split[0], FALSE, error) &&
- validate_string_length ("MNC", split[1], 3, error) &&
+ validate_string_length ("MNC", split[1], 0, 3, error) &&
validate_numeric_string_content ("MNC", split[1], FALSE, error) &&
- validate_string_length ("Location area code", split[2], 4, error) &&
+ validate_string_length ("Location area code", split[2], 0, 4, error) &&
validate_numeric_string_content ("Location area code", split[2], TRUE, error) &&
- validate_string_length ("Cell ID", split[3], 8, error) &&
- validate_numeric_string_content ("Cell ID", split[3], TRUE, error)) {
+ validate_string_length ("Cell ID", split[3], 0, 8, error) &&
+ validate_numeric_string_content ("Cell ID", split[3], TRUE, error) &&
+ validate_string_length ("Tracking area code", split[4], 0, 8, error) &&
+ validate_numeric_string_content ("Tracking area code", split[4], TRUE, error)) {
/* Create new location object */
self = mm_location_3gpp_new ();
- self->priv->mobile_country_code = strtol (split[0], NULL, 10);
- self->priv->mobile_network_code = strtol (split[1], NULL, 10);
+ /* Join MCC and MNC and ensure they are zero-padded to required widths */
+ self->priv->operator_code = g_strdup_printf ("%03lu%0*lu",
+ strtoul (split[0], NULL, 10),
+ strlen (split[1]) == 3 ? 3 : 2,
+ strtoul (split[1], NULL, 10));
self->priv->location_area_code = strtol (split[2], NULL, 16);
self->priv->cell_id = strtol (split[3], NULL, 16);
+ self->priv->tracking_area_code = strtol (split[4], NULL, 16);
}
g_strfreev (split);
@@ -306,6 +415,9 @@ mm_location_3gpp_new_from_string_variant (GVariant *string,
/*****************************************************************************/
+/**
+ * mm_location_3gpp_new: (skip)
+ */
MMLocation3gpp *
mm_location_3gpp_new (void)
{
@@ -322,9 +434,21 @@ mm_location_3gpp_init (MMLocation3gpp *self)
}
static void
+finalize (GObject *object)
+{
+ MMLocation3gpp *self = MM_LOCATION_3GPP (object);
+
+ g_free (self->priv->operator_code);
+
+ G_OBJECT_CLASS (mm_location_3gpp_parent_class)->finalize (object);
+}
+
+static void
mm_location_3gpp_class_init (MMLocation3gppClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMLocation3gppPrivate));
+
+ object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-location-3gpp.h b/libmm-glib/mm-location-3gpp.h
index 99e65a2e..c8c68082 100644
--- a/libmm-glib/mm-location-3gpp.h
+++ b/libmm-glib/mm-location-3gpp.h
@@ -54,11 +54,13 @@ struct _MMLocation3gppClass {
};
GType mm_location_3gpp_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocation3gpp, g_object_unref)
-guint mm_location_3gpp_get_mobile_country_code (MMLocation3gpp *self);
-guint mm_location_3gpp_get_mobile_network_code (MMLocation3gpp *self);
-gulong mm_location_3gpp_get_location_area_code (MMLocation3gpp *self);
-gulong mm_location_3gpp_get_cell_id (MMLocation3gpp *self);
+guint mm_location_3gpp_get_mobile_country_code (MMLocation3gpp *self);
+gulong mm_location_3gpp_get_location_area_code (MMLocation3gpp *self);
+gulong mm_location_3gpp_get_cell_id (MMLocation3gpp *self);
+gulong mm_location_3gpp_get_tracking_area_code (MMLocation3gpp *self);
+const gchar *mm_location_3gpp_get_operator_code (MMLocation3gpp *self);
/*****************************************************************************/
/* ModemManager/libmm-glib/mmcli specific methods */
@@ -73,14 +75,15 @@ MMLocation3gpp *mm_location_3gpp_new (void);
MMLocation3gpp *mm_location_3gpp_new_from_string_variant (GVariant *string,
GError **error);
-gboolean mm_location_3gpp_set_mobile_country_code (MMLocation3gpp *self,
- guint mobile_country_code);
-gboolean mm_location_3gpp_set_mobile_network_code (MMLocation3gpp *self,
- guint mobile_network_code);
-gboolean mm_location_3gpp_set_location_area_code (MMLocation3gpp *self,
- gulong location_area_code);
-gboolean mm_location_3gpp_set_cell_id (MMLocation3gpp *self,
- gulong cell_id);
+gboolean mm_location_3gpp_set_operator_code (MMLocation3gpp *self,
+ const gchar *operator_code);
+gboolean mm_location_3gpp_set_location_area_code (MMLocation3gpp *self,
+ gulong location_area_code);
+gboolean mm_location_3gpp_set_cell_id (MMLocation3gpp *self,
+ gulong cell_id);
+gboolean mm_location_3gpp_set_tracking_area_code (MMLocation3gpp *self,
+ gulong tracking_area_code);
+gboolean mm_location_3gpp_reset (MMLocation3gpp *self);
#endif
diff --git a/libmm-glib/mm-location-cdma-bs.c b/libmm-glib/mm-location-cdma-bs.c
index 993f6bef..d050950b 100644
--- a/libmm-glib/mm-location-cdma-bs.c
+++ b/libmm-glib/mm-location-cdma-bs.c
@@ -53,6 +53,8 @@ struct _MMLocationCdmaBsPrivate {
* Gets the longitude, in the [-180,180] range.
*
* Returns: the longitude, or %MM_LOCATION_LONGITUDE_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gdouble
mm_location_cdma_bs_get_longitude (MMLocationCdmaBs *self)
@@ -72,6 +74,8 @@ mm_location_cdma_bs_get_longitude (MMLocationCdmaBs *self)
* Gets the latitude, in the [-90,90] range.
*
* Returns: the latitude, or %MM_LOCATION_LATITUDE_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gdouble
mm_location_cdma_bs_get_latitude (MMLocationCdmaBs *self)
@@ -84,6 +88,9 @@ mm_location_cdma_bs_get_latitude (MMLocationCdmaBs *self)
/*****************************************************************************/
+/**
+ * mm_location_cdma_bs_set: (skip)
+ */
gboolean
mm_location_cdma_bs_set (MMLocationCdmaBs *self,
gdouble longitude,
@@ -107,6 +114,9 @@ mm_location_cdma_bs_set (MMLocationCdmaBs *self,
/*****************************************************************************/
+/**
+ * mm_location_cdma_bs_get_dictionary: (skip)
+ */
GVariant *
mm_location_cdma_bs_get_dictionary (MMLocationCdmaBs *self)
{
@@ -138,6 +148,9 @@ mm_location_cdma_bs_get_dictionary (MMLocationCdmaBs *self)
/*****************************************************************************/
+/**
+ * mm_location_cdma_bs_new_from_dictionary: (skip)
+ */
MMLocationCdmaBs *
mm_location_cdma_bs_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -192,6 +205,9 @@ mm_location_cdma_bs_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
+/**
+ * mm_location_cdma_bs_new: (skip)
+ */
MMLocationCdmaBs *
mm_location_cdma_bs_new (void)
{
diff --git a/libmm-glib/mm-location-cdma-bs.h b/libmm-glib/mm-location-cdma-bs.h
index ca34e2fc..6e8371b0 100644
--- a/libmm-glib/mm-location-cdma-bs.h
+++ b/libmm-glib/mm-location-cdma-bs.h
@@ -56,6 +56,7 @@ struct _MMLocationCdmaBsClass {
};
GType mm_location_cdma_bs_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationCdmaBs, g_object_unref)
gdouble mm_location_cdma_bs_get_longitude (MMLocationCdmaBs *self);
gdouble mm_location_cdma_bs_get_latitude (MMLocationCdmaBs *self);
diff --git a/libmm-glib/mm-location-common.h b/libmm-glib/mm-location-common.h
index 6397dc6c..afd90f2e 100644
--- a/libmm-glib/mm-location-common.h
+++ b/libmm-glib/mm-location-common.h
@@ -26,8 +26,10 @@
* Identifier for an unknown longitude value.
*
* Proper longitude values fall in the [-180,180] range.
+ *
+ * Since: 1.0
*/
-#define MM_LOCATION_LONGITUDE_UNKNOWN G_MINDOUBLE
+#define MM_LOCATION_LONGITUDE_UNKNOWN -G_MAXDOUBLE
/**
* MM_LOCATION_LATITUDE_UNKNOWN:
@@ -35,14 +37,18 @@
* Identifier for an unknown latitude value.
*
* Proper latitude values fall in the [-90,90] range.
+ *
+ * Since: 1.0
*/
-#define MM_LOCATION_LATITUDE_UNKNOWN G_MINDOUBLE
+#define MM_LOCATION_LATITUDE_UNKNOWN -G_MAXDOUBLE
/**
* MM_LOCATION_ALTITUDE_UNKNOWN:
*
* Identifier for an unknown altitude value.
+ *
+ * Since: 1.0
*/
-#define MM_LOCATION_ALTITUDE_UNKNOWN G_MINDOUBLE
+#define MM_LOCATION_ALTITUDE_UNKNOWN -G_MAXDOUBLE
#endif /* MM_LOCATION_COMMON_H */
diff --git a/libmm-glib/mm-location-gps-nmea.c b/libmm-glib/mm-location-gps-nmea.c
index 08ec97ff..396e24fc 100644
--- a/libmm-glib/mm-location-gps-nmea.c
+++ b/libmm-glib/mm-location-gps-nmea.c
@@ -34,7 +34,7 @@
* mm_modem_location_get_full_sync().
*/
-G_DEFINE_TYPE (MMLocationGpsNmea, mm_location_gps_nmea, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMLocationGpsNmea, mm_location_gps_nmea, G_TYPE_OBJECT)
struct _MMLocationGpsNmeaPrivate {
GHashTable *traces;
@@ -52,7 +52,7 @@ check_append_or_replace (MMLocationGpsNmea *self,
GMatchInfo *match_info = NULL;
if (G_UNLIKELY (!self->priv->sequence_regex))
- self->priv->sequence_regex = g_regex_new ("\\$GPGSV,(\\d),(\\d).*",
+ self->priv->sequence_regex = g_regex_new ("\\$..(?:ALM|GSV|RTE|SFI),(\\d),(\\d).*",
G_REGEX_RAW | G_REGEX_OPTIMIZE,
0,
NULL);
@@ -77,8 +77,10 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self,
gchar *trace_type;
i = strchr (trace, ',');
- if (!i || i == trace)
+ if (!i || i == trace) {
+ g_free (trace);
return FALSE;
+ }
trace_type = g_malloc (i - trace + 1);
memcpy (trace_type, trace, i - trace);
@@ -96,8 +98,11 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self,
gchar *sequence;
/* Skip the trace if we already have it there */
- if (strstr (previous, trace))
+ if (strstr (previous, trace)) {
+ g_free (trace_type);
+ g_free (trace);
return TRUE;
+ }
sequence = g_strdup_printf ("%s%s%s",
previous,
@@ -114,6 +119,9 @@ location_gps_nmea_take_trace (MMLocationGpsNmea *self,
return TRUE;
}
+/**
+ * mm_location_gps_nmea_add_trace: (skip)
+ */
gboolean
mm_location_gps_nmea_add_trace (MMLocationGpsNmea *self,
const gchar *trace)
@@ -130,7 +138,10 @@ mm_location_gps_nmea_add_trace (MMLocationGpsNmea *self,
*
* Gets the last cached value of the specific @trace_type given.
*
- * Returns: the NMEA trace, or %NULL if not available. Do not free the returned value, it is owned by @self.
+ * Returns: the NMEA trace, or %NULL if not available. Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self,
@@ -142,55 +153,65 @@ mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self,
/*****************************************************************************/
static void
-build_full_foreach (const gchar *trace_type,
- const gchar *trace,
- GString **built)
+build_all_foreach (const gchar *trace_type,
+ const gchar *trace,
+ GPtrArray **built)
{
- if ((*built)->len == 0 || g_str_has_suffix ((*built)->str, "\r\n"))
- g_string_append (*built, trace);
- else
- g_string_append_printf (*built, "\r\n%s", trace);
+ if (*built == NULL)
+ *built = g_ptr_array_new ();
+ g_ptr_array_add (*built, g_strdup (trace));
}
/**
- * mm_location_gps_nmea_build_full:
+ * mm_location_gps_nmea_get_traces:
* @self: a #MMLocationGpsNmea.
*
- * Gets a compilation of all cached traces.
+ * Gets all cached traces.
*
- * Returns: (transfer full): a string containing all traces, or #NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The list of traces, or %NULL if none available. The returned value should be freed with g_strfreev().
+ * Since: 1.14
*/
-gchar *
-mm_location_gps_nmea_build_full (MMLocationGpsNmea *self)
+gchar **
+mm_location_gps_nmea_get_traces (MMLocationGpsNmea *self)
{
- GString *built;
+ GPtrArray *built = NULL;
+
+ g_return_val_if_fail (MM_IS_LOCATION_GPS_NMEA (self), NULL);
- built = g_string_new ("");
g_hash_table_foreach (self->priv->traces,
- (GHFunc)build_full_foreach,
+ (GHFunc)build_all_foreach,
&built);
- return g_string_free (built, FALSE);
+ if (!built)
+ return NULL;
+
+ g_ptr_array_add (built, NULL);
+ return (gchar **) g_ptr_array_free (built, FALSE);
}
/*****************************************************************************/
+/**
+ * mm_location_gps_nmea_get_string_variant: (skip)
+ */
GVariant *
mm_location_gps_nmea_get_string_variant (MMLocationGpsNmea *self)
{
- GVariant *variant = NULL;
- gchar *built;
+ g_autofree gchar *built = NULL;
+ g_auto (GStrv) traces = NULL;
g_return_val_if_fail (MM_IS_LOCATION_GPS_NMEA (self), NULL);
- built = mm_location_gps_nmea_build_full (self);
- variant = g_variant_new_string (built);
- g_free (built);
-
- return variant;
+ traces = mm_location_gps_nmea_get_traces (self);
+ if (traces)
+ built = g_strjoinv ("\r\n", traces);
+ return g_variant_ref_sink (g_variant_new_string (built ? built : ""));
}
/*****************************************************************************/
+/**
+ * mm_location_gps_nmea_new_from_string_variant: (skip)
+ */
MMLocationGpsNmea *
mm_location_gps_nmea_new_from_string_variant (GVariant *string,
GError **error)
@@ -222,8 +243,7 @@ mm_location_gps_nmea_new_from_string_variant (GVariant *string,
self = mm_location_gps_nmea_new ();
for (i = 0; split[i]; i++) {
- if (!location_gps_nmea_take_trace (self, split[i]))
- g_free (split[i]);
+ location_gps_nmea_take_trace (self, split[i]);
}
/* Note that the strings in the array of strings were already taken
@@ -235,6 +255,9 @@ mm_location_gps_nmea_new_from_string_variant (GVariant *string,
/*****************************************************************************/
+/**
+ * mm_location_gps_nmea_new: (skip)
+ */
MMLocationGpsNmea *
mm_location_gps_nmea_new (void)
{
@@ -245,7 +268,7 @@ mm_location_gps_nmea_new (void)
static void
mm_location_gps_nmea_init (MMLocationGpsNmea *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_LOCATION_GPS_NMEA,
MMLocationGpsNmeaPrivate);
diff --git a/libmm-glib/mm-location-gps-nmea.h b/libmm-glib/mm-location-gps-nmea.h
index 3317074b..d0f8deb1 100644
--- a/libmm-glib/mm-location-gps-nmea.h
+++ b/libmm-glib/mm-location-gps-nmea.h
@@ -54,10 +54,11 @@ struct _MMLocationGpsNmeaClass {
};
GType mm_location_gps_nmea_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationGpsNmea, g_object_unref)
-const gchar *mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self,
- const gchar *trace_type);
-gchar *mm_location_gps_nmea_build_full (MMLocationGpsNmea *self);
+const gchar *mm_location_gps_nmea_get_trace (MMLocationGpsNmea *self,
+ const gchar *trace_type);
+gchar **mm_location_gps_nmea_get_traces (MMLocationGpsNmea *self);
/*****************************************************************************/
/* ModemManager/libmm-glib/mmcli specific methods */
diff --git a/libmm-glib/mm-location-gps-raw.c b/libmm-glib/mm-location-gps-raw.c
index 4418eff2..a5759e07 100644
--- a/libmm-glib/mm-location-gps-raw.c
+++ b/libmm-glib/mm-location-gps-raw.c
@@ -34,7 +34,7 @@
* mm_modem_location_get_full_sync().
*/
-G_DEFINE_TYPE (MMLocationGpsRaw, mm_location_gps_raw, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMLocationGpsRaw, mm_location_gps_raw, G_TYPE_OBJECT)
#define PROPERTY_UTC_TIME "utc-time"
#define PROPERTY_LATITUDE "latitude"
@@ -42,7 +42,8 @@ G_DEFINE_TYPE (MMLocationGpsRaw, mm_location_gps_raw, G_TYPE_OBJECT);
#define PROPERTY_ALTITUDE "altitude"
struct _MMLocationGpsRawPrivate {
- GRegex *gpgga_regex;
+ GRegex *gga_regex;
+ gboolean prefer_gngga;
gchar *utc_time;
gdouble latitude;
@@ -58,7 +59,10 @@ struct _MMLocationGpsRawPrivate {
*
* Gets the UTC time of the location being reported.
*
- * Returns: a string with the UTC time, or #NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: a string with the UTC time, or #NULL if unknown. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_location_gps_raw_get_utc_time (MMLocationGpsRaw *self)
@@ -77,6 +81,8 @@ mm_location_gps_raw_get_utc_time (MMLocationGpsRaw *self)
* Gets the longitude, in the [-180,180] range.
*
* Returns: the longitude, or %MM_LOCATION_LONGITUDE_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gdouble
mm_location_gps_raw_get_longitude (MMLocationGpsRaw *self)
@@ -96,6 +102,8 @@ mm_location_gps_raw_get_longitude (MMLocationGpsRaw *self)
* Gets the latitude, in the [-90,90] range.
*
* Returns: the latitude, or %MM_LOCATION_LATITUDE_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gdouble
mm_location_gps_raw_get_latitude (MMLocationGpsRaw *self)
@@ -115,6 +123,8 @@ mm_location_gps_raw_get_latitude (MMLocationGpsRaw *self)
* Gets the altitude, in the [-90,90] range.
*
* Returns: the altitude, or %MM_LOCATION_ALTITUDE_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gdouble
mm_location_gps_raw_get_altitude (MMLocationGpsRaw *self)
@@ -166,15 +176,31 @@ out:
return ret;
}
+/**
+ * mm_location_gps_raw_add_trace: (skip)
+ */
gboolean
mm_location_gps_raw_add_trace (MMLocationGpsRaw *self,
const gchar *trace)
{
GMatchInfo *match_info = NULL;
- /* Current implementation works only with $GPGGA traces */
- if (!g_str_has_prefix (trace, "$GPGGA"))
+ /* Current implementation works only with $GPGGA and $GNGGA traces */
+ do {
+ if (g_str_has_prefix (trace, "$GPGGA")) {
+ if (self->priv->prefer_gngga)
+ /* Ignore GPGGA, prefer GNGGA */
+ return FALSE;
+ break;
+ }
+ if (g_str_has_prefix (trace, "$GNGGA")) {
+ if (!self->priv->prefer_gngga)
+ self->priv->prefer_gngga = TRUE;
+ break;
+ }
+ /* Otherwise, ignore trace */
return FALSE;
+ } while (0);
/*
* $GPGGA,hhmmss.ss,llll.ll,a,yyyyy.yy,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh
@@ -195,16 +221,15 @@ mm_location_gps_raw_add_trace (MMLocationGpsRaw *self,
* 14 = Diff. reference station ID#
* 15 = Checksum
*/
- if (G_UNLIKELY (!self->priv->gpgga_regex))
- self->priv->gpgga_regex = g_regex_new ("\\$GPGGA,(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*)\\*(.*).*",
- G_REGEX_RAW | G_REGEX_OPTIMIZE,
- 0,
- NULL);
+ if (G_UNLIKELY (!self->priv->gga_regex))
+ self->priv->gga_regex = g_regex_new ("\\$G(?:P|N)GGA,(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*),(.*)\\*(.*).*",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
- if (g_regex_match (self->priv->gpgga_regex, trace, 0, &match_info)) {
+ if (g_regex_match (self->priv->gga_regex, trace, 0, &match_info)) {
/* UTC time */
- if (self->priv->utc_time)
- g_free (self->priv->utc_time);
+ g_free (self->priv->utc_time);
self->priv->utc_time = g_match_info_fetch (match_info, 1);
/* Latitude */
@@ -243,6 +268,9 @@ mm_location_gps_raw_add_trace (MMLocationGpsRaw *self,
/*****************************************************************************/
+/**
+ * mm_location_gps_raw_get_dictionary: (skip)
+ */
GVariant *
mm_location_gps_raw_get_dictionary (MMLocationGpsRaw *self)
{
@@ -286,6 +314,9 @@ mm_location_gps_raw_get_dictionary (MMLocationGpsRaw *self)
/*****************************************************************************/
+/**
+ * mm_location_gps_raw_new_from_dictionary: (skip)
+ */
MMLocationGpsRaw *
mm_location_gps_raw_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -346,6 +377,9 @@ mm_location_gps_raw_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
+/**
+ * mm_location_gps_raw_new: (skip)
+ */
MMLocationGpsRaw *
mm_location_gps_raw_new (void)
{
@@ -356,7 +390,7 @@ mm_location_gps_raw_new (void)
static void
mm_location_gps_raw_init (MMLocationGpsRaw *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_LOCATION_GPS_RAW,
MMLocationGpsRawPrivate);
@@ -371,8 +405,10 @@ finalize (GObject *object)
{
MMLocationGpsRaw *self = MM_LOCATION_GPS_RAW (object);
- if (self->priv->gpgga_regex)
- g_regex_unref (self->priv->gpgga_regex);
+ if (self->priv->gga_regex)
+ g_regex_unref (self->priv->gga_regex);
+
+ g_free (self->priv->utc_time);
G_OBJECT_CLASS (mm_location_gps_raw_parent_class)->finalize (object);
}
diff --git a/libmm-glib/mm-location-gps-raw.h b/libmm-glib/mm-location-gps-raw.h
index 8ef3747d..20fb3dc8 100644
--- a/libmm-glib/mm-location-gps-raw.h
+++ b/libmm-glib/mm-location-gps-raw.h
@@ -56,6 +56,7 @@ struct _MMLocationGpsRawClass {
};
GType mm_location_gps_raw_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMLocationGpsRaw, g_object_unref)
const gchar *mm_location_gps_raw_get_utc_time (MMLocationGpsRaw *self);
gdouble mm_location_gps_raw_get_longitude (MMLocationGpsRaw *self);
diff --git a/libmm-glib/mm-manager.c b/libmm-glib/mm-manager.c
index 8275f791..1100b2d9 100644
--- a/libmm-glib/mm-manager.c
+++ b/libmm-glib/mm-manager.c
@@ -17,14 +17,14 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2011 - 2012 Aleksander Morgado <aleksander@gnu.org>
* Copyright (C) 2011 - 2012 Google, Inc.
- *
- * Author: Aleksander Morgado <aleksander@lanedo.com>
+ * Copyright (C) 2011 - 2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <ModemManager.h>
+#include "mm-helpers.h"
+#include "mm-common-helpers.h"
#include "mm-errors-types.h"
#include "mm-gdbus-manager.h"
#include "mm-manager.h"
@@ -66,18 +66,20 @@ get_proxy_type (GDBusObjectManagerClient *manager,
if (g_once_init_enter (&once_init_value)) {
lookup_hash = g_hash_table_new (g_str_hash, g_str_equal);
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem", GSIZE_TO_POINTER (MM_TYPE_MODEM));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Messaging", GSIZE_TO_POINTER (MM_TYPE_MODEM_MESSAGING));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Location", GSIZE_TO_POINTER (MM_TYPE_MODEM_LOCATION));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Time", GSIZE_TO_POINTER (MM_TYPE_MODEM_TIME));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Signal", GSIZE_TO_POINTER (MM_TYPE_MODEM_SIGNAL));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Firmware", GSIZE_TO_POINTER (MM_TYPE_MODEM_FIRMWARE));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Oma", GSIZE_TO_POINTER (MM_TYPE_MODEM_OMA));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.ModemCdma", GSIZE_TO_POINTER (MM_TYPE_MODEM_CDMA));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Modem3gpp", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP_USSD));
- g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Simple", GSIZE_TO_POINTER (MM_TYPE_MODEM_SIMPLE));
- /* g_hash_table_insert (lookup_hash, "org.freedesktop.ModemManager1.Modem.Contacts", GSIZE_TO_POINTER (MM_GDBUS_TYPE_MODEM_CONTACTS_PROXY)); */
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem", GSIZE_TO_POINTER (MM_TYPE_MODEM));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Messaging", GSIZE_TO_POINTER (MM_TYPE_MODEM_MESSAGING));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Voice", GSIZE_TO_POINTER (MM_TYPE_MODEM_VOICE));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Location", GSIZE_TO_POINTER (MM_TYPE_MODEM_LOCATION));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Time", GSIZE_TO_POINTER (MM_TYPE_MODEM_TIME));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Sar", GSIZE_TO_POINTER (MM_TYPE_MODEM_SAR));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Signal", GSIZE_TO_POINTER (MM_TYPE_MODEM_SIGNAL));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Firmware", GSIZE_TO_POINTER (MM_TYPE_MODEM_FIRMWARE));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Oma", GSIZE_TO_POINTER (MM_TYPE_MODEM_OMA));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.ModemCdma", GSIZE_TO_POINTER (MM_TYPE_MODEM_CDMA));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Modem3gpp", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP_PROFILE_MANAGER));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd", GSIZE_TO_POINTER (MM_TYPE_MODEM_3GPP_USSD));
+ g_hash_table_insert (lookup_hash, (gpointer) "org.freedesktop.ModemManager1.Modem.Simple", GSIZE_TO_POINTER (MM_TYPE_MODEM_SIMPLE));
g_once_init_leave (&once_init_value, 1);
}
@@ -89,13 +91,23 @@ get_proxy_type (GDBusObjectManagerClient *manager,
/*****************************************************************************/
+static void
+cleanup_modem_manager1_proxy (MMManager *self)
+{
+ if (self->priv->manager_iface_proxy) {
+ g_signal_handlers_disconnect_by_func (self, cleanup_modem_manager1_proxy, NULL);
+ g_clear_object (&self->priv->manager_iface_proxy);
+ }
+}
+
static gboolean
ensure_modem_manager1_proxy (MMManager *self,
GError **error)
{
gchar *name = NULL;
gchar *object_path = NULL;
- GDBusProxyFlags flags = G_DBUS_PROXY_FLAGS_NONE;
+ GDBusObjectManagerClientFlags obj_manager_flags = G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE;
+ GDBusProxyFlags proxy_flags = G_DBUS_PROXY_FLAGS_NONE;
GDBusConnection *connection = NULL;
if (self->priv->manager_iface_proxy)
@@ -105,13 +117,16 @@ ensure_modem_manager1_proxy (MMManager *self,
g_object_get (self,
"name", &name,
"object-path", &object_path,
- "flags", &flags,
+ "flags", &obj_manager_flags,
"connection", &connection,
NULL);
+ if (obj_manager_flags & G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START)
+ proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START;
+
self->priv->manager_iface_proxy =
mm_gdbus_org_freedesktop_modem_manager1_proxy_new_sync (connection,
- flags,
+ proxy_flags,
name,
object_path,
NULL,
@@ -120,6 +135,12 @@ ensure_modem_manager1_proxy (MMManager *self,
g_free (object_path);
g_free (name);
+ if (self->priv->manager_iface_proxy)
+ g_signal_connect (self,
+ "notify::name-owner",
+ G_CALLBACK (cleanup_modem_manager1_proxy),
+ NULL);
+
return !!self->priv->manager_iface_proxy;
}
@@ -127,21 +148,28 @@ ensure_modem_manager1_proxy (MMManager *self,
/**
* mm_manager_new_finish:
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_manager_new().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_manager_new().
* @error: Return location for error or %NULL
*
* Finishes an operation started with mm_manager_new().
*
- * Returns: (transfer full) (type MMManager): The constructed object manager client or %NULL if @error is set.
+ * Returns: (transfer full) (type MMManager): The constructed object manager
+ * client or %NULL if @error is set.
+ *
+ * Since: 1.0
*/
MMManager *
mm_manager_new_finish (GAsyncResult *res,
GError **error)
{
- GDBusObjectManager *ret;
+ GObject *ret;
+ GObject *source_object;
- ret = mm_gdbus_object_manager_client_new_finish (res, error);
- return (ret ? MM_MANAGER (ret) : NULL);
+ source_object = g_async_result_get_source_object (res);
+ ret = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, error);
+ g_object_unref (source_object);
+ return MM_MANAGER (ret);
}
/**
@@ -160,7 +188,10 @@ mm_manager_new_finish (GAsyncResult *res,
*
* You can then call mm_manager_new_finish() to get the result of the operation.
*
- * See mm_manager_new_sync() for the synchronous, blocking version of this constructor.
+ * See mm_manager_new_sync() for the synchronous, blocking version of this
+ * constructor.
+ *
+ * Since: 1.0
*/
void
mm_manager_new (GDBusConnection *connection,
@@ -195,7 +226,10 @@ mm_manager_new (GDBusConnection *connection,
*
* See mm_manager_new() for the asynchronous version of this constructor.
*
- * Returns: (transfer full) (type MMManager): The constructed object manager client or %NULL if @error is set.
+ * Returns: (transfer full) (type MMManager): The constructed object manager
+ * client or %NULL if @error is set.
+ *
+ * Since: 1.0
*/
MMManager *
mm_manager_new_sync (GDBusConnection *connection,
@@ -203,19 +237,15 @@ mm_manager_new_sync (GDBusConnection *connection,
GCancellable *cancellable,
GError **error)
{
- GInitable *ret;
-
- ret = g_initable_new (MM_TYPE_MANAGER,
- cancellable,
- error,
- "name", MM_DBUS_SERVICE,
- "object-path", MM_DBUS_PATH,
- "flags", flags,
- "connection", connection,
- "get-proxy-type-func", get_proxy_type,
- NULL);
-
- return (ret ? MM_MANAGER (ret) : NULL);
+ return MM_MANAGER (g_initable_new (MM_TYPE_MANAGER,
+ cancellable,
+ error,
+ "name", MM_DBUS_SERVICE,
+ "object-path", MM_DBUS_PATH,
+ "flags", flags,
+ "connection", connection,
+ "get-proxy-type-func", get_proxy_type,
+ NULL));
}
/*****************************************************************************/
@@ -226,7 +256,10 @@ mm_manager_new_sync (GDBusConnection *connection,
*
* Gets the #GDBusProxy interface of the @manager.
*
- * Returns: (transfer none): The #GDBusProxy interface of @manager, or #NULL if none. Do not free the returned object, it is owned by @manager.
+ * Returns: (transfer none): The #GDBusProxy interface of @manager, or #NULL if
+ * none. Do not free the returned object, it is owned by @manager.
+ *
+ * Since: 1.0
*/
GDBusProxy *
mm_manager_peek_proxy (MMManager *manager)
@@ -245,7 +278,10 @@ mm_manager_peek_proxy (MMManager *manager)
*
* Gets the #GDBusProxy interface of the @manager.
*
- * Returns: (transfer full): The #GDBusProxy interface of @manager, or #NULL if none. The returned object must be freed with g_object_unref().
+ * Returns: (transfer full): The #GDBusProxy interface of @manager, or #NULL if
+ * none. The returned object must be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
GDBusProxy *
mm_manager_get_proxy (MMManager *manager)
@@ -261,40 +297,69 @@ mm_manager_get_proxy (MMManager *manager)
/*****************************************************************************/
/**
+ * mm_manager_get_version:
+ * @manager: A #MMManager.
+ *
+ * Gets the ModemManager version, as reported by the daemon.
+ *
+ * It is safe to assume this value never changes during runtime.
+ *
+ * Returns: (transfer none): The version, or %NULL if none available. Do not
+ * free the returned value, it belongs to @self.
+ *
+ * Since: 1.0
+ */
+const gchar *
+mm_manager_get_version (MMManager *manager)
+{
+ g_return_val_if_fail (MM_IS_MANAGER (manager), NULL);
+
+ if (!ensure_modem_manager1_proxy (manager, NULL))
+ return NULL;
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_org_freedesktop_modem_manager1_get_version (manager->priv->manager_iface_proxy));
+}
+
+/*****************************************************************************/
+
+/**
* mm_manager_set_logging_finish:
* @manager: A #MMManager.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_manager_set_logging().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_manager_set_logging().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_manager_set_logging().
*
- * Returns: %TRUE if the call succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_manager_set_logging_finish (MMManager *manager,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
set_logging_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
- if (mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish (
+ if (!mm_gdbus_org_freedesktop_modem_manager1_call_set_logging_finish (
manager_iface_proxy,
res,
&error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
/**
@@ -302,7 +367,8 @@ set_logging_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
* @manager: A #MMManager.
* @level: the login level to set.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to set the specified logging level in the daemon.
@@ -312,7 +378,10 @@ set_logging_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
* of the thread you are calling this method from. You can then call
* mm_manager_set_logging_finish() to get the result of the operation.
*
- * See mm_manager_set_logging_sync() for the synchronous, blocking version of this method.
+ * See mm_manager_set_logging_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.0
*/
void
mm_manager_set_logging (MMManager *manager,
@@ -321,20 +390,16 @@ mm_manager_set_logging (MMManager *manager,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
GError *inner_error = NULL;
g_return_if_fail (MM_IS_MANAGER (manager));
- result = g_simple_async_result_new (G_OBJECT (manager),
- callback,
- user_data,
- mm_manager_set_logging);
+ task = g_task_new (manager, cancellable, callback, user_data);
if (!ensure_modem_manager1_proxy (manager, &inner_error)) {
- g_simple_async_result_take_error (result, inner_error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, inner_error);
+ g_object_unref (task);
return;
}
@@ -343,7 +408,7 @@ mm_manager_set_logging (MMManager *manager,
level,
cancellable,
(GAsyncReadyCallback)set_logging_ready,
- result);
+ task);
}
/**
@@ -353,13 +418,15 @@ mm_manager_set_logging (MMManager *manager,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously requests to set the specified logging level in the daemon..
+ * Synchronously requests to set the specified logging level in the daemon.
*
* The calling thread is blocked until a reply is received.
*
* See mm_manager_set_logging() for the asynchronous version of this method.
*
- * Returns: %TRUE if the call succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_manager_set_logging_sync (MMManager *manager,
@@ -384,45 +451,48 @@ mm_manager_set_logging_sync (MMManager *manager,
/**
* mm_manager_scan_devices_finish:
* @manager: A #MMManager.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_manager_scan_devices().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_manager_scan_devices().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_manager_scan_devices().
*
- * Returns: %TRUE if the call succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_manager_scan_devices_finish (MMManager *manager,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
scan_devices_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
- if (mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish (
+ if (!mm_gdbus_org_freedesktop_modem_manager1_call_scan_devices_finish (
manager_iface_proxy,
res,
&error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
/**
* mm_manager_scan_devices:
* @manager: A #MMManager.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to scan looking for devices.
@@ -432,7 +502,10 @@ scan_devices_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
* of the thread you are calling this method from. You can then call
* mm_manager_scan_devices_finish() to get the result of the operation.
*
- * See mm_manager_scan_devices_sync() for the synchronous, blocking version of this method.
+ * See mm_manager_scan_devices_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.0
*/
void
mm_manager_scan_devices (MMManager *manager,
@@ -440,20 +513,16 @@ mm_manager_scan_devices (MMManager *manager,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
GError *inner_error = NULL;
g_return_if_fail (MM_IS_MANAGER (manager));
- result = g_simple_async_result_new (G_OBJECT (manager),
- callback,
- user_data,
- mm_manager_scan_devices);
+ task = g_task_new (manager, cancellable, callback, user_data);
if (!ensure_modem_manager1_proxy (manager, &inner_error)) {
- g_simple_async_result_take_error (result, inner_error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, inner_error);
+ g_object_unref (task);
return;
}
@@ -461,7 +530,7 @@ mm_manager_scan_devices (MMManager *manager,
manager->priv->manager_iface_proxy,
cancellable,
(GAsyncReadyCallback)scan_devices_ready,
- result);
+ task);
}
/**
@@ -476,7 +545,9 @@ mm_manager_scan_devices (MMManager *manager,
*
* See mm_manager_scan_devices() for the asynchronous version of this method.
*
- * Returns: %TRUE if the call succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_manager_scan_devices_sync (MMManager *manager,
@@ -496,30 +567,407 @@ mm_manager_scan_devices_sync (MMManager *manager,
/*****************************************************************************/
+/**
+ * mm_manager_report_kernel_event_finish:
+ * @manager: A #MMManager.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_manager_report_kernel_event().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_manager_report_kernel_event().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.8
+ */
+gboolean
+mm_manager_report_kernel_event_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
static void
-register_dbus_errors (void)
+report_kernel_event_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
+ GAsyncResult *res,
+ GTask *task)
{
- static volatile guint32 aux = 0;
+ GError *error = NULL;
- if (aux)
- return;
+ if (!mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_finish (
+ manager_iface_proxy,
+ res,
+ &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
- /* Register all known own errors */
- aux |= MM_CORE_ERROR;
- aux |= MM_MOBILE_EQUIPMENT_ERROR;
- aux |= MM_CONNECTION_ERROR;
- aux |= MM_SERIAL_ERROR;
- aux |= MM_MESSAGE_ERROR;
- aux |= MM_CDMA_ACTIVATION_ERROR;
+/**
+ * mm_manager_report_kernel_event:
+ * @manager: A #MMManager.
+ * @properties: the properties of the kernel event.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously report kernel event.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_manager_report_kernel_event_finish() to get the result of the operation.
+ *
+ * See mm_manager_report_kernel_event_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.8
+ */
+void
+mm_manager_report_kernel_event (MMManager *manager,
+ MMKernelEventProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GError *inner_error = NULL;
+ GVariant *dictionary;
+
+ g_return_if_fail (MM_IS_MANAGER (manager));
+
+ task = g_task_new (manager, cancellable, callback, user_data);
+
+ if (!ensure_modem_manager1_proxy (manager, &inner_error)) {
+ g_task_return_error (task, inner_error);
+ g_object_unref (task);
+ return;
+ }
+
+ dictionary = mm_kernel_event_properties_get_dictionary (properties);
+ mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event (
+ manager->priv->manager_iface_proxy,
+ dictionary,
+ cancellable,
+ (GAsyncReadyCallback)report_kernel_event_ready,
+ task);
+ g_variant_unref (dictionary);
}
+/**
+ * mm_manager_report_kernel_event_sync:
+ * @manager: A #MMManager.
+ * @properties: the properties of the kernel event.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously report kernel event.
+ *
+ * The calling thread is blocked until a reply is received.
+ *
+ * See mm_manager_report_kernel_event() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.8
+ */
+gboolean
+mm_manager_report_kernel_event_sync (MMManager *manager,
+ MMKernelEventProperties *properties,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *dictionary;
+ gboolean result;
+
+ g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
+
+ if (!ensure_modem_manager1_proxy (manager, error))
+ return FALSE;
+
+ dictionary = mm_kernel_event_properties_get_dictionary (properties);
+ result = (mm_gdbus_org_freedesktop_modem_manager1_call_report_kernel_event_sync (
+ manager->priv->manager_iface_proxy,
+ dictionary,
+ cancellable,
+ error));
+ g_variant_unref (dictionary);
+ return result;
+}
+
+/*****************************************************************************/
+
+static gboolean
+common_inhibit_device_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+inhibit_ready (MmGdbusOrgFreedesktopModemManager1 *manager_iface_proxy,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_finish (manager_iface_proxy, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_inhibit_device (MMManager *manager,
+ const gchar *uid,
+ gboolean inhibit,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GError *inner_error = NULL;
+
+ task = g_task_new (manager, cancellable, callback, user_data);
+
+ if (!ensure_modem_manager1_proxy (manager, &inner_error)) {
+ g_task_return_error (task, inner_error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device (
+ manager->priv->manager_iface_proxy,
+ uid,
+ inhibit,
+ cancellable,
+ (GAsyncReadyCallback)inhibit_ready,
+ task);
+}
+
+static gboolean
+common_inhibit_device_sync (MMManager *manager,
+ const gchar *uid,
+ gboolean inhibit,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (!ensure_modem_manager1_proxy (manager, error))
+ return FALSE;
+
+ return (mm_gdbus_org_freedesktop_modem_manager1_call_inhibit_device_sync (
+ manager->priv->manager_iface_proxy,
+ uid,
+ inhibit,
+ cancellable,
+ error));
+}
+
+/**
+ * mm_manager_inhibit_device_finish:
+ * @manager: A #MMManager.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_manager_inhibit_device().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_manager_inhibit_device().
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_manager_inhibit_device_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
+ return common_inhibit_device_finish (manager, res, error);
+}
+
+/**
+ * mm_manager_inhibit_device:
+ * @manager: A #MMManager.
+ * @uid: the unique ID of the physical device.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to add an inhibition on the device identified by
+ * @uid.
+ *
+ * The @uid must be the unique ID retrieved from an existing #MMModem using
+ * mm_modem_get_device(). The caller should keep track of this @uid and use it
+ * in the mm_manager_uninhibit_device() call when the inhibition is no longer
+ * required.
+ *
+ * The inhibition added with this method may also be automatically removed when
+ * the caller program disappears from the bus (e.g. if the program ends before
+ * having called mm_manager_uninhibit_device() explicitly).
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_manager_inhibit_device_finish() to get the result of the operation.
+ *
+ * See mm_manager_inhibit_device_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.10
+ */
+void
+mm_manager_inhibit_device (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MANAGER (manager));
+ common_inhibit_device (manager, uid, TRUE, cancellable, callback, user_data);
+}
+
+/**
+ * mm_manager_inhibit_device_sync:
+ * @manager: A #MMManager.
+ * @uid: the unique ID of the physical device.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to add an inhibition on the device identified by @uid.
+ *
+ * The @uid must be the unique ID retrieved from an existing #MMModem using
+ * mm_modem_get_device(). The caller should keep track of this @uid and use it
+ * in the mm_manager_uninhibit_device_sync() call when the inhibition is no
+ * longer required.
+ *
+ * The inhibition added with this method may also be automatically removed when
+ * the caller program disappears from the bus (e.g. if the program ends before
+ * having called mm_manager_uninhibit_device_sync() explicitly).
+ *
+ * See mm_manager_inhibit_device() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_manager_inhibit_device_sync (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
+ return common_inhibit_device_sync (manager, uid, TRUE, cancellable, error);
+}
+
+/**
+ * mm_manager_uninhibit_device_finish:
+ * @manager: A #MMManager.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_manager_uninhibit_device().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_manager_uninhibit_device().
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_manager_uninhibit_device_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
+ return common_inhibit_device_finish (manager, res, error);
+}
+
+/**
+ * mm_manager_uninhibit_device:
+ * @manager: A #MMManager.
+ * @uid: the unique ID of the physical device.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to remove an inhibition on the device identified by
+ * @uid.
+ *
+ * The @uid must be the same unique ID that was sent in the inhibition request.
+ *
+ * Only the same program that placed an inhibition on a given device is able to
+ * remove the inhibition.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_manager_uninhibit_device_finish() to get the result of the operation.
+ *
+ * See mm_manager_uninhibit_device_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.10
+ */
+void
+mm_manager_uninhibit_device (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MANAGER (manager));
+ common_inhibit_device (manager, uid, FALSE, cancellable, callback, user_data);
+}
+
+/**
+ * mm_manager_uninhibit_device_sync:
+ * @manager: A #MMManager.
+ * @uid: the unique ID of the physical device.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to remove an inhibition on the device identified by
+ * @uid.
+ *
+ * The @uid must be the same unique ID that was sent in the inhibition request.
+ *
+ * Only the same program that placed an inhibition on a given device is able to
+ * remove the inhibition.
+ *
+ * See mm_manager_uninhibit_device() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_manager_uninhibit_device_sync (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MANAGER (manager), FALSE);
+ return common_inhibit_device_sync (manager, uid, FALSE, cancellable, error);
+}
+
+/*****************************************************************************/
+
static void
mm_manager_init (MMManager *manager)
{
- register_dbus_errors ();
+ mm_common_register_errors ();
/* Setup private data */
- manager->priv = G_TYPE_INSTANCE_GET_PRIVATE ((manager),
+ manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
MM_TYPE_MANAGER,
MMManagerPrivate);
}
diff --git a/libmm-glib/mm-manager.h b/libmm-glib/mm-manager.h
index 96c3066a..c1f514e1 100644
--- a/libmm-glib/mm-manager.h
+++ b/libmm-glib/mm-manager.h
@@ -33,6 +33,7 @@
#include <ModemManager.h>
#include "mm-gdbus-modem.h"
+#include "mm-kernel-event-properties.h"
G_BEGIN_DECLS
@@ -65,6 +66,7 @@ struct _MMManagerClass {
};
GType mm_manager_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMManager, g_object_unref)
void mm_manager_new (
GDBusConnection *connection,
@@ -84,6 +86,8 @@ MMManager *mm_manager_new_sync (
GDBusProxy *mm_manager_peek_proxy (MMManager *manager);
GDBusProxy *mm_manager_get_proxy (MMManager *manager);
+const gchar *mm_manager_get_version (MMManager *manager);
+
void mm_manager_set_logging (MMManager *manager,
const gchar *level,
GCancellable *cancellable,
@@ -108,6 +112,45 @@ gboolean mm_manager_scan_devices_sync (MMManager *manager,
GCancellable *cancellable,
GError **error);
+void mm_manager_report_kernel_event (MMManager *manager,
+ MMKernelEventProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_manager_report_kernel_event_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_manager_report_kernel_event_sync (MMManager *manager,
+ MMKernelEventProperties *properties,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_manager_inhibit_device (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_manager_inhibit_device_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_manager_inhibit_device_sync (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_manager_uninhibit_device (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_manager_uninhibit_device_finish (MMManager *manager,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_manager_uninhibit_device_sync (MMManager *manager,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error);
+
G_END_DECLS
#endif /* _MM_MANAGER_H_ */
diff --git a/libmm-glib/mm-modem-3gpp-profile-manager.c b/libmm-glib/mm-modem-3gpp-profile-manager.c
new file mode 100644
index 00000000..742299e0
--- /dev/null
+++ b/libmm-glib/mm-modem-3gpp-profile-manager.c
@@ -0,0 +1,489 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <gio/gio.h>
+
+#include "mm-helpers.h"
+#include "mm-errors-types.h"
+#include "mm-modem-3gpp-profile-manager.h"
+
+/**
+ * SECTION: mm-modem-3gpp-profile-manager
+ * @title: MMModem3gppProfileManager
+ * @short_description: The 3GPP profile manager interface
+ *
+ * The #MMModem3gppProfileManager is an object providing access to the methods
+ * and signals of the 3GPP Profile Manager interface.
+ *
+ * This interface is only exposed when the 3GPP modem is known to handle profile
+ * management operations.
+ */
+
+G_DEFINE_TYPE (MMModem3gppProfileManager, mm_modem_3gpp_profile_manager, MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_PROXY)
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_profile_manager_get_path:
+ * @self: A #MMModem3gppProfileManager.
+ *
+ * Gets the DBus path of the #MMObject which implements this interface.
+ *
+ * Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_modem_3gpp_profile_manager_get_path (MMModem3gppProfileManager *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)));
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_dup_path:
+ * @self: A #MMModem3gppProfileManager.
+ *
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.18
+ */
+gchar *
+mm_modem_3gpp_profile_manager_dup_path (MMModem3gppProfileManager *self)
+{
+ gchar *value;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL);
+
+ g_object_get (G_OBJECT (self),
+ "g-object-path", &value,
+ NULL);
+ RETURN_NON_EMPTY_STRING (value);
+}
+
+/*****************************************************************************/
+
+static gboolean
+build_list_results (GVariant *dictionaries,
+ GList **out_profiles,
+ GError **error)
+{
+ g_autoptr(GError) saved_error = NULL;
+ GVariantIter iter;
+ guint n;
+ GList *profiles = NULL;
+
+ if (out_profiles)
+ *out_profiles = NULL;
+
+ if (!dictionaries)
+ return TRUE;
+
+ /* Parse array of dictionaries */
+ g_variant_iter_init (&iter, dictionaries);
+ n = g_variant_iter_n_children (&iter);
+
+ if (n > 0) {
+ g_autoptr(GVariant) dictionary = NULL;
+
+ while ((dictionary = g_variant_iter_next_value (&iter))) {
+ MM3gppProfile *profile = NULL;
+ g_autoptr(GError) inner_error = NULL;
+
+ profile = mm_3gpp_profile_new_from_dictionary (dictionary, &inner_error);
+ if (!profile) {
+ g_warning ("Couldn't create 3GPP profile: %s", inner_error->message);
+ if (!saved_error)
+ saved_error = g_steal_pointer (&inner_error);
+ } else
+ profiles = g_list_append (profiles, profile);
+ }
+ }
+
+ if (saved_error && !profiles) {
+ g_propagate_error (error, g_steal_pointer (&saved_error));
+ return FALSE;
+ }
+
+ if (out_profiles)
+ *out_profiles = profiles;
+ else
+ g_list_free_full (profiles, g_object_unref);
+ return TRUE;
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_list_finish:
+ * @self: A #MMModem3gppProfileManager.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_profile_manager_list().
+ * @profiles: (out) (allow-none) (transfer full) (element-type ModemManager.3gppProfile):
+ * A list of #MM3gppProfile objects available in the device. The returned value
+ * should be freed with g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_profile_manager_list().
+ *
+ * Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_modem_3gpp_profile_manager_list_finish (MMModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **profiles,
+ GError **error)
+{
+ g_autoptr(GVariant) dictionaries = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE);
+
+ if (!mm_gdbus_modem3gpp_profile_manager_call_list_finish (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ &dictionaries,
+ res,
+ error))
+ return FALSE;
+
+ return build_list_results (dictionaries, profiles, error);
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_list:
+ * @self: A #MMModem3gppProfileManager.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously gets the list of available connection profiles.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_profile_manager_list_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_3gpp_profile_manager_list_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.18
+ */
+void
+mm_modem_3gpp_profile_manager_list (MMModem3gppProfileManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self));
+
+ mm_gdbus_modem3gpp_profile_manager_call_list (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_list_sync:
+ * @self: A #MMModem3gppProfileManager.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @profiles: (out) (allow-none) (transfer full) (element-type ModemManager.3gppProfile):
+ * A list of #MM3gppProfile objects available in the device. The returned value
+ * should be freed with g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously gets the list of available connection profiles.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_profile_manager_list() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_modem_3gpp_profile_manager_list_sync (MMModem3gppProfileManager *self,
+ GCancellable *cancellable,
+ GList **profiles,
+ GError **error)
+{
+ g_autoptr(GVariant) dictionaries = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE);
+
+ if (!mm_gdbus_modem3gpp_profile_manager_call_list_sync (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ &dictionaries,
+ cancellable,
+ error))
+ return FALSE;
+
+ return build_list_results (dictionaries, profiles, error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_profile_manager_set_finish:
+ * @self: A #MMModem3gppProfileManager.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_profile_manager_set().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_profile_manager_set().
+ *
+ * Returns: (transfer full): A #MM3gppProfile with the stored settings, or %NULL if @error is set.
+ *
+ * Since: 1.18
+ */
+MM3gppProfile *
+mm_modem_3gpp_profile_manager_set_finish (MMModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_autoptr(GVariant) stored_dictionary = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL);
+
+ if (!mm_gdbus_modem3gpp_profile_manager_call_set_finish (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ &stored_dictionary,
+ res,
+ error))
+ return NULL;
+
+ return mm_3gpp_profile_new_from_dictionary (stored_dictionary, error);
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_set:
+ * @self: A #MMModem3gppProfileManager.
+ * @requested: A #MM3gppProfile with the requested settings.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously updates a connection profile with the settings
+ * given in @profile.
+ *
+ * If @profile does not have an explicit profile ID set, a new profile will
+ * be created.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_profile_manager_set_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_3gpp_profile_manager_set_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.18
+ */
+void
+mm_modem_3gpp_profile_manager_set (MMModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GVariant) requested_dictionary = NULL;
+
+ g_return_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self));
+
+ requested_dictionary = mm_3gpp_profile_get_dictionary (requested);
+ mm_gdbus_modem3gpp_profile_manager_call_set (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ requested_dictionary,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_set_sync:
+ * @self: A #MMModem3gppProfileManager.
+ * @requested: A #MM3gppProfile with the requested settings.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously updates a connection profile with the settings
+ * given in @profile.
+ *
+ * If @profile does not have an explicit profile ID set, a new profile will
+ * be created.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_profile_manager_set() for the asynchronous version of this
+ * method.
+ *
+ * Returns: (transfer full): A #MM3gppProfile with the stored settings, or %NULL if @error is set.
+ *
+ * Since: 1.18
+ */
+MM3gppProfile *
+mm_modem_3gpp_profile_manager_set_sync (MMModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GVariant) requested_dictionary = NULL;
+ g_autoptr(GVariant) stored_dictionary = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), NULL);
+
+ requested_dictionary = mm_3gpp_profile_get_dictionary (requested);
+
+ if (!mm_gdbus_modem3gpp_profile_manager_call_set_sync (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ requested_dictionary,
+ &stored_dictionary,
+ cancellable,
+ error))
+ return NULL;
+
+ return mm_3gpp_profile_new_from_dictionary (stored_dictionary, error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_profile_manager_delete_finish:
+ * @self: A #MMModem3gppProfileManager.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_profile_manager_delete().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_profile_manager_delete().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_modem_3gpp_profile_manager_delete_finish (MMModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE);
+
+ return mm_gdbus_modem3gpp_profile_manager_call_delete_finish (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self), res, error);
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_delete:
+ * @self: A #MMModem3gppProfileManager.
+ * @profile: A #MM3gppProfile.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously deletes the connection profile.
+ *
+ * The @profile should have at least the profile ID set for the delete operation
+ * to succeed.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_profile_manager_delete_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_3gpp_profile_manager_delete_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.18
+ */
+void
+mm_modem_3gpp_profile_manager_delete (MMModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GVariant) profile_dictionary = NULL;
+
+ g_return_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self));
+
+ profile_dictionary = mm_3gpp_profile_get_dictionary (profile);
+ mm_gdbus_modem3gpp_profile_manager_call_delete (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ profile_dictionary,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_3gpp_profile_manager_delete_sync:
+ * @self: A #MMModem3gppProfileManager.
+ * @profile: A #MM3gppProfile.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously deletes the connection profile.
+ *
+ * The @profile should have at least the profile ID set for the delete operation
+ * to succeed.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_profile_manager_delete() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_modem_3gpp_profile_manager_delete_sync (MMModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GVariant) profile_dictionary = NULL;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP_PROFILE_MANAGER (self), FALSE);
+
+ profile_dictionary = mm_3gpp_profile_get_dictionary (profile);
+ return mm_gdbus_modem3gpp_profile_manager_call_delete_sync (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (self),
+ profile_dictionary,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_3gpp_profile_manager_init (MMModem3gppProfileManager *self)
+{
+}
+
+static void
+mm_modem_3gpp_profile_manager_class_init (MMModem3gppProfileManagerClass *modem_class)
+{
+}
diff --git a/libmm-glib/mm-modem-3gpp-profile-manager.h b/libmm-glib/mm-modem-3gpp-profile-manager.h
new file mode 100644
index 00000000..ebfd6cc1
--- /dev/null
+++ b/libmm-glib/mm-modem-3gpp-profile-manager.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#ifndef _MM_MODEM_3GPP_PROFILE_MANAGER_H_
+#define _MM_MODEM_3GPP_PROFILE_MANAGER_H_
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+
+#include "mm-3gpp-profile.h"
+#include "mm-gdbus-modem.h"
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_MODEM_3GPP_PROFILE_MANAGER (mm_modem_3gpp_profile_manager_get_type ())
+#define MM_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER, MMModem3gppProfileManager))
+#define MM_MODEM_3GPP_PROFILE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER, MMModem3gppProfileManagerClass))
+#define MM_IS_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER))
+#define MM_IS_MODEM_3GPP_PROFILE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER))
+#define MM_MODEM_3GPP_PROFILE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_3GPP_PROFILE_MANAGER, MMModem3gppProfileManagerClass))
+
+typedef struct _MMModem3gppProfileManager MMModem3gppProfileManager;
+typedef struct _MMModem3gppProfileManagerClass MMModem3gppProfileManagerClass;
+
+/**
+ * MMModem3gppProfileManager:
+ *
+ * The #MMModem3gppProfileManager structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MMModem3gppProfileManager {
+ /*< private >*/
+ MmGdbusModem3gppProfileManagerProxy parent;
+ gpointer unused;
+};
+
+struct _MMModem3gppProfileManagerClass {
+ /*< private >*/
+ MmGdbusModem3gppProfileManagerProxyClass parent;
+};
+
+GType mm_modem_3gpp_profile_manager_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gppProfileManager, g_object_unref)
+
+const gchar *mm_modem_3gpp_profile_manager_get_path (MMModem3gppProfileManager *self);
+gchar *mm_modem_3gpp_profile_manager_dup_path (MMModem3gppProfileManager *self);
+
+void mm_modem_3gpp_profile_manager_list (MMModem3gppProfileManager *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_3gpp_profile_manager_list_finish (MMModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **profiles,
+ GError **error);
+gboolean mm_modem_3gpp_profile_manager_list_sync (MMModem3gppProfileManager *self,
+ GCancellable *cancellable,
+ GList **profiles,
+ GError **error);
+
+void mm_modem_3gpp_profile_manager_set (MMModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MM3gppProfile *mm_modem_3gpp_profile_manager_set_finish (MMModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+MM3gppProfile *mm_modem_3gpp_profile_manager_set_sync (MMModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_3gpp_profile_manager_delete (MMModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_3gpp_profile_manager_delete_finish (MMModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_3gpp_profile_manager_delete_sync (MMModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _MM_MODEM_3GPP_PROFILE_MANAGER_H_ */
diff --git a/libmm-glib/mm-modem-3gpp-ussd.c b/libmm-glib/mm-modem-3gpp-ussd.c
index f77a7f7a..4b975fd9 100644
--- a/libmm-glib/mm-modem-3gpp-ussd.c
+++ b/libmm-glib/mm-modem-3gpp-ussd.c
@@ -49,6 +49,8 @@ G_DEFINE_TYPE (MMModem3gppUssd, mm_modem_3gpp_ussd, MM_GDBUS_TYPE_MODEM3GPP_USSD
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_ussd_get_path (MMModem3gppUssd *self)
@@ -63,9 +65,13 @@ mm_modem_3gpp_ussd_get_path (MMModem3gppUssd *self)
* mm_modem_3gpp_ussd_dup_path:
* @self: A #MMModem3gppUssd.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_dup_path (MMModem3gppUssd *self)
@@ -90,10 +96,12 @@ mm_modem_3gpp_ussd_dup_path (MMModem3gppUssd *self)
*
* <warning>The returned value is only valid until the property changes so
* it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_3gpp_ussd_dup_network_request() if on another
- * thread.</warning>
+ * @self was constructed. Use mm_modem_3gpp_ussd_dup_network_request() if on
+ * another thread.</warning>
*
* Returns: (transfer none): The network request, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_ussd_get_network_request (MMModem3gppUssd *self)
@@ -110,7 +118,10 @@ mm_modem_3gpp_ussd_get_network_request (MMModem3gppUssd *self)
*
* Gets a copy of any pending network-initiated request.
*
- * Returns: (transfer full): The network request, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The network request, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_dup_network_request (MMModem3gppUssd *self)
@@ -127,14 +138,17 @@ mm_modem_3gpp_ussd_dup_network_request (MMModem3gppUssd *self)
* mm_modem_3gpp_ussd_get_network_notification:
* @self: A #MMModem3gppUssd.
*
- * Gets any pending network-initiated request to which no USSD response is required.
+ * Gets any pending network-initiated request to which no USSD response is
+ * required.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_3gpp_ussd_dup_network_notification() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_3gpp_ussd_dup_network_notification() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The network notification, or %NULL if none
+ * available.
*
- * Returns: (transfer none): The network notification, or %NULL if none available.
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_ussd_get_network_notification (MMModem3gppUssd *self)
@@ -149,9 +163,13 @@ mm_modem_3gpp_ussd_get_network_notification (MMModem3gppUssd *self)
* mm_modem_3gpp_ussd_dup_network_notification:
* @self: A #MMModem3gppUssd.
*
- * Gets a copy of any pending network-initiated request to which no USSD response is required.
+ * Gets a copy of any pending network-initiated request to which no USSD
+ * response is required.
*
- * Returns: (transfer full): The network notification, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The network notification, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_dup_network_notification (MMModem3gppUssd *self)
@@ -171,6 +189,8 @@ mm_modem_3gpp_ussd_dup_network_notification (MMModem3gppUssd *self)
* Get the state of the ongoing USSD session, if any.
*
* Returns: A #MMModem3gppUssdSessionState value, specifying the current state.
+ *
+ * Since: 1.0
*/
MMModem3gppUssdSessionState
mm_modem_3gpp_ussd_get_state (MMModem3gppUssd *self)
@@ -185,12 +205,16 @@ mm_modem_3gpp_ussd_get_state (MMModem3gppUssd *self)
/**
* mm_modem_3gpp_ussd_initiate_finish:
* @self: A #MMModem3gppUssd.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_3gpp_ussd_initiate().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_ussd_initiate().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_3gpp_ussd_initiate().
*
- * Returns: The response from the network, if any. The returned value should be freed with g_free().
+ * Returns: The response from the network, if any. The returned value should be
+ * freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_initiate_finish (MMModem3gppUssd *self,
@@ -211,19 +235,26 @@ mm_modem_3gpp_ussd_initiate_finish (MMModem3gppUssd *self,
* @self: A #MMModem3gppUssd.
* @command: The command to start the USSD session with.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously sends a USSD command string to the network initiating a USSD session.
+ * Asynchronously sends a USSD command string to the network initiating a USSD
+ * session.
*
* When the request is handled by the network, the method returns the
* response or an appropriate error. The network may be awaiting further
* response from the ME after returning from this method and no new command.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_3gpp_ussd_initiate_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_ussd_initiate_finish() to get the result of the operation.
+ *
+ * See mm_modem_3gpp_ussd_initiate_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_3gpp_ussd_initiate_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_3gpp_ussd_initiate (MMModem3gppUssd *self,
@@ -244,16 +275,20 @@ mm_modem_3gpp_ussd_initiate (MMModem3gppUssd *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously sends a USSD command string to the network initiating a USSD session.
+ * Synchronously sends a USSD command string to the network initiating a USSD
+ * session.
*
* When the request is handled by the network, the method returns the
* response or an appropriate error. The network may be awaiting further
* response from the ME after returning from this method and no new command.
*
- * The calling thread is blocked until a reply is received. See mm_modem_3gpp_ussd_initiate()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_ussd_initiate() for the asynchronous version of this method.
+ *
+ * Returns: The response from the network, if any. The returned value should be
+ * freed with g_free().
*
- * Returns: The response from the network, if any. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_initiate_sync (MMModem3gppUssd *self,
@@ -275,12 +310,17 @@ mm_modem_3gpp_ussd_initiate_sync (MMModem3gppUssd *self,
/**
* mm_modem_3gpp_ussd_respond_finish:
* @self: A #MMModem3gppUssd.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_3gpp_ussd_respond().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_ussd_respond().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_3gpp_ussd_respond().
*
- * Returns: The network reply to this response to the network-initiated USSD command. The reply may require further responses. The returned value should be freed with g_free().
+ * Returns: The network reply to this response to the network-initiated USSD
+ * command. The reply may require further responses. The returned value should
+ * be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_respond_finish (MMModem3gppUssd *self,
@@ -299,19 +339,26 @@ mm_modem_3gpp_ussd_respond_finish (MMModem3gppUssd *self,
/**
* mm_modem_3gpp_ussd_respond:
* @self: A #MMModem3gppUssd.
- * @response: The response to network-initiated USSD command, or a response to a request for further input.
+ * @response: The response to network-initiated USSD command, or a response to a
+ * request for further input.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously responds to a USSD request that is either initiated by the
* mobile network, or that is awaiting further input after a previous call to
* mm_modem_3gpp_ussd_initiate().
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_3gpp_ussd_respond_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_ussd_respond_finish() to get the result of the operation.
+ *
+ * See mm_modem_3gpp_ussd_respond_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_3gpp_ussd_respond_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_3gpp_ussd_respond (MMModem3gppUssd *self,
@@ -328,7 +375,8 @@ mm_modem_3gpp_ussd_respond (MMModem3gppUssd *self,
/**
* mm_modem_3gpp_ussd_respond_sync:
* @self: A #MMModem3gppUssd.
- * @response: The response to network-initiated USSD command, or a response to a request for further input.
+ * @response: The response to network-initiated USSD command, or a response to a
+ * request for further input.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
@@ -336,10 +384,14 @@ mm_modem_3gpp_ussd_respond (MMModem3gppUssd *self,
* mobile network, or that is awaiting further input after a previous call to
* mm_modem_3gpp_ussd_initiate().
*
- * The calling thread is blocked until a reply is received. See mm_modem_3gpp_ussd_respond()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_ussd_respond() for the asynchronous version of this method.
*
- * Returns: The network reply to this response to the network-initiated USSD command. The reply may require further responses. The returned value should be freed with g_free().
+ * Returns: The network reply to this response to the network-initiated USSD
+ * command. The reply may require further responses. The returned value should
+ * be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_ussd_respond_sync (MMModem3gppUssd *self,
@@ -361,12 +413,16 @@ mm_modem_3gpp_ussd_respond_sync (MMModem3gppUssd *self,
/**
* mm_modem_3gpp_ussd_cancel_finish:
* @self: A #MMModem3gppUssd.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_3gpp_ussd_cancel().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_ussd_cancel().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_3gpp_ussd_cancel().
*
- * Returns: %TRUE if the session was successfully cancelled, %FALSE if @error is set.
+ * Returns: %TRUE if the session was successfully cancelled, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_3gpp_ussd_cancel_finish (MMModem3gppUssd *self,
@@ -382,15 +438,22 @@ mm_modem_3gpp_ussd_cancel_finish (MMModem3gppUssd *self,
* mm_modem_3gpp_ussd_cancel:
* @self: A #MMModem3gppUssd.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously cancels an ongoing USSD session, either mobile or network initiated.
+ * Asynchronously cancels an ongoing USSD session, either mobile or network
+ * initiated.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_3gpp_ussd_cancel_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_ussd_cancel_finish() to get the result of the operation.
*
- * See mm_modem_3gpp_ussd_cancel_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_3gpp_ussd_cancel_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_3gpp_ussd_cancel (MMModem3gppUssd *self,
@@ -409,12 +472,16 @@ mm_modem_3gpp_ussd_cancel (MMModem3gppUssd *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously cancels an ongoing USSD session, either mobile or network initiated.
+ * Synchronously cancels an ongoing USSD session, either mobile or network
+ * initiated.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_ussd_cancel() for the asynchronous version of this method.
*
- * The calling thread is blocked until a reply is received. See mm_modem_3gpp_ussd_cancel()
- * for the asynchronous version of this method.
+ * Returns: %TRUE if the session was successfully cancelled, %FALSE if @error is
+ * set.
*
- * Returns: %TRUE if the session was successfully cancelled, %FALSE if @error is set.
+ * Since: 1.0
*/
gboolean
mm_modem_3gpp_ussd_cancel_sync (MMModem3gppUssd *self,
diff --git a/libmm-glib/mm-modem-3gpp-ussd.h b/libmm-glib/mm-modem-3gpp-ussd.h
index ee7522a8..c1e480f9 100644
--- a/libmm-glib/mm-modem-3gpp-ussd.h
+++ b/libmm-glib/mm-modem-3gpp-ussd.h
@@ -62,6 +62,7 @@ struct _MMModem3gppUssdClass {
};
GType mm_modem_3gpp_ussd_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gppUssd, g_object_unref)
const gchar *mm_modem_3gpp_ussd_get_path (MMModem3gppUssd *self);
gchar *mm_modem_3gpp_ussd_dup_path (MMModem3gppUssd *self);
diff --git a/libmm-glib/mm-modem-3gpp.c b/libmm-glib/mm-modem-3gpp.c
index a926acc1..04ddf06a 100644
--- a/libmm-glib/mm-modem-3gpp.c
+++ b/libmm-glib/mm-modem-3gpp.c
@@ -26,6 +26,8 @@
#include "mm-helpers.h"
#include "mm-errors-types.h"
#include "mm-modem-3gpp.h"
+#include "mm-bearer.h"
+#include "mm-pco.h"
/**
* SECTION: mm-modem-3gpp
@@ -36,11 +38,19 @@
* properties of the 3GPP interface.
*
* The 3GPP interface is exposed whenever a modem has any of the 3GPP
- * capabilities (%MM_MODEM_CAPABILITY_GSM_UMTS, %MM_MODEM_CAPABILITY_LTE or %MM_MODEM_CAPABILITY_LTE_ADVANCED).
+ * capabilities (%MM_MODEM_CAPABILITY_GSM_UMTS, %MM_MODEM_CAPABILITY_LTE
+ * or %MM_MODEM_CAPABILITY_5GNR).
*/
G_DEFINE_TYPE (MMModem3gpp, mm_modem_3gpp, MM_GDBUS_TYPE_MODEM3GPP_PROXY)
+struct _MMModem3gppPrivate {
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_OBJECT_DECLARE (initial_eps_bearer_settings, MMBearerProperties)
+};
+
/*****************************************************************************/
/**
@@ -50,6 +60,8 @@ G_DEFINE_TYPE (MMModem3gpp, mm_modem_3gpp, MM_GDBUS_TYPE_MODEM3GPP_PROXY)
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_get_path (MMModem3gpp *self)
@@ -64,9 +76,13 @@ mm_modem_3gpp_get_path (MMModem3gpp *self)
* mm_modem_3gpp_dup_path:
* @self: A #MMModem3gpp.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_dup_path (MMModem3gpp *self)
@@ -96,6 +112,8 @@ mm_modem_3gpp_dup_path (MMModem3gpp *self)
* thread.</warning>
*
* Returns: (transfer none): The IMEI, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_get_imei (MMModem3gpp *self)
@@ -113,7 +131,10 @@ mm_modem_3gpp_get_imei (MMModem3gpp *self)
* Gets a copy of the <ulink url="http://en.wikipedia.org/wiki/Imei">IMEI</ulink>,
* as reported by this #MMModem3gpp.
*
- * Returns: (transfer full): The IMEI, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The IMEI, or %NULL if none available. The returned
+ * value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_dup_imei (MMModem3gpp *self)
@@ -130,8 +151,7 @@ mm_modem_3gpp_dup_imei (MMModem3gpp *self)
* mm_modem_3gpp_get_operator_code:
* @self: A #MMModem3gpp.
*
- * Gets the code of the operator to which the mobile is
- * currently registered.
+ * Gets the code of the operator to which the mobile is currently registered.
*
* Returned in the format <literal>"MCCMNC"</literal>, where
* <literal>MCC</literal> is the three-digit ITU E.212 Mobile Country Code
@@ -148,6 +168,8 @@ mm_modem_3gpp_dup_imei (MMModem3gpp *self)
* thread.</warning>
*
* Returns: (transfer none): The operator code, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_get_operator_code (MMModem3gpp *self)
@@ -162,15 +184,18 @@ mm_modem_3gpp_get_operator_code (MMModem3gpp *self)
* mm_modem_3gpp_dup_operator_code:
* @self: A #MMModem3gpp.
*
- * Gets a copy of the code of the operator to which the mobile is
- * currently registered.
+ * Gets a copy of the code of the operator to which the mobile is currently
+ * registered.
*
* Returned in the format <literal>"MCCMNC"</literal>, where
* <literal>MCC</literal> is the three-digit ITU E.212 Mobile Country Code
* and <literal>MNC</literal> is the two- or three-digit GSM Mobile Network
* Code. e.g. e<literal>"31026"</literal> or <literal>"310260"</literal>.
*
- * Returns: (transfer full): The operator code, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The operator code, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_dup_operator_code (MMModem3gpp *self)
@@ -196,6 +221,8 @@ mm_modem_3gpp_dup_operator_code (MMModem3gpp *self)
* thread.</warning>
*
* Returns: (transfer none): The operator name, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_get_operator_name (MMModem3gpp *self)
@@ -213,7 +240,10 @@ mm_modem_3gpp_get_operator_name (MMModem3gpp *self)
* Gets a copy of the name of the operator to which the mobile is
* currently registered.
*
- * Returns: (transfer full): The operator name, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The operator name, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_3gpp_dup_operator_name (MMModem3gpp *self)
@@ -233,7 +263,10 @@ mm_modem_3gpp_dup_operator_name (MMModem3gpp *self)
* Get the the mobile registration status as defined in 3GPP TS 27.007
* section 10.1.19.
*
- * Returns: A #MMModem3gppRegistrationState value, specifying the current registration state.
+ * Returns: A #MMModem3gppRegistrationState value, specifying the current
+ * registration state.
+ *
+ * Since: 1.0
*/
MMModem3gppRegistrationState
mm_modem_3gpp_get_registration_state (MMModem3gpp *self)
@@ -246,38 +279,148 @@ mm_modem_3gpp_get_registration_state (MMModem3gpp *self)
/*****************************************************************************/
/**
- * mm_modem_3gpp_get_subscription_state:
- * @self: A #MMModem.
+ * mm_modem_3gpp_get_enabled_facility_locks:
+ * @self: A #MMModem3gpp.
+ *
+ * Get the list of facilities for which PIN locking is enabled.
+ *
+ * Returns: A bitmask of #MMModem3gppFacility flags, specifying which facilities
+ * have locks enabled.
+ *
+ * Since: 1.0
+ */
+MMModem3gppFacility
+mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_FACILITY_NONE);
+
+ return mm_gdbus_modem3gpp_get_enabled_facility_locks (MM_GDBUS_MODEM3GPP (self));
+}
+
+/**
+ * mm_modem_3gpp_get_eps_ue_mode_operation:
+ * @self: A #MMModem3gpp.
*
- * Get the current subscription status of the account. This value is only
- * available after the modem attempts to register with the network.
+ * Get the UE mode of operation for EPS.
*
- * Returns: A #MMModem3gppSubscriptionState value, specifying the current subscription state.
+ * Returns: A #MMModem3gppEpsUeModeOperation.
+ *
+ * Since: 1.8
*/
-MMModem3gppSubscriptionState
-mm_modem_3gpp_get_subscription_state (MMModem3gpp *self)
+MMModem3gppEpsUeModeOperation
+mm_modem_3gpp_get_eps_ue_mode_operation (MMModem3gpp *self)
{
- g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN);
- return mm_gdbus_modem3gpp_get_subscription_state (MM_GDBUS_MODEM3GPP (self));
+ return mm_gdbus_modem3gpp_get_eps_ue_mode_operation (MM_GDBUS_MODEM3GPP (self));
}
/*****************************************************************************/
/**
- * mm_modem_3gpp_get_enabled_facility_locks:
+ * mm_modem_3gpp_get_pco:
* @self: A #MMModem3gpp.
*
- * Get the list of facilities for which PIN locking is enabled.
+ * Get the list of #MMPco received from the network.
*
- * Returns: A bitmask of #MMModem3gppFacility flags, specifying which facilities have locks enabled.
+ * Returns: (transfer full) (element-type ModemManager.Pco): a list of #MMPco
+ * objects, or #NULL if @error is set. The returned value should be freed with
+ * g_list_free_full() using g_object_unref() as #GDestroyNotify function.
+ *
+ * Since: 1.10
*/
-MMModem3gppFacility
-mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self)
+GList *
+mm_modem_3gpp_get_pco (MMModem3gpp *self)
{
- g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_FACILITY_NONE);
+ GList *pco_list = NULL;
+ GVariant *container, *child;
+ GVariantIter iter;
- return mm_gdbus_modem3gpp_get_enabled_facility_locks (MM_GDBUS_MODEM3GPP (self));
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL);
+
+ container = mm_gdbus_modem3gpp_get_pco (MM_GDBUS_MODEM3GPP (self));
+
+ g_return_val_if_fail (g_variant_is_of_type (container, G_VARIANT_TYPE ("a(ubay)")),
+ NULL);
+ g_variant_iter_init (&iter, container);
+ while ((child = g_variant_iter_next_value (&iter))) {
+ MMPco *pco;
+
+ pco = mm_pco_from_variant (child, NULL);
+ pco_list = mm_pco_list_add (pco_list, pco);
+ g_object_unref (pco);
+ g_variant_unref (child);
+ }
+
+ return pco_list;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_get_initial_eps_bearer_path: (skip)
+ * @self: A #MMModem3gpp.
+ *
+ * Gets the DBus path of the initial EPS #MMBearer exposed in this #MMModem3gpp.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_3gpp_dup_initial_eps_bearer_path() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): The DBus path of the #MMBearer, or %NULL if none
+ * available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.10
+ */
+const gchar *
+mm_modem_3gpp_get_initial_eps_bearer_path (MMModem3gpp *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (mm_gdbus_modem3gpp_get_initial_eps_bearer (MM_GDBUS_MODEM3GPP (self)));
+}
+
+/**
+ * mm_modem_3gpp_dup_initial_eps_bearer_path:
+ * @self: A #MMModem3gpp.
+ *
+ * Gets a copy of the DBus path of the initial EPS #MMBearer exposed in this
+ * #MMModem3gpp.
+ *
+ * Returns: (transfer full): The DBus path of the #MMBearer, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.10
+ */
+gchar *
+mm_modem_3gpp_dup_initial_eps_bearer_path (MMModem3gpp *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_modem3gpp_dup_initial_eps_bearer (MM_GDBUS_MODEM3GPP (self)));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_get_packet_service_state:
+ * @self: A #MMModem.
+ *
+ * Get the packet domain service state.
+ *
+ * Returns: A #MMModem3gppPacketServiceState value, specifying the current PS attach
+ * state.
+ *
+ * Since: 1.20
+ */
+MMModem3gppPacketServiceState
+mm_modem_3gpp_get_packet_service_state (MMModem3gpp *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN);
+
+ return mm_gdbus_modem3gpp_get_packet_service_state (MM_GDBUS_MODEM3GPP (self));
}
/*****************************************************************************/
@@ -285,12 +428,15 @@ mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self)
/**
* mm_modem_3gpp_register_finish:
* @self: A #MMModem3gpp.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_3gpp_register().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_register().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_3gpp_register().
*
* Returns: %TRUE if the modem was registered, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_3gpp_register_finish (MMModem3gpp *self,
@@ -305,17 +451,24 @@ mm_modem_3gpp_register_finish (MMModem3gpp *self,
/**
* mm_modem_3gpp_register:
* @self: A #MMModem3gpp.
- * @network_id: The operator ID to register. An empty string can be used to register to the home network.
+ * @network_id: The operator ID to register. An empty string can be used to
+ * register to the home network.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests registration with a given mobile network.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_3gpp_register_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_register_finish() to get the result of the operation.
*
- * See mm_modem_3gpp_register_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_3gpp_register_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_3gpp_register (MMModem3gpp *self,
@@ -332,16 +485,19 @@ mm_modem_3gpp_register (MMModem3gpp *self,
/**
* mm_modem_3gpp_register_sync:
* @self: A #MMModem3gpp.
- * @network_id: The operator ID to register. An empty string can be used to register to the home network.
+ * @network_id: The operator ID to register. An empty string can be used to
+ * register to the home network.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests registration with a given mobile network.
*
- * The calling thread is blocked until a reply is received. See mm_modem_3gpp_register()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_register() for the asynchronous version of this method.
*
* Returns: %TRUE if the modem was registered, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_3gpp_register_sync (MMModem3gpp *self,
@@ -369,6 +525,8 @@ struct _MMModem3gppNetwork {
* @network: A #MMModem3gppNetwork.
*
* Frees a #MMModem3gppNetwork.
+ *
+ * Since: 1.0
*/
void
mm_modem_3gpp_network_free (MMModem3gppNetwork *network)
@@ -382,6 +540,23 @@ mm_modem_3gpp_network_free (MMModem3gppNetwork *network)
g_slice_free (MMModem3gppNetwork, network);
}
+static MMModem3gppNetwork *
+modem_3gpp_network_copy (MMModem3gppNetwork *network)
+{
+ MMModem3gppNetwork *network_copy;
+
+ network_copy = g_slice_new0 (MMModem3gppNetwork);
+ network_copy->availability = network->availability;
+ network_copy->operator_long = g_strdup (network->operator_long);
+ network_copy->operator_short = g_strdup (network->operator_short);
+ network_copy->operator_code = g_strdup (network->operator_code);
+ network_copy->access_technology = network->access_technology;
+
+ return network_copy;
+}
+
+G_DEFINE_BOXED_TYPE (MMModem3gppNetwork, mm_modem_3gpp_network, (GBoxedCopyFunc)modem_3gpp_network_copy, (GBoxedFreeFunc)mm_modem_3gpp_network_free)
+
/**
* mm_modem_3gpp_network_get_availability:
* @network: A #MMModem3gppNetwork.
@@ -389,6 +564,8 @@ mm_modem_3gpp_network_free (MMModem3gppNetwork *network)
* Get availability of the 3GPP network.
*
* Returns: A #MMModem3gppNetworkAvailability.
+ *
+ * Since: 1.0
*/
MMModem3gppNetworkAvailability
mm_modem_3gpp_network_get_availability (const MMModem3gppNetwork *network)
@@ -405,6 +582,8 @@ mm_modem_3gpp_network_get_availability (const MMModem3gppNetwork *network)
* Get the long operator name of the 3GPP network.
*
* Returns: (transfer none): The long operator name, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_network_get_operator_long (const MMModem3gppNetwork *network)
@@ -421,6 +600,8 @@ mm_modem_3gpp_network_get_operator_long (const MMModem3gppNetwork *network)
* Get the short operator name of the 3GPP network.
*
* Returns: (transfer none): The long operator name, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_network_get_operator_short (const MMModem3gppNetwork *network)
@@ -437,6 +618,8 @@ mm_modem_3gpp_network_get_operator_short (const MMModem3gppNetwork *network)
* Get the operator code (MCCMNC) of the 3GPP network.
*
* Returns: (transfer none): The operator code, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_3gpp_network_get_operator_code (const MMModem3gppNetwork *network)
@@ -453,6 +636,8 @@ mm_modem_3gpp_network_get_operator_code (const MMModem3gppNetwork *network)
* Get the technology used to access the 3GPP network.
*
* Returns: A #MMModemAccessTechnology.
+ *
+ * Since: 1.0
*/
MMModemAccessTechnology
mm_modem_3gpp_network_get_access_technology (const MMModem3gppNetwork *network)
@@ -464,6 +649,53 @@ mm_modem_3gpp_network_get_access_technology (const MMModem3gppNetwork *network)
/*****************************************************************************/
+/**
+ * mm_modem_3gpp_get_initial_eps_bearer_settings:
+ * @self: A #MMModem3gpp.
+ *
+ * Gets a #MMBearerProperties object specifying the settings configured in
+ * the device to use when attaching to the LTE network.
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_3gpp_get_initial_eps_bearer_settings() again to get a new
+ * #MMBearerProperties with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMBearerProperties that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.10
+ */
+
+/**
+ * mm_modem_3gpp_peek_initial_eps_bearer_settings:
+ * @self: A #MMModem3gpp.
+ *
+ * Gets a #MMBearerProperties object specifying the settings configured in
+ * the device to use when attaching to the LTE network.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_3gpp_get_initial_eps_bearer_settings()
+ * if on another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMBearerProperties. Do not free the returned
+ * value, it belongs to @self.
+ *
+ * Since: 1.10
+ */
+
+/* helpers to match the property substring name with the one in our API */
+#define mm_gdbus_modem_3gpp_dup_initial_eps_bearer_settings mm_gdbus_modem3gpp_dup_initial_eps_bearer_settings
+#define MM_GDBUS_MODEM_3GPP MM_GDBUS_MODEM3GPP
+
+PROPERTY_OBJECT_DEFINE_FAILABLE (initial_eps_bearer_settings,
+ Modem3gpp, modem_3gpp, MODEM_3GPP,
+ MMBearerProperties,
+ mm_bearer_properties_new_from_dictionary)
+
+/*****************************************************************************/
+
static GList *
create_networks_list (GVariant *variant)
{
@@ -513,12 +745,18 @@ create_networks_list (GVariant *variant)
/**
* mm_modem_3gpp_scan_finish:
* @self: A #MMModem3gpp.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_3gpp_scan().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_scan().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_3gpp_scan().
*
- * Returns: (transfer full) (element-type ModemManager.Modem3gppNetwork): a list of #MMModem3gppNetwork structs, or #NULL if @error is set. The returned value should be freed with g_list_free_full() using mm_modem_3gpp_network_free() as #GDestroyNotify function.
+ * Returns: (transfer full) (element-type ModemManager.Modem3gppNetwork): a list
+ * of #MMModem3gppNetwork structs, or #NULL if @error is set. The returned value
+ * should be freed with g_list_free_full() using mm_modem_3gpp_network_free() as
+ * #GDestroyNotify function.
+ *
+ * Since: 1.0
*/
GList *
mm_modem_3gpp_scan_finish (MMModem3gpp *self,
@@ -539,15 +777,21 @@ mm_modem_3gpp_scan_finish (MMModem3gpp *self,
* mm_modem_3gpp_scan:
* @self: A #MMModem3gpp.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to scan available 3GPP networks.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_3gpp_scan_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_scan_finish() to get the result of the operation.
*
- * See mm_modem_3gpp_scan_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_3gpp_scan_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.0
*/
void
mm_modem_3gpp_scan (MMModem3gpp *self,
@@ -568,10 +812,15 @@ mm_modem_3gpp_scan (MMModem3gpp *self,
*
* Synchronously requests to scan available 3GPP networks.
*
- * The calling thread is blocked until a reply is received. See mm_modem_3gpp_scan()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_scan() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full) (element-type ModemManager.Modem3gppNetwork): a list
+ * of #MMModem3gppNetwork structs, or #NULL if @error is set. The returned value
+ * should be freed with g_list_free_full() using mm_modem_3gpp_network_free() as
+ * #GDestroyNotify function.
*
- * Returns: (transfer full) (element-type ModemManager.Modem3gppNetwork): a list of #MMModem3gppNetwork structs, or #NULL if @error is set. The returned value should be freed with g_list_free_full() using mm_modem_3gpp_network_free() as #GDestroyNotify function.
+ * Since: 1.0
*/
GList *
mm_modem_3gpp_scan_sync (MMModem3gpp *self,
@@ -590,12 +839,562 @@ mm_modem_3gpp_scan_sync (MMModem3gpp *self,
/*****************************************************************************/
+/**
+ * mm_modem_3gpp_set_eps_ue_mode_operation_finish:
+ * @self: A #MMModem3gpp.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_set_eps_ue_mode_operation().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_set_eps_ue_mode_operation().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.8
+ */
+gboolean
+mm_modem_3gpp_set_eps_ue_mode_operation_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE);
+
+ return mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_finish (MM_GDBUS_MODEM3GPP (self), res, error);
+}
+
+/**
+ * mm_modem_3gpp_set_eps_ue_mode_operation:
+ * @self: A #MMModem3gpp.
+ * @mode: A #MMModem3gppEpsUeModeOperation.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to update the EPS UE mode of operation.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_set_eps_ue_mode_operation_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_3gpp_set_eps_ue_mode_operation_sync() for the synchronous,
+ * blocking version of this method. The calling thread is blocked until a reply
+ * is received.
+ *
+ * Since: 1.8
+ */
+void
+mm_modem_3gpp_set_eps_ue_mode_operation (MMModem3gpp *self,
+ MMModem3gppEpsUeModeOperation mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_3GPP (self));
+ g_return_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN);
+
+ mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation (MM_GDBUS_MODEM3GPP (self), (guint) mode, cancellable, callback, user_data);
+}
+
+/**
+ * mm_modem_3gpp_set_eps_ue_mode_operation_sync:
+ * @self: A #MMModem3gpp.
+ * @mode: A #MMModem3gppEpsUeModeOperation.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to update the EPS UE mode of operation.
+ *
+ * The calling thread is blocked until a reply is received.
+ * See mm_modem_3gpp_set_eps_ue_mode_operation() for the asynchronous version
+ * of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.8
+ */
+gboolean
+mm_modem_3gpp_set_eps_ue_mode_operation_sync (MMModem3gpp *self,
+ MMModem3gppEpsUeModeOperation mode,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE);
+ g_return_val_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN, FALSE);
+
+ return mm_gdbus_modem3gpp_call_set_eps_ue_mode_operation_sync (MM_GDBUS_MODEM3GPP (self), (guint) mode, cancellable, error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_get_initial_eps_bearer_finish:
+ * @self: A #MMModem3gpp.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_get_initial_eps_bearer().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_get_initial_eps_bearer().
+ *
+ * Returns: (transfer full): a #MMSim or #NULL if @error is set. The returned
+ * value should be freed with g_object_unref().
+ *
+ * Since: 1.10
+ */
+MMBearer *
+mm_modem_3gpp_get_initial_eps_bearer_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+modem_3gpp_get_initial_eps_bearer_ready (GDBusConnection *connection,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GObject *sim;
+ GObject *source_object;
+
+ source_object = g_async_result_get_source_object (res);
+ sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
+ g_object_unref (source_object);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, sim, g_object_unref);
+
+ g_object_unref (task);
+}
+
+/**
+ * mm_modem_3gpp_get_initial_eps_bearer:
+ * @self: A #MMModem3gpp.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously gets the initial EPS #MMBearer object exposed by this
+ * #MMModem3gpp.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_get_initial_eps_bearer_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_3gpp_get_initial_eps_bearer_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.10
+ */
+void
+mm_modem_3gpp_get_initial_eps_bearer (MMModem3gpp *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ const gchar *bearer_path;
+
+ g_return_if_fail (MM_IS_MODEM_3GPP (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ bearer_path = mm_modem_3gpp_get_initial_eps_bearer_path (self);
+ if (!bearer_path || g_str_equal (bearer_path, "/")) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No initial EPS bearer object available");
+ g_object_unref (task);
+ return;
+ }
+
+ g_async_initable_new_async (MM_TYPE_BEARER,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ (GAsyncReadyCallback)modem_3gpp_get_initial_eps_bearer_ready,
+ task,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", bearer_path,
+ "g-interface-name", "org.freedesktop.ModemManager1.Bearer",
+ NULL);
+}
+
+/**
+ * mm_modem_3gpp_get_initial_eps_bearer_sync:
+ * @self: A #MMModem3gpp.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously gets the initial EPS #MMBearer object exposed by this
+ * #MMModem3gpp.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_get_initial_eps_bearer() for the asynchronous version of this
+ * method.
+ *
+ * Returns: (transfer full): a #MMBearer or #NULL if @error is set. The returned
+ * value should be freed with g_object_unref().
+ *
+ * Since: 1.10
+ */
+MMBearer *
+mm_modem_3gpp_get_initial_eps_bearer_sync (MMModem3gpp *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GObject *bearer;
+ const gchar *bearer_path;
+
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), NULL);
+
+ bearer_path = mm_modem_3gpp_get_initial_eps_bearer_path (self);
+ if (!bearer_path || g_str_equal (bearer_path, "/")) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No initial EPS bearer object available");
+ return NULL;
+ }
+
+ bearer = g_initable_new (MM_TYPE_BEARER,
+ cancellable,
+ error,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", bearer_path,
+ "g-interface-name", "org.freedesktop.ModemManager1.Bearer",
+ NULL);
+
+ return (bearer ? MM_BEARER (bearer) : NULL);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_set_initial_eps_bearer_settings_finish:
+ * @self: A #MMModem3gpp.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_set_initial_eps_bearer_settings().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with
+ * mm_modem_3gpp_set_initial_eps_bearer_settings().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_modem_3gpp_set_initial_eps_bearer_settings_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_finish (MM_GDBUS_MODEM3GPP (self), res, error);
+}
+
+/**
+ * mm_modem_3gpp_set_initial_eps_bearer_settings:
+ * @self: A #MMModem3gpp.
+ * @config: A #MMBearerProperties object with the properties to use.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously configures the settings for the initial LTE default bearer.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_set_initial_eps_bearer_settings_finish() to get the result of
+ * the operation.
+ *
+ * Since: 1.10
+ */
+void
+mm_modem_3gpp_set_initial_eps_bearer_settings (MMModem3gpp *self,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GVariant *dictionary;
+
+ dictionary = mm_bearer_properties_get_dictionary (config);
+ mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings (MM_GDBUS_MODEM3GPP (self),
+ dictionary,
+ cancellable,
+ callback,
+ user_data);
+ g_variant_unref (dictionary);
+}
+
+/**
+ * mm_modem_3gpp_set_initial_eps_bearer_settings_sync:
+ * @self: A #MMModem3gpp.
+ * @config: A #MMBearerProperties object with the properties to use.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously configures the settings for the initial LTE default bearer.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_set_initial_eps_bearer_settings() for the asynchronous
+ * version of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_modem_3gpp_set_initial_eps_bearer_settings_sync (MMModem3gpp *self,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean result;
+ GVariant *dictionary;
+
+ dictionary = mm_bearer_properties_get_dictionary (config);
+ result = mm_gdbus_modem3gpp_call_set_initial_eps_bearer_settings_sync (MM_GDBUS_MODEM3GPP (self),
+ dictionary,
+ cancellable,
+ error);
+ g_variant_unref (dictionary);
+ return result;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_disable_facility_lock:
+ * @self: A #MMModem3gpp.
+ * @facility: Single bit value describing the modem personalization lock to disable.
+ * @control_key: String with control key required to unlock the personalization.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously disables the modem personalization lock.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_disable_facility_lock_finish() to get the result of
+ * the operation.
+ *
+ * Since: 1.20
+ */
+void
+mm_modem_3gpp_disable_facility_lock (MMModem3gpp *self,
+ MMModem3gppFacility facility,
+ const gchar *control_key,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GVariant *properties;
+
+ properties = g_variant_ref_sink (g_variant_new ("(us)", (guint)facility, control_key));
+ mm_gdbus_modem3gpp_call_disable_facility_lock (MM_GDBUS_MODEM3GPP (self),
+ properties,
+ cancellable,
+ callback,
+ user_data);
+ g_variant_unref (properties);
+}
+
+/**
+ * mm_modem_3gpp_disable_facility_lock_finish:
+ * @self: A #MMModem3gpp.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_disable_facility_lock().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_disable_facility_lock().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_3gpp_disable_facility_lock_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return mm_gdbus_modem3gpp_call_disable_facility_lock_finish (MM_GDBUS_MODEM3GPP (self),
+ res,
+ error);
+}
+
+/**
+ * mm_modem_3gpp_disable_facility_lock_sync:
+ * @self: A #MMModem3gpp.
+ * @facility: Single bit value describing the modem personalization lock to disable.
+ * @control_key: String with control key required to unlock the personalization.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously disables facility lock.
+ *
+ * The calling thread is blocked until a reply is received.
+ * See mm_modem_3gpp_disable_facility_lock() for the asynchronous
+ * version of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_3gpp_disable_facility_lock_sync (MMModem3gpp *self,
+ MMModem3gppFacility facility,
+ const gchar *control_key,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GVariant *properties;
+ gboolean result;
+
+ properties = g_variant_ref_sink (g_variant_new ("(us)", (guint)facility, control_key));
+ result = mm_gdbus_modem3gpp_call_disable_facility_lock_sync (MM_GDBUS_MODEM3GPP (self),
+ properties,
+ cancellable,
+ error);
+ g_variant_unref (properties);
+ return result;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_3gpp_set_packet_service_state_finish:
+ * @self: A #MMModem3gpp.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_3gpp_set_packet_service_state().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_3gpp_set_packet_service_state().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_3gpp_set_packet_service_state_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE);
+
+ return mm_gdbus_modem3gpp_call_set_packet_service_state_finish (MM_GDBUS_MODEM3GPP (self), res, error);
+}
+
+/**
+ * mm_modem_3gpp_set_packet_service_state:
+ * @self: A #MMModem3gpp.
+ * @state: A #MMModem3gppPacketServiceState.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously tries to attach or detach from the packet domain service.
+ *
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_3gpp_set_packet_service_state_finish() to get the result of the operation.
+ *
+ * See mm_modem_3gpp_set_packet_service_state_sync() for the synchronous,
+ * blocking version of this method.
+ *
+ * Since: 1.20
+ */
+void
+mm_modem_3gpp_set_packet_service_state (MMModem3gpp *self,
+ MMModem3gppPacketServiceState state,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_3GPP (self));
+
+ mm_gdbus_modem3gpp_call_set_packet_service_state (MM_GDBUS_MODEM3GPP (self), state, cancellable, callback, user_data);
+}
+
+/**
+ * mm_modem_3gpp_set_packet_service_state_sync:
+ * @self: A #MMModem3gpp.
+ * @state: A #MMModem3gppPacketServiceState.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously tries to attach or detach from the packet domain service.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_3gpp_set_packet_service_state() for the asynchronous version of
+ * this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_3gpp_set_packet_service_state_sync (MMModem3gpp *self,
+ MMModem3gppPacketServiceState state,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_3GPP (self), FALSE);
+
+ return mm_gdbus_modem3gpp_call_set_packet_service_state_sync (MM_GDBUS_MODEM3GPP (self), state, cancellable, error);
+}
+
+/*****************************************************************************/
+
static void
mm_modem_3gpp_init (MMModem3gpp *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_3GPP, MMModem3gppPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (initial_eps_bearer_settings, "initial-eps-bearer-settings")
+}
+
+static void
+finalize (GObject *object)
+{
+ MMModem3gpp *self = MM_MODEM_3GPP (object);
+
+ g_mutex_clear (&self->priv->mutex);
+
+ PROPERTY_OBJECT_FINALIZE (initial_eps_bearer_settings);
+
+ G_OBJECT_CLASS (mm_modem_3gpp_parent_class)->finalize (object);
}
static void
mm_modem_3gpp_class_init (MMModem3gppClass *modem_class)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (modem_class);
+
+ g_type_class_add_private (object_class, sizeof (MMModem3gppPrivate));
+
+ object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem-3gpp.h b/libmm-glib/mm-modem-3gpp.h
index 7cfe0115..6bea3c1d 100644
--- a/libmm-glib/mm-modem-3gpp.h
+++ b/libmm-glib/mm-modem-3gpp.h
@@ -30,6 +30,7 @@
#include <ModemManager.h>
+#include "mm-bearer.h"
#include "mm-gdbus-modem.h"
G_BEGIN_DECLS
@@ -43,6 +44,7 @@ G_BEGIN_DECLS
typedef struct _MMModem3gpp MMModem3gpp;
typedef struct _MMModem3gppClass MMModem3gppClass;
+typedef struct _MMModem3gppPrivate MMModem3gppPrivate;
/**
* MMModem3gpp:
@@ -52,8 +54,8 @@ typedef struct _MMModem3gppClass MMModem3gppClass;
*/
struct _MMModem3gpp {
/*< private >*/
- MmGdbusModem3gppProxy parent;
- gpointer unused;
+ MmGdbusModem3gppProxy parent;
+ MMModem3gppPrivate *priv;
};
struct _MMModem3gppClass {
@@ -62,6 +64,7 @@ struct _MMModem3gppClass {
};
GType mm_modem_3gpp_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gpp, g_object_unref)
const gchar *mm_modem_3gpp_get_path (MMModem3gpp *self);
gchar *mm_modem_3gpp_dup_path (MMModem3gpp *self);
@@ -76,10 +79,20 @@ const gchar *mm_modem_3gpp_get_operator_name (MMModem3gpp *self);
gchar *mm_modem_3gpp_dup_operator_name (MMModem3gpp *self);
MMModem3gppRegistrationState mm_modem_3gpp_get_registration_state (MMModem3gpp *self);
-MMModem3gppSubscriptionState mm_modem_3gpp_get_subscription_state (MMModem3gpp *self);
MMModem3gppFacility mm_modem_3gpp_get_enabled_facility_locks (MMModem3gpp *self);
+MMModem3gppEpsUeModeOperation mm_modem_3gpp_get_eps_ue_mode_operation (MMModem3gpp *self);
+
+GList *mm_modem_3gpp_get_pco (MMModem3gpp *self);
+
+const gchar *mm_modem_3gpp_get_initial_eps_bearer_path (MMModem3gpp *self);
+gchar *mm_modem_3gpp_dup_initial_eps_bearer_path (MMModem3gpp *self);
+
+MMBearerProperties *mm_modem_3gpp_get_initial_eps_bearer_settings (MMModem3gpp *self);
+MMBearerProperties *mm_modem_3gpp_peek_initial_eps_bearer_settings (MMModem3gpp *self);
+
+MMModem3gppPacketServiceState mm_modem_3gpp_get_packet_service_state (MMModem3gpp *self);
void mm_modem_3gpp_register (MMModem3gpp *self,
const gchar *network_id,
@@ -102,12 +115,16 @@ gboolean mm_modem_3gpp_register_sync (MMModem3gpp *self,
*/
typedef struct _MMModem3gppNetwork MMModem3gppNetwork;
+#define MM_TYPE_MODEM_3GPP_NETWORK (mm_modem_3gpp_network_get_type ())
+GType mm_modem_3gpp_network_get_type (void);
+
MMModem3gppNetworkAvailability mm_modem_3gpp_network_get_availability (const MMModem3gppNetwork *network);
const gchar *mm_modem_3gpp_network_get_operator_long (const MMModem3gppNetwork *network);
const gchar *mm_modem_3gpp_network_get_operator_short (const MMModem3gppNetwork *network);
const gchar *mm_modem_3gpp_network_get_operator_code (const MMModem3gppNetwork *network);
MMModemAccessTechnology mm_modem_3gpp_network_get_access_technology (const MMModem3gppNetwork *network);
void mm_modem_3gpp_network_free (MMModem3gppNetwork *network);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem3gppNetwork, mm_modem_3gpp_network_free)
void mm_modem_3gpp_scan (MMModem3gpp *self,
GCancellable *cancellable,
@@ -120,6 +137,71 @@ GList *mm_modem_3gpp_scan_sync (MMModem3gpp *self,
GCancellable *cancellable,
GError **error);
+void mm_modem_3gpp_set_eps_ue_mode_operation (MMModem3gpp *self,
+ MMModem3gppEpsUeModeOperation mode,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_3gpp_set_eps_ue_mode_operation_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_3gpp_set_eps_ue_mode_operation_sync (MMModem3gpp *self,
+ MMModem3gppEpsUeModeOperation mode,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_3gpp_get_initial_eps_bearer (MMModem3gpp *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBearer *mm_modem_3gpp_get_initial_eps_bearer_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+MMBearer *mm_modem_3gpp_get_initial_eps_bearer_sync (MMModem3gpp *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_3gpp_set_initial_eps_bearer_settings (MMModem3gpp *self,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_3gpp_set_initial_eps_bearer_settings_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_3gpp_set_initial_eps_bearer_settings_sync (MMModem3gpp *self,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_3gpp_disable_facility_lock (MMModem3gpp *self,
+ MMModem3gppFacility facility,
+ const gchar *control_key,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_3gpp_disable_facility_lock_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_3gpp_disable_facility_lock_sync (MMModem3gpp *self,
+ MMModem3gppFacility facility,
+ const gchar *control_key,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_3gpp_set_packet_service_state (MMModem3gpp *self,
+ MMModem3gppPacketServiceState state,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_3gpp_set_packet_service_state_finish (MMModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_3gpp_set_packet_service_state_sync (MMModem3gpp *self,
+ MMModem3gppPacketServiceState state,
+ GCancellable *cancellable,
+ GError **error);
+
G_END_DECLS
#endif /* _MM_MODEM_3GPP_H_ */
diff --git a/libmm-glib/mm-modem-cdma.c b/libmm-glib/mm-modem-cdma.c
index a7596979..0624915b 100644
--- a/libmm-glib/mm-modem-cdma.c
+++ b/libmm-glib/mm-modem-cdma.c
@@ -50,6 +50,8 @@ G_DEFINE_TYPE (MMModemCdma, mm_modem_cdma, MM_GDBUS_TYPE_MODEM_CDMA_PROXY)
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_cdma_get_path (MMModemCdma *self)
@@ -64,9 +66,13 @@ mm_modem_cdma_get_path (MMModemCdma *self)
* mm_modem_cdma_dup_path:
* @self: A #MMModemCdma.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_cdma_dup_path (MMModemCdma *self)
@@ -87,15 +93,17 @@ mm_modem_cdma_dup_path (MMModemCdma *self)
* mm_modem_cdma_get_meid:
* @self: A #MMModemCdma.
*
- * Gets the <ulink url="http://en.wikipedia.org/wiki/MEID">Mobile Equipment Identifier</ulink>,
+ * Gets the
+ * <ulink url="http://en.wikipedia.org/wiki/MEID">Mobile Equipment Identifier</ulink>,
* as reported by this #MMModemCdma.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_cdma_dup_meid() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_cdma_dup_meid() if on another thread.</warning>
*
* Returns: (transfer none): The MEID, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_cdma_get_meid (MMModemCdma *self)
@@ -110,10 +118,14 @@ mm_modem_cdma_get_meid (MMModemCdma *self)
* mm_modem_cdma_dup_meid:
* @self: A #MMModemCdma.
*
- * Gets a copy of the <ulink url="http://en.wikipedia.org/wiki/MEID">Mobile Equipment Identifier</ulink>,
+ * Gets a copy of the
+ * <ulink url="http://en.wikipedia.org/wiki/MEID">Mobile Equipment Identifier</ulink>,
* as reported by this #MMModemCdma.
*
- * Returns: (transfer full): The MEID, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The MEID, or %NULL if none available. The returned
+ * value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_cdma_dup_meid (MMModemCdma *self)
@@ -130,17 +142,19 @@ mm_modem_cdma_dup_meid (MMModemCdma *self)
* mm_modem_cdma_get_esn:
* @self: A #MMModemCdma.
*
- * Gets the <ulink url="http://en.wikipedia.org/wiki/Electronic_serial_number">Electronic Serial Number</ulink>,
+ * Gets the
+ * <ulink url="http://en.wikipedia.org/wiki/Electronic_serial_number">Electronic Serial Number</ulink>,
* as reported by this #MMModemCdma.
*
* The ESN is superceded by MEID, but still used in older devices.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_cdma_dup_esn() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_cdma_dup_esn() if on another thread.</warning>
*
* Returns: (transfer none): The ESN, or %NULL if none available.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_cdma_get_esn (MMModemCdma *self)
@@ -155,12 +169,16 @@ mm_modem_cdma_get_esn (MMModemCdma *self)
* mm_modem_cdma_dup_esn:
* @self: A #MMModemCdma.
*
- * Gets a copy of the <ulink url="http://en.wikipedia.org/wiki/Electronic_serial_number">Electronic Serial Number</ulink>,
+ * Gets a copy of the
+ * <ulink url="http://en.wikipedia.org/wiki/Electronic_serial_number">Electronic Serial Number</ulink>,
* as reported by this #MMModemCdma.
*
* The ESN is superceded by MEID, but still used in older devices.
*
- * Returns: (transfer full): The ESN, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The ESN, or %NULL if none available. The returned
+ * value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_cdma_dup_esn (MMModemCdma *self)
@@ -177,11 +195,14 @@ mm_modem_cdma_dup_esn (MMModemCdma *self)
* mm_modem_cdma_get_sid:
* @self: A #MMModemCdma.
*
- * Gets the <ulink url="http://en.wikipedia.org/wiki/System_Identification_Number">System Identifier</ulink>
+ * Gets the
+ * <ulink url="http://en.wikipedia.org/wiki/System_Identification_Number">System Identifier</ulink>
* of the serving CDMA 1x network, if known, and if the modem is registered with
* a CDMA 1x network.
*
* Returns: The SID, or %MM_MODEM_CDMA_SID_UNKNOWN.
+ *
+ * Since: 1.0
*/
guint
mm_modem_cdma_get_sid (MMModemCdma *self)
@@ -197,11 +218,14 @@ mm_modem_cdma_get_sid (MMModemCdma *self)
* mm_modem_cdma_get_nid:
* @self: A #MMModemCdma.
*
- * Gets the <ulink url="http://en.wikipedia.org/wiki/Network_Identification_Number">Network Identifier</ulink>
+ * Gets the
+ * <ulink url="http://en.wikipedia.org/wiki/Network_Identification_Number">Network Identifier</ulink>
* of the serving CDMA 1x network, if known, and if the modem is registered with
* a CDMA 1x network.
*
* Returns: The NID, or %MM_MODEM_CDMA_NID_UNKNOWN.
+ *
+ * Since: 1.0
*/
guint
mm_modem_cdma_get_nid (MMModemCdma *self)
@@ -220,6 +244,8 @@ mm_modem_cdma_get_nid (MMModemCdma *self)
* Gets the state of the registration in the CDMA 1x network.
*
* Returns: a #MMModemCdmaRegistrationState.
+ *
+ * Since: 1.0
*/
MMModemCdmaRegistrationState
mm_modem_cdma_get_cdma1x_registration_state (MMModemCdma *self)
@@ -236,6 +262,8 @@ mm_modem_cdma_get_cdma1x_registration_state (MMModemCdma *self)
* Gets the state of the registration in the EV-DO network.
*
* Returns: a #MMModemCdmaRegistrationState.
+ *
+ * Since: 1.0
*/
MMModemCdmaRegistrationState
mm_modem_cdma_get_evdo_registration_state (MMModemCdma *self)
@@ -254,6 +282,8 @@ mm_modem_cdma_get_evdo_registration_state (MMModemCdma *self)
* Gets the state of the activation in the 3GPP2 network.
*
* Returns: a #MMModemCdmaActivationState.
+ *
+ * Since: 1.0
*/
MMModemCdmaActivationState
mm_modem_cdma_get_activation_state (MMModemCdma *self)
@@ -268,12 +298,15 @@ mm_modem_cdma_get_activation_state (MMModemCdma *self)
/**
* mm_modem_cdma_activate_finish:
* @self: A #MMModemCdma.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_cdma_activate().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_cdma_activate().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_cdma_activate().
*
* Returns: %TRUE if the activation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_cdma_activate_finish (MMModemCdma *self,
@@ -290,16 +323,22 @@ mm_modem_cdma_activate_finish (MMModemCdma *self,
* @self: A #MMModemCdma.
* @carrier: Name of the carrier.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to provision the modem for use with a given carrier
* using the modem's OTA activation functionality, if any.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_cdma_activate_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_cdma_activate_finish() to get the result of the operation.
+ *
+ * See mm_modem_cdma_activate_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_cdma_activate_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_cdma_activate (MMModemCdma *self,
@@ -323,10 +362,12 @@ mm_modem_cdma_activate (MMModemCdma *self,
* Synchronously requests to provision the modem for use with a given carrier
* using the modem's OTA activation functionality, if any.
*
- * The calling thread is blocked until a reply is received. See mm_modem_cdma_activate()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_cdma_activate() for the asynchronous version of this method.
*
* Returns: %TRUE if the activation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_cdma_activate_sync (MMModemCdma *self,
@@ -344,12 +385,15 @@ mm_modem_cdma_activate_sync (MMModemCdma *self,
/**
* mm_modem_cdma_activate_manual_finish:
* @self: A #MMModemCdma.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_cdma_activate_manual().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_cdma_activate_manual().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_cdma_activate_manual().
*
* Returns: %TRUE if the activation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_cdma_activate_manual_finish (MMModemCdma *self,
@@ -366,15 +410,22 @@ mm_modem_cdma_activate_manual_finish (MMModemCdma *self,
* @self: A #MMModemCdma.
* @properties: A #MMCdmaManualActivationProperties.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to provision the modem with the given properties.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_cdma_activate_manual_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from.
+ * You can then call mm_modem_cdma_activate_manual_finish() to get the result of
+ * the operation.
*
- * See mm_modem_cdma_activate_manual_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_cdma_activate_manual_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_cdma_activate_manual (MMModemCdma *self,
@@ -405,10 +456,12 @@ mm_modem_cdma_activate_manual (MMModemCdma *self,
*
* Synchronously requests to provision the modem with the given properties.
*
- * The calling thread is blocked until a reply is received. See mm_modem_cdma_activate_manual()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_cdma_activate_manual() for the asynchronous version of this method.
*
* Returns: %TRUE if the activation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_cdma_activate_manual_sync (MMModemCdma *self,
diff --git a/libmm-glib/mm-modem-cdma.h b/libmm-glib/mm-modem-cdma.h
index 31e7824a..931428ee 100644
--- a/libmm-glib/mm-modem-cdma.h
+++ b/libmm-glib/mm-modem-cdma.h
@@ -63,6 +63,7 @@ struct _MMModemCdmaClass {
};
GType mm_modem_cdma_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemCdma, g_object_unref)
const gchar *mm_modem_cdma_get_path (MMModemCdma *self);
gchar *mm_modem_cdma_dup_path (MMModemCdma *self);
diff --git a/libmm-glib/mm-modem-firmware.c b/libmm-glib/mm-modem-firmware.c
index 0bc6c6bd..9a67cb2a 100644
--- a/libmm-glib/mm-modem-firmware.c
+++ b/libmm-glib/mm-modem-firmware.c
@@ -40,6 +40,13 @@
G_DEFINE_TYPE (MMModemFirmware, mm_modem_firmware, MM_GDBUS_TYPE_MODEM_FIRMWARE_PROXY)
+struct _MMModemFirmwarePrivate {
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_OBJECT_DECLARE (update_settings, MMFirmwareUpdateSettings)
+};
+
/*****************************************************************************/
/**
@@ -49,6 +56,8 @@ G_DEFINE_TYPE (MMModemFirmware, mm_modem_firmware, MM_GDBUS_TYPE_MODEM_FIRMWARE_
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_firmware_get_path (MMModemFirmware *self)
@@ -63,9 +72,13 @@ mm_modem_firmware_get_path (MMModemFirmware *self)
* mm_modem_firmware_dup_path:
* @self: A #MMModemFirmware.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_firmware_dup_path (MMModemFirmware *self)
@@ -83,14 +96,60 @@ mm_modem_firmware_dup_path (MMModemFirmware *self)
/*****************************************************************************/
/**
+ * mm_modem_firmware_get_update_settings:
+ * @self: A #MMModemFirmware.
+ *
+ * Gets a #MMFirmwareUpdateSettings object specifying the expected update
+ * settings.
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_firmware_get_update_settings() again to get a new
+ * #MMFirmwareUpdateSettings with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMFirmwareUpdateSettings that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.10
+ */
+
+/**
+ * mm_modem_firmware_peek_update_settings:
+ * @self: A #MMModemFirmware.
+ *
+ * Gets a #MMFirmwareUpdateSettings object specifying the expected update
+ * settings.
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_firmware_get_update_settings() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMFirmwareUpdateSettings. Do not free the
+ * returned value, it belongs to @self.
+ *
+ * Since: 1.10
+ */
+
+PROPERTY_OBJECT_DEFINE_FAILABLE (update_settings,
+ ModemFirmware, modem_firmware, MODEM_FIRMWARE,
+ MMFirmwareUpdateSettings,
+ mm_firmware_update_settings_new_from_variant)
+
+/*****************************************************************************/
+
+/**
* mm_modem_firmware_select_finish:
* @self: A #MMModemFirmware.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_firmware_select().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_firmware_select().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_firmware_select().
*
* Returns: %TRUE if the selection was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_firmware_select_finish (MMModemFirmware *self,
@@ -107,7 +166,8 @@ mm_modem_firmware_select_finish (MMModemFirmware *self,
* @self: A #MMModemFirmware.
* @unique_id: Unique ID of the firmware image to select.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously selects a firmware image to boot.
@@ -115,10 +175,15 @@ mm_modem_firmware_select_finish (MMModemFirmware *self,
* <warning>The modem will possibly disappear once this action is run, as it
* needs to reboot in order to select the new image.</warning>
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_firmware_select_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_firmware_select_finish() to get the result of the operation.
+ *
+ * See mm_modem_firmware_select_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_firmware_select_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_firmware_select (MMModemFirmware *self,
@@ -128,7 +193,7 @@ mm_modem_firmware_select (MMModemFirmware *self,
gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_FIRMWARE (self));
- g_return_if_fail (unique_id != NULL || unique_id[0] == '\0');
+ g_return_if_fail (unique_id != NULL && unique_id[0] != '\0');
mm_gdbus_modem_firmware_call_select (MM_GDBUS_MODEM_FIRMWARE (self), unique_id, cancellable, callback, user_data);
}
@@ -145,10 +210,12 @@ mm_modem_firmware_select (MMModemFirmware *self,
* <warning>The modem will possibly disappear once this action is run, as it
* needs to reboot in order to select the new image.</warning>
*
- * The calling thread is blocked until a reply is received. See mm_modem_firmware_select()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_firmware_select() for the asynchronous version of this method.
*
* Returns: %TRUE if the selection was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_firmware_select_sync (MMModemFirmware *self,
@@ -157,7 +224,7 @@ mm_modem_firmware_select_sync (MMModemFirmware *self,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_FIRMWARE (self), FALSE);
- g_return_val_if_fail (unique_id != NULL || unique_id[0] == '\0', FALSE);
+ g_return_val_if_fail (unique_id != NULL && unique_id[0] != '\0', FALSE);
return mm_gdbus_modem_firmware_call_select_sync (MM_GDBUS_MODEM_FIRMWARE (self), unique_id, cancellable, error);
}
@@ -244,14 +311,22 @@ build_results (const gchar *str_selected,
/**
* mm_modem_firmware_list_finish:
* @self: A #MMModemFirmware.
- * @selected: (out) (allow-none) (transfer full): The selected firmware slot, or NULL if no slot is selected (such as if all slots are empty, or no slots exist). The returned value should be freed with g_object_unref().
- * @installed: (out) (allow-none) (transfer full) (element-type ModemManager.FirmwareProperties): A list of #MMFirmwareProperties objects specifying the installed images. The returned value should be freed with g_list_free_full() using g_object_unref() as #GDestroyNotify.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_firmware_list().
+ * @selected: (out) (allow-none) (transfer full): The selected firmware slot, or
+ * %NULL if no slot is selected (such as if all slots are empty, or no slots
+ * exist). The returned value should be freed with g_object_unref().
+ * @installed: (out) (allow-none) (transfer full) (element-type ModemManager.FirmwareProperties):
+ * A list of #MMFirmwareProperties objects specifying the installed images. The
+ * returned value should be freed with g_list_free_full() using
+ * g_object_unref() as #GDestroyNotify.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_firmware_list().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_firmware_list().
*
* Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_firmware_list_finish (MMModemFirmware *self,
@@ -293,15 +368,21 @@ mm_modem_firmware_list_finish (MMModemFirmware *self,
* mm_modem_firmware_list:
* @self: A #MMModemFirmware.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously gets the list of available firmware images.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_firmware_list_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_firmware_list_finish() to get the result of the operation.
+ *
+ * See mm_modem_firmware_list_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_firmware_list_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_firmware_list (MMModemFirmware *self,
@@ -320,17 +401,24 @@ mm_modem_firmware_list (MMModemFirmware *self,
/**
* mm_modem_firmware_list_sync:
* @self: A #MMModemFirmware.
- * @selected: (out) (allow-none) (transfer full): The selected firmware slot, or NULL if no slot is selected (such as if all slots are empty, or no slots exist). The returned value should be freed with g_object_unref().
- * @installed: (out) (allow-none) (transfer full) (element-type ModemManager.FirmwareProperties): A list of #MMFirmwareProperties objects specifying the installed images. The returned value should be freed with g_list_free_full() using g_object_unref() as #GDestroyNotify.
+ * @selected: (out) (allow-none) (transfer full): The selected firmware slot,
+ * or NULL if no slot is selected (such as if all slots are empty, or no slots
+ * exist). The returned value should be freed with g_object_unref().
+ * @installed: (out) (allow-none) (transfer full) (element-type ModemManager.FirmwareProperties):
+ * A list of #MMFirmwareProperties objects specifying the installed images. The
+ * returned value should be freed with g_list_free_full() using
+ * g_object_unref() as #GDestroyNotify.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return firmware for error or %NULL.
*
* Synchronously gets the list of available firmware images.
*
- * The calling thread is blocked until a reply is received. See mm_modem_firmware_list()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_firmware_list() for the asynchronous version of this method.
*
* Returns: %TRUE if the list was correctly retrieved, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_firmware_list_sync (MMModemFirmware *self,
@@ -372,9 +460,30 @@ mm_modem_firmware_list_sync (MMModemFirmware *self,
static void
mm_modem_firmware_init (MMModemFirmware *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_FIRMWARE, MMModemFirmwarePrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (update_settings, "update-settings")
+}
+
+static void
+finalize (GObject *object)
+{
+ MMModemFirmware *self = MM_MODEM_FIRMWARE (object);
+
+ g_mutex_clear (&self->priv->mutex);
+
+ PROPERTY_OBJECT_FINALIZE (update_settings)
+
+ G_OBJECT_CLASS (mm_modem_firmware_parent_class)->finalize (object);
}
static void
mm_modem_firmware_class_init (MMModemFirmwareClass *modem_class)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (modem_class);
+
+ g_type_class_add_private (object_class, sizeof (MMModemFirmwarePrivate));
+
+ object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem-firmware.h b/libmm-glib/mm-modem-firmware.h
index b600ad8b..bbd66a02 100644
--- a/libmm-glib/mm-modem-firmware.h
+++ b/libmm-glib/mm-modem-firmware.h
@@ -18,6 +18,7 @@
* Boston, MA 02110-1301 USA.
*
* Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef _MM_MODEM_FIRMWARE_H_
@@ -31,6 +32,7 @@
#include "mm-gdbus-modem.h"
#include "mm-firmware-properties.h"
+#include "mm-firmware-update-settings.h"
G_BEGIN_DECLS
@@ -43,6 +45,7 @@ G_BEGIN_DECLS
typedef struct _MMModemFirmware MMModemFirmware;
typedef struct _MMModemFirmwareClass MMModemFirmwareClass;
+typedef struct _MMModemFirmwarePrivate MMModemFirmwarePrivate;
/**
* MMModemFirmware:
@@ -53,7 +56,7 @@ typedef struct _MMModemFirmwareClass MMModemFirmwareClass;
struct _MMModemFirmware {
/*< private >*/
MmGdbusModemFirmwareProxy parent;
- gpointer unused;
+ MMModemFirmwarePrivate *priv;
};
struct _MMModemFirmwareClass {
@@ -62,10 +65,14 @@ struct _MMModemFirmwareClass {
};
GType mm_modem_firmware_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemFirmware, g_object_unref)
const gchar *mm_modem_firmware_get_path (MMModemFirmware *self);
gchar *mm_modem_firmware_dup_path (MMModemFirmware *self);
+MMFirmwareUpdateSettings *mm_modem_firmware_get_update_settings (MMModemFirmware *self);
+MMFirmwareUpdateSettings *mm_modem_firmware_peek_update_settings (MMModemFirmware *self);
+
void mm_modem_firmware_list (MMModemFirmware *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
diff --git a/libmm-glib/mm-modem-location.c b/libmm-glib/mm-modem-location.c
index 229d604a..9c46b046 100644
--- a/libmm-glib/mm-modem-location.c
+++ b/libmm-glib/mm-modem-location.c
@@ -40,6 +40,18 @@
G_DEFINE_TYPE (MMModemLocation, mm_modem_location, MM_GDBUS_TYPE_MODEM_LOCATION_PROXY)
+struct _MMModemLocationPrivate {
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ MMLocation3gpp *signaled_3gpp;
+ MMLocationGpsNmea *signaled_gps_nmea;
+ MMLocationGpsRaw *signaled_gps_raw;
+ MMLocationCdmaBs *signaled_cdma_bs;
+
+ PROPERTY_COMMON_DECLARE (signaled_location)
+};
+
/*****************************************************************************/
/**
@@ -49,6 +61,8 @@ G_DEFINE_TYPE (MMModemLocation, mm_modem_location, MM_GDBUS_TYPE_MODEM_LOCATION_
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_location_get_path (MMModemLocation *self)
@@ -63,9 +77,13 @@ mm_modem_location_get_path (MMModemLocation *self)
* mm_modem_location_dup_path:
* @self: A #MMModemLocation.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_location_dup_path (MMModemLocation *self)
@@ -86,9 +104,12 @@ mm_modem_location_dup_path (MMModemLocation *self)
* mm_modem_location_get_capabilities:
* @self: A #MMModemLocation.
*
- * Gets a bitmask of the location capabilities supported by this #MMModemLocation.
+ * Gets a bitmask of the location capabilities supported by this
+ * #MMModemLocation.
*
* Returns: A #MMModemLocationSource.
+ *
+ * Since: 1.0
*/
MMModemLocationSource
mm_modem_location_get_capabilities (MMModemLocation *self)
@@ -101,12 +122,34 @@ mm_modem_location_get_capabilities (MMModemLocation *self)
/*****************************************************************************/
/**
+ * mm_modem_location_get_supported_assistance_data:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a bitmask of the supported assistance data types.
+ *
+ * Returns: A #MMModemLocationAssistanceDataType.
+ *
+ * Since: 1.10
+ */
+MMModemLocationAssistanceDataType
+mm_modem_location_get_supported_assistance_data (MMModemLocation *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
+
+ return (MMModemLocationAssistanceDataType) mm_gdbus_modem_location_get_supported_assistance_data (MM_GDBUS_MODEM_LOCATION (self));
+}
+
+/*****************************************************************************/
+
+/**
* mm_modem_location_get_enabled:
* @self: A #MMModemLocation.
*
* Gets a bitmask of the location capabilities which are enabled in this #MMModemLocation.
*
* Returns: A #MMModemLocationSource.
+ *
+ * Since: 1.0
*/
MMModemLocationSource
mm_modem_location_get_enabled (MMModemLocation *self)
@@ -124,7 +167,9 @@ mm_modem_location_get_enabled (MMModemLocation *self)
*
* Gets the status of the location signaling in the #MMModemLocation.
*
- * Returns: %TRUE if location changes are signaled, %FALSE otherwise..
+ * Returns: %TRUE if location changes are signaled, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_location_signals_location (MMModemLocation *self)
@@ -145,6 +190,8 @@ mm_modem_location_signals_location (MMModemLocation *self)
* Finishes an operation started with mm_modem_location_setup().
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_location_setup_finish (MMModemLocation *self,
@@ -159,19 +206,26 @@ mm_modem_location_setup_finish (MMModemLocation *self,
/**
* mm_modem_location_setup:
* @self: A #MMModemLocation.
- * @sources: Bitmask of #MMModemLocationSource values specifying which locations should get enabled.
+ * @sources: Bitmask of #MMModemLocationSource values specifying which locations
+ * should get enabled.
* @signal_location: Flag to enable or disable location signaling.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously configures the location sources to use when gathering location
* information. Also enable or disable location information gathering.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_setup_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_setup_finish() to get the result of the operation.
*
- * See mm_modem_location_setup_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_location_setup_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_location_setup (MMModemLocation *self,
@@ -194,7 +248,8 @@ mm_modem_location_setup (MMModemLocation *self,
/**
* mm_modem_location_setup_sync:
* @self: A #MMModemLocation.
- * @sources: Bitmask of #MMModemLocationSource values specifying which locations should get enabled.
+ * @sources: Bitmask of #MMModemLocationSource values specifying which locations
+ * should get enabled.
* @signal_location: Flag to enable or disable location signaling.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
@@ -202,10 +257,12 @@ mm_modem_location_setup (MMModemLocation *self,
* Synchronously configures the location sources to use when gathering location
* information. Also enable or disable location information gathering.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_setup()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_setup() for the asynchronous version of this method.
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_location_setup_sync (MMModemLocation *self,
@@ -228,12 +285,16 @@ mm_modem_location_setup_sync (MMModemLocation *self,
/**
* mm_modem_location_set_supl_server_finish:
* @self: A #MMModemLocation.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_set_supl_server().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_set_supl_server().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_location_set_supl_server().
*
- * Returns: %TRUE if setting the SUPL server was successful, %FALSE if @error is set.
+ * Returns: %TRUE if setting the SUPL server was successful, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.6
*/
gboolean
mm_modem_location_set_supl_server_finish (MMModemLocation *self,
@@ -250,15 +311,21 @@ mm_modem_location_set_supl_server_finish (MMModemLocation *self,
* @self: A #MMModemLocation.
* @supl: The SUPL server address, given as IP:PORT or with a full URL.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously configures the address of the SUPL server for A-GPS operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_set_supl_server_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_set_supl_server_finish() to get the result of the operation.
*
- * See mm_modem_location_set_supl_server_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_location_set_supl_server_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.6
*/
void
mm_modem_location_set_supl_server (MMModemLocation *self,
@@ -285,10 +352,14 @@ mm_modem_location_set_supl_server (MMModemLocation *self,
*
* Synchronously configures the address of the SUPL server for A-GPS operation.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_set_supl_server()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_set_supl_server() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if setting the SUPL server was successful, %FALSE if @error is
+ * set.
*
- * Returns: %TRUE if setting the SUPL server was successful, %FALSE if @error is set.
+ * Since: 1.6
*/
gboolean
mm_modem_location_set_supl_server_sync (MMModemLocation *self,
@@ -306,26 +377,225 @@ mm_modem_location_set_supl_server_sync (MMModemLocation *self,
/*****************************************************************************/
+/**
+ * mm_modem_location_inject_assistance_data_finish:
+ * @self: A #MMModemLocation.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_inject_assistance_data().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with
+ * mm_modem_location_inject_assistance_data().
+ *
+ * Returns: %TRUE if the injection was successful, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_modem_location_inject_assistance_data_finish (MMModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE);
+
+ return mm_gdbus_modem_location_call_inject_assistance_data_finish (MM_GDBUS_MODEM_LOCATION (self), res, error);
+}
+
+/**
+ * mm_modem_location_inject_assistance_data:
+ * @self: A #MMModemLocation.
+ * @data: (array length=data_size): Data to inject.
+ * @data_size: size of @data.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Aynchronously injects assistance data to the GNSS module.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_inject_assistance_data_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_location_inject_assistance_data_sync() for the synchronous,
+ * blocking version of this method.
+ *
+ * Since: 1.10
+ */
+void
+mm_modem_location_inject_assistance_data (MMModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LOCATION (self));
+
+ mm_gdbus_modem_location_call_inject_assistance_data (MM_GDBUS_MODEM_LOCATION (self),
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, data_size, sizeof (guint8)),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_location_inject_assistance_data_sync:
+ * @self: A #MMModemLocation.
+ * @data: (array length=data_size): Data to inject.
+ * @data_size: size of @data.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously injects assistance data to the GNSS module.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_inject_assistance_data() for the asynchronous version of
+ * this method.
+ *
+ * Returns: %TRUE if the injection was successful, %FALSE if @error is set.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_modem_location_inject_assistance_data_sync (MMModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE);
+
+ return mm_gdbus_modem_location_call_inject_assistance_data_sync (MM_GDBUS_MODEM_LOCATION (self),
+ g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, data, data_size, sizeof (guint8)),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_location_set_gps_refresh_rate_finish:
+ * @self: A #MMModemLocation.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_set_gps_refresh_rate().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_location_set_gps_refresh_rate().
+ *
+ * Returns: %TRUE if setting the GPS refresh rate was successful, %FALSE if
+ * @error is set.
+ *
+ * Since: 1.0
+ */
+gboolean
+mm_modem_location_set_gps_refresh_rate_finish (MMModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE);
+
+ return mm_gdbus_modem_location_call_set_gps_refresh_rate_finish (MM_GDBUS_MODEM_LOCATION (self), res, error);
+}
+
+/**
+ * mm_modem_location_set_gps_refresh_rate:
+ * @self: A #MMModemLocation.
+ * @rate: The GPS refresh rate, in seconds.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously configures the GPS refresh rate.
+
+ * If a 0 rate is used, the GPS location updates will be immediately propagated
+ * to the interface.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_set_gps_refresh_rate_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_location_set_gps_refresh_rate_sync() for the synchronous,
+ * blocking version of this method.
+ *
+ * Since: 1.0
+ */
+void
+mm_modem_location_set_gps_refresh_rate (MMModemLocation *self,
+ guint rate,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_LOCATION (self));
+
+ mm_gdbus_modem_location_call_set_gps_refresh_rate (MM_GDBUS_MODEM_LOCATION (self),
+ rate,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_location_set_gps_refresh_rate_sync:
+ * @self: A #MMModemLocation.
+ * @rate: The GPS refresh rate, in seconds.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously configures the GPS refresh rate.
+ *
+ * If a 0 rate is used, the GPS location updates will be immediately propagated
+ * to the interface.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_set_gps_refresh_rate() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if setting the refresh rate was successful, %FALSE if @error
+ * is set.
+ *
+ * Since: 1.0
+ */
+gboolean
+mm_modem_location_set_gps_refresh_rate_sync (MMModemLocation *self,
+ guint rate,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE);
+
+ return mm_gdbus_modem_location_call_set_gps_refresh_rate_sync (MM_GDBUS_MODEM_LOCATION (self),
+ rate,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
static gboolean
-build_locations (GVariant *dictionary,
- MMLocation3gpp **location_3gpp,
+build_locations (GVariant *dictionary,
+ MMLocation3gpp **location_3gpp,
MMLocationGpsNmea **location_gps_nmea,
- MMLocationGpsRaw **location_gps_raw,
- MMLocationCdmaBs **location_cdma_bs,
- GError **error)
+ MMLocationGpsRaw **location_gps_raw,
+ MMLocationCdmaBs **location_cdma_bs,
+ GError **error)
{
- GError *inner_error = NULL;
- GVariant *value;
- guint source;
- GVariantIter iter;
+ GError *inner_error = NULL;
+ GVariant *value;
+ guint source;
+ GVariantIter iter;
if (!dictionary)
/* No location provided. Not actually an error. */
return TRUE;
g_variant_iter_init (&iter, dictionary);
- while (!inner_error &&
- g_variant_iter_next (&iter, "{uv}", &source, &value)) {
+ while (!inner_error && g_variant_iter_next (&iter, "{uv}", &source, &value)) {
switch (source) {
case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI:
if (location_3gpp)
@@ -347,16 +617,12 @@ build_locations (GVariant *dictionary,
g_warn_if_reached ();
break;
}
-
g_variant_unref (value);
}
- g_variant_unref (dictionary);
-
if (inner_error) {
g_propagate_error (error, inner_error);
- g_prefix_error (error,
- "Couldn't build locations result: ");
+ g_prefix_error (error, "Couldn't build locations result: ");
return FALSE;
}
@@ -366,16 +632,27 @@ build_locations (GVariant *dictionary,
/**
* mm_modem_location_get_full_finish:
* @self: A #MMModemLocation.
- * @location_3gpp: (out) (allow-none) (transfer full): Return location for a #MMLocation3gpp if 3GPP location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @location_gps_nmea: (out) (allow-none) (transfer full): Return location for a #MMLocationGpsNmea if GPS NMEA location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @location_gps_raw: (out) (allow-none) (transfer full): Return location for a #MMLocationGpsRaw if GPS raw location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @location_cdma_bs: (out) (allow-none) (transfer full): Return location for a #MMLocationCdmaBs if CDMA Base Station location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_get_full().
+ * @location_3gpp: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocation3gpp if 3GPP location is requested, or #NULL if not required. The
+ * returned value should be freed with g_object_unref().
+ * @location_gps_nmea: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocationGpsNmea if GPS NMEA location is requested, or #NULL if not
+ * required. The returned value should be freed with g_object_unref().
+ * @location_gps_raw: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocationGpsRaw if GPS raw location is requested, or #NULL if not required.
+ * The returned value should be freed with g_object_unref().
+ * @location_cdma_bs: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocationCdmaBs if CDMA Base Station location is requested, or #NULL if
+ * not required. The returned value should be freed with g_object_unref().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_get_full().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_location_get_full().
*
* Returns: %TRUE if the retrieval was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_location_get_full_finish (MMModemLocation *self,
@@ -386,7 +663,7 @@ mm_modem_location_get_full_finish (MMModemLocation *self,
MMLocationCdmaBs **location_cdma_bs,
GError **error)
{
- GVariant *dictionary = NULL;
+ g_autoptr(GVariant) dictionary = NULL;
g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE);
@@ -400,15 +677,21 @@ mm_modem_location_get_full_finish (MMModemLocation *self,
* mm_modem_location_get_full:
* @self: A #MMModemLocation.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously gets the current location information.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_get_full_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_get_full_finish() to get the result of the operation.
*
- * See mm_modem_location_get_full_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_location_get_full_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_location_get_full (MMModemLocation *self,
@@ -427,19 +710,29 @@ mm_modem_location_get_full (MMModemLocation *self,
/**
* mm_modem_location_get_full_sync:
* @self: A #MMModemLocation.
- * @location_3gpp: (out) (allow-none) (transfer full): Return location for a #MMLocation3gpp if 3GPP location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @location_gps_nmea: (out) (allow-none) (transfer full): Return location for a #MMLocationGpsNmea if GPS NMEA location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @location_gps_raw: (out) (allow-none) (transfer full): Return location for a #MMLocationGpsRaw if GPS raw location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
- * @location_cdma_bs: (out) (allow-none) (transfer full): Return location for a #MMLocationCdmaBs if CDMA Base Station location is requested, or #NULL if not required. The returned value should be freed with g_object_unref().
+ * @location_3gpp: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocation3gpp if 3GPP location is requested, or #NULL if not required. The
+ * returned value should be freed with g_object_unref().
+ * @location_gps_nmea: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocationGpsNmea if GPS NMEA location is requested, or #NULL if not
+ * required. The returned value should be freed with g_object_unref().
+ * @location_gps_raw: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocationGpsRaw if GPS raw location is requested, or #NULL if not required.
+ * The returned value should be freed with g_object_unref().
+ * @location_cdma_bs: (out) (allow-none) (transfer full): Return location for a
+ * #MMLocationCdmaBs if CDMA Base Station location is requested, or #NULL if
+ * not required. The returned value should be freed with g_object_unref().
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously gets the current location information.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_get_full()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_get_full() for the asynchronous version of this method.
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_location_get_full_sync (MMModemLocation *self,
@@ -450,7 +743,7 @@ mm_modem_location_get_full_sync (MMModemLocation *self,
GCancellable *cancellable,
GError **error)
{
- GVariant *dictionary = NULL;
+ g_autoptr(GVariant) dictionary = NULL;
g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), FALSE);
@@ -465,12 +758,16 @@ mm_modem_location_get_full_sync (MMModemLocation *self,
/**
* mm_modem_location_get_3gpp_finish:
* @self: A #MMModemLocation.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_get_3gpp().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_get_3gpp().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_location_get_3gpp().
*
- * Returns: (transfer full): A #MMLocation3gpp, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMLocation3gpp, or #NULL if not available. The
+ * returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMLocation3gpp *
mm_modem_location_get_3gpp_finish (MMModemLocation *self,
@@ -488,15 +785,21 @@ mm_modem_location_get_3gpp_finish (MMModemLocation *self,
* mm_modem_location_get_3gpp:
* @self: A #MMModemLocation.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously gets the current 3GPP location information.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_get_3gpp_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_get_3gpp_finish() to get the result of the operation.
+ *
+ * See mm_modem_location_get_3gpp_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_location_get_3gpp_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_location_get_3gpp (MMModemLocation *self,
@@ -515,10 +818,13 @@ mm_modem_location_get_3gpp (MMModemLocation *self,
*
* Synchronously gets the current 3GPP location information.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_get_3gpp()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_get_3gpp() for the asynchronous version of this method.
*
- * Returns: (transfer full): A #MMLocation3gpp, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMLocation3gpp, or #NULL if not available. The
+ * returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMLocation3gpp *
mm_modem_location_get_3gpp_sync (MMModemLocation *self,
@@ -537,12 +843,16 @@ mm_modem_location_get_3gpp_sync (MMModemLocation *self,
/**
* mm_modem_location_get_gps_nmea_finish:
* @self: A #MMModemLocation.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_get_gps_nmea().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_get_gps_nmea().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_location_get_gps_nmea().
*
- * Returns: (transfer full): A #MMLocationGpsNmea, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMLocationGpsNmea, or #NULL if not available.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMLocationGpsNmea *
mm_modem_location_get_gps_nmea_finish (MMModemLocation *self,
@@ -560,15 +870,21 @@ mm_modem_location_get_gps_nmea_finish (MMModemLocation *self,
* mm_modem_location_get_gps_nmea:
* @self: A #MMModemLocation.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously gets the current GPS NMEA location information.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_get_gps_nmea_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_get_gps_nmea_finish() to get the result of the operation.
+ *
+ * See mm_modem_location_get_gps_nmea_sync() for the synchronous, blocking
+ * version of this method.
*
- * See mm_modem_location_get_gps_nmea_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_location_get_gps_nmea (MMModemLocation *self,
@@ -587,10 +903,13 @@ mm_modem_location_get_gps_nmea (MMModemLocation *self,
*
* Synchronously gets the current GPS NMEA location information.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_get_gps_nmea()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_get_gps_nmea() for the asynchronous version of this method.
*
- * Returns: (transfer full): A #MMLocationGpsNmea, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMLocationGpsNmea, or #NULL if not available.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMLocationGpsNmea *
mm_modem_location_get_gps_nmea_sync (MMModemLocation *self,
@@ -609,12 +928,16 @@ mm_modem_location_get_gps_nmea_sync (MMModemLocation *self,
/**
* mm_modem_location_get_gps_raw_finish:
* @self: A #MMModemLocation.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_get_gps_raw().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_get_gps_raw().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_location_get_gps_raw().
*
- * Returns: (transfer full): A #MMLocationGpsRaw, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMLocationGpsRaw, or #NULL if not available.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMLocationGpsRaw *
mm_modem_location_get_gps_raw_finish (MMModemLocation *self,
@@ -632,15 +955,21 @@ mm_modem_location_get_gps_raw_finish (MMModemLocation *self,
* mm_modem_location_get_gps_raw:
* @self: A #MMModemLocation.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously gets the current GPS raw location information.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_get_gps_raw_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_get_gps_raw_finish() to get the result of the operation.
+ *
+ * See mm_modem_location_get_gps_raw_sync() for the synchronous, blocking
+ * version of this method.
*
- * See mm_modem_location_get_gps_raw_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_location_get_gps_raw (MMModemLocation *self,
@@ -659,10 +988,13 @@ mm_modem_location_get_gps_raw (MMModemLocation *self,
*
* Synchronously gets the current GPS raw location information.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_get_gps_raw()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_get_gps_raw() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A #MMLocationGpsRaw, or #NULL if not available.
+ * The returned value should be freed with g_object_unref().
*
- * Returns: (transfer full): A #MMLocationGpsRaw, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Since: 1.0
*/
MMLocationGpsRaw *
mm_modem_location_get_gps_raw_sync (MMModemLocation *self,
@@ -681,12 +1013,16 @@ mm_modem_location_get_gps_raw_sync (MMModemLocation *self,
/**
* mm_modem_location_get_cdma_bs_finish:
* @self: A #MMModemLocation.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_location_get_cdma_bs().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_location_get_cdma_bs().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_location_get_cdma_bs().
*
- * Returns: (transfer full): A #MMLocationCdmaBs, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A #MMLocationCdmaBs, or #NULL if not available.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMLocationCdmaBs *
mm_modem_location_get_cdma_bs_finish (MMModemLocation *self,
@@ -704,15 +1040,21 @@ mm_modem_location_get_cdma_bs_finish (MMModemLocation *self,
* mm_modem_location_get_cdma_bs:
* @self: A #MMModemLocation.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously gets the current CDMA base station location information.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_location_get_cdma_bs_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_location_get_cdma_bs_finish() to get the result of the operation.
*
- * See mm_modem_location_get_cdma_bs_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_location_get_cdma_bs_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_location_get_cdma_bs (MMModemLocation *self,
@@ -731,10 +1073,13 @@ mm_modem_location_get_cdma_bs (MMModemLocation *self,
*
* Synchronously gets the current CDMA base station location information.
*
- * The calling thread is blocked until a reply is received. See mm_modem_location_get_cdma_bs()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_location_get_cdma_bs() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A #MMLocationCdmaBs, or #NULL if not available.
+ * The returned value should be freed with g_object_unref().
*
- * Returns: (transfer full): A #MMLocationCdmaBs, or #NULL if not available. The returned value should be freed with g_object_unref().
+ * Since: 1.0
*/
MMLocationCdmaBs *
mm_modem_location_get_cdma_bs_sync (MMModemLocation *self,
@@ -756,12 +1101,14 @@ mm_modem_location_get_cdma_bs_sync (MMModemLocation *self,
*
* Gets the address of the SUPL server.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_supl_server() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_location_dup_supl_server() if on another thread.</warning>
*
- * Returns: (transfer none): The SUPL server address, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The SUPL server address, or %NULL if none
+ * available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.6
*/
const gchar *
mm_modem_location_get_supl_server (MMModemLocation *self)
@@ -778,7 +1125,10 @@ mm_modem_location_get_supl_server (MMModemLocation *self)
*
* Gets the address of the SUPL server.
*
- * Returns: (transfer full): The SUPL server address, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The SUPL server address, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.6
*/
gchar *
mm_modem_location_dup_supl_server (MMModemLocation *self)
@@ -791,12 +1141,352 @@ mm_modem_location_dup_supl_server (MMModemLocation *self)
/*****************************************************************************/
+/**
+ * mm_modem_location_get_assistance_data_servers:
+ * @self: A #MMModemLocation.
+ *
+ * Gets the list of assistance data servers.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_location_dup_assistance_data_servers() if on another thread.
+ * </warning>
+ *
+ * Returns: (transfer none): a %NULL-terminated array of server addresses, or
+ * %NULL if none available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.10
+ */
+const gchar **
+mm_modem_location_get_assistance_data_servers (MMModemLocation *self)
+{
+ const gchar **tmp;
+
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ tmp = (const gchar **) mm_gdbus_modem_location_get_assistance_data_servers (MM_GDBUS_MODEM_LOCATION (self));
+
+ return ((tmp && tmp[0]) ? tmp : NULL);
+}
+
+/**
+ * mm_modem_location_dup_assistance_data_servers:
+ * @self: A #MMModemLocation.
+ *
+ * Gets the list of assistance data servers.
+ *
+ * Returns: (transfer full): a %NULL-terminated array of server addresses, or
+ * %NULL if none available. The returned value should be freed with
+ * g_strfreev().
+ *
+ * Since: 1.10
+ */
+gchar **
+mm_modem_location_dup_assistance_data_servers (MMModemLocation *self)
+{
+ gchar **tmp;
+
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), NULL);
+
+ tmp = mm_gdbus_modem_location_dup_assistance_data_servers (MM_GDBUS_MODEM_LOCATION (self));
+ if (tmp && tmp[0])
+ return tmp;
+
+ g_strfreev (tmp);
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_location_get_gps_refresh_rate:
+ * @self: A #MMModemLocation.
+ *
+ * Gets the GPS refresh rate, in seconds.
+ *
+ * Returns: The GPS refresh rate, or 0 if no fixed rate is used.
+ *
+ * Since: 1.0
+ */
+guint
+mm_modem_location_get_gps_refresh_rate (MMModemLocation *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_LOCATION (self), 0);
+
+ return mm_gdbus_modem_location_get_gps_refresh_rate (MM_GDBUS_MODEM_LOCATION (self));
+}
+
+/*****************************************************************************/
+
+/* custom refresh method instead of PROPERTY_OBJECT_DEFINE_REFRESH() */
+static void
+signaled_location_refresh (MMModemLocation *self)
+{
+ g_autoptr(GVariant) variant = NULL;
+ g_autoptr(GError) inner_error = NULL;
+
+ g_clear_object (&self->priv->signaled_3gpp);
+ g_clear_object (&self->priv->signaled_gps_nmea);
+ g_clear_object (&self->priv->signaled_gps_raw);
+ g_clear_object (&self->priv->signaled_cdma_bs);
+
+ variant = mm_gdbus_modem_location_dup_location (MM_GDBUS_MODEM_LOCATION (self));
+ if (!variant)
+ return;
+
+ if (!build_locations (variant,
+ &self->priv->signaled_3gpp,
+ &self->priv->signaled_gps_nmea,
+ &self->priv->signaled_gps_raw,
+ &self->priv->signaled_cdma_bs,
+ &inner_error))
+ g_warning ("Invalid signaled location received: %s", inner_error->message);
+}
+
+PROPERTY_DEFINE_UPDATED (signaled_location, ModemLocation)
+
+/**
+ * mm_modem_location_peek_signaled_3gpp:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocation3gpp object with the current 3GPP location information.
+ *
+ * Unlike mm_modem_location_get_3gpp() or mm_modem_location_get_3gpp_sync(),
+ * this method does not perform an explicit query. Instead, this method will
+ * return the location information that may have been signaled by the modem.
+ * Therefore, this method will only succeed if location signaling is enabled
+ * (e.g. with mm_modem_location_setup() in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_3gpp() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocation3gpp, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_3gpp, ModemLocation, modem_location, MODEM_LOCATION, MMLocation3gpp)
+
+/**
+ * mm_modem_location_get_signaled_3gpp:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocation3gpp object with the current 3GPP location information.
+ *
+ * Unlike mm_modem_location_get_3gpp() or mm_modem_location_get_3gpp_sync(),
+ * this method does not perform an explicit query. Instead, this method will
+ * return the location information that may have been signaled by the modem.
+ * Therefore, this method will only succeed if location signaling is enabled
+ * (e.g. with mm_modem_location_setup() in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_3gpp() again to get a new #MMLocation3gpp
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocation3gpp that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_3gpp, ModemLocation, modem_location, MODEM_LOCATION, MMLocation3gpp)
+
+/**
+ * mm_modem_location_peek_signaled_gps_nmea:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsNmea object with the current GPS NMEA location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_nmea() or
+ * mm_modem_location_get_gps_nmea_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_gps_nmea() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocationGpsNmea, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_gps_nmea, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsNmea)
+
+/**
+ * mm_modem_location_get_signaled_gps_nmea:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsNmea object with the current GPS NMEA location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_nmea() or
+ * mm_modem_location_get_gps_nmea_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_gps_nmea() again to get a new #MMLocationGpsNmea
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocationGpsNmea that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_gps_nmea, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsNmea)
+
+/**
+ * mm_modem_location_peek_signaled_gps_raw:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsRaw object with the current GPS raw location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_raw() or
+ * mm_modem_location_get_gps_raw_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_gps_raw() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocationGpsRaw, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_gps_raw, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsRaw)
+
+/**
+ * mm_modem_location_get_signaled_gps_raw:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationGpsRaw object with the current GPS raw location
+ * information.
+ *
+ * Unlike mm_modem_location_get_gps_raw() or
+ * mm_modem_location_get_gps_raw_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_gps_raw() again to get a new #MMLocationGpsRaw
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocationGpsRaw that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_gps_raw, ModemLocation, modem_location, MODEM_LOCATION, MMLocationGpsRaw)
+
+/**
+ * mm_modem_location_peek_signaled_cdma_bs:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationCdmaBs object with the current CDMA base station location
+ * information.
+ *
+ * Unlike mm_modem_location_get_cdma_bs() or
+ * mm_modem_location_get_cdma_bs_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The returned value is only valid until the property changes so
+ * it is only safe to use this function on the thread where
+ * @self was constructed. Use mm_modem_location_get_signaled_cdma_bs() if on
+ * another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMLocationCdmaBs, or %NULL if none available. Do
+ * not free the returned value, it belongs to @self.
+ *
+ * Since: 1.18
+ */
+
+PROPERTY_OBJECT_DEFINE_PEEK (signaled_location, signaled_cdma_bs, ModemLocation, modem_location, MODEM_LOCATION, MMLocationCdmaBs)
+
+/**
+ * mm_modem_location_get_signaled_cdma_bs:
+ * @self: A #MMModemLocation.
+ *
+ * Gets a #MMLocationCdmaBs object with the current CDMA base station location
+ * information.
+ *
+ * Unlike mm_modem_location_get_cdma_bs() or
+ * mm_modem_location_get_cdma_bs_sync(), this method does not perform an
+ * explicit query. Instead, this method will return the location information
+ * that may have been signaled by the modem. Therefore, this method will only
+ * succeed if location signaling is enabled (e.g. with mm_modem_location_setup()
+ * in the #MMModemLocation).
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_location_get_signaled_cdma_bs() again to get a new #MMLocationCdmaBs
+ * with the new values.</warning>
+ *
+ * Returns: (transfer full): A #MMLocationCdmaBs that must be freed with
+ * g_object_unref() or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+PROPERTY_OBJECT_DEFINE_GET (signaled_location, signaled_cdma_bs, ModemLocation, modem_location, MODEM_LOCATION, MMLocationCdmaBs)
+
+/*****************************************************************************/
+
static void
mm_modem_location_init (MMModemLocation *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_LOCATION, MMModemLocationPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (signaled_location, "location")
+}
+
+static void
+finalize (GObject *object)
+{
+ MMModemLocation *self = MM_MODEM_LOCATION (object);
+
+ g_mutex_clear (&self->priv->mutex);
+
+ PROPERTY_OBJECT_FINALIZE (signaled_3gpp)
+ PROPERTY_OBJECT_FINALIZE (signaled_gps_nmea)
+ PROPERTY_OBJECT_FINALIZE (signaled_gps_raw)
+ PROPERTY_OBJECT_FINALIZE (signaled_cdma_bs)
+
+ G_OBJECT_CLASS (mm_modem_location_parent_class)->finalize (object);
}
static void
mm_modem_location_class_init (MMModemLocationClass *modem_class)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (modem_class);
+
+ g_type_class_add_private (object_class, sizeof (MMModemLocationPrivate));
+
+ object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem-location.h b/libmm-glib/mm-modem-location.h
index 279288d7..142c34e8 100644
--- a/libmm-glib/mm-modem-location.h
+++ b/libmm-glib/mm-modem-location.h
@@ -47,6 +47,7 @@ G_BEGIN_DECLS
typedef struct _MMModemLocation MMModemLocation;
typedef struct _MMModemLocationClass MMModemLocationClass;
+typedef struct _MMModemLocationPrivate MMModemLocationPrivate;
/**
* MMModemLocation:
@@ -56,8 +57,8 @@ typedef struct _MMModemLocationClass MMModemLocationClass;
*/
struct _MMModemLocation {
/*< private >*/
- MmGdbusModemLocationProxy parent;
- gpointer unused;
+ MmGdbusModemLocationProxy parent;
+ MMModemLocationPrivate *priv;
};
struct _MMModemLocationClass {
@@ -66,6 +67,7 @@ struct _MMModemLocationClass {
};
GType mm_modem_location_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemLocation, g_object_unref)
const gchar *mm_modem_location_get_path (MMModemLocation *self);
gchar *mm_modem_location_dup_path (MMModemLocation *self);
@@ -76,9 +78,16 @@ MMModemLocationSource mm_modem_location_get_enabled (MMModemLocation *self)
gboolean mm_modem_location_signals_location (MMModemLocation *self);
+MMModemLocationAssistanceDataType mm_modem_location_get_supported_assistance_data (MMModemLocation *self);
+
const gchar *mm_modem_location_get_supl_server (MMModemLocation *self);
gchar *mm_modem_location_dup_supl_server (MMModemLocation *self);
+const gchar **mm_modem_location_get_assistance_data_servers (MMModemLocation *self);
+gchar **mm_modem_location_dup_assistance_data_servers (MMModemLocation *self);
+
+guint mm_modem_location_get_gps_refresh_rate (MMModemLocation *self);
+
void mm_modem_location_setup (MMModemLocation *self,
MMModemLocationSource sources,
gboolean signal_location,
@@ -107,6 +116,34 @@ gboolean mm_modem_location_set_supl_server_sync (MMModemLocation *self,
GCancellable *cancellable,
GError **error);
+void mm_modem_location_inject_assistance_data (MMModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_location_inject_assistance_data_finish (MMModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_location_inject_assistance_data_sync (MMModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_location_set_gps_refresh_rate (MMModemLocation *self,
+ guint rate,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_location_set_gps_refresh_rate_finish (MMModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_location_set_gps_refresh_rate_sync (MMModemLocation *self,
+ guint rate,
+ GCancellable *cancellable,
+ GError **error);
+
void mm_modem_location_get_3gpp (MMModemLocation *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -170,6 +207,15 @@ gboolean mm_modem_location_get_full_sync (MMModemLocation *self,
GCancellable *cancellable,
GError **error);
+MMLocation3gpp *mm_modem_location_peek_signaled_3gpp (MMModemLocation *self);
+MMLocation3gpp *mm_modem_location_get_signaled_3gpp (MMModemLocation *self);
+MMLocationGpsNmea *mm_modem_location_peek_signaled_gps_nmea (MMModemLocation *self);
+MMLocationGpsNmea *mm_modem_location_get_signaled_gps_nmea (MMModemLocation *self);
+MMLocationGpsRaw *mm_modem_location_peek_signaled_gps_raw (MMModemLocation *self);
+MMLocationGpsRaw *mm_modem_location_get_signaled_gps_raw (MMModemLocation *self);
+MMLocationCdmaBs *mm_modem_location_peek_signaled_cdma_bs (MMModemLocation *self);
+MMLocationCdmaBs *mm_modem_location_get_signaled_cdma_bs (MMModemLocation *self);
+
G_END_DECLS
#endif /* _MM_MODEM_LOCATION_H_ */
diff --git a/libmm-glib/mm-modem-messaging.c b/libmm-glib/mm-modem-messaging.c
index c6191949..30e8c060 100644
--- a/libmm-glib/mm-modem-messaging.c
+++ b/libmm-glib/mm-modem-messaging.c
@@ -42,10 +42,10 @@
G_DEFINE_TYPE (MMModemMessaging, mm_modem_messaging, MM_GDBUS_TYPE_MODEM_MESSAGING_PROXY)
struct _MMModemMessagingPrivate {
- /* Supported Storage */
- GMutex supported_storages_mutex;
- guint supported_storages_id;
- GArray *supported_storages;
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_ARRAY_DECLARE (supported_storages)
};
/*****************************************************************************/
@@ -57,6 +57,8 @@ struct _MMModemMessagingPrivate {
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_messaging_get_path (MMModemMessaging *self)
@@ -71,9 +73,13 @@ mm_modem_messaging_get_path (MMModemMessaging *self)
* mm_modem_messaging_dup_path:
* @self: A #MMModemMessaging.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_messaging_dup_path (MMModemMessaging *self)
@@ -90,113 +96,39 @@ mm_modem_messaging_dup_path (MMModemMessaging *self)
/*****************************************************************************/
-static void
-supported_storages_updated (MMModemMessaging *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->supported_storages_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->supported_storages)
- g_array_unref (self->priv->supported_storages);
-
- dictionary = mm_gdbus_modem_messaging_get_supported_storages (MM_GDBUS_MODEM_MESSAGING (self));
- self->priv->supported_storages = (dictionary ?
- mm_common_sms_storages_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->supported_storages_mutex);
-}
-
-static void
-ensure_internal_supported_storages (MMModemMessaging *self,
- GArray **dup)
-{
- g_mutex_lock (&self->priv->supported_storages_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->supported_storages_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_messaging_dup_supported_storages (MM_GDBUS_MODEM_MESSAGING (self));
- if (dictionary) {
- self->priv->supported_storages = mm_common_sms_storages_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->supported_storages_id =
- g_signal_connect (self,
- "notify::supported-storages",
- G_CALLBACK (supported_storages_updated),
- NULL);
- }
-
- if (dup && self->priv->supported_storages)
- *dup = g_array_ref (self->priv->supported_storages);
- }
- g_mutex_unlock (&self->priv->supported_storages_mutex);
-}
-
/**
* mm_modem_messaging_get_supported_storages:
* @self: A #MMModem.
- * @storages: (out) (array length=n_storages): Return location for the array of #MMSmsStorage values. The returned array should be freed with g_free() when no longer needed.
+ * @storages: (out) (array length=n_storages): Return location for the array of
+ * #MMSmsStorage values. The returned array should be freed with g_free() when
+ * no longer needed.
* @n_storages: (out): Return location for the number of values in @storages.
*
* Gets the list of SMS storages supported by the #MMModem.
*
* Returns: %TRUE if @storages and @n_storages are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_messaging_get_supported_storages (MMModemMessaging *self,
- MMSmsStorage **storages,
- guint *n_storages)
-{
- GArray *array = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), FALSE);
- g_return_val_if_fail (storages != NULL, FALSE);
- g_return_val_if_fail (n_storages != NULL, FALSE);
-
- ensure_internal_supported_storages (self, &array);
- if (!array)
- return FALSE;
-
- *n_storages = array->len;
- *storages = (MMSmsStorage *)g_array_free (array, FALSE);
- return TRUE;
-}
/**
* mm_modem_messaging_peek_supported_storages:
* @self: A #MMModem.
- * @storages: (out): Return location for the array of #MMSmsStorage values. Do not free the returned array, it is owned by @self.
+ * @storages: (out): Return location for the array of #MMSmsStorage values. Do
+ * not free the returned array, it is owned by @self.
* @n_storages: (out): Return location for the number of values in @storages.
*
* Gets the list of SMS storages supported by the #MMModem.
*
* Returns: %TRUE if @storages and @n_storages are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_messaging_peek_supported_storages (MMModemMessaging *self,
- const MMSmsStorage **storages,
- guint *n_storages)
-{
- g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), FALSE);
- g_return_val_if_fail (storages != NULL, FALSE);
- g_return_val_if_fail (n_storages != NULL, FALSE);
-
- ensure_internal_supported_storages (self, NULL);
- if (!self->priv->supported_storages)
- return FALSE;
- *n_storages = self->priv->supported_storages->len;
- *storages = (MMSmsStorage *)self->priv->supported_storages->data;
- return TRUE;
-}
+PROPERTY_ARRAY_DEFINE (supported_storages,
+ ModemMessaging, modem_messaging, MODEM_MESSAGING,
+ MMSmsStorage,
+ mm_common_sms_storages_variant_to_garray)
/*****************************************************************************/
@@ -207,6 +139,8 @@ mm_modem_messaging_peek_supported_storages (MMModemMessaging *self,
* Gets the default SMS storage used when storing or receiving SMS messages.
*
* Returns: the default #MMSmsStorage.
+ *
+ * Since: 1.0
*/
MMSmsStorage
mm_modem_messaging_get_default_storage (MMModemMessaging *self)
@@ -219,9 +153,6 @@ mm_modem_messaging_get_default_storage (MMModemMessaging *self)
/*****************************************************************************/
typedef struct {
- MMModemMessaging *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
gchar **sms_paths;
GList *sms_objects;
guint i;
@@ -230,102 +161,101 @@ typedef struct {
static void
sms_object_list_free (GList *list)
{
- g_list_free_full (list, (GDestroyNotify) g_object_unref);
+ g_list_free_full (list, g_object_unref);
}
static void
-list_sms_context_complete_and_free (ListSmsContext *ctx)
+list_sms_context_free (ListSmsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
-
g_strfreev (ctx->sms_paths);
sms_object_list_free (ctx->sms_objects);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_ref (ctx->self);
g_slice_free (ListSmsContext, ctx);
}
/**
* mm_modem_messaging_list_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_messaging_list().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_messaging_list().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_messaging_list().
*
- * Returns: (element-type ModemManager.Sms) (transfer full): A list of #MMSms objects, or #NULL if either not found or @error is set. The returned value should be freed with g_list_free_full() using g_object_unref() as #GDestroyNotify function.
+ * Returns: (element-type ModemManager.Sms) (transfer full): A list of #MMSms
+ * objects, or #NULL if either not found or @error is set. The returned value
+ * should be freed with g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify function.
+ *
+ * Since: 1.0
*/
GList *
mm_modem_messaging_list_finish (MMModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- GList *list;
-
g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), FALSE);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- list = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
- /* The list we got, including the objects within, is owned by the async result;
- * so we'll make sure we return a new list */
- g_list_foreach (list, (GFunc)g_object_ref, NULL);
- return g_list_copy (list);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void create_next_sms (ListSmsContext *ctx);
+static void create_next_sms (GTask *task);
static void
list_build_object_ready (GDBusConnection *connection,
GAsyncResult *res,
- ListSmsContext *ctx)
+ GTask *task)
{
GError *error = NULL;
GObject *sms;
GObject *source_object;
+ ListSmsContext *ctx;
source_object = g_async_result_get_source_object (res);
sms = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
g_object_unref (source_object);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- list_sms_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Keep the object */
ctx->sms_objects = g_list_prepend (ctx->sms_objects, sms);
/* If no more smss, just end here. */
if (!ctx->sms_paths[++ctx->i]) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- ctx->sms_objects,
- (GDestroyNotify)sms_object_list_free);
- ctx->sms_objects = NULL;
- list_sms_context_complete_and_free (ctx);
+ GList *sms_objects;
+
+ sms_objects = g_list_copy_deep (ctx->sms_objects, (GCopyFunc)g_object_ref, NULL);
+ g_task_return_pointer (task, sms_objects, (GDestroyNotify)sms_object_list_free);
+ g_object_unref (task);
return;
}
/* Keep on creating next object */
- create_next_sms (ctx);
+ create_next_sms (task);
}
static void
-create_next_sms (ListSmsContext *ctx)
+create_next_sms (GTask *task)
{
+ MMModemMessaging *self;
+ ListSmsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
g_async_initable_new_async (MM_TYPE_SMS,
G_PRIORITY_DEFAULT,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)list_build_object_ready,
- ctx,
+ task,
"g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"g-name", MM_DBUS_SERVICE,
- "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (ctx->self)),
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
"g-object-path", ctx->sms_paths[ctx->i],
"g-interface-name", "org.freedesktop.ModemManager1.Sms",
NULL);
@@ -335,15 +265,21 @@ create_next_sms (ListSmsContext *ctx)
* mm_modem_messaging_list:
* @self: A #MMModemMessaging.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously lists the #MMSms objects in the modem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_messaging_list_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_messaging_list_finish() to get the result of the operation.
*
- * See mm_modem_messaging_list_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_messaging_list_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_messaging_list (MMModemMessaging *self,
@@ -352,30 +288,26 @@ mm_modem_messaging_list (MMModemMessaging *self,
gpointer user_data)
{
ListSmsContext *ctx;
+ GTask *task;
g_return_if_fail (MM_IS_MODEM_MESSAGING (self));
ctx = g_slice_new0 (ListSmsContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_modem_messaging_list);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
-
ctx->sms_paths = mm_gdbus_modem_messaging_dup_messages (MM_GDBUS_MODEM_MESSAGING (self));
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)list_sms_context_free);
+
/* If no SMS, just end here. */
if (!ctx->sms_paths || !ctx->sms_paths[0]) {
- g_simple_async_result_set_op_res_gpointer (ctx->result, NULL, NULL);
- list_sms_context_complete_and_free (ctx);
+ g_task_return_pointer (task, NULL, NULL);
+ g_object_unref (task);
return;
}
/* Got list of paths. If at least one found, start creating objects for each */
ctx->i = 0;
- create_next_sms (ctx);
+ create_next_sms (task);
}
/**
@@ -386,10 +318,15 @@ mm_modem_messaging_list (MMModemMessaging *self,
*
* Synchronously lists the #MMSms objects in the modem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_messaging_list()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_messaging_list() for the asynchronous version of this method.
+ *
+ * Returns: (element-type ModemManager.Sms) (transfer full): A list of #MMSms
+ * objects, or #NULL if either not found or @error is set. The returned value
+ * should be freed with g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify function.
*
- * Returns: (element-type MMSms) (transfer full): A list of #MMSms objects, or #NULL if either not found or @error is set. The returned value should be freed with g_list_free_full() using g_object_unref() as #GDestroyNotify function.
+ * Since: 1.0
*/
GList *
mm_modem_messaging_list_sync (MMModemMessaging *self,
@@ -436,30 +373,19 @@ mm_modem_messaging_list_sync (MMModemMessaging *self,
/*****************************************************************************/
-typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
-} CreateSmsContext;
-
-static void
-create_sms_context_complete_and_free (CreateSmsContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_slice_free (CreateSmsContext, ctx);
-}
-
/**
* mm_modem_messaging_create_finish:
* @self: A #MMModemMessaging.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_messaging_create().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_messaging_create().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_messaging_create().
*
- * Returns: (transfer full): A newly created #MMSms, or %NULL if @error is set. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): A newly created #MMSms, or %NULL if @error is set.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMSms *
mm_modem_messaging_create_finish (MMModemMessaging *self,
@@ -468,16 +394,13 @@ mm_modem_messaging_create_finish (MMModemMessaging *self,
{
g_return_val_if_fail (MM_IS_MODEM_MESSAGING (self), NULL);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
new_sms_object_ready (GDBusConnection *connection,
GAsyncResult *res,
- CreateSmsContext *ctx)
+ GTask *task)
{
GError *error = NULL;
GObject *sms;
@@ -488,19 +411,17 @@ new_sms_object_ready (GDBusConnection *connection,
g_object_unref (source_object);
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- sms,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, sms, g_object_unref);
- create_sms_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
create_sms_ready (MMModemMessaging *self,
GAsyncResult *res,
- CreateSmsContext *ctx)
+ GTask *task)
{
GError *error = NULL;
gchar *sms_path = NULL;
@@ -509,17 +430,17 @@ create_sms_ready (MMModemMessaging *self,
&sms_path,
res,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
- create_sms_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_free (sms_path);
return;
}
g_async_initable_new_async (MM_TYPE_SMS,
G_PRIORITY_DEFAULT,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)new_sms_object_ready,
- ctx,
+ task,
"g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"g-name", MM_DBUS_SERVICE,
"g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
@@ -530,19 +451,25 @@ create_sms_ready (MMModemMessaging *self,
}
/**
- * mm_modem_messaging_create_sms:
+ * mm_modem_messaging_create:
* @self: A #MMModemMessaging.
* @properties: A ##MMSmsProperties object with the properties to use.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously creates a new #MMSms in the modem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_messaging_create_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_messaging_create_finish() to get the result of the operation.
+ *
+ * See mm_modem_messaging_create_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_messaging_create_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_messaging_create (MMModemMessaging *self,
@@ -551,26 +478,19 @@ mm_modem_messaging_create (MMModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- CreateSmsContext *ctx;
+ GTask *task;
GVariant *dictionary;
g_return_if_fail (MM_IS_MODEM_MESSAGING (self));
- ctx = g_slice_new0 (CreateSmsContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_modem_messaging_create);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
-
+ task = g_task_new (self, cancellable, callback, user_data);
dictionary = (mm_sms_properties_get_dictionary (properties));
mm_gdbus_modem_messaging_call_create (
MM_GDBUS_MODEM_MESSAGING (self),
dictionary,
cancellable,
(GAsyncReadyCallback)create_sms_ready,
- ctx);
+ task);
g_variant_unref (dictionary);
}
@@ -584,10 +504,13 @@ mm_modem_messaging_create (MMModemMessaging *self,
*
* Synchronously creates a new #MMSms in the modem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_messaging_create()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_messaging_create() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A newly created #MMSms, or %NULL if @error is set.
+ * The returned value should be freed with g_object_unref().
*
- * Returns: (transfer full): A newly created #MMSms, or %NULL if @error is set. The returned value should be freed with g_object_unref().
+ * Since: 1.0
*/
MMSms *
mm_modem_messaging_create_sync (MMModemMessaging *self,
@@ -630,12 +553,15 @@ mm_modem_messaging_create_sync (MMModemMessaging *self,
/**
* mm_modem_messaging_delete_finish:
* @self: A #MMModemMessaging.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_messaging_delete().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_messaging_delete().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_messaging_delete().
*
* Returns: %TRUE if the sms was deleted, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_messaging_delete_finish (MMModemMessaging *self,
@@ -652,15 +578,21 @@ mm_modem_messaging_delete_finish (MMModemMessaging *self,
* @self: A #MMModemMessaging.
* @sms: Path of the #MMSms to delete.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously deletes a given #MMSms from the modem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_messaging_delete_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_messaging_delete_finish() to get the result of the operation.
+ *
+ * See mm_modem_messaging_delete_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_messaging_delete_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_messaging_delete (MMModemMessaging *self,
@@ -687,10 +619,12 @@ mm_modem_messaging_delete (MMModemMessaging *self,
* Synchronously deletes a given #MMSms from the modem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_messaging_delete()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_messaging_delete() for the asynchronous version of this method.
*
* Returns: %TRUE if the SMS was deleted, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_messaging_delete_sync (MMModemMessaging *self,
@@ -711,11 +645,10 @@ mm_modem_messaging_delete_sync (MMModemMessaging *self,
static void
mm_modem_messaging_init (MMModemMessaging *self)
{
- /* Setup private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_MODEM_MESSAGING,
- MMModemMessagingPrivate);
- g_mutex_init (&self->priv->supported_storages_mutex);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_MESSAGING, MMModemMessagingPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (supported_storages, "supported-storages")
}
static void
@@ -723,10 +656,9 @@ finalize (GObject *object)
{
MMModemMessaging *self = MM_MODEM_MESSAGING (object);
- g_mutex_clear (&self->priv->supported_storages_mutex);
+ g_mutex_clear (&self->priv->mutex);
- if (self->priv->supported_storages)
- g_array_unref (self->priv->supported_storages);
+ PROPERTY_ARRAY_FINALIZE (supported_storages)
G_OBJECT_CLASS (mm_modem_messaging_parent_class)->finalize (object);
}
@@ -738,6 +670,5 @@ mm_modem_messaging_class_init (MMModemMessagingClass *modem_class)
g_type_class_add_private (object_class, sizeof (MMModemMessagingPrivate));
- /* Virtual methods */
object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem-messaging.h b/libmm-glib/mm-modem-messaging.h
index 0162a857..d80193e9 100644
--- a/libmm-glib/mm-modem-messaging.h
+++ b/libmm-glib/mm-modem-messaging.h
@@ -65,6 +65,7 @@ struct _MMModemMessagingClass {
};
GType mm_modem_messaging_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemMessaging, g_object_unref)
const gchar *mm_modem_messaging_get_path (MMModemMessaging *self);
gchar *mm_modem_messaging_dup_path (MMModemMessaging *self);
diff --git a/libmm-glib/mm-modem-oma.c b/libmm-glib/mm-modem-oma.c
index 380bb08c..bd109628 100644
--- a/libmm-glib/mm-modem-oma.c
+++ b/libmm-glib/mm-modem-oma.c
@@ -36,16 +36,17 @@
* The #MMModemOma is an object providing access to the methods, signals and
* properties of the OMA interface.
*
- * The OMA interface is exposed whenever a modem has OMA device management capabilities.
+ * The OMA interface is exposed whenever a modem has OMA device management
+ * capabilities.
*/
G_DEFINE_TYPE (MMModemOma, mm_modem_oma, MM_GDBUS_TYPE_MODEM_OMA_PROXY)
struct _MMModemOmaPrivate {
- /* Supported Modes */
- GMutex pending_network_initiated_sessions_mutex;
- guint pending_network_initiated_sessions_id;
- GArray *pending_network_initiated_sessions;
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_ARRAY_DECLARE (pending_network_initiated_sessions)
};
/*****************************************************************************/
@@ -57,6 +58,8 @@ struct _MMModemOmaPrivate {
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.2
*/
const gchar *
mm_modem_oma_get_path (MMModemOma *self)
@@ -71,9 +74,13 @@ mm_modem_oma_get_path (MMModemOma *self)
* mm_modem_oma_dup_path:
* @self: A #MMModemOma.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.2
*/
gchar *
mm_modem_oma_dup_path (MMModemOma *self)
@@ -93,12 +100,15 @@ mm_modem_oma_dup_path (MMModemOma *self)
/**
* mm_modem_oma_setup_finish:
* @self: A #MMModemOma.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_oma_setup().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_oma_setup().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_oma_setup().
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_setup_finish (MMModemOma *self,
@@ -113,17 +123,23 @@ mm_modem_oma_setup_finish (MMModemOma *self,
/**
* mm_modem_oma_setup:
* @self: A #MMModemOma.
- * @features: Mask of #MMOmaFeatures to enable.
+ * @features: Mask of #MMOmaFeature values to enable.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously sets up the OMA device management service.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_oma_setup_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_oma_setup_finish() to get the result of the operation.
+ *
+ * See mm_modem_oma_setup_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_modem_oma_setup_sync() for the synchronous, blocking version of this method.
+ * Since: 1.2
*/
void
mm_modem_oma_setup (MMModemOma *self,
@@ -140,16 +156,18 @@ mm_modem_oma_setup (MMModemOma *self,
/**
* mm_modem_oma_setup_sync:
* @self: A #MMModemOma.
- * @features: Mask of #MMOmaFeatures to enable.
+ * @features: Mask of #MMOmaFeature values to enable.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously sets up the OMA device management service.
*
- * The calling thread is blocked until a reply is received. See mm_modem_oma_setup()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_oma_setup() for the asynchronous version of this method.
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_setup_sync (MMModemOma *self,
@@ -167,12 +185,16 @@ mm_modem_oma_setup_sync (MMModemOma *self,
/**
* mm_modem_oma_start_client_initiated_session_finish:
* @self: A #MMModemOma.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_oma_start_client_initiated_session().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_oma_start_client_initiated_session().
* @error: Return location for error or %NULL.
*
- * Finishes an operation started with mm_modem_oma_start_client_initiated_session().
+ * Finishes an operation started with
+ * mm_modem_oma_start_client_initiated_session().
*
* Returns: %TRUE if the session was started, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_start_client_initiated_session_finish (MMModemOma *self,
@@ -189,15 +211,22 @@ mm_modem_oma_start_client_initiated_session_finish (MMModemOma *self,
* @self: A #MMModemOma.
* @session_type: A #MMOmaSessionType.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously starts a client-initiated OMA device management session.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_oma_start_client_initiated_session_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_oma_start_client_initiated_session_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_oma_start_client_initiated_session_sync() for the synchronous,
+ * blocking version of this method.
*
- * See mm_modem_oma_start_client_initiated_session_sync() for the synchronous, blocking version of this method.
+ * Since: 1.2
*/
void
mm_modem_oma_start_client_initiated_session (MMModemOma *self,
@@ -220,10 +249,13 @@ mm_modem_oma_start_client_initiated_session (MMModemOma *self,
*
* Synchronously starts a client-initiated OMA device management session.
*
- * The calling thread is blocked until a reply is received. See mm_modem_oma_start_client_initiated_session()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_oma_start_client_initiated_session() for the asynchronous version
+ * of this method.
*
* Returns: %TRUE if the session was started, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_start_client_initiated_session_sync (MMModemOma *self,
@@ -241,12 +273,16 @@ mm_modem_oma_start_client_initiated_session_sync (MMModemOma *self,
/**
* mm_modem_oma_accept_network_initiated_session_finish:
* @self: A #MMModemOma.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_oma_accept_network_initiated_session().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_oma_accept_network_initiated_session().
* @error: Return location for error or %NULL.
*
- * Finishes an operation started with mm_modem_oma_accept_network_initiated_session().
+ * Finishes an operation started with
+ * mm_modem_oma_accept_network_initiated_session().
*
* Returns: %TRUE if the session was started, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_accept_network_initiated_session_finish (MMModemOma *self,
@@ -264,15 +300,22 @@ mm_modem_oma_accept_network_initiated_session_finish (MMModemOma *self,
* @session_id: The unique ID of the network-initiated session.
* @accept: %TRUE if the session is to be accepted, %FALSE otherwise.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously accepts a nework-initiated OMA device management session.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_oma_accept_network_initiated_session_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_oma_accept_network_initiated_session_finish() to get the result of
+ * the operation.
*
- * See mm_modem_oma_accept_network_initiated_session_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_oma_accept_network_initiated_session_sync() for the synchronous,
+ * blocking version of this method.
+ *
+ * Since: 1.2
*/
void
mm_modem_oma_accept_network_initiated_session (MMModemOma *self,
@@ -297,10 +340,13 @@ mm_modem_oma_accept_network_initiated_session (MMModemOma *self,
*
* Synchronously accepts a nework-initiated OMA device management session.
*
- * The calling thread is blocked until a reply is received. See mm_modem_oma_accept_network_initiated_session()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_oma_accept_network_initiated_session() for the asynchronous version
+ * of this method.
*
* Returns: %TRUE if the session was started, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_accept_network_initiated_session_sync (MMModemOma *self,
@@ -319,12 +365,15 @@ mm_modem_oma_accept_network_initiated_session_sync (MMModemOma *self,
/**
* mm_modem_oma_cancel_session_finish:
* @self: A #MMModemOma.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_oma_cancel_session().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_oma_cancel_session().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_oma_cancel_session().
*
* Returns: %TRUE if the session was started, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_cancel_session_finish (MMModemOma *self,
@@ -340,15 +389,21 @@ mm_modem_oma_cancel_session_finish (MMModemOma *self,
* mm_modem_oma_cancel_session:
* @self: A #MMModemOma.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously cancels the current OMA device management session.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_oma_cancel_session_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_oma_cancel_session_finish() to get the result of the operation.
*
- * See mm_modem_oma_cancel_session_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_oma_cancel_session_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.2
*/
void
mm_modem_oma_cancel_session (MMModemOma *self,
@@ -369,10 +424,12 @@ mm_modem_oma_cancel_session (MMModemOma *self,
*
* Synchronously cancels the current OMA device management session.
*
- * The calling thread is blocked until a reply is received. See mm_modem_oma_cancel_session()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_oma_cancel_session() for the asynchronous version of this method.
*
* Returns: %TRUE if the session was started, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_oma_cancel_session_sync (MMModemOma *self,
@@ -393,11 +450,13 @@ mm_modem_oma_cancel_session_sync (MMModemOma *self,
* Gets the currently enabled OMA features.
*
* Returns: a bitmask of #MMOmaFeature values.
+ *
+ * Since: 1.2
*/
MMOmaFeature
mm_modem_oma_get_features (MMModemOma *self)
{
- g_return_val_if_fail (MM_IS_MODEM_OMA (self), MM_OMA_SESSION_TYPE_UNKNOWN);
+ g_return_val_if_fail (MM_IS_MODEM_OMA (self), MM_OMA_FEATURE_NONE);
return (MMOmaFeature) mm_gdbus_modem_oma_get_features (MM_GDBUS_MODEM_OMA (self));
}
@@ -411,6 +470,8 @@ mm_modem_oma_get_features (MMModemOma *self)
* Gets the type of the current OMA device management session.
*
* Returns: a #MMOmaSessionType.
+ *
+ * Since: 1.2
*/
MMOmaSessionType
mm_modem_oma_get_session_type (MMModemOma *self)
@@ -429,6 +490,8 @@ mm_modem_oma_get_session_type (MMModemOma *self)
* Gets the state of the current OMA device management session.
*
* Returns: a #MMOmaSessionState.
+ *
+ * Since: 1.2
*/
MMOmaSessionState
mm_modem_oma_get_session_state (MMModemOma *self)
@@ -440,132 +503,51 @@ mm_modem_oma_get_session_state (MMModemOma *self)
/*****************************************************************************/
-static void
-pending_network_initiated_sessions_updated (MMModemOma *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->pending_network_initiated_sessions_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->pending_network_initiated_sessions)
- g_array_unref (self->priv->pending_network_initiated_sessions);
-
- dictionary = mm_gdbus_modem_oma_get_pending_network_initiated_sessions (MM_GDBUS_MODEM_OMA (self));
- self->priv->pending_network_initiated_sessions = (dictionary ?
- mm_common_oma_pending_network_initiated_sessions_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->pending_network_initiated_sessions_mutex);
-}
-
-static gboolean
-ensure_internal_pending_network_initiated_sessions (MMModemOma *self,
- MMOmaPendingNetworkInitiatedSession **dup_sessions,
- guint *dup_sessions_n)
-{
- gboolean ret;
-
- g_mutex_lock (&self->priv->pending_network_initiated_sessions_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->pending_network_initiated_sessions_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_oma_dup_pending_network_initiated_sessions (MM_GDBUS_MODEM_OMA (self));
- if (dictionary) {
- self->priv->pending_network_initiated_sessions = mm_common_oma_pending_network_initiated_sessions_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->pending_network_initiated_sessions_id =
- g_signal_connect (self,
- "notify::pending-network-initiated-sessions",
- G_CALLBACK (pending_network_initiated_sessions_updated),
- NULL);
- }
-
- if (!self->priv->pending_network_initiated_sessions)
- ret = FALSE;
- else {
- ret = TRUE;
-
- if (dup_sessions && dup_sessions_n) {
- *dup_sessions_n = self->priv->pending_network_initiated_sessions->len;
- if (self->priv->pending_network_initiated_sessions->len > 0) {
- *dup_sessions = g_malloc (sizeof (MMOmaPendingNetworkInitiatedSession) * self->priv->pending_network_initiated_sessions->len);
- memcpy (*dup_sessions, self->priv->pending_network_initiated_sessions->data, sizeof (MMOmaPendingNetworkInitiatedSession) * self->priv->pending_network_initiated_sessions->len);
- } else
- *dup_sessions = NULL;
- }
- }
- }
- g_mutex_unlock (&self->priv->pending_network_initiated_sessions_mutex);
-
- return ret;
-}
-
/**
- * mm_modem_get_pending_network_initiated_sessions:
+ * mm_modem_oma_get_pending_network_initiated_sessions:
* @self: A #MMModem.
- * @sessions: (out) (array length=n_sessions): Return location for the array of #MMOmaPendingNetworkInitiatedSession structs. The returned array should be freed with g_free() when no longer needed.
+ * @sessions: (out) (array length=n_sessions): Return location for the array of
+ * #MMOmaPendingNetworkInitiatedSession structs. The returned array should be
+ * freed with g_free() when no longer needed.
* @n_sessions: (out): Return location for the number of values in @sessions.
*
* Gets the list of pending network-initiated OMA sessions.
*
* Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise.
+ *
+ * Since: 1.18
*/
-gboolean
-mm_modem_get_pending_network_initiated_sessions (MMModemOma *self,
- MMOmaPendingNetworkInitiatedSession **sessions,
- guint *n_sessions)
-{
- g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE);
- g_return_val_if_fail (sessions != NULL, FALSE);
- g_return_val_if_fail (n_sessions != NULL, FALSE);
- return ensure_internal_pending_network_initiated_sessions (self, sessions, n_sessions);
-}
/**
- * mm_modem_peek_pending_network_initiated_sessions:
+ * mm_modem_oma_peek_pending_network_initiated_sessions:
* @self: A #MMModem.
- * @sessions: (out) (array length=n_sessions): Return location for the array of #MMOmaPendingNetworkInitiatedSession values. Do not free the returned array, it is owned by @self.
+ * @sessions: (out) (array length=n_sessions): Return location for the array of
+ * #MMOmaPendingNetworkInitiatedSession values. Do not free the returned array,
+ * it is owned by @self.
* @n_sessions: (out): Return location for the number of values in @sessions.
*
* Gets the list of pending network-initiated OMA sessions.
*
* Returns: %TRUE if @sessions and @n_sessions are set, %FALSE otherwise.
+ *
+ * Since: 1.18
*/
-gboolean
-mm_modem_peek_pending_network_initiated_sessions (MMModemOma *self,
- const MMOmaPendingNetworkInitiatedSession **sessions,
- guint *n_sessions)
-{
- g_return_val_if_fail (MM_IS_MODEM_OMA (self), FALSE);
- g_return_val_if_fail (sessions != NULL, FALSE);
- g_return_val_if_fail (n_sessions != NULL, FALSE);
-
- if (!ensure_internal_pending_network_initiated_sessions (self, NULL, NULL))
- return FALSE;
- *n_sessions = self->priv->pending_network_initiated_sessions->len;
- *sessions = (MMOmaPendingNetworkInitiatedSession *)self->priv->pending_network_initiated_sessions->data;
- return TRUE;
-}
+PROPERTY_ARRAY_DEFINE (pending_network_initiated_sessions,
+ ModemOma, modem_oma, MODEM_OMA,
+ MMOmaPendingNetworkInitiatedSession,
+ mm_common_oma_pending_network_initiated_sessions_variant_to_garray)
/*****************************************************************************/
static void
mm_modem_oma_init (MMModemOma *self)
{
- /* Setup private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_MODEM_OMA,
- MMModemOmaPrivate);
- g_mutex_init (&self->priv->pending_network_initiated_sessions_mutex);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_OMA, MMModemOmaPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (pending_network_initiated_sessions, "pending-network-initiated-sessions")
}
static void
@@ -573,10 +555,9 @@ finalize (GObject *object)
{
MMModemOma *self = MM_MODEM_OMA (object);
- g_mutex_clear (&self->priv->pending_network_initiated_sessions_mutex);
+ g_mutex_clear (&self->priv->mutex);
- if (self->priv->pending_network_initiated_sessions)
- g_array_unref (self->priv->pending_network_initiated_sessions);
+ PROPERTY_ARRAY_FINALIZE (pending_network_initiated_sessions)
G_OBJECT_CLASS (mm_modem_oma_parent_class)->finalize (object);
}
@@ -588,6 +569,5 @@ mm_modem_oma_class_init (MMModemOmaClass *modem_class)
g_type_class_add_private (object_class, sizeof (MMModemOmaPrivate));
- /* Virtual methods */
object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem-oma.h b/libmm-glib/mm-modem-oma.h
index e6dc619e..d935bdfa 100644
--- a/libmm-glib/mm-modem-oma.h
+++ b/libmm-glib/mm-modem-oma.h
@@ -63,6 +63,7 @@ struct _MMModemOmaClass {
};
GType mm_modem_oma_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemOma, g_object_unref)
const gchar *mm_modem_oma_get_path (MMModemOma *self);
gchar *mm_modem_oma_dup_path (MMModemOma *self);
@@ -123,12 +124,12 @@ MMOmaFeature mm_modem_oma_get_features (MMModemOma *self);
MMOmaSessionType mm_modem_oma_get_session_type (MMModemOma *self);
MMOmaSessionState mm_modem_oma_get_session_state (MMModemOma *self);
-gboolean mm_modem_peek_pending_network_initiated_sessions (MMModemOma *self,
- const MMOmaPendingNetworkInitiatedSession **sessions,
- guint *n_sessions);
-gboolean mm_modem_get_pending_network_initiated_sessions (MMModemOma *self,
- MMOmaPendingNetworkInitiatedSession **sessions,
- guint *n_sessions);
+gboolean mm_modem_oma_peek_pending_network_initiated_sessions (MMModemOma *self,
+ const MMOmaPendingNetworkInitiatedSession **sessions,
+ guint *n_sessions);
+gboolean mm_modem_oma_get_pending_network_initiated_sessions (MMModemOma *self,
+ MMOmaPendingNetworkInitiatedSession **sessions,
+ guint *n_sessions);
G_END_DECLS
diff --git a/libmm-glib/mm-modem-sar.c b/libmm-glib/mm-modem-sar.c
new file mode 100644
index 00000000..c93594e2
--- /dev/null
+++ b/libmm-glib/mm-modem-sar.c
@@ -0,0 +1,306 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2021 Fibocom Wireless Inc.
+ */
+
+#include <gio/gio.h>
+
+#include "mm-helpers.h"
+#include "mm-errors-types.h"
+#include "mm-modem-sar.h"
+#include "mm-common-helpers.h"
+
+/**
+ * SECTION: mm-modem-sar
+ * @title: MMModemSar
+ * @short_description: The SAR interface
+ *
+ * The #MMModemSar is an object providing access to the methods, signals and
+ * properties of the SAR interface.
+ *
+ * The SAR interface is exposed whenever a modem has SAR capabilities.
+ */
+
+G_DEFINE_TYPE (MMModemSar, mm_modem_sar, MM_GDBUS_TYPE_MODEM_SAR_PROXY)
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_sar_get_path:
+ * @self: A #MMModemSar.
+ *
+ * Gets the DBus path of the #MMObject which implements this interface.
+ *
+ * Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.20
+ */
+const gchar *
+mm_modem_sar_get_path (MMModemSar *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)));
+}
+
+/**
+ * mm_modem_sar_dup_path:
+ * @self: A #MMModemSar.
+ *
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.20
+ */
+gchar *
+mm_modem_sar_dup_path (MMModemSar *self)
+{
+ gchar *value;
+
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), NULL);
+
+ g_object_get (G_OBJECT (self),
+ "g-object-path", &value,
+ NULL);
+ RETURN_NON_EMPTY_STRING (value);
+}
+/*****************************************************************************/
+
+/**
+ * mm_modem_sar_enable_finish:
+ * @self: A #MMModemSar.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_sar_enable().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_sar_enable().
+ *
+ * Returns: %TRUE if the enable was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_sar_enable_finish (MMModemSar *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE);
+
+ return mm_gdbus_modem_sar_call_enable_finish (MM_GDBUS_MODEM_SAR (self), res, error);
+}
+
+/**
+ * mm_modem_sar_enable:
+ * @self: A #MMModemSar.
+ * @enable: %TRUE to enable dynamic SAR and %FALSE to disable it.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously enable or disable dynamic SAR.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_sar_enable_finish() to get the result of the operation.
+ *
+ * See mm_modem_sar_enable_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.20
+ */
+void
+mm_modem_sar_enable (MMModemSar *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_SAR (self));
+
+ mm_gdbus_modem_sar_call_enable (MM_GDBUS_MODEM_SAR (self), enable, cancellable, callback, user_data);
+}
+
+/**
+ * mm_modem_sar_enable_sync:
+ * @self: A #MMModemSar.
+ * @enable: %TRUE to enable dynamic SAR and %FALSE to disable it.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously enable or disable dynamic SAR.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_sar_enable() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the enable was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_sar_enable_sync (MMModemSar *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE);
+
+ return mm_gdbus_modem_sar_call_enable_sync (MM_GDBUS_MODEM_SAR (self), enable, cancellable, error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_sar_power_level_finish:
+ * @self: A #MMModemSar.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_sar_enable().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_sar_set_power_level().
+ *
+ * Returns: %TRUE if set power level was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_sar_set_power_level_finish (MMModemSar *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE);
+
+ return mm_gdbus_modem_sar_call_set_power_level_finish (MM_GDBUS_MODEM_SAR (self), res, error);
+}
+
+/**
+ * mm_modem_sar_set_power_level:
+ * @self: A #MMModemSar.
+ * @level: Index of the SAR power level mapping table
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously set current dynamic SAR power level.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_sar_set_power_level_finish() to get the result of the operation.
+ *
+ * See mm_modem_sar_set_power_level_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.20
+ */
+void
+mm_modem_sar_set_power_level (MMModemSar *self,
+ guint level,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_SAR (self));
+
+ mm_gdbus_modem_sar_call_set_power_level (MM_GDBUS_MODEM_SAR (self), level, cancellable, callback, user_data);
+}
+
+/**
+ * mm_modem_sar_set_power_level_sync:
+ * @self: A #MMModemSar.
+ * @level: Index of the SAR power level mapping table
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously set current dynamic SAR power level.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_sar_set_power_level() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if set power level was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_sar_set_power_level_sync (MMModemSar *self,
+ guint level,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE);
+
+ return mm_gdbus_modem_sar_call_set_power_level_sync (MM_GDBUS_MODEM_SAR (self), level, cancellable, error);
+}
+
+/*****************************************************************************/
+/**
+ * mm_modem_sar_get_state:
+ * @self: A #MMModem.
+ *
+ * Gets the state of dynamic SAR.
+ *
+ * Returns: %TRUE if dynamic SAR is enabled, %FALSE otherwise.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_sar_get_state (MMModemSar *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE);
+
+ return mm_gdbus_modem_sar_get_state (MM_GDBUS_MODEM_SAR (self));
+}
+
+/*****************************************************************************/
+/**
+ * mm_modem_sar_get_power_level:
+ * @self: A #MMModem.
+ *
+ * Gets the index of the SAR power level mapping table.
+ *
+ * Returns: the index.
+ *
+ * Since: 1.20
+ */
+guint
+mm_modem_sar_get_power_level (MMModemSar *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_SAR (self), FALSE);
+
+ return mm_gdbus_modem_sar_get_power_level (MM_GDBUS_MODEM_SAR (self));
+}
+
+/*****************************************************************************/
+static void
+mm_modem_sar_init (MMModemSar *self)
+{
+}
+
+static void
+mm_modem_sar_class_init (MMModemSarClass *modem_class)
+{
+ /* Virtual methods */
+}
diff --git a/libmm-glib/mm-modem-sar.h b/libmm-glib/mm-modem-sar.h
new file mode 100644
index 00000000..d5784c99
--- /dev/null
+++ b/libmm-glib/mm-modem-sar.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2021 Fibocom Wireless Inc.
+ */
+
+#ifndef _MM_MODEM_SAR_H_
+#define _MM_MODEM_SAR_H_
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+
+#include "mm-gdbus-modem.h"
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_MODEM_SAR (mm_modem_sar_get_type ())
+#define MM_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_SAR, MMModemSar))
+#define MM_MODEM_SAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_SAR, MMModemSarClass))
+#define MM_IS_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_SAR))
+#define MM_IS_MODEM_SAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_SAR))
+#define MM_MODEM_SAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_SAR, MMModemSarClass))
+
+typedef struct _MMModemSar MMModemSar;
+typedef struct _MMModemSarClass MMModemSarClass;
+
+/**
+ * MMModemSar:
+ *
+ * The #MMModemSar structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MMModemSar {
+ /*< private >*/
+ MmGdbusModemSarProxy parent;
+ gpointer unused;
+};
+
+struct _MMModemSarClass {
+ /*< private >*/
+ MmGdbusModemSarProxyClass parent;
+};
+
+GType mm_modem_sar_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemSar, g_object_unref)
+
+const gchar *mm_modem_sar_get_path (MMModemSar *self);
+gchar *mm_modem_sar_dup_path (MMModemSar *self);
+
+void mm_modem_sar_enable (MMModemSar *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_modem_sar_enable_finish (MMModemSar *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_modem_sar_enable_sync (MMModemSar *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_sar_set_power_level (MMModemSar *self,
+ guint level,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_sar_set_power_level_finish (MMModemSar *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_modem_sar_set_power_level_sync (MMModemSar *self,
+ guint level,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean mm_modem_sar_get_state (MMModemSar *self);
+guint mm_modem_sar_get_power_level (MMModemSar *self);
+
+
+G_END_DECLS
+
+#endif /* _MM_MODEM_SAR_H_ */
diff --git a/libmm-glib/mm-modem-signal.c b/libmm-glib/mm-modem-signal.c
index 620dd2a9..c94a3eb6 100644
--- a/libmm-glib/mm-modem-signal.c
+++ b/libmm-glib/mm-modem-signal.c
@@ -19,6 +19,8 @@
*
* Copyright (C) 2012 Google, Inc.
* Copyright (C) 2012 Lanedo GmbH <aleksander@lanedo.com>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
*/
#include <gio/gio.h>
@@ -35,29 +37,22 @@
* The #MMModemSignal is an object providing access to the methods, signals and
* properties of the Signal interface.
*
- * The Signal interface is exposed whenever a modem has extended signal retrieval
- * capabilities.
+ * The Signal interface is exposed whenever a modem has extended signal
+ * retrieval capabilities.
*/
G_DEFINE_TYPE (MMModemSignal, mm_modem_signal, MM_GDBUS_TYPE_MODEM_SIGNAL_PROXY)
-typedef struct {
+struct _MMModemSignalPrivate {
+ /* Common mutex to sync access */
GMutex mutex;
- guint id;
- MMSignal *info;
-} UpdatedProperty;
-
-typedef enum {
- UPDATED_PROPERTY_TYPE_CDMA = 0,
- UPDATED_PROPERTY_TYPE_EVDO = 1,
- UPDATED_PROPERTY_TYPE_GSM = 2,
- UPDATED_PROPERTY_TYPE_UMTS = 3,
- UPDATED_PROPERTY_TYPE_LTE = 4,
- UPDATED_PROPERTY_TYPE_LAST
-} UpdatedPropertyType;
-struct _MMModemSignalPrivate {
- UpdatedProperty values [UPDATED_PROPERTY_TYPE_LAST];
+ PROPERTY_OBJECT_DECLARE (cdma, MMSignal)
+ PROPERTY_OBJECT_DECLARE (evdo, MMSignal)
+ PROPERTY_OBJECT_DECLARE (gsm, MMSignal)
+ PROPERTY_OBJECT_DECLARE (umts, MMSignal)
+ PROPERTY_OBJECT_DECLARE (lte, MMSignal)
+ PROPERTY_OBJECT_DECLARE (nr5g, MMSignal)
};
/*****************************************************************************/
@@ -69,6 +64,8 @@ struct _MMModemSignalPrivate {
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.2
*/
const gchar *
mm_modem_signal_get_path (MMModemSignal *self)
@@ -83,9 +80,13 @@ mm_modem_signal_get_path (MMModemSignal *self)
* mm_modem_signal_dup_path:
* @self: A #MMModemSignal.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.2
*/
gchar *
mm_modem_signal_dup_path (MMModemSignal *self)
@@ -105,17 +106,20 @@ mm_modem_signal_dup_path (MMModemSignal *self)
/**
* mm_modem_signal_setup_finish:
* @self: A #MMModemSignal.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_signal_setup().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_signal_setup().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_signal_setup().
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
mm_modem_signal_setup_finish (MMModemSignal *self,
- GAsyncResult *res,
- GError **error)
+ GAsyncResult *res,
+ GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE);
@@ -125,24 +129,31 @@ mm_modem_signal_setup_finish (MMModemSignal *self,
/**
* mm_modem_signal_setup:
* @self: A #MMModemSignal.
- * @rate: Rate to use when refreshing signal values.
+ * @rate: Refresh rate to set, in seconds. Use 0 to disable periodic polling.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously setups the extended signal quality retrieval.
+ * Asynchronously enables or disables the extended signal quality information
+ * retrieval via periodic polling.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_signal_setup_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_signal_setup_finish() to get the result of the operation.
+ * See mm_modem_signal_setup_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_signal_setup_sync() for the synchronous, blocking version of this method.
+ * Since: 1.2
*/
void
-mm_modem_signal_setup (MMModemSignal *self,
- guint rate,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_modem_signal_setup (MMModemSignal *self,
+ guint rate,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_SIGNAL (self));
@@ -152,22 +163,25 @@ mm_modem_signal_setup (MMModemSignal *self,
/**
* mm_modem_signal_setup_sync:
* @self: A #MMModemSignal.
- * @rate: Rate to use when refreshing signal values.
+ * @rate: Refresh rate to set, in seconds. Use 0 to disable periodic polling.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously setups the extended signal quality retrieval.
+ * Synchronously enables or disables the extended signal quality information
+ * retrieval via periodic polling.
*
- * The calling thread is blocked until a reply is received. See mm_modem_signal_setup()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_signal_setup() for the asynchronous version of this method.
*
* Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.2
*/
gboolean
-mm_modem_signal_setup_sync (MMModemSignal *self,
- guint rate,
- GCancellable *cancellable,
- GError **error)
+mm_modem_signal_setup_sync (MMModemSignal *self,
+ guint rate,
+ GCancellable *cancellable,
+ GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE);
@@ -177,138 +191,156 @@ mm_modem_signal_setup_sync (MMModemSignal *self,
/*****************************************************************************/
/**
- * mm_modem_signal_get_rate:
+ * mm_modem_signal_setup_thresholds_finish:
* @self: A #MMModemSignal.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_signal_setup_thresholds().
+ * @error: Return location for error or %NULL.
*
- * Gets the currently configured refresh rate.
+ * Finishes an operation started with mm_modem_signal_setup_thresholds().
*
- * Returns: the refresh rate, in seconds.
+ * Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
*/
-guint
-mm_modem_signal_get_rate (MMModemSignal *self)
+gboolean
+mm_modem_signal_setup_thresholds_finish (MMModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
{
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), 0);
+ g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE);
- return mm_gdbus_modem_signal_get_rate (MM_GDBUS_MODEM_SIGNAL (self));
+ return mm_gdbus_modem_signal_call_setup_thresholds_finish (MM_GDBUS_MODEM_SIGNAL (self), res, error);
}
-/*****************************************************************************/
+/**
+ * mm_modem_signal_setup_thresholds:
+ * @self: A #MMModemSignal.
+ * @properties: Threshold values to set.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously setups thresholds so that the device itself decides when to report the
+ * extended signal quality information updates.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_signal_setup_thresholds_finish() to get the result of the operation.
+ *
+ * See mm_modem_signal_setup_thresholds_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.20
+ */
+void
+mm_modem_signal_setup_thresholds (MMModemSignal *self,
+ MMSignalThresholdProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GVariant) dictionary = NULL;
-static void values_updated (MMModemSignal *self, GParamSpec *pspec, UpdatedPropertyType type);
+ g_return_if_fail (MM_IS_MODEM_SIGNAL (self));
-static void
-cdma_updated (MMModemSignal *self,
- GParamSpec *pspec)
-{
- values_updated (self, pspec, UPDATED_PROPERTY_TYPE_CDMA);
+ dictionary = mm_signal_threshold_properties_get_dictionary (properties);
+ mm_gdbus_modem_signal_call_setup_thresholds (MM_GDBUS_MODEM_SIGNAL (self), dictionary, cancellable, callback, user_data);
}
-static void
-evdo_updated (MMModemSignal *self,
- GParamSpec *pspec)
+/**
+ * mm_modem_signal_setup_thresholds_sync:
+ * @self: A #MMModemSignal.
+ * @properties: Threshold values to set.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously setups thresholds so that the device itself decides when to report the
+ * extended signal quality information updates.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_signal_setup_thresholds() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the setup was successful, %FALSE if @error is set.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_signal_setup_thresholds_sync (MMModemSignal *self,
+ MMSignalThresholdProperties *properties,
+ GCancellable *cancellable,
+ GError **error)
{
- values_updated (self, pspec, UPDATED_PROPERTY_TYPE_EVDO);
-}
+ g_autoptr(GVariant) dictionary = NULL;
-static void
-gsm_updated (MMModemSignal *self,
- GParamSpec *pspec)
-{
- values_updated (self, pspec, UPDATED_PROPERTY_TYPE_GSM);
-}
+ g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE);
-static void
-umts_updated (MMModemSignal *self,
- GParamSpec *pspec)
-{
- values_updated (self, pspec, UPDATED_PROPERTY_TYPE_UMTS);
+ dictionary = mm_signal_threshold_properties_get_dictionary (properties);
+ return mm_gdbus_modem_signal_call_setup_thresholds_sync (MM_GDBUS_MODEM_SIGNAL (self), dictionary, cancellable, error);
}
-static void
-lte_updated (MMModemSignal *self,
- GParamSpec *pspec)
+/*****************************************************************************/
+
+/**
+ * mm_modem_signal_get_rate:
+ * @self: A #MMModemSignal.
+ *
+ * Gets the currently configured refresh rate.
+ *
+ * Returns: the refresh rate, in seconds.
+ *
+ * Since: 1.2
+ */
+guint
+mm_modem_signal_get_rate (MMModemSignal *self)
{
- values_updated (self, pspec, UPDATED_PROPERTY_TYPE_LTE);
+ g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), 0);
+
+ return mm_gdbus_modem_signal_get_rate (MM_GDBUS_MODEM_SIGNAL (self));
}
-typedef GVariant * (* Getter) (MmGdbusModemSignal *self);
-typedef GVariant * (* Dupper) (MmGdbusModemSignal *self);
-typedef void (* UpdatedCallback) (MMModemSignal *self, GParamSpec *pspec);
-typedef struct {
- const gchar *signal_name;
- Getter get;
- Dupper dup;
- UpdatedCallback updated_callback;
-} SignalData;
-
-static const SignalData signal_data [UPDATED_PROPERTY_TYPE_LAST] = {
- { "notify::cdma", mm_gdbus_modem_signal_get_cdma, mm_gdbus_modem_signal_dup_cdma, cdma_updated },
- { "notify::evdo", mm_gdbus_modem_signal_get_evdo, mm_gdbus_modem_signal_dup_evdo, evdo_updated },
- { "notify::gsm", mm_gdbus_modem_signal_get_gsm, mm_gdbus_modem_signal_dup_gsm, gsm_updated },
- { "notify::umts", mm_gdbus_modem_signal_get_umts, mm_gdbus_modem_signal_dup_umts, umts_updated },
- { "notify::lte", mm_gdbus_modem_signal_get_lte, mm_gdbus_modem_signal_dup_lte, lte_updated }
-};
+/*****************************************************************************/
-static void
-values_updated (MMModemSignal *self,
- GParamSpec *pspec,
- UpdatedPropertyType type)
+/**
+ * mm_modem_signal_get_rssi_threshold:
+ * @self: A #MMModemSignal.
+ *
+ * Gets the currently configured RSSI threshold, in dBm.
+ *
+ * A value of 0 indicates the threshold is disabled.
+ *
+ * Returns: the RSSI threshold.
+ *
+ * Since: 1.20
+ */
+guint
+mm_modem_signal_get_rssi_threshold (MMModemSignal *self)
{
- g_mutex_lock (&self->priv->values[type].mutex);
- {
- GVariant *dictionary;
-
- g_clear_object (&self->priv->values[type].info);
- dictionary = signal_data[type].get (MM_GDBUS_MODEM_SIGNAL (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->values[type].info = mm_signal_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid signal info update received: %s", error->message);
- g_error_free (error);
- }
- }
- }
- g_mutex_unlock (&self->priv->values[type].mutex);
+ g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), 0);
+
+ return mm_gdbus_modem_signal_get_rssi_threshold (MM_GDBUS_MODEM_SIGNAL (self));
}
-static void
-ensure_internal (MMModemSignal *self,
- MMSignal **dup,
- UpdatedPropertyType type)
+/*****************************************************************************/
+
+/**
+ * mm_modem_signal_get_error_rate_threshold:
+ * @self: A #MMModemSignal.
+ *
+ * Gets whether the error rate threshold is enabled or not.
+ *
+ * Returns: %TRUE if the error rate threshold is enabled, %FALSE otherwise.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_modem_signal_get_error_rate_threshold (MMModemSignal *self)
{
- g_mutex_lock (&self->priv->values[type].mutex);
- {
- /* If this is the first time ever asking for the object, setup the
- * update listener and the initial object, if any. */
- if (!self->priv->values[type].id) {
- GVariant *dictionary;
-
- dictionary = signal_data[type].dup (MM_GDBUS_MODEM_SIGNAL (self));
- if (dictionary) {
- GError *error = NULL;
-
- self->priv->values[type].info = mm_signal_new_from_dictionary (dictionary, &error);
- if (error) {
- g_warning ("Invalid signal info: %s", error->message);
- g_error_free (error);
- }
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->values[type].id =
- g_signal_connect (self,
- signal_data[type].signal_name,
- G_CALLBACK (signal_data[type].updated_callback),
- NULL);
- }
-
- if (dup && self->priv->values[type].info)
- *dup = g_object_ref (self->priv->values[type].info);
- }
- g_mutex_unlock (&self->priv->values[type].mutex);
+ g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), FALSE);
+
+ return mm_gdbus_modem_signal_get_error_rate_threshold (MM_GDBUS_MODEM_SIGNAL (self));
}
/*****************************************************************************/
@@ -321,21 +353,14 @@ ensure_internal (MMModemSignal *self,
*
* <warning>The values reported by @self are not updated when the values in the
* interface change. Instead, the client is expected to call
- * mm_modem_signal_get_cdma() again to get a new #MMSignal with the
- * new values.</warning>
+ * mm_modem_signal_get_cdma() again to get a new #MMSignal with the new values.
+ * </warning>
*
- * Returns: (transfer full): A #MMSignal that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMSignal that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_get_cdma (MMModemSignal *self)
-{
- MMSignal *info = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
-
- ensure_internal (self, &info, UPDATED_PROPERTY_TYPE_CDMA);
- return info;
-}
/**
* mm_modem_signal_peek_cdma:
@@ -343,21 +368,20 @@ mm_modem_signal_get_cdma (MMModemSignal *self)
*
* Gets a #MMSignal object specifying the CDMA signal information.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_signal_get_cdma() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_signal_get_cdma() if on another thread.</warning>
*
- * Returns: (transfer none): A #MMSignal. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMSignal. Do not free the returned value, it
+ * belongs to @self.
+ *
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_peek_cdma (MMModemSignal *self)
-{
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
- ensure_internal (self, NULL, UPDATED_PROPERTY_TYPE_CDMA);
- return self->priv->values[UPDATED_PROPERTY_TYPE_CDMA].info;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (cdma,
+ ModemSignal, modem_signal, MODEM_SIGNAL,
+ MMSignal,
+ mm_signal_new_from_dictionary)
/*****************************************************************************/
@@ -369,21 +393,14 @@ mm_modem_signal_peek_cdma (MMModemSignal *self)
*
* <warning>The values reported by @self are not updated when the values in the
* interface change. Instead, the client is expected to call
- * mm_modem_signal_get_evdo() again to get a new #MMSignal with the
- * new values.</warning>
+ * mm_modem_signal_get_evdo() again to get a new #MMSignal with the new values.
+ * </warning>
+ *
+ * Returns: (transfer full): A #MMSignal that must be freed with
+ * g_object_unref() or %NULL if unknown.
*
- * Returns: (transfer full): A #MMSignal that must be freed with g_object_unref() or %NULL if unknown.
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_get_evdo (MMModemSignal *self)
-{
- MMSignal *info = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
-
- ensure_internal (self, &info, UPDATED_PROPERTY_TYPE_EVDO);
- return info;
-}
/**
* mm_modem_signal_peek_evdo:
@@ -391,21 +408,20 @@ mm_modem_signal_get_evdo (MMModemSignal *self)
*
* Gets a #MMSignal object specifying the EV-DO signal information.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_signal_get_evdo() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_signal_get_evdo() if on another thread.</warning>
*
- * Returns: (transfer none): A #MMSignal. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMSignal. Do not free the returned value, it
+ * belongs to @self.
+ *
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_peek_evdo (MMModemSignal *self)
-{
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
- ensure_internal (self, NULL, UPDATED_PROPERTY_TYPE_EVDO);
- return self->priv->values[UPDATED_PROPERTY_TYPE_EVDO].info;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (evdo,
+ ModemSignal, modem_signal, MODEM_SIGNAL,
+ MMSignal,
+ mm_signal_new_from_dictionary)
/*****************************************************************************/
@@ -420,18 +436,11 @@ mm_modem_signal_peek_evdo (MMModemSignal *self)
* mm_modem_signal_get_gsm() again to get a new #MMSignal with the
* new values.</warning>
*
- * Returns: (transfer full): A #MMSignal that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMSignal that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_get_gsm (MMModemSignal *self)
-{
- MMSignal *info = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
-
- ensure_internal (self, &info, UPDATED_PROPERTY_TYPE_GSM);
- return info;
-}
/**
* mm_modem_signal_peek_gsm:
@@ -439,21 +448,20 @@ mm_modem_signal_get_gsm (MMModemSignal *self)
*
* Gets a #MMSignal object specifying the GSM signal information.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_signal_get_gsm() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_signal_get_gsm() if on another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMSignal. Do not free the returned value, it
+ * belongs to @self.
*
- * Returns: (transfer none): A #MMSignal. Do not free the returned value, it belongs to @self.
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_peek_gsm (MMModemSignal *self)
-{
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
- ensure_internal (self, NULL, UPDATED_PROPERTY_TYPE_GSM);
- return self->priv->values[UPDATED_PROPERTY_TYPE_GSM].info;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (gsm,
+ ModemSignal, modem_signal, MODEM_SIGNAL,
+ MMSignal,
+ mm_signal_new_from_dictionary)
/*****************************************************************************/
@@ -465,21 +473,14 @@ mm_modem_signal_peek_gsm (MMModemSignal *self)
*
* <warning>The values reported by @self are not updated when the values in the
* interface change. Instead, the client is expected to call
- * mm_modem_signal_get_umts() again to get a new #MMSignal with the
- * new values.</warning>
+ * mm_modem_signal_get_umts() again to get a new #MMSignal with the new values.
+ * </warning>
+ *
+ * Returns: (transfer full): A #MMSignal that must be freed with
+ * g_object_unref() or %NULL if unknown.
*
- * Returns: (transfer full): A #MMSignal that must be freed with g_object_unref() or %NULL if unknown.
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_get_umts (MMModemSignal *self)
-{
- MMSignal *info = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
-
- ensure_internal (self, &info, UPDATED_PROPERTY_TYPE_UMTS);
- return info;
-}
/**
* mm_modem_signal_peek_umts:
@@ -487,21 +488,20 @@ mm_modem_signal_get_umts (MMModemSignal *self)
*
* Gets a #MMSignal object specifying the UMTS signal information.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_signal_get_umts() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_signal_get_umts() if on another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMSignal. Do not free the returned value, it
+ * belongs to @self.
*
- * Returns: (transfer none): A #MMSignal. Do not free the returned value, it belongs to @self.
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_peek_umts (MMModemSignal *self)
-{
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
- ensure_internal (self, NULL, UPDATED_PROPERTY_TYPE_UMTS);
- return self->priv->values[UPDATED_PROPERTY_TYPE_UMTS].info;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (umts,
+ ModemSignal, modem_signal, MODEM_SIGNAL,
+ MMSignal,
+ mm_signal_new_from_dictionary)
/*****************************************************************************/
@@ -513,21 +513,14 @@ mm_modem_signal_peek_umts (MMModemSignal *self)
*
* <warning>The values reported by @self are not updated when the values in the
* interface change. Instead, the client is expected to call
- * mm_modem_signal_get_lte() again to get a new #MMSignal with the
- * new values.</warning>
+ * mm_modem_signal_get_lte() again to get a new #MMSignal with the new values.
+ * </warning>
+ *
+ * Returns: (transfer full): A #MMSignal that must be freed with
+ * g_object_unref() or %NULL if unknown.
*
- * Returns: (transfer full): A #MMSignal that must be freed with g_object_unref() or %NULL if unknown.
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_get_lte (MMModemSignal *self)
-{
- MMSignal *info = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
-
- ensure_internal (self, &info, UPDATED_PROPERTY_TYPE_LTE);
- return info;
-}
/**
* mm_modem_signal_peek_lte:
@@ -535,58 +528,92 @@ mm_modem_signal_get_lte (MMModemSignal *self)
*
* Gets a #MMSignal object specifying the LTE signal information.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_signal_get_lte() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_signal_get_lte() if on another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMSignal. Do not free the returned value, it
+ * belongs to @self.
*
- * Returns: (transfer none): A #MMSignal. Do not free the returned value, it belongs to @self.
+ * Since: 1.2
*/
-MMSignal *
-mm_modem_signal_peek_lte (MMModemSignal *self)
-{
- g_return_val_if_fail (MM_IS_MODEM_SIGNAL (self), NULL);
- ensure_internal (self, NULL, UPDATED_PROPERTY_TYPE_LTE);
- return self->priv->values[UPDATED_PROPERTY_TYPE_LTE].info;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (lte,
+ ModemSignal, modem_signal, MODEM_SIGNAL,
+ MMSignal,
+ mm_signal_new_from_dictionary)
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_signal_get_nr5g:
+ * @self: A #MMModem.
+ *
+ * Gets a #MMSignal object specifying the 5G signal information.
+ *
+ * <warning>The values reported by @self are not updated when the values in the
+ * interface change. Instead, the client is expected to call
+ * mm_modem_signal_get_nr5g() again to get a new #MMSignal with the new values.
+ * </warning>
+ *
+ * Returns: (transfer full): A #MMSignal that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.16
+ */
+
+/**
+ * mm_modem_signal_peek_nr5g:
+ * @self: A #MMModem.
+ *
+ * Gets a #MMSignal object specifying the 5G signal information.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_signal_get_nr5g() if on another thread.</warning>
+ *
+ * Returns: (transfer none): A #MMSignal. Do not free the returned value, it
+ * belongs to @self.
+ *
+ * Since: 1.16
+ */
+
+PROPERTY_OBJECT_DEFINE_FAILABLE (nr5g,
+ ModemSignal, modem_signal, MODEM_SIGNAL,
+ MMSignal,
+ mm_signal_new_from_dictionary)
/*****************************************************************************/
static void
mm_modem_signal_init (MMModemSignal *self)
{
- guint i;
-
- /* Setup private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_SIGNAL, MMModemSignalPrivate);
-
- for (i = 0; i < UPDATED_PROPERTY_TYPE_LAST; i++)
- g_mutex_init (&self->priv->values[i].mutex);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (cdma, "cdma")
+ PROPERTY_INITIALIZE (evdo, "evdo")
+ PROPERTY_INITIALIZE (gsm, "gsm")
+ PROPERTY_INITIALIZE (umts, "umts")
+ PROPERTY_INITIALIZE (lte, "lte")
+ PROPERTY_INITIALIZE (nr5g, "nr5g")
}
static void
finalize (GObject *object)
{
MMModemSignal *self = MM_MODEM_SIGNAL (object);
- guint i;
- for (i = 0; i < UPDATED_PROPERTY_TYPE_LAST; i++)
- g_mutex_clear (&self->priv->values[i].mutex);
+ g_mutex_clear (&self->priv->mutex);
- G_OBJECT_CLASS (mm_modem_signal_parent_class)->finalize (object);
-}
+ PROPERTY_OBJECT_FINALIZE (cdma)
+ PROPERTY_OBJECT_FINALIZE (evdo)
+ PROPERTY_OBJECT_FINALIZE (gsm)
+ PROPERTY_OBJECT_FINALIZE (umts)
+ PROPERTY_OBJECT_FINALIZE (lte)
+ PROPERTY_OBJECT_FINALIZE (nr5g)
-static void
-dispose (GObject *object)
-{
- MMModemSignal *self = MM_MODEM_SIGNAL (object);
- guint i;
-
- for (i = 0; i < UPDATED_PROPERTY_TYPE_LAST; i++)
- g_clear_object (&self->priv->values[i].info);
-
- G_OBJECT_CLASS (mm_modem_signal_parent_class)->dispose (object);
+ G_OBJECT_CLASS (mm_modem_signal_parent_class)->finalize (object);
}
static void
@@ -596,7 +623,5 @@ mm_modem_signal_class_init (MMModemSignalClass *modem_class)
g_type_class_add_private (object_class, sizeof (MMModemSignalPrivate));
- /* Virtual methods */
- object_class->dispose = dispose;
object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem-signal.h b/libmm-glib/mm-modem-signal.h
index ef2c8e1c..d494b06a 100644
--- a/libmm-glib/mm-modem-signal.h
+++ b/libmm-glib/mm-modem-signal.h
@@ -17,7 +17,8 @@
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
*/
#ifndef _MM_MODEM_SIGNAL_H_
@@ -30,6 +31,7 @@
#include <ModemManager.h>
#include "mm-signal.h"
+#include "mm-signal-threshold-properties.h"
#include "mm-gdbus-modem.h"
G_BEGIN_DECLS
@@ -63,25 +65,40 @@ struct _MMModemSignalClass {
};
GType mm_modem_signal_get_type (void);
-
-const gchar *mm_modem_signal_get_path (MMModemSignal *self);
-gchar *mm_modem_signal_dup_path (MMModemSignal *self);
-guint mm_modem_signal_get_rate (MMModemSignal *self);
-
-void mm_modem_signal_setup (MMModemSignal *self,
- guint rate,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_modem_signal_setup_finish (MMModemSignal *self,
- GAsyncResult *res,
- GError **error);
-gboolean mm_modem_signal_setup_sync (MMModemSignal *self,
- guint rate,
- GCancellable *cancellable,
- GError **error);
-
-MMSignal *mm_modem_signal_get_cdma (MMModemSignal *self);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemSignal, g_object_unref)
+
+const gchar *mm_modem_signal_get_path (MMModemSignal *self);
+gchar *mm_modem_signal_dup_path (MMModemSignal *self);
+guint mm_modem_signal_get_rate (MMModemSignal *self);
+guint mm_modem_signal_get_rssi_threshold (MMModemSignal *self);
+gboolean mm_modem_signal_get_error_rate_threshold (MMModemSignal *self);
+
+void mm_modem_signal_setup (MMModemSignal *self,
+ guint rate,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_signal_setup_finish (MMModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_signal_setup_sync (MMModemSignal *self,
+ guint rate,
+ GCancellable *cancellable,
+ GError **error);
+void mm_modem_signal_setup_thresholds (MMModemSignal *self,
+ MMSignalThresholdProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_signal_setup_thresholds_finish (MMModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_signal_setup_thresholds_sync (MMModemSignal *self,
+ MMSignalThresholdProperties *properties,
+ GCancellable *cancellable,
+ GError **error);
+
+MMSignal *mm_modem_signal_get_cdma (MMModemSignal *self);
MMSignal *mm_modem_signal_peek_cdma (MMModemSignal *self);
MMSignal *mm_modem_signal_get_evdo (MMModemSignal *self);
@@ -96,6 +113,9 @@ MMSignal *mm_modem_signal_peek_umts (MMModemSignal *self);
MMSignal *mm_modem_signal_get_lte (MMModemSignal *self);
MMSignal *mm_modem_signal_peek_lte (MMModemSignal *self);
+MMSignal *mm_modem_signal_get_nr5g (MMModemSignal *self);
+MMSignal *mm_modem_signal_peek_nr5g (MMModemSignal *self);
+
G_END_DECLS
#endif /* _MM_MODEM_SIGNAL_H_ */
diff --git a/libmm-glib/mm-modem-simple.c b/libmm-glib/mm-modem-simple.c
index b96e31da..360a41f8 100644
--- a/libmm-glib/mm-modem-simple.c
+++ b/libmm-glib/mm-modem-simple.c
@@ -35,7 +35,8 @@
* The #MMModemSimple is an object providing access to the methods, signals and
* properties of the Simple interface.
*
- * The Simple interface is exposed on modems which are not in %MM_MODEM_STATE_FAILED state.
+ * The Simple interface is exposed on modems which are not in
+ * %MM_MODEM_STATE_FAILED state.
*/
G_DEFINE_TYPE (MMModemSimple, mm_modem_simple, MM_GDBUS_TYPE_MODEM_SIMPLE_PROXY)
@@ -49,6 +50,8 @@ G_DEFINE_TYPE (MMModemSimple, mm_modem_simple, MM_GDBUS_TYPE_MODEM_SIMPLE_PROXY)
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_simple_get_path (MMModemSimple *self)
@@ -63,9 +66,13 @@ mm_modem_simple_get_path (MMModemSimple *self)
* mm_modem_simple_dup_path:
* @self: A #MMModemSimple.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_simple_dup_path (MMModemSimple *self)
@@ -82,30 +89,19 @@ mm_modem_simple_dup_path (MMModemSimple *self)
/*****************************************************************************/
-typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
-} ConnectContext;
-
-static void
-connect_context_complete_and_free (ConnectContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_slice_free (ConnectContext, ctx);
-}
-
/**
* mm_modem_simple_connect_finish:
* @self: A #MMModemSimple.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_simple_connect().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_simple_connect().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_simple_connect().
*
- * Returns: (transfer full): A #MMBearer, or %FALSE if @error is set. The returned value must be freed with g_object_ref().
+ * Returns: (transfer full): A #MMBearer, or %FALSE if @error is set. The
+ * returned value must be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMBearer *
mm_modem_simple_connect_finish (MMModemSimple *self,
@@ -114,16 +110,13 @@ mm_modem_simple_connect_finish (MMModemSimple *self,
{
g_return_val_if_fail (MM_IS_MODEM_SIMPLE (self), NULL);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
new_bearer_ready (GDBusConnection *connection,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
GError *error = NULL;
GObject *bearer;
@@ -134,19 +127,17 @@ new_bearer_ready (GDBusConnection *connection,
g_object_unref (source_object);
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- bearer,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, bearer, g_object_unref);
- connect_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
simple_connect_ready (MMModemSimple *self,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
GError *error = NULL;
gchar *bearer_path = NULL;
@@ -155,22 +146,23 @@ simple_connect_ready (MMModemSimple *self,
&bearer_path,
res,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_async_initable_new_async (MM_TYPE_BEARER,
G_PRIORITY_DEFAULT,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)new_bearer_ready,
- ctx,
+ task,
"g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"g-name", MM_DBUS_SERVICE,
"g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
"g-object-path", bearer_path,
"g-interface-name", "org.freedesktop.ModemManager1.Bearer",
NULL);
+ g_free (bearer_path);
}
/**
@@ -178,15 +170,21 @@ simple_connect_ready (MMModemSimple *self,
* @self: A #MMModemSimple.
* @properties: (transfer none): A #MMSimpleConnectProperties bundle.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to connect the modem using the given @properties.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_simple_connect_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_simple_connect_finish() to get the result of the operation.
+ *
+ * See mm_modem_simple_connect_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_simple_connect_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_simple_connect (MMModemSimple *self,
@@ -195,18 +193,12 @@ mm_modem_simple_connect (MMModemSimple *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- ConnectContext *ctx;
+ GTask *task;
GVariant *variant;
g_return_if_fail (MM_IS_MODEM_SIMPLE (self));
- ctx = g_slice_new0 (ConnectContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_modem_simple_connect);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
+ task = g_task_new (self, cancellable, callback, user_data);
variant = mm_simple_connect_properties_get_dictionary (properties);
mm_gdbus_modem_simple_call_connect (
@@ -214,7 +206,7 @@ mm_modem_simple_connect (MMModemSimple *self,
variant,
cancellable,
(GAsyncReadyCallback)simple_connect_ready,
- ctx);
+ task);
g_variant_unref (variant);
}
@@ -228,10 +220,13 @@ mm_modem_simple_connect (MMModemSimple *self,
*
* Synchronously requests to connect the modem using the given @properties.
*
- * The calling thread is blocked until a reply is received. See mm_modem_simple_connect()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_simple_connect() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A #MMBearer, or %FALSE if @error is set. The
+ * returned value must be freed with g_object_unref().
*
- * Returns: (transfer full): A #MMBearer, or %FALSE if @error is set. The returned value must be freed with g_object_ref().
+ * Since: 1.0
*/
MMBearer *
mm_modem_simple_connect_sync (MMModemSimple *self,
@@ -274,12 +269,16 @@ mm_modem_simple_connect_sync (MMModemSimple *self,
/**
* mm_modem_simple_disconnect_finish:
* @self: A #MMModemSimple.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_simple_disconnect().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_simple_disconnect().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_simple_disconnect().
*
- * Returns: %TRUE if the modem is successfully disconnected, %FALSE if @error is set.
+ * Returns: %TRUE if the modem is successfully disconnected, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_simple_disconnect_finish (MMModemSimple *self,
@@ -294,17 +293,24 @@ mm_modem_simple_disconnect_finish (MMModemSimple *self,
/**
* mm_modem_simple_disconnect:
* @self: A #MMModemSimple.
- * @bearer: Path of the bearer to disconnect, or %NULL to disconnect all connected bearers.
+ * @bearer: (allow-none): Path of the bearer to disconnect, or %NULL to
+ * disconnect all connected bearers.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to disconnect the modem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_simple_disconnect_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_simple_disconnect_finish() to get the result of the operation.
+ *
+ * See mm_modem_simple_disconnect_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_simple_disconnect_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_simple_disconnect (MMModemSimple *self,
@@ -325,16 +331,20 @@ mm_modem_simple_disconnect (MMModemSimple *self,
/**
* mm_modem_simple_disconnect_sync:
* @self: A #MMModemSimple.
- * @bearer: Path of the bearer to disconnect, or %NULL to disconnect all connected bearers.
+ * @bearer: (allow-none): Path of the bearer to disconnect, or %NULL to
+ * disconnect all connected bearers.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests to disconnect the modem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_simple_disconnect()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_simple_disconnect() for the asynchronous version of this method.
*
- * Returns: %TRUE if the modem is successfully disconnected, %FALSE if @error is set.
+ * Returns: %TRUE if the modem is successfully disconnected, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_simple_disconnect_sync (MMModemSimple *self,
@@ -355,12 +365,16 @@ mm_modem_simple_disconnect_sync (MMModemSimple *self,
/**
* mm_modem_simple_get_status_finish:
* @self: A #MMModemSimple.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_simple_connect().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_simple_connect().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_simple_get_status().
*
- * Returns: (transfer full): A #MMSimpleStatus, or %FALSE if @error is set. The returned value must be freed with g_object_ref().
+ * Returns: (transfer full): A #MMSimpleStatus, or %FALSE if @error is set. The
+ * returned value must be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMSimpleStatus *
mm_modem_simple_get_status_finish (MMModemSimple *self,
@@ -384,15 +398,21 @@ mm_modem_simple_get_status_finish (MMModemSimple *self,
* mm_modem_simple_get_status:
* @self: A #MMModemSimple.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests a compilation of the status of the modem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_simple_get_status_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_simple_get_status_finish() to get the result of the operation.
*
- * See mm_modem_simple_get_status_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_simple_get_status_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_simple_get_status (MMModemSimple *self,
@@ -416,10 +436,13 @@ mm_modem_simple_get_status (MMModemSimple *self,
*
* Synchronously requests a compilation of the status of the modem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_simple_get_status()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_simple_get_status() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A #MMSimpleStatus, or %FALSE if @error is set. The
+ * returned value must be freed with g_object_unref().
*
- * Returns: (transfer full): A #MMSimpleStatus, or %FALSE if @error is set. The returned value must be freed with g_object_ref().
+ * Since: 1.0
*/
MMSimpleStatus *
mm_modem_simple_get_status_sync (MMModemSimple *self,
diff --git a/libmm-glib/mm-modem-simple.h b/libmm-glib/mm-modem-simple.h
index 0503a87f..366c317d 100644
--- a/libmm-glib/mm-modem-simple.h
+++ b/libmm-glib/mm-modem-simple.h
@@ -65,6 +65,7 @@ struct _MMModemSimpleClass {
};
GType mm_modem_simple_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemSimple, g_object_unref)
const gchar *mm_modem_simple_get_path (MMModemSimple *self);
gchar *mm_modem_simple_dup_path (MMModemSimple *self);
diff --git a/libmm-glib/mm-modem-time.c b/libmm-glib/mm-modem-time.c
index 0cab5212..99ea4f02 100644
--- a/libmm-glib/mm-modem-time.c
+++ b/libmm-glib/mm-modem-time.c
@@ -41,10 +41,10 @@
G_DEFINE_TYPE (MMModemTime, mm_modem_time, MM_GDBUS_TYPE_MODEM_TIME_PROXY)
struct _MMModemTimePrivate {
- /* Timezone */
- GMutex timezone_mutex;
- guint timezone_id;
- MMNetworkTimezone *timezone;
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_OBJECT_DECLARE (network_timezone, MMNetworkTimezone)
};
/*****************************************************************************/
@@ -56,6 +56,8 @@ struct _MMModemTimePrivate {
* Gets the DBus path of the #MMObject which implements this interface.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_time_get_path (MMModemTime *self)
@@ -70,9 +72,13 @@ mm_modem_time_get_path (MMModemTime *self)
* mm_modem_time_dup_path:
* @self: A #MMModemTime.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_time_dup_path (MMModemTime *self)
@@ -92,12 +98,16 @@ mm_modem_time_dup_path (MMModemTime *self)
/**
* mm_modem_time_get_network_time_finish:
* @self: A #MMModemTime.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_enable().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_enable().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_time_get_network_time().
*
- * Returns: (transfer full): A string containing the network time, or %NULL if @error is set. The returned value should be freed with g_free().
+ * Returns: (transfer full): A string containing the network time, or %NULL if
+ * @error is set. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_time_get_network_time_finish (MMModemTime *self,
@@ -118,15 +128,21 @@ mm_modem_time_get_network_time_finish (MMModemTime *self,
* mm_modem_time_get_network_time:
* @self: A #MMModemTime.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests the current network time.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_time_get_network_time_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_time_get_network_time_finish() to get the result of the operation.
*
- * See mm_modem_time_get_network_time_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_time_get_network_time_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_time_get_network_time (MMModemTime *self,
@@ -150,10 +166,13 @@ mm_modem_time_get_network_time (MMModemTime *self,
*
* Synchronously requests the current network time.
*
- * The calling thread is blocked until a reply is received. See mm_modem_time_get_network_time()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_time_get_network_time() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A string containing the network time, or %NULL if
+ * @error is set. The returned value should be freed with g_free().
*
- * Returns: (transfer full): A string containing the network time, or %NULL if @error is set. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_time_get_network_time_sync (MMModemTime *self,
@@ -172,54 +191,6 @@ mm_modem_time_get_network_time_sync (MMModemTime *self,
/*****************************************************************************/
-static void
-timezone_updated (MMModemTime *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->timezone_mutex);
- {
- GVariant *dictionary;
-
- g_clear_object (&self->priv->timezone);
- dictionary = mm_gdbus_modem_time_get_network_timezone (MM_GDBUS_MODEM_TIME (self));
- self->priv->timezone = (dictionary ?
- mm_network_timezone_new_from_dictionary (dictionary, NULL) :
- NULL);
- }
- g_mutex_unlock (&self->priv->timezone_mutex);
-}
-
-static void
-ensure_internal_timezone (MMModemTime *self,
- MMNetworkTimezone **dup)
-{
- g_mutex_lock (&self->priv->timezone_mutex);
- {
- /* If this is the first time ever asking for the object, setup the
- * update listener and the initial object, if any. */
- if (!self->priv->timezone_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_time_dup_network_timezone (MM_GDBUS_MODEM_TIME (self));
- if (dictionary) {
- self->priv->timezone = mm_network_timezone_new_from_dictionary (dictionary, NULL);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->timezone_id =
- g_signal_connect (self,
- "notify::network-timezone",
- G_CALLBACK (timezone_updated),
- NULL);
- }
-
- if (dup && self->priv->timezone)
- *dup = g_object_ref (self->priv->timezone);
- }
- g_mutex_unlock (&self->priv->timezone_mutex);
-}
-
/**
* mm_modem_time_get_network_timezone:
* @self: A #MMModemTime.
@@ -228,21 +199,14 @@ ensure_internal_timezone (MMModemTime *self,
*
* <warning>The values reported by @self are not updated when the values in the
* interface change. Instead, the client is expected to call
- * mm_modem_time_get_network_timezone() again to get a new #MMNetworkTimezone with the
- * new values.</warning>
+ * mm_modem_time_get_network_timezone() again to get a new #MMNetworkTimezone
+ * with the new values.</warning>
*
- * Returns: (transfer full): A #MMNetworkTimezone that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMNetworkTimezone that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.0
*/
-MMNetworkTimezone *
-mm_modem_time_get_network_timezone (MMModemTime *self)
-{
- MMNetworkTimezone *tz = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM_TIME (self), NULL);
-
- ensure_internal_timezone (self, &tz);
- return tz;
-}
/**
* mm_modem_time_peek_network_timezone:
@@ -250,42 +214,30 @@ mm_modem_time_get_network_timezone (MMModemTime *self)
*
* Gets the network timezone information.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_time_get_network_timezone() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_time_get_network_timezone() if on another thread.</warning>
*
- * Returns: (transfer none): A #MMNetworkTimezone. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMNetworkTimezone. Do not free the returned
+ * value, it belongs to @self.
+ *
+ * Since: 1.0
*/
-MMNetworkTimezone *
-mm_modem_time_peek_network_timezone (MMModemTime *self)
-{
- g_return_val_if_fail (MM_IS_MODEM_TIME (self), NULL);
- ensure_internal_timezone (self, NULL);
- return self->priv->timezone;
-}
+PROPERTY_OBJECT_DEFINE_FAILABLE (network_timezone,
+ ModemTime, modem_time, MODEM_TIME,
+ MMNetworkTimezone,
+ mm_network_timezone_new_from_dictionary)
/*****************************************************************************/
static void
mm_modem_time_init (MMModemTime *self)
{
- /* Setup private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_MODEM_TIME,
- MMModemTimePrivate);
- g_mutex_init (&self->priv->timezone_mutex);
-}
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM_TIME, MMModemTimePrivate);
+ g_mutex_init (&self->priv->mutex);
-static void
-dispose (GObject *object)
-{
- MMModemTime *self = MM_MODEM_TIME (object);
-
- g_clear_object (&self->priv->timezone);
-
- G_OBJECT_CLASS (mm_modem_time_parent_class)->dispose (object);
+ PROPERTY_INITIALIZE (network_timezone, "network-timezone")
}
static void
@@ -293,7 +245,9 @@ finalize (GObject *object)
{
MMModemTime *self = MM_MODEM_TIME (object);
- g_mutex_clear (&self->priv->timezone_mutex);
+ g_mutex_clear (&self->priv->mutex);
+
+ PROPERTY_OBJECT_FINALIZE (network_timezone)
G_OBJECT_CLASS (mm_modem_time_parent_class)->finalize (object);
}
@@ -305,7 +259,5 @@ mm_modem_time_class_init (MMModemTimeClass *modem_class)
g_type_class_add_private (object_class, sizeof (MMModemTimePrivate));
- /* Virtual methods */
object_class->finalize = finalize;
- object_class->dispose = dispose;
}
diff --git a/libmm-glib/mm-modem-time.h b/libmm-glib/mm-modem-time.h
index c0211792..8c520654 100644
--- a/libmm-glib/mm-modem-time.h
+++ b/libmm-glib/mm-modem-time.h
@@ -64,6 +64,7 @@ struct _MMModemTimeClass {
};
GType mm_modem_time_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemTime, g_object_unref)
const gchar *mm_modem_time_get_path (MMModemTime *self);
gchar *mm_modem_time_dup_path (MMModemTime *self);
diff --git a/libmm-glib/mm-modem-voice.c b/libmm-glib/mm-modem-voice.c
new file mode 100644
index 00000000..8d13a240
--- /dev/null
+++ b/libmm-glib/mm-modem-voice.c
@@ -0,0 +1,1141 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#include <gio/gio.h>
+
+#include "mm-helpers.h"
+#include "mm-common-helpers.h"
+#include "mm-errors-types.h"
+#include "mm-modem-voice.h"
+
+/**
+ * SECTION: mm-modem-voice
+ * @title: MMModemVoice
+ * @short_description: The Voice interface
+ *
+ * The #MMModemVoice is an object providing access to the methods, signals and
+ * properties of the Voice interface.
+ *
+ * The Voice interface is exposed whenever a modem has voice capabilities.
+ */
+
+G_DEFINE_TYPE (MMModemVoice, mm_modem_voice, MM_GDBUS_TYPE_MODEM_VOICE_PROXY)
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_get_path:
+ * @self: A #MMModemVoice.
+ *
+ * Gets the DBus path of the #MMObject which implements this interface.
+ *
+ * Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.6
+ */
+const gchar *
+mm_modem_voice_get_path (MMModemVoice *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ g_dbus_proxy_get_object_path (G_DBUS_PROXY (self)));
+}
+
+/**
+ * mm_modem_voice_dup_path:
+ * @self: A #MMModemVoice.
+ *
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
+ *
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.6
+ */
+gchar *
+mm_modem_voice_dup_path (MMModemVoice *self)
+{
+ gchar *value;
+
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL);
+
+ g_object_get (G_OBJECT (self),
+ "g-object-path", &value,
+ NULL);
+ RETURN_NON_EMPTY_STRING (value);
+}
+
+/**
+ * mm_modem_voice_get_emergency_only:
+ * @self: A #MMModemVoice.
+ *
+ * Checks whether emergency calls only are allowed.
+ *
+ * Returns: %TRUE if only emergency calls are allowed, %FALSE otherwise.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_get_emergency_only (MMModemVoice *self)
+{
+ return mm_gdbus_modem_voice_get_emergency_only (MM_GDBUS_MODEM_VOICE (self));
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ gchar **call_paths;
+ GList *call_objects;
+ guint i;
+} ListCallsContext;
+
+static void
+call_object_list_free (GList *list)
+{
+ g_list_free_full (list, g_object_unref);
+}
+
+static void
+list_call_context_free (ListCallsContext *ctx)
+{
+ g_strfreev (ctx->call_paths);
+ call_object_list_free (ctx->call_objects);
+ g_slice_free (ListCallsContext, ctx);
+}
+
+/**
+ * mm_modem_voice_list_calls_finish:
+ * @self: A #MMModem.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_list_calls().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_list_calls().
+ *
+ * Returns: (element-type ModemManager.Call) (transfer full): A list of #MMCall
+ * objects, or #NULL if either not found or @error is set. The returned value
+ * should be freed with g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify function.
+ *
+ * Since: 1.6
+ */
+GList *
+mm_modem_voice_list_calls_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void create_next_call (GTask *task);
+
+static void
+list_build_object_ready (GDBusConnection *connection,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GObject *call;
+ GObject *source_object;
+ ListCallsContext *ctx;
+
+ source_object = g_async_result_get_source_object (res);
+ call = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
+ g_object_unref (source_object);
+
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ /* Keep the object */
+ ctx->call_objects = g_list_prepend (ctx->call_objects, call);
+
+ /* If no more calls, just end here. */
+ if (!ctx->call_paths[++ctx->i]) {
+ GList *call_objects;
+
+ call_objects = g_list_copy_deep (ctx->call_objects, (GCopyFunc)g_object_ref, NULL);
+ g_task_return_pointer (task, call_objects, (GDestroyNotify)call_object_list_free);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on creating next object */
+ create_next_call (task);
+}
+
+static void
+create_next_call (GTask *task)
+{
+ MMModemVoice *self;
+ ListCallsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_async_initable_new_async (MM_TYPE_CALL,
+ G_PRIORITY_DEFAULT,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)list_build_object_ready,
+ task,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", ctx->call_paths[ctx->i],
+ "g-interface-name", "org.freedesktop.ModemManager1.Call",
+ NULL);
+}
+
+/**
+ * mm_modem_voice_list_calls:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously lists the #MMCall objects in the modem.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_list_calls_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_list_calls_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.6
+ */
+void
+mm_modem_voice_list_calls (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ListCallsContext *ctx;
+ GTask *task;
+
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ ctx = g_slice_new0 (ListCallsContext);
+ ctx->call_paths = mm_gdbus_modem_voice_dup_calls (MM_GDBUS_MODEM_VOICE (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)list_call_context_free);
+
+ /* If no CALL, just end here. */
+ if (!ctx->call_paths || !ctx->call_paths[0]) {
+ g_task_return_pointer (task, NULL, NULL);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Got list of paths. If at least one found, start creating objects for each */
+ ctx->i = 0;
+ create_next_call (task);
+}
+
+/**
+ * mm_modem_voice_list_calls_sync:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously lists the #MMCall objects in the modem.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_list_calls() for the asynchronous version of this method.
+ *
+ * Returns: (element-type ModemManager.Call) (transfer full): A list of #MMCall
+ * objects, or #NULL if either not found or @error is set. The returned value
+ * should be freed with g_list_free_full() using g_object_unref() as
+ * #GDestroyNotify function.
+ *
+ * Since: 1.6
+ */
+GList *
+mm_modem_voice_list_calls_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GList *call_objects = NULL;
+ gchar **call_paths = NULL;
+ guint i;
+
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL);
+
+ call_paths = mm_gdbus_modem_voice_dup_calls (MM_GDBUS_MODEM_VOICE (self));
+
+ /* Only non-empty lists are returned */
+ if (!call_paths)
+ return NULL;
+
+ for (i = 0; call_paths[i]; i++) {
+ GObject *call;
+
+ call = g_initable_new (MM_TYPE_CALL,
+ cancellable,
+ error,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", call_paths[i],
+ "g-interface-name", "org.freedesktop.ModemManager1.Call",
+ NULL);
+ if (!call) {
+ call_object_list_free (call_objects);
+ g_strfreev (call_paths);
+ return NULL;
+ }
+
+ /* Keep the object */
+ call_objects = g_list_prepend (call_objects, call);
+ }
+
+ g_strfreev (call_paths);
+ return call_objects;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_create_call_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_create_call().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_create_call().
+ *
+ * Returns: (transfer full): A newly created #MMCall, or %NULL if @error is set.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.6
+ */
+MMCall *
+mm_modem_voice_create_call_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+new_call_object_ready (GDBusConnection *connection,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GObject *call;
+ GObject *source_object;
+
+ source_object = g_async_result_get_source_object (res);
+ call = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
+ g_object_unref (source_object);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, call, g_object_unref);
+
+ g_object_unref (task);
+}
+
+static void
+create_call_ready (MMModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ gchar *call_path = NULL;
+
+ if (!mm_gdbus_modem_voice_call_create_call_finish (MM_GDBUS_MODEM_VOICE (self),
+ &call_path,
+ res,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ g_free (call_path);
+ return;
+ }
+
+ g_async_initable_new_async (MM_TYPE_CALL,
+ G_PRIORITY_DEFAULT,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)new_call_object_ready,
+ task,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", call_path,
+ "g-interface-name", "org.freedesktop.ModemManager1.Call",
+ NULL);
+ g_free (call_path);
+}
+
+/**
+ * mm_modem_voice_create_call:
+ * @self: A #MMModemVoice.
+ * @properties: A ##MMCallProperties object with the properties to use.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously creates a new #MMCall in the modem.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_create_call_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_create_call_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.6
+ */
+void
+mm_modem_voice_create_call (MMModemVoice *self,
+ MMCallProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GVariant *dictionary;
+
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ dictionary = mm_call_properties_get_dictionary (properties);
+ mm_gdbus_modem_voice_call_create_call (
+ MM_GDBUS_MODEM_VOICE (self),
+ dictionary,
+ cancellable,
+ (GAsyncReadyCallback)create_call_ready,
+ task);
+
+ g_variant_unref (dictionary);
+}
+
+/**
+ * mm_modem_voice_create_call_sync:
+ * @self: A #MMModemVoice.
+ * @properties: A ##MMCallProperties object with the properties to use.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously creates a new #MMCall in the modem.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_create_call() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A newly created #MMCall, or %NULL if @error is set.
+ * The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.6
+ */
+MMCall *
+mm_modem_voice_create_call_sync (MMModemVoice *self,
+ MMCallProperties *properties,
+ GCancellable *cancellable,
+ GError **error)
+{
+ MMCall *call = NULL;
+ gchar *call_path = NULL;
+ GVariant *dictionary;
+
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), NULL);
+
+ dictionary = mm_call_properties_get_dictionary (properties);
+ mm_gdbus_modem_voice_call_create_call_sync (MM_GDBUS_MODEM_VOICE (self),
+ dictionary,
+ &call_path,
+ cancellable,
+ error);
+ if (call_path) {
+ call = g_initable_new (MM_TYPE_CALL,
+ cancellable,
+ error,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", call_path,
+ "g-interface-name", "org.freedesktop.ModemManager1.Call",
+ NULL);
+ g_free (call_path);
+ }
+
+ g_variant_unref (dictionary);
+
+ return (call ? MM_CALL (call) : NULL);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_delete_call_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_delete_call().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_delete_call().
+ *
+ * Returns: %TRUE if the call was deleted, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_modem_voice_delete_call_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_delete_call_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
+}
+
+/**
+ * mm_modem_voice_delete_call:
+ * @self: A #MMModemVoice.
+ * @call: Path of the #MMCall to delete.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously deletes a given #MMCall from the modem.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_delete_call_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_delete_call_sync() for the synchronous, blocking version
+ * of this method.
+ *
+ * Since: 1.6
+ */
+void
+mm_modem_voice_delete_call (MMModemVoice *self,
+ const gchar *call,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_delete_call (MM_GDBUS_MODEM_VOICE (self),
+ call,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_delete_call_sync:
+ * @self: A #MMModemVoice.
+ * @call: Path of the #MMCall to delete.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+
+ * Synchronously deletes a given #MMCall from the modem.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_delete_call() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the CALL was deleted, %FALSE if @error is set.
+ *
+ * Since: 1.6
+ */
+gboolean
+mm_modem_voice_delete_call_sync (MMModemVoice *self,
+ const gchar *call,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_delete_call_sync (MM_GDBUS_MODEM_VOICE (self),
+ call,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_hold_and_accept_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_hold_and_accept().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_hold_and_accept().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_hold_and_accept_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_hold_and_accept_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
+}
+
+/**
+ * mm_modem_voice_hold_and_accept:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously puts all active calls on hold and accepts the next waiting or
+ * held call.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_hold_and_accept_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_hold_and_accept_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_modem_voice_hold_and_accept (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_hold_and_accept (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_hold_and_accept_sync:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously puts all active calls on hold and accepts the next waiting or
+ * held call.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_hold_and_accept() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_hold_and_accept_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_hold_and_accept_sync (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_hangup_and_accept_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_hangup_and_accept().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_hangup_and_accept().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_hangup_and_accept_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_hangup_and_accept_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
+}
+
+/**
+ * mm_modem_voice_hangup_and_accept:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously hangs up all active calls and accepts the next waiting or held
+ * call.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_hangup_and_accept_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_hangup_and_accept_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_modem_voice_hangup_and_accept (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_hangup_and_accept (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_hangup_and_accept_sync:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously hangs up all active calls and accepts the next waiting or held
+ * call.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_hangup_and_accept() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_hangup_and_accept_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_hangup_and_accept_sync (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_hangup_all_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_hangup_all().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_hangup_all().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_hangup_all_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_hangup_all_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
+}
+
+/**
+ * mm_modem_voice_hangup_all:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously hangs up all ongoing (active, waiting, held) calls.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_hangup_all_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_hangup_all_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_modem_voice_hangup_all (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_hangup_all (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_hangup_all_sync:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously hangs up all ongoing (active, waiting, held) calls.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_hangup_all() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_hangup_all_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_hangup_all_sync (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_transfer_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_transfer().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_transfer().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_transfer_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_transfer_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
+}
+
+/**
+ * mm_modem_voice_transfer:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously joins all active and held calls, and disconnects from them.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_transfer_finish() to get the result of the operation.
+ *
+ * See mm_modem_voice_transfer_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_modem_voice_transfer (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_transfer (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_transfer_sync:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously joins all active and held calls, and disconnects from them.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_transfer() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_transfer_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_transfer_sync (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_call_waiting_setup_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_call_waiting_setup().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_call_waiting_setup().
+ *
+ * Returns: %TRUE if @status is set, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_call_waiting_setup_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_call_waiting_setup_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
+}
+
+/**
+ * mm_modem_voice_call_waiting_setup:
+ * @self: A #MMModemVoice.
+ * @enable: Whether the call waiting service should be enabled.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously enables or disables the call waiting network service.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_call_waiting_setup_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_voice_call_waiting_setup_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_modem_voice_call_waiting_setup (MMModemVoice *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_call_waiting_setup (MM_GDBUS_MODEM_VOICE (self),
+ enable,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_call_waiting_setup_sync:
+ * @self: A #MMModemVoice.
+ * @enable: Whether the call waiting service should be enabled.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously enables or disables the call waiting network service.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_call_waiting_setup() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if the operation is successful, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_call_waiting_setup_sync (MMModemVoice *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_call_waiting_setup_sync (MM_GDBUS_MODEM_VOICE (self),
+ enable,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_voice_call_waiting_query_finish:
+ * @self: A #MMModemVoice.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_voice_call_waiting_query().
+ * @status: Output location where to store the status.
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_voice_call_waiting_query().
+ *
+ * Returns: %TRUE if @status is set, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_call_waiting_query_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ gboolean *status,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_call_waiting_query_finish (MM_GDBUS_MODEM_VOICE (self), status, res, error);
+}
+
+/**
+ * mm_modem_voice_call_waiting_query:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously queries the status of the call waiting network service.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_voice_call_waiting_query_finish() to get the result of the
+ * operation.
+ *
+ * See mm_modem_voice_call_waiting_query_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.12
+ */
+void
+mm_modem_voice_call_waiting_query (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM_VOICE (self));
+
+ mm_gdbus_modem_voice_call_call_waiting_query (MM_GDBUS_MODEM_VOICE (self),
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_modem_voice_call_waiting_query_sync:
+ * @self: A #MMModemVoice.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @status: Output location where to store the status.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously queries the status of the call waiting network service.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_voice_call_waiting_query() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %TRUE if @status is set, %FALSE if @error is set.
+ *
+ * Since: 1.12
+ */
+gboolean
+mm_modem_voice_call_waiting_query_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ gboolean *status,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
+
+ return mm_gdbus_modem_voice_call_call_waiting_query_sync (MM_GDBUS_MODEM_VOICE (self),
+ status,
+ cancellable,
+ error);
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_voice_init (MMModemVoice *self)
+{
+}
+
+static void
+mm_modem_voice_class_init (MMModemVoiceClass *modem_class)
+{
+}
diff --git a/libmm-glib/mm-modem-voice.h b/libmm-glib/mm-modem-voice.h
new file mode 100644
index 00000000..b5f3dbd1
--- /dev/null
+++ b/libmm-glib/mm-modem-voice.h
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * libmm -- Access modem status & information from glib applications
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2012 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#ifndef _MM_MODEM_VOICE_H_
+#define _MM_MODEM_VOICE_H_
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+
+#include "mm-gdbus-modem.h"
+#include "mm-call.h"
+#include "mm-call-properties.h"
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_MODEM_VOICE (mm_modem_voice_get_type ())
+#define MM_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_VOICE, MMModemVoice))
+#define MM_MODEM_VOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_MODEM_VOICE, MMModemVoiceClass))
+#define MM_IS_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_VOICE))
+#define MM_IS_MODEM_VOICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_MODEM_VOICE))
+#define MM_MODEM_VOICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_MODEM_VOICE, MMModemVoiceClass))
+
+typedef struct _MMModemVoice MMModemVoice;
+typedef struct _MMModemVoiceClass MMModemVoiceClass;
+typedef struct _MMModemVoicePrivate MMModemVoicePrivate;
+
+/**
+ * MMModemVoice:
+ *
+ * The #MMModemVoice structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MMModemVoice {
+ /*< private >*/
+ MmGdbusModemVoiceProxy parent;
+ MMModemVoicePrivate *priv;
+};
+
+struct _MMModemVoiceClass {
+ /*< private >*/
+ MmGdbusModemVoiceProxyClass parent;
+};
+
+GType mm_modem_voice_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModemVoice, g_object_unref)
+
+const gchar *mm_modem_voice_get_path (MMModemVoice *self);
+gchar *mm_modem_voice_dup_path (MMModemVoice *self);
+gboolean mm_modem_voice_get_emergency_only (MMModemVoice *self);
+
+void mm_modem_voice_create_call (MMModemVoice *self,
+ MMCallProperties *properties,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMCall *mm_modem_voice_create_call_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+MMCall *mm_modem_voice_create_call_sync (MMModemVoice *self,
+ MMCallProperties *properties,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_list_calls (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GList *mm_modem_voice_list_calls_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+GList *mm_modem_voice_list_calls_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_delete_call (MMModemVoice *self,
+ const gchar *call,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_delete_call_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_voice_delete_call_sync (MMModemVoice *self,
+ const gchar *call,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_hold_and_accept (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_hold_and_accept_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_voice_hold_and_accept_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_hangup_and_accept (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_hangup_and_accept_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_voice_hangup_and_accept_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_hangup_all (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_hangup_all_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_voice_hangup_all_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_transfer (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_transfer_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_voice_transfer_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_call_waiting_setup (MMModemVoice *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_call_waiting_setup_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_voice_call_waiting_setup_sync (MMModemVoice *self,
+ gboolean enable,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_voice_call_waiting_query (MMModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_voice_call_waiting_query_finish (MMModemVoice *self,
+ GAsyncResult *res,
+ gboolean *status,
+ GError **error);
+gboolean mm_modem_voice_call_waiting_query_sync (MMModemVoice *self,
+ GCancellable *cancellable,
+ gboolean *status,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* _MM_MODEM_VOICE_H_ */
diff --git a/libmm-glib/mm-modem.c b/libmm-glib/mm-modem.c
index 5221c2d4..1d39da43 100644
--- a/libmm-glib/mm-modem.c
+++ b/libmm-glib/mm-modem.c
@@ -44,35 +44,16 @@
G_DEFINE_TYPE (MMModem, mm_modem, MM_GDBUS_TYPE_MODEM_PROXY)
struct _MMModemPrivate {
- /* Ports */
- GMutex ports_mutex;
- guint ports_id;
- GArray *ports;
-
- /* UnlockRetries */
- GMutex unlock_retries_mutex;
- guint unlock_retries_id;
- MMUnlockRetries *unlock_retries;
-
- /* Supported Modes */
- GMutex supported_modes_mutex;
- guint supported_modes_id;
- GArray *supported_modes;
-
- /* Supported Capabilities */
- GMutex supported_capabilities_mutex;
- guint supported_capabilities_id;
- GArray *supported_capabilities;
-
- /* Supported Bands */
- GMutex supported_bands_mutex;
- guint supported_bands_id;
- GArray *supported_bands;
-
- /* Current Bands */
- GMutex current_bands_mutex;
- guint current_bands_id;
- GArray *current_bands;
+ /* Common mutex to sync access */
+ GMutex mutex;
+
+ PROPERTY_ARRAY_DECLARE (ports)
+ PROPERTY_ARRAY_DECLARE (supported_modes)
+ PROPERTY_ARRAY_DECLARE (supported_capabilities)
+ PROPERTY_ARRAY_DECLARE (supported_bands)
+ PROPERTY_ARRAY_DECLARE (current_bands)
+
+ PROPERTY_OBJECT_DECLARE (unlock_retries, MMUnlockRetries)
};
/*****************************************************************************/
@@ -83,7 +64,10 @@ struct _MMModemPrivate {
*
* Gets the DBus path of the #MMObject which implements this interface.
*
- * Returns: (transfer none): The DBus path of the #MMObject object. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The DBus path of the #MMObject object. Do not free
+ * the returned value, it belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_get_path (MMModem *self)
@@ -98,9 +82,13 @@ mm_modem_get_path (MMModem *self)
* mm_modem_dup_path:
* @self: A #MMModem.
*
- * Gets a copy of the DBus path of the #MMObject object which implements this interface.
+ * Gets a copy of the DBus path of the #MMObject object which implements this
+ * interface.
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_path (MMModem *self)
@@ -124,12 +112,15 @@ mm_modem_dup_path (MMModem *self)
*
* Gets the DBus path of the #MMSim handled in this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_sim_path() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_sim_path() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The DBus path of the #MMSim handled in this
+ * #MMModem, or %NULL if none available. Do not free the returned value, it
+ * belongs to @self.
*
- * Returns: (transfer none): The DBus path of the #MMSim handled in this #MMModem, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Since: 1.0
*/
const gchar *
mm_modem_get_sim_path (MMModem *self)
@@ -145,7 +136,11 @@ mm_modem_get_sim_path (MMModem *self)
*
* Gets a copy of the DBus path of the #MMSim handled in this #MMModem.
*
- * Returns: (transfer full): The DBus path of the #MMSim handled in this #MMModem, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMSim handled in this
+ * #MMModem, or %NULL if none available. The returned value should be freed
+ * with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_sim_path (MMModem *self)
@@ -158,119 +153,118 @@ mm_modem_dup_sim_path (MMModem *self)
/*****************************************************************************/
-static void
-supported_capabilities_updated (MMModem *self,
- GParamSpec *pspec)
+/**
+ * mm_modem_get_sim_slot_paths:
+ * @self: A #MMModem.
+ *
+ * Gets the DBus paths of the #MMSim objects available in the different SIM
+ * slots handled in this #MMModem. If a given SIM slot at a given index doesn't
+ * have a SIM card available, an empty object path will be given. This list
+ * includes the currently active SIM object path.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_sim_slot_paths() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The DBus paths of the #MMSim objects handled in
+ * this #MMModem, or %NULL if none available. Do not free the returned value, it
+ * belongs to @self.
+ *
+ * Since: 1.16
+ */
+const gchar * const *
+mm_modem_get_sim_slot_paths (MMModem *self)
{
- g_mutex_lock (&self->priv->supported_capabilities_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->supported_capabilities)
- g_array_unref (self->priv->supported_capabilities);
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- dictionary = mm_gdbus_modem_get_supported_capabilities (MM_GDBUS_MODEM (self));
- self->priv->supported_capabilities = (dictionary ?
- mm_common_capability_combinations_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->supported_capabilities_mutex);
-}
-
-static gboolean
-ensure_internal_supported_capabilities (MMModem *self,
- MMModemCapability **dup_capabilities,
- guint *dup_capabilities_n)
-{
- gboolean ret;
-
- g_mutex_lock (&self->priv->supported_capabilities_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->supported_capabilities_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_dup_supported_capabilities (MM_GDBUS_MODEM (self));
- if (dictionary) {
- self->priv->supported_capabilities = mm_common_capability_combinations_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->supported_capabilities_id =
- g_signal_connect (self,
- "notify::supported-capabilities",
- G_CALLBACK (supported_capabilities_updated),
- NULL);
- }
+ return mm_gdbus_modem_get_sim_slots (MM_GDBUS_MODEM (self));
+}
- if (!self->priv->supported_capabilities)
- ret = FALSE;
- else {
- ret = TRUE;
-
- if (dup_capabilities && dup_capabilities_n) {
- *dup_capabilities_n = self->priv->supported_capabilities->len;
- if (self->priv->supported_capabilities->len > 0) {
- *dup_capabilities = g_malloc (sizeof (MMModemCapability) * self->priv->supported_capabilities->len);
- memcpy (*dup_capabilities, self->priv->supported_capabilities->data, sizeof (MMModemCapability) * self->priv->supported_capabilities->len);
- } else
- *dup_capabilities = NULL;
- }
- }
- }
- g_mutex_unlock (&self->priv->supported_capabilities_mutex);
+/**
+ * mm_modem_dup_sim_slot_paths:
+ * @self: A #MMModem.
+ *
+ * Gets a copy of the DBus paths of the #MMSim objects available in the
+ * different SIM slots handled in this #MMModem. If a given SIM slot at a given
+ * index doesn't have a SIM card available, an empty object path will be given.
+ * This list includes the currently active SIM object path.
+ *
+ * Returns: (transfer full): The DBus paths of the #MMSim objects handled in
+ * this #MMModem, or %NULL if none available. The returned value should be
+ * freed with g_strfreev().
+ *
+ * Since: 1.16
+ */
+gchar **
+mm_modem_dup_sim_slot_paths (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- return ret;
+ return mm_gdbus_modem_dup_sim_slots (MM_GDBUS_MODEM (self));
}
+/*****************************************************************************/
+
/**
- * mm_modem_get_supported_capabilities:
+ * mm_modem_get_primary_sim_slot:
* @self: A #MMModem.
- * @capabilities: (out) (array length=n_capabilities): Return location for the array of #MMModemCapability values. The returned array should be freed with g_free() when no longer needed.
- * @n_capabilities: (out): Return location for the number of values in @capabilities.
*
- * Gets the list of combinations of generic families of access technologies supported by this #MMModem.
+ * Gets the SIM slot number of the primary active SIM.
+ *
+ * Returns: slot number, in the [1,N] range.
*
- * Returns: %TRUE if @capabilities and @n_capabilities are set, %FALSE otherwise.
+ * Since: 1.16
*/
-gboolean
-mm_modem_get_supported_capabilities (MMModem *self,
- MMModemCapability **capabilities,
- guint *n_capabilities)
+guint
+mm_modem_get_primary_sim_slot (MMModem *self)
{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+ g_return_val_if_fail (MM_IS_MODEM (self), 0);
- return ensure_internal_supported_capabilities (self, capabilities, n_capabilities);
+ return mm_gdbus_modem_get_primary_sim_slot (MM_GDBUS_MODEM (self));
}
+/*****************************************************************************/
+
/**
- * mm_modem_peek_supported_capabilities:
+ * mm_modem_get_supported_capabilities:
* @self: A #MMModem.
- * @capabilities: (out) (array length=n_capabilities): Return location for the array of #MMModemCapability values. Do not free the returned array, it is owned by @self.
- * @n_capabilities: (out): Return location for the number of values in @capabilities.
+ * @capabilities: (out) (array length=n_capabilities): Return location for the
+ * array of #MMModemCapability values. The returned array should be freed with
+ * g_free() when no longer needed.
+ * @n_capabilities: (out): Return location for the number of values in
+ * @capabilities.
+ *
+ * Gets the list of combinations of generic families of access technologies
+ * supported by this #MMModem.
*
- * Gets the list of combinations of generic families of access technologies supported by this #MMModem.
+ * Returns: %TRUE if @capabilities and @n_capabilities are set, %FALSE
+ * otherwise.
*
- * Returns: %TRUE if @capabilities and @n_capabilities are set, %FALSE otherwise.
+ * Since: 1.0
*/
-gboolean
-mm_modem_peek_supported_capabilities (MMModem *self,
- const MMModemCapability **capabilities,
- guint *n_capabilities)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (capabilities != NULL, FALSE);
- g_return_val_if_fail (n_capabilities != NULL, FALSE);
- if (!ensure_internal_supported_capabilities (self, NULL, NULL))
- return FALSE;
+/**
+ * mm_modem_peek_supported_capabilities:
+ * @self: A #MMModem.
+ * @capabilities: (out) (array length=n_capabilities): Return location for the
+ * array of #MMModemCapability values. Do not free the returned array, it is
+ * owned by @self.
+ * @n_capabilities: (out): Return location for the number of values in
+ * @capabilities.
+ *
+ * Gets the list of combinations of generic families of access technologies
+ * supported by this #MMModem.
+ *
+ * Returns: %TRUE if @capabilities and @n_capabilities are set, %FALSE
+ * otherwise.
+ *
+ * Since: 1.0
+ */
- *n_capabilities = self->priv->supported_capabilities->len;
- *capabilities = (MMModemCapability *)self->priv->supported_capabilities->data;
- return TRUE;
-}
+PROPERTY_ARRAY_DEFINE (supported_capabilities,
+ Modem, modem, MODEM,
+ MMModemCapability,
+ mm_common_capability_combinations_variant_to_garray)
/*****************************************************************************/
@@ -278,10 +272,12 @@ mm_modem_peek_supported_capabilities (MMModem *self,
* mm_modem_get_current_capabilities:
* @self: A #MMModem.
*
- * Gets the list of generic families of access technologies supported by this #MMModem
- * without a firmware reload or reinitialization.
+ * Gets the list of generic families of access technologies supported by this
+ * #MMModem without a firmware reload or reinitialization.
*
* Returns: A bitmask of #MMModemCapability flags.
+ *
+ * Since: 1.0
*/
MMModemCapability
mm_modem_get_current_capabilities (MMModem *self)
@@ -294,48 +290,49 @@ mm_modem_get_current_capabilities (MMModem *self)
/*****************************************************************************/
/**
- * mm_modem_get_max_bearers:
+ * mm_modem_get_max_active_bearers:
* @self: a #MMModem.
*
- * Gets the maximum number of defined packet data bearers this #MMModem supports.
+ * Gets the maximum number of active packet data bearers this #MMModem supports
+ * without enabling multiplexing support.
*
- * This is not the number of active/connected bearers the modem supports,
- * but simply the number of bearers that may be defined at any given time.
- * For example, POTS and CDMA2000-only devices support only one bearer,
- * while GSM/UMTS devices typically support three or more, and any
- * LTE-capable device (whether LTE-only, GSM/UMTS-capable, and/or
- * CDMA2000-capable) also typically support three or more.
+ * POTS and CDMA2000-only devices support one active bearer, while GSM/UMTS
+ * and LTE/5GNR capable devices (including 3GPP+3GPP3 multimode devices) may support
+ * one or more active bearers, depending on the amount of physical ports exposed
+ * by the device.
+ *
+ * Returns: the maximum number of active packet data bearers.
*
- * Returns: the maximum number of defined packet data bearers.
+ * Since: 1.0
*/
guint
-mm_modem_get_max_bearers (MMModem *self)
+mm_modem_get_max_active_bearers (MMModem *self)
{
g_return_val_if_fail (MM_IS_MODEM (self), 0);
- return mm_gdbus_modem_get_max_bearers (MM_GDBUS_MODEM (self));
+ return mm_gdbus_modem_get_max_active_bearers (MM_GDBUS_MODEM (self));
}
/*****************************************************************************/
/**
- * mm_modem_get_max_active_bearers:
+ * mm_modem_get_max_active_multiplexed_bearers:
* @self: a #MMModem.
*
- * Gets the maximum number of active packet data bearers this #MMModem supports.
+ * Gets the maximum number of active packet data bearers this #MMModem supports
+ * after enabling multiplexing support on one single network interface.
*
- * POTS and CDMA2000-only devices support one active bearer, while GSM/UMTS
- * and LTE-capable devices (including LTE/CDMA devices) typically support
- * at least two active bearers.
+ * Returns: the maximum number of active packet data bearers, or 0 if
+ * multiplexing is not supported.
*
- * Returns: the maximum number of defined packet data bearers.
+ * Since: 1.18
*/
guint
-mm_modem_get_max_active_bearers (MMModem *self)
+mm_modem_get_max_active_multiplexed_bearers (MMModem *self)
{
g_return_val_if_fail (MM_IS_MODEM (self), 0);
- return mm_gdbus_modem_get_max_active_bearers (MM_GDBUS_MODEM (self));
+ return mm_gdbus_modem_get_max_active_multiplexed_bearers (MM_GDBUS_MODEM (self));
}
/*****************************************************************************/
@@ -346,12 +343,15 @@ mm_modem_get_max_active_bearers (MMModem *self)
*
* Gets the DBus paths of the #MMBearer handled in this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_bearer_paths() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_bearer_paths() if on another thread.</warning>
*
- * Returns: (transfer none): The DBus paths of the #MMBearer handled in this #MMModem, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The DBus paths of the #MMBearer handled in this
+ * #MMModem, or %NULL if none available. Do not free the returned value, it
+ * belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar * const *
mm_modem_get_bearer_paths (MMModem *self)
@@ -367,7 +367,11 @@ mm_modem_get_bearer_paths (MMModem *self)
*
* Gets a copy of the DBus paths of the #MMBearer handled in this #MMModem.
*
- * Returns: (transfer full): The DBus paths of the #MMBearer handled in this #MMModem, or %NULL if none available. The returned value should be freed with g_strfreev().
+ * Returns: (transfer full): The DBus paths of the #MMBearer handled in this
+ * #MMModem, or %NULL if none available. The returned value should be freed
+ * with g_strfreev().
+ *
+ * Since: 1.0
*/
gchar **
mm_modem_dup_bearer_paths (MMModem *self)
@@ -385,12 +389,14 @@ mm_modem_dup_bearer_paths (MMModem *self)
*
* Gets the equipment manufacturer, as reported by this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_manufacturer() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_manufacturer() if on another thread.</warning>
*
- * Returns: (transfer none): The equipment manufacturer, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The equipment manufacturer, or %NULL if none
+ * available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_get_manufacturer (MMModem *self)
@@ -407,7 +413,10 @@ mm_modem_get_manufacturer (MMModem *self)
*
* Gets a copy of the equipment manufacturer, as reported by this #MMModem.
*
- * Returns: (transfer full): The equipment manufacturer, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The equipment manufacturer, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_manufacturer (MMModem *self)
@@ -426,12 +435,14 @@ mm_modem_dup_manufacturer (MMModem *self)
*
* Gets the equipment model, as reported by this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_model() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_model() if on another thread.</warning>
*
- * Returns: (transfer none): The equipment model, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The equipment model, or %NULL if none available.
+ * Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_get_model (MMModem *self)
@@ -448,7 +459,10 @@ mm_modem_get_model (MMModem *self)
*
* Gets a copy of the equipment model, as reported by this #MMModem.
*
- * Returns: (transfer full): The equipment model, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The equipment model, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_model (MMModem *self)
@@ -467,12 +481,14 @@ mm_modem_dup_model (MMModem *self)
*
* Gets the equipment revision, as reported by this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_revision() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_revision() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The equipment revision, or %NULL if none available.
+ * Do not free the returned value, it belongs to @self.
*
- * Returns: (transfer none): The equipment revision, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Since: 1.0
*/
const gchar *
mm_modem_get_revision (MMModem *self)
@@ -489,7 +505,10 @@ mm_modem_get_revision (MMModem *self)
*
* Gets a copy of the equipment revision, as reported by this #MMModem.
*
- * Returns: (transfer full): The equipment revision, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The equipment revision, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_revision (MMModem *self)
@@ -503,6 +522,148 @@ mm_modem_dup_revision (MMModem *self)
/*****************************************************************************/
/**
+ * mm_modem_get_carrier_configuration:
+ * @self: A #MMModem.
+ *
+ * Gets the carrier-specific configuration (MCFG) in use, as reported by this
+ * #MMModem.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_carrier_configuration() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The carrier configuration, or %NULL if none
+ * available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.12
+ */
+const gchar *
+mm_modem_get_carrier_configuration (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_modem_get_carrier_configuration (MM_GDBUS_MODEM (self)));
+}
+
+/**
+ * mm_modem_dup_carrier_configuration:
+ * @self: A #MMModem.
+ *
+ * Gets a copy of the carrier-specific configuration (MCFG) in use, as reported
+ * by this #MMModem.
+ *
+ * Returns: (transfer full): The carrier configuration, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.12
+ */
+gchar *
+mm_modem_dup_carrier_configuration (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_modem_dup_carrier_configuration (MM_GDBUS_MODEM (self)));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_get_carrier_configuration_revision:
+ * @self: A #MMModem.
+ *
+ * Gets the carrier-specific configuration revision in use, as reported by this
+ * #MMModem.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_carrier_configuration() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The carrier configuration revision, or %NULL if
+ * none available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.12
+ */
+const gchar *
+mm_modem_get_carrier_configuration_revision (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_modem_get_carrier_configuration_revision (MM_GDBUS_MODEM (self)));
+}
+
+/**
+ * mm_modem_dup_carrier_configuration_revision:
+ * @self: A #MMModem.
+ *
+ * Gets a copy of the carrier-specific configuration revision in use, as
+ * reported by this #MMModem.
+ *
+ * Returns: (transfer full): The carrier configuration revision, or %NULL if
+ * none available. The returned value should be freed with g_free().
+ *
+ * Since: 1.12
+ */
+gchar *
+mm_modem_dup_carrier_configuration_revision (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_modem_dup_carrier_configuration_revision (MM_GDBUS_MODEM (self)));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_get_hardware_revision:
+ * @self: A #MMModem.
+ *
+ * Gets the equipment hardware revision, as reported by this #MMModem.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_hardware_revision() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The equipment hardware revision, or %NULL if none
+ * available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.8
+ */
+const gchar *
+mm_modem_get_hardware_revision (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_modem_get_hardware_revision (MM_GDBUS_MODEM (self)));
+}
+
+/**
+ * mm_modem_dup_hardware_revision:
+ * @self: A #MMModem.
+ *
+ * Gets a copy of the equipment hardware revision, as reported by this #MMModem.
+ *
+ * Returns: (transfer full): The equipment hardware revision, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.8
+ */
+gchar *
+mm_modem_dup_hardware_revision (MMModem *self)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_modem_dup_hardware_revision (MM_GDBUS_MODEM (self)));
+}
+
+/*****************************************************************************/
+
+/**
* mm_modem_get_device_identifier:
* @self: A #MMModem.
*
@@ -517,12 +678,14 @@ mm_modem_dup_revision (MMModem *self)
* This is not the device's IMEI or ESN since those may not be available
* before unlocking the device via a PIN.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_device_identifier() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_device_identifier() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The device identifier, or %NULL if none available.
+ * Do not free the returned value, it belongs to @self.
*
- * Returns: (transfer none): The device identifier, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Since: 1.0
*/
const gchar *
mm_modem_get_device_identifier (MMModem *self)
@@ -537,8 +700,9 @@ mm_modem_get_device_identifier (MMModem *self)
* mm_modem_dup_device_identifier:
* @self: A #MMModem.
*
- * Gets a copy of a best-effort device identifier based on various device information
- * like model name, firmware revision, USB/PCI/PCMCIA IDs, and other properties.
+ * Gets a copy of a best-effort device identifier based on various device
+ * information like model name, firmware revision, USB/PCI/PCMCIA IDs, and other
+ * properties.
*
* This ID is not guaranteed to be unique and may be shared between
* identical devices with the same firmware, but is intended to be "unique
@@ -548,7 +712,10 @@ mm_modem_get_device_identifier (MMModem *self)
* This is not the device's IMEI or ESN since those may not be available
* before unlocking the device via a PIN.
*
- * Returns: (transfer full): The device identifier, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The device identifier, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_device_identifier (MMModem *self)
@@ -568,12 +735,14 @@ mm_modem_dup_device_identifier (MMModem *self)
* Gets the physical modem device reference (ie, USB, PCI, PCMCIA device), which
* may be dependent upon the operating system.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_device() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_device() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The device, or %NULL if none available. Do not free
+ * the returned value, it belongs to @self.
*
- * Returns: (transfer none): The device, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Since: 1.0
*/
const gchar *
mm_modem_get_device (MMModem *self)
@@ -588,10 +757,13 @@ mm_modem_get_device (MMModem *self)
* mm_modem_dup_device:
* @self: A #MMModem.
*
- * Gets a copy of the physical modem device reference (ie, USB, PCI, PCMCIA device), which
- * may be dependent upon the operating system.
+ * Gets a copy of the physical modem device reference (ie, USB, PCI, PCMCIA
+ * device), which may be dependent upon the operating system.
+ *
+ * Returns: (transfer full): The device, or %NULL if none available. The
+ * returned value should be freed with g_free().
*
- * Returns: (transfer full): The device, or %NULL if none available. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_modem_dup_device (MMModem *self)
@@ -608,15 +780,17 @@ mm_modem_dup_device (MMModem *self)
* mm_modem_get_drivers:
* @self: A #MMModem.
*
- * Gets the Operating System device drivers handling communication with the modem
- * hardware.
+ * Gets the Operating System device drivers handling communication with the
+ * modem hardware.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_drivers() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_drivers() if on another thread.</warning>
*
- * Returns: (transfer none): The drivers, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The drivers, or %NULL if none available. Do not
+ * free the returned value, it belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar * const *
mm_modem_get_drivers (MMModem *self)
@@ -630,10 +804,13 @@ mm_modem_get_drivers (MMModem *self)
* mm_modem_dup_drivers:
* @self: A #MMModem.
*
- * Gets a copy of the Operating System device driver handling communication with the modem
- * hardware.
+ * Gets a copy of the Operating System device driver handling communication with
+ * the modem hardware.
+ *
+ * Returns: (transfer full): The drivers, or %NULL if none available. The
+ * returned value should be freed with g_strfreev().
*
- * Returns: (transfer full): The drivers, or %NULL if none available. The returned value should be freed with g_strfreev().
+ * Since: 1.0
*/
gchar **
mm_modem_dup_drivers (MMModem *self)
@@ -651,12 +828,14 @@ mm_modem_dup_drivers (MMModem *self)
*
* Gets the name of the plugin handling this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_plugin() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_plugin() if on another thread.</warning>
*
- * Returns: (transfer none): The name of the plugin, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The name of the plugin, or %NULL if none
+ *available. Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_get_plugin (MMModem *self)
@@ -673,7 +852,10 @@ mm_modem_get_plugin (MMModem *self)
*
* Gets a copy of the name of the plugin handling this #MMModem.
*
- * Returns: (transfer full): The name of the plugin, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The name of the plugin, or %NULL if none available.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_plugin (MMModem *self)
@@ -692,12 +874,14 @@ mm_modem_dup_plugin (MMModem *self)
*
* Gets the name of the primary port controlling this #MMModem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_primary_port() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_primary_port() if on another thread.</warning>
*
- * Returns: (transfer none): The name of the primary port. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): The name of the primary port. Do not free the
+ * returned value, it belongs to @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_modem_get_primary_port (MMModem *self)
@@ -714,7 +898,10 @@ mm_modem_get_primary_port (MMModem *self)
*
* Gets a copy of the name of the primary port controlling this #MMModem.
*
- * Returns: (transfer full): The name of the primary port. The returned value should be freed with g_free().
+ * Returns: (transfer full): The name of the primary port. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_primary_port (MMModem *self)
@@ -727,130 +914,41 @@ mm_modem_dup_primary_port (MMModem *self)
/*****************************************************************************/
-static void
-ports_updated (MMModem *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->ports_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->ports)
- g_array_unref (self->priv->ports);
-
- dictionary = mm_gdbus_modem_get_ports (MM_GDBUS_MODEM (self));
- self->priv->ports = (dictionary ?
- mm_common_ports_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->ports_mutex);
-}
-
-static gboolean
-ensure_internal_ports (MMModem *self,
- MMModemPortInfo **dup_ports,
- guint *dup_ports_n)
-{
- gboolean ret;
- guint i;
-
- g_mutex_lock (&self->priv->ports_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->ports_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_dup_ports (MM_GDBUS_MODEM (self));
- if (dictionary) {
- self->priv->ports = mm_common_ports_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->ports_id =
- g_signal_connect (self,
- "notify::ports",
- G_CALLBACK (ports_updated),
- NULL);
- }
-
- if (!self->priv->ports)
- ret = FALSE;
- else {
- ret = TRUE;
-
- if (dup_ports && dup_ports_n) {
- *dup_ports_n = self->priv->ports->len;
- if (self->priv->ports->len > 0) {
- *dup_ports = g_malloc (sizeof (MMModemPortInfo) * self->priv->ports->len);
-
- /* Deep-copy the array */
- for (i = 0; i < self->priv->ports->len; i++) {
- MMModemPortInfo *dst = &(*dup_ports)[i];
- MMModemPortInfo *src = &g_array_index (self->priv->ports, MMModemPortInfo, i);
-
- dst->name = g_strdup (src->name);
- dst->type = src->type;
- }
- } else
- *dup_ports = NULL;
- }
- }
- }
- g_mutex_unlock (&self->priv->ports_mutex);
-
- return ret;
-}
-
/**
* mm_modem_peek_ports:
* @self: A #MMModem.
- * @ports: (out) (array length=n_ports) (transfer none): Return location for the array of #MMModemPortInfo values. Do not free the returned value, it is owned by @self.
+ * @ports: (out) (array length=n_ports) (transfer none): Return location for the
+ * array of #MMModemPortInfo values. Do not free the returned value, it is
+ * owned by @self.
* @n_ports: (out): Return location for the number of values in @ports.
*
* Gets the list of ports in the modem.
*
* Returns: %TRUE if @ports and @n_ports are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_peek_ports (MMModem *self,
- const MMModemPortInfo **ports,
- guint *n_ports)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (ports != NULL, FALSE);
- g_return_val_if_fail (n_ports != NULL, FALSE);
-
- if (!ensure_internal_ports (self, NULL, NULL))
- return FALSE;
-
- *n_ports = self->priv->ports->len;
- *ports = (MMModemPortInfo *)self->priv->ports->data;
- return TRUE;
-}
/**
* mm_modem_get_ports:
* @self: A #MMModem.
- * @ports: (out) (array length=n_ports): Return location for the array of #MMModemPortInfo values. The returned array should be freed with mm_modem_port_info_array_free() when no longer needed.
+ * @ports: (out) (array length=n_ports): Return location for the array of
+ * #MMModemPortInfo values. The returned array should be freed with
+ * mm_modem_port_info_array_free() when no longer needed.
* @n_ports: (out): Return location for the number of values in @ports.
*
* Gets the list of ports in the modem.
*
* Returns: %TRUE if @ports and @n_ports are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_get_ports (MMModem *self,
- MMModemPortInfo **ports,
- guint *n_ports)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (ports != NULL, FALSE);
- g_return_val_if_fail (n_ports != NULL, FALSE);
- return ensure_internal_ports (self, ports, n_ports);
-}
+PROPERTY_ARRAY_DEFINE_DEEP (ports,
+ Modem, modem, MODEM,
+ MMModemPortInfo,
+ mm_common_ports_variant_to_garray,
+ mm_common_ports_garray_to_array)
/*****************************************************************************/
@@ -863,12 +961,14 @@ mm_modem_get_ports (MMModem *self,
* This will be the IMEI number for GSM devices and the hex-format ESN/MEID
* for CDMA devices.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_plugin() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_equipment_identifier() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The equipment identifier, or %NULL if none
+ * available. Do not free the returned value, it belongs to @self.
*
- * Returns: (transfer none): The equipment identifier, or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Since: 1.0
*/
const gchar *
mm_modem_get_equipment_identifier (MMModem *self)
@@ -888,7 +988,10 @@ mm_modem_get_equipment_identifier (MMModem *self)
* This will be the IMEI number for GSM devices and the hex-format ESN/MEID
* for CDMA devices.
*
- * Returns: (transfer full): The equipment identifier, or %NULL if none available. The returned value should be freed with g_free().
+ * Returns: (transfer full): The equipment identifier, or %NULL if none
+ * available. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_dup_equipment_identifier (MMModem *self)
@@ -908,12 +1011,14 @@ mm_modem_dup_equipment_identifier (MMModem *self)
* Gets the list of numbers (e.g. MSISDN in 3GPP) being currently handled by
* this modem.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_dup_own_numbers() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_dup_own_numbers() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The list of own numbers or %NULL if none available.
+ * Do not free the returned value, it belongs to @self.
*
- * Returns: (transfer none): The list of own numbers or %NULL if none available. Do not free the returned value, it belongs to @self.
+ * Since: 1.0
*/
const gchar *const *
mm_modem_get_own_numbers (MMModem *self)
@@ -934,7 +1039,10 @@ mm_modem_get_own_numbers (MMModem *self)
* Gets a copy of the list of numbers (e.g. MSISDN in 3GPP) being currently
* handled by this modem.
*
- * Returns: (transfer full): The list of own numbers or %NULL if none is available. The returned value should be freed with g_strfreev().
+ * Returns: (transfer full): The list of own numbers or %NULL if none is
+ * available. The returned value should be freed with g_strfreev().
+ *
+ * Since: 1.0
*/
gchar **
mm_modem_dup_own_numbers (MMModem *self)
@@ -960,6 +1068,8 @@ mm_modem_dup_own_numbers (MMModem *self)
* Gets current lock state of the #MMModem.
*
* Returns: A #MMModemLock value, specifying the current lock state.
+ *
+ * Since: 1.0
*/
MMModemLock
mm_modem_get_unlock_required (MMModem *self)
@@ -971,81 +1081,25 @@ mm_modem_get_unlock_required (MMModem *self)
/*****************************************************************************/
-static void
-unlock_retries_updated (MMModem *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->unlock_retries_mutex);
- {
- GVariant *dictionary;
-
- g_clear_object (&self->priv->unlock_retries);
-
- /* TODO: update existing object instead of re-creating? */
- dictionary = mm_gdbus_modem_get_unlock_retries (MM_GDBUS_MODEM (self));
- if (dictionary)
- self->priv->unlock_retries = mm_unlock_retries_new_from_dictionary (dictionary);
- }
- g_mutex_unlock (&self->priv->unlock_retries_mutex);
-}
-
-static void
-ensure_internal_unlock_retries (MMModem *self,
- MMUnlockRetries **dup)
-{
- g_mutex_lock (&self->priv->unlock_retries_mutex);
- {
- /* If this is the first time ever asking for the object, setup the
- * update listener and the initial object, if any. */
- if (!self->priv->unlock_retries_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_dup_unlock_retries (MM_GDBUS_MODEM (self));
- if (dictionary) {
- self->priv->unlock_retries = mm_unlock_retries_new_from_dictionary (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->unlock_retries_id =
- g_signal_connect (self,
- "notify::unlock-retries",
- G_CALLBACK (unlock_retries_updated),
- NULL);
- }
-
- if (dup && self->priv->unlock_retries)
- *dup = g_object_ref (self->priv->unlock_retries);
- }
- g_mutex_unlock (&self->priv->unlock_retries_mutex);
-}
-
/**
* mm_modem_get_unlock_retries:
* @self: A #MMModem.
*
* Gets a #MMUnlockRetries object, which provides, for each
* <link linkend="MMModemLock">MMModemLock</link> handled by the modem, the
- * number of PIN tries remaining before the code becomes blocked (requiring a PUK)
- * or permanently blocked.
+ * number of PIN tries remaining before the code becomes blocked (requiring a
+ * PUK) or permanently blocked.
*
* <warning>The values reported by @self are not updated when the values in the
* interface change. Instead, the client is expected to call
* mm_modem_get_unlock_retries() again to get a new #MMUnlockRetries with the
* new values.</warning>
*
- * Returns: (transfer full): A #MMUnlockRetries that must be freed with g_object_unref() or %NULL if unknown.
+ * Returns: (transfer full): A #MMUnlockRetries that must be freed with
+ * g_object_unref() or %NULL if unknown.
+ *
+ * Since: 1.0
*/
-MMUnlockRetries *
-mm_modem_get_unlock_retries (MMModem *self)
-{
- MMUnlockRetries *unlock_retries = NULL;
-
- g_return_val_if_fail (MM_IS_MODEM (self), NULL);
-
- ensure_internal_unlock_retries (self, &unlock_retries);
- return unlock_retries;
-}
/**
* mm_modem_peek_unlock_retries:
@@ -1053,24 +1107,23 @@ mm_modem_get_unlock_retries (MMModem *self)
*
* Gets a #MMUnlockRetries object, which provides, for each
* <link linkend="MMModemLock">MMModemLock</link> handled by the modem, the
- * number of PIN tries remaining before the code becomes blocked (requiring a PUK)
- * or permanently blocked.
+ * number of PIN tries remaining before the code becomes blocked (requiring a
+ * PUK) or permanently blocked.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_modem_get_unlock_retries() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_modem_get_unlock_retries() if on another thread.</warning>
*
- * Returns: (transfer none): A #MMUnlockRetries. Do not free the returned value, it belongs to @self.
+ * Returns: (transfer none): A #MMUnlockRetries. Do not free the returned value,
+ * it belongs to @self.
+ *
+ * Since: 1.0
*/
-MMUnlockRetries *
-mm_modem_peek_unlock_retries (MMModem *self)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- ensure_internal_unlock_retries (self, NULL);
- return self->priv->unlock_retries;
-}
+PROPERTY_OBJECT_DEFINE (unlock_retries,
+ Modem, modem, MODEM,
+ MMUnlockRetries,
+ mm_unlock_retries_new_from_dictionary)
/*****************************************************************************/
@@ -1081,6 +1134,8 @@ mm_modem_peek_unlock_retries (MMModem *self)
* Gets the overall state of the #MMModem.
*
* Returns: A #MMModemState value.
+ *
+ * Since: 1.0
*/
MMModemState
mm_modem_get_state (MMModem *self)
@@ -1099,6 +1154,8 @@ mm_modem_get_state (MMModem *self)
* Gets the reason specifying why the modem is in #MM_MODEM_STATE_FAILED state.
*
* Returns: A #MMModemStateFailedReason value.
+ *
+ * Since: 1.0
*/
MMModemStateFailedReason
mm_modem_get_state_failed_reason (MMModem *self)
@@ -1117,6 +1174,8 @@ mm_modem_get_state_failed_reason (MMModem *self)
* Gets the power state of the #MMModem.
*
* Returns: A #MMModemPowerState value.
+ *
+ * Since: 1.0
*/
MMModemPowerState
mm_modem_get_power_state (MMModem *self)
@@ -1129,13 +1188,15 @@ mm_modem_get_power_state (MMModem *self)
/*****************************************************************************/
/**
- * mm_modem_get_access_technology:
+ * mm_modem_get_access_technologies:
* @self: A #MMModem.
*
- * Gets the current network access technology used by the #MMModem to communicate
- * with the network.
+ * Gets the current network access technology used by the #MMModem to
+ * communicate with the network.
*
* Returns: A ##MMModemAccessTechnology value.
+ *
+ * Since: 1.0
*/
MMModemAccessTechnology
mm_modem_get_access_technologies (MMModem *self)
@@ -1150,14 +1211,17 @@ mm_modem_get_access_technologies (MMModem *self)
/**
* mm_modem_get_signal_quality:
* @self: A #MMModem.
- * @recent: (out): Return location for the flag specifying if the signal quality value was recent or not.
+ * @recent: (out): Return location for the flag specifying if the signal quality
+ * value was recent or not.
*
- * Gets the signal quality value in percent (0 - 100) of the dominant access technology
- * the #MMModem is using to communicate with the network.
+ * Gets the signal quality value in percent (0 - 100) of the dominant access
+ * technology the #MMModem is using to communicate with the network.
*
* Always 0 for POTS devices.
*
* Returns: The signal quality.
+ *
+ * Since: 1.0
*/
guint
mm_modem_get_signal_quality (MMModem *self,
@@ -1185,121 +1249,41 @@ mm_modem_get_signal_quality (MMModem *self,
/*****************************************************************************/
-static void
-supported_modes_updated (MMModem *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->supported_modes_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->supported_modes)
- g_array_unref (self->priv->supported_modes);
-
- dictionary = mm_gdbus_modem_get_supported_modes (MM_GDBUS_MODEM (self));
- self->priv->supported_modes = (dictionary ?
- mm_common_mode_combinations_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->supported_modes_mutex);
-}
-
-static gboolean
-ensure_internal_supported_modes (MMModem *self,
- MMModemModeCombination **dup_modes,
- guint *dup_modes_n)
-{
- gboolean ret;
-
- g_mutex_lock (&self->priv->supported_modes_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->supported_modes_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_dup_supported_modes (MM_GDBUS_MODEM (self));
- if (dictionary) {
- self->priv->supported_modes = mm_common_mode_combinations_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->supported_modes_id =
- g_signal_connect (self,
- "notify::supported-modes",
- G_CALLBACK (supported_modes_updated),
- NULL);
- }
-
- if (!self->priv->supported_modes)
- ret = FALSE;
- else {
- ret = TRUE;
-
- if (dup_modes && dup_modes_n) {
- *dup_modes_n = self->priv->supported_modes->len;
- if (self->priv->supported_modes->len > 0) {
- *dup_modes = g_malloc (sizeof (MMModemModeCombination) * self->priv->supported_modes->len);
- memcpy (*dup_modes, self->priv->supported_modes->data, sizeof (MMModemModeCombination) * self->priv->supported_modes->len);
- } else
- *dup_modes = NULL;
- }
- }
- }
- g_mutex_unlock (&self->priv->supported_modes_mutex);
-
- return ret;
-}
-
/**
* mm_modem_get_supported_modes:
* @self: A #MMModem.
- * @modes: (out) (array length=n_modes): Return location for the array of #MMModemModeCombination structs. The returned array should be freed with g_free() when no longer needed.
+ * @modes: (out) (array length=n_modes): Return location for the array of
+ * #MMModemModeCombination structs. The returned array should be freed with
+ * g_free() when no longer needed.
* @n_modes: (out): Return location for the number of values in @modes.
*
* Gets the list of supported mode combinations.
*
* Returns: %TRUE if @modes and @n_modes are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_get_supported_modes (MMModem *self,
- MMModemModeCombination **modes,
- guint *n_modes)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (modes != NULL, FALSE);
- g_return_val_if_fail (n_modes != NULL, FALSE);
- return ensure_internal_supported_modes (self, modes, n_modes);
-}
/**
* mm_modem_peek_supported_modes:
* @self: A #MMModem.
- * @modes: (out) (array length=n_modes): Return location for the array of #MMModemModeCombination values. Do not free the returned array, it is owned by @self.
+ * @modes: (out) (array length=n_modes): Return location for the array of
+ * #MMModemModeCombination values. Do not free the returned array, it is owned
+ * by @self.
* @n_modes: (out): Return location for the number of values in @modes.
*
* Gets the list of supported mode combinations.
*
* Returns: %TRUE if @modes and @n_modes are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_peek_supported_modes (MMModem *self,
- const MMModemModeCombination **modes,
- guint *n_modes)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (modes != NULL, FALSE);
- g_return_val_if_fail (n_modes != NULL, FALSE);
- if (!ensure_internal_supported_modes (self, NULL, NULL))
- return FALSE;
-
- *n_modes = self->priv->supported_modes->len;
- *modes = (MMModemModeCombination *)self->priv->supported_modes->data;
- return TRUE;
-}
+PROPERTY_ARRAY_DEFINE (supported_modes,
+ Modem, modem, MODEM,
+ MMModemModeCombination,
+ mm_common_mode_combinations_variant_to_garray)
/*****************************************************************************/
@@ -1314,6 +1298,8 @@ mm_modem_peek_supported_modes (MMModem *self,
* well as the preferred one, if any.
*
* Returns: %TRUE if @allowed and @preferred are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_get_current_modes (MMModem *self,
@@ -1341,249 +1327,87 @@ mm_modem_get_current_modes (MMModem *self,
/*****************************************************************************/
-static void
-supported_bands_updated (MMModem *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->supported_bands_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->supported_bands)
- g_array_unref (self->priv->supported_bands);
-
- dictionary = mm_gdbus_modem_get_supported_bands (MM_GDBUS_MODEM (self));
- self->priv->supported_bands = (dictionary ?
- mm_common_bands_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->supported_bands_mutex);
-}
-
-static gboolean
-ensure_internal_supported_bands (MMModem *self,
- MMModemBand **dup_bands,
- guint *dup_bands_n)
-{
- gboolean ret;
-
- g_mutex_lock (&self->priv->supported_bands_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->supported_bands_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_dup_supported_bands (MM_GDBUS_MODEM (self));
- if (dictionary) {
- self->priv->supported_bands = mm_common_bands_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->supported_bands_id =
- g_signal_connect (self,
- "notify::supported-bands",
- G_CALLBACK (supported_bands_updated),
- NULL);
- }
-
- if (!self->priv->supported_bands)
- ret = FALSE;
- else {
- ret = TRUE;
-
- if (dup_bands && dup_bands_n) {
- *dup_bands_n = self->priv->supported_bands->len;
- if (self->priv->supported_bands->len > 0) {
- *dup_bands = g_malloc (sizeof (MMModemBand) * self->priv->supported_bands->len);
- memcpy (*dup_bands, self->priv->supported_bands->data, sizeof (MMModemBand) * self->priv->supported_bands->len);
- } else
- *dup_bands = NULL;
- }
- }
- }
- g_mutex_unlock (&self->priv->supported_bands_mutex);
-
- return ret;
-}
-
/**
* mm_modem_get_supported_bands:
* @self: A #MMModem.
- * @bands: (out) (array length=n_bands): Return location for the array of #MMModemBand values. The returned array should be freed with g_free() when no longer needed.
+ * @bands: (out) (array length=n_bands): Return location for the array of
+ * #MMModemBand values. The returned array should be freed with g_free() when
+ * no longer needed.
* @n_bands: (out): Return location for the number of values in @bands.
*
- * Gets the list of radio frequency and technology bands supported by the #MMModem.
+ * Gets the list of radio frequency and technology bands supported by the
+ * #MMModem.
*
* For POTS devices, only #MM_MODEM_BAND_ANY will be returned in @bands.
*
* Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_get_supported_bands (MMModem *self,
- MMModemBand **bands,
- guint *n_bands)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (bands != NULL, FALSE);
- g_return_val_if_fail (n_bands != NULL, FALSE);
-
- return ensure_internal_supported_bands (self, bands, n_bands);
-}
/**
* mm_modem_peek_supported_bands:
* @self: A #MMModem.
- * @bands: (out) (array length=n_bands): Return location for the array of #MMModemBand values. Do not free the returned array, it is owned by @self.
+ * @bands: (out) (array length=n_bands): Return location for the array of
+ * #MMModemBand values. Do not free the returned array, it is owned by @self.
* @n_bands: (out): Return location for the number of values in @bands.
*
- * Gets the list of radio frequency and technology bands supported by the #MMModem.
+ * Gets the list of radio frequency and technology bands supported by the
+ * #MMModem.
*
* For POTS devices, only #MM_MODEM_BAND_ANY will be returned in @bands.
*
* Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_peek_supported_bands (MMModem *self,
- const MMModemBand **bands,
- guint *n_bands)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (bands != NULL, FALSE);
- g_return_val_if_fail (n_bands != NULL, FALSE);
- if (!ensure_internal_supported_bands (self, NULL, NULL))
- return FALSE;
-
- *n_bands = self->priv->supported_bands->len;
- *bands = (MMModemBand *)self->priv->supported_bands->data;
- return TRUE;
-}
+PROPERTY_ARRAY_DEFINE (supported_bands,
+ Modem, modem, MODEM,
+ MMModemBand,
+ mm_common_bands_variant_to_garray)
/*****************************************************************************/
-static void
-current_bands_updated (MMModem *self,
- GParamSpec *pspec)
-{
- g_mutex_lock (&self->priv->current_bands_mutex);
- {
- GVariant *dictionary;
-
- if (self->priv->current_bands)
- g_array_unref (self->priv->current_bands);
-
- dictionary = mm_gdbus_modem_get_current_bands (MM_GDBUS_MODEM (self));
- self->priv->current_bands = (dictionary ?
- mm_common_bands_variant_to_garray (dictionary) :
- NULL);
- }
- g_mutex_unlock (&self->priv->current_bands_mutex);
-}
-
-static gboolean
-ensure_internal_current_bands (MMModem *self,
- MMModemBand **dup_bands,
- guint *dup_bands_n)
-{
- gboolean ret;
-
- g_mutex_lock (&self->priv->current_bands_mutex);
- {
- /* If this is the first time ever asking for the array, setup the
- * update listener and the initial array, if any. */
- if (!self->priv->current_bands_id) {
- GVariant *dictionary;
-
- dictionary = mm_gdbus_modem_dup_current_bands (MM_GDBUS_MODEM (self));
- if (dictionary) {
- self->priv->current_bands = mm_common_bands_variant_to_garray (dictionary);
- g_variant_unref (dictionary);
- }
-
- /* No need to clear this signal connection when freeing self */
- self->priv->current_bands_id =
- g_signal_connect (self,
- "notify::current-bands",
- G_CALLBACK (current_bands_updated),
- NULL);
- }
-
- if (!self->priv->current_bands)
- ret = FALSE;
- else {
- ret = TRUE;
-
- if (dup_bands && dup_bands_n) {
- *dup_bands_n = self->priv->current_bands->len;
- if (self->priv->current_bands->len > 0) {
- *dup_bands = g_malloc (sizeof (MMModemBand) * self->priv->current_bands->len);
- memcpy (*dup_bands, self->priv->current_bands->data, sizeof (MMModemBand) * self->priv->current_bands->len);
- } else
- *dup_bands = NULL;
- }
- }
- }
- g_mutex_unlock (&self->priv->current_bands_mutex);
-
- return ret;
-}
-
/**
* mm_modem_get_current_bands:
* @self: A #MMModem.
- * @bands: (out) (array length=n_bands): Return location for the array of #MMModemBand values. The returned array should be freed with g_free() when no longer needed.
+ * @bands: (out) (array length=n_bands): Return location for the array of
+ * #MMModemBand values. The returned array should be freed with g_free() when
+ * no longer needed.
* @n_bands: (out): Return location for the number of values in @bands.
*
- * Gets the list of radio frequency and technology bands the #MMModem is currently
- * using when connecting to a network.
+ * Gets the list of radio frequency and technology bands the #MMModem is
+ * currently using when connecting to a network.
*
* For POTS devices, only the #MM_MODEM_BAND_ANY band is supported.
*
* Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_get_current_bands (MMModem *self,
- MMModemBand **bands,
- guint *n_bands)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (bands != NULL, FALSE);
- g_return_val_if_fail (n_bands != NULL, FALSE);
-
- return ensure_internal_current_bands (self, bands, n_bands);
-}
/**
* mm_modem_peek_current_bands:
* @self: A #MMModem.
- * @bands: (out) (array length=n_bands): Return location for the array of #MMModemBand values. Do not free the returned value, it is owned by @self.
+ * @bands: (out) (array length=n_bands): Return location for the array of
+ * #MMModemBand values. Do not free the returned value, it is owned by @self.
* @n_bands: (out): Return location for the number of values in @bands.
*
- * Gets the list of radio frequency and technology bands the #MMModem is currently
- * using when connecting to a network.
+ * Gets the list of radio frequency and technology bands the #MMModem is
+ * currently using when connecting to a network.
*
* For POTS devices, only the #MM_MODEM_BAND_ANY band is supported.
*
* Returns: %TRUE if @bands and @n_bands are set, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
-gboolean
-mm_modem_peek_current_bands (MMModem *self,
- const MMModemBand **bands,
- guint *n_bands)
-{
- g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
- g_return_val_if_fail (bands != NULL, FALSE);
- g_return_val_if_fail (n_bands != NULL, FALSE);
-
- if (!ensure_internal_current_bands (self, NULL, NULL))
- return FALSE;
- *n_bands = self->priv->current_bands->len;
- *bands = (MMModemBand *)self->priv->current_bands->data;
- return TRUE;
-}
+PROPERTY_ARRAY_DEFINE (current_bands,
+ Modem, modem, MODEM,
+ MMModemBand,
+ mm_common_bands_variant_to_garray)
/*****************************************************************************/
@@ -1594,6 +1418,8 @@ mm_modem_peek_current_bands (MMModem *self,
* Gets the list of supported IP families.
*
* Returns: A bitmask of #MMBearerIpFamily values.
+ *
+ * Since: 1.0
*/
MMBearerIpFamily
mm_modem_get_supported_ip_families (MMModem *self)
@@ -1608,12 +1434,15 @@ mm_modem_get_supported_ip_families (MMModem *self)
/**
* mm_modem_enable_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_enable().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_enable().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_enable().
*
* Returns: %TRUE if the modem was properly enabled, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_enable_finish (MMModem *self,
@@ -1629,17 +1458,23 @@ mm_modem_enable_finish (MMModem *self,
* mm_modem_enable:
* @self: A #MMModem.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously tries to enable the #MMModem. When enabled, the modem's radio is
- * powered on and data sessions, voice calls, location services, and Short Message
- * Service may be available.
+ * Asynchronously tries to enable the #MMModem. When enabled, the modem's radio
+ * is powered on and data sessions, voice calls, location services, and Short
+ * Message Service may be available.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_enable_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_enable_finish() to get the result of the operation.
+ * See mm_modem_enable_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_modem_enable_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_enable (MMModem *self,
@@ -1658,14 +1493,16 @@ mm_modem_enable (MMModem *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously tries to enable the #MMModem. When enabled, the modem's radio is
- * powered on and data sessions, voice calls, location services, and Short Message
- * Service may be available.
+ * Synchronously tries to enable the #MMModem. When enabled, the modem's radio
+ * is powered on and data sessions, voice calls, location services, and Short
+ * Message Service may be available.
*
- * The calling thread is blocked until a reply is received. See mm_modem_enable()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_enable() for the asynchronous version of this method.
*
* Returns: %TRUE if the modem was properly enabled, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_enable_sync (MMModem *self,
@@ -1682,12 +1519,15 @@ mm_modem_enable_sync (MMModem *self,
/**
* mm_modem_disable_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_disable().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_disable().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_disable().
*
* Returns: %TRUE if the modem was properly disabled, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_disable_finish (MMModem *self,
@@ -1703,16 +1543,22 @@ mm_modem_disable_finish (MMModem *self,
* mm_modem_disable:
* @self: A #MMModem.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously tries to disable the #MMModem. When disabled, the modem enters
* low-power state and no network-related operations are available.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_disable_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_disable_finish() to get the result of the operation.
+ *
+ * See mm_modem_disable_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_modem_disable_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_disable (MMModem *self,
@@ -1734,10 +1580,12 @@ mm_modem_disable (MMModem *self,
* Synchronously tries to disable the #MMModem. When disabled, the modem enters
* low-power state and no network-related operations are available.
*
- * The calling thread is blocked until a reply is received. See mm_modem_disable()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_disable() for the asynchronous version of this method.
*
* Returns: %TRUE if the modem was properly disabled, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_disable_sync (MMModem *self,
@@ -1752,9 +1600,6 @@ mm_modem_disable_sync (MMModem *self,
/*****************************************************************************/
typedef struct {
- MMModem *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
gchar **bearer_paths;
GList *bearer_objects;
guint i;
@@ -1763,102 +1608,103 @@ typedef struct {
static void
bearer_object_list_free (GList *list)
{
- g_list_free_full (list, (GDestroyNotify) g_object_unref);
+ g_list_free_full (list, g_object_unref);
}
static void
-list_bearers_context_complete_and_free (ListBearersContext *ctx)
+list_bearers_context_free (ListBearersContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
-
g_strfreev (ctx->bearer_paths);
bearer_object_list_free (ctx->bearer_objects);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_ref (ctx->self);
g_slice_free (ListBearersContext, ctx);
}
/**
* mm_modem_list_bearers_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_list_bearers().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_list_bearers().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_list_bearers().
*
- * Returns: (transfer full) (element-type ModemManager.Modem): The list of #MMBearer objects, or %NULL if either none found or if @error is set.
+ * Returns: (transfer full) (element-type ModemManager.Bearer): The list of
+ * #MMBearer objects, or %NULL if either none found or if @error is set.
+ *
+ * Since: 1.0
*/
GList *
mm_modem_list_bearers_finish (MMModem *self,
GAsyncResult *res,
GError **error)
{
- GList *list;
-
g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- list = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
- /* The list we got, including the objects within, is owned by the async result;
- * so we'll make sure we return a new list */
- g_list_foreach (list, (GFunc)g_object_ref, NULL);
- return g_list_copy (list);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void create_next_bearer (ListBearersContext *ctx);
+static void create_next_bearer (GTask *task);
static void
modem_list_bearers_build_object_ready (GDBusConnection *connection,
GAsyncResult *res,
- ListBearersContext *ctx)
+ GTask *task)
{
GObject *bearer;
GError *error = NULL;
GObject *source_object;
+ ListBearersContext *ctx;
source_object = g_async_result_get_source_object (res);
bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
g_object_unref (source_object);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- list_bearers_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Keep the object */
ctx->bearer_objects = g_list_prepend (ctx->bearer_objects, bearer);
/* If no more bearers, just end here. */
if (!ctx->bearer_paths[++ctx->i]) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- ctx->bearer_objects,
- (GDestroyNotify)bearer_object_list_free);
- ctx->bearer_objects = NULL;
- list_bearers_context_complete_and_free (ctx);
+ GList *bearer_objects;
+
+ bearer_objects = g_list_copy_deep (ctx->bearer_objects,
+ (GCopyFunc)g_object_ref,
+ NULL);
+ g_task_return_pointer (task,
+ bearer_objects,
+ (GDestroyNotify)bearer_object_list_free);
+ g_object_unref (task);
return;
}
/* Keep on creating next object */
- create_next_bearer (ctx);
+ create_next_bearer (task);
}
static void
-create_next_bearer (ListBearersContext *ctx)
+create_next_bearer (GTask *task)
{
+ MMModem *self;
+ ListBearersContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
g_async_initable_new_async (MM_TYPE_BEARER,
G_PRIORITY_DEFAULT,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)modem_list_bearers_build_object_ready,
- ctx,
+ task,
"g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"g-name", MM_DBUS_SERVICE,
- "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (ctx->self)),
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
"g-object-path", ctx->bearer_paths[ctx->i],
"g-interface-name", "org.freedesktop.ModemManager1.Bearer",
NULL);
@@ -1868,15 +1714,21 @@ create_next_bearer (ListBearersContext *ctx)
* mm_modem_list_bearers:
* @self: A #MMModem.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously lists the packet data bearers in the #MMModem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_list_bearers_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_list_bearers_finish() to get the result of the operation.
+ *
+ * See mm_modem_list_bearers_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_list_bearers_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_list_bearers (MMModem *self,
@@ -1885,31 +1737,28 @@ mm_modem_list_bearers (MMModem *self,
gpointer user_data)
{
ListBearersContext *ctx;
+ GTask *task;
g_return_if_fail (MM_IS_MODEM (self));
ctx = g_slice_new0 (ListBearersContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_modem_list_bearers);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
/* Read from the property, skip List() */
ctx->bearer_paths = mm_gdbus_modem_dup_bearers (MM_GDBUS_MODEM (self));
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)list_bearers_context_free);
+
/* If no bearers, just end here. */
if (!ctx->bearer_paths || !ctx->bearer_paths[0]) {
- g_simple_async_result_set_op_res_gpointer (ctx->result, NULL, NULL);
- list_bearers_context_complete_and_free (ctx);
+ g_task_return_pointer (task, NULL, NULL);
+ g_object_unref (task);
return;
}
/* Got list of paths. If at least one found, start creating objects for each */
ctx->i = 0;
- create_next_bearer (ctx);
+ create_next_bearer (task);
}
/**
@@ -1920,10 +1769,13 @@ mm_modem_list_bearers (MMModem *self,
*
* Synchronously lists the packet data bearers in the #MMModem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_list_bearers()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_list_bearers() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full) (element-type ModemManager.Bearer): The list of
+ * #MMBearer objects, or %NULL if either none found or if @error is set.
*
- * Returns: (transfer full) (element-type ModemManager.Modem): The list of #MMBearer objects, or %NULL if either none found or if @error is set.
+ * Since: 1.0
*/
GList *
mm_modem_list_bearers_sync (MMModem *self,
@@ -1971,30 +1823,19 @@ mm_modem_list_bearers_sync (MMModem *self,
/*****************************************************************************/
-typedef struct {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
-} CreateBearerContext;
-
-static void
-create_bearer_context_complete_and_free (CreateBearerContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_slice_free (CreateBearerContext, ctx);
-}
-
/**
* mm_modem_create_bearer_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_create_bearer().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_create_bearer().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_create_bearer().
*
- * Returns: (transfer full): A newly created #MMBearer, or %NULL if @error is set.
+ * Returns: (transfer full): A newly created #MMBearer, or %NULL if @error is
+ * set.
+ *
+ * Since: 1.0
*/
MMBearer *
mm_modem_create_bearer_finish (MMModem *self,
@@ -2003,16 +1844,13 @@ mm_modem_create_bearer_finish (MMModem *self,
{
g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
modem_new_bearer_ready (GDBusConnection *connection,
GAsyncResult *res,
- CreateBearerContext *ctx)
+ GTask *task)
{
GError *error = NULL;
GObject *bearer;
@@ -2023,19 +1861,17 @@ modem_new_bearer_ready (GDBusConnection *connection,
g_object_unref (source_object);
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- bearer,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, bearer, g_object_unref);
- create_bearer_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
modem_create_bearer_ready (MMModem *self,
GAsyncResult *res,
- CreateBearerContext *ctx)
+ GTask *task)
{
GError *error = NULL;
gchar *bearer_path = NULL;
@@ -2044,17 +1880,17 @@ modem_create_bearer_ready (MMModem *self,
&bearer_path,
res,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
- create_bearer_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_free (bearer_path);
return;
}
g_async_initable_new_async (MM_TYPE_BEARER,
G_PRIORITY_DEFAULT,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)modem_new_bearer_ready,
- ctx,
+ task,
"g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"g-name", MM_DBUS_SERVICE,
"g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
@@ -2069,7 +1905,8 @@ modem_create_bearer_ready (MMModem *self,
* @self: A #MMModem.
* @properties: A #MMBearerProperties object with the properties to use.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously creates a new packet data bearer in the #MMModem.
@@ -2077,12 +1914,18 @@ modem_create_bearer_ready (MMModem *self,
* This request may fail if the modem does not support additional bearers,
* if too many bearers are already defined, or if @properties are invalid.
*
- * See <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem.CreateBearer">CreateBearer</link> to check which properties may be passed.
+ * See <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem.CreateBearer">CreateBearer</link>
+ * to check which properties may be passed.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_create_bearer_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_create_bearer_finish() to get the result of the operation.
+ * See mm_modem_create_bearer_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_create_bearer_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_create_bearer (MMModem *self,
@@ -2091,18 +1934,12 @@ mm_modem_create_bearer (MMModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- CreateBearerContext *ctx;
+ GTask *task;
GVariant *dictionary;
g_return_if_fail (MM_IS_MODEM (self));
- ctx = g_slice_new0 (CreateBearerContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_modem_create_bearer);
- if (cancellable)
- ctx->cancellable = g_object_ref (cancellable);
+ task = g_task_new (self, cancellable, callback, user_data);
dictionary = mm_bearer_properties_get_dictionary (properties);
@@ -2111,7 +1948,7 @@ mm_modem_create_bearer (MMModem *self,
dictionary,
cancellable,
(GAsyncReadyCallback)modem_create_bearer_ready,
- ctx);
+ task);
g_variant_unref (dictionary);
}
@@ -2128,12 +1965,16 @@ mm_modem_create_bearer (MMModem *self,
* This request may fail if the modem does not support additional bearers,
* if too many bearers are already defined, or if @properties are invalid.
*
- * See <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem.CreateBearer">CreateBearer</link> to check which properties may be passed.
+ * See <link linkend="gdbus-method-org-freedesktop-ModemManager1-Modem.CreateBearer">CreateBearer</link>
+ * to check which properties may be passed.
*
- * The calling thread is blocked until a reply is received. See mm_modem_create_bearer()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_create_bearer() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): A newly created #MMBearer, or %NULL if @error is
+ * set.
*
- * Returns: (transfer full): A newly created #MMBearer, or %NULL if @error is set.
+ * Since: 1.0
*/
MMBearer *
mm_modem_create_bearer_sync (MMModem *self,
@@ -2176,12 +2017,15 @@ mm_modem_create_bearer_sync (MMModem *self,
/**
* mm_modem_delete_bearer_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_delete_bearer().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_delete_bearer().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_delete_bearer().
*
* Returns: %TRUE if the bearer was deleted, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_delete_bearer_finish (MMModem *self,
@@ -2198,15 +2042,21 @@ mm_modem_delete_bearer_finish (MMModem *self,
* @self: A #MMModem.
* @bearer: Path of the bearer to delete.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously deletes a given bearer from the #MMModem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_delete_bearer_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_delete_bearer_finish() to get the result of the operation.
+ *
+ * See mm_modem_delete_bearer_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_delete_bearer_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_delete_bearer (MMModem *self,
@@ -2229,10 +2079,12 @@ mm_modem_delete_bearer (MMModem *self,
* Synchronously deletes a given bearer from the #MMModem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_delete_bearer()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_delete_bearer() for the asynchronous version of this method.
*
* Returns: %TRUE if the bearer was deleted, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_delete_bearer_sync (MMModem *self,
@@ -2250,12 +2102,15 @@ mm_modem_delete_bearer_sync (MMModem *self,
/**
* mm_modem_reset_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_reset().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_reset().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_reset().
*
* Returns: %TRUE if the reset was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_reset_finish (MMModem *self,
@@ -2271,16 +2126,22 @@ mm_modem_reset_finish (MMModem *self,
* mm_modem_reset:
* @self: A #MMModem.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously clears non-persistent configuration and state, and returns the device to
- * a newly-powered-on state.
+ * Asynchronously clears non-persistent configuration and state, and returns the
+ * device to a newly-powered-on state.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_reset_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_reset_finish() to get the result of the operation.
*
- * See mm_modem_reset_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_reset_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.0
*/
void
mm_modem_reset (MMModem *self,
@@ -2299,13 +2160,15 @@ mm_modem_reset (MMModem *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously clears non-persistent configuration and state, and returns the device to
- * a newly-powered-on state.
+ * Synchronously clears non-persistent configuration and state, and returns the
+ * device to a newly-powered-on state.
*
* The calling thread is blocked until a reply is received. See mm_modem_reset()
* for the asynchronous version of this method.
*
* Returns: %TRUE if the reset was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_reset_sync (MMModem *self,
@@ -2322,12 +2185,15 @@ mm_modem_reset_sync (MMModem *self,
/**
* mm_modem_factory_reset_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_factory_reset().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_factory_reset().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_factory_reset().
*
* Returns: %TRUE if the factory_reset was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_factory_reset_finish (MMModem *self,
@@ -2344,16 +2210,22 @@ mm_modem_factory_reset_finish (MMModem *self,
* @self: A #MMModem.
* @code: Carrier-supplied code required to reset the modem.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously clears the modem's configuration (including persistent configuration and
- * state), and returns the device to a factory-default state.
+ * Asynchronously clears the modem's configuration (including persistent
+ * configuration and state), and returns the device to a factory-default state.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_factory_reset_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_factory_reset_finish() to get the result of the operation.
+ * See mm_modem_factory_reset_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_factory_reset_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_factory_reset (MMModem *self,
@@ -2374,13 +2246,15 @@ mm_modem_factory_reset (MMModem *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously clears the modem's configuration (including persistent configuration and
- * state), and returns the device to a factory-default state.
+ * Synchronously clears the modem's configuration (including persistent
+ * configuration and state), and returns the device to a factory-default state.
*
- * The calling thread is blocked until a reply is received. See mm_modem_factory_reset()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_factory_reset() for the asynchronous version of this method.
*
* Returns: %TRUE if the factory reset was successful, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_factory_reset_sync (MMModem *self,
@@ -2398,12 +2272,17 @@ mm_modem_factory_reset_sync (MMModem *self,
/**
* mm_modem_command_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_command().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_command().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_command().
*
- * Returns: (transfer full): A newly allocated string with the reply to the command, or #NULL if @error is set. The returned value should be freed with g_free().
+ * Returns: (transfer full): A newly allocated string with the reply to the
+ * command, or #NULL if @error is set. The returned value should be freed with
+ * g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_command_finish (MMModem *self,
@@ -2426,15 +2305,21 @@ mm_modem_command_finish (MMModem *self,
* @cmd: AT command to run.
* @timeout: Maximum time to wait for the response, in seconds.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously runs an AT command in the modem.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_command_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_command_finish() to get the result of the operation.
*
- * See mm_modem_command_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_command_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.0
*/
void
mm_modem_command (MMModem *self,
@@ -2447,8 +2332,6 @@ mm_modem_command (MMModem *self,
g_return_if_fail (MM_IS_MODEM (self));
- if (g_dbus_proxy_get_default_timeout (G_DBUS_PROXY (self)) < timeout)
- g_warning ("Requested command timeout is shorter than the default DBus timeout");
mm_gdbus_modem_call_command (MM_GDBUS_MODEM (self), cmd, timeout, cancellable, callback, user_data);
}
@@ -2462,10 +2345,14 @@ mm_modem_command (MMModem *self,
*
* Synchronously runs an AT command in the modem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_command()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_command() for the asynchronous version of this method.
*
- * Returns: (transfer full): A newly allocated string with the reply to the command, or #NULL if @error is set. The returned value should be freed with g_free().
+ * Returns: (transfer full): A newly allocated string with the reply to the
+ * command, or #NULL if @error is set. The returned value should be freed
+ * with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_modem_command_sync (MMModem *self,
@@ -2478,9 +2365,6 @@ mm_modem_command_sync (MMModem *self,
g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- if (g_dbus_proxy_get_default_timeout (G_DBUS_PROXY (self)) < timeout)
- g_warning ("Requested command timeout is shorter than the default DBus timeout");
-
if (!mm_gdbus_modem_call_command_sync (MM_GDBUS_MODEM (self), cmd, timeout, &result, cancellable, error))
return NULL;
@@ -2492,12 +2376,16 @@ mm_modem_command_sync (MMModem *self,
/**
* mm_modem_set_power_state_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_set_power_state().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_set_power_state().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_set_power_state().
*
- * Returns: %TRUE if the power state was successfully set, %FALSE if @error is set.
+ * Returns: %TRUE if the power state was successfully set, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_power_state_finish (MMModem *self,
@@ -2512,18 +2400,25 @@ mm_modem_set_power_state_finish (MMModem *self,
/**
* mm_modem_set_power_state:
* @self: A #MMModem.
- * @state: Either %MM_MODEM_POWER_STATE_LOW or %MM_MODEM_POWER_STATE_ON. Every other #MMModemPowerState value is not allowed.
+ * @state: Either %MM_MODEM_POWER_STATE_LOW or %MM_MODEM_POWER_STATE_ON. Every
+ * other #MMModemPowerState value is not allowed.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously sets the power state of the device. This method can only be
* used while the modem is in %MM_MODEM_STATE_DISABLED state.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_set_power_state_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_set_power_state_finish() to get the result of the operation.
+ *
+ * See mm_modem_set_power_state_sync() for the synchronous, blocking version of
+ * this method.
*
- * See mm_modem_set_power_state_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_set_power_state (MMModem *self,
@@ -2540,17 +2435,21 @@ mm_modem_set_power_state (MMModem *self,
/**
* mm_modem_set_power_state_sync:
* @self: A #MMModem.
- * @state: Either %MM_MODEM_POWER_STATE_LOW or %MM_MODEM_POWER_STATE_ON. Every other #MMModemPowerState value is not allowed.
+ * @state: Either %MM_MODEM_POWER_STATE_LOW or %MM_MODEM_POWER_STATE_ON. Every
+ * other #MMModemPowerState value is not allowed.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously sets the power state of the device. This method can only be
* used while the modem is in %MM_MODEM_STATE_DISABLED state.
*
- * The calling thread is blocked until a reply is received. See mm_modem_set_power_state()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_set_power_state() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the power state was successfully set, %FALSE if @error is
+ * set.
*
- * Returns: %TRUE if the power state was successfully set, %FALSE if @error is set.
+ * Since: 1.0
*/
gboolean
mm_modem_set_power_state_sync (MMModem *self,
@@ -2568,12 +2467,16 @@ mm_modem_set_power_state_sync (MMModem *self,
/**
* mm_modem_set_current_capabilities_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_set_current_capabilities().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_set_current_capabilities().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_set_current_capabilities().
*
- * Returns: %TRUE if the capabilities were successfully set, %FALSE if @error is set.
+ * Returns: %TRUE if the capabilities were successfully set, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_current_capabilities_finish (MMModem *self,
@@ -2590,15 +2493,23 @@ mm_modem_set_current_capabilities_finish (MMModem *self,
* @self: A #MMModem.
* @capabilities: A #MMModemCapability mask.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously sets the capabilities of the device. A restart of the modem may be required.
+ * Asynchronously sets the capabilities of the device. A restart of the modem
+ * may be required.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_set_current_capabilities_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_set_current_capabilities_finish() to get the result of the
+ * operation.
*
- * See mm_modem_set_current_capabilities_sync() for the synchronous, blocking version of this method.
+ * See mm_modem_set_current_capabilities_sync() for the synchronous, blocking
+ * version of this method.
+ *
+ * Since: 1.0
*/
void
mm_modem_set_current_capabilities (MMModem *self,
@@ -2623,12 +2534,17 @@ mm_modem_set_current_capabilities (MMModem *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously sets the capabilities of the device. A restart of the modem may be required.
+ * Synchronously sets the capabilities of the device. A restart of the modem may
+ * be required.
*
- * The calling thread is blocked until a reply is received. See mm_modem_set_current_capabilities()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_set_current_capabilities() for the asynchronous version of this
+ * method.
*
- * Returns: %TRUE if the capabilities were successfully set, %FALSE if @error is set.
+ * Returns: %TRUE if the capabilities were successfully set, %FALSE if @error is
+ * set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_current_capabilities_sync (MMModem *self,
@@ -2650,12 +2566,16 @@ mm_modem_set_current_capabilities_sync (MMModem *self,
/**
* mm_modem_set_current_modes_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_set_current_modes().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_set_current_modes().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_set_current_modes().
*
- * Returns: %TRUE if the allowed modes were successfully set, %FALSE if @error is set.
+ * Returns: %TRUE if the allowed modes were successfully set, %FALSE if @error
+ * is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_current_modes_finish (MMModem *self,
@@ -2671,18 +2591,25 @@ mm_modem_set_current_modes_finish (MMModem *self,
* mm_modem_set_current_modes:
* @self: A #MMModem.
* @modes: Mask of #MMModemMode values specifying which modes are allowed.
- * @preferred: A #MMModemMode value specifying which of the modes given in @modes is the preferred one, or #MM_MODEM_MODE_NONE if none.
+ * @preferred: A #MMModemMode value specifying which of the modes given in
+ * @modes is the preferred one, or #MM_MODEM_MODE_NONE if none.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously sets the access technologies (e.g. 2G/3G/4G preference) the device is
- * currently allowed to use when connecting to a network.
+ * Asynchronously sets the access technologies (e.g. 2G/3G/4G preference) the
+ * device is currently allowed to use when connecting to a network.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_set_current_modes_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_set_current_modes_finish() to get the result of the operation.
+ * See mm_modem_set_current_modes_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_set_current_modes_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_set_current_modes (MMModem *self,
@@ -2705,17 +2632,21 @@ mm_modem_set_current_modes (MMModem *self,
* mm_modem_set_current_modes_sync:
* @self: A #MMModem.
* @modes: Mask of #MMModemMode values specifying which modes are allowed.
- * @preferred: A #MMModemMode value specifying which of the modes given in @modes is the preferred one, or #MM_MODEM_MODE_NONE if none.
+ * @preferred: A #MMModemMode value specifying which of the modes given in
+ * @modes is the preferred one, or #MM_MODEM_MODE_NONE if none.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously sets the access technologies (e.g. 2G/3G/4G preference) the device is
- * currently allowed to use when connecting to a network.
+ * Synchronously sets the access technologies (e.g. 2G/3G/4G preference) the
+ * device is currently allowed to use when connecting to a network.
*
- * The calling thread is blocked until a reply is received. See mm_modem_set_current_modes()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_set_current_modes() for the asynchronous version of this method.
*
- * Returns: %TRUE if the allowed modes were successfully set, %FALSE if @error is set.
+ * Returns: %TRUE if the allowed modes were successfully set, %FALSE if @error
+ * is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_current_modes_sync (MMModem *self,
@@ -2737,12 +2668,15 @@ mm_modem_set_current_modes_sync (MMModem *self,
/**
* mm_modem_set_current_bands_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_set_current_bands().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_set_current_bands().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_set_current_bands().
*
* Returns: %TRUE if the bands were successfully set, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_current_bands_finish (MMModem *self,
@@ -2760,16 +2694,22 @@ mm_modem_set_current_bands_finish (MMModem *self,
* @bands: An array of #MMModemBand values specifying which bands are allowed.
* @n_bands: Number of elements in @bands.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Asynchronously sets the radio frequency and technology bands the device is currently
- * allowed to use when connecting to a network.
+ * Asynchronously sets the radio frequency and technology bands the device is
+ * currently allowed to use when connecting to a network.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_set_current_bands_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_set_current_bands_finish() to get the result of the operation.
+ * See mm_modem_set_current_bands_sync() for the synchronous, blocking version
+ * of this method.
*
- * See mm_modem_set_current_bands_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_set_current_bands (MMModem *self,
@@ -2796,13 +2736,15 @@ mm_modem_set_current_bands (MMModem *self,
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronously sets the radio frequency and technology bands the device is currently
- * allowed to use when connecting to a network.
+ * Synchronously sets the radio frequency and technology bands the device is
+ * currently allowed to use when connecting to a network.
*
- * The calling thread is blocked until a reply is received. See mm_modem_set_current_bands()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_set_current_bands() for the asynchronous version of this method.
*
* Returns: %TRUE if the bands were successfully set, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_modem_set_current_bands_sync (MMModem *self,
@@ -2825,33 +2767,31 @@ mm_modem_set_current_bands_sync (MMModem *self,
/**
* mm_modem_get_sim_finish:
* @self: A #MMModem.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_get_sim().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_get_sim().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_get_sim().
*
- * Returns: (transfer full): a #MMSim or #NULL if none available. The returned value should be freed with g_object_unref().
+ * Returns: (transfer full): a #MMSim or #NULL if @error is set. The returned
+ * value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMSim *
mm_modem_get_sim_finish (MMModem *self,
GAsyncResult *res,
GError **error)
{
- MMSim *sim;
-
g_return_val_if_fail (MM_IS_MODEM (self), NULL);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- sim = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- return (sim ? (MMSim *)g_object_ref (sim) : NULL);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
modem_get_sim_ready (GDBusConnection *connection,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GObject *sim;
@@ -2862,29 +2802,32 @@ modem_get_sim_ready (GDBusConnection *connection,
g_object_unref (source_object);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- sim,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, sim, g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
/**
* mm_modem_get_sim:
* @self: A #MMModem.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
- * Synchronously gets the #MMSim object managed by this #MMModem.
+ * Asynchronously gets the #MMSim object managed by this #MMModem.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_get_sim_finish() to get the result of the operation.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_modem_get_sim_finish() to get the result of the operation.
+ * See mm_modem_get_sim_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_modem_get_sim_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_modem_get_sim (MMModem *self,
@@ -2892,21 +2835,20 @@ mm_modem_get_sim (MMModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
const gchar *sim_path;
g_return_if_fail (MM_IS_MODEM (self));
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_modem_get_sim);
+ task = g_task_new (self, cancellable, callback, user_data);
sim_path = mm_modem_get_sim_path (self);
- if (!sim_path) {
- g_simple_async_result_set_op_res_gpointer (result, NULL, NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (!sim_path || g_str_equal (sim_path, "/")) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No SIM object available");
+ g_object_unref (task);
return;
}
@@ -2914,7 +2856,7 @@ mm_modem_get_sim (MMModem *self,
G_PRIORITY_DEFAULT,
cancellable,
(GAsyncReadyCallback)modem_get_sim_ready,
- result,
+ task,
"g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
"g-name", MM_DBUS_SERVICE,
"g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
@@ -2931,10 +2873,13 @@ mm_modem_get_sim (MMModem *self,
*
* Synchronously gets the #MMSim object managed by this #MMModem.
*
- * The calling thread is blocked until a reply is received. See mm_modem_get_sim()
- * for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_get_sim() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full): a #MMSim or #NULL if @error is set. The returned
+ * value should be freed with g_object_unref().
*
- * Returns: (transfer full): a #MMSim or #NULL if none available. The returned value should be freed with g_object_unref().
+ * Since: 1.0
*/
MMSim *
mm_modem_get_sim_sync (MMModem *self,
@@ -2947,8 +2892,13 @@ mm_modem_get_sim_sync (MMModem *self,
g_return_val_if_fail (MM_IS_MODEM (self), NULL);
sim_path = mm_modem_get_sim_path (self);
- if (!sim_path)
+ if (!sim_path || g_str_equal (sim_path, "/")) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No SIM object available");
return NULL;
+ }
sim = g_initable_new (MM_TYPE_SIM,
cancellable,
@@ -2965,51 +2915,367 @@ mm_modem_get_sim_sync (MMModem *self,
/*****************************************************************************/
+typedef struct {
+ gchar **sim_paths;
+ GPtrArray *sim_slots;
+ guint n_sim_paths;
+ guint i;
+} ListSimSlotsContext;
+
static void
-mm_modem_init (MMModem *self)
+list_sim_slots_context_free (ListSimSlotsContext *ctx)
+{
+ g_strfreev (ctx->sim_paths);
+ g_ptr_array_unref (ctx->sim_slots);
+ g_slice_free (ListSimSlotsContext, ctx);
+}
+
+/**
+ * mm_modem_list_sim_slots_finish:
+ * @self: A #MMModem.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_list_sim_slots().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_list_sim_slots().
+ *
+ * Returns: (transfer full) (element-type ModemManager.Sim): The array of
+ * #MMSim objects, or %NULL if @error is set.
+ *
+ * Since: 1.16
+ */
+GPtrArray *
+mm_modem_list_sim_slots_finish (MMModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- /* Setup private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_MODEM,
- MMModemPrivate);
- g_mutex_init (&self->priv->unlock_retries_mutex);
- g_mutex_init (&self->priv->supported_modes_mutex);
- g_mutex_init (&self->priv->supported_capabilities_mutex);
- g_mutex_init (&self->priv->supported_bands_mutex);
- g_mutex_init (&self->priv->current_bands_mutex);
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-finalize (GObject *object)
+sim_slot_free (MMSim *sim)
{
- MMModem *self = MM_MODEM (object);
+ if (sim)
+ g_object_unref (sim);
+}
- g_mutex_clear (&self->priv->unlock_retries_mutex);
- g_mutex_clear (&self->priv->supported_modes_mutex);
- g_mutex_clear (&self->priv->supported_capabilities_mutex);
- g_mutex_clear (&self->priv->supported_bands_mutex);
- g_mutex_clear (&self->priv->current_bands_mutex);
-
- if (self->priv->supported_modes)
- g_array_unref (self->priv->supported_modes);
- if (self->priv->supported_capabilities)
- g_array_unref (self->priv->supported_capabilities);
- if (self->priv->supported_bands)
- g_array_unref (self->priv->supported_bands);
- if (self->priv->current_bands)
- g_array_unref (self->priv->current_bands);
+static void create_next_sim (GTask *task);
- G_OBJECT_CLASS (mm_modem_parent_class)->finalize (object);
+static void
+modem_list_sim_slots_build_object_ready (GDBusConnection *connection,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GObject *sim;
+ GError *error = NULL;
+ GObject *source_object;
+ ListSimSlotsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ source_object = g_async_result_get_source_object (res);
+ sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), res, &error);
+ g_object_unref (source_object);
+
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_ptr_array_add (ctx->sim_slots, sim);
+
+ /* Keep on creating next object */
+ ctx->i++;
+ create_next_sim (task);
}
static void
-dispose (GObject *object)
+create_next_sim (GTask *task)
+{
+ MMModem *self;
+ ListSimSlotsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* If no more additional sims, just end here. */
+ if (ctx->i == ctx->n_sim_paths) {
+ g_assert_cmpuint (ctx->n_sim_paths, ==, ctx->sim_slots->len);
+ g_task_return_pointer (task, g_steal_pointer (&ctx->sim_slots), (GDestroyNotify)g_ptr_array_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Empty slot? */
+ if (g_str_equal (ctx->sim_paths[ctx->i], "/")) {
+ g_ptr_array_add (ctx->sim_slots, NULL);
+ ctx->i++;
+ create_next_sim (task);
+ return;
+ }
+
+ g_async_initable_new_async (MM_TYPE_SIM,
+ G_PRIORITY_DEFAULT,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)modem_list_sim_slots_build_object_ready,
+ task,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", ctx->sim_paths[ctx->i],
+ "g-interface-name", "org.freedesktop.ModemManager1.Sim",
+ NULL);
+}
+
+/**
+ * mm_modem_list_sim_slots:
+ * @self: A #MMModem.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously lists the SIM slots available in the #MMModem.
+ *
+ * The returned array contains one element per slot available in the system;
+ * a #MMSim in each of the slots that contains a valid SIM card or %NULL if
+ * no SIM card is found.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_list_sim_slots_finish() to get the result of the operation.
+ *
+ * See mm_modem_list_sim_slots_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.16
+ */
+void
+mm_modem_list_sim_slots (MMModem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ListSimSlotsContext *ctx;
+ GTask *task;
+
+ g_return_if_fail (MM_IS_MODEM (self));
+
+ ctx = g_slice_new0 (ListSimSlotsContext);
+ ctx->sim_paths = mm_gdbus_modem_dup_sim_slots (MM_GDBUS_MODEM (self));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)list_sim_slots_context_free);
+
+ /* If no sim slots, just end here. */
+ if (!ctx->sim_paths) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No SIM slots available");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Got list of paths, start creating objects for each */
+ ctx->n_sim_paths = g_strv_length (ctx->sim_paths);
+ ctx->sim_slots = g_ptr_array_new_full (ctx->n_sim_paths, (GDestroyNotify)sim_slot_free);
+ ctx->i = 0;
+ create_next_sim (task);
+}
+
+/**
+ * mm_modem_list_sim_slots_sync:
+ * @self: A #MMModem.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously lists the SIM slots available in the #MMModem.
+ *
+ * The returned array contains one element per slot available in the system;
+ * a #MMSim in each of the slots that contains a valid SIM card or %NULL if
+ * no SIM card is found.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_list_sim_slots() for the asynchronous version of this method.
+ *
+ * Returns: (transfer full) (element-type ModemManager.Sim): The array of
+ * #MMSim objects, or %NULL if @error is set.
+ *
+ * Since: 1.16
+ */
+GPtrArray *
+mm_modem_list_sim_slots_sync (MMModem *self,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_autoptr(GPtrArray) sim_slots = NULL;
+ g_auto(GStrv) sim_paths = NULL;
+ guint n_sim_paths;
+ guint i;
+
+ g_return_val_if_fail (MM_IS_MODEM (self), NULL);
+
+ sim_paths = mm_gdbus_modem_dup_sim_slots (MM_GDBUS_MODEM (self));
+
+ /* Only non-empty lists are returned */
+ if (!sim_paths)
+ return NULL;
+
+ n_sim_paths = g_strv_length (sim_paths);
+
+ sim_slots = g_ptr_array_new_full (n_sim_paths, (GDestroyNotify)sim_slot_free);
+ for (i = 0; i < n_sim_paths; i++) {
+ GObject *sim;
+
+ if (g_str_equal (sim_paths[i], "/")) {
+ g_ptr_array_add (sim_slots, NULL);
+ continue;
+ }
+
+ sim = g_initable_new (MM_TYPE_SIM,
+ cancellable,
+ error,
+ "g-flags", G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
+ "g-name", MM_DBUS_SERVICE,
+ "g-connection", g_dbus_proxy_get_connection (G_DBUS_PROXY (self)),
+ "g-object-path", sim_paths[i],
+ "g-interface-name", "org.freedesktop.ModemManager1.Sim",
+ NULL);
+ if (!sim)
+ return NULL;
+
+ /* Keep the object */
+ g_ptr_array_add (sim_slots, sim);
+ }
+ g_assert_cmpuint (sim_slots->len, ==, n_sim_paths);
+
+ return g_steal_pointer (&sim_slots);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_modem_set_primary_sim_slot_finish:
+ * @self: A #MMModem.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_modem_set_primary_sim_slot().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_modem_set_primary_sim_slot().
+ *
+ * Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if
+ * @error is set.
+ *
+ * Since: 1.16
+ */
+gboolean
+mm_modem_set_primary_sim_slot_finish (MMModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+
+ return mm_gdbus_modem_call_set_primary_sim_slot_finish (MM_GDBUS_MODEM (self), res, error);
+}
+
+/**
+ * mm_modem_set_primary_sim_slot:
+ * @self: A #MMModem.
+ * @sim_slot: SIM slot number.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously requests to select which SIM slot to be considered as primary.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_modem_set_primary_sim_slot_finish() to get the result of the operation.
+ *
+ * See mm_modem_set_primary_sim_slot_sync() for the synchronous, blocking version of
+ * this method.
+ *
+ * Since: 1.16
+ */
+void
+mm_modem_set_primary_sim_slot (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_MODEM (self));
+
+ mm_gdbus_modem_call_set_primary_sim_slot (MM_GDBUS_MODEM (self), sim_slot, cancellable, callback, user_data);
+}
+
+/**
+ * mm_modem_set_primary_sim_slot_sync:
+ * @self: A #MMModem.
+ * @sim_slot: SIM slot number.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to select which SIM slot to be considered as primary.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_modem_set_primary_sim_slot() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the SIM slot switch has been successfully requested, %FALSE if
+ * @error is set.
+ *
+ * Since: 1.16
+ */
+gboolean
+mm_modem_set_primary_sim_slot_sync (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_MODEM (self), FALSE);
+
+ return mm_gdbus_modem_call_set_primary_sim_slot_sync (MM_GDBUS_MODEM (self), sim_slot, cancellable, error);
+}
+
+/*****************************************************************************/
+
+static void
+mm_modem_init (MMModem *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_MODEM, MMModemPrivate);
+ g_mutex_init (&self->priv->mutex);
+
+ PROPERTY_INITIALIZE (ports, "ports")
+ PROPERTY_INITIALIZE (supported_modes, "supported-modes")
+ PROPERTY_INITIALIZE (supported_capabilities, "supported-capabilities")
+ PROPERTY_INITIALIZE (supported_bands, "supported-bands")
+ PROPERTY_INITIALIZE (current_bands, "current-bands")
+ PROPERTY_INITIALIZE (unlock_retries, "unlock-retries")
+}
+
+static void
+finalize (GObject *object)
{
MMModem *self = MM_MODEM (object);
- g_clear_object (&self->priv->unlock_retries);
+ g_mutex_clear (&self->priv->mutex);
+
+ PROPERTY_ARRAY_FINALIZE (ports)
+ PROPERTY_ARRAY_FINALIZE (supported_modes)
+ PROPERTY_ARRAY_FINALIZE (supported_capabilities)
+ PROPERTY_ARRAY_FINALIZE (supported_bands)
+ PROPERTY_ARRAY_FINALIZE (current_bands)
- G_OBJECT_CLASS (mm_modem_parent_class)->dispose (object);
+ PROPERTY_OBJECT_FINALIZE (unlock_retries)
+
+ G_OBJECT_CLASS (mm_modem_parent_class)->finalize (object);
}
static void
@@ -3020,6 +3286,5 @@ mm_modem_class_init (MMModemClass *modem_class)
g_type_class_add_private (object_class, sizeof (MMModemPrivate));
/* Virtual methods */
- object_class->dispose = dispose;
object_class->finalize = finalize;
}
diff --git a/libmm-glib/mm-modem.h b/libmm-glib/mm-modem.h
index 04906285..37b6fd28 100644
--- a/libmm-glib/mm-modem.h
+++ b/libmm-glib/mm-modem.h
@@ -67,6 +67,7 @@ struct _MMModemClass {
};
GType mm_modem_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMModem, g_object_unref)
const gchar *mm_modem_get_path (MMModem *self);
gchar *mm_modem_dup_path (MMModem *self);
@@ -74,6 +75,11 @@ gchar *mm_modem_dup_path (MMModem *self);
const gchar *mm_modem_get_sim_path (MMModem *self);
gchar *mm_modem_dup_sim_path (MMModem *self);
+const gchar * const *mm_modem_get_sim_slot_paths (MMModem *self);
+gchar **mm_modem_dup_sim_slot_paths (MMModem *self);
+
+guint mm_modem_get_primary_sim_slot (MMModem *self);
+
gboolean mm_modem_peek_supported_capabilities (MMModem *self,
const MMModemCapability **capabilities,
guint *n_capabilities);
@@ -83,9 +89,8 @@ gboolean mm_modem_get_supported_capabilities (MMModem *self,
MMModemCapability mm_modem_get_current_capabilities (MMModem *self);
-guint mm_modem_get_max_bearers (MMModem *self);
-
-guint mm_modem_get_max_active_bearers (MMModem *self);
+guint mm_modem_get_max_active_bearers (MMModem *self);
+guint mm_modem_get_max_active_multiplexed_bearers (MMModem *self);
const gchar * const *mm_modem_get_bearer_paths (MMModem *self);
gchar **mm_modem_dup_bearer_paths (MMModem *self);
@@ -99,6 +104,14 @@ gchar *mm_modem_dup_model (MMModem *self);
const gchar *mm_modem_get_revision (MMModem *self);
gchar *mm_modem_dup_revision (MMModem *self);
+const gchar *mm_modem_get_carrier_configuration (MMModem *self);
+gchar *mm_modem_dup_carrier_configuration (MMModem *self);
+const gchar *mm_modem_get_carrier_configuration_revision (MMModem *self);
+gchar *mm_modem_dup_carrier_configuration_revision (MMModem *self);
+
+const gchar *mm_modem_get_hardware_revision (MMModem *self);
+gchar *mm_modem_dup_hardware_revision (MMModem *self);
+
const gchar *mm_modem_get_device_identifier (MMModem *self);
gchar *mm_modem_dup_device_identifier (MMModem *self);
@@ -335,6 +348,30 @@ MMSim *mm_modem_get_sim_sync (MMModem *self,
GCancellable *cancellable,
GError **error);
+void mm_modem_list_sim_slots (MMModem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GPtrArray *mm_modem_list_sim_slots_finish (MMModem *self,
+ GAsyncResult *res,
+ GError **error);
+GPtrArray *mm_modem_list_sim_slots_sync (MMModem *self,
+ GCancellable *cancellable,
+ GError **error);
+
+void mm_modem_set_primary_sim_slot (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_modem_set_primary_sim_slot_finish (MMModem *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_modem_set_primary_sim_slot_sync (MMModem *self,
+ guint sim_slot,
+ GCancellable *cancellable,
+ GError **error);
+
G_END_DECLS
#endif /* _MM_MODEM_H_ */
diff --git a/libmm-glib/mm-network-timezone.c b/libmm-glib/mm-network-timezone.c
index 0a37c0ea..b78dde0e 100644
--- a/libmm-glib/mm-network-timezone.c
+++ b/libmm-glib/mm-network-timezone.c
@@ -47,6 +47,8 @@ struct _MMNetworkTimezonePrivate {
* Gets the timezone offset (in minutes) reported by the network.
*
* Returns: the offset, or %MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gint
mm_network_timezone_get_offset (MMNetworkTimezone *self)
@@ -57,6 +59,9 @@ mm_network_timezone_get_offset (MMNetworkTimezone *self)
return self->priv->offset;
}
+/**
+ * mm_network_timezone_set_offset: (skip)
+ */
void
mm_network_timezone_set_offset (MMNetworkTimezone *self,
gint offset)
@@ -76,6 +81,8 @@ mm_network_timezone_set_offset (MMNetworkTimezone *self,
* the network.
*
* Returns: the offset, or %MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gint
mm_network_timezone_get_dst_offset (MMNetworkTimezone *self)
@@ -86,6 +93,9 @@ mm_network_timezone_get_dst_offset (MMNetworkTimezone *self)
return self->priv->dst_offset;
}
+/**
+ * mm_network_timezone_set_dst_offset: (skip)
+ */
void
mm_network_timezone_set_dst_offset (MMNetworkTimezone *self,
gint dst_offset)
@@ -103,7 +113,10 @@ mm_network_timezone_set_dst_offset (MMNetworkTimezone *self,
*
* Gets the number of leap seconds (TAI-UTC), as reported by the network.
*
- * Returns: the number of leap seconds, or %MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN if unknown.
+ * Returns: the number of leap seconds, or
+ * %MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
gint
mm_network_timezone_get_leap_seconds (MMNetworkTimezone *self)
@@ -114,6 +127,9 @@ mm_network_timezone_get_leap_seconds (MMNetworkTimezone *self)
return self->priv->leap_seconds;
}
+/**
+ * mm_network_timezone_set_leap_seconds: (skip)
+ */
void
mm_network_timezone_set_leap_seconds (MMNetworkTimezone *self,
gint leap_seconds)
@@ -125,6 +141,9 @@ mm_network_timezone_set_leap_seconds (MMNetworkTimezone *self,
/*****************************************************************************/
+/**
+ * mm_network_timezone_get_dictionary: (skip)
+ */
GVariant *
mm_network_timezone_get_dictionary (MMNetworkTimezone *self)
{
@@ -161,6 +180,9 @@ mm_network_timezone_get_dictionary (MMNetworkTimezone *self)
/*****************************************************************************/
+/**
+ * mm_network_timezone_new_from_dictionary: (skip)
+ */
MMNetworkTimezone *
mm_network_timezone_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -226,6 +248,9 @@ mm_network_timezone_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
+/**
+ * mm_network_timezone_new: (skip)
+ */
MMNetworkTimezone *
mm_network_timezone_new (void)
{
@@ -236,7 +261,7 @@ mm_network_timezone_new (void)
static void
mm_network_timezone_init (MMNetworkTimezone *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_NETWORK_TIMEZONE,
MMNetworkTimezonePrivate);
diff --git a/libmm-glib/mm-network-timezone.h b/libmm-glib/mm-network-timezone.h
index 2bbb3058..f2b639a7 100644
--- a/libmm-glib/mm-network-timezone.h
+++ b/libmm-glib/mm-network-timezone.h
@@ -36,13 +36,17 @@ G_BEGIN_DECLS
* MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN:
*
* Identifier for an unknown timezone offset.
+ *
+ * Since: 1.0
*/
-#define MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN G_MAXINT32
+#define MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN G_MAXINT32
/**
* MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN:
*
* Identifier for an unknown leap seconds value.
+ *
+ * Since: 1.0
*/
#define MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN G_MAXINT32
@@ -68,6 +72,7 @@ struct _MMNetworkTimezoneClass {
};
GType mm_network_timezone_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMNetworkTimezone, g_object_unref)
gint32 mm_network_timezone_get_offset (MMNetworkTimezone *self);
gint32 mm_network_timezone_get_dst_offset (MMNetworkTimezone *self);
diff --git a/libmm-glib/mm-object.c b/libmm-glib/mm-object.c
index fa8fae25..fd582292 100644
--- a/libmm-glib/mm-object.c
+++ b/libmm-glib/mm-object.c
@@ -47,6 +47,8 @@ G_DEFINE_TYPE (MMObject, mm_object, MM_GDBUS_TYPE_OBJECT_PROXY)
* Gets the DBus path of the #MMObject object.
*
* Returns: (transfer none): The DBus path of the #MMObject object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_object_get_path (MMObject *self)
@@ -62,7 +64,10 @@ mm_object_get_path (MMObject *self)
*
* Gets a copy of the DBus path of the #MMObject object.
*
- * Returns: (transfer full): The DBus path of the #MMObject. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMObject. The returned value
+ * should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_object_dup_path (MMObject *self)
@@ -84,9 +89,13 @@ mm_object_dup_path (MMObject *self)
* mm_object_get_modem:
* @self: A #MMModem
*
- * Gets the #MMModem instance for the D-Bus interface org.freedesktop.ModemManager1.Modem on @self, if any.
+ * Gets the #MMModem instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem on @self, if any.
*
- * Returns: (transfer full): A #MMModem that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Returns: (transfer full): A #MMModem that must be freed with g_object_unref()
+ * or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.0
*/
MMModem *
mm_object_get_modem (MMObject *self)
@@ -104,11 +113,16 @@ mm_object_get_modem (MMObject *self)
* mm_object_peek_modem: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem() but doesn't increase the reference count on the
+ * returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModem or %NULL if @self does not implement
+ * the interface. Do not free the returned object, it is owned by @self.
*
- * Returns: (transfer none): A #MMModem or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModem *
mm_object_peek_modem (MMObject *self)
@@ -128,9 +142,13 @@ mm_object_peek_modem (MMObject *self)
* mm_object_get_modem_3gpp:
* @self: A #MMObject.
*
- * Gets the #MMModem3gpp instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Modem3gpp on @self, if any.
+ * Gets the #MMModem3gpp instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modem3gpp on @self, if any.
*
- * Returns: (transfer full): A #MMModem3gpp that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Returns: (transfer full): A #MMModem3gpp that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.0
*/
MMModem3gpp *
mm_object_get_modem_3gpp (MMObject *self)
@@ -144,11 +162,16 @@ mm_object_get_modem_3gpp (MMObject *self)
* mm_object_peek_modem_3gpp: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_3gpp() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_3gpp() but doesn't increase the reference count on
+ * the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModem3gpp or %NULL if @self does not implement
+ * the interface. Do not free the returned object, it is owned by @self.
*
- * Returns: (transfer none): A #MMModem3gpp or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModem3gpp *
mm_object_peek_modem_3gpp (MMObject *self)
@@ -161,12 +184,62 @@ mm_object_peek_modem_3gpp (MMObject *self)
/*****************************************************************************/
/**
+ * mm_object_get_modem_3gpp_profile_manager:
+ * @self: A #MMObject.
+ *
+ * Gets the #MMModem3gppProfileManager instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modem3gpp-ProfileManager on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModem3gppProfileManager that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.18
+ */
+MMModem3gppProfileManager *
+mm_object_get_modem_3gpp_profile_manager (MMObject *self)
+{
+ g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL);
+
+ return (MMModem3gppProfileManager *)mm_gdbus_object_get_modem3gpp_profile_manager (MM_GDBUS_OBJECT (self));
+}
+
+/**
+ * mm_object_peek_modem_3gpp_profile_manager: (skip)
+ * @self: A #MMObject.
+ *
+ * Like mm_object_get_modem_3gpp_profile_manager() but doesn't increase the reference count
+ * on the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
+ *
+ * Returns: (transfer none): A #MMModem3gppProfileManager or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
+ *
+ * Since: 1.18
+ */
+MMModem3gppProfileManager *
+mm_object_peek_modem_3gpp_profile_manager (MMObject *self)
+{
+ g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL);
+
+ return (MMModem3gppProfileManager *)mm_gdbus_object_peek_modem3gpp_profile_manager (MM_GDBUS_OBJECT (self));
+}
+
+/*****************************************************************************/
+
+/**
* mm_object_get_modem_3gpp_ussd:
* @self: A #MMObject.
*
- * Gets the #MMModem3gppUssd instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Modem3gpp-Ussd on @self, if any.
+ * Gets the #MMModem3gppUssd instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modem3gpp-Ussd on @self, if any.
*
- * Returns: (transfer full): A #MMModem3gppUssd that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Returns: (transfer full): A #MMModem3gppUssd that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.0
*/
MMModem3gppUssd *
mm_object_get_modem_3gpp_ussd (MMObject *self)
@@ -180,11 +253,17 @@ mm_object_get_modem_3gpp_ussd (MMObject *self)
* mm_object_peek_modem_3gpp_ussd: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_3gpp_ussd() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_3gpp_ussd() but doesn't increase the reference count
+ * on the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModem3gppUssd or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
*
- * Returns: (transfer none): A #MMModem3gppUssd or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModem3gppUssd *
mm_object_peek_modem_3gpp_ussd (MMObject *self)
@@ -200,9 +279,13 @@ mm_object_peek_modem_3gpp_ussd (MMObject *self)
* mm_object_get_modem_cdma:
* @self: A #MMObject.
*
- * Gets the #MMModemCdma instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.ModemCdma on @self, if any.
+ * Gets the #MMModemCdma instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.ModemCdma on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemCdma that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
*
- * Returns: (transfer full): A #MMModemCdma that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Since: 1.0
*/
MMModemCdma *
mm_object_get_modem_cdma (MMObject *self)
@@ -216,11 +299,16 @@ mm_object_get_modem_cdma (MMObject *self)
* mm_object_peek_modem_cdma: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_cdma() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_cdma() but doesn't increase the reference count on
+ * the returned object.
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * Returns: (transfer none): A #MMModemCdma or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Returns: (transfer none): A #MMModemCdma or %NULL if @self does not implement
+ * the interface. Do not free the returned object, it is owned by @self.
+ *
+ * Since: 1.0
*/
MMModemCdma *
mm_object_peek_modem_cdma (MMObject *self)
@@ -236,9 +324,13 @@ mm_object_peek_modem_cdma (MMObject *self)
* mm_object_get_modem_simple:
* @self: A #MMObject.
*
- * Gets the #MMModemSimple instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Modemsimple on @self, if any.
+ * Gets the #MMModemSimple instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modemsimple on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemSimple that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
*
- * Returns: (transfer full): A #MMModemSimple that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Since: 1.0
*/
MMModemSimple *
mm_object_get_modem_simple (MMObject *self)
@@ -252,11 +344,17 @@ mm_object_get_modem_simple (MMObject *self)
* mm_object_peek_modem_simple: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_simple() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_simple() but doesn't increase the reference count on
+ * the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModemSimple or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
*
- * Returns: (transfer none): A #MMModemSimple or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModemSimple *
mm_object_peek_modem_simple (MMObject *self)
@@ -272,9 +370,13 @@ mm_object_peek_modem_simple (MMObject *self)
* mm_object_get_modem_location:
* @self: A #MMObject.
*
- * Gets the #MMModemLocation instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Modemlocation on @self, if any.
+ * Gets the #MMModemLocation instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modemlocation on @self, if any.
*
- * Returns: (transfer full): A #MMModemLocation that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Returns: (transfer full): A #MMModemLocation that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.0
*/
MMModemLocation *
mm_object_get_modem_location (MMObject *self)
@@ -288,11 +390,17 @@ mm_object_get_modem_location (MMObject *self)
* mm_object_peek_modem_location: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_location() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_location() but doesn't increase the reference count
+ * on the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModemLocation or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
*
- * Returns: (transfer none): A #MMModemLocation or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModemLocation *
mm_object_peek_modem_location (MMObject *self)
@@ -308,9 +416,13 @@ mm_object_peek_modem_location (MMObject *self)
* mm_object_get_modem_messaging:
* @self: A #MMObject.
*
- * Gets the #MMModemMessaging instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Modemmessaging on @self, if any.
+ * Gets the #MMModemMessaging instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modemmessaging on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemMessaging that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
*
- * Returns: (transfer full): A #MMModemMessaging that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Since: 1.0
*/
MMModemMessaging *
mm_object_get_modem_messaging (MMObject *self)
@@ -324,11 +436,17 @@ mm_object_get_modem_messaging (MMObject *self)
* mm_object_peek_modem_messaging: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_messaging() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_messaging() but doesn't increase the reference count
+ * on the returned object.
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * Returns: (transfer none): A #MMModemMessaging or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Returns: (transfer none): A #MMModemMessaging or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
+ *
+ * Since: 1.0
*/
MMModemMessaging *
mm_object_peek_modem_messaging (MMObject *self)
@@ -341,12 +459,62 @@ mm_object_peek_modem_messaging (MMObject *self)
/*****************************************************************************/
/**
+ * mm_object_get_modem_voice:
+ * @self: A #MMObject.
+ *
+ * Gets the #MMModemVoice instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Modemvoice on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemVoice that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.6
+ */
+MMModemVoice *
+mm_object_get_modem_voice (MMObject *self)
+{
+ g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL);
+
+ return (MMModemVoice *)mm_gdbus_object_get_modem_voice (MM_GDBUS_OBJECT (self));
+}
+
+/**
+ * mm_object_peek_modem_voice: (skip)
+ * @self: A #MMObject.
+ *
+ * Like mm_object_get_modem_voice() but doesn't increase the reference count on
+ * the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
+ *
+ * Returns: (transfer none): A #MMModemVoice or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
+ *
+ * Since: 1.6
+ */
+MMModemVoice *
+mm_object_peek_modem_voice (MMObject *self)
+{
+ g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL);
+
+ return (MMModemVoice *)mm_gdbus_object_peek_modem_voice (MM_GDBUS_OBJECT (self));
+}
+
+/*****************************************************************************/
+
+/**
* mm_object_get_modem_time:
* @self: A #MMObject.
*
- * Gets the #MMModemTime instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Time on @self, if any.
+ * Gets the #MMModemTime instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Time on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemTime that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
*
- * Returns: (transfer full): A #MMModemTime that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Since: 1.0
*/
MMModemTime *
mm_object_get_modem_time (MMObject *self)
@@ -360,11 +528,16 @@ mm_object_get_modem_time (MMObject *self)
* mm_object_peek_modem_time: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_time() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_time() but doesn't increase the reference count on
+ * the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModemTime or %NULL if @self does not implement
+ * the interface. Do not free the returned object, it is owned by @self.
*
- * Returns: (transfer none): A #MMModemTime or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModemTime *
mm_object_peek_modem_time (MMObject *self)
@@ -380,9 +553,13 @@ mm_object_peek_modem_time (MMObject *self)
* mm_object_get_modem_firmware:
* @self: A #MMObject.
*
- * Gets the #MMModemFirmware instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Firmware on @self, if any.
+ * Gets the #MMModemFirmware instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Firmware on @self, if any.
*
- * Returns: (transfer full): A #MMModemFirmware that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Returns: (transfer full): A #MMModemFirmware that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.0
*/
MMModemFirmware *
mm_object_get_modem_firmware (MMObject *self)
@@ -396,11 +573,17 @@ mm_object_get_modem_firmware (MMObject *self)
* mm_object_peek_modem_firmware: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_firmware() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_firmware() but doesn't increase the reference count
+ * on the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModemFirmware or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
*
- * Returns: (transfer none): A #MMModemFirmware or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.0
*/
MMModemFirmware *
mm_object_peek_modem_firmware (MMObject *self)
@@ -413,12 +596,61 @@ mm_object_peek_modem_firmware (MMObject *self)
/*****************************************************************************/
/**
+ * mm_object_get_modem_sar:
+ * @self: A #MMObject.
+ *
+ * Gets the #MMModemSar instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Sar on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemSar that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
+ *
+ * Since: 1.20
+ */
+MMModemSar *
+mm_object_get_modem_sar (MMObject *self)
+{
+ g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL);
+
+ return (MMModemSar *)mm_gdbus_object_get_modem_sar (MM_GDBUS_OBJECT (self));
+}
+
+/**
+ * mm_object_peek_modem_sar: (skip)
+ * @self: A #MMObject.
+ *
+ * Like mm_object_get_modem_sar() but doesn't increase the reference count
+ * on the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
+ *
+ * Returns: (transfer none): A #MMModemSar or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
+ *
+ * Since: 1.20
+ */
+MMModemSar *
+mm_object_peek_modem_sar (MMObject *self)
+{
+ g_return_val_if_fail (MM_IS_OBJECT (MM_GDBUS_OBJECT (self)), NULL);
+
+ return (MMModemSar *)mm_gdbus_object_peek_modem_sar (MM_GDBUS_OBJECT (self));
+}
+/*****************************************************************************/
+
+/**
* mm_object_get_modem_signal:
* @self: A #MMObject.
*
- * Gets the #MMModemSignal instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Signal on @self, if any.
+ * Gets the #MMModemSignal instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Signal on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemSignal that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
*
- * Returns: (transfer full): A #MMModemSignal that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Since: 1.2
*/
MMModemSignal *
mm_object_get_modem_signal (MMObject *self)
@@ -432,11 +664,17 @@ mm_object_get_modem_signal (MMObject *self)
* mm_object_peek_modem_signal: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_signal() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_signal() but doesn't increase the reference count on
+ * the returned object.
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * Returns: (transfer none): A #MMModemSignal or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Returns: (transfer none): A #MMModemSignal or %NULL if @self does not
+ * implement the interface. Do not free the returned object, it is owned by
+ * @self.
+ *
+ * Since: 1.2
*/
MMModemSignal *
mm_object_peek_modem_signal (MMObject *self)
@@ -452,9 +690,13 @@ mm_object_peek_modem_signal (MMObject *self)
* mm_object_get_modem_oma:
* @self: A #MMObject.
*
- * Gets the #MMModemOma instance for the D-Bus interface org.freedesktop.ModemManager1.Modem.Oma on @self, if any.
+ * Gets the #MMModemOma instance for the D-Bus interface
+ * org.freedesktop.ModemManager1.Modem.Oma on @self, if any.
+ *
+ * Returns: (transfer full): A #MMModemOma that must be freed with
+ * g_object_unref() or %NULL if @self does not implement the interface.
*
- * Returns: (transfer full): A #MMModemOma that must be freed with g_object_unref() or %NULL if @self does not implement the interface.
+ * Since: 1.2
*/
MMModemOma *
mm_object_get_modem_oma (MMObject *self)
@@ -468,11 +710,16 @@ mm_object_get_modem_oma (MMObject *self)
* mm_object_peek_modem_oma: (skip)
* @self: A #MMObject.
*
- * Like mm_object_get_modem_oma() but doesn't increase the reference count on the returned object.
+ * Like mm_object_get_modem_oma() but doesn't increase the reference count on
+ * the returned object.
+ *
+ * <warning>It is not safe to use the returned object if you are on another
+ * thread than the one where the #MMManager is running.</warning>
*
- * <warning>It is not safe to use the returned object if you are on another thread than the one where the #MMManager is running.</warning>
+ * Returns: (transfer none): A #MMModemOma or %NULL if @self does not implement
+ * the interface. Do not free the returned object, it is owned by @self.
*
- * Returns: (transfer none): A #MMModemOma or %NULL if @self does not implement the interface. Do not free the returned object, it is owned by @self.
+ * Since: 1.2
*/
MMModemOma *
mm_object_peek_modem_oma (MMObject *self)
diff --git a/libmm-glib/mm-object.h b/libmm-glib/mm-object.h
index bef90b94..f19c5208 100644
--- a/libmm-glib/mm-object.h
+++ b/libmm-glib/mm-object.h
@@ -33,13 +33,16 @@
#include "mm-gdbus-modem.h"
#include "mm-modem.h"
#include "mm-modem-3gpp.h"
+#include "mm-modem-3gpp-profile-manager.h"
#include "mm-modem-3gpp-ussd.h"
#include "mm-modem-cdma.h"
#include "mm-modem-simple.h"
#include "mm-modem-location.h"
#include "mm-modem-messaging.h"
+#include "mm-modem-voice.h"
#include "mm-modem-time.h"
#include "mm-modem-firmware.h"
+#include "mm-modem-sar.h"
#include "mm-modem-signal.h"
#include "mm-modem-oma.h"
@@ -73,33 +76,40 @@ struct _MMObjectClass {
};
GType mm_object_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMObject, g_object_unref)
const gchar *mm_object_get_path (MMObject *self);
gchar *mm_object_dup_path (MMObject *self);
-MMModem *mm_object_get_modem (MMObject *self);
-MMModem3gpp *mm_object_get_modem_3gpp (MMObject *self);
-MMModem3gppUssd *mm_object_get_modem_3gpp_ussd (MMObject *self);
-MMModemCdma *mm_object_get_modem_cdma (MMObject *self);
-MMModemSimple *mm_object_get_modem_simple (MMObject *self);
-MMModemLocation *mm_object_get_modem_location (MMObject *self);
-MMModemMessaging *mm_object_get_modem_messaging (MMObject *self);
-MMModemTime *mm_object_get_modem_time (MMObject *self);
-MMModemFirmware *mm_object_get_modem_firmware (MMObject *self);
-MMModemSignal *mm_object_get_modem_signal (MMObject *self);
-MMModemOma *mm_object_get_modem_oma (MMObject *self);
+MMModem *mm_object_get_modem (MMObject *self);
+MMModem3gpp *mm_object_get_modem_3gpp (MMObject *self);
+MMModem3gppProfileManager *mm_object_get_modem_3gpp_profile_manager (MMObject *self);
+MMModem3gppUssd *mm_object_get_modem_3gpp_ussd (MMObject *self);
+MMModemCdma *mm_object_get_modem_cdma (MMObject *self);
+MMModemSimple *mm_object_get_modem_simple (MMObject *self);
+MMModemLocation *mm_object_get_modem_location (MMObject *self);
+MMModemMessaging *mm_object_get_modem_messaging (MMObject *self);
+MMModemVoice *mm_object_get_modem_voice (MMObject *self);
+MMModemTime *mm_object_get_modem_time (MMObject *self);
+MMModemFirmware *mm_object_get_modem_firmware (MMObject *self);
+MMModemSar *mm_object_get_modem_sar (MMObject *self);
+MMModemSignal *mm_object_get_modem_signal (MMObject *self);
+MMModemOma *mm_object_get_modem_oma (MMObject *self);
-MMModem *mm_object_peek_modem (MMObject *self);
-MMModem3gpp *mm_object_peek_modem_3gpp (MMObject *self);
-MMModem3gppUssd *mm_object_peek_modem_3gpp_ussd (MMObject *self);
-MMModemCdma *mm_object_peek_modem_cdma (MMObject *self);
-MMModemSimple *mm_object_peek_modem_simple (MMObject *self);
-MMModemLocation *mm_object_peek_modem_location (MMObject *self);
-MMModemMessaging *mm_object_peek_modem_messaging (MMObject *self);
-MMModemTime *mm_object_peek_modem_time (MMObject *self);
-MMModemFirmware *mm_object_peek_modem_firmware (MMObject *self);
-MMModemSignal *mm_object_peek_modem_signal (MMObject *self);
-MMModemOma *mm_object_peek_modem_oma (MMObject *self);
+MMModem *mm_object_peek_modem (MMObject *self);
+MMModem3gpp *mm_object_peek_modem_3gpp (MMObject *self);
+MMModem3gppProfileManager *mm_object_peek_modem_3gpp_profile_manager (MMObject *self);
+MMModem3gppUssd *mm_object_peek_modem_3gpp_ussd (MMObject *self);
+MMModemCdma *mm_object_peek_modem_cdma (MMObject *self);
+MMModemSimple *mm_object_peek_modem_simple (MMObject *self);
+MMModemLocation *mm_object_peek_modem_location (MMObject *self);
+MMModemMessaging *mm_object_peek_modem_messaging (MMObject *self);
+MMModemVoice *mm_object_peek_modem_voice (MMObject *self);
+MMModemTime *mm_object_peek_modem_time (MMObject *self);
+MMModemFirmware *mm_object_peek_modem_firmware (MMObject *self);
+MMModemSar *mm_object_peek_modem_sar (MMObject *self);
+MMModemSignal *mm_object_peek_modem_signal (MMObject *self);
+MMModemOma *mm_object_peek_modem_oma (MMObject *self);
G_END_DECLS
diff --git a/libmm-glib/mm-pco.c b/libmm-glib/mm-pco.c
new file mode 100644
index 00000000..23753b10
--- /dev/null
+++ b/libmm-glib/mm-pco.c
@@ -0,0 +1,317 @@
+/* -*- 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 2018 Google LLC.
+ */
+
+#include <string.h>
+#include <glib.h>
+
+#include "mm-enums-types.h"
+#include "mm-errors-types.h"
+#include "mm-common-helpers.h"
+#include "mm-pco.h"
+
+/**
+ * SECTION: mm-pco
+ * @title: MMPco
+ * @short_description: Helper object to handle 3GPP PCO.
+ *
+ * The #MMPco is an object handling the raw 3GPP Protocol Configuration Options
+ * (PCO) that the modem has received from the network.
+ *
+ * This object is retrieved with mm_modem_3gpp_get_pco().
+ */
+
+G_DEFINE_TYPE (MMPco, mm_pco, G_TYPE_OBJECT)
+
+struct _MMPcoPrivate {
+ /* Session ID, signature 'u' */
+ guint32 session_id;
+ /* Flag indicating if the PCO data is complete or partial, signature 'b' */
+ gboolean is_complete;
+ /* Raw PCO data, signature 'ay' */
+ GBytes *data;
+};
+
+/*****************************************************************************/
+
+static GBytes *
+_g_variant_get_bytes (GVariant *variant)
+{
+ GByteArray *byte_array;
+ guint num_bytes;
+ GVariantIter iter;
+ guint8 byte;
+
+ g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE ("ay")));
+
+ num_bytes = g_variant_n_children (variant);
+ if (num_bytes == 0)
+ return NULL;
+
+ byte_array = g_byte_array_sized_new (num_bytes);
+
+ g_variant_iter_init (&iter, variant);
+ while (g_variant_iter_loop (&iter, "y", &byte))
+ g_byte_array_append (byte_array, &byte, sizeof (byte));
+
+ return g_byte_array_free_to_bytes (byte_array);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_get_session_id:
+ * @self: a #MMPco.
+ *
+ * Gets the session ID associated with the PCO.
+ *
+ * Returns: the session ID.
+ *
+ * Since: 1.10
+ */
+guint32
+mm_pco_get_session_id (MMPco *self)
+{
+ g_return_val_if_fail (MM_IS_PCO (self), G_MAXUINT32);
+
+ return self->priv->session_id;
+}
+
+/**
+ * mm_pco_set_session_id: (skip)
+ */
+void
+mm_pco_set_session_id (MMPco *self,
+ guint32 session_id)
+{
+ g_return_if_fail (MM_IS_PCO (self));
+
+ self->priv->session_id = session_id;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_is_complete:
+ * @self: a #MMPco.
+ *
+ * Gets the complete flag that indicates whether the PCO data contains the
+ * complete PCO structure received from the network.
+ *
+ * Returns: %TRUE if the PCO data contains the complete PCO structure, %FALSE
+ * otherwise.
+ *
+ * Since: 1.10
+ */
+gboolean
+mm_pco_is_complete (MMPco *self)
+{
+ g_return_val_if_fail (MM_IS_PCO (self), FALSE);
+
+ return self->priv->is_complete;
+}
+
+/**
+ * mm_pco_set_complete: (skip)
+ */
+void
+mm_pco_set_complete (MMPco *self,
+ gboolean is_complete)
+{
+ g_return_if_fail (MM_IS_PCO (self));
+
+ self->priv->is_complete = is_complete;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_get_data:
+ * @self: a #MMPco.
+ * @data_size: (out): Size of the PCO data, if any given.
+ *
+ * Gets the PCO data in raw bytes.
+ *
+ * Returns: (transfer none): the PCO data, or %NULL if it doesn't contain any.
+ *
+ * Since: 1.10
+ */
+const guint8 *
+mm_pco_get_data (MMPco *self,
+ gsize *data_size)
+{
+ g_return_val_if_fail (MM_IS_PCO (self), NULL);
+
+ return g_bytes_get_data (self->priv->data, data_size);
+}
+
+/**
+ * mm_pco_set_data: (skip)
+ */
+void
+mm_pco_set_data (MMPco *self,
+ const guint8 *data,
+ gsize data_size)
+{
+ g_return_if_fail (MM_IS_PCO (self));
+
+ g_bytes_unref (self->priv->data);
+
+ self->priv->data = (data && data_size) ? g_bytes_new (data, data_size)
+ : NULL;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_from_variant: (skip)
+ */
+MMPco *
+mm_pco_from_variant (GVariant *variant,
+ GError **error)
+{
+ MMPco *pco;
+ GVariant *pco_data = NULL;
+
+ pco = mm_pco_new ();
+ if (!variant)
+ return pco;
+
+ if (!g_variant_is_of_type (variant, G_VARIANT_TYPE ("(ubay)"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create PCO from variant: "
+ "invalid variant type received");
+ g_object_unref (pco);
+ return NULL;
+ }
+
+ g_variant_get (variant, "(ub@ay)",
+ &pco->priv->session_id,
+ &pco->priv->is_complete,
+ &pco_data);
+
+ g_bytes_unref (pco->priv->data);
+ pco->priv->data = _g_variant_get_bytes (pco_data);
+ g_variant_unref (pco_data);
+
+ return pco;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_to_variant: (skip)
+ */
+GVariant *
+mm_pco_to_variant (MMPco *self)
+{
+ GVariantBuilder builder;
+ gsize i, pco_data_size;
+ const guint8 *pco_data;
+
+ /* Allow NULL */
+ if (!self)
+ return NULL;
+
+ g_return_val_if_fail (MM_IS_PCO (self), NULL);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("(ubay)"));
+
+ g_variant_builder_add (&builder, "u", self->priv->session_id);
+ g_variant_builder_add (&builder, "b", self->priv->is_complete);
+
+ g_variant_builder_open (&builder, G_VARIANT_TYPE ("ay"));
+ if (self->priv->data) {
+ pco_data = g_bytes_get_data (self->priv->data, &pco_data_size);
+ for (i = 0; i < pco_data_size; ++i)
+ g_variant_builder_add (&builder, "y", pco_data[i]);
+ }
+ g_variant_builder_close (&builder);
+
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_list_add: (skip)
+ */
+GList *
+mm_pco_list_add (GList *pco_list,
+ MMPco *pco)
+{
+ GList *iter;
+ guint32 session_id;
+
+ g_return_val_if_fail (pco != NULL, pco_list);
+
+ session_id = mm_pco_get_session_id (pco);
+
+ for (iter = g_list_first (pco_list); iter; iter = g_list_next (iter)) {
+ MMPco *iter_pco = iter->data;
+ guint32 iter_session_id = mm_pco_get_session_id (iter_pco);
+
+ if (iter_session_id < session_id)
+ continue;
+ else if (iter_session_id == session_id) {
+ iter->data = g_object_ref (pco);
+ g_object_unref (iter_pco);
+ return pco_list;
+ } else
+ break;
+ }
+
+ return g_list_insert_before (pco_list, iter, g_object_ref (pco));
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_pco_new: (skip)
+ */
+MMPco *
+mm_pco_new (void)
+{
+ return (MM_PCO (g_object_new (MM_TYPE_PCO, NULL)));
+}
+
+static void
+mm_pco_init (MMPco *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PCO, MMPcoPrivate);
+
+ self->priv->session_id = G_MAXUINT32;
+}
+
+static void
+finalize (GObject *object)
+{
+ MMPco *self = MM_PCO (object);
+
+ g_bytes_unref (self->priv->data);
+
+ G_OBJECT_CLASS (mm_pco_parent_class)->finalize (object);
+}
+
+static void
+mm_pco_class_init (MMPcoClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMPcoPrivate));
+
+ object_class->finalize = finalize;
+}
diff --git a/libmm-glib/mm-pco.h b/libmm-glib/mm-pco.h
new file mode 100644
index 00000000..11203577
--- /dev/null
+++ b/libmm-glib/mm-pco.h
@@ -0,0 +1,89 @@
+/* -*- 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 2018 Google LLC.
+ */
+
+#ifndef MM_PCO_H
+#define MM_PCO_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_PCO (mm_pco_get_type ())
+#define MM_PCO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PCO, MMPco))
+#define MM_PCO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PCO, MMPcoClass))
+#define MM_IS_PCO(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PCO))
+#define MM_IS_PCO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PCO))
+#define MM_PCO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PCO, MMPcoClass))
+
+typedef struct _MMPco MMPco;
+typedef struct _MMPcoClass MMPcoClass;
+typedef struct _MMPcoPrivate MMPcoPrivate;
+
+/**
+ * MMPco:
+ *
+ * The #MMPco structure contains private data and should only be accessed
+ * using the provided API.
+ */
+struct _MMPco {
+ /*< private >*/
+ GObject parent;
+ MMPcoPrivate *priv;
+};
+
+struct _MMPcoClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_pco_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPco, g_object_unref)
+
+guint32 mm_pco_get_session_id (MMPco *self);
+gboolean mm_pco_is_complete (MMPco *self);
+const guint8 *mm_pco_get_data (MMPco *self,
+ gsize *data_size);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMPco *mm_pco_new (void);
+MMPco *mm_pco_from_variant (GVariant *variant,
+ GError **error);
+GVariant *mm_pco_to_variant (MMPco *self);
+void mm_pco_set_session_id (MMPco *self,
+ guint32 session_id);
+void mm_pco_set_complete (MMPco *self,
+ gboolean is_complete);
+void mm_pco_set_data (MMPco *self,
+ const guint8 *data,
+ gsize data_size);
+
+GList *mm_pco_list_add (GList *pco_list,
+ MMPco *pco);
+#endif
+
+G_END_DECLS
+
+#endif /* MM_PCO_H */
diff --git a/libmm-glib/mm-signal-threshold-properties.c b/libmm-glib/mm-signal-threshold-properties.c
new file mode 100644
index 00000000..48a55b81
--- /dev/null
+++ b/libmm-glib/mm-signal-threshold-properties.c
@@ -0,0 +1,322 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#include <string.h>
+
+#include "mm-errors-types.h"
+#include "mm-common-helpers.h"
+#include "mm-signal-threshold-properties.h"
+
+/**
+ * SECTION: mm-signal-threshold-properties
+ * @title: MMSignalThresholdProperties
+ * @short_description: Helper object to handle signal threshold properties.
+ *
+ * The #MMSignalThresholdProperties is an object handling the properties requested
+ * when setting up threshold based signal quality information reporting.
+ *
+ * This object is created by the user and passed to ModemManager with either
+ * mm_modem_signal_setup_thresholds() or mm_modem_signal_setup_thresholds_sync().
+ */
+
+G_DEFINE_TYPE (MMSignalThresholdProperties, mm_signal_threshold_properties, G_TYPE_OBJECT)
+
+#define PROPERTY_RSSI_THRESHOLD "rssi-threshold"
+#define PROPERTY_ERROR_RATE_THRESHOLD "error-rate-threshold"
+
+struct _MMSignalThresholdPropertiesPrivate {
+ guint rssi_threshold;
+ gboolean rssi_threshold_set;
+ gboolean error_rate_threshold;
+ gboolean error_rate_threshold_set;
+};
+
+/*****************************************************************************/
+
+/**
+ * mm_signal_threshold_properties_set_rssi:
+ * @self: a #MMSignalThresholdProperties.
+ * @rssi_threshold: the RSSI threshold, or 0 to disable.
+ *
+ * Sets the RSSI threshold, in dBm.
+ *
+ * Since: 1.20
+ */
+void
+mm_signal_threshold_properties_set_rssi (MMSignalThresholdProperties *self,
+ guint rssi_threshold)
+{
+ g_return_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self));
+
+ self->priv->rssi_threshold = rssi_threshold;
+ self->priv->rssi_threshold_set = TRUE;
+}
+
+/**
+ * mm_signal_threshold_properties_get_rssi:
+ * @self: a #MMSignalThresholdProperties.
+ *
+ * Gets the RSSI threshold, in dBm.
+ *
+ * Returns: the RSSI threshold, or 0 if disabled.
+ *
+ * Since: 1.20
+ */
+guint
+mm_signal_threshold_properties_get_rssi (MMSignalThresholdProperties *self)
+{
+ g_return_val_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self), 0);
+
+ return self->priv->rssi_threshold;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_signal_threshold_properties_set_error_rate:
+ * @self: a #MMSignalThresholdProperties.
+ * @error_rate_threshold: %TRUE to enable, %FALSE to disable.
+ *
+ * Enables or disables the error rate threshold.
+ *
+ * Since: 1.20
+ */
+void
+mm_signal_threshold_properties_set_error_rate (MMSignalThresholdProperties *self,
+ gboolean error_rate_threshold)
+{
+ g_return_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self));
+
+ self->priv->error_rate_threshold = error_rate_threshold;
+ self->priv->error_rate_threshold_set = TRUE;
+}
+
+/**
+ * mm_signal_threshold_properties_get_error_rate:
+ * @self: a #MMSignalThresholdProperties.
+ *
+ * Gets whether the error rate threshold is enabled or disabled.
+ *
+ * Returns: %TRUE if the error rate threshold is enabled, %FALSE otherwise.
+ *
+ * Since: 1.20
+ */
+gboolean
+mm_signal_threshold_properties_get_error_rate (MMSignalThresholdProperties *self)
+{
+ g_return_val_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self), FALSE);
+
+ return self->priv->error_rate_threshold;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_signal_threshold_properties_get_dictionary: (skip)
+ */
+GVariant *
+mm_signal_threshold_properties_get_dictionary (MMSignalThresholdProperties *self)
+{
+ GVariantBuilder builder;
+
+ /* We do allow NULL */
+ if (!self)
+ return NULL;
+
+ g_return_val_if_fail (MM_IS_SIGNAL_THRESHOLD_PROPERTIES (self), NULL);
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (self->priv->rssi_threshold_set)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_RSSI_THRESHOLD,
+ g_variant_new_uint32 (self->priv->rssi_threshold));
+
+ if (self->priv->error_rate_threshold_set)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ERROR_RATE_THRESHOLD,
+ g_variant_new_boolean (self->priv->error_rate_threshold));
+
+ return g_variant_ref_sink (g_variant_builder_end (&builder));
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMSignalThresholdProperties *properties;
+ GError *error;
+} ParseKeyValueContext;
+
+static gboolean
+key_value_foreach (const gchar *key,
+ const gchar *value,
+ ParseKeyValueContext *ctx)
+{
+ if (g_str_equal (key, PROPERTY_RSSI_THRESHOLD)) {
+ guint rssi_threshold;
+
+ if (!mm_get_uint_from_str (value, &rssi_threshold)) {
+ g_set_error (&ctx->error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "invalid RSSI threshold value given: %s", value);
+ return FALSE;
+ }
+ mm_signal_threshold_properties_set_rssi (ctx->properties, rssi_threshold);
+ return TRUE;
+ }
+
+ if (g_str_equal (key, PROPERTY_ERROR_RATE_THRESHOLD)) {
+ gboolean error_rate_threshold;
+
+ error_rate_threshold = mm_common_get_boolean_from_string (value, &ctx->error);
+ if (ctx->error) {
+ g_prefix_error (&ctx->error, "invalid error rate threshold value given: ");
+ return FALSE;
+ }
+ mm_signal_threshold_properties_set_error_rate (ctx->properties, error_rate_threshold);
+ return TRUE;
+ }
+
+ g_set_error (&ctx->error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Invalid properties string, unsupported key '%s'", key);
+ return FALSE;
+}
+
+/**
+ * mm_signal_threshold_properties_new_from_string: (skip)
+ */
+MMSignalThresholdProperties *
+mm_signal_threshold_properties_new_from_string (const gchar *str,
+ GError **error)
+{
+ ParseKeyValueContext ctx;
+
+ ctx.error = NULL;
+ ctx.properties = mm_signal_threshold_properties_new ();
+
+ mm_common_parse_key_value_string (str,
+ &ctx.error,
+ (MMParseKeyValueForeachFn)key_value_foreach,
+ &ctx);
+ /* If error, destroy the object */
+ if (ctx.error) {
+ g_propagate_error (error, ctx.error);
+ g_clear_object (&ctx.properties);
+ }
+
+ return ctx.properties;
+}
+
+/*****************************************************************************/
+
+static gboolean
+consume_variant (MMSignalThresholdProperties *self,
+ const gchar *key,
+ GVariant *value,
+ GError **error)
+{
+ if (g_str_equal (key, PROPERTY_RSSI_THRESHOLD))
+ mm_signal_threshold_properties_set_rssi (self, g_variant_get_uint32 (value));
+ else if (g_str_equal (key, PROPERTY_ERROR_RATE_THRESHOLD))
+ mm_signal_threshold_properties_set_error_rate (self, g_variant_get_boolean (value));
+ else {
+ /* Set error */
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid properties dictionary, unexpected key '%s'", key);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * mm_signal_threshold_properties_new_from_dictionary: (skip)
+ */
+MMSignalThresholdProperties *
+mm_signal_threshold_properties_new_from_dictionary (GVariant *dictionary,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ MMSignalThresholdProperties *properties;
+
+ properties = mm_signal_threshold_properties_new ();
+ if (!dictionary)
+ return properties;
+
+ if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create signal threshold properties from dictionary: "
+ "invalid variant type received");
+ g_object_unref (properties);
+ return NULL;
+ }
+
+ g_variant_iter_init (&iter, dictionary);
+ while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ consume_variant (properties, key, value, &inner_error);
+ g_free (key);
+ g_variant_unref (value);
+ }
+
+ /* If error, destroy the object */
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_object_unref (properties);
+ properties = NULL;
+ }
+
+ return properties;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_signal_threshold_properties_new:
+ *
+ * Creates a new empty #MMSignalThresholdProperties.
+ *
+ * Returns: (transfer full): a #MMSignalThresholdProperties. The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.20
+ */
+MMSignalThresholdProperties *
+mm_signal_threshold_properties_new (void)
+{
+ return (MM_SIGNAL_THRESHOLD_PROPERTIES (
+ g_object_new (MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, NULL)));
+}
+
+static void
+mm_signal_threshold_properties_init (MMSignalThresholdProperties *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES,
+ MMSignalThresholdPropertiesPrivate);
+}
+
+static void
+mm_signal_threshold_properties_class_init (MMSignalThresholdPropertiesClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMSignalThresholdPropertiesPrivate));
+}
diff --git a/libmm-glib/mm-signal-threshold-properties.h b/libmm-glib/mm-signal-threshold-properties.h
new file mode 100644
index 00000000..ccba9a34
--- /dev/null
+++ b/libmm-glib/mm-signal-threshold-properties.h
@@ -0,0 +1,86 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
+ */
+
+#ifndef MM_SIGNAL_THRESHOLD_PROPERTIES_H
+#define MM_SIGNAL_THRESHOLD_PROPERTIES_H
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES (mm_signal_threshold_properties_get_type ())
+#define MM_SIGNAL_THRESHOLD_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdProperties))
+#define MM_SIGNAL_THRESHOLD_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdPropertiesClass))
+#define MM_IS_SIGNAL_THRESHOLD_PROPERTIES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES))
+#define MM_IS_SIGNAL_THRESHOLD_PROPERTIES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES))
+#define MM_SIGNAL_THRESHOLD_PROPERTIES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIGNAL_THRESHOLD_PROPERTIES, MMSignalThresholdPropertiesClass))
+
+typedef struct _MMSignalThresholdProperties MMSignalThresholdProperties;
+typedef struct _MMSignalThresholdPropertiesClass MMSignalThresholdPropertiesClass;
+typedef struct _MMSignalThresholdPropertiesPrivate MMSignalThresholdPropertiesPrivate;
+
+/**
+ * MMSignalThresholdProperties:
+ *
+ * The #MMSignalThresholdProperties structure contains private data and should
+ * only be accessed using the provided API.
+ */
+struct _MMSignalThresholdProperties {
+ /*< private >*/
+ GObject parent;
+ MMSignalThresholdPropertiesPrivate *priv;
+};
+
+struct _MMSignalThresholdPropertiesClass {
+ /*< private >*/
+ GObjectClass parent;
+};
+
+GType mm_signal_threshold_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSignalThresholdProperties, g_object_unref)
+
+MMSignalThresholdProperties *mm_signal_threshold_properties_new (void);
+
+void mm_signal_threshold_properties_set_rssi (MMSignalThresholdProperties *self,
+ guint rssi_threshold);
+void mm_signal_threshold_properties_set_error_rate (MMSignalThresholdProperties *self,
+ gboolean error_rate_threshold);
+guint mm_signal_threshold_properties_get_rssi (MMSignalThresholdProperties *self);
+gboolean mm_signal_threshold_properties_get_error_rate (MMSignalThresholdProperties *self);
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMSignalThresholdProperties *mm_signal_threshold_properties_new_from_string (const gchar *str,
+ GError **error);
+MMSignalThresholdProperties *mm_signal_threshold_properties_new_from_dictionary (GVariant *dictionary,
+ GError **error);
+GVariant *mm_signal_threshold_properties_get_dictionary (MMSignalThresholdProperties *self);
+
+#endif
+
+G_END_DECLS
+
+#endif /* MM_SIGNAL_THRESHOLD_PROPERTIES_H */
diff --git a/libmm-glib/mm-signal.c b/libmm-glib/mm-signal.c
index 57baf5d7..fbe44f97 100644
--- a/libmm-glib/mm-signal.c
+++ b/libmm-glib/mm-signal.c
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
*/
#include <string.h>
@@ -31,22 +32,26 @@
G_DEFINE_TYPE (MMSignal, mm_signal, G_TYPE_OBJECT)
-#define PROPERTY_RSSI "rssi"
-#define PROPERTY_ECIO "ecio"
-#define PROPERTY_SINR "sinr"
-#define PROPERTY_IO "io"
-#define PROPERTY_RSRQ "rsrq"
-#define PROPERTY_RSRP "rsrp"
-#define PROPERTY_SNR "snr"
+#define PROPERTY_RSSI "rssi"
+#define PROPERTY_RSCP "rscp"
+#define PROPERTY_ECIO "ecio"
+#define PROPERTY_SINR "sinr"
+#define PROPERTY_IO "io"
+#define PROPERTY_RSRQ "rsrq"
+#define PROPERTY_RSRP "rsrp"
+#define PROPERTY_SNR "snr"
+#define PROPERTY_ERROR_RATE "error-rate"
struct _MMSignalPrivate {
gdouble rssi;
+ gdouble rscp;
gdouble ecio;
gdouble sinr;
gdouble io;
gdouble rsrq;
gdouble rsrp;
gdouble snr;
+ gdouble error_rate;
};
/*****************************************************************************/
@@ -58,6 +63,8 @@ struct _MMSignalPrivate {
* Gets the RSSI (Received Signal Strength Indication), in dBm.
*
* Returns: the RSSI, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_rssi (MMSignal *self)
@@ -67,6 +74,9 @@ mm_signal_get_rssi (MMSignal *self)
return self->priv->rssi;
}
+/**
+ * mm_signal_set_rssi: (skip)
+ */
void
mm_signal_set_rssi (MMSignal *self,
gdouble value)
@@ -79,14 +89,48 @@ mm_signal_set_rssi (MMSignal *self,
/*****************************************************************************/
/**
+ * mm_signal_get_rscp:
+ * @self: a #MMSignal.
+ *
+ * Gets the RSCP (Received Signal Code Power), in dBm.
+ *
+ * Returns: the RSCP, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
+ */
+gdouble
+mm_signal_get_rscp (MMSignal *self)
+{
+ g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN);
+
+ return self->priv->rscp;
+}
+
+/**
+ * mm_signal_set_rscp: (skip)
+ */
+void
+mm_signal_set_rscp (MMSignal *self,
+ gdouble value)
+{
+ g_return_if_fail (MM_IS_SIGNAL (self));
+
+ self->priv->rscp = value;
+}
+
+/*****************************************************************************/
+
+/**
* mm_signal_get_ecio:
* @self: a #MMSignal.
*
- * Gets the Ec/Io, in dBm.
+ * Gets the Ec/Io, in dB.
*
* Only applicable to CDMA1x, CDMA EV-DO and UMTS (WCDMA).
*
* Returns: the ECIO, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_ecio (MMSignal *self)
@@ -96,6 +140,9 @@ mm_signal_get_ecio (MMSignal *self)
return self->priv->ecio;
}
+/**
+ * mm_signal_set_ecio: (skip)
+ */
void
mm_signal_set_ecio (MMSignal *self,
gdouble value)
@@ -116,6 +163,8 @@ mm_signal_set_ecio (MMSignal *self,
* Only applicable to CDMA EV-DO.
*
* Returns: the SINR, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_sinr (MMSignal *self)
@@ -125,6 +174,9 @@ mm_signal_get_sinr (MMSignal *self)
return self->priv->sinr;
}
+/**
+ * mm_signal_set_sinr: (skip)
+ */
void
mm_signal_set_sinr (MMSignal *self,
gdouble value)
@@ -145,6 +197,8 @@ mm_signal_set_sinr (MMSignal *self,
* Only applicable to CDMA EV-DO.
*
* Returns: the Io, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_io (MMSignal *self)
@@ -154,6 +208,9 @@ mm_signal_get_io (MMSignal *self)
return self->priv->io;
}
+/**
+ * mm_signal_set_io: (skip)
+ */
void
mm_signal_set_io (MMSignal *self,
gdouble value)
@@ -174,6 +231,8 @@ mm_signal_set_io (MMSignal *self,
* Only applicable to LTE.
*
* Returns: the RSRP, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_rsrp (MMSignal *self)
@@ -183,6 +242,9 @@ mm_signal_get_rsrp (MMSignal *self)
return self->priv->rsrp;
}
+/**
+ * mm_signal_set_rsrp: (skip)
+ */
void
mm_signal_set_rsrp (MMSignal *self,
gdouble value)
@@ -203,6 +265,8 @@ mm_signal_set_rsrp (MMSignal *self,
* Only applicable to LTE.
*
* Returns: the RSRQ, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_rsrq (MMSignal *self)
@@ -212,6 +276,9 @@ mm_signal_get_rsrq (MMSignal *self)
return self->priv->rsrq;
}
+/**
+ * mm_signal_set_rsrq: (skip)
+ */
void
mm_signal_set_rsrq (MMSignal *self,
gdouble value)
@@ -232,6 +299,8 @@ mm_signal_set_rsrq (MMSignal *self,
* Only applicable to LTE.
*
* Returns: the S/R ratio, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.2
*/
gdouble
mm_signal_get_snr (MMSignal *self)
@@ -241,6 +310,9 @@ mm_signal_get_snr (MMSignal *self)
return self->priv->snr;
}
+/**
+ * mm_signal_set_snr: (skip)
+ */
void
mm_signal_set_snr (MMSignal *self,
gdouble value)
@@ -253,12 +325,42 @@ mm_signal_set_snr (MMSignal *self,
/*****************************************************************************/
/**
- * mm_signal_get_dictionary:
- * @self: A #MMSignal.
+ * mm_signal_get_error_rate:
+ * @self: a #MMSignal.
+ *
+ * Gets the channel error rate (BER, BLER,... depends on the RAT), in
+ * percentage.
*
- * Gets a variant dictionary with the contents of @self.
+ * Applicable to all RAT.
*
- * Returns: (transfer full): A dictionary with the signal values. The returned value should be freed with g_variant_unref().
+ * Returns: the error rate, or %MM_SIGNAL_UNKNOWN if unknown.
+ *
+ * Since: 1.20
+ */
+gdouble
+mm_signal_get_error_rate (MMSignal *self)
+{
+ g_return_val_if_fail (MM_IS_SIGNAL (self), MM_SIGNAL_UNKNOWN);
+
+ return self->priv->error_rate;
+}
+
+/**
+ * mm_signal_set_error_rate: (skip)
+ */
+void
+mm_signal_set_error_rate (MMSignal *self,
+ gdouble value)
+{
+ g_return_if_fail (MM_IS_SIGNAL (self));
+
+ self->priv->error_rate = value;
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_signal_get_dictionary: (skip)
*/
GVariant *
mm_signal_get_dictionary (MMSignal *self)
@@ -279,6 +381,12 @@ mm_signal_get_dictionary (MMSignal *self)
PROPERTY_RSSI,
g_variant_new_double (self->priv->rssi));
+ if (self->priv->rscp != MM_SIGNAL_UNKNOWN)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_RSCP,
+ g_variant_new_double (self->priv->rscp));
+
if (self->priv->ecio != MM_SIGNAL_UNKNOWN)
g_variant_builder_add (&builder,
"{sv}",
@@ -315,6 +423,12 @@ mm_signal_get_dictionary (MMSignal *self)
PROPERTY_SNR,
g_variant_new_double (self->priv->snr));
+ if (self->priv->error_rate != MM_SIGNAL_UNKNOWN)
+ g_variant_builder_add (&builder,
+ "{sv}",
+ PROPERTY_ERROR_RATE,
+ g_variant_new_double (self->priv->error_rate));
+
return g_variant_ref_sink (g_variant_builder_end (&builder));
}
@@ -328,6 +442,8 @@ consume_variant (MMSignal *self,
{
if (g_str_equal (key, PROPERTY_RSSI))
self->priv->rssi = g_variant_get_double (value);
+ else if (g_str_equal (key, PROPERTY_RSCP))
+ self->priv->rscp = g_variant_get_double (value);
else if (g_str_equal (key, PROPERTY_ECIO))
self->priv->ecio = g_variant_get_double (value);
else if (g_str_equal (key, PROPERTY_SINR))
@@ -340,6 +456,8 @@ consume_variant (MMSignal *self,
self->priv->rsrq = g_variant_get_double (value);
else if (g_str_equal (key, PROPERTY_SNR))
self->priv->snr = g_variant_get_double (value);
+ else if (g_str_equal (key, PROPERTY_ERROR_RATE))
+ self->priv->error_rate = g_variant_get_double (value);
else {
/* Set error */
g_set_error (error,
@@ -354,14 +472,7 @@ consume_variant (MMSignal *self,
}
/**
- * mm_signal_new_from_dictionary:
- * @dictionary: A variant dictionary with the signal information.
- * @error: Return location for error or %NULL.
- *
- * Creates a new #MMSignal object with the values exposed in
- * the dictionary.
- *
- * Returns: (transfer full): A #MMSignal or %NULL if @error is set. The returned value should be freed with g_object_unref().
+ * mm_signal_new_from_dictionary: (skip)
*/
MMSignal *
mm_signal_new_from_dictionary (GVariant *dictionary,
@@ -413,6 +524,9 @@ mm_signal_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
+/**
+ * mm_signal_new: (skip)
+ */
MMSignal *
mm_signal_new (void)
{
@@ -424,12 +538,14 @@ mm_signal_init (MMSignal *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SIGNAL, MMSignalPrivate);
self->priv->rssi = MM_SIGNAL_UNKNOWN;
+ self->priv->rscp = MM_SIGNAL_UNKNOWN;
self->priv->ecio = MM_SIGNAL_UNKNOWN;
self->priv->sinr = MM_SIGNAL_UNKNOWN;
self->priv->io = MM_SIGNAL_UNKNOWN;
self->priv->rsrq = MM_SIGNAL_UNKNOWN;
self->priv->rsrp = MM_SIGNAL_UNKNOWN;
self->priv->snr = MM_SIGNAL_UNKNOWN;
+ self->priv->error_rate = MM_SIGNAL_UNKNOWN;
}
static void
diff --git a/libmm-glib/mm-signal.h b/libmm-glib/mm-signal.h
index 47391f0e..b0950a79 100644
--- a/libmm-glib/mm-signal.h
+++ b/libmm-glib/mm-signal.h
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
*/
#ifndef MM_SIGNAL_H
@@ -28,9 +29,11 @@ G_BEGIN_DECLS
/**
* MM_SIGNAL_UNKNOWN:
*
- * Identifier for an unknown signal value.
+ * Identifier for an unknown signal or error rate value.
+ *
+ * Since: 1.2
*/
-#define MM_SIGNAL_UNKNOWN G_MINDOUBLE
+#define MM_SIGNAL_UNKNOWN -G_MAXDOUBLE
#define MM_TYPE_SIGNAL (mm_signal_get_type ())
#define MM_SIGNAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIGNAL, MMSignal))
@@ -61,14 +64,17 @@ struct _MMSignalClass {
};
GType mm_signal_get_type (void);
-
-gdouble mm_signal_get_rssi (MMSignal *self);
-gdouble mm_signal_get_ecio (MMSignal *self);
-gdouble mm_signal_get_sinr (MMSignal *self);
-gdouble mm_signal_get_io (MMSignal *self);
-gdouble mm_signal_get_rsrq (MMSignal *self);
-gdouble mm_signal_get_rsrp (MMSignal *self);
-gdouble mm_signal_get_snr (MMSignal *self);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSignal, g_object_unref)
+
+gdouble mm_signal_get_rssi (MMSignal *self);
+gdouble mm_signal_get_rscp (MMSignal *self);
+gdouble mm_signal_get_ecio (MMSignal *self);
+gdouble mm_signal_get_sinr (MMSignal *self);
+gdouble mm_signal_get_io (MMSignal *self);
+gdouble mm_signal_get_rsrq (MMSignal *self);
+gdouble mm_signal_get_rsrp (MMSignal *self);
+gdouble mm_signal_get_snr (MMSignal *self);
+gdouble mm_signal_get_error_rate (MMSignal *self);
/*****************************************************************************/
/* ModemManager/libmm-glib/mmcli specific methods */
@@ -83,13 +89,15 @@ MMSignal *mm_signal_new (void);
MMSignal *mm_signal_new_from_dictionary (GVariant *dictionary,
GError **error);
-void mm_signal_set_rssi (MMSignal *self, gdouble value);
-void mm_signal_set_ecio (MMSignal *self, gdouble value);
-void mm_signal_set_sinr (MMSignal *self, gdouble value);
-void mm_signal_set_io (MMSignal *self, gdouble value);
-void mm_signal_set_rsrq (MMSignal *self, gdouble value);
-void mm_signal_set_rsrp (MMSignal *self, gdouble value);
-void mm_signal_set_snr (MMSignal *self, gdouble value);
+void mm_signal_set_rssi (MMSignal *self, gdouble value);
+void mm_signal_set_rscp (MMSignal *self, gdouble value);
+void mm_signal_set_ecio (MMSignal *self, gdouble value);
+void mm_signal_set_sinr (MMSignal *self, gdouble value);
+void mm_signal_set_io (MMSignal *self, gdouble value);
+void mm_signal_set_rsrq (MMSignal *self, gdouble value);
+void mm_signal_set_rsrp (MMSignal *self, gdouble value);
+void mm_signal_set_snr (MMSignal *self, gdouble value);
+void mm_signal_set_error_rate (MMSignal *self, gdouble value);
#endif
diff --git a/libmm-glib/mm-sim-preferred-network.c b/libmm-glib/mm-sim-preferred-network.c
new file mode 100644
index 00000000..020cdab7
--- /dev/null
+++ b/libmm-glib/mm-sim-preferred-network.c
@@ -0,0 +1,229 @@
+/* -*- 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) 2021 UROS Ltd
+ */
+
+#include "mm-sim-preferred-network.h"
+
+struct _MMSimPreferredNetwork {
+ gchar *operator_code;
+ MMModemAccessTechnology access_technology;
+};
+
+static MMSimPreferredNetwork *
+mm_sim_preferred_network_copy (MMSimPreferredNetwork *preferred_network)
+{
+ MMSimPreferredNetwork *preferred_network_copy;
+
+ preferred_network_copy = g_slice_new0 (MMSimPreferredNetwork);
+ preferred_network_copy->operator_code = g_strdup (preferred_network->operator_code);
+ preferred_network_copy->access_technology = preferred_network->access_technology;
+
+ return preferred_network_copy;
+}
+
+G_DEFINE_BOXED_TYPE (MMSimPreferredNetwork, mm_sim_preferred_network, (GBoxedCopyFunc) mm_sim_preferred_network_copy, (GBoxedFreeFunc) mm_sim_preferred_network_free)
+
+/**
+ * mm_sim_preferred_network_free:
+ * @self: A #MMSimPreferredNetwork.
+ *
+ * Frees a #MMSimPreferredNetwork.
+ *
+ * Since: 1.18
+ */
+void
+mm_sim_preferred_network_free (MMSimPreferredNetwork *self)
+{
+ if (!self)
+ return;
+
+ g_free (self->operator_code);
+ g_slice_free (MMSimPreferredNetwork, self);
+}
+
+/**
+ * mm_sim_preferred_network_get_operator_code:
+ * @self: A #MMSimPreferredNetwork.
+ *
+ * Get the operator code (MCCMNC) of the preferred network.
+ *
+ * Returns: (transfer none): The operator code, or %NULL if none available.
+ *
+ * Since: 1.18
+ */
+const gchar *
+mm_sim_preferred_network_get_operator_code (const MMSimPreferredNetwork *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+
+ return self->operator_code;
+}
+
+/**
+ * mm_sim_preferred_network_get_access_technology:
+ * @self: A #MMSimPreferredNetwork.
+ *
+ * Get the access technology mask of the preferred network.
+ *
+ * Returns: A #MMModemAccessTechnology.
+ *
+ * Since: 1.18
+ */
+MMModemAccessTechnology
+mm_sim_preferred_network_get_access_technology (const MMSimPreferredNetwork *self)
+{
+ g_return_val_if_fail (self != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+
+ return self->access_technology;
+}
+
+/**
+ * mm_sim_preferred_network_set_operator_code:
+ * @self: A #MMSimPreferredNetwork.
+ * @operator_code: Operator code
+ *
+ * Set the operator code (MCCMNC) of this preferred network.
+ *
+ * Since: 1.18
+ */
+void
+mm_sim_preferred_network_set_operator_code (MMSimPreferredNetwork *self,
+ const gchar *operator_code)
+{
+ g_return_if_fail (self != NULL);
+
+ g_free (self->operator_code);
+ self->operator_code = g_strdup (operator_code);
+}
+
+/**
+ * mm_sim_preferred_network_set_access_technology:
+ * @self: A #MMSimPreferredNetwork.
+ * @access_technology: A #MMModemAccessTechnology mask.
+ *
+ * Set the desired access technologies of this preferred network entry.
+ *
+ * Since: 1.18
+ */
+void
+mm_sim_preferred_network_set_access_technology (MMSimPreferredNetwork *self,
+ MMModemAccessTechnology access_technology)
+{
+ g_return_if_fail (self != NULL);
+
+ self->access_technology = access_technology;
+}
+
+/**
+ * mm_sim_preferred_network_new:
+ *
+ * Creates a new empty #MMSimPreferredNetwork.
+ *
+ * Returns: (transfer full): a #MMSimPreferredNetwork. The returned value should be freed
+ * with mm_sim_preferred_network_free().
+ *
+ * Since: 1.18
+ */
+MMSimPreferredNetwork *
+mm_sim_preferred_network_new (void)
+{
+ return g_slice_new0 (MMSimPreferredNetwork);
+}
+
+/**
+ * mm_sim_preferred_network_new_from_variant: (skip)
+ */
+MMSimPreferredNetwork *
+mm_sim_preferred_network_new_from_variant (GVariant *variant)
+{
+ MMSimPreferredNetwork *preferred_net;
+
+ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE ("(su)")),
+ NULL);
+
+ preferred_net = mm_sim_preferred_network_new ();
+ g_variant_get (variant, "(su)", &preferred_net->operator_code, &preferred_net->access_technology);
+ return preferred_net;
+}
+
+/**
+ * mm_sim_preferred_network_get_tuple: (skip)
+ */
+GVariant *
+mm_sim_preferred_network_get_tuple (const MMSimPreferredNetwork *self)
+{
+ return g_variant_new ("(su)",
+ self->operator_code,
+ self->access_technology);
+}
+
+/**
+ * mm_sim_preferred_network_list_get_variant: (skip)
+ */
+GVariant *
+mm_sim_preferred_network_list_get_variant (const GList *preferred_network_list)
+{
+ GVariantBuilder builder;
+ const GList *iter;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(su)"));
+ for (iter = preferred_network_list; iter; iter = g_list_next (iter)) {
+ g_variant_builder_add_value (&builder,
+ mm_sim_preferred_network_get_tuple ((const MMSimPreferredNetwork *) iter->data));
+ }
+ return g_variant_builder_end (&builder);
+}
+
+/**
+ * mm_sim_preferred_network_list_new_from_variant: (skip)
+ */
+GList *
+mm_sim_preferred_network_list_new_from_variant (GVariant *variant)
+{
+ GList *network_list = NULL;
+ GVariant *child;
+ GVariantIter iter;
+
+ g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE ("a(su)")), NULL);
+
+ g_variant_iter_init (&iter, variant);
+ while ((child = g_variant_iter_next_value (&iter))) {
+ MMSimPreferredNetwork *preferred_net;
+
+ preferred_net = mm_sim_preferred_network_new_from_variant (child);
+ if (preferred_net)
+ network_list = g_list_append (network_list, preferred_net);
+ g_variant_unref (child);
+ }
+
+ return network_list;
+}
+
+/**
+ * mm_sim_preferred_network_list_copy: (skip)
+ */
+GList *
+mm_sim_preferred_network_list_copy (GList *preferred_network_list)
+{
+ return g_list_copy_deep (preferred_network_list, (GCopyFunc) mm_sim_preferred_network_copy, NULL);
+}
+
+/**
+ * mm_sim_preferred_network_list_free: (skip)
+ */
+void
+mm_sim_preferred_network_list_free (GList *preferred_network_list)
+{
+ g_list_free_full (preferred_network_list, (GDestroyNotify) mm_sim_preferred_network_free);
+}
diff --git a/libmm-glib/mm-sim-preferred-network.h b/libmm-glib/mm-sim-preferred-network.h
new file mode 100644
index 00000000..f514540c
--- /dev/null
+++ b/libmm-glib/mm-sim-preferred-network.h
@@ -0,0 +1,71 @@
+/* -*- 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) 2021 UROS Ltd
+ */
+
+#ifndef MM_SIM_PREFERRED_NETWORK_H
+#define MM_SIM_PREFERRED_NETWORK_H
+
+#if !defined (__LIBMM_GLIB_H_INSIDE__) && !defined (LIBMM_GLIB_COMPILATION)
+#error "Only <libmm-glib.h> can be included directly."
+#endif
+
+#include <ModemManager.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+/**
+ * MMSimPreferredNetwork:
+ *
+ * The #MMSimPreferredNetwork structure contains private data and should only be accessed
+ * using the provided API.
+ */
+typedef struct _MMSimPreferredNetwork MMSimPreferredNetwork;
+
+#define MM_TYPE_SIM_PREFERRED_NETWORK (mm_sim_preferred_network_get_type ())
+GType mm_sim_preferred_network_get_type (void);
+
+MMSimPreferredNetwork * mm_sim_preferred_network_new (void);
+
+const gchar *mm_sim_preferred_network_get_operator_code (const MMSimPreferredNetwork *self);
+MMModemAccessTechnology mm_sim_preferred_network_get_access_technology (const MMSimPreferredNetwork *self);
+
+void mm_sim_preferred_network_set_operator_code (MMSimPreferredNetwork *self,
+ const gchar *operator_code);
+void mm_sim_preferred_network_set_access_technology (MMSimPreferredNetwork *self,
+ MMModemAccessTechnology access_technology);
+
+void mm_sim_preferred_network_free (MMSimPreferredNetwork *self);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimPreferredNetwork, mm_sim_preferred_network_free)
+
+/*****************************************************************************/
+/* ModemManager/libmm-glib/mmcli specific methods */
+
+#if defined (_LIBMM_INSIDE_MM) || \
+ defined (_LIBMM_INSIDE_MMCLI) || \
+ defined (LIBMM_GLIB_COMPILATION)
+
+MMSimPreferredNetwork * mm_sim_preferred_network_new_from_variant (GVariant *variant);
+
+GVariant *mm_sim_preferred_network_get_tuple (const MMSimPreferredNetwork *self);
+GVariant *mm_sim_preferred_network_list_get_variant (const GList *preferred_network_list);
+GList *mm_sim_preferred_network_list_new_from_variant (GVariant *variant);
+GList *mm_sim_preferred_network_list_copy (GList *preferred_network_list);
+void mm_sim_preferred_network_list_free (GList *preferred_network_list);
+#endif
+
+G_END_DECLS
+
+#endif /* MM_SIM_PREFERRED_NETWORK_H */
diff --git a/libmm-glib/mm-sim.c b/libmm-glib/mm-sim.c
index 74cb3f0b..12665f07 100644
--- a/libmm-glib/mm-sim.c
+++ b/libmm-glib/mm-sim.c
@@ -23,6 +23,7 @@
#include "mm-helpers.h"
#include "mm-sim.h"
+#include "mm-sim-preferred-network.h"
/**
* SECTION: mm-sim
@@ -47,6 +48,8 @@ G_DEFINE_TYPE (MMSim, mm_sim, MM_GDBUS_TYPE_SIM_PROXY)
* Gets the DBus path of the #MMSim object.
*
* Returns: (transfer none): The DBus path of the #MMSim object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sim_get_path (MMSim *self)
@@ -63,7 +66,10 @@ mm_sim_get_path (MMSim *self)
*
* Gets a copy of the DBus path of the #MMSim object.
*
- * Returns: (transfer full): The DBus path of the #MMSim object. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMSim object. The returned
+ * value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sim_dup_path (MMSim *self)
@@ -82,17 +88,39 @@ mm_sim_dup_path (MMSim *self)
/*****************************************************************************/
/**
+ * mm_sim_get_active:
+ * @self: A #MMSim.
+ *
+ * Checks whether the #MMSim is currently active.
+ *
+ * Returns: %TRUE if the SIM is active, %FALSE otherwise.
+ *
+ * Since: 1.16
+ */
+gboolean
+mm_sim_get_active (MMSim *self)
+{
+ g_return_val_if_fail (MM_IS_SIM (self), FALSE);
+
+ return mm_gdbus_sim_get_active (MM_GDBUS_SIM (self));
+}
+
+/*****************************************************************************/
+
+/**
* mm_sim_get_identifier:
* @self: A #MMSim.
*
* Gets the unique SIM identifier of the #MMSim object.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sim_dup_identifier() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sim_dup_identifier() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The unique identifier of the #MMSim object, or
+ * %NULL if it couldn't be retrieved.
*
- * Returns: (transfer none): The unique identifier of the #MMSim object, or %NULL if it couldn't be retrieved.
+ * Since: 1.0
*/
const gchar *
mm_sim_get_identifier (MMSim *self)
@@ -109,7 +137,11 @@ mm_sim_get_identifier (MMSim *self)
*
* Gets a copy of the unique SIM identifier of the #MMSim object.
*
- * Returns: (transfer full): The unique identifier of the #MMSim object, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Returns: (transfer full): The unique identifier of the #MMSim object, or
+ * %NULL if it couldn't be retrieved. The returned value should be freed with
+ * g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sim_dup_identifier (MMSim *self)
@@ -126,14 +158,17 @@ mm_sim_dup_identifier (MMSim *self)
* mm_sim_get_imsi:
* @self: A #MMSim.
*
- * Gets the International Mobile Subscriber Identity (IMSI) of the #MMSim object.
+ * Gets the International Mobile Subscriber Identity (IMSI) of the #MMSim
+ * object.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sim_dup_imsi() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sim_dup_imsi() if on another thread.</warning>
*
- * Returns: (transfer none): The IMSI of the #MMSim object, or %NULL if it couldn't be retrieved.
+ * Returns: (transfer none): The IMSI of the #MMSim object, or %NULL if it
+ * couldn't be retrieved.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sim_get_imsi (MMSim *self)
@@ -148,9 +183,13 @@ mm_sim_get_imsi (MMSim *self)
* mm_sim_dup_imsi:
* @self: A #MMSim.
*
- * Gets a copy of the International Mobile Subscriber Identity (IMSI) of the #MMSim object.
+ * Gets a copy of the International Mobile Subscriber Identity (IMSI) of the
+ * #MMSim object.
+ *
+ * Returns: (transfer full): The IMSI of the #MMSim object, or %NULL if it
+ * couldn't be retrieved. The returned value should be freed with g_free().
*
- * Returns: (transfer full): The IMSI of the #MMSim object, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_sim_dup_imsi (MMSim *self)
@@ -164,17 +203,65 @@ mm_sim_dup_imsi (MMSim *self)
/*****************************************************************************/
/**
+ * mm_sim_get_eid:
+ * @self: A #MMSim.
+ *
+ * Gets the Embedded UICC ID (or EID) of the #MMSim object.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sim_dup_eid() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The EID of the #MMSim object, or %NULL if it
+ * couldn't be retrieved.
+ *
+ * Since: 1.16
+ */
+const gchar *
+mm_sim_get_eid (MMSim *self)
+{
+ g_return_val_if_fail (MM_IS_SIM (self), NULL);
+
+ RETURN_NON_EMPTY_CONSTANT_STRING (
+ mm_gdbus_sim_get_eid (MM_GDBUS_SIM (self)));
+}
+
+/**
+ * mm_sim_dup_eid:
+ * @self: A #MMSim.
+ *
+ * Gets a copy of the Embedded UICC ID (EID) of the #MMSim object.
+ *
+ * Returns: (transfer full): The EID of the #MMSim object, or %NULL if it
+ * couldn't be retrieved. The returned value should be freed with g_free().
+ *
+ * Since: 1.16
+ */
+gchar *
+mm_sim_dup_eid (MMSim *self)
+{
+ g_return_val_if_fail (MM_IS_SIM (self), NULL);
+
+ RETURN_NON_EMPTY_STRING (
+ mm_gdbus_sim_dup_eid (MM_GDBUS_SIM (self)));
+}
+
+/*****************************************************************************/
+
+/**
* mm_sim_get_operator_identifier:
* @self: A #MMSim.
*
* Gets the Operator Identifier of the #MMSim object.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sim_dup_operator_identifier() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sim_dup_operator_identifier() if on another thread.</warning>
*
- * Returns: (transfer none): The Operator Identifier of the #MMSim object, or %NULL if it couldn't be retrieved.
+ * Returns: (transfer none): The Operator Identifier of the #MMSim object, or
+ * %NULL if it couldn't be retrieved.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sim_get_operator_identifier (MMSim *self)
@@ -191,7 +278,11 @@ mm_sim_get_operator_identifier (MMSim *self)
*
* Gets a copy of the Operator Identifier of the #MMSim object.
*
- * Returns: (transfer full): The Operator Identifier of the #MMSim object, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Returns: (transfer full): The Operator Identifier of the #MMSim object, or
+ * %NULL if it couldn't be retrieved. The returned value should be freed with
+ * g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sim_dup_operator_identifier (MMSim *self)
@@ -210,12 +301,14 @@ mm_sim_dup_operator_identifier (MMSim *self)
*
* Gets the Operator Name of the #MMSim object.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sim_dup_operator_name() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sim_dup_operator_name() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The Operator Name of the #MMSim object, or %NULL if
+ * it couldn't be retrieved.
*
- * Returns: (transfer none): The Operator Name of the #MMSim object, or %NULL if it couldn't be retrieved.
+ * Since: 1.0
*/
const gchar *
mm_sim_get_operator_name (MMSim *self)
@@ -232,7 +325,10 @@ mm_sim_get_operator_name (MMSim *self)
*
* Gets a copy of the Operator Name of the #MMSim object.
*
- * Returns: (transfer full): The Operator Name of the #MMSim object, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Returns: (transfer full): The Operator Name of the #MMSim object, or %NULL if
+ * it couldn't be retrieved. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sim_dup_operator_name (MMSim *self)
@@ -246,14 +342,61 @@ mm_sim_dup_operator_name (MMSim *self)
/*****************************************************************************/
/**
+ * mm_sim_get_emergency_numbers:
+ * @self: A #MMSim.
+ *
+ * Gets the list of emergency call numbers programmed in the SIM card.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sim_dup_emergency_numbers() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The emergency numbers, or %NULL if none available.
+ * Do not free the returned value, it belongs to @self.
+ *
+ * Since: 1.12
+ */
+const gchar * const *
+mm_sim_get_emergency_numbers (MMSim *self)
+{
+ g_return_val_if_fail (MM_IS_SIM (self), NULL);
+
+ return mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self));
+}
+
+/**
+ * mm_sim_dup_emergency_numbers:
+ * @self: A #MMSim.
+ *
+ * Gets a copy of the list of emergency call numbers programmed in the SIM card.
+ *
+ * Returns: (transfer full): The emergency numbers, or %NULL if none available.
+ * The returned value should be freed with g_strfreev().
+ *
+ * Since: 1.12
+ */
+gchar **
+mm_sim_dup_emergency_numbers (MMSim *self)
+{
+ g_return_val_if_fail (MM_IS_SIM (self), NULL);
+
+ return mm_gdbus_sim_dup_emergency_numbers (MM_GDBUS_SIM (self));
+}
+
+/*****************************************************************************/
+
+/**
* mm_sim_send_pin_finish:
* @self: A #MMSim.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sim_send_pin().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sim_send_pin().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sim_send_pin().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_send_pin_finish (MMSim *self,
@@ -270,15 +413,21 @@ mm_sim_send_pin_finish (MMSim *self,
* @self: A #MMSim.
* @pin: The PIN code.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously sends the PIN code to the SIM card.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sim_send_pin_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_send_pin_finish() to get the result of the operation.
+ *
+ * See mm_sim_send_pin_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_sim_send_pin_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_sim_send_pin (MMSim *self,
@@ -305,10 +454,12 @@ mm_sim_send_pin (MMSim *self,
*
* Synchronously sends the PIN to the SIM card.
*
- * The calling thread is blocked until a reply is received.
- * See mm_sim_send_pin() for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See
+ * mm_sim_send_pin() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_send_pin_sync (MMSim *self,
@@ -329,12 +480,15 @@ mm_sim_send_pin_sync (MMSim *self,
/**
* mm_sim_send_puk_finish:
* @self: A #MMSim.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sim_send_puk().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sim_send_puk().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sim_send_puk().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_send_puk_finish (MMSim *self,
@@ -352,15 +506,21 @@ mm_sim_send_puk_finish (MMSim *self,
* @puk: The PUK code.
* @pin: The PIN code.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously sends the PUK code to the SIM card.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sim_send_puk_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_send_puk_finish() to get the result of the operation.
*
- * See mm_sim_send_puk_sync() for the synchronous, blocking version of this method.
+ * See mm_sim_send_puk_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.0
*/
void
mm_sim_send_puk (MMSim *self,
@@ -393,7 +553,9 @@ mm_sim_send_puk (MMSim *self,
* The calling thread is blocked until a reply is received.
* See mm_sim_send_puk() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_send_puk_sync (MMSim *self,
@@ -416,12 +578,15 @@ mm_sim_send_puk_sync (MMSim *self,
/**
* mm_sim_enable_pin_finish:
* @self: A #MMSim.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sim_enable_pin().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sim_enable_pin().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sim_enable_pin().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_enable_pin_finish (MMSim *self,
@@ -438,15 +603,21 @@ mm_sim_enable_pin_finish (MMSim *self,
* @self: A #MMSim.
* @pin: The PIN code.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously enables requesting the PIN code in the SIM card.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sim_enable_pin_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_enable_pin_finish() to get the result of the operation.
*
- * See mm_sim_enable_pin_sync() for the synchronous, blocking version of this method.
+ * See mm_sim_enable_pin_sync() for the synchronous, blocking version of this
+ * method.
+ *
+ * Since: 1.0
*/
void
mm_sim_enable_pin (MMSim *self,
@@ -477,7 +648,9 @@ mm_sim_enable_pin (MMSim *self,
* The calling thread is blocked until a reply is received.
* See mm_sim_enable_pin() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_enable_pin_sync (MMSim *self,
@@ -499,12 +672,15 @@ mm_sim_enable_pin_sync (MMSim *self,
/**
* mm_sim_disable_pin_finish:
* @self: A #MMSim.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sim_disable_pin().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sim_disable_pin().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sim_disable_pin().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_disable_pin_finish (MMSim *self,
@@ -521,15 +697,21 @@ mm_sim_disable_pin_finish (MMSim *self,
* @self: A #MMSim.
* @pin: The PIN code.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously disables requesting the PIN code in the SIM card.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sim_disable_pin_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_disable_pin_finish() to get the result of the operation.
+ *
+ * See mm_sim_disable_pin_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_sim_disable_pin_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_sim_disable_pin (MMSim *self,
@@ -560,7 +742,9 @@ mm_sim_disable_pin (MMSim *self,
* The calling thread is blocked until a reply is received.
* See mm_sim_disable_pin() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_disable_pin_sync (MMSim *self,
@@ -582,12 +766,15 @@ mm_sim_disable_pin_sync (MMSim *self,
/**
* mm_sim_change_pin_finish:
* @self: A #MMSim.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sim_change_pin().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sim_change_pin().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sim_change_pin().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_change_pin_finish (MMSim *self,
@@ -605,15 +792,21 @@ mm_sim_change_pin_finish (MMSim *self,
* @old_pin: The current PIN code.
* @new_pin: The new PIN code to be set.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously changes the PIN code in the SIM card.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sim_change_pin_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_change_pin_finish() to get the result of the operation.
+ *
+ * See mm_sim_change_pin_sync() for the synchronous, blocking version of this
+ * method.
*
- * See mm_sim_change_pin_sync() for the synchronous, blocking version of this method.
+ * Since: 1.0
*/
void
mm_sim_change_pin (MMSim *self,
@@ -646,7 +839,9 @@ mm_sim_change_pin (MMSim *self,
* The calling thread is blocked until a reply is received.
* See mm_sim_change_pin() for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sim_change_pin_sync (MMSim *self,
@@ -666,6 +861,141 @@ mm_sim_change_pin_sync (MMSim *self,
/*****************************************************************************/
+/**
+ * mm_sim_get_preferred_networks:
+ * @self: A #MMSim.
+ *
+ * Gets the list of #MMSimPreferredNetwork objects exposed by this
+ * #MMSim.
+ *
+ * Returns: (transfer full) (element-type ModemManager.SimPreferredNetwork): a list of
+ * #MMSimPreferredNetwork objects, or #NULL. The returned value should
+ * be freed with g_list_free_full() using mm_sim_preferred_network_free() as #GDestroyNotify
+ * function.
+ *
+ * Since: 1.18
+ */
+GList *
+mm_sim_get_preferred_networks (MMSim *self)
+{
+ GList *network_list = NULL;
+ GVariant *container;
+
+ g_return_val_if_fail (MM_IS_SIM (self), NULL);
+
+ container = mm_gdbus_sim_get_preferred_networks (MM_GDBUS_SIM (self));
+ network_list = mm_sim_preferred_network_list_new_from_variant (container);
+
+ return network_list;
+}
+
+/**
+ * mm_sim_set_preferred_networks_finish:
+ * @self: A #MMSim.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sim_set_preferred_networks().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_sim_set_preferred_networks().
+ *
+ * Returns: %TRUE if the operation was successful, %FALSE if @error is set.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_sim_set_preferred_networks_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_SIM (self), FALSE);
+
+ return mm_gdbus_sim_call_set_preferred_networks_finish (MM_GDBUS_SIM (self), res, error);
+}
+
+/**
+ * mm_sim_set_preferred_networks:
+ * @self: A #MMSim.
+ * @preferred_networks: (element-type ModemManager.SimPreferredNetwork):
+ * A list of #MMSimPreferredNetwork objects
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
+ * @user_data: User data to pass to @callback.
+ *
+ * Asynchronously sets the preferred network list of this #MMSim.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_set_preferred_networks_finish() to get the result of
+ * the operation.
+ *
+ * Since: 1.18
+ */
+void
+mm_sim_set_preferred_networks (MMSim *self,
+ const GList *preferred_networks,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GVariant *networks_list;
+
+ g_return_if_fail (MM_IS_SIM (self));
+
+ networks_list = mm_sim_preferred_network_list_get_variant (preferred_networks);
+
+ mm_gdbus_sim_call_set_preferred_networks (MM_GDBUS_SIM (self),
+ networks_list,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_sim_set_preferred_networks_sync:
+ * @self: A #MMSim.
+ * @preferred_networks: (element-type ModemManager.SimPreferredNetwork):
+ * A list of #MMSimPreferredNetwork objects
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously sets the preferred network list of this #MMSim.
+ *
+ * The calling thread is blocked until a reply is received. See
+ * mm_sim_set_preferred_networks() for the asynchronous
+ * version of this method.
+ *
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sim_set_preferred_networks_finish() to get the result of
+ * the operation.
+ *
+ * Since: 1.18
+ */
+gboolean
+mm_sim_set_preferred_networks_sync (MMSim *self,
+ const GList *preferred_networks,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean result;
+ GVariant *networks_list;
+
+ g_return_val_if_fail (MM_IS_SIM (self), FALSE);
+
+ networks_list = mm_sim_preferred_network_list_get_variant (preferred_networks);
+
+ result = mm_gdbus_sim_call_set_preferred_networks_sync (MM_GDBUS_SIM (self),
+ networks_list,
+ cancellable,
+ error);
+ return result;
+}
+
+/*****************************************************************************/
+
static void
mm_sim_init (MMSim *self)
{
diff --git a/libmm-glib/mm-sim.h b/libmm-glib/mm-sim.h
index 1998bf6e..d64b46c9 100644
--- a/libmm-glib/mm-sim.h
+++ b/libmm-glib/mm-sim.h
@@ -62,22 +62,33 @@ struct _MMSimClass {
};
GType mm_sim_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSim, g_object_unref)
const gchar *mm_sim_get_path (MMSim *self);
gchar *mm_sim_dup_path (MMSim *self);
+gboolean mm_sim_get_active (MMSim *self);
+
const gchar *mm_sim_get_identifier (MMSim *self);
gchar *mm_sim_dup_identifier (MMSim *self);
const gchar *mm_sim_get_imsi (MMSim *self);
gchar *mm_sim_dup_imsi (MMSim *self);
+const gchar *mm_sim_get_eid (MMSim *self);
+gchar *mm_sim_dup_eid (MMSim *self);
+
const gchar *mm_sim_get_operator_identifier (MMSim *self);
gchar *mm_sim_dup_operator_identifier (MMSim *self);
const gchar *mm_sim_get_operator_name (MMSim *self);
gchar *mm_sim_dup_operator_name (MMSim *self);
+const gchar * const *mm_sim_get_emergency_numbers (MMSim *self);
+gchar **mm_sim_dup_emergency_numbers (MMSim *self);
+
+GList* mm_sim_get_preferred_networks (MMSim *self);
+
void mm_sim_send_pin (MMSim *self,
const gchar *pin,
GCancellable *cancellable,
@@ -148,6 +159,19 @@ gboolean mm_sim_change_pin_sync (MMSim *self,
GCancellable *cancellable,
GError **error);
+void mm_sim_set_preferred_networks (MMSim *self,
+ const GList *preferred_networks,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_sim_set_preferred_networks_finish (MMSim *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_sim_set_preferred_networks_sync (MMSim *self,
+ const GList *preferred_networks,
+ GCancellable *cancellable,
+ GError **error);
+
G_END_DECLS
#endif /* _MM_SIM_H_ */
diff --git a/libmm-glib/mm-simple-connect-properties.c b/libmm-glib/mm-simple-connect-properties.c
index 200b7c38..353a1257 100644
--- a/libmm-glib/mm-simple-connect-properties.c
+++ b/libmm-glib/mm-simple-connect-properties.c
@@ -17,6 +17,7 @@
#include "mm-errors-types.h"
#include "mm-common-helpers.h"
+#include "mm-3gpp-profile.h"
#include "mm-simple-connect-properties.h"
/**
@@ -31,10 +32,10 @@
* mm_modem_simple_connect() or mm_modem_simple_connect_sync().
*/
-G_DEFINE_TYPE (MMSimpleConnectProperties, mm_simple_connect_properties, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMSimpleConnectProperties, mm_simple_connect_properties, G_TYPE_OBJECT)
-#define PROPERTY_PIN "pin"
-#define PROPERTY_OPERATOR_ID "operator-id"
+#define PROPERTY_PIN "pin"
+#define PROPERTY_OPERATOR_ID "operator-id"
struct _MMSimpleConnectPropertiesPrivate {
/* PIN */
@@ -53,6 +54,8 @@ struct _MMSimpleConnectPropertiesPrivate {
* @pin: PIN code.
*
* Sets the PIN code to use when unlocking the modem.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_pin (MMSimpleConnectProperties *self,
@@ -70,7 +73,10 @@ mm_simple_connect_properties_set_pin (MMSimpleConnectProperties *self,
*
* Gets the PIN code to use when unlocking the modem.
*
- * Returns: (transfer none): the PIN, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the PIN, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_simple_connect_properties_get_pin (MMSimpleConnectProperties *self)
@@ -88,6 +94,8 @@ mm_simple_connect_properties_get_pin (MMSimpleConnectProperties *self)
* @operator_id: operator ID, given as MCC/MNC.
*
* Sets the ID of the network to which register before connecting.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_operator_id (MMSimpleConnectProperties *self,
@@ -105,7 +113,10 @@ mm_simple_connect_properties_set_operator_id (MMSimpleConnectProperties *self,
*
* Gets the ID of the network to which register before connecting.
*
- * Returns: (transfer none): the operator ID, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the operator ID, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_simple_connect_properties_get_operator_id (MMSimpleConnectProperties *self)
@@ -123,6 +134,8 @@ mm_simple_connect_properties_get_operator_id (MMSimpleConnectProperties *self)
* @apn: Name of the access point.
*
* Sets the name of the access point to use when connecting.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_apn (MMSimpleConnectProperties *self,
@@ -140,7 +153,10 @@ mm_simple_connect_properties_set_apn (MMSimpleConnectProperties *self,
*
* Gets the name of the access point to use when connecting.
*
- * Returns: (transfer none): the access point, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the access point, or #NULL if not set. Do not free
+ * the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_simple_connect_properties_get_apn (MMSimpleConnectProperties *self)
@@ -155,9 +171,12 @@ mm_simple_connect_properties_get_apn (MMSimpleConnectProperties *self)
/**
* mm_simple_connect_properties_set_allowed_auth:
* @self: a #MMSimpleConnectProperties.
- * @allowed_auth: a bitmask of #MMBearerAllowedAuth values. %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default method.
+ * @allowed_auth: a bitmask of #MMBearerAllowedAuth values.
+ * %MM_BEARER_ALLOWED_AUTH_UNKNOWN may be given to request the modem-default method.
*
* Sets the authentication method to use.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_allowed_auth (MMSimpleConnectProperties *self,
@@ -174,7 +193,10 @@ mm_simple_connect_properties_set_allowed_auth (MMSimpleConnectProperties *self,
*
* Gets the authentication methods allowed in the connection.
*
- * Returns: a bitmask of #MMBearerAllowedAuth values, or %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method.
+ * Returns: a bitmask of #MMBearerAllowedAuth values, or
+ * %MM_BEARER_ALLOWED_AUTH_UNKNOWN to request the modem-default method.
+ *
+ * Since: 1.0
*/
MMBearerAllowedAuth
mm_simple_connect_properties_get_allowed_auth (MMSimpleConnectProperties *self)
@@ -192,6 +214,8 @@ mm_simple_connect_properties_get_allowed_auth (MMSimpleConnectProperties *self)
* @user: the username
*
* Sets the username used to authenticate with the access point.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_user (MMSimpleConnectProperties *self,
@@ -209,7 +233,10 @@ mm_simple_connect_properties_set_user (MMSimpleConnectProperties *self,
*
* Gets the username used to authenticate with the access point.
*
- * Returns: (transfer none): the username, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the username, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_simple_connect_properties_get_user (MMSimpleConnectProperties *self)
@@ -227,6 +254,8 @@ mm_simple_connect_properties_get_user (MMSimpleConnectProperties *self)
* @password: the password
*
* Sets the password used to authenticate with the access point.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_password (MMSimpleConnectProperties *self,
@@ -244,7 +273,10 @@ mm_simple_connect_properties_set_password (MMSimpleConnectProperties *self,
*
* Gets the password used to authenticate with the access point.
*
- * Returns: (transfer none): the password, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): the password, or #NULL if not set. Do not free the
+ * returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_simple_connect_properties_get_password (MMSimpleConnectProperties *self)
@@ -262,6 +294,8 @@ mm_simple_connect_properties_get_password (MMSimpleConnectProperties *self)
* @ip_type: a #MMBearerIpFamily.
*
* Sets the IP type to use.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_ip_type (MMSimpleConnectProperties *self,
@@ -280,6 +314,8 @@ mm_simple_connect_properties_set_ip_type (MMSimpleConnectProperties *self,
* Sets the IP type to use.
*
* Returns: a #MMBearerIpFamily.
+ *
+ * Since: 1.0
*/
MMBearerIpFamily
mm_simple_connect_properties_get_ip_type (MMSimpleConnectProperties *self)
@@ -292,12 +328,90 @@ mm_simple_connect_properties_get_ip_type (MMSimpleConnectProperties *self)
/*****************************************************************************/
/**
+ * mm_simple_connect_properties_set_apn_type:
+ * @self: a #MMSimpleConnectProperties.
+ * @apn_type: a mask of #MMBearerApnType values.
+ *
+ * Sets the APN types to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_simple_connect_properties_set_apn_type (MMSimpleConnectProperties *self,
+ MMBearerApnType apn_type)
+{
+ g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self));
+
+ mm_bearer_properties_set_apn_type (self->priv->bearer_properties, apn_type);
+}
+
+/**
+ * mm_simple_connect_properties_get_apn_type:
+ * @self: a #MMSimpleConnectProperties.
+ *
+ * Gets the APN types to use.
+ *
+ * Returns: a mask of #MMBearerApnType values.
+ *
+ * Since: 1.18
+ */
+MMBearerApnType
+mm_simple_connect_properties_get_apn_type (MMSimpleConnectProperties *self)
+{
+ g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_BEARER_APN_TYPE_NONE);
+
+ return mm_bearer_properties_get_apn_type (self->priv->bearer_properties);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_simple_connect_properties_set_profile_id:
+ * @self: a #MMSimpleConnectProperties.
+ * @profile_id: a profile id.
+ *
+ * Sets the profile ID to use.
+ *
+ * Since: 1.18
+ */
+void
+mm_simple_connect_properties_set_profile_id (MMSimpleConnectProperties *self,
+ gint profile_id)
+{
+ g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self));
+
+ mm_bearer_properties_set_profile_id (self->priv->bearer_properties, profile_id);
+}
+
+/**
+ * mm_simple_connect_properties_get_profile_id:
+ * @self: a #MMSimpleConnectProperties.
+ *
+ * Gets the profile ID to use.
+ *
+ * Returns: the profile id.
+ *
+ * Since: 1.18
+ */
+gint
+mm_simple_connect_properties_get_profile_id (MMSimpleConnectProperties *self)
+{
+ g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ return mm_bearer_properties_get_profile_id (self->priv->bearer_properties);
+}
+
+/*****************************************************************************/
+
+/**
* mm_simple_connect_properties_set_allow_roaming:
* @self: a #MMSimpleConnectProperties.
* @allow_roaming: boolean value.
*
* Sets the flag to indicate whether roaming is allowed or not in the
* connection.
+ *
+ * Since: 1.0
*/
void
mm_simple_connect_properties_set_allow_roaming (MMSimpleConnectProperties *self,
@@ -315,7 +429,9 @@ mm_simple_connect_properties_set_allow_roaming (MMSimpleConnectProperties *self,
*
* Checks whether roaming is allowed in the connection.
*
- * Returns: %TRUE if roaming is allowed, %FALSE otherwise..
+ * Returns: %TRUE if roaming is allowed, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_simple_connect_properties_get_allow_roaming (MMSimpleConnectProperties *self)
@@ -325,51 +441,86 @@ mm_simple_connect_properties_get_allow_roaming (MMSimpleConnectProperties *self)
return mm_bearer_properties_get_allow_roaming (self->priv->bearer_properties);
}
-
/*****************************************************************************/
/**
- * mm_simple_connect_properties_set_number:
+ * mm_simple_connect_properties_set_rm_protocol:
* @self: a #MMSimpleConnectProperties.
- * @number: the number.
+ * @protocol: a #MMModemCdmaRmProtocol.
*
- * Sets the number to use when performing the connection.
+ * Sets the RM protocol requested by the user.
+ *
+ * Since: 1.16
*/
void
-mm_simple_connect_properties_set_number (MMSimpleConnectProperties *self,
- const gchar *number)
+mm_simple_connect_properties_set_rm_protocol (MMSimpleConnectProperties *self,
+ MMModemCdmaRmProtocol protocol)
{
g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self));
- mm_bearer_properties_set_number (self->priv->bearer_properties,
- number);
+ mm_bearer_properties_set_rm_protocol (self->priv->bearer_properties, protocol);
}
/**
- * mm_simple_connect_properties_get_number:
+ * mm_simple_connect_properties_get_rm_protocol:
* @self: a #MMSimpleConnectProperties.
*
- * Gets the number to use when performing the connection.
+ * Get the RM protocol requested by the user.
+ *
+ * Returns: a #MMModemCdmaRmProtocol.
*
- * Returns: (transfer none): the number, or #NULL if not set. Do not free the returned value, it is owned by @self.
+ * Since: 1.16
*/
-const gchar *
-mm_simple_connect_properties_get_number (MMSimpleConnectProperties *self)
+MMModemCdmaRmProtocol
+mm_simple_connect_properties_get_rm_protocol (MMSimpleConnectProperties *self)
{
- g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), NULL);
+ g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN);
- return mm_bearer_properties_get_number (self->priv->bearer_properties);
+ return mm_bearer_properties_get_rm_protocol (self->priv->bearer_properties);
}
/*****************************************************************************/
/**
- * mm_simple_connect_properties_get_bearer_properties:
- * @self: a #MMSimpleConnectProperties:
+ * mm_simple_connect_properties_set_multiplex:
+ * @self: a #MMSimpleConnectProperties.
+ * @multiplex: a #MMBearerMultiplexSupport.
+ *
+ * Sets the multiplex support requested by the user.
+ *
+ * Since: 1.18
+ */
+void
+mm_simple_connect_properties_set_multiplex (MMSimpleConnectProperties *self,
+ MMBearerMultiplexSupport multiplex)
+{
+ g_return_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self));
+
+ mm_bearer_properties_set_multiplex (self->priv->bearer_properties, multiplex);
+}
+
+/**
+ * mm_simple_connect_properties_get_multiplex:
+ * @self: a #MMSimpleConnectProperties.
*
- * Returns the bearer properties of @self.
+ * Get the multiplex support requested by the user.
*
- * Returns: (transfer full): a #MMBearerProperties
+ * Returns: a #MMBearerMultiplexSupport.
+ *
+ * Since: 1.18
+ */
+MMBearerMultiplexSupport
+mm_simple_connect_properties_get_multiplex (MMSimpleConnectProperties *self)
+{
+ g_return_val_if_fail (MM_IS_SIMPLE_CONNECT_PROPERTIES (self), MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN);
+
+ return mm_bearer_properties_get_multiplex (self->priv->bearer_properties);
+}
+
+/*****************************************************************************/
+
+/**
+ * mm_simple_connect_properties_get_bearer_properties: (skip)
*/
MMBearerProperties *
mm_simple_connect_properties_get_bearer_properties (MMSimpleConnectProperties *self)
@@ -381,6 +532,9 @@ mm_simple_connect_properties_get_bearer_properties (MMSimpleConnectProperties *s
/*****************************************************************************/
+/**
+ * mm_simple_connect_properties_get_dictionary: (skip)
+ */
GVariant *
mm_simple_connect_properties_get_dictionary (MMSimpleConnectProperties *self)
{
@@ -440,26 +594,41 @@ key_value_foreach (const gchar *key,
const gchar *value,
ParseKeyValueContext *ctx)
{
+ GError *inner_error = NULL;
+
/* First, check if we can consume this as bearer properties */
if (mm_bearer_properties_consume_string (ctx->self->priv->bearer_properties,
key, value,
- NULL))
+ &inner_error))
return TRUE;
+ /* Unknown keys are reported as unsupported. Any other error is right away
+ * fatal (e.g. an invalid value given to a known bearer property) */
+ if (!g_error_matches (inner_error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ ctx->error = inner_error;
+ return FALSE;
+ }
+
+ /* On unsupported errors, try with the Simple.Connect specific properties */
+ g_clear_error (&inner_error);
+
if (g_str_equal (key, PROPERTY_PIN))
mm_simple_connect_properties_set_pin (ctx->self, value);
else if (g_str_equal (key, PROPERTY_OPERATOR_ID))
mm_simple_connect_properties_set_operator_id (ctx->self, value);
else {
ctx->error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Invalid properties string, unexpected key '%s'",
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Invalid properties string, unsupported key '%s'",
key);
}
return !ctx->error;
}
+/**
+ * mm_simple_connect_properties_new_from_string: (skip)
+ */
MMSimpleConnectProperties *
mm_simple_connect_properties_new_from_string (const gchar *str,
GError **error)
@@ -486,6 +655,9 @@ mm_simple_connect_properties_new_from_string (const gchar *str,
/*****************************************************************************/
+/**
+ * mm_simple_connect_properties_new_from_dictionary: (skip)
+ */
MMSimpleConnectProperties *
mm_simple_connect_properties_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -557,6 +729,8 @@ mm_simple_connect_properties_new_from_dictionary (GVariant *dictionary,
* Creates a new empty #MMSimpleConnectProperties.
*
* Returns: (transfer full): a #MMSimpleConnectProperties. The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMSimpleConnectProperties *
mm_simple_connect_properties_new (void)
diff --git a/libmm-glib/mm-simple-connect-properties.h b/libmm-glib/mm-simple-connect-properties.h
index 3167db0a..353d3638 100644
--- a/libmm-glib/mm-simple-connect-properties.h
+++ b/libmm-glib/mm-simple-connect-properties.h
@@ -56,6 +56,7 @@ struct _MMSimpleConnectPropertiesClass {
};
GType mm_simple_connect_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimpleConnectProperties, g_object_unref)
MMSimpleConnectProperties *mm_simple_connect_properties_new (void);
@@ -73,20 +74,29 @@ void mm_simple_connect_properties_set_password (MMSimpleConnectProperties *
const gchar *password);
void mm_simple_connect_properties_set_ip_type (MMSimpleConnectProperties *self,
MMBearerIpFamily ip_type);
+void mm_simple_connect_properties_set_apn_type (MMSimpleConnectProperties *self,
+ MMBearerApnType apn_type);
+void mm_simple_connect_properties_set_profile_id (MMSimpleConnectProperties *self,
+ gint profile_id);
void mm_simple_connect_properties_set_allow_roaming (MMSimpleConnectProperties *self,
gboolean allow_roaming);
-void mm_simple_connect_properties_set_number (MMSimpleConnectProperties *self,
- const gchar *number);
-
-const gchar *mm_simple_connect_properties_get_pin (MMSimpleConnectProperties *self);
-const gchar *mm_simple_connect_properties_get_operator_id (MMSimpleConnectProperties *self);
-const gchar *mm_simple_connect_properties_get_apn (MMSimpleConnectProperties *self);
-MMBearerAllowedAuth mm_simple_connect_properties_get_allowed_auth (MMSimpleConnectProperties *self);
-const gchar *mm_simple_connect_properties_get_user (MMSimpleConnectProperties *self);
-const gchar *mm_simple_connect_properties_get_password (MMSimpleConnectProperties *self);
-MMBearerIpFamily mm_simple_connect_properties_get_ip_type (MMSimpleConnectProperties *self);
-gboolean mm_simple_connect_properties_get_allow_roaming (MMSimpleConnectProperties *self);
-const gchar *mm_simple_connect_properties_get_number (MMSimpleConnectProperties *self);
+void mm_simple_connect_properties_set_rm_protocol (MMSimpleConnectProperties *self,
+ MMModemCdmaRmProtocol protocol);
+void mm_simple_connect_properties_set_multiplex (MMSimpleConnectProperties *self,
+ MMBearerMultiplexSupport multiplex);
+
+const gchar *mm_simple_connect_properties_get_pin (MMSimpleConnectProperties *self);
+const gchar *mm_simple_connect_properties_get_operator_id (MMSimpleConnectProperties *self);
+const gchar *mm_simple_connect_properties_get_apn (MMSimpleConnectProperties *self);
+MMBearerAllowedAuth mm_simple_connect_properties_get_allowed_auth (MMSimpleConnectProperties *self);
+const gchar *mm_simple_connect_properties_get_user (MMSimpleConnectProperties *self);
+const gchar *mm_simple_connect_properties_get_password (MMSimpleConnectProperties *self);
+MMBearerIpFamily mm_simple_connect_properties_get_ip_type (MMSimpleConnectProperties *self);
+MMBearerApnType mm_simple_connect_properties_get_apn_type (MMSimpleConnectProperties *self);
+gint mm_simple_connect_properties_get_profile_id (MMSimpleConnectProperties *self);
+gboolean mm_simple_connect_properties_get_allow_roaming (MMSimpleConnectProperties *self);
+MMModemCdmaRmProtocol mm_simple_connect_properties_get_rm_protocol (MMSimpleConnectProperties *self);
+MMBearerMultiplexSupport mm_simple_connect_properties_get_multiplex (MMSimpleConnectProperties *self);
/*****************************************************************************/
/* ModemManager/libmm-glib/mmcli specific methods */
diff --git a/libmm-glib/mm-simple-status.c b/libmm-glib/mm-simple-status.c
index 9be9573b..cc805a9e 100644
--- a/libmm-glib/mm-simple-status.c
+++ b/libmm-glib/mm-simple-status.c
@@ -33,7 +33,7 @@
* mm_modem_simple_get_status_sync().
*/
-G_DEFINE_TYPE (MMSimpleStatus, mm_simple_status, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMSimpleStatus, mm_simple_status, G_TYPE_OBJECT)
enum {
PROP_0,
@@ -73,8 +73,6 @@ struct _MMSimpleStatusPrivate {
gchar *modem_3gpp_operator_code;
/* 3GPP operator name, given only when registered, signature 's' */
gchar *modem_3gpp_operator_name;
- /* 3GPP subsctiption state, signature 'u' */
- MMModem3gppSubscriptionState modem_3gpp_subscription_state;
/* <--- From the Modem CDMA interface ---> */
/* CDMA/CDMA1x registration state, signature 'u' */
@@ -96,6 +94,8 @@ struct _MMSimpleStatusPrivate {
* Gets the state of the modem.
*
* Returns: a #MMModemState.
+ *
+ * Since: 1.0
*/
MMModemState
mm_simple_status_get_state (MMSimpleStatus *self)
@@ -110,11 +110,14 @@ mm_simple_status_get_state (MMSimpleStatus *self)
/**
* mm_simple_status_get_signal_quality:
* @self: a #MMSimpleStatus.
- * @recent: (out) (allow-none): indication of whether the given signal quality is considered recent.
+ * @recent: (out) (allow-none): indication of whether the given signal quality
+ * is considered recent.
*
* Gets the signal quality.
*
* Returns: the signal quality.
+ *
+ * Since: 1.0
*/
guint32
mm_simple_status_get_signal_quality (MMSimpleStatus *self,
@@ -142,10 +145,13 @@ mm_simple_status_get_signal_quality (MMSimpleStatus *self,
/**
* mm_simple_status_get_current_bands:
* @self: a #MMSimpleStatus.
- * @bands: (out): location for an array of #MMModemBand values. Do not free the returned value, it is owned by @self.
+ * @bands: (out): location for an array of #MMModemBand values. Do not free the
+ * returned value, it is owned by @self.
* @n_bands: (out): number of elements in @bands.
*
* Gets the currently used frequency bands.
+ *
+ * Since: 1.0
*/
void
mm_simple_status_get_current_bands (MMSimpleStatus *self,
@@ -170,6 +176,8 @@ mm_simple_status_get_current_bands (MMSimpleStatus *self,
* Gets the currently used access technologies.
*
* Returns: a bitmask of #MMModemAccessTechnology values.
+ *
+ * Since: 1.0
*/
MMModemAccessTechnology
mm_simple_status_get_access_technologies (MMSimpleStatus *self)
@@ -188,6 +196,8 @@ mm_simple_status_get_access_technologies (MMSimpleStatus *self)
* Gets the current state of the registration in the 3GPP network.
*
* Returns: a #MMModem3gppRegistrationState.
+ *
+ * Since: 1.0
*/
MMModem3gppRegistrationState
mm_simple_status_get_3gpp_registration_state (MMSimpleStatus *self)
@@ -203,9 +213,13 @@ mm_simple_status_get_3gpp_registration_state (MMSimpleStatus *self)
* mm_simple_status_get_3gpp_operator_code:
* @self: a #MMSimpleStatus.
*
- * Gets the MCC/MNC of the operator of the 3GPP network where the modem is registered.
+ * Gets the MCC/MNC of the operator of the 3GPP network where the modem is
+ * registered.
*
- * Returns: the operator code, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Returns: the operator code, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_simple_status_get_3gpp_operator_code (MMSimpleStatus *self)
@@ -221,9 +235,13 @@ mm_simple_status_get_3gpp_operator_code (MMSimpleStatus *self)
* mm_simple_status_get_3gpp_operator_name:
* @self: a #MMSimpleStatus.
*
- * Gets the name of the operator of the 3GPP network where the modem is registered.
+ * Gets the name of the operator of the 3GPP network where the modem is
+ * registered.
+ *
+ * Returns: the operator name, or %NULL if unknown. Do not free the returned
+ * value, it is owned by @self.
*
- * Returns: the operator name, or %NULL if unknown. Do not free the returned value, it is owned by @self.
+ * Since: 1.0
*/
const gchar *
mm_simple_status_get_3gpp_operator_name (MMSimpleStatus *self)
@@ -236,31 +254,14 @@ mm_simple_status_get_3gpp_operator_name (MMSimpleStatus *self)
/*****************************************************************************/
/**
- * mm_simple_status_get_3gpp_subscription_state:
- * @self: a #MMSimpleStatus.
- *
- * Gets the current subscription status of the account.
- *
- * Returns: a #MMModem3gppSubscriptionState.
- */
-MMModem3gppSubscriptionState
-mm_simple_status_get_3gpp_subscription_state (MMSimpleStatus *self)
-{
- g_return_val_if_fail (MM_IS_SIMPLE_STATUS (self), MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
-
- return self->priv->modem_3gpp_subscription_state;
-}
-
-
-/*****************************************************************************/
-
-/**
* mm_simple_status_get_cdma_cdma1x_registration_state:
* @self: a #MMSimpleStatus.
*
* Gets the current state of the registration in the CDMA-1x network.
*
* Returns: a #MMModemCdmaRegistrationState.
+ *
+ * Since: 1.0
*/
MMModemCdmaRegistrationState
mm_simple_status_get_cdma_cdma1x_registration_state (MMSimpleStatus *self)
@@ -279,6 +280,8 @@ mm_simple_status_get_cdma_cdma1x_registration_state (MMSimpleStatus *self)
* Gets the current state of the registration in the EV-DO network.
*
* Returns: a #MMModemCdmaRegistrationState.
+ *
+ * Since: 1.0
*/
MMModemCdmaRegistrationState
mm_simple_status_get_cdma_evdo_registration_state (MMSimpleStatus *self)
@@ -297,6 +300,8 @@ mm_simple_status_get_cdma_evdo_registration_state (MMSimpleStatus *self)
* Gets the System Identification number of the CDMA network.
*
* Returns: the SID, or %MM_MODEM_CDMA_SID_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
guint
mm_simple_status_get_cdma_sid (MMSimpleStatus *self)
@@ -315,6 +320,8 @@ mm_simple_status_get_cdma_sid (MMSimpleStatus *self)
* Gets the Network Identification number of the CDMA network.
*
* Returns: the NID, or %MM_MODEM_CDMA_NID_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
guint
mm_simple_status_get_cdma_nid (MMSimpleStatus *self)
@@ -326,6 +333,9 @@ mm_simple_status_get_cdma_nid (MMSimpleStatus *self)
/*****************************************************************************/
+/**
+ * mm_simple_status_get_dictionary: (skip)
+ */
GVariant *
mm_simple_status_get_dictionary (MMSimpleStatus *self)
{
@@ -397,30 +407,28 @@ mm_simple_status_get_dictionary (MMSimpleStatus *self)
}
- if (self->priv->modem_3gpp_subscription_state)
- g_variant_builder_add (&builder,
- "{sv}",
- MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE,
- g_variant_new_uint32 (self->priv->modem_3gpp_subscription_state));
-
return g_variant_ref_sink (g_variant_builder_end (&builder));
}
/*****************************************************************************/
+/**
+ * mm_simple_status_new_from_dictionary: (skip)
+ */
MMSimpleStatus *
mm_simple_status_new_from_dictionary (GVariant *dictionary,
GError **error)
{
- GError *inner_error = NULL;
- GVariantIter iter;
- gchar *key;
- GVariant *value;
- MMSimpleStatus *properties;
+ GError *inner_error = NULL;
+ GVariantIter iter;
+ gchar *key;
+ GVariant *value;
+ g_autoptr(MMSimpleStatus) props = NULL;
+
+ props = mm_simple_status_new ();
- properties = mm_simple_status_new ();
if (!dictionary)
- return properties;
+ return g_steal_pointer (&props);
if (!g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{sv}"))) {
g_set_error (error,
@@ -428,13 +436,11 @@ mm_simple_status_new_from_dictionary (GVariant *dictionary,
MM_CORE_ERROR_INVALID_ARGS,
"Cannot create Simple status from dictionary: "
"invalid variant type received");
- g_object_unref (properties);
return NULL;
}
g_variant_iter_init (&iter, dictionary);
- while (!inner_error &&
- g_variant_iter_next (&iter, "{sv}", &key, &value)) {
+ while (!inner_error && g_variant_iter_next (&iter, "{sv}", &key, &value)) {
/* Note: we could do a more efficient matching by checking the variant type
* and just g_object_set()-ing they specific 'key' and value, but we do want
* to check which input keys we receive, in order to propagate the error.
@@ -442,25 +448,24 @@ mm_simple_status_new_from_dictionary (GVariant *dictionary,
if (g_str_equal (key, MM_SIMPLE_PROPERTY_STATE) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_ACCESS_TECHNOLOGIES) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE) ||
- g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_CDMA1X_REGISTRATION_STATE) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_EVDO_REGISTRATION_STATE) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_SID) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_CDMA_NID)) {
/* uint properties */
- g_object_set (properties,
+ g_object_set (props,
key, g_variant_get_uint32 (value),
NULL);
} else if (g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME)) {
/* string properties */
- g_object_set (properties,
+ g_object_set (props,
key, g_variant_get_string (value, NULL),
NULL);
} else if (g_str_equal (key, MM_SIMPLE_PROPERTY_CURRENT_BANDS) ||
g_str_equal (key, MM_SIMPLE_PROPERTY_SIGNAL_QUALITY)) {
/* remaining complex types, as variant */
- g_object_set (properties,
+ g_object_set (props,
key, value,
NULL);
} else {
@@ -478,15 +483,17 @@ mm_simple_status_new_from_dictionary (GVariant *dictionary,
/* If error, destroy the object */
if (inner_error) {
g_propagate_error (error, inner_error);
- g_object_unref (properties);
- properties = NULL;
+ return NULL;
}
- return properties;
+ return g_steal_pointer (&props);
}
/*****************************************************************************/
+/**
+ * mm_simple_status_new: (skip)
+ */
MMSimpleStatus *
mm_simple_status_new (void)
{
@@ -535,7 +542,7 @@ set_property (GObject *object,
self->priv->modem_3gpp_operator_name = g_value_dup_string (value);
break;
case PROP_3GPP_SUBSCRIPTION_STATE:
- self->priv->modem_3gpp_subscription_state = g_value_get_enum (value);
+ /* no-op */
break;
case PROP_CDMA_CDMA1X_REGISTRATION_STATE:
self->priv->modem_cdma_cdma1x_registration_state = g_value_get_enum (value);
@@ -586,7 +593,7 @@ get_property (GObject *object,
g_value_set_string (value, self->priv->modem_3gpp_operator_name);
break;
case PROP_3GPP_SUBSCRIPTION_STATE:
- g_value_set_enum (value, self->priv->modem_3gpp_subscription_state);
+ g_value_set_enum (value, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN);
break;
case PROP_CDMA_CDMA1X_REGISTRATION_STATE:
g_value_set_enum (value, self->priv->modem_cdma_cdma1x_registration_state);
@@ -617,7 +624,6 @@ mm_simple_status_init (MMSimpleStatus *self)
self->priv->state = MM_MODEM_STATE_UNKNOWN;
self->priv->access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
self->priv->modem_3gpp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- self->priv->modem_3gpp_subscription_state = MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN;
self->priv->current_bands = g_variant_ref_sink (mm_common_build_bands_unknown ());
self->priv->signal_quality = g_variant_ref_sink (g_variant_new ("(ub)", 0, 0));
self->priv->modem_cdma_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
@@ -717,7 +723,7 @@ mm_simple_status_class_init (MMSimpleStatusClass *klass)
properties[PROP_3GPP_SUBSCRIPTION_STATE] =
g_param_spec_enum (MM_SIMPLE_PROPERTY_3GPP_SUBSCRIPTION_STATE,
"3GPP subscription state",
- "Subscription state of the account",
+ "Subscription state of the account (deprecated)",
MM_TYPE_MODEM_3GPP_SUBSCRIPTION_STATE,
MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN,
G_PARAM_READWRITE);
diff --git a/libmm-glib/mm-simple-status.h b/libmm-glib/mm-simple-status.h
index cbf93bcf..86c7ce4f 100644
--- a/libmm-glib/mm-simple-status.h
+++ b/libmm-glib/mm-simple-status.h
@@ -54,7 +54,7 @@ struct _MMSimpleStatusClass {
};
GType mm_simple_status_get_type (void);
-
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimpleStatus, g_object_unref)
MMModemState mm_simple_status_get_state (MMSimpleStatus *self);
guint32 mm_simple_status_get_signal_quality (MMSimpleStatus *self,
@@ -67,7 +67,6 @@ MMModemAccessTechnology mm_simple_status_get_access_technologies (MMSimple
MMModem3gppRegistrationState mm_simple_status_get_3gpp_registration_state (MMSimpleStatus *self);
const gchar *mm_simple_status_get_3gpp_operator_code (MMSimpleStatus *self);
const gchar *mm_simple_status_get_3gpp_operator_name (MMSimpleStatus *self);
-MMModem3gppSubscriptionState mm_simple_status_get_3gpp_subscription_state (MMSimpleStatus *self);
MMModemCdmaRegistrationState mm_simple_status_get_cdma_cdma1x_registration_state (MMSimpleStatus *self);
MMModemCdmaRegistrationState mm_simple_status_get_cdma_evdo_registration_state (MMSimpleStatus *self);
diff --git a/libmm-glib/mm-sms-properties.c b/libmm-glib/mm-sms-properties.c
index 93e8b9b5..30d5b719 100644
--- a/libmm-glib/mm-sms-properties.c
+++ b/libmm-glib/mm-sms-properties.c
@@ -68,6 +68,8 @@ struct _MMSmsPropertiesPrivate {
* @text: The text to set, in UTF-8.
*
* Sets the message text.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_text (MMSmsProperties *self,
@@ -85,7 +87,11 @@ mm_sms_properties_set_text (MMSmsProperties *self,
*
* Gets the message text, in UTF-8.
*
- * Returns: (transfer none): The message text, or %NULL if it doesn't contain any (e.g. contains data instead). Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The message text, or %NULL if it doesn't contain
+ * any (e.g. contains data instead). Do not free the returned value, it is
+ * owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sms_properties_get_text (MMSmsProperties *self)
@@ -104,6 +110,8 @@ mm_sms_properties_get_text (MMSmsProperties *self)
* @data_length: Length of @data.
*
* Sets the message data.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_data (MMSmsProperties *self,
@@ -126,9 +134,12 @@ mm_sms_properties_set_data (MMSmsProperties *self,
/**
* mm_sms_properties_set_data_bytearray:
* @self: A #MMSmsProperties.
- * @data: A #GByteArray with the data to set. This method takes a new reference of @data.
+ * @data: A #GByteArray with the data to set. This method takes a new reference
+ * of @data.
*
* Sets the message data.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_data_bytearray (MMSmsProperties *self,
@@ -149,7 +160,10 @@ mm_sms_properties_set_data_bytearray (MMSmsProperties *self,
*
* Gets the message data.
*
- * Returns: (transfer none): The message data, or %NULL if it doesn't contain any (e.g. contains text instead).
+ * Returns: (transfer none): The message data, or %NULL if it doesn't contain
+ * any (e.g. contains text instead).
+ *
+ * Since: 1.0
*/
const guint8 *
mm_sms_properties_get_data (MMSmsProperties *self,
@@ -157,10 +171,10 @@ mm_sms_properties_get_data (MMSmsProperties *self,
{
g_return_val_if_fail (MM_IS_SMS_PROPERTIES (self), NULL);
- if (self->priv->data && data_len)
- *data_len = self->priv->data->len;
+ if (data_len)
+ *data_len = (self->priv->data ? self->priv->data->len : 0);
- return self->priv->data->data;
+ return (self->priv->data ? self->priv->data->data : NULL);
}
/**
@@ -169,7 +183,11 @@ mm_sms_properties_get_data (MMSmsProperties *self,
*
* Gets the message data.
*
- * Returns: (transfer none): A #GByteArray with the message data, or %NULL if it doesn't contain any (e.g. contains text instead). Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): A #GByteArray with the message data, or %NULL if it
+ * doesn't contain any (e.g. contains text instead). Do not free the returned
+ * value, it is owned by @self.
+ *
+ * Since: 1.0
*/
GByteArray *
mm_sms_properties_peek_data_bytearray (MMSmsProperties *self)
@@ -185,7 +203,11 @@ mm_sms_properties_peek_data_bytearray (MMSmsProperties *self)
*
* Gets the message data.
*
- * Returns: (transfer full): A #GByteArray with the message data, or %NULL if it doesn't contain any (e.g. contains text instead). The returned value should be freed with g_byte_array_unref().
+ * Returns: (transfer full): A #GByteArray with the message data, or %NULL if it
+ * doesn't contain any (e.g. contains text instead). The returned value should
+ * be freed with g_byte_array_unref().
+ *
+ * Since: 1.0
*/
GByteArray *
mm_sms_properties_get_data_bytearray (MMSmsProperties *self)
@@ -203,6 +225,8 @@ mm_sms_properties_get_data_bytearray (MMSmsProperties *self)
* @number: The number.
*
* Sets the number to which the message is addressed.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_number (MMSmsProperties *self,
@@ -220,7 +244,10 @@ mm_sms_properties_set_number (MMSmsProperties *self,
*
* Gets the number to which the message is addressed.
*
- * Returns: (transfer none): The number, or %NULL if it couldn't be retrieved. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The number, or %NULL if it couldn't be retrieved.
+ * Do not free the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sms_properties_get_number (MMSmsProperties *self)
@@ -238,6 +265,8 @@ mm_sms_properties_get_number (MMSmsProperties *self)
* @smsc: The SMSC number.
*
* Sets the SMS service center number.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_smsc (MMSmsProperties *self,
@@ -255,7 +284,10 @@ mm_sms_properties_set_smsc (MMSmsProperties *self,
*
* Gets the SMS service center number.
*
- * Returns: (transfer none): The number of the SMSC, or %NULL if it couldn't be retrieved. Do not free the returned value, it is owned by @self.
+ * Returns: (transfer none): The number of the SMSC, or %NULL if it couldn't be
+ * retrieved. Do not free the returned value, it is owned by @self.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sms_properties_get_smsc (MMSmsProperties *self)
@@ -273,6 +305,8 @@ mm_sms_properties_get_smsc (MMSmsProperties *self)
* @validity: The validity of %MM_SMS_VALIDITY_TYPE_RELATIVE type.
*
* Sets the relative validity time of the SMS.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_validity_relative (MMSmsProperties *self,
@@ -291,6 +325,8 @@ mm_sms_properties_set_validity_relative (MMSmsProperties *self,
* Gets the relative validity type the SMS.
*
* Returns: a #MMSmsValidityType.
+ *
+ * Since: 1.0
*/
MMSmsValidityType
mm_sms_properties_get_validity_type (MMSmsProperties *self)
@@ -307,6 +343,8 @@ mm_sms_properties_get_validity_type (MMSmsProperties *self)
* Gets the relative validity time of the SMS.
*
* Returns: the validity time or 0 if unknown.
+ *
+ * Since: 1.0
*/
guint
mm_sms_properties_get_validity_relative (MMSmsProperties *self)
@@ -322,18 +360,20 @@ mm_sms_properties_get_validity_relative (MMSmsProperties *self)
/**
* mm_sms_properties_set_class:
* @self: A #MMSmsProperties.
- * @class: The message class (0..3), or -1 for invalid/unset class.
+ * @message_class: The message class (0..3), or -1 for invalid/unset class.
*
* Sets the 3GPP message class of the SMS.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_class (MMSmsProperties *self,
- gint class)
+ gint message_class)
{
g_return_if_fail (MM_IS_SMS_PROPERTIES (self));
- g_return_if_fail (class >= -1 && class <= 3);
+ g_return_if_fail (message_class >= -1 && message_class <= 3);
- self->priv->class = class;
+ self->priv->class = message_class;
}
/**
@@ -343,6 +383,8 @@ mm_sms_properties_set_class (MMSmsProperties *self,
* Gets the 3GPP message class of the SMS.
*
* Returns: the message class, or -1 for invalid/unset class.
+ *
+ * Since: 1.0
*/
gint
mm_sms_properties_get_class (MMSmsProperties *self)
@@ -360,6 +402,8 @@ mm_sms_properties_get_class (MMSmsProperties *self)
* @request: %TRUE if delivery report is requested, %FALSE otherwise.
*
* Sets whether delivery report is requested for the SMS.
+ *
+ * Since: 1.0
*/
void
mm_sms_properties_set_delivery_report_request (MMSmsProperties *self,
@@ -378,6 +422,8 @@ mm_sms_properties_set_delivery_report_request (MMSmsProperties *self,
* Checks whether delivery report is requested for the SMS.
*
* Returns: %TRUE if delivery report is requested, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_sms_properties_get_delivery_report_request (MMSmsProperties *self)
@@ -395,6 +441,8 @@ mm_sms_properties_get_delivery_report_request (MMSmsProperties *self)
* @teleservice_id: The CDMA teleservice ID.
*
* Sets the CDMA teleservice ID of the SMS.
+ *
+ * Since: 1.2
*/
void
mm_sms_properties_set_teleservice_id (MMSmsProperties *self,
@@ -412,6 +460,8 @@ mm_sms_properties_set_teleservice_id (MMSmsProperties *self,
* Gets the CDMA teleservice ID of the SMS.
*
* Returns: the CDMA teleservice ID.
+ *
+ * Since: 1.2
*/
MMSmsCdmaTeleserviceId
mm_sms_properties_get_teleservice_id (MMSmsProperties *self)
@@ -429,6 +479,8 @@ mm_sms_properties_get_teleservice_id (MMSmsProperties *self)
* @service_category: The CDMA service category.
*
* Sets the CDMA service category of the SMS.
+ *
+ * Since: 1.2
*/
void
mm_sms_properties_set_service_category (MMSmsProperties *self,
@@ -446,6 +498,8 @@ mm_sms_properties_set_service_category (MMSmsProperties *self,
* Gets the CDMA message service category of the SMS.
*
* Returns: the CDMA service category.
+ *
+ * Since: 1.2
*/
MMSmsCdmaServiceCategory
mm_sms_properties_get_service_category (MMSmsProperties *self)
@@ -457,6 +511,9 @@ mm_sms_properties_get_service_category (MMSmsProperties *self)
/*****************************************************************************/
+/**
+ * mm_sms_properties_get_dictionary: (skip)
+ */
GVariant *
mm_sms_properties_get_dictionary (MMSmsProperties *self)
{
@@ -680,6 +737,9 @@ key_value_foreach (const gchar *key,
&ctx->error);
}
+/**
+ * mm_sms_properties_new_from_string: (skip)
+ */
MMSmsProperties *
mm_sms_properties_new_from_string (const gchar *str,
GError **error)
@@ -775,6 +835,9 @@ consume_variant (MMSmsProperties *properties,
return TRUE;
}
+/**
+ * mm_sms_properties_new_from_dictionary: (skip)
+ */
MMSmsProperties *
mm_sms_properties_new_from_dictionary (GVariant *dictionary,
GError **error)
@@ -823,38 +886,13 @@ mm_sms_properties_new_from_dictionary (GVariant *dictionary,
/*****************************************************************************/
/**
- * mm_sms_properties_dup:
- * @orig: a #MMSmsProperties
- *
- * Returns a copy of @orig.
- *
- * Returns: (transfer full): a #MMSmsProperties
- */
-MMSmsProperties *
-mm_sms_properties_dup (MMSmsProperties *orig)
-{
- GVariant *dict;
- MMSmsProperties *copy;
- GError *error = NULL;
-
- g_return_val_if_fail (MM_IS_SMS_PROPERTIES (orig), NULL);
-
- dict = mm_sms_properties_get_dictionary (orig);
- copy = mm_sms_properties_new_from_dictionary (dict, &error);
- g_assert_no_error (error);
- g_variant_unref (dict);
-
- return copy;
-}
-
-/*****************************************************************************/
-
-/**
* mm_sms_properties_new:
*
* Creates a new empty #MMSmsProperties.
*
* Returns: (transfer full): a #MMSmsProperties. The returned value should be freed with g_object_unref().
+ *
+ * Since: 1.0
*/
MMSmsProperties *
mm_sms_properties_new (void)
diff --git a/libmm-glib/mm-sms-properties.h b/libmm-glib/mm-sms-properties.h
index fb9dbf9e..14f4f1bb 100644
--- a/libmm-glib/mm-sms-properties.h
+++ b/libmm-glib/mm-sms-properties.h
@@ -54,6 +54,7 @@ struct _MMSmsPropertiesClass {
};
GType mm_sms_properties_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsProperties, g_object_unref)
MMSmsProperties *mm_sms_properties_new (void);
@@ -71,7 +72,7 @@ void mm_sms_properties_set_smsc (MMSmsProperties *self,
void mm_sms_properties_set_validity_relative (MMSmsProperties *self,
guint validity);
void mm_sms_properties_set_class (MMSmsProperties *self,
- gint class);
+ gint message_class);
void mm_sms_properties_set_delivery_report_request (MMSmsProperties *self,
gboolean request);
void mm_sms_properties_set_teleservice_id (MMSmsProperties *self,
@@ -105,8 +106,6 @@ MMSmsProperties *mm_sms_properties_new_from_string (const gchar *str,
MMSmsProperties *mm_sms_properties_new_from_dictionary (GVariant *dictionary,
GError **error);
-MMSmsProperties *mm_sms_properties_dup (MMSmsProperties *orig);
-
GVariant *mm_sms_properties_get_dictionary (MMSmsProperties *self);
#endif
diff --git a/libmm-glib/mm-sms.c b/libmm-glib/mm-sms.c
index a49deb88..2caf01f5 100644
--- a/libmm-glib/mm-sms.c
+++ b/libmm-glib/mm-sms.c
@@ -49,6 +49,8 @@ G_DEFINE_TYPE (MMSms, mm_sms, MM_GDBUS_TYPE_SMS_PROXY)
* Gets the DBus path of the #MMSms object.
*
* Returns: (transfer none): The DBus path of the #MMSms object.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sms_get_path (MMSms *self)
@@ -65,7 +67,10 @@ mm_sms_get_path (MMSms *self)
*
* Gets a copy of the DBus path of the #MMSms object.
*
- * Returns: (transfer full): The DBus path of the #MMSms object. The returned value should be freed with g_free().
+ * Returns: (transfer full): The DBus path of the #MMSms object. The returned
+ * value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sms_dup_path (MMSms *self)
@@ -89,12 +94,14 @@ mm_sms_dup_path (MMSms *self)
*
* Gets the message text, in UTF-8.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sms_dup_text() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sms_dup_text() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The message text, or %NULL if it doesn't contain
+ * any (e.g. contains data instead).
*
- * Returns: (transfer none): The message text, or %NULL if it doesn't contain any (e.g. contains data instead).
+ * Since: 1.0
*/
const gchar *
mm_sms_get_text (MMSms *self)
@@ -111,7 +118,11 @@ mm_sms_get_text (MMSms *self)
*
* Gets the message text, in UTF-8.
*
- * Returns: (transfer full): The message text, or %NULL if it doesn't contain any (e.g. contains data instead). The returned value should be freed with g_free().
+ * Returns: (transfer full): The message text, or %NULL if it doesn't contain
+ * any (e.g. contains data instead). The returned value should be freed with
+ * g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sms_dup_text (MMSms *self)
@@ -131,7 +142,11 @@ mm_sms_dup_text (MMSms *self)
*
* Gets the message data.
*
- * Returns: (transfer none): The message data, or %NULL if it doesn't contain any (e.g. contains text instead).
+ * Returns: (transfer none) (array length=data_len) (element-type guint8): The
+ * message data, or %NULL if it doesn't contain any (e.g. contains text
+ * instead).
+ *
+ * Since: 1.0
*/
const guint8 *
mm_sms_get_data (MMSms *self,
@@ -158,7 +173,11 @@ mm_sms_get_data (MMSms *self,
*
* Gets the message data.
*
- * Returns: (transfer full): The message data, or %NULL if it doesn't contain any (e.g. contains text instead). The returned value should be freed with g_free().
+ * Returns: (transfer full) (array length=data_len) (element-type guint8): The
+ * message data, or %NULL if it doesn't contain any (e.g. contains text
+ * instead). The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
guint8 *
mm_sms_dup_data (MMSms *self,
@@ -198,12 +217,13 @@ mm_sms_dup_data (MMSms *self,
*
* Gets the number to which the message is addressed.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sms_dup_number() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sms_dup_number() if on another thread.</warning>
*
* Returns: (transfer none): The number, or %NULL if it couldn't be retrieved.
+ *
+ * Since: 1.0
*/
const gchar *
mm_sms_get_number (MMSms *self)
@@ -220,7 +240,10 @@ mm_sms_get_number (MMSms *self)
*
* Gets the number to which the message is addressed.
*
- * Returns: (transfer full): The number, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Returns: (transfer full): The number, or %NULL if it couldn't be retrieved.
+ * The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sms_dup_number (MMSms *self)
@@ -239,12 +262,14 @@ mm_sms_dup_number (MMSms *self)
*
* Gets the SMS service center number.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sms_dup_smsc() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sms_dup_smsc() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The number of the SMSC, or %NULL if it couldn't be
+ * retrieved.
*
- * Returns: (transfer none): The number of the SMSC, or %NULL if it couldn't be retrieved.
+ * Since: 1.0
*/
const gchar *
mm_sms_get_smsc (MMSms *self)
@@ -261,7 +286,10 @@ mm_sms_get_smsc (MMSms *self)
*
* Gets the SMS service center number.
*
- * Returns: (transfer full): The number of the SMSC, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Returns: (transfer full): The number of the SMSC, or %NULL if it couldn't be
+ * retrieved. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sms_dup_smsc (MMSms *self)
@@ -285,12 +313,14 @@ mm_sms_dup_smsc (MMSms *self)
* This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_DELIVER or
* %MM_SMS_PDU_TYPE_STATUS_REPORT.
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sms_dup_timestamp() if on another
- * thread.</warning>
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sms_dup_timestamp() if on another thread.</warning>
+ *
+ * Returns: (transfer none): The timestamp, or %NULL if it couldn't be
+ * retrieved.
*
- * Returns: (transfer none): The timestamp, or %NULL if it couldn't be retrieved.
+ * Since: 1.0
*/
const gchar *
mm_sms_get_timestamp (MMSms *self)
@@ -312,7 +342,10 @@ mm_sms_get_timestamp (MMSms *self)
* This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_DELIVER or
* %MM_SMS_PDU_TYPE_STATUS_REPORT.
*
- * Returns: (transfer full): The timestamp, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Returns: (transfer full): The timestamp, or %NULL if it couldn't be
+ * retrieved. The returned value should be freed with g_free().
+ *
+ * Since: 1.0
*/
gchar *
mm_sms_dup_timestamp (MMSms *self)
@@ -333,14 +366,17 @@ mm_sms_dup_timestamp (MMSms *self)
* <ulink url="http://en.wikipedia.org/wiki/ISO_8601">ISO8601</ulink>
* format.
*
- * This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_STATUS_REPORT.
+ * This field is only applicable if the PDU type is
+ * %MM_SMS_PDU_TYPE_STATUS_REPORT.
+ *
+ * <warning>The returned value is only valid until the property changes so it is
+ * only safe to use this function on the thread where @self was constructed. Use
+ * mm_sms_dup_discharge_timestamp() if on another thread.</warning>
*
- * <warning>The returned value is only valid until the property changes so
- * it is only safe to use this function on the thread where
- * @self was constructed. Use mm_sms_dup_discharge_timestamp() if on another
- * thread.</warning>
+ * Returns: (transfer none): The timestamp, or %NULL if it couldn't be
+ * retrieved.
*
- * Returns: (transfer none): The timestamp, or %NULL if it couldn't be retrieved.
+ * Since: 1.0
*/
const gchar *
mm_sms_get_discharge_timestamp (MMSms *self)
@@ -359,9 +395,13 @@ mm_sms_get_discharge_timestamp (MMSms *self)
* <ulink url="http://en.wikipedia.org/wiki/ISO_8601">ISO8601</ulink>
* format.
*
- * This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_STATUS_REPORT.
+ * This field is only applicable if the PDU type is
+ * %MM_SMS_PDU_TYPE_STATUS_REPORT.
+ *
+ * Returns: (transfer full): The timestamp, or %NULL if it couldn't be
+ * retrieved. The returned value should be freed with g_free().
*
- * Returns: (transfer full): The timestamp, or %NULL if it couldn't be retrieved. The returned value should be freed with g_free().
+ * Since: 1.0
*/
gchar *
mm_sms_dup_discharge_timestamp (MMSms *self)
@@ -381,6 +421,8 @@ mm_sms_dup_discharge_timestamp (MMSms *self)
* Gets the type of validity information in the SMS.
*
* Returns: the validity type or #MM_SMS_VALIDITY_TYPE_UNKNOWN.
+ *
+ * Since: 1.0
*/
MMSmsValidityType
mm_sms_get_validity_type (MMSms *self)
@@ -411,6 +453,8 @@ mm_sms_get_validity_type (MMSms *self)
* Only applicable if the type of validity is #MM_SMS_VALIDITY_TYPE_RELATIVE.
*
* Returns: the length of the validity period, or 0 if unknown.
+ *
+ * Since: 1.0
*/
guint
mm_sms_get_validity_relative (MMSms *self)
@@ -446,6 +490,8 @@ mm_sms_get_validity_relative (MMSms *self)
* Gets the 3GPP message class of the SMS.
*
* Returns: the message class, or -1 for invalid/unset class.
+ *
+ * Since: 1.0
*/
gint
mm_sms_get_class (MMSms *self)
@@ -467,6 +513,8 @@ mm_sms_get_class (MMSms *self)
* message reference of the PDU associated to the status report.
*
* Returns: The message reference.
+ *
+ * Since: 1.0
*/
guint
mm_sms_get_message_reference (MMSms *self)
@@ -485,6 +533,8 @@ mm_sms_get_message_reference (MMSms *self)
* Checks whether delivery report is requested for this SMS.
*
* Returns: %TRUE if delivery report is requested, %FALSE otherwise.
+ *
+ * Since: 1.0
*/
gboolean
mm_sms_get_delivery_report_request (MMSms *self)
@@ -502,9 +552,12 @@ mm_sms_get_delivery_report_request (MMSms *self)
*
* Gets the delivery state of this SMS.
*
- * This field is only applicable if the PDU type is %MM_SMS_PDU_TYPE_STATUS_REPORT.
+ * This field is only applicable if the PDU type is
+ * %MM_SMS_PDU_TYPE_STATUS_REPORT.
*
* Returns: A #MMSmsDeliveryState specifying the delivery state.
+ *
+ * Since: 1.0
*/
guint
mm_sms_get_delivery_state (MMSms *self)
@@ -523,6 +576,8 @@ mm_sms_get_delivery_state (MMSms *self)
* Gets the state of this SMS.
*
* Returns: A #MMSmsState specifying the state.
+ *
+ * Since: 1.0
*/
MMSmsState
mm_sms_get_state (MMSms *self)
@@ -541,6 +596,8 @@ mm_sms_get_state (MMSms *self)
* Gets the storage in which this SMS is kept.
*
* Returns: A #MMSmsStorage specifying the storage.
+ *
+ * Since: 1.0
*/
MMSmsStorage
mm_sms_get_storage (MMSms *self)
@@ -559,6 +616,8 @@ mm_sms_get_storage (MMSms *self)
* Gets the PDU type on which this SMS is based.
*
* Returns: A #MMSmsPduType specifying the PDU type.
+ *
+ * Since: 1.0
*/
MMSmsPduType
mm_sms_get_pdu_type (MMSms *self)
@@ -577,6 +636,8 @@ mm_sms_get_pdu_type (MMSms *self)
* Gets the 3GPP2 Teleservice ID.
*
* Returns: a #MMSmsCdmaTeleserviceId.
+ *
+ * Since: 1.2
*/
MMSmsCdmaTeleserviceId
mm_sms_get_teleservice_id (MMSms *self)
@@ -595,6 +656,8 @@ mm_sms_get_teleservice_id (MMSms *self)
* Gets the 3GPP2 Service Category.
*
* Returns: a #MMSmsCdmaServiceCategory.
+ *
+ * Since: 1.2
*/
MMSmsCdmaServiceCategory
mm_sms_get_service_category (MMSms *self)
@@ -609,12 +672,15 @@ mm_sms_get_service_category (MMSms *self)
/**
* mm_sms_send_finish:
* @self: A #MMSms.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sms_send().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sms_send().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sms_send().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sms_send_finish (MMSms *self,
@@ -630,17 +696,22 @@ mm_sms_send_finish (MMSms *self,
* mm_sms_send:
* @self: A #MMSms.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronously requests to queue the message for delivery.
*
* SMS objects can only be sent once.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sms_send_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sms_send_finish() to get the result of the operation.
*
* See mm_sms_send_sync() for the synchronous, blocking version of this method.
+ *
+ * Since: 1.0
*/
void
mm_sms_send (MMSms *self,
@@ -666,10 +737,12 @@ mm_sms_send (MMSms *self,
*
* SMS objects can only be sent once.
*
- * The calling thread is blocked until a reply is received.
- * See mm_sms_send() for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See mm_sms_send()
+ * for the asynchronous version of this method.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sms_send_sync (MMSms *self,
@@ -688,12 +761,15 @@ mm_sms_send_sync (MMSms *self,
/**
* mm_sms_store_finish:
* @self: A #MMSms.
- * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_sms_store().
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to
+ * mm_sms_store().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_sms_store().
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ *
+ * Since: 1.0
*/
gboolean
mm_sms_store_finish (MMSms *self,
@@ -708,19 +784,25 @@ mm_sms_store_finish (MMSms *self,
/**
* mm_sms_store:
* @self: A #MMSms.
- * @storage: A #MMSmsStorage specifying where to store the SMS, or #MM_SMS_STORAGE_UNKNOWN to use the default.
+ * @storage: A #MMSmsStorage specifying where to store the SMS, or
+ * %MM_SMS_STORAGE_UNKNOWN to use the default.
* @cancellable: (allow-none): A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or
+ * %NULL.
* @user_data: User data to pass to @callback.
*
* Asynchronoulsy requests to store the message in the device if not already done.
*
* SMS objects can only be stored once.
*
- * When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
- * You can then call mm_sms_store_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from. You can then call
+ * mm_sms_store_finish() to get the result of the operation.
*
* See mm_sms_store_sync() for the synchronous, blocking version of this method.
+ *
+ * Since: 1.0
*/
void
mm_sms_store (MMSms *self,
@@ -741,18 +823,22 @@ mm_sms_store (MMSms *self,
/**
* mm_sms_store_sync:
* @self: A #MMSms.
- * @storage: A #MMSmsStorage specifying where to store the SMS, or #MM_SMS_STORAGE_UNKNOWN to use the default.
+ * @storage: A #MMSmsStorage specifying where to store the SMS, or
+ * %MM_SMS_STORAGE_UNKNOWN to use the default.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
- * Synchronoulsy requests to store the message in the device if not already done.
+ * Synchronoulsy requests to store the message in the device if not already
+ * done.
*
* SMS objects can only be stored once.
*
- * The calling thread is blocked until a reply is received.
- * See mm_sms_store() for the asynchronous version of this method.
+ * The calling thread is blocked until a reply is received. See mm_sms_store()
+ * for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
*
- * Returns: %TRUE if the operation succeded, %FALSE if @error is set.
+ * Since: 1.0
*/
gboolean
mm_sms_store_sync (MMSms *self,
diff --git a/libmm-glib/mm-sms.h b/libmm-glib/mm-sms.h
index 4e4e935b..58eeb41e 100644
--- a/libmm-glib/mm-sms.h
+++ b/libmm-glib/mm-sms.h
@@ -62,6 +62,7 @@ struct _MMSmsClass {
};
GType mm_sms_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSms, g_object_unref)
const gchar *mm_sms_get_path (MMSms *self);
gchar *mm_sms_dup_path (MMSms *self);
diff --git a/libmm-glib/mm-unlock-retries.c b/libmm-glib/mm-unlock-retries.c
index 935c7ceb..08b10a27 100644
--- a/libmm-glib/mm-unlock-retries.c
+++ b/libmm-glib/mm-unlock-retries.c
@@ -32,7 +32,7 @@
* mm_modem_get_unlock_retries() or mm_modem_peek_unlock_retries().
*/
-G_DEFINE_TYPE (MMUnlockRetries, mm_unlock_retries, G_TYPE_OBJECT);
+G_DEFINE_TYPE (MMUnlockRetries, mm_unlock_retries, G_TYPE_OBJECT)
struct _MMUnlockRetriesPrivate {
GHashTable *ht;
@@ -40,6 +40,9 @@ struct _MMUnlockRetriesPrivate {
/*****************************************************************************/
+/**
+ * mm_unlock_retries_set: (skip)
+ */
void
mm_unlock_retries_set (MMUnlockRetries *self,
MMModemLock lock,
@@ -50,6 +53,9 @@ mm_unlock_retries_set (MMUnlockRetries *self,
GUINT_TO_POINTER (retries));
}
+/**
+ * mm_unlock_retries_unset: (skip)
+ */
void
mm_unlock_retries_unset (MMUnlockRetries *self,
MMModemLock lock)
@@ -68,6 +74,8 @@ mm_unlock_retries_unset (MMUnlockRetries *self,
* Gets the unlock retries for the given @lock.
*
* Returns: the unlock retries or %MM_UNLOCK_RETRIES_UNKNOWN if unknown.
+ *
+ * Since: 1.0
*/
guint
mm_unlock_retries_get (MMUnlockRetries *self,
@@ -85,6 +93,9 @@ mm_unlock_retries_get (MMUnlockRetries *self,
/*****************************************************************************/
+/**
+ * mm_unlock_retries_cmp: (skip)
+ */
gboolean
mm_unlock_retries_cmp (MMUnlockRetries *a,
MMUnlockRetries *b)
@@ -116,6 +127,8 @@ mm_unlock_retries_cmp (MMUnlockRetries *a,
* @user_data: (closure): data to pass to @callback.
*
* Executes @callback for each lock information found in @self.
+ *
+ * Since: 1.0
*/
void
mm_unlock_retries_foreach (MMUnlockRetries *self,
@@ -135,6 +148,9 @@ mm_unlock_retries_foreach (MMUnlockRetries *self,
/*****************************************************************************/
+/**
+ * mm_unlock_retries_get_dictionary: (skip)
+ */
GVariant *
mm_unlock_retries_get_dictionary (MMUnlockRetries *self)
{
@@ -161,6 +177,9 @@ mm_unlock_retries_get_dictionary (MMUnlockRetries *self)
/*****************************************************************************/
+/**
+ * mm_unlock_retries_new_from_dictionary: (skip)
+ */
MMUnlockRetries *
mm_unlock_retries_new_from_dictionary (GVariant *dictionary)
{
@@ -184,6 +203,9 @@ mm_unlock_retries_new_from_dictionary (GVariant *dictionary)
/*****************************************************************************/
+/**
+ * mm_unlock_retries_build_string: (skip)
+ */
gchar *
mm_unlock_retries_build_string (MMUnlockRetries *self)
{
@@ -211,6 +233,9 @@ mm_unlock_retries_build_string (MMUnlockRetries *self)
/*****************************************************************************/
+/**
+ * mm_unlock_retries_new: (skip)
+ */
MMUnlockRetries *
mm_unlock_retries_new (void)
{
@@ -221,7 +246,7 @@ mm_unlock_retries_new (void)
static void
mm_unlock_retries_init (MMUnlockRetries *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self),
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_UNLOCK_RETRIES,
MMUnlockRetriesPrivate);
self->priv->ht = g_hash_table_new (g_direct_hash,
diff --git a/libmm-glib/mm-unlock-retries.h b/libmm-glib/mm-unlock-retries.h
index babdcbb3..4d76edcd 100644
--- a/libmm-glib/mm-unlock-retries.h
+++ b/libmm-glib/mm-unlock-retries.h
@@ -36,6 +36,8 @@ G_BEGIN_DECLS
* MM_UNLOCK_RETRIES_UNKNOWN:
*
* Identifier for reporting unknown unlock retries.
+ *
+ * Since: 1.0
*/
#define MM_UNLOCK_RETRIES_UNKNOWN 999
@@ -61,6 +63,7 @@ struct _MMUnlockRetriesClass {
};
GType mm_unlock_retries_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMUnlockRetries, g_object_unref)
guint mm_unlock_retries_get (MMUnlockRetries *self,
MMModemLock lock);
@@ -72,6 +75,8 @@ guint mm_unlock_retries_get (MMUnlockRetries *self,
* @user_data: data passed to the function.
*
* Specifies the type of function passed to mm_unlock_retries_foreach().
+ *
+ * Since: 1.0
*/
typedef void (* MMUnlockRetriesForeachCb) (MMModemLock lock,
guint count,
diff --git a/libmm-glib/tests/Makefile.am b/libmm-glib/tests/Makefile.am
index be93d4c6..8984d08d 100644
--- a/libmm-glib/tests/Makefile.am
+++ b/libmm-glib/tests/Makefile.am
@@ -1,12 +1,9 @@
include $(top_srcdir)/gtester.make
-noinst_PROGRAMS = test-common-helpers
-TEST_PROGS += $(noinst_PROGRAMS)
-
-test_common_helpers_SOURCES = \
- test-common-helpers.c
+AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
+AM_LDFLAGS = $(CODE_COVERAGE_LDFLAGS)
-test_common_helpers_CPPFLAGS = \
+LIBMM_GLIB_TESTS_COMMON_CPPFLAGS = \
$(MM_CFLAGS) \
-I$(top_srcdir) \
-I$(top_srcdir)/include \
@@ -17,6 +14,19 @@ test_common_helpers_CPPFLAGS = \
-I${top_builddir}/libmm-glib/generated \
-DLIBMM_GLIB_COMPILATION
-test_common_helpers_LDADD = \
+LIBMM_GLIB_TESTS_COMMON_LDADD = \
$(top_builddir)/libmm-glib/libmm-glib.la \
$(MM_LIBS)
+
+noinst_PROGRAMS = \
+ test-common-helpers \
+ test-pco
+TEST_PROGS += $(noinst_PROGRAMS)
+
+test_common_helpers_SOURCES = test-common-helpers.c
+test_common_helpers_CPPFLAGS = $(LIBMM_GLIB_TESTS_COMMON_CPPFLAGS)
+test_common_helpers_LDADD = $(LIBMM_GLIB_TESTS_COMMON_LDADD)
+
+test_pco_SOURCES = test-pco.c
+test_pco_CPPFLAGS = $(LIBMM_GLIB_TESTS_COMMON_CPPFLAGS)
+test_pco_LDADD = $(LIBMM_GLIB_TESTS_COMMON_LDADD)
diff --git a/libmm-glib/tests/meson.build b/libmm-glib/tests/meson.build
new file mode 100644
index 00000000..83dd80d1
--- /dev/null
+++ b/libmm-glib/tests/meson.build
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+test_units = [
+ 'common-helpers',
+ 'pco',
+]
+
+foreach test_unit: test_units
+ test_name = 'test-' + test_unit
+
+ exe = executable(
+ test_name,
+ test_name + '.c',
+ include_directories: top_inc,
+ dependencies: libmm_glib_dep,
+ c_args: '-DLIBMM_GLIB_COMPILATION',
+ )
+
+ test(test_name, exe)
+endforeach
diff --git a/libmm-glib/tests/test-common-helpers.c b/libmm-glib/tests/test-common-helpers.c
index 9b71e6b9..69b95cb1 100644
--- a/libmm-glib/tests/test-common-helpers.c
+++ b/libmm-glib/tests/test-common-helpers.c
@@ -13,6 +13,7 @@
* Copyright (C) 2012 Google, Inc.
*/
+#include <string.h>
#include <glib-object.h>
#include <libmm-glib.h>
@@ -366,6 +367,8 @@ field_parser_int (void)
g_assert (mm_get_int_from_str ("100a", &num) == FALSE);
+ g_assert (mm_get_int_from_str ("\r\n", &num) == FALSE);
+
str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64)G_MAXINT + 1);
g_assert (mm_get_int_from_str (str, &num) == FALSE);
g_free (str);
@@ -385,6 +388,9 @@ field_parser_int (void)
g_assert (mm_get_int_from_str ("100", &num) == TRUE);
g_assert_cmpint (num, ==, 100);
+ g_assert (mm_get_int_from_str ("-256\r\n", &num) == TRUE);
+ g_assert_cmpint (num, ==, -256);
+
str = g_strdup_printf ("%" G_GINT64_FORMAT, (gint64)G_MAXINT);
g_assert (mm_get_int_from_str (str, &num) == TRUE);
g_assert_cmpint (num, ==, G_MAXINT);
@@ -416,6 +422,8 @@ field_parser_uint (void)
g_assert (mm_get_uint_from_str ("-100", &num) == FALSE);
+ g_assert (mm_get_uint_from_str ("\r\n", &num) == FALSE);
+
str = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64)(G_MAXUINT) + 1);
g_assert (mm_get_uint_from_str (str, &num) == FALSE);
g_free (str);
@@ -428,6 +436,9 @@ field_parser_uint (void)
g_assert (mm_get_uint_from_str ("100", &num) == TRUE);
g_assert_cmpuint (num, ==, 100);
+ g_assert (mm_get_uint_from_str ("256\r\n", &num) == TRUE);
+ g_assert_cmpuint (num, ==, 256);
+
str = g_strdup_printf ("%" G_GUINT64_FORMAT, (guint64)G_MAXUINT);
g_assert (mm_get_uint_from_str (str, &num) == TRUE);
g_assert_cmpuint (num, ==, G_MAXUINT);
@@ -452,6 +463,8 @@ field_parser_double (void)
g_assert (mm_get_double_from_str ("100a", &num) == FALSE);
+ g_assert (mm_get_double_from_str ("\r\n", &num) == FALSE);
+
/* Successes */
g_assert (mm_get_double_from_str ("-100", &num) == TRUE);
@@ -475,6 +488,9 @@ field_parser_double (void)
g_assert (mm_get_double_from_str ("100.7567", &num) == TRUE);
g_assert (num - (100.7567) < 0000000.1);
+ g_assert (mm_get_double_from_str ("100.7567\r\n", &num) == TRUE);
+ g_assert (num - (100.7567) < 0000000.1);
+
str = g_strdup_printf ("%lf", (gdouble)G_MINDOUBLE);
g_assert (mm_get_double_from_str (str, &num) == TRUE);
g_assert (num - G_MINDOUBLE < 0000000.1);
@@ -487,10 +503,123 @@ field_parser_double (void)
}
/**************************************************************/
+/* hexstr2bin & bin2hexstr */
+
+static void
+common_hexstr2bin_test_failure (const gchar *input_hex)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree guint8 *bin = NULL;
+ gsize bin_len = 0;
+
+ g_assert (mm_utils_ishexstr (input_hex) == FALSE);
+
+ bin = mm_utils_hexstr2bin (input_hex, -1, &bin_len, &error);
+ g_assert_null (bin);
+ g_assert_nonnull (error);
+}
+
+static void
+common_hexstr2bin_test_success_len (const gchar *input_hex,
+ gssize input_hex_len)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree guint8 *bin = NULL;
+ gsize bin_len = 0;
+ g_autofree gchar *hex = NULL;
+
+ bin = mm_utils_hexstr2bin (input_hex, input_hex_len, &bin_len, &error);
+ g_assert_nonnull (bin);
+ g_assert_no_error (error);
+
+ hex = mm_utils_bin2hexstr (bin, bin_len);
+ g_assert_nonnull (hex);
+
+ if (input_hex_len == -1)
+ g_assert (g_ascii_strcasecmp (input_hex, hex) == 0);
+ else
+ g_assert (g_ascii_strncasecmp (input_hex, hex, (gsize)input_hex_len) == 0);
+}
+
+static void
+common_hexstr2bin_test_success (const gchar *input_hex)
+{
+ gsize input_hex_len;
+ gssize i;
+
+ g_assert (mm_utils_ishexstr (input_hex) == TRUE);
+
+ common_hexstr2bin_test_success_len (input_hex, -1);
+
+ input_hex_len = strlen (input_hex);
+ for (i = input_hex_len; i >= 2; i-=2)
+ common_hexstr2bin_test_success_len (input_hex, i);
+}
+
+static void
+hexstr_lower_case (void)
+{
+ common_hexstr2bin_test_success ("000123456789abcdefff");
+}
+
+static void
+hexstr_upper_case (void)
+{
+ common_hexstr2bin_test_success ("000123456789ABCDEFFF");
+}
+
+static void
+hexstr_mixed_case (void)
+{
+ common_hexstr2bin_test_success ("000123456789AbcDefFf");
+}
+
+static void
+hexstr_empty (void)
+{
+ common_hexstr2bin_test_failure ("");
+}
+
+static void
+hexstr_missing_digits (void)
+{
+ common_hexstr2bin_test_failure ("012");
+}
+
+static void
+hexstr_wrong_digits_all (void)
+{
+ common_hexstr2bin_test_failure ("helloworld");
+}
+
+static void
+hexstr_wrong_digits_some (void)
+{
+ common_hexstr2bin_test_failure ("012345k7");
+}
+
+static void
+date_time_iso8601 (void)
+{
+ gchar *date = NULL;
+
+ date = mm_new_iso8601_time_from_unix_time (1634307342);
+ g_assert_cmpstr (date, ==, "2021-10-15T14:15:42Z");
+ g_free (date);
+
+ date = mm_new_iso8601_time (2021, 10, 15, 16, 15, 42, FALSE, 0);
+ g_assert_cmpstr (date, ==, "2021-10-15T16:15:42Z");
+ g_free (date);
+
+ date = mm_new_iso8601_time (2021, 10, 15, 16, 15, 42, TRUE, 120);
+ g_assert_cmpstr (date, ==, "2021-10-15T16:15:42+02");
+ g_free (date);
+}
+
+/**************************************************************/
int main (int argc, char **argv)
{
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/Common/KeyValue/standard", key_value_test_standard);
@@ -525,5 +654,14 @@ int main (int argc, char **argv)
g_test_add_func ("/MM/Common/FieldParsers/Uint", field_parser_uint);
g_test_add_func ("/MM/Common/FieldParsers/Double", field_parser_double);
+ g_test_add_func ("/MM/Common/HexStr/lower-case", hexstr_lower_case);
+ g_test_add_func ("/MM/Common/HexStr/upper-case", hexstr_upper_case);
+ g_test_add_func ("/MM/Common/HexStr/mixed-case", hexstr_mixed_case);
+ g_test_add_func ("/MM/Common/HexStr/missing-empty", hexstr_empty);
+ g_test_add_func ("/MM/Common/HexStr/missing-digits", hexstr_missing_digits);
+ g_test_add_func ("/MM/Common/HexStr/wrong-digits-all", hexstr_wrong_digits_all);
+ g_test_add_func ("/MM/Common/HexStr/wrong-digits-some", hexstr_wrong_digits_some);
+
+ g_test_add_func ("/MM/Common/DateTime/iso8601", date_time_iso8601);
return g_test_run ();
}
diff --git a/libmm-glib/tests/test-pco.c b/libmm-glib/tests/test-pco.c
new file mode 100644
index 00000000..4b09d4c9
--- /dev/null
+++ b/libmm-glib/tests/test-pco.c
@@ -0,0 +1,94 @@
+/* -*- 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 2018 Google LLC.
+ */
+
+#include <glib.h>
+#include <libmm-glib.h>
+#include <string.h>
+
+typedef struct {
+ guint32 session_id;
+ gboolean is_complete;
+ gsize pco_data_size;
+ guint8 pco_data[50];
+} TestPco;
+
+static const TestPco test_pco_list[] = {
+ { 3, TRUE, 8, { 0x27, 0x06, 0x80, 0x00, 0x10, 0x02, 0x05, 0x94 } },
+ { 1, FALSE, 3, { 0x27, 0x01, 0x80 } },
+ { 5, FALSE, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
+ { 4, TRUE, 14, { 0x27, 0x0C, 0x80, 0x10, 0x02, 0x05, 0x94, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
+ { 3, FALSE, 10, { 0x27, 0x08, 0x80, 0x00, 0x0D, 0x04, 0xC6, 0xE0, 0xAD, 0x87 } },
+};
+
+static const TestPco expected_pco_list[] = {
+ { 1, FALSE, 3, { 0x27, 0x01, 0x80 } },
+ { 3, FALSE, 10, { 0x27, 0x08, 0x80, 0x00, 0x0D, 0x04, 0xC6, 0xE0, 0xAD, 0x87 } },
+ { 4, TRUE, 14, { 0x27, 0x0C, 0x80, 0x10, 0x02, 0x05, 0x94, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
+ { 5, FALSE, 10, { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
+};
+
+static void
+test_pco_list_add (void)
+{
+ GList *list = NULL;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_pco_list); ++i) {
+ const TestPco *test_pco = &test_pco_list[i];
+ MMPco *pco;
+
+ pco = mm_pco_new ();
+ mm_pco_set_session_id (pco, test_pco->session_id);
+ mm_pco_set_complete (pco, test_pco->is_complete);
+ mm_pco_set_data (pco, test_pco->pco_data, test_pco->pco_data_size);
+ list = mm_pco_list_add (list, pco);
+ }
+
+ g_assert (list != NULL);
+ g_assert_cmpuint (g_list_length (list), ==, G_N_ELEMENTS (expected_pco_list));
+
+ for (i = 0; i < G_N_ELEMENTS (expected_pco_list); ++i) {
+ GList *current;
+ MMPco *pco;
+ const TestPco *expected_pco;
+ gsize pco_data_size;
+ const guint8 *pco_data;
+
+ current = g_list_nth (list, i);
+ pco = current->data;
+ expected_pco = &expected_pco_list[i];
+
+ g_assert (pco != NULL);
+ g_assert_cmpuint (mm_pco_get_session_id (pco), ==, expected_pco->session_id);
+ g_assert (mm_pco_is_complete (pco) == expected_pco->is_complete);
+ pco_data = mm_pco_get_data (pco, &pco_data_size);
+ g_assert (pco_data != NULL);
+ g_assert_cmpuint (pco_data_size, ==, expected_pco->pco_data_size);
+ g_assert_cmpint (memcmp (pco_data, expected_pco->pco_data, pco_data_size), ==, 0);
+ }
+
+ g_list_free_full (list, g_object_unref);
+}
+
+/**************************************************************/
+
+int main (int argc, char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/Pco/pco-list-add", test_pco_list_add);
+
+ return g_test_run ();
+}
diff --git a/libqcdm/src/Makefile.am b/libqcdm/src/Makefile.am
index 9aa9500f..69143bdf 100644
--- a/libqcdm/src/Makefile.am
+++ b/libqcdm/src/Makefile.am
@@ -1,5 +1,15 @@
-noinst_LTLIBRARIES = libqcdm.la libqcdm-test.la
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(CODE_COVERAGE_LDFLAGS) \
+ $(NULL)
+
+noinst_LTLIBRARIES = libqcdm.la libqcdm-test.la
libqcdm_la_CPPFLAGS = \
$(MM_CFLAGS)
@@ -14,6 +24,8 @@ libqcdm_la_SOURCES = \
commands.h \
errors.c \
errors.h \
+ logs.c \
+ logs.h \
result.c \
result.h \
result-private.h \
@@ -37,4 +49,3 @@ libqcdm_test_la_SOURCES = \
libqcdm_test_la_LIBADD = \
$(MM_LIBS)
-
diff --git a/libqcdm/src/com.c b/libqcdm/src/com.c
index ad50dd0c..d6283e06 100644
--- a/libqcdm/src/com.c
+++ b/libqcdm/src/com.c
@@ -39,7 +39,7 @@ qcdm_port_setup (int fd)
stbuf.c_iflag &= ~(HUPCL | IUTF8 | IUCLC | ISTRIP | IXON | IXOFF | IXANY | ICRNL);
stbuf.c_oflag &= ~(OPOST | OCRNL | ONLCR | OLCUC | ONLRET);
stbuf.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL);
- stbuf.c_lflag &= ~(NOFLSH | XCASE | TOSTOP | ECHOPRT | ECHOCTL | ECHOKE);
+ stbuf.c_lflag &= ~(NOFLSH | TOSTOP | ECHOPRT | ECHOCTL | ECHOKE);
stbuf.c_cc[VMIN] = 1;
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
diff --git a/libqcdm/src/commands.c b/libqcdm/src/commands.c
index fe436e97..a97cb9af 100644
--- a/libqcdm/src/commands.c
+++ b/libqcdm/src/commands.c
@@ -29,8 +29,8 @@
/**********************************************************************/
-static u_int8_t
-cdma_prev_to_qcdm (u_int8_t cdma)
+static uint8_t
+cdma_prev_to_qcdm (uint8_t cdma)
{
switch (cdma) {
case CDMA_PREV_IS_95:
@@ -53,8 +53,8 @@ cdma_prev_to_qcdm (u_int8_t cdma)
return QCDM_CDMA_PREV_UNKNOWN;
}
-static u_int8_t
-cdma_band_class_to_qcdm (u_int8_t cdma)
+static uint8_t
+cdma_band_class_to_qcdm (uint8_t cdma)
{
switch (cdma) {
case CDMA_BAND_CLASS_0_CELLULAR_800:
@@ -103,8 +103,8 @@ cdma_band_class_to_qcdm (u_int8_t cdma)
return QCDM_CDMA_BAND_CLASS_UNKNOWN;
}
-static u_int8_t
-nv_mode_pref_from_qcdm (u_int8_t qcdm)
+static uint8_t
+nv_mode_pref_from_qcdm (uint8_t qcdm)
{
switch (qcdm) {
case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_DIGITAL:
@@ -135,6 +135,8 @@ nv_mode_pref_from_qcdm (u_int8_t qcdm)
return DIAG_NV_MODE_PREF_GSM_UMTS_LTE_ONLY;
case QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_1X_HDR_LTE_ONLY:
return DIAG_NV_MODE_PREF_1X_HDR_LTE_ONLY;
+ default:
+ break;
}
return DIAG_NV_MODE_PREF_AUTO;
};
@@ -152,7 +154,7 @@ nv_mode_pref_from_qcdm (u_int8_t qcdm)
*
*/
static char *
-bin2hexstr (const u_int8_t *bytes, int len)
+bin2hexstr (const uint8_t *bytes, int len)
{
static char hex_digits[] = "0123456789abcdef";
char *result;
@@ -178,7 +180,7 @@ bin2hexstr (const u_int8_t *bytes, int len)
/**********************************************************************/
static qcdmbool
-check_command (const char *buf, size_t len, u_int8_t cmd, size_t min_len, int *out_error)
+check_command (const char *buf, size_t len, uint8_t cmd, size_t min_len, int *out_error)
{
if (len < 1) {
qcdm_err (0, "DM command response malformed (must be at least 1 byte in length)");
@@ -241,7 +243,7 @@ check_command (const char *buf, size_t len, u_int8_t cmd, size_t min_len, int *o
}
static int
-nv_status_to_qcdm_error (u_int16_t status)
+nv_status_to_qcdm_error (uint16_t status)
{
switch (status) {
case DIAG_NV_STATUS_OK:
@@ -266,9 +268,9 @@ nv_status_to_qcdm_error (u_int16_t status)
}
static qcdmbool
-check_nv_cmd (DMCmdNVReadWrite *cmd, u_int16_t nv_item, int *out_error)
+check_nv_cmd (DMCmdNVReadWrite *cmd, uint16_t nv_item, int *out_error)
{
- u_int16_t cmd_item;
+ uint16_t cmd_item;
qcdm_return_val_if_fail (cmd != NULL, FALSE);
qcdm_return_val_if_fail ((cmd->code == DIAG_CMD_NV_READ) || (cmd->code == DIAG_CMD_NV_WRITE), FALSE);
@@ -375,7 +377,7 @@ qcdm_cmd_esn_result (const char *buf, size_t len, int *out_error)
QcdmResult *result = NULL;
DMCmdEsnRsp *rsp = (DMCmdEsnRsp *) buf;
char *tmp;
- u_int8_t swapped[4];
+ uint8_t swapped[4];
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -403,7 +405,7 @@ qcdm_cmd_esn_result (const char *buf, size_t len, int *out_error)
/**********************************************************************/
size_t
-qcdm_cmd_control_new (char *buf, size_t len, u_int8_t mode)
+qcdm_cmd_control_new (char *buf, size_t len, uint8_t mode)
{
char cmdbuf[5];
DMCmdControl *cmd = (DMCmdControl *) &cmdbuf[0];
@@ -413,7 +415,7 @@ qcdm_cmd_control_new (char *buf, size_t len, u_int8_t mode)
memset (cmd, 0, sizeof (*cmd));
cmd->code = DIAG_CMD_CONTROL;
- cmd->mode = htole16 ((u_int16_t) mode);
+ cmd->mode = htole16 ((uint16_t) mode);
return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
}
@@ -452,8 +454,8 @@ qcdm_cmd_cdma_status_result (const char *buf, size_t len, int *out_error)
QcdmResult *result = NULL;
DMCmdStatusRsp *rsp = (DMCmdStatusRsp *) buf;
char *tmp;
- u_int8_t swapped[4];
- u_int32_t tmp_num;
+ uint8_t swapped[4];
+ uint32_t tmp_num;
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -474,27 +476,27 @@ qcdm_cmd_cdma_status_result (const char *buf, size_t len, int *out_error)
qcdm_result_add_string (result, QCDM_CMD_CDMA_STATUS_ITEM_ESN, tmp);
free (tmp);
- tmp_num = (u_int32_t) le16toh (rsp->rf_mode);
+ tmp_num = (uint32_t) le16toh (rsp->rf_mode);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RF_MODE, tmp_num);
- tmp_num = (u_int32_t) le16toh (rsp->cdma_rx_state);
+ tmp_num = (uint32_t) le16toh (rsp->cdma_rx_state);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_RX_STATE, tmp_num);
- tmp_num = (u_int32_t) le16toh (rsp->entry_reason);
+ tmp_num = (uint32_t) le16toh (rsp->entry_reason);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_ENTRY_REASON, tmp_num);
- tmp_num = (u_int32_t) le16toh (rsp->curr_chan);
+ tmp_num = (uint32_t) le16toh (rsp->curr_chan);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_CURRENT_CHANNEL, tmp_num);
qcdm_result_add_u8 (result, QCDM_CMD_CDMA_STATUS_ITEM_CODE_CHANNEL, rsp->cdma_code_chan);
- tmp_num = (u_int32_t) le16toh (rsp->pilot_base);
+ tmp_num = (uint32_t) le16toh (rsp->pilot_base);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_PILOT_BASE, tmp_num);
- tmp_num = (u_int32_t) le16toh (rsp->sid);
+ tmp_num = (uint32_t) le16toh (rsp->sid);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_SID, tmp_num);
- tmp_num = (u_int32_t) le16toh (rsp->nid);
+ tmp_num = (uint32_t) le16toh (rsp->nid);
qcdm_result_add_u32 (result, QCDM_CMD_CDMA_STATUS_ITEM_NID, tmp_num);
return result;
@@ -566,15 +568,15 @@ qcdm_cmd_status_snapshot_new (char *buf, size_t len)
return dm_encapsulate_buffer (cmdbuf, sizeof (*cmd), sizeof (cmdbuf), buf, len);
}
-static u_int8_t
-snapshot_state_to_qcdm (u_int8_t cdma_state)
+static uint8_t
+snapshot_state_to_qcdm (uint8_t cdma_state)
{
/* CDMA_STATUS_SNAPSHOT_STATE_* -> QCDM_STATUS_SNAPSHOT_STATE_* */
return cdma_state + 1;
}
-static inline u_int8_t
-digit_fixup (u_int8_t d)
+static inline uint8_t
+digit_fixup (uint8_t d)
{
/* CDMA MCC/IMSI conversion adds 1 to each digit, and digits equal to
* 10 are really zero.
@@ -590,9 +592,9 @@ qcdm_cmd_status_snapshot_result (const char *buf, size_t len, int *out_error)
QcdmResult *result = NULL;
DMCmdStatusSnapshotRsp *rsp = (DMCmdStatusSnapshotRsp *) buf;
char *tmp;
- u_int8_t swapped[4];
- u_int8_t tmcc[3];
- u_int16_t mcc, hmcc;
+ uint8_t swapped[4];
+ uint8_t tmcc[3];
+ uint16_t mcc, hmcc;
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -653,7 +655,7 @@ qcdm_cmd_pilot_sets_new (char *buf, size_t len)
#define PILOT_SETS_CMD_NEIGHBOR_SET "neighbor-set"
static const char *
-set_num_to_str (u_int32_t num)
+set_num_to_str (uint32_t num)
{
if (num == QCDM_CMD_PILOT_SETS_TYPE_ACTIVE)
return PILOT_SETS_CMD_ACTIVE_SET;
@@ -682,7 +684,7 @@ qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error)
if (sets_len > 0) {
qcdm_result_add_u8_array (result,
PILOT_SETS_CMD_ACTIVE_SET,
- (const u_int8_t *) &rsp->sets[0],
+ (const uint8_t *) &rsp->sets[0],
sets_len);
}
@@ -690,7 +692,7 @@ qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error)
if (sets_len > 0) {
qcdm_result_add_u8_array (result,
PILOT_SETS_CMD_CANDIDATE_SET,
- (const u_int8_t *) &rsp->sets[rsp->active_count],
+ (const uint8_t *) &rsp->sets[rsp->active_count],
sets_len);
}
@@ -698,7 +700,7 @@ qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error)
if (sets_len > 0) {
qcdm_result_add_u8_array (result,
PILOT_SETS_CMD_NEIGHBOR_SET,
- (const u_int8_t *) &rsp->sets[rsp->active_count + rsp->candidate_count],
+ (const uint8_t *) &rsp->sets[rsp->active_count + rsp->candidate_count],
sets_len);
}
@@ -707,11 +709,11 @@ qcdm_cmd_pilot_sets_result (const char *buf, size_t len, int *out_error)
qcdmbool
qcdm_cmd_pilot_sets_result_get_num (QcdmResult *result,
- u_int32_t set_type,
- u_int32_t *out_num)
+ uint32_t set_type,
+ uint32_t *out_num)
{
const char *set_name;
- const u_int8_t *array = NULL;
+ const uint8_t *array = NULL;
size_t array_len = 0;
qcdm_return_val_if_fail (result != NULL, FALSE);
@@ -728,15 +730,15 @@ qcdm_cmd_pilot_sets_result_get_num (QcdmResult *result,
qcdmbool
qcdm_cmd_pilot_sets_result_get_pilot (QcdmResult *result,
- u_int32_t set_type,
- u_int32_t num,
- u_int32_t *out_pn_offset,
- u_int32_t *out_ecio,
+ uint32_t set_type,
+ uint32_t num,
+ uint32_t *out_pn_offset,
+ uint32_t *out_ecio,
float *out_db)
{
const char *set_name;
DMCmdPilotSetsSet *set;
- const u_int8_t *array = NULL;
+ const uint8_t *array = NULL;
size_t array_len = 0;
qcdm_return_val_if_fail (result != NULL, FALSE);
@@ -753,14 +755,14 @@ qcdm_cmd_pilot_sets_result_get_pilot (QcdmResult *result,
*out_pn_offset = set->pn_offset;
*out_ecio = set->ecio;
/* EC/IO is in units of -0.5 dB per the specs */
- *out_db = (float) set->ecio * -0.5;
+ *out_db = (float) (set->ecio * -0.5);
return TRUE;
}
/**********************************************************************/
size_t
-qcdm_cmd_nv_get_mdn_new (char *buf, size_t len, u_int8_t profile)
+qcdm_cmd_nv_get_mdn_new (char *buf, size_t len, uint8_t profile)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -812,7 +814,7 @@ qcdm_cmd_nv_get_mdn_result (const char *buf, size_t len, int *out_error)
/**********************************************************************/
static qcdmbool
-roam_pref_validate (u_int8_t dm)
+roam_pref_validate (uint8_t dm)
{
if ( dm == DIAG_NV_ROAM_PREF_HOME_ONLY
|| dm == DIAG_NV_ROAM_PREF_ROAM_ONLY
@@ -822,7 +824,7 @@ roam_pref_validate (u_int8_t dm)
}
size_t
-qcdm_cmd_nv_get_roam_pref_new (char *buf, size_t len, u_int8_t profile)
+qcdm_cmd_nv_get_roam_pref_new (char *buf, size_t len, uint8_t profile)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -873,8 +875,8 @@ qcdm_cmd_nv_get_roam_pref_result (const char *buf, size_t len, int *out_error)
size_t
qcdm_cmd_nv_set_roam_pref_new (char *buf,
size_t len,
- u_int8_t profile,
- u_int8_t roam_pref)
+ uint8_t profile,
+ uint8_t roam_pref)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -916,7 +918,7 @@ qcdm_cmd_nv_set_roam_pref_result (const char *buf, size_t len, int *out_error)
/**********************************************************************/
size_t
-qcdm_cmd_nv_get_mode_pref_new (char *buf, size_t len, u_int8_t profile)
+qcdm_cmd_nv_get_mode_pref_new (char *buf, size_t len, uint8_t profile)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -962,8 +964,8 @@ qcdm_cmd_nv_get_mode_pref_result (const char *buf, size_t len, int *out_error)
size_t
qcdm_cmd_nv_set_mode_pref_new (char *buf,
size_t len,
- u_int8_t profile,
- u_int8_t mode_pref)
+ uint8_t profile,
+ uint8_t mode_pref)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -1044,7 +1046,7 @@ qcdm_cmd_nv_get_hybrid_pref_result (const char *buf, size_t len, int *out_error)
size_t
qcdm_cmd_nv_set_hybrid_pref_new (char *buf,
size_t len,
- u_int8_t hybrid_pref)
+ uint8_t hybrid_pref)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -1132,7 +1134,7 @@ qcdm_cmd_nv_get_ipv6_enabled_result (const char *buf, size_t len, int *out_error
size_t
qcdm_cmd_nv_set_ipv6_enabled_new (char *buf,
size_t len,
- u_int8_t enabled)
+ uint8_t enabled)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -1176,7 +1178,7 @@ qcdm_cmd_nv_set_ipv6_enabled_result (const char *buf, size_t len, int *out_error
/**********************************************************************/
static qcdmbool
-hdr_rev_pref_validate (u_int8_t dm)
+hdr_rev_pref_validate (uint8_t dm)
{
if ( dm == DIAG_NV_HDR_REV_PREF_0
|| dm == DIAG_NV_HDR_REV_PREF_A
@@ -1232,7 +1234,7 @@ qcdm_cmd_nv_get_hdr_rev_pref_result (const char *buf, size_t len, int *out_error
size_t
qcdm_cmd_nv_set_hdr_rev_pref_new (char *buf,
size_t len,
- u_int8_t rev_pref)
+ uint8_t rev_pref)
{
char cmdbuf[sizeof (DMCmdNVReadWrite) + 2];
DMCmdNVReadWrite *cmd = (DMCmdNVReadWrite *) &cmdbuf[0];
@@ -1294,15 +1296,15 @@ qcdm_cmd_cm_subsys_state_info_result (const char *buf, size_t len, int *out_erro
{
QcdmResult *result = NULL;
DMCmdSubsysCMStateInfoRsp *rsp = (DMCmdSubsysCMStateInfoRsp *) buf;
- u_int32_t tmp_num;
- u_int32_t roam_pref;
+ uint32_t tmp_num;
+ uint32_t roam_pref;
qcdm_return_val_if_fail (buf != NULL, NULL);
if (!check_command (buf, len, DIAG_CMD_SUBSYS, sizeof (DMCmdSubsysCMStateInfoRsp), out_error))
return NULL;
- roam_pref = (u_int32_t) le32toh (rsp->roam_pref);
+ roam_pref = (uint32_t) le32toh (rsp->roam_pref);
if (!roam_pref_validate (roam_pref)) {
qcdm_err (0, "Unknown roam preference 0x%X", roam_pref);
return NULL;
@@ -1310,33 +1312,33 @@ qcdm_cmd_cm_subsys_state_info_result (const char *buf, size_t len, int *out_erro
result = qcdm_result_new ();
- tmp_num = (u_int32_t) le32toh (rsp->call_state);
+ tmp_num = (uint32_t) le32toh (rsp->call_state);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_CALL_STATE, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->oper_mode);
+ tmp_num = (uint32_t) le32toh (rsp->oper_mode);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->system_mode);
+ tmp_num = (uint32_t) le32toh (rsp->system_mode);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->mode_pref);
+ tmp_num = (uint32_t) le32toh (rsp->mode_pref);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_MODE_PREF, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->band_pref);
+ tmp_num = (uint32_t) le32toh (rsp->band_pref);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_BAND_PREF, tmp_num);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ROAM_PREF, roam_pref);
- tmp_num = (u_int32_t) le32toh (rsp->srv_domain_pref);
+ tmp_num = (uint32_t) le32toh (rsp->srv_domain_pref);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SERVICE_DOMAIN_PREF, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->acq_order_pref);
+ tmp_num = (uint32_t) le32toh (rsp->acq_order_pref);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_ACQ_ORDER_PREF, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->hybrid_pref);
+ tmp_num = (uint32_t) le32toh (rsp->hybrid_pref);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, tmp_num);
- tmp_num = (u_int32_t) le32toh (rsp->network_sel_mode_pref);
+ tmp_num = (uint32_t) le32toh (rsp->network_sel_mode_pref);
qcdm_result_add_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_NETWORK_SELECTION_PREF, tmp_num);
return result;
@@ -1392,14 +1394,14 @@ qcdm_cmd_hdr_subsys_state_info_result (const char *buf, size_t len, int *out_err
size_t
qcdm_cmd_ext_logmask_new (char *buf,
size_t len,
- u_int32_t items[],
- u_int16_t maxlog)
+ uint32_t items[],
+ uint16_t maxlog)
{
char cmdbuf[sizeof (DMCmdExtLogMask) + 2];
DMCmdExtLogMask *cmd = (DMCmdExtLogMask *) &cmdbuf[0];
- u_int16_t highest = 0;
+ uint16_t highest = 0;
size_t total = 3;
- u_int32_t i;
+ uint32_t i;
qcdm_return_val_if_fail (buf != NULL, 0);
qcdm_return_val_if_fail (len >= sizeof (*cmd) + DIAG_TRAILER_LEN, 0);
@@ -1432,7 +1434,7 @@ qcdm_cmd_ext_logmask_result (const char *buf, size_t len, int *out_error)
{
QcdmResult *result = NULL;
DMCmdExtLogMask *rsp = (DMCmdExtLogMask *) buf;
- u_int32_t masklen = 0, maxlog = 0;
+ uint32_t masklen = 0, maxlog = 0;
size_t minlen = 0;
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -1479,7 +1481,7 @@ qcdm_cmd_ext_logmask_result (const char *buf, size_t len, int *out_error)
qcdmbool
qcmd_cmd_ext_logmask_result_get_item (QcdmResult *result,
- u_int16_t item)
+ uint16_t item)
{
return FALSE;
}
@@ -1555,7 +1557,7 @@ qcdm_cmd_zte_subsys_status_result (const char *buf, size_t len, int *out_error)
size_t
qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (char *buf,
size_t len,
- u_int8_t chipset)
+ uint8_t chipset)
{
char cmdbuf[sizeof (DMCmdSubsysNwSnapshotReq) + 2];
DMCmdSubsysNwSnapshotReq *cmd = (DMCmdSubsysNwSnapshotReq *) &cmdbuf[0];
@@ -1594,8 +1596,8 @@ qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf, size_t len, int
QcdmResult *result = NULL;
DMCmdSubsysNwSnapshotRsp *rsp = (DMCmdSubsysNwSnapshotRsp *) buf;
DMCmdSubsysNwSnapshotCdma *cdma = (DMCmdSubsysNwSnapshotCdma *) &rsp->data;
- u_int32_t num;
- u_int8_t num8;
+ uint32_t num;
+ uint8_t num8;
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -1638,7 +1640,7 @@ qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf, size_t len, int
size_t
qcdm_cmd_nw_subsys_eri_new (char *buf,
size_t len,
- u_int8_t chipset)
+ uint8_t chipset)
{
char cmdbuf[sizeof (DMCmdSubsysHeader) + 2];
DMCmdSubsysHeader *cmd = (DMCmdSubsysHeader *) &cmdbuf[0];
@@ -1708,16 +1710,17 @@ qcdm_cmd_nw_subsys_eri_result (const char *buf, size_t len, int *out_error)
static size_t
qcdm_cmd_log_config_new (char *buf,
size_t len,
- u_int32_t op,
- u_int32_t equip_id,
- u_int16_t items[])
+ uint32_t op,
+ uint32_t equip_id,
+ uint16_t items[])
{
DMCmdLogConfig *cmd;
- u_int16_t highest = 0;
- u_int32_t items_len = 0;
+ uint16_t highest = 0;
+ uint32_t items_len = 0;
size_t cmdsize = 0, cmdbufsize;
- u_int32_t i;
- u_int16_t log_code;
+ uint32_t i;
+ uint16_t log_code;
+ size_t ret;
qcdm_return_val_if_fail (buf != NULL, 0);
qcdm_return_val_if_fail ((equip_id & 0xFFF0) == 0, 0);
@@ -1751,13 +1754,16 @@ qcdm_cmd_log_config_new (char *buf,
cmd->num_items = htole32 (highest);
}
- return dm_encapsulate_buffer ((char *) cmd, cmdsize, cmdbufsize, buf, len);
+ ret = dm_encapsulate_buffer ((char *) cmd, cmdsize, cmdbufsize, buf, len);
+ free (cmd);
+
+ return ret;
}
size_t
qcdm_cmd_log_config_get_mask_new (char *buf,
size_t len,
- u_int32_t equip_id)
+ uint32_t equip_id)
{
return qcdm_cmd_log_config_new (buf,
len,
@@ -1767,7 +1773,7 @@ qcdm_cmd_log_config_get_mask_new (char *buf,
}
static int
-check_log_config_respose (const char *buf, size_t len, u_int32_t op)
+check_log_config_respose (const char *buf, size_t len, uint32_t op)
{
DMCmdLogConfigRsp *rsp = (DMCmdLogConfigRsp *) buf;
size_t minlen = 16; /* minimum valid resposne */
@@ -1781,7 +1787,7 @@ check_log_config_respose (const char *buf, size_t len, u_int32_t op)
}
if (rsp->code == DIAG_CMD_LOG_CONFIG) {
- u_int32_t rspop;
+ uint32_t rspop;
if (len < 16) {
/* At least enough for code + op + result + equipid */
@@ -1833,13 +1839,13 @@ check_log_config_respose (const char *buf, size_t len, u_int32_t op)
#define LOG_CODE_SET(mask, code) (mask[code / 8] & (1 << (code % 8)))
static QcdmResult *
-log_config_get_set_result (const char *buf, size_t len, u_int32_t op, int *out_error)
+log_config_get_set_result (const char *buf, size_t len, uint32_t op, int *out_error)
{
QcdmResult *result = NULL;
DMCmdLogConfigRsp *rsp = (DMCmdLogConfigRsp *) buf;
int err;
- u_int32_t num_items;
- u_int32_t equipid;
+ uint32_t num_items;
+ uint32_t equipid;
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -1859,8 +1865,8 @@ log_config_get_set_result (const char *buf, size_t len, u_int32_t op, int *out_e
qcdm_result_add_u32 (result, QCDM_CMD_LOG_CONFIG_MASK_ITEM_NUM_ITEMS, num_items);
if (num_items > 0) {
- u_int32_t i, num_result_items = 0, count = 0;
- u_int16_t *items;
+ uint32_t i, num_result_items = 0, count = 0;
+ uint16_t *items;
/* First pass to find out how many are actually enabled */
for (i = 0; i < num_items; i++) {
@@ -1893,8 +1899,8 @@ qcdm_cmd_log_config_get_mask_result (const char *buf, size_t len, int *out_error
size_t
qcdm_cmd_log_config_set_mask_new (char *buf,
size_t len,
- u_int32_t equip_id,
- u_int16_t items[])
+ uint32_t equip_id,
+ uint16_t items[])
{
return qcdm_cmd_log_config_new (buf,
len,
@@ -1911,12 +1917,12 @@ qcdm_cmd_log_config_set_mask_result (const char *buf, size_t len, int *out_error
qcdmbool
qcmd_cmd_log_config_mask_result_code_set (QcdmResult *result,
- u_int32_t equipid,
- u_int16_t log_code)
+ uint32_t equipid,
+ uint16_t log_code)
{
- const u_int16_t *items = NULL;
+ const uint16_t *items = NULL;
size_t len = 0;
- u_int32_t i, tmp;
+ uint32_t i, tmp;
qcdm_return_val_if_fail (result != NULL, FALSE);
@@ -1941,10 +1947,10 @@ qcmd_cmd_log_config_mask_result_code_set (QcdmResult *result,
static char bcd_chars[] = "0123456789\0\0\0\0\0\0";
static qcdmbool
-imxi_bcd_to_string (u_int8_t bytes[8], size_t len, char *buf, size_t buflen)
+imxi_bcd_to_string (uint8_t bytes[8], size_t len, char *buf, size_t buflen)
{
char *p;
- u_int32_t i;
+ uint32_t i;
if (bytes[0] == 0)
return TRUE;
@@ -2042,8 +2048,8 @@ qcdm_cmd_gsm_subsys_state_info_result (const char *buf, size_t len, int *out_err
QcdmResult *result = NULL;
DMCmdSubsysGsmStateInfoRsp *rsp = (DMCmdSubsysGsmStateInfoRsp *) buf;
char imxi[18];
- u_int32_t mcc = 0, mnc = 0;
- u_int8_t mnc3;
+ uint32_t mcc = 0, mnc = 0;
+ uint8_t mnc3;
qcdm_return_val_if_fail (buf != NULL, NULL);
@@ -2081,7 +2087,7 @@ qcdm_cmd_gsm_subsys_state_info_result (const char *buf, size_t len, int *out_err
qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_MNC, mnc);
qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_LAI_LAC,
- le16toh (*(u_int16_t *)(&rsp->lai[3])));
+ rsp->lai[4] << 8 | rsp->lai[3]);
qcdm_result_add_u32 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CELLID, le16toh (rsp->cellid));
}
@@ -2090,4 +2096,3 @@ qcdm_cmd_gsm_subsys_state_info_result (const char *buf, size_t len, int *out_err
}
/**********************************************************************/
-
diff --git a/libqcdm/src/commands.h b/libqcdm/src/commands.h
index 6f05f1c7..23121ff9 100644
--- a/libqcdm/src/commands.h
+++ b/libqcdm/src/commands.h
@@ -110,7 +110,7 @@ enum {
QCDM_CMD_CONTROL_MODE_RESET = 2,
};
-size_t qcdm_cmd_control_new (char *buf, size_t len, u_int8_t mode);
+size_t qcdm_cmd_control_new (char *buf, size_t len, uint8_t mode);
QcdmResult *qcdm_cmd_control_result (const char *buf,
size_t len,
@@ -236,14 +236,14 @@ QcdmResult *qcdm_cmd_pilot_sets_result (const char *buf,
int *out_error);
qcdmbool qcdm_cmd_pilot_sets_result_get_num (QcdmResult *result,
- u_int32_t set_type,
- u_int32_t *out_num);
+ uint32_t set_type,
+ uint32_t *out_num);
qcdmbool qcdm_cmd_pilot_sets_result_get_pilot (QcdmResult *result,
- u_int32_t set_type,
- u_int32_t num,
- u_int32_t *out_pn_offset,
- u_int32_t *out_ecio,
+ uint32_t set_type,
+ uint32_t num,
+ uint32_t *out_pn_offset,
+ uint32_t *out_ecio,
float *out_db);
/**********************************************************************/
@@ -251,7 +251,7 @@ qcdmbool qcdm_cmd_pilot_sets_result_get_pilot (QcdmResult *result,
#define QCDM_CMD_NV_GET_MDN_ITEM_PROFILE "profile"
#define QCDM_CMD_NV_GET_MDN_ITEM_MDN "mdn"
-size_t qcdm_cmd_nv_get_mdn_new (char *buf, size_t len, u_int8_t profile);
+size_t qcdm_cmd_nv_get_mdn_new (char *buf, size_t len, uint8_t profile);
QcdmResult *qcdm_cmd_nv_get_mdn_result (const char *buf,
size_t len,
@@ -271,7 +271,7 @@ enum {
size_t qcdm_cmd_nv_get_roam_pref_new (char *buf,
size_t len,
- u_int8_t profile);
+ uint8_t profile);
QcdmResult *qcdm_cmd_nv_get_roam_pref_result (const char *buf,
size_t len,
@@ -279,8 +279,8 @@ QcdmResult *qcdm_cmd_nv_get_roam_pref_result (const char *buf,
size_t qcdm_cmd_nv_set_roam_pref_new (char *buf,
size_t len,
- u_int8_t profile,
- u_int8_t roam_pref);
+ uint8_t profile,
+ uint8_t roam_pref);
QcdmResult *qcdm_cmd_nv_set_roam_pref_result (const char *buf,
size_t len,
@@ -311,7 +311,7 @@ enum {
size_t qcdm_cmd_nv_get_mode_pref_new (char *buf,
size_t len,
- u_int8_t profile);
+ uint8_t profile);
QcdmResult *qcdm_cmd_nv_get_mode_pref_result (const char *buf,
size_t len,
@@ -319,8 +319,8 @@ QcdmResult *qcdm_cmd_nv_get_mode_pref_result (const char *buf,
size_t qcdm_cmd_nv_set_mode_pref_new (char *buf,
size_t len,
- u_int8_t profile,
- u_int8_t mode_pref);
+ uint8_t profile,
+ uint8_t mode_pref);
QcdmResult *qcdm_cmd_nv_set_mode_pref_result (const char *buf,
size_t len,
@@ -343,7 +343,7 @@ QcdmResult *qcdm_cmd_nv_get_hybrid_pref_result (const char *buf,
size_t qcdm_cmd_nv_set_hybrid_pref_new (char *buf,
size_t len,
- u_int8_t hybrid_pref);
+ uint8_t hybrid_pref);
QcdmResult *qcdm_cmd_nv_set_hybrid_pref_result (const char *buf,
size_t len,
@@ -366,7 +366,7 @@ QcdmResult *qcdm_cmd_nv_get_ipv6_enabled_result (const char *buf,
size_t qcdm_cmd_nv_set_ipv6_enabled_new (char *buf,
size_t len,
- u_int8_t enabled);
+ uint8_t enabled);
QcdmResult *qcdm_cmd_nv_set_ipv6_enabled_result (const char *buf,
size_t len,
@@ -391,7 +391,7 @@ QcdmResult *qcdm_cmd_nv_get_hdr_rev_pref_result (const char *buf,
size_t qcdm_cmd_nv_set_hdr_rev_pref_new (char *buf,
size_t len,
- u_int8_t rev_pref);
+ uint8_t rev_pref);
QcdmResult *qcdm_cmd_nv_set_hdr_rev_pref_result (const char *buf,
size_t len,
@@ -568,8 +568,8 @@ QcdmResult *qcdm_cmd_hdr_subsys_state_info_result (const char *buf,
size_t qcdm_cmd_ext_logmask_new (char *buf,
size_t len,
- u_int32_t items[], /* terminated by 0 */
- u_int16_t maxlog);
+ uint32_t items[], /* terminated by 0 */
+ uint16_t maxlog);
QcdmResult *qcdm_cmd_ext_logmask_result (const char *buf,
size_t len,
@@ -577,7 +577,7 @@ QcdmResult *qcdm_cmd_ext_logmask_result (const char *buf,
/* Returns TRUE if 'item' is set in the log mask */
qcdmbool qcmd_cmd_ext_logmask_result_get_item (QcdmResult *result,
- u_int16_t item);
+ uint16_t item);
/**********************************************************************/
@@ -593,12 +593,12 @@ QcdmResult *qcdm_cmd_event_report_result (const char *buf,
size_t qcdm_cmd_log_config_get_mask_new (char *buf,
size_t len,
- u_int32_t equip_id);
+ uint32_t equip_id);
size_t qcdm_cmd_log_config_set_mask_new (char *buf,
size_t len,
- u_int32_t equip_id,
- u_int16_t items[]);
+ uint32_t equip_id,
+ uint16_t items[]);
#define QCDM_CMD_LOG_CONFIG_MASK_ITEM_EQUIP_ID "equip-id"
@@ -615,8 +615,8 @@ QcdmResult *qcdm_cmd_log_config_set_mask_result (const char *buf,
int *out_error);
qcdmbool qcmd_cmd_log_config_mask_result_code_set (QcdmResult *result,
- u_int32_t equipid,
- u_int16_t log_code);
+ uint32_t equipid,
+ uint16_t log_code);
/**********************************************************************/
@@ -651,7 +651,7 @@ enum {
size_t qcdm_cmd_nw_subsys_modem_snapshot_cdma_new (char *buf,
size_t len,
- u_int8_t chipset);
+ uint8_t chipset);
QcdmResult *qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf,
size_t len,
@@ -677,7 +677,7 @@ QcdmResult *qcdm_cmd_nw_subsys_modem_snapshot_cdma_result (const char *buf,
size_t qcdm_cmd_nw_subsys_eri_new (char *buf,
size_t len,
- u_int8_t chipset);
+ uint8_t chipset);
QcdmResult *qcdm_cmd_nw_subsys_eri_result (const char *buf,
size_t len,
@@ -692,18 +692,20 @@ QcdmResult *qcdm_cmd_nw_subsys_eri_result (const char *buf,
/* Values for QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE */
enum {
- QCDM_WCDMA_L1_STATE_INIT = 0,
- QCDM_WCDMA_L1_STATE_IDLE = 1,
- QCDM_WCDMA_L1_STATE_FS = 2,
- QCDM_WCDMA_L1_STATE_ACQ = 3,
- QCDM_WCDMA_L1_STATE_BCH = 4,
- QCDM_WCDMA_L1_STATE_PCH = 5,
- QCDM_WCDMA_L1_STATE_FACH = 6,
- QCDM_WCDMA_L1_STATE_DCH = 7,
- QCDM_WCDMA_L1_STATE_DEACTIVATE = 8,
+ QCDM_WCDMA_L1_STATE_IDLE = 0,
+ QCDM_WCDMA_L1_STATE_FS = 1,
+ QCDM_WCDMA_L1_STATE_ACQ = 2,
+ QCDM_WCDMA_L1_STATE_BCH = 3,
+ QCDM_WCDMA_L1_STATE_PCH = 4,
+ QCDM_WCDMA_L1_STATE_FACH = 5,
+ QCDM_WCDMA_L1_STATE_DCH = 6,
+ QCDM_WCDMA_L1_STATE_DEACTIVATE = 7,
+ QCDM_WCDMA_L1_STATE_PCH_SLEEP = 8,
QCDM_WCDMA_L1_STATE_DEEP_SLEEP = 9,
QCDM_WCDMA_L1_STATE_STOPPED = 10,
QCDM_WCDMA_L1_STATE_SUSPENDED = 11,
+ QCDM_WCDMA_L1_STATE_PCH_BPLMN = 12,
+ QCDM_WCDMA_L1_STATE_WAIT_TRM_STOP = 13,
};
/* One of QCDM_WCDMA_L1_STATE_* */
diff --git a/libqcdm/src/dm-commands.h b/libqcdm/src/dm-commands.h
index 2cbd4177..19c2b4bd 100644
--- a/libqcdm/src/dm-commands.h
+++ b/libqcdm/src/dm-commands.h
@@ -55,12 +55,12 @@ enum {
DIAG_CMD_NV_READ = 38, /* Read NV item */
DIAG_CMD_NV_WRITE = 39, /* Write NV item */
DIAG_CMD_CONTROL = 41, /* Mode change request */
- DIAG_CMD_ERR_READ = 42, /* Error record retreival */
+ DIAG_CMD_ERR_READ = 42, /* Error record retrieval */
DIAG_CMD_ERR_CLEAR = 43, /* Error record clear */
DIAG_CMD_SER_RESET = 44, /* Symbol error rate counter reset */
DIAG_CMD_SER_REPORT = 45, /* Symbol error rate counter report */
DIAG_CMD_TEST = 46, /* Run a specified test */
- DIAG_CMD_GET_DIPSW = 47, /* Retreive the current DIP switch setting */
+ DIAG_CMD_GET_DIPSW = 47, /* Retrieve the current DIP switch setting */
DIAG_CMD_SET_DIPSW = 48, /* Write new DIP switch setting */
DIAG_CMD_VOC_PCM_LB = 49, /* Start/Stop Vocoder PCM loopback */
DIAG_CMD_VOC_PKT_LB = 50, /* Start/Stop Vocoder PKT loopback */
@@ -236,15 +236,15 @@ enum {
/* Generic DM command header */
struct DMCmdHeader {
- u_int8_t code;
+ uint8_t code;
} __attribute__ ((packed));
typedef struct DMCmdHeader DMCmdHeader;
/* DIAG_CMD_SUBSYS */
struct DMCmdSubsysHeader {
- u_int8_t code;
- u_int8_t subsys_id;
- u_int16_t subsys_cmd;
+ uint8_t code;
+ uint8_t subsys_id;
+ uint16_t subsys_cmd;
} __attribute__ ((packed));
typedef struct DMCmdSubsysHeader DMCmdSubsysHeader;
@@ -255,82 +255,82 @@ typedef enum {
/* DIAG_CMD_CONTROL */
struct DMCmdControl {
- u_int8_t code;
+ uint8_t code;
/* DMControlMode */
- u_int16_t mode;
+ uint16_t mode;
} __attribute__ ((packed));
typedef struct DMCmdControl DMCmdControl;
/* DIAG_CMD_NV_READ / DIAG_CMD_NV_WRITE */
struct DMCmdNVReadWrite {
- u_int8_t code;
- u_int16_t nv_item;
- u_int8_t data[128];
- u_int16_t status;
+ uint8_t code;
+ uint16_t nv_item;
+ uint8_t data[128];
+ uint16_t status;
} __attribute__ ((packed));
typedef struct DMCmdNVReadWrite DMCmdNVReadWrite;
/* DIAG_CMD_VERSION_INFO */
struct DMCmdVersionInfoRsp {
- u_int8_t code;
+ uint8_t code;
char comp_date[11];
char comp_time[8];
char rel_date[11];
char rel_time[8];
char model[8];
- u_int8_t scm;
- u_int8_t mob_cai_rev;
- u_int8_t mob_model;
- u_int16_t mob_firmware_rev;
- u_int8_t slot_cycle_index;
- u_int8_t msm_ver;
- u_int8_t _unknown;
+ uint8_t scm;
+ uint8_t mob_cai_rev;
+ uint8_t mob_model;
+ uint16_t mob_firmware_rev;
+ uint8_t slot_cycle_index;
+ uint8_t msm_ver;
+ uint8_t _unknown;
} __attribute__ ((packed));
typedef struct DMCmdVersionInfoRsp DMCmdVersionInfoRsp;
/* DIAG_CMD_ESN */
struct DMCmdEsnRsp {
- u_int8_t code;
- u_int8_t esn[4];
+ uint8_t code;
+ uint8_t esn[4];
} __attribute__ ((packed));
typedef struct DMCmdEsnRsp DMCmdEsnRsp;
/* DIAG_CMD_STATUS */
struct DMCmdStatusRsp {
- u_int8_t code;
- u_int8_t _unknown[3];
- u_int8_t esn[4];
- u_int16_t rf_mode;
- u_int8_t min1_analog[4];
- u_int8_t min1_cdma[4];
- u_int8_t min2_analog[2];
- u_int8_t min2_cdma[2];
- u_int8_t _unknown1;
- u_int16_t cdma_rx_state;
- u_int8_t good_frames;
- u_int16_t analog_corrected_frames;
- u_int16_t analog_bad_frames;
- u_int16_t analog_word_syncs;
- u_int16_t entry_reason;
- u_int16_t curr_chan;
- u_int8_t cdma_code_chan;
- u_int16_t pilot_base;
- u_int16_t sid;
- u_int16_t nid;
- u_int16_t analog_locaid;
- u_int16_t analog_rssi;
- u_int8_t analog_power;
+ uint8_t code;
+ uint8_t _unknown[3];
+ uint8_t esn[4];
+ uint16_t rf_mode;
+ uint8_t min1_analog[4];
+ uint8_t min1_cdma[4];
+ uint8_t min2_analog[2];
+ uint8_t min2_cdma[2];
+ uint8_t _unknown1;
+ uint16_t cdma_rx_state;
+ uint8_t good_frames;
+ uint16_t analog_corrected_frames;
+ uint16_t analog_bad_frames;
+ uint16_t analog_word_syncs;
+ uint16_t entry_reason;
+ uint16_t curr_chan;
+ uint8_t cdma_code_chan;
+ uint16_t pilot_base;
+ uint16_t sid;
+ uint16_t nid;
+ uint16_t analog_locaid;
+ uint16_t analog_rssi;
+ uint8_t analog_power;
} __attribute__ ((packed));
typedef struct DMCmdStatusRsp DMCmdStatusRsp;
/* DIAG_CMD_SW_VERSION */
struct DMCmdSwVersionRsp {
- u_int8_t code;
+ uint8_t code;
char version[31];
char comp_date[11];
- u_int8_t _unknown1[2];
+ uint8_t _unknown1[2];
char comp_time[8];
- u_int8_t _unknown2[2];
+ uint8_t _unknown2[2];
} __attribute__ ((packed));
typedef struct DMCmdSwVersionRsp DMCmdSwVersionRsp;
@@ -347,55 +347,55 @@ typedef enum {
/* DIAG_CMD_STATUS_SNAPSHOT */
struct DMCmdStatusSnapshotRsp {
- u_int8_t code;
- u_int8_t esn[4];
- u_int8_t imsi_s1[4];
- u_int8_t imsi_s2[2];
- u_int8_t imsi_s[8];
- u_int8_t imsi_11_12;
- u_int16_t mcc;
- u_int8_t imsi_addr_num;
- u_int16_t sid;
- u_int16_t nid;
- u_int8_t prev;
- u_int8_t prev_in_use;
- u_int8_t mob_prev;
- u_int8_t band_class;
- u_int16_t frequency;
- u_int8_t oper_mode;
- u_int8_t state;
- u_int8_t sub_state;
+ uint8_t code;
+ uint8_t esn[4];
+ uint8_t imsi_s1[4];
+ uint8_t imsi_s2[2];
+ uint8_t imsi_s[8];
+ uint8_t imsi_11_12;
+ uint16_t mcc;
+ uint8_t imsi_addr_num;
+ uint16_t sid;
+ uint16_t nid;
+ uint8_t prev;
+ uint8_t prev_in_use;
+ uint8_t mob_prev;
+ uint8_t band_class;
+ uint16_t frequency;
+ uint8_t oper_mode;
+ uint8_t state;
+ uint8_t sub_state;
} __attribute__ ((packed));
typedef struct DMCmdStatusSnapshotRsp DMCmdStatusSnapshotRsp;
/* DIAG_SUBSYS_CM_STATE_INFO subsys command */
struct DMCmdSubsysCMStateInfoRsp {
DMCmdSubsysHeader header;
- u_int32_t call_state;
- u_int32_t oper_mode;
- u_int32_t system_mode;
- u_int32_t mode_pref;
- u_int32_t band_pref;
- u_int32_t roam_pref;
- u_int32_t srv_domain_pref;
- u_int32_t acq_order_pref;
- u_int32_t hybrid_pref;
- u_int32_t network_sel_mode_pref;
+ uint32_t call_state;
+ uint32_t oper_mode;
+ uint32_t system_mode;
+ uint32_t mode_pref;
+ uint32_t band_pref;
+ uint32_t roam_pref;
+ uint32_t srv_domain_pref;
+ uint32_t acq_order_pref;
+ uint32_t hybrid_pref;
+ uint32_t network_sel_mode_pref;
} __attribute__ ((packed));
typedef struct DMCmdSubsysCMStateInfoRsp DMCmdSubsysCMStateInfoRsp;
/* DIAG_SUBSYS_HDR_STATE_INFO subsys command */
struct DMCmdSubsysHDRStateInfoRsp {
DMCmdSubsysHeader header;
- u_int8_t at_state;
- u_int8_t session_state;
- u_int8_t almp_state;
- u_int8_t init_state;
- u_int8_t idle_state;
- u_int8_t connected_state;
- u_int8_t route_update_state;
- u_int8_t overhead_msg_state;
- u_int8_t hdr_hybrid_mode;
+ uint8_t at_state;
+ uint8_t session_state;
+ uint8_t almp_state;
+ uint8_t init_state;
+ uint8_t idle_state;
+ uint8_t connected_state;
+ uint8_t route_update_state;
+ uint8_t overhead_msg_state;
+ uint8_t hdr_hybrid_mode;
} __attribute__ ((packed));
typedef struct DMCmdSubsysHDRStateInfoRsp DMCmdSubsysHDRStateInfoRsp;
@@ -403,124 +403,124 @@ typedef struct DMCmdSubsysHDRStateInfoRsp DMCmdSubsysHDRStateInfoRsp;
/* DIAG_SUBSYS_ZTE_STATUS subsys command */
struct DMCmdSubsysZteStatusRsp {
DMCmdSubsysHeader header;
- u_int8_t _unknown1[8];
- u_int8_t signal_ind;
- u_int8_t _unknown2;
+ uint8_t _unknown1[8];
+ uint8_t signal_ind;
+ uint8_t _unknown2;
} __attribute__ ((packed));
typedef struct DMCmdSubsysZteStatusRsp DMCmdSubsysZteStatusRsp;
/* DIAG_CMD_PILOT_SETS command */
struct DMCmdPilotSetsSet {
- u_int16_t pn_offset;
- u_int16_t ecio;
+ uint16_t pn_offset;
+ uint16_t ecio;
} __attribute__ ((packed));
typedef struct DMCmdPilotSetsSet DMCmdPilotSetsSet;
struct DMCmdPilotSetsRsp {
- u_int8_t code;
- u_int16_t pilot_inc;
- u_int8_t active_count;
- u_int8_t candidate_count;
- u_int8_t neighbor_count;
+ uint8_t code;
+ uint16_t pilot_inc;
+ uint8_t active_count;
+ uint8_t candidate_count;
+ uint8_t neighbor_count;
DMCmdPilotSetsSet sets[52];
} __attribute__ ((packed));
typedef struct DMCmdPilotSetsRsp DMCmdPilotSetsRsp;
struct DMCmdLog {
- u_int8_t code;
- u_int8_t more;
- u_int16_t len; /* size of packet after this member */
- u_int16_t _unknown2; /* contains same value as len */
- u_int16_t log_code;
- u_int64_t timestamp;
- u_int8_t data[0];
+ uint8_t code;
+ uint8_t more;
+ uint16_t len; /* size of packet after this member */
+ uint16_t _unknown2; /* contains same value as len */
+ uint16_t log_code;
+ uint64_t timestamp;
+ uint8_t data[0];
} __attribute__ ((packed));
typedef struct DMCmdLog DMCmdLog;
struct DMCmdExtLogMask {
- u_int8_t code;
+ uint8_t code;
/* Bit number of highest '1' in 'mask'; set to 0 to get current mask. */
- u_int16_t len;
+ uint16_t len;
/* Bitfield of log messages to receive */
- u_int8_t mask[512];
+ uint8_t mask[512];
} __attribute__ ((packed));
typedef struct DMCmdExtLogMask DMCmdExtLogMask;
struct DMCmdEventReport {
- u_int8_t code;
- u_int8_t on;
+ uint8_t code;
+ uint8_t on;
} __attribute__ ((packed));
typedef struct DMCmdEventReport DMCmdEventReport;
struct DMCmdEventReportRsp {
- u_int8_t code;
- u_int16_t len;
- u_int16_t event_id;
- u_int8_t data[0];
+ uint8_t code;
+ uint16_t len;
+ uint16_t event_id;
+ uint8_t data[0];
} __attribute__ ((packed));
typedef struct DMCmdEventReportRsp DMCmdEventReportRsp;
/* DIAG_SUBSYS_NOVATEL_* subsys command */
struct DMCmdSubsysNwSnapshotReq {
DMCmdSubsysHeader hdr;
- u_int8_t technology; /* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT_TECH_* */
- u_int32_t snapshot_mask;
+ uint8_t technology; /* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT_TECH_* */
+ uint32_t snapshot_mask;
} __attribute__ ((packed));
typedef struct DMCmdSubsysNwSnapshotReq DMCmdSubsysNwSnapshotReq;
/* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT response */
struct DMCmdSubsysNwSnapshotRsp {
DMCmdSubsysHeader hdr;
- u_int8_t response_code;
- u_int32_t bitfield1;
- u_int32_t bitfield2;
- u_int8_t data[100]; /* DMCmdSubsysNwSnapshotCdma */
+ uint8_t response_code;
+ uint32_t bitfield1;
+ uint32_t bitfield2;
+ uint8_t data[100]; /* DMCmdSubsysNwSnapshotCdma */
} __attribute__ ((packed));
typedef struct DMCmdSubsysNwSnapshotRsp DMCmdSubsysNwSnapshotRsp;
struct DMCmdSubsysNwSnapshotCdma {
- u_int32_t rssi;
- u_int32_t battery_level;
- u_int8_t call_info;
- u_int8_t new_sms_ind;
- u_int8_t missed_calls;
- u_int32_t voicemail_ind;
- u_int8_t pkt_call_ctrl_state;
- u_int8_t mip_rrp_err_code;
- u_int8_t cur_packet_zone_id;
- u_int8_t prev;
- u_int8_t band_class;
- u_int8_t eri;
- u_int8_t eri_alert_id;
- u_int32_t cur_call_total_time;
- u_int32_t cur_call_active_time;
- u_int32_t cur_call_tx_ip_bytes;
- u_int32_t cur_call_rx_ip_bytes;
- u_int8_t connection_status;
- u_int16_t dominant_pn;
- u_int8_t wdisable_mask;
- u_int8_t hdr_rev;
+ uint32_t rssi;
+ uint32_t battery_level;
+ uint8_t call_info;
+ uint8_t new_sms_ind;
+ uint8_t missed_calls;
+ uint32_t voicemail_ind;
+ uint8_t pkt_call_ctrl_state;
+ uint8_t mip_rrp_err_code;
+ uint8_t cur_packet_zone_id;
+ uint8_t prev;
+ uint8_t band_class;
+ uint8_t eri;
+ uint8_t eri_alert_id;
+ uint32_t cur_call_total_time;
+ uint32_t cur_call_active_time;
+ uint32_t cur_call_tx_ip_bytes;
+ uint32_t cur_call_rx_ip_bytes;
+ uint8_t connection_status;
+ uint16_t dominant_pn;
+ uint8_t wdisable_mask;
+ uint8_t hdr_rev;
} __attribute__ ((packed));
typedef struct DMCmdSubsysNwSnapshotCdma DMCmdSubsysNwSnapshotCdma;
/* DIAG_SUBSYS_NOVATEL_MODEM_SNAPSHOT response */
struct DMCmdSubsysNwEriRsp {
DMCmdSubsysHeader hdr;
- u_int8_t status;
- u_int16_t error;
- u_int8_t roam;
- u_int8_t eri_header[6];
- u_int8_t eri_call_prompt[38];
+ uint8_t status;
+ uint16_t error;
+ uint8_t roam;
+ uint8_t eri_header[6];
+ uint8_t eri_call_prompt[38];
/* Roaming Indicator */
- u_int8_t indicator_id;
- u_int8_t icon_id;
- u_int8_t icon_mode;
- u_int8_t call_prompt_id; /* Call Guard? */
- u_int8_t alert_id; /* Ringer? */
- u_int8_t encoding_type;
- u_int8_t text_len;
- u_int8_t text[32];
+ uint8_t indicator_id;
+ uint8_t icon_id;
+ uint8_t icon_mode;
+ uint8_t call_prompt_id; /* Call Guard? */
+ uint8_t alert_id; /* Ringer? */
+ uint8_t encoding_type;
+ uint8_t text_len;
+ uint8_t text[32];
} __attribute__ ((packed));
typedef struct DMCmdSubsysNwEriRsp DMCmdSubsysNwEriRsp;
@@ -531,26 +531,26 @@ enum {
};
struct DMCmdLogConfig {
- u_int8_t code;
- u_int8_t pad[3];
- u_int32_t op;
- u_int32_t equipid;
- u_int32_t num_items;
- u_int8_t mask[0];
+ uint8_t code;
+ uint8_t pad[3];
+ uint32_t op;
+ uint32_t equipid;
+ uint32_t num_items;
+ uint8_t mask[0];
} __attribute__ ((packed));
typedef struct DMCmdLogConfig DMCmdLogConfig;
struct DMCmdLogConfigRsp {
- u_int8_t code;
- u_int8_t pad[3];
- u_int32_t op;
- u_int32_t result; /* 0 = success */
- u_int32_t equipid;
+ uint8_t code;
+ uint8_t pad[3];
+ uint32_t op;
+ uint32_t result; /* 0 = success */
+ uint32_t equipid;
union {
- u_int32_t get_range_items[16];
+ uint32_t get_range_items[16];
struct {
- u_int32_t num_items;
- u_int8_t mask[0];
+ uint32_t num_items;
+ uint8_t mask[0];
} get_set_items;
} u;
} __attribute__ ((packed));
@@ -559,51 +559,50 @@ typedef struct DMCmdLogConfigRsp DMCmdLogConfigRsp;
/* DIAG_SUBSYS_WCDMA_CALL_START command */
struct DMCmdSubsysWcdmaCallStart {
DMCmdSubsysHeader hdr;
- u_int8_t number_len;
- u_int8_t number_digits[32];
- u_int8_t amr_rate; /* default to 7 */
+ uint8_t number_len;
+ uint8_t number_digits[32];
+ uint8_t amr_rate; /* default to 7 */
} __attribute__ ((packed));
typedef struct DMCmdSubsysWcdmaCallStart DMCmdSubsysWcdmaCallStart;
/* DIAG_SUBSYS_WCDMA_STATE_INFO response */
struct DMCmdSubsysWcdmaStateInfoRsp {
DMCmdSubsysHeader hdr;
- u_int8_t imei_len;
- u_int8_t imei[8];
- u_int8_t imsi_len;
- u_int8_t imsi[8];
- u_int8_t l1_state;
+ uint8_t imei_len;
+ uint8_t imei[8];
+ uint8_t imsi_len;
+ uint8_t imsi[8];
+ uint8_t l1_state;
} __attribute__ ((packed));
typedef struct DMCmdSubsysWcdmaStateInfoRsp DMCmdSubsysWcdmaStateInfoRsp;
/* DIAG_SUBSYS_GSM_STATE_INFO response */
struct DMCmdSubsysGsmStateInfoRsp {
DMCmdSubsysHeader hdr;
- u_int8_t imei_len;
- u_int8_t imei[8];
- u_int8_t imsi_len;
- u_int8_t imsi[8];
- u_int8_t lai[5];
- u_int16_t cellid;
- u_int8_t cm_call_state;
- u_int8_t cm_opmode;
- u_int8_t cm_sysmode;
+ uint8_t imei_len;
+ uint8_t imei[8];
+ uint8_t imsi_len;
+ uint8_t imsi[8];
+ uint8_t lai[5];
+ uint16_t cellid;
+ uint8_t cm_call_state;
+ uint8_t cm_opmode;
+ uint8_t cm_sysmode;
} __attribute__ ((packed));
typedef struct DMCmdSubsysGsmStateInfoRsp DMCmdSubsysGsmStateInfoRsp;
/* DIAG_CMD_SAMSUNG_IND response */
struct DMCmdSamsungIndRsp {
DMCmdHeader hdr;
- u_int8_t _unknown1; /* always zero */
- u_int8_t _unknown2; /* 0x0c */
- u_int8_t _unknown3[4]; /* always zero */
- u_int8_t _unknown4; /* 0x05 */
- u_int8_t _unknown5; /* always zero */
- u_int8_t _unknown6; /* 0x01 */
- u_int8_t _unknown7; /* always zero */
- u_int8_t signal; /* 0 - 5 */
+ uint8_t _unknown1; /* always zero */
+ uint8_t _unknown2; /* 0x0c */
+ uint8_t _unknown3[4]; /* always zero */
+ uint8_t _unknown4; /* 0x05 */
+ uint8_t _unknown5; /* always zero */
+ uint8_t _unknown6; /* 0x01 */
+ uint8_t _unknown7; /* always zero */
+ uint8_t signal; /* 0 - 5 */
} __attribute__ ((packed));
typedef struct DMCmdSamsungIndRsp DMCmdSamsungIndRsp;
#endif /* LIBQCDM_DM_COMMANDS_H */
-
diff --git a/libqcdm/src/log-items.h b/libqcdm/src/log-items.h
index 6a0a9ab5..4e2f509d 100644
--- a/libqcdm/src/log-items.h
+++ b/libqcdm/src/log-items.h
@@ -18,6 +18,8 @@
#ifndef LIBQCDM_LOG_ITEMS_H
#define LIBQCDM_LOG_ITEMS_H
+#include <stdint.h>
+
enum {
/* CDMA and EVDO items */
DM_LOG_ITEM_CDMA_ACCESS_CHANNEL_MSG = 0x1004,
@@ -37,7 +39,7 @@ enum {
DM_LOG_ITEM_EVDO_REV_POWER_CONTROL = 0x1063,
DM_LOG_ITEM_EVDO_ARQ_EFFECTIVE_RECEIVE_RATE = 0x1066,
DM_LOG_ITEM_EVDO_AIR_LINK_SUMMARY = 0x1068,
- DM_LOG_ITEM_EVDO_POWER = 0x1069
+ DM_LOG_ITEM_EVDO_POWER = 0x1069,
DM_LOG_ITEM_EVDO_FWD_LINK_PACKET_SNAPSHOT = 0x106A,
DM_LOG_ITEM_EVDO_ACCESS_ATTEMPT = 0x106C,
DM_LOG_ITEM_EVDO_REV_ACTIVITY_BITS_BUFFER = 0x106D,
@@ -60,64 +62,114 @@ enum {
/* DM_LOG_ITEM_CDMA_PAGING_CHANNEL_MSG */
struct DMLogItemPagingChannelMsg {
- u_int8_t msg_len; /* size of entire struct including this field */
- u_int8_t msg_type; /* MSG_TYPE as in 3GPP2 C.S0004 Table 3.1.2.3.1.1.2 */
- u_int8_t data[0]; /* Packed message as in 3GPP2 C.S0005 3.7.2.3.2.x */
+ uint8_t msg_len; /* size of entire struct including this field */
+ uint8_t msg_type; /* MSG_TYPE as in 3GPP2 C.S0004 Table 3.1.2.3.1.1.2 */
+ uint8_t data[0]; /* Packed message as in 3GPP2 C.S0005 3.7.2.3.2.x */
} __attribute ((packed));
typedef struct DMLogItemPagingChannelMsg DMLogItemPagingChannelMsg;
/* DM_LOG_ITEM_CDMA_REVERSE_POWER_CONTROL */
struct DMLogItemRPCItem {
- u_int8_t channel_set_mask;
- u_int16_t frame_count;
- u_int8_t len_per_frame;
- u_int16_t dec_history;
- u_int8_t rx_agc_vals;
- u_int8_t tx_power_vals;
- u_int8_t tx_gain_adjust;
+ uint8_t channel_set_mask;
+ uint16_t frame_count;
+ uint8_t len_per_frame;
+ uint16_t dec_history;
+ uint8_t rx_agc_vals;
+ uint8_t tx_power_vals;
+ uint8_t tx_gain_adjust;
} __attribute__ ((packed));
typedef struct DMLogItemRPCItem DMLogItemRPCItem;
struct DMLogItemCdmaReversePowerControl {
- u_int8_t frame_offset;
- u_int8_t band_class;
- u_int16_t rev_chan_rc;
- u_int8_t pilot_gating_rate;
- u_int8_t step_size;
- u_int8_t num_records;
+ uint8_t frame_offset;
+ uint8_t band_class;
+ uint16_t rev_chan_rc;
+ uint8_t pilot_gating_rate;
+ uint8_t step_size;
+ uint8_t num_records;
DMLogItemRPCItem records[];
} __attribute__ ((packed));
typedef struct DMLogItemCdmaReversePowerControl DMLogItemCdmaReversePowerControl;
+/* DM_LOG_ITEM_EVDO_PILOT_SETS_V2 */
+struct DMLogItemEvdoPilotSetsV2Pilot {
+ uint16_t pilot_pn;
+ /* HDR pilot energy doesn't appear to be in the same units as 1x pilot
+ * energy (eg, -0.5 dBm increments). Instead it appears roughly correlated
+ * to RSSI dBm by using this formula empirically derived from simultaneous
+ * AT!RSSI and HDR Pilot Sets V2 results from a Sierra modem:
+ *
+ * RSSI dBm = -110 + (MAX(pilot_energy - 50, 0) / 14)
+ */
+ uint16_t pilot_energy;
+ union {
+ struct {
+ uint16_t mac_index;
+ uint8_t unknown1;
+ uint8_t unknown2;
+ uint16_t window_center;
+ } Active;
+ struct {
+ uint16_t channel_number;
+ uint8_t unknown1;
+ uint8_t unknown2;
+ uint16_t window_center;
+ } Candidate;
+ struct {
+ uint16_t channel_number;
+ uint16_t window_center;
+ uint8_t unknown1; // Offset?
+ uint8_t unknown2; // Age?
+ } Remaining;
+ };
+} __attribute__ ((packed));
+typedef struct DMLogItemEvdoPilotSetsV2Pilot DMLogItemEvdoPilotSetsV2Pilot;
+
+/* DM_LOG_ITEM_EVDO_PILOT_SETS_V2 */
+struct DMLogItemEvdoPilotSetsV2 {
+ uint8_t pn_offset;
+ uint8_t active_count;
+ uint8_t active_window;
+ uint16_t active_channel;
+ uint8_t unknown1;
+ uint8_t candidate_count;
+ uint8_t candidate_window;
+ uint8_t remaining_count;
+ uint8_t remaining_window;
+ uint8_t unknown2;
+
+ DMLogItemEvdoPilotSetsV2Pilot sets[];
+} __attribute__ ((packed));
+typedef struct DMLogItemEvdoPilotSetsV2 DMLogItemEvdoPilotSetsV2;
/* DM_LOG_ITEM_WCDMA_TA_FINGER_INFO */
struct DMLogItemWcdmaTaFingerInfo {
int32_t tx_pos;
int16_t coherent_interval_len;
- u_int8_t non_coherent_interval_len;
- u_int8_t num_paths;
- u_int32_t path_enr;
- int32_t pn_pos_path
+ uint8_t non_coherent_interval_len;
+ uint8_t num_paths;
+ uint32_t path_enr;
+ int32_t pn_pos_path;
int16_t pri_cpich_psc;
- u_int8_t unknown1;
- u_int8_t sec_cpich_ssc;
- u_int8_t finger_channel_code_index;
- u_int8_t finger_index;
+ uint8_t unknown1;
+ uint8_t sec_cpich_ssc;
+ uint8_t finger_channel_code_index;
+ uint8_t finger_index;
} __attribute__ ((packed));
typedef struct DMLogItemWcdmaTaFingerInfo DMLogItemWcdmaTaFingerInfo;
/* DM_LOG_ITEM_WCDMA_AGC_INFO */
struct DMLogItemWcdmaAgcInfo {
- u_int8_t num_samples;
- u_int16_t rx_agc;
- u_int16_t tx_agc;
- u_int16_t rx_agc_adj_pdm;
- u_int16_t tx_agc_adj_pdm;
- u_int16_t max_tx;
+ uint8_t num_samples;
+ uint16_t rx_agc;
+ uint16_t tx_agc;
+ uint16_t rx_agc_adj_pdm;
+ uint16_t tx_agc_adj_pdm;
+ uint16_t max_tx;
/* Bit 4 means tx_agc is valid */
- u_int8_t agc_info;
+ uint8_t agc_info;
} __attribute__ ((packed));
typedef struct DMLogItemWcdmaAgcInfo DMLogItemWcdmaAgcInfo;
@@ -133,38 +185,38 @@ enum {
};
struct DMLogItemWcdmaRrcState {
- u_int8_t rrc_state;
+ uint8_t rrc_state;
} __attribute__ ((packed));
typedef struct DMLogItemWcdmaRrcState DMLogItemWcdmaRrcState;
/* DM_LOG_ITEM_WCDMA_CELL_ID */
struct DMLogItemWcdmaCellId {
- u_int8_t unknown1[8];
- u_int32_t cellid;
- u_int8_t unknown2[4];
+ uint8_t unknown1[8];
+ uint32_t cellid;
+ uint8_t unknown2[4];
} __attribute__ ((packed));
typedef struct DMLogItemWcdmaCellId DMLogItemWcdmaCellId;
/* DM_LOG_ITEM_GSM_BURST_METRICS */
struct DMLogItemGsmBurstMetric {
- u_int32_t fn;
- u_int16_t arfcn;
- u_int32_t rssi;
- u_int16_t power;
- u_int16_t dc_offset_i;
- u_int16_t dc_offset_q;
- u_int16_t freq_offset;
- u_int16_t timing_offset;
- u_int16_t snr;
- u_int8_t gain_state;
+ uint32_t fn;
+ uint16_t arfcn;
+ uint32_t rssi;
+ uint16_t power;
+ uint16_t dc_offset_i;
+ uint16_t dc_offset_q;
+ uint16_t freq_offset;
+ uint16_t timing_offset;
+ uint16_t snr;
+ uint8_t gain_state;
} __attribute__ ((packed));
typedef struct DMLogItemGsmBurstMetric DMLogItemGsmBurstMetric;
struct DMLogItemGsmBurstMetrics {
- u_int8_t channel;
- DMLogItemBurstMetric metrics[4];
+ uint8_t channel;
+ DMLogItemGsmBurstMetric metrics[4];
} __attribute__ ((packed));
typedef struct DMLogItemGsmBurstMetrics DMLogItemGsmBurstMetrics;
@@ -181,12 +233,12 @@ enum {
struct DMLogItemGsmBcchMessage {
/* Band is top 4 bits; lower 12 is ARFCN */
- u_int16_t bcch_arfcn;
- u_int16_t bsic;
- u_int16_t cell_id;
- u_int8_t lai[5];
- u_int8_t cell_selection_prio;
- u_int8_t ncc_permitted;
+ uint16_t bcch_arfcn;
+ uint16_t bsic;
+ uint16_t cell_id;
+ uint8_t lai[5];
+ uint8_t cell_selection_prio;
+ uint8_t ncc_permitted;
} __attribute__ ((packed));
typedef struct DMLogItemGsmBcchMessage DMLogItemGsmBcchMessage;
diff --git a/libqcdm/src/logs.c b/libqcdm/src/logs.c
new file mode 100644
index 00000000..91152c42
--- /dev/null
+++ b/libqcdm/src/logs.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <endian.h>
+
+#include "log-items.h"
+#include "logs.h"
+#include "errors.h"
+#include "dm-commands.h"
+#include "result-private.h"
+#include "utils.h"
+
+
+/**********************************************************************/
+
+static qcdmbool
+check_log_item (const char *buf, size_t len, uint16_t log_code, size_t min_len, int *out_error)
+{
+ DMCmdLog *log_cmd = (DMCmdLog *) buf;
+
+ if (len < sizeof (DMCmdLog)) {
+ qcdm_err (0, "DM log item malformed (must be at least %zu bytes in length)", sizeof (DMCmdLog));
+ if (out_error)
+ *out_error = -QCDM_ERROR_RESPONSE_MALFORMED;
+ return FALSE;
+ }
+
+ if (buf[0] != DIAG_CMD_LOG) {
+ if (out_error)
+ *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED;
+ return FALSE;
+ }
+
+ if (le16toh (log_cmd->log_code) != log_code) {
+ if (out_error)
+ *out_error = -QCDM_ERROR_RESPONSE_UNEXPECTED;
+ return FALSE;
+ }
+
+ if (len < sizeof (DMCmdLog) + min_len) {
+ qcdm_err (0, "DM log item response not long enough (got %zu, expected "
+ "at least %zu).", len, sizeof (DMCmdLog) + min_len);
+ if (out_error)
+ *out_error = -QCDM_ERROR_RESPONSE_BAD_LENGTH;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**********************************************************************/
+
+#define PILOT_SETS_LOG_ACTIVE_SET "active-set"
+#define PILOT_SETS_LOG_CANDIDATE_SET "candidate-set"
+#define PILOT_SETS_LOG_REMAINING_SET "remaining-set"
+
+static const char *
+set_num_to_str (uint32_t num)
+{
+ if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE)
+ return PILOT_SETS_LOG_ACTIVE_SET;
+ if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_CANDIDATE)
+ return PILOT_SETS_LOG_CANDIDATE_SET;
+ if (num == QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_REMAINING)
+ return PILOT_SETS_LOG_REMAINING_SET;
+ return NULL;
+}
+
+QcdmResult *
+qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf, size_t len, int *out_error)
+{
+ QcdmResult *result = NULL;
+ DMLogItemEvdoPilotSetsV2 *pilot_sets;
+ DMCmdLog *log_cmd = (DMCmdLog *) buf;
+ size_t sets_len;
+
+ qcdm_return_val_if_fail (buf != NULL, NULL);
+
+ if (!check_log_item (buf, len, DM_LOG_ITEM_EVDO_PILOT_SETS_V2, sizeof (DMLogItemEvdoPilotSetsV2), out_error))
+ return NULL;
+
+ pilot_sets = (DMLogItemEvdoPilotSetsV2 *) log_cmd->data;
+
+ result = qcdm_result_new ();
+
+ sets_len = pilot_sets->active_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot);
+ if (sets_len > 0) {
+ qcdm_result_add_u8_array (result,
+ PILOT_SETS_LOG_ACTIVE_SET,
+ (const uint8_t *) &pilot_sets->sets[0],
+ sets_len);
+ }
+
+ sets_len = pilot_sets->candidate_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot);
+ if (sets_len > 0) {
+ qcdm_result_add_u8_array (result,
+ PILOT_SETS_LOG_CANDIDATE_SET,
+ (const uint8_t *) &pilot_sets->sets[pilot_sets->active_count],
+ sets_len);
+ }
+
+ sets_len = pilot_sets->remaining_count * sizeof (DMLogItemEvdoPilotSetsV2Pilot);
+ if (sets_len > 0) {
+ qcdm_result_add_u8_array (result,
+ PILOT_SETS_LOG_REMAINING_SET,
+ (const uint8_t *) &pilot_sets->sets[pilot_sets->active_count + pilot_sets->candidate_count],
+ sets_len);
+ }
+
+ return result;
+
+}
+
+qcdmbool
+qcdm_log_item_evdo_pilot_sets_v2_get_num (QcdmResult *result,
+ uint32_t set_type,
+ uint32_t *out_num)
+{
+ const char *set_name;
+ const uint8_t *array = NULL;
+ size_t array_len = 0;
+
+ qcdm_return_val_if_fail (result != NULL, FALSE);
+
+ set_name = set_num_to_str (set_type);
+ qcdm_return_val_if_fail (set_name != NULL, FALSE);
+
+ if (qcdm_result_get_u8_array (result, set_name, &array, &array_len))
+ return FALSE;
+
+ *out_num = array_len / sizeof (DMLogItemEvdoPilotSetsV2Pilot);
+ return TRUE;
+}
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+qcdmbool
+qcdm_log_item_evdo_pilot_sets_v2_get_pilot (QcdmResult *result,
+ uint32_t set_type,
+ uint32_t num,
+ uint32_t *out_pilot_pn,
+ uint32_t *out_pilot_energy,
+ int32_t *out_rssi_dbm)
+{
+ const char *set_name;
+ DMLogItemEvdoPilotSetsV2Pilot *pilot;
+ const uint8_t *array = NULL;
+ size_t array_len = 0;
+
+ qcdm_return_val_if_fail (result != NULL, FALSE);
+
+ set_name = set_num_to_str (set_type);
+ qcdm_return_val_if_fail (set_name != NULL, FALSE);
+
+ if (qcdm_result_get_u8_array (result, set_name, &array, &array_len))
+ return FALSE;
+
+ qcdm_return_val_if_fail (num < array_len / sizeof (DMLogItemEvdoPilotSetsV2Pilot), FALSE);
+
+ pilot = (DMLogItemEvdoPilotSetsV2Pilot *) &array[num * sizeof (DMLogItemEvdoPilotSetsV2Pilot)];
+ *out_pilot_pn = le16toh (pilot->pilot_pn);
+ *out_pilot_energy = le16toh (pilot->pilot_energy);
+ *out_rssi_dbm = (int32_t) (-110.0 + (MAX (le16toh (pilot->pilot_energy) - 50, 0) / 14.0));
+ return TRUE;
+}
+
+/**********************************************************************/
diff --git a/libqcdm/src/logs.h b/libqcdm/src/logs.h
new file mode 100644
index 00000000..cebd030b
--- /dev/null
+++ b/libqcdm/src/logs.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This program is free software: you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBQCDM_LOGS_H
+#define LIBQCDM_LOGS_H
+
+#include "utils.h"
+#include "result.h"
+
+/**********************************************************************/
+
+enum {
+ QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_UNKNOWN = 0,
+ QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE = 1,
+ QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_CANDIDATE = 2,
+ QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_REMAINING = 3,
+};
+
+QcdmResult *qcdm_log_item_evdo_pilot_sets_v2_new (const char *buf,
+ size_t len,
+ int *out_error);
+
+qcdmbool qcdm_log_item_evdo_pilot_sets_v2_get_num (QcdmResult *result,
+ uint32_t set_type,
+ uint32_t *out_num);
+
+qcdmbool qcdm_log_item_evdo_pilot_sets_v2_get_pilot (QcdmResult *result,
+ uint32_t set_type,
+ uint32_t num,
+ uint32_t *out_pilot_pn,
+ uint32_t *out_pilot_energy,
+ int32_t *out_rssi_dbm);
+
+/**********************************************************************/
+
+#endif /* LIBQCDM_LOGS_H */
diff --git a/libqcdm/src/meson.build b/libqcdm/src/meson.build
new file mode 100644
index 00000000..32f6e9f5
--- /dev/null
+++ b/libqcdm/src/meson.build
@@ -0,0 +1,29 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+sources = files(
+ 'com.c',
+ 'commands.c',
+ 'errors.c',
+ 'logs.c',
+ 'result.c',
+ 'utils.c',
+)
+
+libqcdm = static_library(
+ 'qcdm',
+ sources: sources,
+ include_directories: top_inc,
+)
+
+libqcdm_dep = declare_dependency(
+ include_directories: '.',
+ link_with: libqcdm,
+)
+
+# FIXME: Created following autotools but actually unused
+libqcdm_test = static_library(
+ 'qcdm-test',
+ sources: 'utils.c',
+ include_directories: top_inc,
+)
diff --git a/libqcdm/src/nv-items.h b/libqcdm/src/nv-items.h
index dae7f9df..bd7889a8 100644
--- a/libqcdm/src/nv-items.h
+++ b/libqcdm/src/nv-items.h
@@ -64,15 +64,15 @@ enum {
/* DIAG_NV_MODE_PREF */
struct DMNVItemModePref {
- u_int8_t profile;
- u_int8_t mode_pref;
+ uint8_t profile;
+ uint8_t mode_pref;
} __attribute__ ((packed));
typedef struct DMNVItemModePref DMNVItemModePref;
/* DIAG_NV_DIR_NUMBER */
struct DMNVItemMdn {
- u_int8_t profile;
- u_int8_t mdn[10];
+ uint8_t profile;
+ uint8_t mdn[10];
} __attribute__ ((packed));
typedef struct DMNVItemMdn DMNVItemMdn;
@@ -85,8 +85,8 @@ enum {
/* DIAG_NV_ROAM_PREF */
struct DMNVItemRoamPref {
- u_int8_t profile;
- u_int8_t roam_pref;
+ uint8_t profile;
+ uint8_t roam_pref;
} __attribute__ ((packed));
typedef struct DMNVItemRoamPref DMNVItemRoamPref;
@@ -99,7 +99,7 @@ enum {
/* DIAG_NV_HDR_REV_PREF */
struct DMNVItemHdrRevPref {
- u_int8_t rev_pref;
+ uint8_t rev_pref;
} __attribute__ ((packed));
typedef struct DMNVItemHdrRevPref DMNVItemHdrRevPref;
@@ -111,7 +111,7 @@ enum {
/* DIAG_NV_HYBRID_PREF */
struct DMNVItemHybridPref {
- u_int8_t hybrid_pref;
+ uint8_t hybrid_pref;
} __attribute__ ((packed));
typedef struct DMNVItemHybridPref DMNVItemHybridPref;
@@ -123,9 +123,8 @@ enum {
/* DIAG_NV_IPV6_ENABLED */
struct DMNVItemIPv6Enabled {
- u_int8_t enabled;
+ uint8_t enabled;
} __attribute__ ((packed));
typedef struct DMNVItemIPv6Enabled DMNVItemIPv6Enabled;
#endif /* LIBQCDM_NV_ITEMS_H */
-
diff --git a/libqcdm/src/result-private.h b/libqcdm/src/result-private.h
index 382411c1..fae39034 100644
--- a/libqcdm/src/result-private.h
+++ b/libqcdm/src/result-private.h
@@ -28,26 +28,25 @@ void qcdm_result_add_string (QcdmResult *result,
void qcdm_result_add_u8 (QcdmResult *result,
const char *key,
- u_int8_t num);
+ uint8_t num);
void qcdm_result_add_u8_array (QcdmResult *result,
const char *key,
- const u_int8_t *array,
+ const uint8_t *array,
size_t array_len);
int qcdm_result_get_u8_array (QcdmResult *result,
const char *key,
- const u_int8_t **out_val,
+ const uint8_t **out_val,
size_t *out_len);
void qcdm_result_add_u16_array (QcdmResult *result,
const char *key,
- const u_int16_t *array,
+ const uint16_t *array,
size_t array_len);
void qcdm_result_add_u32 (QcdmResult *result,
const char *key,
- u_int32_t num);
+ uint32_t num);
#endif /* LIBQCDM_RESULT_PRIVATE_H */
-
diff --git a/libqcdm/src/result.c b/libqcdm/src/result.c
index d508fb76..65f6c6a5 100644
--- a/libqcdm/src/result.c
+++ b/libqcdm/src/result.c
@@ -37,15 +37,15 @@ typedef enum {
struct Val {
char *key;
- u_int8_t type;
+ uint8_t type;
union {
char *s;
- u_int8_t u8;
- u_int32_t u32;
- u_int8_t *u8_array;
- u_int16_t *u16_array;
+ uint8_t u8;
+ uint32_t u32;
+ uint8_t *u8_array;
+ uint16_t *u16_array;
} u;
- u_int32_t array_len;
+ uint32_t array_len;
Val *next;
};
@@ -84,7 +84,7 @@ val_new_string (const char *key, const char *value)
}
static Val *
-val_new_u8 (const char *key, u_int8_t u)
+val_new_u8 (const char *key, uint8_t u)
{
Val *v;
@@ -102,7 +102,7 @@ val_new_u8 (const char *key, u_int8_t u)
}
static Val *
-val_new_u8_array (const char *key, const u_int8_t *array, size_t array_len)
+val_new_u8_array (const char *key, const uint8_t *array, size_t array_len)
{
Val *v;
@@ -129,7 +129,7 @@ val_new_u8_array (const char *key, const u_int8_t *array, size_t array_len)
}
static Val *
-val_new_u32 (const char *key, u_int32_t u)
+val_new_u32 (const char *key, uint32_t u)
{
Val *v;
@@ -147,7 +147,7 @@ val_new_u32 (const char *key, u_int32_t u)
}
static Val *
-val_new_u16_array (const char *key, const u_int16_t *array, size_t array_len)
+val_new_u16_array (const char *key, const uint16_t *array, size_t array_len)
{
Val *v;
size_t sz;
@@ -163,7 +163,7 @@ val_new_u16_array (const char *key, const u_int16_t *array, size_t array_len)
v->key = strdup (key);
v->type = VAL_TYPE_U16_ARRAY;
- sz = sizeof (u_int16_t) * array_len;
+ sz = sizeof (uint16_t) * array_len;
v->u.u16_array = malloc (sz);
if (v->u.u16_array == NULL) {
val_free (v);
@@ -178,7 +178,7 @@ val_new_u16_array (const char *key, const u_int16_t *array, size_t array_len)
/*********************************************************/
struct QcdmResult {
- u_int32_t refcount;
+ uint32_t refcount;
Val *first;
};
@@ -289,7 +289,7 @@ qcdm_result_get_string (QcdmResult *r,
void
qcdm_result_add_u8 (QcdmResult *r,
const char *key,
- u_int8_t num)
+ uint8_t num)
{
Val *v;
@@ -306,7 +306,7 @@ qcdm_result_add_u8 (QcdmResult *r,
int
qcdm_result_get_u8 (QcdmResult *r,
const char *key,
- u_int8_t *out_val)
+ uint8_t *out_val)
{
Val *v;
@@ -326,7 +326,7 @@ qcdm_result_get_u8 (QcdmResult *r,
void
qcdm_result_add_u8_array (QcdmResult *r,
const char *key,
- const u_int8_t *array,
+ const uint8_t *array,
size_t array_len)
{
Val *v;
@@ -335,7 +335,6 @@ qcdm_result_add_u8_array (QcdmResult *r,
qcdm_return_if_fail (r->refcount > 0);
qcdm_return_if_fail (key != NULL);
qcdm_return_if_fail (array != NULL);
- qcdm_return_if_fail (array_len >= 0);
v = val_new_u8_array (key, array, array_len);
qcdm_return_if_fail (v != NULL);
@@ -346,7 +345,7 @@ qcdm_result_add_u8_array (QcdmResult *r,
int
qcdm_result_get_u8_array (QcdmResult *r,
const char *key,
- const u_int8_t **out_val,
+ const uint8_t **out_val,
size_t *out_len)
{
Val *v;
@@ -369,7 +368,7 @@ qcdm_result_get_u8_array (QcdmResult *r,
void
qcdm_result_add_u32 (QcdmResult *r,
const char *key,
- u_int32_t num)
+ uint32_t num)
{
Val *v;
@@ -386,7 +385,7 @@ qcdm_result_add_u32 (QcdmResult *r,
int
qcdm_result_get_u32 (QcdmResult *r,
const char *key,
- u_int32_t *out_val)
+ uint32_t *out_val)
{
Val *v;
@@ -406,7 +405,7 @@ qcdm_result_get_u32 (QcdmResult *r,
void
qcdm_result_add_u16_array (QcdmResult *r,
const char *key,
- const u_int16_t *array,
+ const uint16_t *array,
size_t array_len)
{
Val *v;
@@ -415,7 +414,6 @@ qcdm_result_add_u16_array (QcdmResult *r,
qcdm_return_if_fail (r->refcount > 0);
qcdm_return_if_fail (key != NULL);
qcdm_return_if_fail (array != NULL);
- qcdm_return_if_fail (array_len >= 0);
v = val_new_u16_array (key, array, array_len);
qcdm_return_if_fail (v != NULL);
@@ -426,7 +424,7 @@ qcdm_result_add_u16_array (QcdmResult *r,
int
qcdm_result_get_u16_array (QcdmResult *r,
const char *key,
- const u_int16_t **out_val,
+ const uint16_t **out_val,
size_t *out_len)
{
Val *v;
@@ -445,4 +443,3 @@ qcdm_result_get_u16_array (QcdmResult *r,
*out_len = v->array_len;
return 0;
}
-
diff --git a/libqcdm/src/result.h b/libqcdm/src/result.h
index a71c0bf5..76dc63fe 100644
--- a/libqcdm/src/result.h
+++ b/libqcdm/src/result.h
@@ -18,7 +18,7 @@
#ifndef LIBQCDM_RESULT_H
#define LIBQCDM_RESULT_H
-#include <sys/types.h>
+#include <stdint.h>
typedef struct QcdmResult QcdmResult;
@@ -28,15 +28,15 @@ int qcdm_result_get_string (QcdmResult *r,
int qcdm_result_get_u8 (QcdmResult *r,
const char *key,
- u_int8_t *out_val);
+ uint8_t *out_val);
int qcdm_result_get_u32 (QcdmResult *r,
const char *key,
- u_int32_t *out_val);
+ uint32_t *out_val);
int qcdm_result_get_u16_array (QcdmResult *result,
const char *key,
- const u_int16_t **out_val,
+ const uint16_t **out_val,
size_t *out_len);
QcdmResult *qcdm_result_ref (QcdmResult *r);
@@ -44,4 +44,3 @@ QcdmResult *qcdm_result_ref (QcdmResult *r);
void qcdm_result_unref (QcdmResult *r);
#endif /* LIBQCDM_RESULT_H */
-
diff --git a/libqcdm/src/utils.c b/libqcdm/src/utils.c
index c3624795..4c1ce31e 100644
--- a/libqcdm/src/utils.c
+++ b/libqcdm/src/utils.c
@@ -33,7 +33,7 @@
*/
/* Table of CRCs for each possible byte, with a generator polynomial of 0x8408 */
-static const u_int16_t crc_table[256] = {
+static const uint16_t crc_table[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
@@ -69,10 +69,10 @@ static const u_int16_t crc_table[256] = {
};
/* Calculate the CRC for a buffer using a seed of 0xffff */
-u_int16_t
+uint16_t
dm_crc16 (const char *buffer, size_t len)
{
- u_int16_t crc = 0xffff;
+ uint16_t crc = 0xffff;
while (len--)
crc = crc_table[(crc ^ *buffer++) & 0xff] ^ (crc >> 8);
@@ -102,7 +102,7 @@ dm_escape (const char *inbuf,
/* Since escaping potentially doubles the # of bytes, short-circuit the
* length check if destination buffer is clearly large enough. Note the
- *
+ *
*/
if (outbuf_len <= inbuf_len << 1) {
size_t outbuf_required = inbuf_len + 1; /* +1 for the trailing control char */
@@ -187,7 +187,7 @@ dm_encapsulate_buffer (char *inbuf,
char *outbuf,
size_t outbuf_len)
{
- u_int16_t crc;
+ uint16_t crc;
size_t escaped_len;
qcdm_return_val_if_fail (inbuf != NULL, 0);
@@ -242,7 +242,7 @@ dm_decapsulate_buffer (const char *inbuf,
{
qcdmbool escaping = FALSE;
size_t i, pkt_len = 0, unesc_len;
- u_int16_t crc, pkt_crc;
+ uint16_t crc, pkt_crc;
qcdm_return_val_if_fail (inbuf != NULL, FALSE);
qcdm_return_val_if_fail (outbuf != NULL, FALSE);
@@ -309,4 +309,3 @@ dm_decapsulate_buffer (const char *inbuf,
*out_decap_len = unesc_len - 2; /* decap_len should not include the CRC */
return TRUE;
}
-
diff --git a/libqcdm/src/utils.h b/libqcdm/src/utils.h
index 457c2e99..86b65b08 100644
--- a/libqcdm/src/utils.h
+++ b/libqcdm/src/utils.h
@@ -19,20 +19,20 @@
#define LIBQCDM_UTILS_H
#include <config.h>
-#include <sys/types.h>
+#include <stdint.h>
-typedef u_int8_t qcdmbool;
+typedef uint8_t qcdmbool;
#ifndef TRUE
-#define TRUE ((u_int8_t) 1)
+#define TRUE ((uint8_t) 1)
#endif
#ifndef FALSE
-#define FALSE ((u_int8_t) 0)
+#define FALSE ((uint8_t) 0)
#endif
#define DIAG_CONTROL_CHAR 0x7E
#define DIAG_TRAILER_LEN 3
-u_int16_t dm_crc16 (const char *buffer, size_t len);
+uint16_t dm_crc16 (const char *buffer, size_t len);
size_t dm_escape (const char *inbuf,
size_t inbuf_len,
@@ -60,4 +60,3 @@ qcdmbool dm_decapsulate_buffer (const char *inbuf,
qcdmbool *out_need_more);
#endif /* LIBQCDM_UTILS_H */
-
diff --git a/libqcdm/tests/Makefile.am b/libqcdm/tests/Makefile.am
index 32599068..961c5e1d 100644
--- a/libqcdm/tests/Makefile.am
+++ b/libqcdm/tests/Makefile.am
@@ -1,5 +1,15 @@
include $(top_srcdir)/gtester.make
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(CODE_COVERAGE_LDFLAGS) \
+ $(NULL)
+
noinst_PROGRAMS = test-qcdm modepref ipv6pref reset
TEST_PROGS += test-qcdm
diff --git a/libqcdm/tests/ipv6pref.c b/libqcdm/tests/ipv6pref.c
index ba777d88..f5bd71c4 100644
--- a/libqcdm/tests/ipv6pref.c
+++ b/libqcdm/tests/ipv6pref.c
@@ -36,10 +36,10 @@ static int debug = 0;
static void
print_buf (const char *detail, const char *buf, size_t len)
{
- int i = 0, z;
+ unsigned int i, z;
qcdmbool newline = FALSE;
char tmp[500];
- u_int32_t flen;
+ uint32_t flen;
flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len);
fprintf (stdout, "%s", tmp);
@@ -120,7 +120,7 @@ qcdm_wait_reply (int fd, char *buf, size_t len)
struct timeval timeout = { 1, 0 };
char readbuf[1024];
ssize_t bytes_read;
- int total = 0, retries = 0;
+ unsigned int total = 0, retries = 0;
size_t decap_len = 0;
FD_ZERO (&in);
@@ -173,7 +173,7 @@ qcdm_wait_reply (int fd, char *buf, size_t len)
}
static int
-qcdm_set_ipv6_enabled (int fd, u_int8_t ipv6pref)
+qcdm_set_ipv6_enabled (int fd, uint8_t ipv6pref)
{
int err;
char buf[512];
@@ -216,7 +216,7 @@ qcdm_get_ipv6_enabled (int fd)
size_t len;
QcdmResult *result;
size_t reply_len;
- u_int8_t mode;
+ uint8_t mode;
len = qcdm_cmd_nv_get_ipv6_enabled_new (buf, sizeof (buf));
assert (len);
@@ -285,7 +285,7 @@ main (int argc, char *argv[])
}
if (debug)
- putenv ("QCDM_DEBUG=1");
+ putenv ((char *)"QCDM_DEBUG=1");
fd = com_setup (dmport);
if (fd < 0)
@@ -313,4 +313,3 @@ main (int argc, char *argv[])
return 0;
}
-
diff --git a/libqcdm/tests/meson.build b/libqcdm/tests/meson.build
new file mode 100644
index 00000000..174f91a8
--- /dev/null
+++ b/libqcdm/tests/meson.build
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+test_units = [
+ ['ipv6pref', files('ipv6pref.c'), false],
+ ['modepref', files('modepref.c'), false],
+ ['reset', files('reset.c'), false],
+]
+
+sources = files(
+ 'test-qcdm.c',
+ 'test-qcdm-com.c',
+ 'test-qcdm-crc.c',
+ 'test-qcdm-escaping.c',
+ 'test-qcdm-result.c',
+ 'test-qcdm-utils.c',
+)
+
+test_units += [['test-qcdm', sources, true]]
+
+deps = [
+ glib_deps,
+ libqcdm_dep,
+]
+
+foreach test_unit: test_units
+ exe = executable(
+ test_unit[0],
+ test_unit[1],
+ include_directories: top_inc,
+ dependencies: deps,
+ )
+
+ if test_unit[2]
+ test(test_unit[0], exe)
+ endif
+endforeach
diff --git a/libqcdm/tests/modepref.c b/libqcdm/tests/modepref.c
index 43272411..f8855161 100644
--- a/libqcdm/tests/modepref.c
+++ b/libqcdm/tests/modepref.c
@@ -36,10 +36,10 @@ static int debug = 0;
static void
print_buf (const char *detail, const char *buf, size_t len)
{
- int i = 0, z;
+ unsigned int i, z;
qcdmbool newline = FALSE;
char tmp[500];
- u_int32_t flen;
+ uint32_t flen;
flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len);
fprintf (stdout, "%s", tmp);
@@ -120,7 +120,7 @@ qcdm_wait_reply (int fd, char *buf, size_t len)
struct timeval timeout = { 1, 0 };
char readbuf[1024];
ssize_t bytes_read;
- int total = 0, retries = 0;
+ unsigned int total = 0, retries = 0;
size_t decap_len = 0;
FD_ZERO (&in);
@@ -173,7 +173,7 @@ qcdm_wait_reply (int fd, char *buf, size_t len)
}
static int
-qcdm_set_mode_pref (int fd, u_int8_t modepref)
+qcdm_set_mode_pref (int fd, uint8_t modepref)
{
int err;
char buf[512];
@@ -217,7 +217,7 @@ qcdm_get_mode_pref (int fd)
QcdmResult *result;
size_t reply_len;
const char *smode = NULL;
- u_int8_t mode = 0;
+ uint8_t mode = 0;
len = qcdm_cmd_nv_get_mode_pref_new (buf, sizeof (buf), 0);
assert (len);
@@ -291,7 +291,7 @@ qcdm_get_mode_pref (int fd)
}
static int
-qcdm_set_hdr_pref (int fd, u_int8_t hdrpref)
+qcdm_set_hdr_pref (int fd, uint8_t hdrpref)
{
int err;
char buf[512];
@@ -334,7 +334,7 @@ qcdm_get_hdr_pref (int fd)
size_t len;
QcdmResult *result = NULL;
size_t reply_len;
- u_int8_t pref;
+ uint8_t pref;
const char *spref = NULL;
len = qcdm_cmd_nv_get_hdr_rev_pref_new (buf, sizeof (buf));
@@ -385,7 +385,7 @@ error:
}
static int
-qcdm_set_mode (int fd, u_int8_t mode)
+qcdm_set_mode (int fd, uint8_t mode)
{
int err;
char buf[512];
@@ -435,8 +435,8 @@ usage (const char *prog)
static qcdmbool
parse_mode (const char *s,
- u_int8_t *out_mode,
- u_int8_t *out_hdrpref,
+ uint8_t *out_mode,
+ uint8_t *out_hdrpref,
qcdmbool *out_set_evdo)
{
if (strcasecmp (s, "lte") == 0) {
@@ -506,8 +506,8 @@ parse_mode (const char *s,
int
main (int argc, char *argv[])
{
- u_int8_t mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO;
- u_int8_t hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD;
+ uint8_t mode = QCDM_CMD_NV_MODE_PREF_ITEM_MODE_PREF_AUTO;
+ uint8_t hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD;
const char *dmport = argv[1];
const char *smode = argv[2];
const char *msg;
@@ -536,7 +536,7 @@ main (int argc, char *argv[])
}
if (debug)
- putenv ("QCDM_DEBUG=1");
+ putenv ((char *)"QCDM_DEBUG=1");
fd = com_setup (dmport);
if (fd < 0)
@@ -570,4 +570,3 @@ main (int argc, char *argv[])
return 0;
}
-
diff --git a/libqcdm/tests/reset.c b/libqcdm/tests/reset.c
index 10fd1bc0..079ee386 100644
--- a/libqcdm/tests/reset.c
+++ b/libqcdm/tests/reset.c
@@ -37,10 +37,10 @@ static int debug = 0;
static void
print_buf (const char *detail, const char *buf, size_t len)
{
- int i = 0, z;
+ unsigned int i, z;
qcdmbool newline = FALSE;
char tmp[500];
- u_int32_t flen;
+ uint32_t flen;
flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len);
fprintf (stdout, "%s", tmp);
@@ -121,7 +121,7 @@ qcdm_wait_reply (int fd, char *buf, size_t len)
struct timeval timeout = { 1, 0 };
char readbuf[1024];
ssize_t bytes_read;
- int total = 0, retries = 0;
+ unsigned int total = 0, retries = 0;
size_t decap_len = 0;
FD_ZERO (&in);
@@ -174,7 +174,7 @@ qcdm_wait_reply (int fd, char *buf, size_t len)
}
static int
-qcdm_set_mode (int fd, u_int8_t mode)
+qcdm_set_mode (int fd, uint8_t mode)
{
int err;
char buf[512];
@@ -235,7 +235,7 @@ main (int argc, char *argv[])
debug = 1;
if (debug)
- putenv ("QCDM_DEBUG=1");
+ putenv ((char *)"QCDM_DEBUG=1");
fd = com_setup (dmport);
if (fd < 0)
diff --git a/libqcdm/tests/test-qcdm-com.c b/libqcdm/tests/test-qcdm-com.c
index 86152846..81679157 100644
--- a/libqcdm/tests/test-qcdm-com.c
+++ b/libqcdm/tests/test-qcdm-com.c
@@ -156,7 +156,7 @@ status_snapshot_state_to_string (guint8 state)
}
static const char *
-cm_call_state_to_string (u_int32_t state)
+cm_call_state_to_string (uint32_t state)
{
switch (state) {
case QCDM_CMD_CM_SUBSYS_STATE_INFO_CALL_STATE_IDLE:
@@ -176,7 +176,7 @@ cm_call_state_to_string (u_int32_t state)
}
static const char *
-cm_system_mode_to_string (u_int32_t mode)
+cm_system_mode_to_string (uint32_t mode)
{
switch (mode) {
case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE:
@@ -266,7 +266,7 @@ test_com_teardown (gpointer user_data)
static void
print_buf (const char *detail, const char *buf, gsize len)
{
- int i = 0;
+ unsigned int i = 0;
gboolean newline = FALSE;
g_print ("%s (%zu) ", detail, len);
@@ -320,7 +320,7 @@ wait_reply (TestComData *d, char *buf, gsize len)
struct timeval timeout = { 1, 0 };
char readbuf[1024];
ssize_t bytes_read;
- int total = 0, retries = 0;
+ unsigned int total = 0, retries = 0;
gsize decap_len = 0;
FD_ZERO (&in);
@@ -1082,7 +1082,7 @@ test_com_pilot_sets (void *f, void *data)
&ecio,
&db);
g_message (" %d: PN offset %d", i, pn_offset);
- g_message (" EC/IO %d (%.1f dB)", ecio, db);
+ g_message (" EC/IO %d (%.1lf dB)", ecio, (double)db);
}
num = 0;
@@ -1480,7 +1480,7 @@ test_com_ext_logmask (void *f, void *data)
gint len;
QcdmResult *result;
gsize reply_len;
- u_int32_t items[] = { 0x002C, 0x002E, 0 };
+ uint32_t items[] = { 0x002C, 0x002E, 0 };
guint32 maxlog = 0;
/* First get # of items the device supports */
@@ -1505,7 +1505,7 @@ test_com_ext_logmask (void *f, void *data)
qcdm_result_unref (result);
/* Now enable some log items */
- len = qcdm_cmd_ext_logmask_new (buf, sizeof (buf), items, (u_int16_t) maxlog);
+ len = qcdm_cmd_ext_logmask_new (buf, sizeof (buf), items, (uint16_t) maxlog);
/* Send the command */
success = send_command (d, buf, len);
@@ -1581,11 +1581,11 @@ test_com_log_config (void *f, void *data)
gint len;
QcdmResult *result;
gsize reply_len;
- u_int32_t num_items = 0;
- const u_int16_t *items = NULL, *reread_items;
+ uint32_t num_items = 0;
+ const uint16_t *items = NULL, *reread_items;
size_t items_len = 0, reread_len;
- u_int32_t i;
- u_int16_t test_items[] = { 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x102C, 0x102E, 0 };
+ uint32_t i;
+ uint16_t test_items[] = { 0x1004, 0x1005, 0x1006, 0x1007, 0x1008, 0x102C, 0x102E, 0 };
/* Get existing mask for CDMA/EVDO equip ID */
len = qcdm_cmd_log_config_get_mask_new (buf, sizeof (buf), 0x01);
@@ -1861,9 +1861,6 @@ test_com_wcdma_subsys_state_info (void *f, void *data)
str = "unknown";
qcdm_result_get_u8 (result, QCDM_CMD_WCDMA_SUBSYS_STATE_INFO_ITEM_L1_STATE, &num8);
switch (num8) {
- case QCDM_WCDMA_L1_STATE_INIT:
- str = "Init";
- break;
case QCDM_WCDMA_L1_STATE_IDLE:
str = "Idle";
break;
@@ -1888,6 +1885,9 @@ test_com_wcdma_subsys_state_info (void *f, void *data)
case QCDM_WCDMA_L1_STATE_DEACTIVATE:
str = "Deactivated";
break;
+ case QCDM_WCDMA_L1_STATE_PCH_SLEEP:
+ str = "PCH Sleep";
+ break;
case QCDM_WCDMA_L1_STATE_DEEP_SLEEP:
str = "Deep Sleep";
break;
@@ -1897,6 +1897,12 @@ test_com_wcdma_subsys_state_info (void *f, void *data)
case QCDM_WCDMA_L1_STATE_SUSPENDED:
str = "Suspended";
break;
+ case QCDM_WCDMA_L1_STATE_PCH_BPLMN:
+ str = "PCH BPLMN";
+ break;
+ case QCDM_WCDMA_L1_STATE_WAIT_TRM_STOP:
+ str = "Wait TRM Stop";
+ break;
default:
break;
}
@@ -1916,8 +1922,8 @@ test_com_gsm_subsys_state_info (void *f, void *data)
QcdmResult *result;
gsize reply_len;
const char *str;
- u_int32_t num;
- u_int8_t u8;
+ uint32_t num;
+ uint8_t u8;
len = qcdm_cmd_gsm_subsys_state_info_new (buf, sizeof (buf));
g_assert (len == 7);
@@ -1978,4 +1984,3 @@ test_com_gsm_subsys_state_info (void *f, void *data)
qcdm_result_unref (result);
}
-
diff --git a/libqcdm/tests/test-qcdm-result.c b/libqcdm/tests/test-qcdm-result.c
index 1c9c74f0..f0e097af 100644
--- a/libqcdm/tests/test-qcdm-result.c
+++ b/libqcdm/tests/test-qcdm-result.c
@@ -76,8 +76,8 @@ test_result_uint8 (void *f, void *data)
void
test_result_uint8_array (void *f, void *data)
{
- u_int8_t array[] = { 0, 1, 255, 32, 128, 127 };
- const u_int8_t *tmp = NULL;
+ uint8_t array[] = { 0, 1, 255, 32, 128, 127 };
+ const uint8_t *tmp = NULL;
size_t tmp_len = 0;
QcdmResult *result;
@@ -90,4 +90,3 @@ test_result_uint8_array (void *f, void *data)
qcdm_result_unref (result);
}
-
diff --git a/libqcdm/tests/test-qcdm.c b/libqcdm/tests/test-qcdm.c
index 2a21522d..59ca3a86 100644
--- a/libqcdm/tests/test-qcdm.c
+++ b/libqcdm/tests/test-qcdm.c
@@ -35,15 +35,15 @@ typedef GTestFixtureFunc TCFunc;
static TestData *
test_data_new (const char *port)
{
- TestData *d;
+ TestData *d;
- d = g_malloc0 (sizeof (TestData));
- g_assert (d);
+ d = g_malloc0 (sizeof (TestData));
+ g_assert (d);
if (port)
d->com_data = test_com_setup (port);
- return d;
+ return d;
}
static void
@@ -52,7 +52,7 @@ test_data_free (TestData *d)
if (d->com_data)
test_com_teardown (d->com_data);
- g_free (d);
+ g_free (d);
}
int main (int argc, char **argv)
@@ -118,9 +118,9 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_com_gsm_subsys_state_info, data->com_data));
}
- result = g_test_run ();
+ result = g_test_run ();
- test_data_free (data);
+ test_data_free (data);
- return result;
+ return result;
}
diff --git a/libwmc/AUTHORS b/libwmc/AUTHORS
deleted file mode 100644
index e69de29b..00000000
--- a/libwmc/AUTHORS
+++ /dev/null
diff --git a/libwmc/ChangeLog b/libwmc/ChangeLog
deleted file mode 100644
index e69de29b..00000000
--- a/libwmc/ChangeLog
+++ /dev/null
diff --git a/libwmc/Makefile.am b/libwmc/Makefile.am
deleted file mode 100644
index 77f28d7a..00000000
--- a/libwmc/Makefile.am
+++ /dev/null
@@ -1,2 +0,0 @@
-SUBDIRS=src tests
-
diff --git a/libwmc/NEWS b/libwmc/NEWS
deleted file mode 100644
index e69de29b..00000000
--- a/libwmc/NEWS
+++ /dev/null
diff --git a/libwmc/README b/libwmc/README
deleted file mode 100644
index e69de29b..00000000
--- a/libwmc/README
+++ /dev/null
diff --git a/libwmc/autogen.sh b/libwmc/autogen.sh
deleted file mode 100755
index df15fcec..00000000
--- a/libwmc/autogen.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/bin/sh
-# Run this to generate all the initial makefiles, etc.
-# NOTE
-# This autogen.sh is only used when building libwmc separately from ModemManager
-
-srcdir=`dirname $0`
-test -z "$srcdir" && srcdir=.
-REQUIRED_AUTOMAKE_VERSION=1.7
-PKG_NAME=libwmc
-
-(test -f $srcdir/configure.ac \
- && test -f $srcdir/src/com.c) || {
- echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
- echo " top-level $PKG_NAME directory"
- exit 1
-}
-
-(cd $srcdir;
- autoreconf --install --symlink &&
- autoreconf &&
- ./configure --enable-maintainer-mode $@
-)
diff --git a/libwmc/configure.ac b/libwmc/configure.ac
deleted file mode 100644
index 4d2b90c6..00000000
--- a/libwmc/configure.ac
+++ /dev/null
@@ -1,48 +0,0 @@
-# NOTE
-# This configure.ac is only used when building libqcdm separately from
-# ModemManager.
-#
-
-AC_PREREQ(2.52)
-
-AC_INIT(libwmc, 0.1, dcbw@redhat.com, libwmc)
-AM_INIT_AUTOMAKE([1.9 subdir-objects tar-ustar no-dist-gzip dist-bzip2])
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-AM_MAINTAINER_MODE
-
-AC_CONFIG_MACRO_DIR([m4])
-
-AC_CONFIG_HEADERS(config.h)
-
-dnl Define _GNU_SOURCE for various things like strcasestr()
-AC_GNU_SOURCE
-
-dnl Required programs
-AC_PROG_CC
-AM_PROG_CC_C_O
-AC_PROG_INSTALL
-AC_PROG_LIBTOOL
-
-dnl
-dnl Tests
-dnl
-AC_ARG_WITH(tests, AS_HELP_STRING([--with-tests], [Build libwmc tests]))
-AM_CONDITIONAL(WITH_TESTS, test "x$with_tests" = "xyes")
-case $with_tests in
- yes)
- PKG_CHECK_MODULES(MM, glib-2.0 >= 2.20)
- AC_SUBST(MM_CFLAGS)
- AC_SUBST(MM_LIBS)
- ;;
- *) ;;
-esac
-
-AM_CONDITIONAL(WMC_STANDALONE, test "yes" = "yes")
-
-AC_CONFIG_FILES([
-Makefile
-src/Makefile
-tests/Makefile
-])
-AC_OUTPUT
-
diff --git a/libwmc/src/Makefile.am b/libwmc/src/Makefile.am
deleted file mode 100644
index 46205016..00000000
--- a/libwmc/src/Makefile.am
+++ /dev/null
@@ -1,23 +0,0 @@
-noinst_LTLIBRARIES = libwmc.la
-
-
-libwmc_la_CPPFLAGS = \
- $(MM_CFLAGS)
-
-libwmc_la_SOURCES = \
- protocol.h \
- result-private.h \
- errors.c \
- errors.h \
- utils.c \
- utils.h \
- result.c \
- result.h \
- com.c \
- com.h \
- commands.c \
- commands.h
-
-libwmc_la_LIBADD = \
- $(MM_LIBS)
-
diff --git a/libwmc/src/com.c b/libwmc/src/com.c
deleted file mode 100644
index 2f4d3b3c..00000000
--- a/libwmc/src/com.c
+++ /dev/null
@@ -1,56 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <errno.h>
-#include <termios.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include "com.h"
-#include "errors.h"
-
-int
-wmc_port_setup (int fd)
-{
- struct termios stbuf;
-
- errno = 0;
- memset (&stbuf, 0, sizeof (stbuf));
- if (tcgetattr (fd, &stbuf) != 0) {
- wmc_err (0, "tcgetattr() error: %d", errno);
- return -WMC_ERROR_SERIAL_CONFIG_FAILED;
- }
-
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | CLOCAL | PARENB);
- stbuf.c_iflag &= ~(HUPCL | IUTF8 | IUCLC | ISTRIP | IXON | IXOFF | IXANY | ICRNL);
- stbuf.c_oflag &= ~(OPOST | OCRNL | ONLCR | OLCUC | ONLRET);
- stbuf.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOK | ECHONL);
- stbuf.c_lflag &= ~(NOFLSH | XCASE | TOSTOP | ECHOPRT | ECHOCTL | ECHOKE);
- stbuf.c_cc[VMIN] = 1;
- stbuf.c_cc[VTIME] = 0;
- stbuf.c_cc[VEOF] = 1;
- stbuf.c_cflag |= (B115200 | CS8 | CREAD | 0 | 0); /* No parity, 1 stop bit */
-
- errno = 0;
- if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
- wmc_err (0, "tcgetattr() error: %d", errno);
- return -WMC_ERROR_SERIAL_CONFIG_FAILED;
- }
-
- return 0;
-}
-
diff --git a/libwmc/src/com.h b/libwmc/src/com.h
deleted file mode 100644
index 219a0169..00000000
--- a/libwmc/src/com.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_COM_H
-#define LIBWMC_COM_H
-
-int wmc_port_setup (int fd);
-
-#endif /* LIBWMC_COM_H */
diff --git a/libwmc/src/commands.c b/libwmc/src/commands.c
deleted file mode 100644
index d3567e89..00000000
--- a/libwmc/src/commands.c
+++ /dev/null
@@ -1,467 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <time.h>
-
-#include "commands.h"
-#include "errors.h"
-#include "result-private.h"
-#include "utils.h"
-#include "protocol.h"
-
-
-/**********************************************************************/
-
-static int
-check_command (const char *buf, size_t len, u_int8_t cmd, size_t min_len)
-{
- if (len < 1) {
- wmc_err (0, "Zero-length response");
- return -WMC_ERROR_RESPONSE_BAD_LENGTH;
- }
-
- if ((u_int8_t) buf[0] != WMC_CMD_MARKER) {
- wmc_err (0, "Missing WMC command marker (expected 0x%02X, got 0x%02X)",
- WMC_CMD_MARKER, (u_int8_t) buf[0]);
- return -WMC_ERROR_RESPONSE_UNEXPECTED;
- }
-
- if ((u_int8_t) buf[1] != cmd) {
- wmc_err (0, "Unexpected WMC command response (expected 0x%02X, got 0x%02X)",
- (u_int8_t) cmd, (u_int8_t) buf[1]);
- return -WMC_ERROR_RESPONSE_UNEXPECTED;
- }
-
- if (len < min_len) {
- wmc_err (0, "WMC command %d response not long enough (got %zu, expected "
- "at least %zu).", cmd, len, min_len);
- return -WMC_ERROR_RESPONSE_BAD_LENGTH;
- }
-
- return 0;
-}
-
-/**********************************************************************/
-
-/**
- * wmc_cmd_init_new:
- * @buf: buffer in which to store constructed command
- * @buflen: size of @buf
- * @wmc2: if %TRUE add additional data that later-model devices (UML290) want
- *
- * Returns: size of the constructed command on success, or 0 on failure
- */
-size_t
-wmc_cmd_init_new (char *buf, size_t buflen, int wmc2)
-{
- wmc_return_val_if_fail (buf != NULL, 0);
-
- if (wmc2) {
- WmcCmdInit2 *cmd = (WmcCmdInit2 *) buf;
- time_t now;
- struct tm *tm;
-
- wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
-
- now = time (NULL);
- tm = localtime (&now);
-
- memset (cmd, 0, sizeof (*cmd));
- cmd->hdr.marker = WMC_CMD_MARKER;
- cmd->hdr.cmd = WMC_CMD_INIT;
- cmd->year = htole16 (tm->tm_year + 1900);
- cmd->month = tm->tm_mon + 1;
- cmd->day = htobe16 (tm->tm_mday);
- cmd->hours = htobe16 (tm->tm_hour);
- cmd->minutes = htobe16 (tm->tm_min);
- cmd->seconds = htobe16 (tm->tm_sec);
- return sizeof (*cmd);
- } else {
- WmcCmdHeader *cmd = (WmcCmdHeader *) buf;
-
- wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
-
- memset (cmd, 0, sizeof (*cmd));
- cmd->marker = WMC_CMD_MARKER;
- cmd->cmd = WMC_CMD_INIT;
- return sizeof (*cmd);
- }
-}
-
-WmcResult *
-wmc_cmd_init_result (const char *buf, size_t buflen, int wmc2)
-{
- wmc_return_val_if_fail (buf != NULL, NULL);
-
- if (wmc2) {
- if (check_command (buf, buflen, WMC_CMD_INIT, sizeof (WmcCmdInit2Rsp)) < 0)
- return NULL;
- } else {
- if (check_command (buf, buflen, WMC_CMD_INIT, sizeof (WmcCmdHeader)) < 0)
- return NULL;
- }
-
- return wmc_result_new ();
-}
-
-/**********************************************************************/
-
-size_t
-wmc_cmd_device_info_new (char *buf, size_t buflen)
-{
- WmcCmdHeader *cmd = (WmcCmdHeader *) buf;
-
- wmc_return_val_if_fail (buf != NULL, 0);
- wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
-
- memset (cmd, 0, sizeof (*cmd));
- cmd->marker = WMC_CMD_MARKER;
- cmd->cmd = WMC_CMD_DEVICE_INFO;
- return sizeof (*cmd);
-}
-
-WmcResult *
-wmc_cmd_device_info_result (const char *buf, size_t buflen)
-{
- WmcResult *r = NULL;
- WmcCmdDeviceInfoRsp *rsp = (WmcCmdDeviceInfoRsp *) buf;
- WmcCmdDeviceInfo2Rsp *rsp2 = (WmcCmdDeviceInfo2Rsp *) buf;
- WmcCmdDeviceInfo3Rsp *rsp3 = (WmcCmdDeviceInfo3Rsp *) buf;
- char tmp[65];
-
- wmc_return_val_if_fail (buf != NULL, NULL);
-
- if (check_command (buf, buflen, WMC_CMD_DEVICE_INFO, sizeof (WmcCmdDeviceInfo3Rsp)) < 0) {
- rsp3 = NULL;
- if (check_command (buf, buflen, WMC_CMD_DEVICE_INFO, sizeof (WmcCmdDeviceInfo2Rsp)) < 0) {
- rsp2 = NULL;
- if (check_command (buf, buflen, WMC_CMD_DEVICE_INFO, sizeof (WmcCmdDeviceInfoRsp)) < 0)
- return NULL;
- }
- }
-
- r = wmc_result_new ();
-
- /* Manf */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp->manf) <= sizeof (tmp));
- memcpy (tmp, rsp->manf, sizeof (rsp->manf));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_MANUFACTURER, tmp);
-
- /* Model */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp->model) <= sizeof (tmp));
- memcpy (tmp, rsp->model, sizeof (rsp->model));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_MODEL, tmp);
-
- /* Firmware revision */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp->fwrev) <= sizeof (tmp));
- memcpy (tmp, rsp->fwrev, sizeof (rsp->fwrev));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_FW_REVISION, tmp);
-
- /* Hardware revision */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp->hwrev) <= sizeof (tmp));
- memcpy (tmp, rsp->hwrev, sizeof (rsp->hwrev));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_HW_REVISION, tmp);
-
- /* MIN */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp->min) <= sizeof (tmp));
- memcpy (tmp, rsp->min, sizeof (rsp->min));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_CDMA_MIN, tmp);
-
- wmc_result_add_u32 (r, WMC_CMD_DEVICE_INFO_ITEM_HOME_SID, le16toh (rsp->home_sid));
- wmc_result_add_u32 (r, WMC_CMD_DEVICE_INFO_ITEM_PRL_VERSION, le16toh (rsp->prlver));
- wmc_result_add_u32 (r, WMC_CMD_DEVICE_INFO_ITEM_ERI_VERSION, le16toh (rsp->eriver));
-
- if (rsp2) {
- /* MEID */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp2->meid) <= sizeof (tmp));
- memcpy (tmp, rsp2->meid, sizeof (rsp2->meid));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_MEID, tmp);
-
- /* IMEI */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp2->imei) <= sizeof (tmp));
- memcpy (tmp, rsp2->imei, sizeof (rsp2->imei));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_IMEI, tmp);
-
- /* IMSI */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp2->iccid) <= sizeof (tmp));
- memcpy (tmp, rsp2->iccid, sizeof (rsp2->iccid));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_ICCID, tmp);
- }
-
- if (rsp3) {
- /* MCC */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp3->mcc) <= sizeof (tmp));
- memcpy (tmp, rsp3->mcc, sizeof (rsp3->mcc));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_MCC, tmp);
-
- /* MNC */
- memset (tmp, 0, sizeof (tmp));
- wmc_assert (sizeof (rsp3->mnc) <= sizeof (tmp));
- memcpy (tmp, rsp3->mnc, sizeof (rsp3->mnc));
- wmc_result_add_string (r, WMC_CMD_DEVICE_INFO_ITEM_MNC, tmp);
- }
-
- return r;
-}
-
-/**********************************************************************/
-
-size_t
-wmc_cmd_network_info_new (char *buf, size_t buflen)
-{
- WmcCmdHeader *cmd = (WmcCmdHeader *) buf;
-
- wmc_return_val_if_fail (buf != NULL, 0);
- wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
-
- memset (cmd, 0, sizeof (*cmd));
- cmd->marker = WMC_CMD_MARKER;
- cmd->cmd = WMC_CMD_NET_INFO;
- return sizeof (*cmd);
-}
-
-static wmcbool
-is_gsm_service (u_int8_t service)
-{
- return (service == WMC_SERVICE_GSM || service == WMC_SERVICE_GPRS || service == WMC_SERVICE_EDGE);
-}
-
-static wmcbool
-is_umts_service (u_int8_t service)
-{
- return (service == WMC_SERVICE_UMTS || service == WMC_SERVICE_HSDPA
- || service == WMC_SERVICE_HSUPA || service == WMC_SERVICE_HSPA);
-}
-
-static wmcbool
-is_cdma_service (u_int8_t service)
-{
- return (service == WMC_SERVICE_IS95A || service == WMC_SERVICE_IS95B || service == WMC_SERVICE_1XRTT);
-}
-
-static wmcbool
-is_evdo_service (u_int8_t service)
-{
- return (service == WMC_SERVICE_EVDO_0 || service == WMC_SERVICE_EVDO_A || service == WMC_SERVICE_EVDO_A_EHRPD);
-}
-
-static wmcbool
-is_lte_service (u_int8_t service)
-{
- return (service == WMC_SERVICE_LTE);
-}
-
-static u_int8_t
-sanitize_dbm (u_int8_t in_dbm, u_int8_t service)
-{
- u_int8_t cutoff;
-
- /* 0x6A (-106 dBm) = no signal for GSM/GPRS/EDGE */
- /* 0x7D (-125 dBm) = no signal for everything else */
- cutoff = is_gsm_service (service) ? 0x6A : 0x7D;
-
- return in_dbm >= cutoff ? 0 : in_dbm;
-}
-
-
-WmcResult *
-wmc_cmd_network_info_result (const char *buf, size_t buflen)
-{
- WmcResult *r = NULL;
- WmcCmdNetworkInfoRsp *rsp = (WmcCmdNetworkInfoRsp *) buf;
- WmcCmdNetworkInfo2Rsp *rsp2 = (WmcCmdNetworkInfo2Rsp *) buf;
- WmcCmdNetworkInfo3Rsp *rsp3 = (WmcCmdNetworkInfo3Rsp *) buf;
- char tmp[65];
- int err;
- u_int32_t mccmnc = 0, mcc, mnc;
-
- wmc_return_val_if_fail (buf != NULL, NULL);
-
- err = check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfo3Rsp));
- if (err != WMC_SUCCESS) {
- if (err != -WMC_ERROR_RESPONSE_BAD_LENGTH)
- return NULL;
- rsp3 = NULL;
-
- err = check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfo2Rsp));
- if (err != WMC_SUCCESS) {
- if (err != -WMC_ERROR_RESPONSE_BAD_LENGTH)
- return NULL;
- rsp2 = NULL;
-
- err = check_command (buf, buflen, WMC_CMD_NET_INFO, sizeof (WmcCmdNetworkInfoRsp));
- if (err != WMC_SUCCESS)
- return NULL;
- }
- }
-
- r = wmc_result_new ();
-
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_SERVICE, rsp->service);
-
- if (rsp2) {
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_2G_DBM, sanitize_dbm (rsp2->two_g_dbm, rsp->service));
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_3G_DBM, sanitize_dbm (rsp2->three_g_dbm, WMC_SERVICE_NONE));
-
- memset (tmp, 0, sizeof (tmp));
- if ( (is_cdma_service (rsp->service) && sanitize_dbm (rsp2->two_g_dbm, rsp->service))
- || (is_evdo_service (rsp->service) && sanitize_dbm (rsp2->three_g_dbm, rsp->service))) {
- /* CDMA2000 operator name */
- wmc_assert (sizeof (rsp2->cdma_opname) <= sizeof (tmp));
- memcpy (tmp, rsp2->cdma_opname, sizeof (rsp2->cdma_opname));
- wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
- } else {
- if ( (is_gsm_service (rsp->service) && sanitize_dbm (rsp2->two_g_dbm, rsp->service))
- || (is_umts_service (rsp->service) && sanitize_dbm (rsp2->three_g_dbm, rsp->service))) {
- /* GSM/UMTS operator name */
- wmc_assert (sizeof (rsp2->tgpp_opname) <= sizeof (tmp));
- memcpy (tmp, rsp2->tgpp_opname, sizeof (rsp2->tgpp_opname));
- wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
- }
- }
-
- /* MCC/MNC */
- mccmnc = le32toh (rsp2->mcc_mnc);
- if (mccmnc < 100000)
- mccmnc *= 10; /* account for possible 2-digit MNC */
- mcc = mccmnc / 1000;
- mnc = mccmnc - (mcc * 1000);
-
- if (mcc > 100) {
- memset (tmp, 0, sizeof (tmp));
- snprintf (tmp, sizeof (tmp), "%u", mccmnc / 1000);
- wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_MCC, tmp);
-
- memset (tmp, 0, sizeof (tmp));
- snprintf (tmp, sizeof (tmp), "%03u", mnc);
- wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_MNC, tmp);
- }
- } else {
- /* old format */
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_2G_DBM, sanitize_dbm (rsp->two_g_dbm, rsp->service));
- }
-
- if (rsp3) {
- wmc_result_add_u8 (r, WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM, sanitize_dbm (rsp3->lte_dbm, WMC_SERVICE_NONE));
-
- memset (tmp, 0, sizeof (tmp));
- if (is_lte_service (rsp->service) && sanitize_dbm (rsp3->lte_dbm, rsp->service)) {
- /* LTE operator name */
- wmc_assert (sizeof (rsp2->tgpp_opname) <= sizeof (tmp));
- memcpy (tmp, rsp2->tgpp_opname, sizeof (rsp2->tgpp_opname));
- wmc_result_add_string (r, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, tmp);
- }
- }
-
- return r;
-}
-
-/**********************************************************************/
-
-size_t
-wmc_cmd_get_global_mode_new (char *buf, size_t buflen)
-{
- WmcCmdGetGlobalMode *cmd = (WmcCmdGetGlobalMode *) buf;
-
- wmc_return_val_if_fail (buf != NULL, 0);
- wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
-
- memset (cmd, 0, sizeof (*cmd));
- cmd->hdr.marker = WMC_CMD_MARKER;
- cmd->hdr.cmd = WMC_CMD_GET_GLOBAL_MODE;
- return sizeof (*cmd);
-}
-
-WmcResult *
-wmc_cmd_get_global_mode_result (const char *buf, size_t buflen)
-{
- WmcResult *r = NULL;
- WmcCmdGetGlobalModeRsp *rsp = (WmcCmdGetGlobalModeRsp *) buf;
-
- wmc_return_val_if_fail (buf != NULL, NULL);
-
- if (check_command (buf, buflen, WMC_CMD_GET_GLOBAL_MODE, sizeof (WmcCmdGetGlobalModeRsp)) < 0)
- return NULL;
-
- r = wmc_result_new ();
- wmc_result_add_u8 (r, WMC_CMD_GET_GLOBAL_MODE_ITEM_MODE, rsp->mode);
- return r;
-}
-
-/**********************************************************************/
-
-static wmcbool
-validate_mode (u_int8_t mode)
-{
- switch (mode) {
- case WMC_NETWORK_MODE_AUTO_CDMA:
- case WMC_NETWORK_MODE_CDMA_ONLY:
- case WMC_NETWORK_MODE_EVDO_ONLY:
- case WMC_NETWORK_MODE_AUTO_GSM:
- case WMC_NETWORK_MODE_GPRS_ONLY:
- case WMC_NETWORK_MODE_UMTS_ONLY:
- case WMC_NETWORK_MODE_AUTO:
- case WMC_NETWORK_MODE_LTE_ONLY:
- return TRUE;
- default:
- break;
- }
- return FALSE;
-}
-
-size_t
-wmc_cmd_set_global_mode_new (char *buf, size_t buflen, u_int8_t mode)
-{
- WmcCmdSetGlobalMode *cmd = (WmcCmdSetGlobalMode *) buf;
-
- wmc_return_val_if_fail (buf != NULL, 0);
- wmc_return_val_if_fail (buflen >= sizeof (*cmd), 0);
- wmc_return_val_if_fail (validate_mode (mode) == TRUE, 0);
-
- memset (cmd, 0, sizeof (*cmd));
- cmd->hdr.marker = WMC_CMD_MARKER;
- cmd->hdr.cmd = WMC_CMD_SET_GLOBAL_MODE;
- cmd->_unknown1 = 0x01;
- cmd->mode = mode;
- cmd->_unknown2 = 0x05;
- cmd->_unknown3 = 0x00;
- return sizeof (*cmd);
-}
-
-WmcResult *
-wmc_cmd_set_global_mode_result (const char *buf, size_t buflen)
-{
- wmc_return_val_if_fail (buf != NULL, NULL);
-
- if (check_command (buf, buflen, WMC_CMD_SET_GLOBAL_MODE, sizeof (WmcCmdGetGlobalModeRsp)) < 0)
- return NULL;
-
- return wmc_result_new ();
-}
-
-/**********************************************************************/
-
diff --git a/libwmc/src/commands.h b/libwmc/src/commands.h
deleted file mode 100644
index 3e5a483d..00000000
--- a/libwmc/src/commands.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_COMMANDS_H
-#define LIBWMC_COMMANDS_H
-
-#include "result.h"
-
-/**********************************************************************/
-
-/* Generic enums */
-
-/**********************************************************************/
-
-size_t wmc_cmd_init_new (char *buf, size_t buflen, int wmc2);
-
-WmcResult * wmc_cmd_init_result (const char *buf, size_t len, int wmc2);
-
-/**********************************************************************/
-
-#define WMC_CMD_DEVICE_INFO_ITEM_MANUFACTURER "manufacturer"
-#define WMC_CMD_DEVICE_INFO_ITEM_MODEL "model"
-#define WMC_CMD_DEVICE_INFO_ITEM_FW_REVISION "firmware-revision"
-#define WMC_CMD_DEVICE_INFO_ITEM_HW_REVISION "hardware-revision"
-#define WMC_CMD_DEVICE_INFO_ITEM_CDMA_MIN "cdma-min"
-#define WMC_CMD_DEVICE_INFO_ITEM_HOME_SID "home-sid"
-#define WMC_CMD_DEVICE_INFO_ITEM_PRL_VERSION "prl-version"
-#define WMC_CMD_DEVICE_INFO_ITEM_ERI_VERSION "eri-version"
-#define WMC_CMD_DEVICE_INFO_ITEM_MEID "meid"
-#define WMC_CMD_DEVICE_INFO_ITEM_IMEI "imei"
-#define WMC_CMD_DEVICE_INFO_ITEM_ICCID "iccid"
-#define WMC_CMD_DEVICE_INFO_ITEM_MCC "mcc"
-#define WMC_CMD_DEVICE_INFO_ITEM_MNC "mnc"
-
-size_t wmc_cmd_device_info_new (char *buf, size_t buflen);
-
-WmcResult * wmc_cmd_device_info_result (const char *buf, size_t len);
-
-/**********************************************************************/
-
-enum {
- WMC_NETWORK_SERVICE_NONE = 0,
- WMC_NETWORK_SERVICE_AMPS = 1,
- WMC_NETWORK_SERVICE_IS95A = 2,
- WMC_NETWORK_SERVICE_IS95B = 3,
- WMC_NETWORK_SERVICE_GSM = 4,
- WMC_NETWORK_SERVICE_GPRS = 5,
- WMC_NETWORK_SERVICE_1XRTT = 6,
- WMC_NETWORK_SERVICE_EVDO_0 = 7,
- WMC_NETWORK_SERVICE_UMTS = 8,
- WMC_NETWORK_SERVICE_EVDO_A = 9,
- WMC_NETWORK_SERVICE_EDGE = 10,
- WMC_NETWORK_SERVICE_HSDPA = 11,
- WMC_NETWORK_SERVICE_HSUPA = 12,
- WMC_NETWORK_SERVICE_HSPA = 13,
- WMC_NETWORK_SERVICE_LTE = 14,
- WMC_NETWORK_SERVICE_EVDO_A_EHRPD = 15
-};
-
-/* One of WMC_NETWORK_SERVICE_* */
-#define WMC_CMD_NETWORK_INFO_ITEM_SERVICE "service"
-
-#define WMC_CMD_NETWORK_INFO_ITEM_2G_DBM "2g-dbm"
-#define WMC_CMD_NETWORK_INFO_ITEM_3G_DBM "3g-dbm"
-#define WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM "lte-dbm"
-#define WMC_CMD_NETWORK_INFO_ITEM_OPNAME "opname"
-#define WMC_CMD_NETWORK_INFO_ITEM_MCC "mcc"
-#define WMC_CMD_NETWORK_INFO_ITEM_MNC "mnc"
-
-size_t wmc_cmd_network_info_new (char *buf, size_t buflen);
-
-WmcResult * wmc_cmd_network_info_result (const char *buf, size_t len);
-
-/**********************************************************************/
-
-enum {
- WMC_NETWORK_MODE_AUTO_CDMA = 0x00,
- WMC_NETWORK_MODE_CDMA_ONLY = 0x01,
- WMC_NETWORK_MODE_EVDO_ONLY = 0x02,
- WMC_NETWORK_MODE_AUTO_GSM = 0x0A,
- WMC_NETWORK_MODE_GPRS_ONLY = 0x0B,
- WMC_NETWORK_MODE_UMTS_ONLY = 0x0C,
- WMC_NETWORK_MODE_AUTO = 0x14,
- WMC_NETWORK_MODE_LTE_ONLY = 0x1E,
-};
-
-/* One of WMC_NETWORK_MODE_* */
-#define WMC_CMD_GET_GLOBAL_MODE_ITEM_MODE "mode"
-
-size_t wmc_cmd_get_global_mode_new (char *buf, size_t buflen);
-
-WmcResult * wmc_cmd_get_global_mode_result (const char *buf, size_t len);
-
-/**********************************************************************/
-
-size_t wmc_cmd_set_global_mode_new (char *buf, size_t buflen, u_int8_t mode);
-
-WmcResult * wmc_cmd_set_global_mode_result (const char *buf, size_t len);
-
-/**********************************************************************/
-
-#endif /* LIBWMC_COMMANDS_H */
diff --git a/libwmc/src/errors.c b/libwmc/src/errors.c
deleted file mode 100644
index 0403b229..00000000
--- a/libwmc/src/errors.c
+++ /dev/null
@@ -1,58 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "errors.h"
-#include <stdlib.h>
-#include <string.h>
-
-void
-_wmc_log (const char *file,
- int line,
- const char *func,
- int level,
- int domain,
- const char *format,
- ...)
-{
- va_list args;
- char *message = NULL;
- int n;
- const char *prefix = "info";
-
- wmc_return_if_fail (format != NULL);
- wmc_return_if_fail (format[0] != '\0');
-
- /* level & domain ignored for now */
-
- if (getenv ("WMC_DEBUG") == NULL)
- return;
-
- va_start (args, format);
- n = vasprintf (&message, format, args);
- va_end (args);
-
- if (level & LOGL_ERR)
- prefix = "err";
- else if (level & LOGL_DEBUG)
- prefix = "dbg";
-
- if (n >= 0) {
- fprintf (stderr, "<%s> [%s:%u] %s(): %s\n", prefix, file, line, func, message);
- free (message);
- }
-}
-
diff --git a/libwmc/src/errors.h b/libwmc/src/errors.h
deleted file mode 100644
index 18a99794..00000000
--- a/libwmc/src/errors.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_ERRORS_H
-#define LIBWMC_ERRORS_H
-
-#include <config.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <stdio.h>
-#include <stdarg.h>
-
-enum {
- LOGL_ERR = 0x00000001,
- LOGL_WARN = 0x00000002,
- LOGL_INFO = 0x00000004,
- LOGL_DEBUG = 0x00000008
-};
-
-enum {
- WMC_SUCCESS = 0,
- WMC_ERROR_INVALID_ARGUMENTS = 1,
- WMC_ERROR_SERIAL_CONFIG_FAILED = 2,
- WMC_ERROR_VALUE_NOT_FOUND = 3,
- WMC_ERROR_RESPONSE_UNEXPECTED = 4,
- WMC_ERROR_RESPONSE_BAD_LENGTH = 5,
-};
-
-#define wmc_assert assert
-
-#define wmc_return_if_fail(e) \
-{ \
- if (!(e)) { \
- wmc_warn (0, "failed: " #e "\n"); \
- return; \
- } \
-}
-
-#define wmc_return_val_if_fail(e, v) \
-{ \
- if (!(e)) { \
- wmc_warn (0, "failed: " #e "\n"); \
- return v; \
- } \
-}
-
-void _wmc_log (const char *file,
- int line,
- const char *func,
- int domain,
- int level,
- const char *format,
- ...) __attribute__((__format__ (__printf__, 6, 7)));
-
-#define wmc_dbg(domain, ...) \
- _wmc_log (__FILE__, __LINE__, __func__, domain, LOGL_DEBUG, ## __VA_ARGS__ )
-
-#define wmc_warn(domain, ...) \
- _wmc_log (__FILE__, __LINE__, __func__, domain, LOGL_WARN, ## __VA_ARGS__ )
-
-#define wmc_err(domain, ...) \
- _wmc_log (__FILE__, __LINE__, __func__, domain, LOGL_ERR, ## __VA_ARGS__ )
-
-#endif /* LIBWMC_ERRORS_H */
diff --git a/libwmc/src/protocol.h b/libwmc/src/protocol.h
deleted file mode 100644
index e341f56f..00000000
--- a/libwmc/src/protocol.h
+++ /dev/null
@@ -1,462 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_PROTOCOL_H
-#define LIBWMC_PROTOCOL_H
-
-#define WMC_CMD_MARKER ((u_int8_t) 0xC8)
-
-enum {
- WMC_CMD_GET_GLOBAL_MODE = 0x03,
- WMC_CMD_SET_GLOBAL_MODE = 0x04,
- WMC_CMD_DEVICE_INFO = 0x06,
- WMC_CMD_CONNECTION_INFO = 0x0A,
- WMC_CMD_NET_INFO = 0x0B,
- WMC_CMD_INIT = 0x0D,
- WMC_CMD_FIELD_TEST = 0x0F,
- WMC_CMD_SET_OPERATOR = 0x33,
- WMC_CMD_GET_FIRST_OPERATOR = 0x34,
- WMC_CMD_GET_NEXT_OPERATOR = 0x35,
- WMC_CMD_GET_APN = 0x4D,
-};
-
-/* MCC/MNC representation
- *
- * Various commands accept or return an MCC/MNC. When sending, convert
- * the MCC/MNC into a number using eg. atoi() and store it as an LE 32-bit
- * value. 3-digit MNCs appear to be sent as 3-digit only if the firmware
- * reports them as 3-digit. For example:
- *
- * T-Mobile US: 310-260
- * mcc_mnc = 0x00007932 = 31026 (note the 2-digit-only MNC)
- *
- * AT&T: 310-410
- * mcc_mnc = 0x0004bc8a = 310410
- */
-
-
-/* Generic WMC command header */
-struct WmcCmdHeader {
- u_int8_t marker; /* Always 0xC8 */
- u_int8_t cmd;
-} __attribute__ ((packed));
-typedef struct WmcCmdHeader WmcCmdHeader;
-
-/* Used on newer devices like the UML190 and later */
-struct WmcCmdInit2 {
- WmcCmdHeader hdr;
- u_int16_t year;
- u_int8_t month;
- u_int16_t day; /* big endian */
- u_int16_t hours; /* big endian */
- u_int16_t minutes; /* big endian */
- u_int16_t seconds; /* big endian */
- u_int8_t _unknown1[3];
-} __attribute__ ((packed));
-typedef struct WmcCmdInit2 WmcCmdInit2;
-
-struct WmcCmdInit2Rsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1[4];
-} __attribute__ ((packed));
-typedef struct WmcCmdInit2Rsp WmcCmdInit2Rsp;
-
-struct WmcCmdDeviceInfoRsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1[27];
- char manf[64];
- char model[64];
- char fwrev[64];
- char hwrev[64];
- u_int8_t _unknown2[64];
- u_int8_t _unknown3[64];
- char min[10]; /* CDMA2000/IS-95 MIN */
- u_int8_t _unknown4[12];
- u_int16_t home_sid;
- u_int8_t _unknown5[2];
- u_int16_t prlver;
- u_int8_t _unknown6[2];
- u_int16_t eriver;
- u_int8_t _unknown7[4];
-} __attribute__ ((packed));
-typedef struct WmcCmdDeviceInfoRsp WmcCmdDeviceInfoRsp;
-
-struct WmcCmdDeviceInfo2Rsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1[27];
- char manf[64];
- char model[64];
- char fwrev[64];
- char hwrev[64];
- u_int8_t _unknown2[64];
- u_int8_t _unknown3[64];
- u_int8_t min[10]; /* CDMA2000/IS-95 MIN */
- u_int8_t _unknown4[12];
- u_int16_t home_sid;
- u_int8_t _unknown5[2];
- u_int16_t prlver;
- u_int8_t _unknown6[2];
- u_int16_t eriver;
- u_int8_t _unknown7[4];
- u_int8_t _unknown8[64];
- u_int8_t meid[14];
- u_int8_t _unknown10[6]; /* always zero */
- u_int8_t imei[16];
- u_int8_t _unknown11[6]; /* always zero */
- u_int8_t _unknown12[16];
- u_int8_t iccid[20];
- u_int8_t _unknown13[6];
-} __attribute__ ((packed));
-typedef struct WmcCmdDeviceInfo2Rsp WmcCmdDeviceInfo2Rsp;
-
-struct WmcCmdDeviceInfo3Rsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1[27];
- char manf[64];
- char model[64];
- char fwrev[64];
- char hwrev[64];
- u_int8_t _unknown2[64];
- u_int8_t _unknown3[64];
- u_int8_t min[10]; /* CDMA2000/IS-95 MIN */
- u_int8_t _unknown4[12];
- u_int16_t home_sid;
- u_int8_t _unknown5[2];
- u_int16_t prlver;
- u_int8_t _unknown6[2];
- u_int16_t eri_ver;
- u_int8_t _unknown7[4];
- u_int8_t _unknown8[64];
- u_int8_t meid[14];
- u_int8_t _unknown10[6]; /* always zero */
- u_int8_t imei[16];
- u_int8_t _unknown11[6]; /* always zero */
- u_int8_t _unknown12[16];
- u_int8_t iccid[20];
- u_int8_t _unknown13[6];
- u_int8_t mcc[16];
- u_int8_t mnc[16];
- u_int8_t _unknown14[4];
- u_int8_t _unknown15[4];
- u_int8_t _unknown16[4];
-} __attribute__ ((packed));
-typedef struct WmcCmdDeviceInfo3Rsp WmcCmdDeviceInfo3Rsp;
-
-/*****************************************************/
-
-enum {
- WMC_SERVICE_NONE = 0,
- WMC_SERVICE_AMPS = 1,
- WMC_SERVICE_IS95A = 2,
- WMC_SERVICE_IS95B = 3,
- WMC_SERVICE_GSM = 4,
- WMC_SERVICE_GPRS = 5,
- WMC_SERVICE_1XRTT = 6,
- WMC_SERVICE_EVDO_0 = 7,
- WMC_SERVICE_UMTS = 8,
- WMC_SERVICE_EVDO_A = 9,
- WMC_SERVICE_EDGE = 10,
- WMC_SERVICE_HSDPA = 11,
- WMC_SERVICE_HSUPA = 12,
- WMC_SERVICE_HSPA = 13,
- WMC_SERVICE_LTE = 14,
- WMC_SERVICE_EVDO_A_EHRPD = 15,
-};
-
-/* PC5740 response */
-struct WmcCmdNetworkInfoRsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1;
- u_int8_t _unknown2[3]; /* Always zero */
- u_int8_t service; /* One of WMC_SERVICE_* */
- u_int8_t _unknown3; /* Either 0x00 or 0x01 */
- u_int16_t ts_year;
- u_int8_t ts_month;
- u_int16_t ts_day; /* BE */
- u_int16_t ts_hours; /* BE */
- u_int16_t ts_minutes; /* BE */
- u_int16_t ts_seconds; /* BE */
- u_int16_t counter1; /* A timestamp/counter? */
- u_int16_t _unknown4;
- u_int8_t _unknown5[3]; /* Always 0xFE 0xFF 0xFF */
- u_int8_t two_g_dbm; /* 0x7D = no signal */
- u_int8_t _unknown6[37]; /* Always zero */
-} __attribute__ ((packed));
-typedef struct WmcCmdNetworkInfoRsp WmcCmdNetworkInfoRsp;
-
-/* UML190 response */
-struct WmcCmdNetworkInfo2Rsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* 0x00 on LTE, 0x07 or 0x1F on CDMA */
- u_int8_t _unknown2[3]; /* Always zero */
- u_int8_t service; /* One of WMC_SERVICE_* */
- u_int8_t _unknown3;
- u_int16_t ts_year;
- u_int8_t ts_month;
- u_int16_t ts_day; /* BE */
- u_int16_t ts_hours; /* BE */
- u_int16_t ts_minutes; /* BE */
- u_int16_t ts_seconds; /* BE */
- u_int8_t _unknown4; /* always zero */
- u_int16_t uptime_secs;
- u_int8_t _unknown5;
- u_int8_t _unknown6[3]; /* always zero on LTE, 0xFE 0xFF 0xFF on CDMA */
- u_int8_t two_g_dbm; /* 0x7D = no CDMA signal, 0x6a = no GSM signal */
- u_int8_t _unknown7[3]; /* Always zero */
- u_int8_t cdma_opname[16]; /* Zero terminated? */
- u_int8_t _unknown8[18]; /* Always zero */
- u_int8_t three_g_dbm; /* 0x7D = no signal */
- u_int8_t _unknown9[3]; /* Always zero */
- u_int8_t _unknown10; /* 0x01 on LTE, 0x40 on CDMA */
- u_int8_t _unknown11[3]; /* Always zero */
- u_int8_t _unknown12; /* Always 0x01 */
- u_int8_t tgpp_opname[8]; /* 3GPP operator name (Zero terminated? Sometimes "MCC MNC" too */
- u_int8_t _unknown13[4]; /* Always zero */
- u_int32_t _unknown14; /* 43 75 3a 00 on GSM/WCDMA mode, zero on others */
- u_int32_t _unknown15; /* 49 7d 3a 00 on GSM/WCDMA mode, zero on others */
- u_int8_t _unknown16[44]; /* Always zero */
- u_int32_t mcc_mnc; /* GSM/WCDMA only, see MCC/MNC format note */
-} __attribute__ ((packed));
-typedef struct WmcCmdNetworkInfo2Rsp WmcCmdNetworkInfo2Rsp;
-
-/* UML290 response */
-struct WmcCmdNetworkInfo3Rsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* 0x00 on LTE, 0x07 or 0x1F on CDMA */
- u_int8_t _unknown2[3]; /* Always zero */
- u_int8_t service; /* One of WMC_SERVICE_* */
- u_int8_t _unknown3;
- u_int16_t ts_year;
- u_int8_t ts_month;
- u_int16_t ts_day; /* BE */
- u_int16_t ts_hours; /* BE */
- u_int16_t ts_minutes; /* BE */
- u_int16_t ts_seconds; /* BE */
- u_int8_t _unknown4; /* always zero */
- u_int16_t uptime_secs;
- u_int8_t _unknown5;
- u_int8_t _unknown6[3]; /* always zero on LTE, 0xFE 0xFF 0xFF on CDMA */
- u_int8_t two_g_dbm; /* 0x7D = no CDMA signal, 0x6a = no GSM signal */
- u_int8_t _unknown7[3]; /* Always zero */
- u_int8_t cdma_opname[16]; /* Zero terminated? */
- u_int8_t _unknown8[18]; /* Always zero */
- u_int8_t three_g_dbm; /* 0x7D = no signal */
- u_int8_t _unknown9[3]; /* Always zero */
- u_int8_t _unknown10; /* 0x01 on LTE, 0x40 on CDMA */
- u_int8_t _unknown11[3]; /* Always zero */
- u_int8_t _unknown12; /* Always 0x01 */
- u_int8_t tgpp_opname[8]; /* Zero terminated? Sometimes "MCC MNC" too */
- u_int8_t _unknown13[4]; /* Always zero */
- u_int32_t _unknown14; /* 43 75 3a 00 on GSM/WCDMA mode, zero on others */
- u_int32_t _unknown15; /* 49 7d 3a 00 on GSM/WCDMA mode, zero on others */
- u_int8_t _unknown16[44]; /* Always zero */
- u_int32_t mcc_mnc; /* GSM/WCDMA only, see MCC/MNC format note */
- u_int8_t lte_dbm; /* 0x00 if not in LTE mode */
- u_int8_t _unknown17[3]; /* Always zero */
- u_int8_t _unknown18[4];
-} __attribute__ ((packed));
-typedef struct WmcCmdNetworkInfo3Rsp WmcCmdNetworkInfo3Rsp;
-
-/*****************************************************/
-
-enum {
- WMC_CONNECTION_STATE_UNKNOWN = 0,
- WMC_CONNECTION_STATE_IDLE = 1,
- WMC_CONNECTION_STATE_CONNECTING = 2,
- WMC_CONNECTION_STATE_AUTHENTICATING = 3,
- WMC_CONNECTION_STATE_CONNECTED = 4,
- WMC_CONNECTION_STATE_DORMANT = 5,
- WMC_CONNECTION_STATE_UPDATING_NAM = 6,
- WMC_CONNECTION_STATE_UPDATING_PRL = 7,
- WMC_CONNECTION_STATE_DISCONNECTING = 8,
- WMC_CONNECTION_STATE_ERROR = 9,
- WMC_CONNECTION_STATE_UPDATING_UICC = 10,
- WMC_CONNECTION_STATE_UPDATING_PLMN = 11
-};
-
-/* Used on UML190 */
-struct WmcCmdConnectionInfoRsp {
- WmcCmdHeader hdr;
- u_int32_t rx_bytes;
- u_int32_t tx_bytes;
- u_int8_t _unknown1[8];
- u_int8_t state; /* One of WMC_CONNECTION_STATE_* */
- u_int8_t _unknown2[3]; /* Always 0xc0 0x0b 0x00 */
-} __attribute__ ((packed));
-typedef struct WmcCmdConnectionInfoRsp WmcCmdConnectionInfoRsp;
-
-/* Used on UML290 */
-struct WmcCmdConnectionInfo2Rsp {
- WmcCmdHeader hdr;
- u_int32_t rx_bytes;
- u_int32_t tx_bytes;
- u_int8_t _unknown1[8];
- u_int8_t state; /* One of WMC_CONNECTION_STATE_* */
- u_int8_t _unknown2[3]; /* Always 0xc0 0x0b 0x00 */
- u_int8_t _unknown3[4]; /* Always 0x01 0x00 0x00 0x00 */
- u_int8_t ip4_address[16]; /* String format, ie "10.156.45.3" */
- u_int8_t _unknown4[8]; /* Netmask? */
- u_int8_t ip6_address[40]; /* String format */
-} __attribute__ ((packed));
-typedef struct WmcCmdConnection2InfoRsp WmcCmdConnection2InfoRsp;
-
-/*****************************************************/
-
-enum {
- WMC_GLOBAL_MODE_AUTO_CDMA = 0x00,
- WMC_GLOBAL_MODE_CDMA_ONLY = 0x01,
- WMC_GLOBAL_MODE_EVDO_ONLY = 0x02,
- WMC_GLOBAL_MODE_AUTO_GSM = 0x0A,
- WMC_GLOBAL_MODE_GPRS_ONLY = 0x0B,
- WMC_GLOBAL_MODE_UMTS_ONLY = 0x0C,
- WMC_GLOBAL_MODE_AUTO = 0x14,
- WMC_GLOBAL_MODE_LTE_ONLY = 0x1E,
-};
-
-struct WmcCmdGetGlobalMode {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* always 0 */
-} __attribute__ ((packed));
-typedef struct WmcCmdGetGlobalMode WmcCmdGetGlobalMode;
-
-struct WmcCmdGetGlobalModeRsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* always 0x01 */
- u_int8_t mode; /* one of WMC_GLOBAL_MODE_* */
- u_int8_t _unknown2; /* always 0x05 */
- u_int8_t _unknown3; /* always 0x00 */
-} __attribute__ ((packed));
-typedef struct WmcCmdGetGlobalModeRsp WmcCmdGetGlobalModeRsp;
-
-/*****************************************************/
-
-struct WmcCmdSetGlobalMode {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* always 0x01 */
- u_int8_t mode; /* one of WMC_GLOBAL_MODE_* */
- u_int8_t _unknown2; /* always 0x05 */
- u_int8_t _unknown3; /* always 0x00 */
-} __attribute__ ((packed));
-typedef struct WmcCmdSetGlobalMode WmcCmdSetGlobalMode;
-
-struct WmcCmdSetGlobalModeRsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* always 0x01 */
- u_int32_t _unknown2; /* always zero */
-} __attribute__ ((packed));
-typedef struct WmcCmdSetGlobalModeRsp WmcCmdSetGlobalModeRsp;
-
-/*****************************************************/
-
-struct WmcCmdSetOperator {
- WmcCmdHeader hdr;
- u_int8_t automatic; /* 0x00 = manual, 0x01 = auto */
- u_int8_t _unknown1; /* always 0x50 */
- u_int8_t _unknown2[3]; /* always zero */
- u_int32_t mcc_mnc; /* MCC/MNC for manual reg (see format note), zero for auto */
- u_int8_t _unknown3[56]; /* always zero */
-} __attribute__ ((packed));
-typedef struct WmcCmdSetOperator WmcCmdSetOperator;
-
-enum {
- WMC_SET_OPERATOR_STATUS_OK = 0,
- WMC_SET_OPERATOR_STATUS_REGISTERING = 0x63,
- WMC_SET_OPERATOR_STATUS_FAILED = 0x68,
-};
-
-struct WmcCmdSetOperatorRsp {
- WmcCmdHeader hdr;
- u_int8_t status; /* one of WMC_SET_OPERATOR_STATUS_* */
- u_int8_t _unknown1[3]; /* always zero */
-} __attribute__ ((packed));
-typedef struct WmcCmdSetOperatorRsp WmcCmdSetOperatorRsp;
-
-/*****************************************************/
-
-enum {
- WMC_OPERATOR_SERVICE_UNKNOWN = 0,
- WMC_OPERATOR_SERVICE_GSM = 1,
- WMC_OPERATOR_SERVICE_UMTS = 2,
-};
-
-/* Response for both GET_FIRST_OPERATOR and GET_NEXT_OPERATOR */
-struct WmcCmdGetOperatorRsp {
- WmcCmdHeader hdr;
- u_int8_t _unknown1; /* Usually 0x50, sometimes 0x00 */
- u_int8_t _unknown2[3]; /* always zero */
- u_int32_t mcc_mnc; /* see format note */
- u_int8_t opname[8];
- u_int8_t _unknown3[56]; /* always zero */
- u_int8_t stat; /* follows 3GPP TS27.007 +COPS <stat> ? */
- u_int8_t _unknown4[3]; /* always zero */
- u_int8_t service; /* one of WMC_OPERATOR_SERVICE_* */
- u_int8_t _unknown5[3]; /* always zero */
- u_int8_t _unknown6; /* 0x63 (GET_FIRST_OP) might mean "wait" */
- u_int8_t _unknown7; /* 0x00 or 0x01 */
- u_int8_t _unknown8[2]; /* always zero */
-} __attribute__ ((packed));
-typedef struct WmcCmdGetOperatorRsp WmcCmdGetOperatorRsp;
-
-/*****************************************************/
-
-enum {
- WMC_FIELD_TEST_MOBILE_IP_MODE_MIP_OFF = 0,
- WMC_FIELD_TEST_MOBILE_IP_MODE_MIP_PREF = 1,
- WMC_FIELD_TEST_MOBILE_IP_MODE_MIP_ONLY = 2
-};
-
-/* Later devices return all zeros for this command */
-struct WmcCmdFieldTestRsp {
- WmcCmdHeader hdr;
- u_int8_t prl_requirements;
- u_int8_t eri_support;
- char nam_name[7];
- u_int8_t _unknown1; /* always zero */
- u_int8_t _unknown2[3]; /* always 0x0A 0x0A 0x0A */
- u_int8_t _unknown3[5]; /* always zero */
- u_int8_t _unknown4[10]; /* all 0x0F */
- u_int16_t home_sid;
- u_int16_t home_nid;
- char min1[7];
- char min2[3];
- char mcc[3];
- char imsi_s[10];
- char mnc[2];
- u_int16_t primary_cdma_chan_a;
- u_int16_t secondary_cdma_chan_a;
- u_int16_t primary_cdma_chan_b;
- u_int16_t secondary_cdma_chan_b;
- u_int8_t accolc;
- char sw_version[64];
- char hw_version[64];
- u_int16_t prlver;
- u_int16_t eriver;
- u_int16_t nid;
- u_int8_t last_call_end_reason; /* ? */
- u_int8_t rssi;
- u_int16_t channel;
- u_int8_t prev;
- u_int16_t pn_offset;
- u_int8_t sys_select_pref;
- u_int8_t mip_pref;
- u_int8_t hybrid_pref;
-} __attribute__ ((packed));
-typedef struct WmcCmdFieldTestRsp WmcCmdFieldTestRsp;
-
-/*****************************************************/
-
-#endif /* LIBWMC_PROTOCOL_H */
diff --git a/libwmc/src/result-private.h b/libwmc/src/result-private.h
deleted file mode 100644
index 32a3fcb3..00000000
--- a/libwmc/src/result-private.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_RESULT_PRIVATE_H
-#define LIBWMC_RESULT_PRIVATE_H
-
-#include "result.h"
-
-WmcResult *wmc_result_new (void);
-
-void wmc_result_add_string (WmcResult *result,
- const char *key,
- const char *str);
-
-void wmc_result_add_u8 (WmcResult *result,
- const char *key,
- u_int8_t num);
-
-void wmc_result_add_u32 (WmcResult *result,
- const char *key,
- u_int32_t num);
-
-#endif /* LIBWMC_RESULT_PRIVATE_H */
-
diff --git a/libwmc/src/result.c b/libwmc/src/result.c
deleted file mode 100644
index b66f864b..00000000
--- a/libwmc/src/result.c
+++ /dev/null
@@ -1,298 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "result.h"
-#include "result-private.h"
-#include "errors.h"
-
-/*********************************************************/
-
-typedef struct Val Val;
-
-typedef enum {
- VAL_TYPE_NONE = 0,
- VAL_TYPE_STRING = 1,
- VAL_TYPE_U8 = 2,
- VAL_TYPE_U32 = 3
-} ValType;
-
-struct Val {
- char *key;
- ValType type;
- union {
- char *s;
- u_int8_t u8;
- u_int32_t u32;
- } u;
- Val *next;
-};
-
-static void
-val_free (Val *v)
-{
- if (v->type == VAL_TYPE_STRING) {
- if (v->u.s)
- free (v->u.s);
- }
- free (v->key);
- memset (v, 0, sizeof (*v));
- free (v);
-}
-
-static Val *
-val_new_string (const char *key, const char *value)
-{
- Val *v;
-
- wmc_return_val_if_fail (key != NULL, NULL);
- wmc_return_val_if_fail (key[0] != '\0', NULL);
- wmc_return_val_if_fail (value != NULL, NULL);
-
- v = calloc (sizeof (Val), 1);
- if (v == NULL)
- return NULL;
-
- v->key = strdup (key);
- v->type = VAL_TYPE_STRING;
- v->u.s = strdup (value);
- return v;
-}
-
-static Val *
-val_new_u8 (const char *key, u_int8_t u)
-{
- Val *v;
-
- wmc_return_val_if_fail (key != NULL, NULL);
- wmc_return_val_if_fail (key[0] != '\0', NULL);
-
- v = calloc (sizeof (Val), 1);
- if (v == NULL)
- return NULL;
-
- v->key = strdup (key);
- v->type = VAL_TYPE_U8;
- v->u.u8 = u;
- return v;
-}
-
-static Val *
-val_new_u32 (const char *key, u_int32_t u)
-{
- Val *v;
-
- wmc_return_val_if_fail (key != NULL, NULL);
- wmc_return_val_if_fail (key[0] != '\0', NULL);
-
- v = calloc (sizeof (Val), 1);
- if (v == NULL)
- return NULL;
-
- v->key = strdup (key);
- v->type = VAL_TYPE_U32;
- v->u.u32 = u;
- return v;
-}
-
-/*********************************************************/
-
-struct WmcResult {
- u_int32_t refcount;
- Val *first;
-};
-
-WmcResult *
-wmc_result_new (void)
-{
- WmcResult *r;
-
- r = calloc (sizeof (WmcResult), 1);
- if (r)
- r->refcount = 1;
- return r;
-}
-
-WmcResult *
-wmc_result_ref (WmcResult *r)
-{
- wmc_return_val_if_fail (r != NULL, NULL);
- wmc_return_val_if_fail (r->refcount > 0, NULL);
-
- r->refcount++;
- return r;
-}
-
-static void
-wmc_result_free (WmcResult *r)
-{
- Val *v, *n;
-
- v = r->first;
- while (v) {
- n = v->next;
- val_free (v);
- v = n;
- }
- memset (r, 0, sizeof (*r));
- free (r);
-}
-
-void
-wmc_result_unref (WmcResult *r)
-{
- wmc_return_if_fail (r != NULL);
- wmc_return_if_fail (r->refcount > 0);
-
- r->refcount--;
- if (r->refcount == 0)
- wmc_result_free (r);
-}
-
-static Val *
-find_val (WmcResult *r, const char *key, ValType expected_type)
-{
- Val *v, *n;
-
- v = r->first;
- while (v) {
- n = v->next;
- if (strcmp (v->key, key) == 0) {
- /* Check type */
- wmc_return_val_if_fail (v->type == expected_type, NULL);
- return v;
- }
- v = n;
- }
- return NULL;
-}
-
-void
-wmc_result_add_string (WmcResult *r,
- const char *key,
- const char *str)
-{
- Val *v;
-
- wmc_return_if_fail (r != NULL);
- wmc_return_if_fail (r->refcount > 0);
- wmc_return_if_fail (key != NULL);
- wmc_return_if_fail (str != NULL);
-
- v = val_new_string (key, str);
- wmc_return_if_fail (v != NULL);
- v->next = r->first;
- r->first = v;
-}
-
-int
-wmc_result_get_string (WmcResult *r,
- const char *key,
- const char **out_val)
-{
- Val *v;
-
- wmc_return_val_if_fail (r != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (r->refcount > 0, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (key != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (out_val != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (*out_val == NULL, -WMC_ERROR_INVALID_ARGUMENTS);
-
- v = find_val (r, key, VAL_TYPE_STRING);
- if (v == NULL)
- return -WMC_ERROR_VALUE_NOT_FOUND;
-
- *out_val = v->u.s;
- return 0;
-}
-
-void
-wmc_result_add_u8 (WmcResult *r,
- const char *key,
- u_int8_t num)
-{
- Val *v;
-
- wmc_return_if_fail (r != NULL);
- wmc_return_if_fail (r->refcount > 0);
- wmc_return_if_fail (key != NULL);
-
- v = val_new_u8 (key, num);
- wmc_return_if_fail (v != NULL);
- v->next = r->first;
- r->first = v;
-}
-
-int
-wmc_result_get_u8 (WmcResult *r,
- const char *key,
- u_int8_t *out_val)
-{
- Val *v;
-
- wmc_return_val_if_fail (r != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (r->refcount > 0, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (key != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (out_val != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
-
- v = find_val (r, key, VAL_TYPE_U8);
- if (v == NULL)
- return -WMC_ERROR_VALUE_NOT_FOUND;
-
- *out_val = v->u.u8;
- return 0;
-}
-
-void
-wmc_result_add_u32 (WmcResult *r,
- const char *key,
- u_int32_t num)
-{
- Val *v;
-
- wmc_return_if_fail (r != NULL);
- wmc_return_if_fail (r->refcount > 0);
- wmc_return_if_fail (key != NULL);
-
- v = val_new_u32 (key, num);
- wmc_return_if_fail (v != NULL);
- v->next = r->first;
- r->first = v;
-}
-
-int
-wmc_result_get_u32 (WmcResult *r,
- const char *key,
- u_int32_t *out_val)
-{
- Val *v;
-
- wmc_return_val_if_fail (r != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (r->refcount > 0, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (key != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
- wmc_return_val_if_fail (out_val != NULL, -WMC_ERROR_INVALID_ARGUMENTS);
-
- v = find_val (r, key, VAL_TYPE_U32);
- if (v == NULL)
- return -WMC_ERROR_VALUE_NOT_FOUND;
-
- *out_val = v->u.u32;
- return 0;
-}
-
diff --git a/libwmc/src/result.h b/libwmc/src/result.h
deleted file mode 100644
index 516f0ba7..00000000
--- a/libwmc/src/result.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_RESULT_H
-#define LIBWMC_RESULT_H
-
-#include <sys/types.h>
-
-typedef struct WmcResult WmcResult;
-
-int wmc_result_get_string (WmcResult *r,
- const char *key,
- const char **out_val);
-
-int wmc_result_get_u8 (WmcResult *r,
- const char *key,
- u_int8_t *out_val);
-
-int wmc_result_get_u32 (WmcResult *r,
- const char *key,
- u_int32_t *out_val);
-
-WmcResult *wmc_result_ref (WmcResult *r);
-
-void wmc_result_unref (WmcResult *r);
-
-#endif /* LIBWMC_RESULT_H */
-
diff --git a/libwmc/src/utils.c b/libwmc/src/utils.c
deleted file mode 100644
index 408e1077..00000000
--- a/libwmc/src/utils.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <malloc.h>
-#include <fcntl.h>
-#include <string.h>
-
-#include "utils.h"
-#include "errors.h"
-
-/* QCDM protocol frames are pseudo Async HDLC frames which end with a 3-byte
- * trailer. This trailer consists of the 16-bit CRC of the frame plus an ending
- * "async control character" whose value is 0x7E. The frame *and* the CRC are
- * escaped before adding the trailing control character so that the control
- * character (0x7E) and the escape marker (0x7D) are never seen in the frame.
- */
-
-/* Table of CRCs for each possible byte, with a generator polynomial of 0x8408 */
-static const u_int16_t crc_table[256] = {
- 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-
-/* Calculate the CRC for a buffer using a seed of 0xffff */
-u_int16_t
-wmc_crc16 (const char *buffer, size_t len, u_int16_t seed)
-{
- u_int16_t crc = seed ? seed : 0xFFFF;
-
- while (len--)
- crc = crc_table[(crc ^ *buffer++) & 0xff] ^ (crc >> 8);
- return ~crc;
-}
-
-#define DIAG_ESC_CHAR 0x7D /* Escape sequence 1st character value */
-#define DIAG_ESC_MASK 0x20 /* Escape sequence complement value */
-
-/* Performs DM escaping on inbuf putting the result into outbuf, and returns
- * the final length of the buffer.
- */
-size_t
-hdlc_escape (const char *inbuf,
- size_t inbuf_len,
- wmcbool escape_all_ctrl,
- char *outbuf,
- size_t outbuf_len)
-{
- const char *src = inbuf;
- char *dst = outbuf;
- size_t i = inbuf_len;
-
- wmc_return_val_if_fail (inbuf != NULL, 0);
- wmc_return_val_if_fail (inbuf_len > 0, 0);
- wmc_return_val_if_fail (outbuf != NULL, 0);
- wmc_return_val_if_fail (outbuf_len > inbuf_len, 0);
-
- /* Since escaping potentially doubles the # of bytes, short-circuit the
- * length check if destination buffer is clearly large enough. Note the
- *
- */
- if (outbuf_len <= inbuf_len << 1) {
- size_t outbuf_required = inbuf_len + 1; /* +1 for the trailing control char */
-
- /* Each escaped character takes up two bytes in the output buffer */
- while (i--) {
- if ( *src == DIAG_CONTROL_CHAR
- || *src == DIAG_ESC_CHAR
- || (escape_all_ctrl && *src <= 0x20))
- outbuf_required++;
- src++;
- }
-
- if (outbuf_len < outbuf_required)
- return 0;
- }
-
- /* Do the actual escaping. Replace both the control character and
- * the escape character in the source buffer with the following sequence:
- *
- * <escape_char> <src_byte ^ escape_mask>
- */
- src = inbuf;
- i = inbuf_len;
- while (i--) {
- u_int8_t byte = (u_int8_t) *src++;
-
- if ( byte == DIAG_CONTROL_CHAR
- || byte == DIAG_ESC_CHAR
- || (escape_all_ctrl && byte <= 0x20)) {
- *dst++ = DIAG_ESC_CHAR;
- *dst++ = byte ^ DIAG_ESC_MASK;
- } else
- *dst++ = byte;
- }
-
- return (dst - outbuf);
-}
-
-size_t
-hdlc_unescape (const char *inbuf,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len,
- wmcbool *escaping)
-{
- size_t i, outsize;
-
- wmc_return_val_if_fail (inbuf_len > 0, 0);
- wmc_return_val_if_fail (outbuf_len >= inbuf_len, 0);
- wmc_return_val_if_fail (escaping != NULL, 0);
-
- for (i = 0, outsize = 0; i < inbuf_len; i++) {
- if (*escaping) {
- outbuf[outsize++] = inbuf[i] ^ DIAG_ESC_MASK;
- *escaping = FALSE;
- } else if (inbuf[i] == DIAG_ESC_CHAR)
- *escaping = TRUE;
- else
- outbuf[outsize++] = inbuf[i];
-
- /* About to overrun output buffer size */
- if (outsize >= outbuf_len)
- return 0;
- }
-
- return outsize;
-}
-
-/**
- * hdlc_encapsulate_buffer:
- * @inbuf: data buffer to encapsulate
- * @cmd_len: size of the data contained in @inbuf
- * @inbuf_len: total size of @inbuf itself (not just the data)
- * @crc_seed: if non-zero, CRC-16 seed to use; if 0, uses standard 0xFFFF
- * @add_trailer: if %TRUE, adds trailing 0x7E
- * @escape_all_ctrl: if %TRUE, escapes all control characters instead of only
- * special HDLC escape characters 0x7D and 0x7E
- * @outbuf: buffer in which to put the encapsulated data
- * @outbuf_len: total size of @outbuf
- *
- * Escapes and CRCs given data using HDLC-style mechanisms, and optionally adds
- * the trailing control character that denotes the end of the HDLC frame.
- *
- * Returns: size of the encapsulated data writted to @outbuf.
- **/
-size_t
-hdlc_encapsulate_buffer (char *inbuf,
- size_t cmd_len,
- size_t inbuf_len,
- u_int16_t crc_seed,
- wmcbool add_trailer,
- wmcbool escape_all_ctrl,
- char *outbuf,
- size_t outbuf_len)
-{
- u_int16_t crc;
- size_t escaped_len;
-
- wmc_return_val_if_fail (inbuf != NULL, 0);
- wmc_return_val_if_fail (cmd_len >= 1, 0);
- wmc_return_val_if_fail (inbuf_len >= cmd_len + 2, 0); /* space for CRC */
- wmc_return_val_if_fail (outbuf != NULL, 0);
-
- /* Add the CRC */
- crc = wmc_crc16 (inbuf, cmd_len, crc_seed ? crc_seed : 0xFFFF);
- inbuf[cmd_len++] = crc & 0xFF;
- inbuf[cmd_len++] = (crc >> 8) & 0xFF;
-
- escaped_len = hdlc_escape (inbuf, cmd_len, escape_all_ctrl, outbuf, outbuf_len);
- wmc_return_val_if_fail (outbuf_len > escaped_len, 0);
-
- if (add_trailer)
- outbuf[escaped_len++] = DIAG_CONTROL_CHAR;
-
- return escaped_len;
-}
-
-#define AT_WMC_PREFIX "AT*WMC="
-
-/**
- * uml290_wmc_encapsulate:
- * @inbuf: data buffer to encapsulate
- * @cmd_len: size of the data contained in @inbuf
- * @inbuf_len: total size of @inbuf itself (not just the data)
- * @outbuf: buffer in which to put the encapsulated data
- * @outbuf_len: total size of @outbuf
- *
- * Escapes and CRCs given data using HDLC-style mechanisms with UML290 specific
- * quirks.
- *
- * Returns: size of the encapsulated data writted to @outbuf.
- */
-static size_t
-uml290_wmc_encapsulate (char *inbuf,
- size_t cmd_len,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len)
-{
- size_t encap_len;
- size_t estimated_out_len;
-
- wmc_return_val_if_fail (inbuf != NULL, 0);
- wmc_return_val_if_fail (cmd_len >= 1, 0);
- wmc_return_val_if_fail (inbuf_len >= cmd_len + 2, 0); /* space for CRC */
- wmc_return_val_if_fail (outbuf != NULL, 0);
-
- estimated_out_len = cmd_len + strlen (AT_WMC_PREFIX);
- estimated_out_len += 3; /* CRC + trailer */
- estimated_out_len += cmd_len * 2; /* escaping */
- wmc_return_val_if_fail (outbuf_len > estimated_out_len, 0);
-
- memcpy (outbuf, AT_WMC_PREFIX, strlen (AT_WMC_PREFIX));
-
- encap_len = hdlc_encapsulate_buffer (inbuf, cmd_len, inbuf_len, 0xAAFE,
- FALSE, TRUE,
- outbuf + strlen (AT_WMC_PREFIX),
- outbuf_len);
- if (encap_len > 0) {
- encap_len += strlen (AT_WMC_PREFIX);
- outbuf[encap_len++] = 0x0D; /* trailer */
- }
-
- return encap_len;
-}
-
-/**
- * wmc_encapsulate:
- * @inbuf: data buffer to encapsulate
- * @cmd_len: size of the data contained in @inbuf
- * @inbuf_len: total size of @inbuf itself (not just the data)
- * @outbuf: buffer in which to put the encapsulated data
- * @outbuf_len: total size of @outbuf
- * @uml290: if %TRUE return buffer suitable for sending to UML290 devices
- *
- * Escapes and CRCs given data using HDLC-style mechanisms.
- *
- * Returns: size of the encapsulated data writted to @outbuf.
- */
-size_t
-wmc_encapsulate (char *inbuf,
- size_t cmd_len,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len,
- wmcbool uml290)
-{
- wmc_return_val_if_fail (inbuf != NULL, 0);
- wmc_return_val_if_fail (cmd_len >= 1, 0);
- wmc_return_val_if_fail (inbuf_len >= cmd_len + 3, 0); /* space for CRC + trailer */
- wmc_return_val_if_fail (outbuf != NULL, 0);
-
- if (uml290)
- return uml290_wmc_encapsulate (inbuf, cmd_len, inbuf_len, outbuf, outbuf_len);
-
- /* Otherwise do normal WMC */
- return hdlc_encapsulate_buffer (inbuf, cmd_len, inbuf_len,
- 0, TRUE, FALSE, outbuf, outbuf_len);
-}
-
-/**
- * hdlc_decapsulate_buffer:
- * @inbuf: buffer in which to look for an HDLC frame
- * @inbuf_len: length of valid data in @inbuf
- * @check_known_crc: if %TRUE, validate the CRC using @known_crc if the normal
- * CRC check fails
- * @known_crc: if @check_known_crc is %TRUE, compare the frame's CRC against
- * @known_crc if the normal CRC check fails. @known_crc must be in Little
- * Endian (LE) byte order.
- * @outbuf: buffer in which to put decapsulated data from the HDLC frame
- * @outbuf_len: max size of @outbuf
- * @out_decap_len: on success, size of the decapsulated data
- * @out_used: on either success or failure, amount of data used; caller should
- * discard this much data from @inbuf before the next call to this function
- * @out_need_more: when TRUE, indicates that more data is required before
- * a determination about a valid HDLC frame can be made; caller should add
- * more data to @inbuf before calling this function again.
- *
- * Attempts to retrieve, unescape, and CRC-check an HDLC frame from the given
- * buffer.
- *
- * Returns: FALSE on error (packet was invalid or malformed, or the CRC check
- * failed, etc) and places number of bytes to discard from @inbuf in @out_used.
- * When TRUE, either more data is required (in which case @out_need_more will
- * be TRUE), or a data packet was successfully retrieved from @inbuf and the
- * decapsulated packet of length @out_decap_len was placed into @outbuf. In
- * all cases the caller should advance the buffer by the number of bytes
- * returned in @out_used before calling this function again.
- **/
-wmcbool
-hdlc_decapsulate_buffer (const char *inbuf,
- size_t inbuf_len,
- wmcbool check_known_crc,
- u_int16_t known_crc,
- char *outbuf,
- size_t outbuf_len,
- size_t *out_decap_len,
- size_t *out_used,
- wmcbool *out_need_more)
-{
- wmcbool escaping = FALSE;
- size_t i, pkt_len = 0, unesc_len;
- u_int16_t crc, pkt_crc;
-
- wmc_return_val_if_fail (inbuf != NULL, FALSE);
- wmc_return_val_if_fail (outbuf != NULL, FALSE);
- wmc_return_val_if_fail (outbuf_len > 0, FALSE);
- wmc_return_val_if_fail (out_decap_len != NULL, FALSE);
- wmc_return_val_if_fail (out_used != NULL, FALSE);
- wmc_return_val_if_fail (out_need_more != NULL, FALSE);
-
- *out_decap_len = 0;
- *out_used = 0;
- *out_need_more = FALSE;
-
- if (inbuf_len < 4) {
- *out_need_more = TRUE;
- return TRUE;
- }
-
- /* Find the async control character */
- for (i = 0; i < inbuf_len; i++) {
- if (inbuf[i] == DIAG_CONTROL_CHAR) {
- /* If the control character shows up in a position before a valid
- * QCDM packet length (4), the packet is malformed.
- */
- if (i < 3) {
- /* Tell the caller to advance the buffer past the control char */
- *out_used = i + 1;
- return FALSE;
- }
-
- pkt_len = i;
- break;
- }
- }
-
- /* No control char yet, need more data */
- if (!pkt_len) {
- *out_need_more = TRUE;
- return TRUE;
- }
-
- /* Unescape first; note that pkt_len */
- unesc_len = hdlc_unescape (inbuf, pkt_len, outbuf, outbuf_len, &escaping);
- if (!unesc_len) {
- /* Tell the caller to advance the buffer past the control char */
- *out_used = pkt_len + 1;
- return FALSE;
- }
-
- if (escaping) {
- *out_need_more = TRUE;
- return TRUE;
- }
-
- /* Check the CRC of the packet's data */
- crc = wmc_crc16 (outbuf, unesc_len - 2, 0);
- pkt_crc = outbuf[unesc_len - 2] & 0xFF;
- pkt_crc |= (outbuf[unesc_len - 1] & 0xFF) << 8;
- if (crc != pkt_crc) {
- if (!check_known_crc || (pkt_crc != known_crc)) {
- *out_used = pkt_len + 1; /* packet + CRC + 0x7E */
- return FALSE;
- }
- }
-
- *out_used = pkt_len + 1; /* packet + CRC + 0x7E */
- *out_decap_len = unesc_len - 2; /* decap_len should not include the CRC */
- return TRUE;
-}
-
-/**
- * wmc_decapsulate:
- * @inbuf: buffer in which to look for an HDLC frame
- * @inbuf_len: length of valid data in @inbuf
- * @outbuf: buffer in which to put decapsulated data from the HDLC frame
- * @outbuf_len: max size of @outbuf
- * @out_decap_len: on success, size of the decapsulated data
- * @out_used: on either success or failure, amount of data used; caller should
- * discard this much data from @inbuf before the next call to this function
- * @out_need_more: when TRUE, indicates that more data is required before
- * a determination about a valid HDLC frame can be made; caller should add
- * more data to @inbuf before calling this function again.
- * @uml290: if %TRUE decapsulate response from UML290 devices
- *
- * Attempts to retrieve, unescape, and CRC-check an HDLC frame from the given
- * buffer.
- *
- * Returns: FALSE on error (packet was invalid or malformed, or the CRC check
- * failed, etc) and places number of bytes to discard from @inbuf in @out_used.
- * When TRUE, either more data is required (in which case @out_need_more will
- * be TRUE), or a data packet was successfully retrieved from @inbuf and the
- * decapsulated packet of length @out_decap_len was placed into @outbuf. In
- * all cases the caller should advance the buffer by the number of bytes
- * returned in @out_used before calling this function again.
- **/
-wmcbool
-wmc_decapsulate (const char *inbuf,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len,
- size_t *out_decap_len,
- size_t *out_used,
- wmcbool *out_need_more,
- wmcbool uml290)
-{
- return hdlc_decapsulate_buffer (inbuf, inbuf_len,
- uml290, uml290 ? 0x3030 : 0,
- outbuf, outbuf_len,
- out_decap_len, out_used, out_need_more);
-}
-
diff --git a/libwmc/src/utils.h b/libwmc/src/utils.h
deleted file mode 100644
index f1fc4236..00000000
--- a/libwmc/src/utils.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LIBWMC_UTILS_H
-#define LIBWMC_UTILS_H
-
-#include <sys/types.h>
-
-typedef u_int8_t wmcbool;
-#ifndef TRUE
-#define TRUE ((u_int8_t) 1)
-#endif
-#ifndef FALSE
-#define FALSE ((u_int8_t) 0)
-#endif
-
-#define DIAG_CONTROL_CHAR 0x7E
-#define DIAG_TRAILER_LEN 3
-
-/* Utility and testcase functions */
-
-u_int16_t wmc_crc16 (const char *buffer, size_t len, u_int16_t seed);
-
-size_t hdlc_escape (const char *inbuf,
- size_t inbuf_len,
- wmcbool escape_all_ctrl,
- char *outbuf,
- size_t outbuf_len);
-
-size_t hdlc_unescape (const char *inbuf,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len,
- wmcbool *escaping);
-
-size_t hdlc_encapsulate_buffer (char *inbuf,
- size_t cmd_len,
- size_t inbuf_len,
- u_int16_t crc_seed,
- wmcbool add_trailer,
- wmcbool escape_all_ctrl,
- char *outbuf,
- size_t outbuf_len);
-
-wmcbool hdlc_decapsulate_buffer (const char *inbuf,
- size_t inbuf_len,
- wmcbool check_known_crc,
- u_int16_t known_crc,
- char *outbuf,
- size_t outbuf_len,
- size_t *out_decap_len,
- size_t *out_used,
- wmcbool *out_need_more);
-
-/* Functions for actual communication */
-
-size_t wmc_encapsulate (char *inbuf,
- size_t cmd_len,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len,
- wmcbool uml290);
-
-wmcbool wmc_decapsulate (const char *inbuf,
- size_t inbuf_len,
- char *outbuf,
- size_t outbuf_len,
- size_t *out_decap_len,
- size_t *out_used,
- wmcbool *out_need_more,
- wmcbool uml290);
-
-#endif /* LIBWMC_UTILS_H */
-
diff --git a/libwmc/tests/Makefile.am b/libwmc/tests/Makefile.am
deleted file mode 100644
index 33eaf3de..00000000
--- a/libwmc/tests/Makefile.am
+++ /dev/null
@@ -1,28 +0,0 @@
-include $(top_srcdir)/gtester.make
-
-noinst_PROGRAMS = test-wmc
-TEST_PROGS += $(noinst_PROGRAMS)
-
-test_wmc_SOURCES = \
- test-wmc-crc.c \
- test-wmc-crc.h \
- test-wmc-escaping.c \
- test-wmc-escaping.h \
- test-wmc-utils.c \
- test-wmc-utils.h \
- test-wmc-com.c \
- test-wmc-com.h \
- test-wmc.c
-
-test_wmc_CPPFLAGS = \
- $(MM_CFLAGS) \
- -I$(top_srcdir)/libwmc/src \
- -I$(top_srcdir)/src
-
-test_wmc_LDADD = $(MM_LIBS)
-
-if WMC_STANDALONE
-test_wmc_LDADD += $(top_builddir)/src/libwmc.la
-else
-test_wmc_LDADD += $(top_builddir)/libwmc/src/libwmc.la
-endif
diff --git a/libwmc/tests/test-wmc-com.c b/libwmc/tests/test-wmc-com.c
deleted file mode 100644
index 8b4b01d6..00000000
--- a/libwmc/tests/test-wmc-com.c
+++ /dev/null
@@ -1,505 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <termios.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-#include "test-wmc-com.h"
-#include "com.h"
-#include "utils.h"
-#include "errors.h"
-#include "commands.h"
-
-/************************************************************/
-
-typedef struct {
- char *port;
- int fd;
- struct termios old_t;
- wmcbool debug;
- wmcbool uml290;
-} TestComData;
-
-gpointer
-test_com_setup (const char *port, wmcbool uml290, wmcbool debug)
-{
- TestComData *d;
- int ret;
-
- d = g_malloc0 (sizeof (TestComData));
- g_assert (d);
- d->uml290 = uml290;
- d->debug = debug;
-
- if (getenv ("SERIAL_DEBUG"))
- d->debug = TRUE;
-
- errno = 0;
- d->fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
- if (d->fd < 0)
- g_warning ("%s: open failed: %d", port, errno);
- g_assert (d->fd >= 0);
-
- ret = ioctl (d->fd, TIOCEXCL);
- if (ret) {
- g_warning ("%s: lock failed: %d", port, errno);
- close (d->fd);
- d->fd = -1;
- }
- g_assert (ret == 0);
-
- ret = ioctl (d->fd, TCGETA, &d->old_t);
- if (ret) {
- g_warning ("%s: old termios failed: (%d) %s", port, errno, strerror (errno));
- close (d->fd);
- d->fd = -1;
- }
- g_assert (ret == 0);
-
- d->port = g_strdup (port);
- return d;
-}
-
-void
-test_com_teardown (gpointer user_data)
-{
- TestComData *d = user_data;
-
- g_assert (d);
-
- g_free (d->port);
- close (d->fd);
- g_free (d);
-}
-
-static void
-print_buf (const char *detail, const char *buf, size_t len)
-{
- int i = 0, z;
- wmcbool newline = FALSE;
- char *f;
- guint flen;
-
- f = g_strdup_printf ("%s (%zu) ", detail, len);
- flen = strlen (f);
- g_print ("%s", f);
- for (i = 0; i < len; i++) {
- g_print ("%02x ", buf[i] & 0xFF);
- if (((i + 1) % 16) == 0) {
- g_print ("\n");
- z = flen;
- while (z--)
- g_print (" ");
- newline = TRUE;
- } else
- newline = FALSE;
- }
-
- if (!newline)
- g_print ("\n");
-}
-
-static wmcbool
-send_command (TestComData *d,
- char *inbuf,
- size_t inbuf_len,
- size_t cmd_len)
-{
- int status;
- int eagain_count = 1000;
- size_t i = 0, sendlen;
- char sendbuf[600];
-
- if (d->debug)
- print_buf ("\nRAW>>>", inbuf, cmd_len);
-
- /* Encapsulate the data for the device */
- sendlen = wmc_encapsulate (inbuf, cmd_len, inbuf_len, sendbuf, sizeof (sendbuf), d->uml290);
- if (sendlen <= 0) {
- g_warning ("Failed to encapsulate WMC command");
- return FALSE;
- }
-
- if (d->debug)
- print_buf ("ENC>>>", sendbuf, sendlen);
-
- while (i < sendlen) {
- errno = 0;
- status = write (d->fd, &sendbuf[i], 1);
- if (status < 0) {
- if (errno == EAGAIN) {
- eagain_count--;
- if (eagain_count <= 0)
- return FALSE;
- } else
- g_assert (errno == 0);
- } else
- i++;
-
- usleep (1000);
- }
-
- return TRUE;
-}
-
-static size_t
-wait_reply (TestComData *d, char *buf, size_t len)
-{
- fd_set in;
- int result;
- struct timeval timeout = { 1, 0 };
- char readbuf[2048];
- ssize_t bytes_read;
- int total = 0, retries = 0;
- size_t decap_len = 0;
-
- FD_ZERO (&in);
- FD_SET (d->fd, &in);
- result = select (d->fd + 1, &in, NULL, NULL, &timeout);
- if (result != 1 || !FD_ISSET (d->fd, &in))
- return 0;
-
- do {
- errno = 0;
- bytes_read = read (d->fd, &readbuf[total], 1);
- if ((bytes_read == 0) || (errno == EAGAIN)) {
- /* Haven't gotten the async control char yet */
- if (retries > 20)
- return 0; /* 2 seconds, give up */
-
- /* Otherwise wait a bit and try again */
- usleep (100000);
- retries++;
- continue;
- } else if (bytes_read == 1) {
- wmcbool more = FALSE, success;
- size_t used = 0;
-
- total++;
- decap_len = 0;
- success = wmc_decapsulate (readbuf, total, buf, len, &decap_len, &used, &more, d->uml290);
-
- if (success && !more && d->debug)
- print_buf ("RAW<<<", readbuf, total);
-
- /* Discard used data */
- if (used > 0) {
- total -= used;
- memmove (readbuf, &readbuf[used], total);
- }
-
- if (success && !more) {
- /* Success; we have a packet */
- break;
- }
- } else {
- /* Some error occurred */
- return 0;
- }
- } while (total < sizeof (readbuf));
-
- if (d->debug)
- print_buf ("DCP<<<", buf, decap_len);
-
- return decap_len;
-}
-
-void
-test_com_port_init (void *f, void *data)
-{
- TestComData *d = data;
- int ret;
-
- ret = wmc_port_setup (d->fd);
- if (ret < 0)
- g_warning ("%s: error setting up serial port: (%d)", d->port, ret);
- g_assert_cmpint (ret, ==, 0);
-}
-
-void
-test_com_init (void *f, void *data)
-{
- TestComData *d = data;
- wmcbool success;
- char buf[512];
- gint len;
- WmcResult *result;
- size_t reply_len;
-
- len = wmc_cmd_init_new (buf, sizeof (buf), d->uml290);
- g_assert (len);
-
- /* Send the command */
- success = send_command (d, buf, sizeof (buf), len);
- g_assert (success);
-
- /* Get a response */
- reply_len = wait_reply (d, buf, sizeof (buf));
-
- /* Parse the response into a result structure */
- result = wmc_cmd_init_result (buf, reply_len, d->uml290);
- g_assert (result);
-
- wmc_result_unref (result);
-}
-
-void
-test_com_device_info (void *f, void *data)
-{
- TestComData *d = data;
- wmcbool success;
- char buf[2048];
- const char *str, *str2;
- gint len;
- WmcResult *result;
- size_t reply_len;
- guint32 u32;
-
- len = wmc_cmd_device_info_new (buf, sizeof (buf));
- g_assert (len == 2);
-
- /* Send the command */
- success = send_command (d, buf, sizeof (buf), len);
- g_assert (success);
-
- /* Get a response */
- reply_len = wait_reply (d, buf, sizeof (buf));
-
- /* Parse the response into a result structure */
- result = wmc_cmd_device_info_result (buf, reply_len);
- g_assert (result);
-
- g_print ("\n");
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_MANUFACTURER, &str);
- g_message ("%s: Manuf: %s", __func__, str);
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_MODEL, &str);
- g_message ("%s: Model: %s", __func__, str);
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_FW_REVISION, &str);
- g_message ("%s: FW Rev: %s", __func__, str);
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_HW_REVISION, &str);
- g_message ("%s: HW Rev: %s", __func__, str);
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_CDMA_MIN, &str);
- g_message ("%s: CDMA MIN: %s", __func__, str);
-
- u32 = 0;
- wmc_result_get_u32 (result, WMC_CMD_DEVICE_INFO_ITEM_HOME_SID, &u32);
- g_message ("%s: Home SID: %d", __func__, u32);
-
- u32 = 0;
- wmc_result_get_u32 (result, WMC_CMD_DEVICE_INFO_ITEM_PRL_VERSION, &u32);
- g_message ("%s: PRL Ver: %d", __func__, u32);
-
- u32 = 0;
- wmc_result_get_u32 (result, WMC_CMD_DEVICE_INFO_ITEM_ERI_VERSION, &u32);
- g_message ("%s: ERI Ver: %d", __func__, u32);
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_MEID, &str);
- g_message ("%s: MEID: %s", __func__, str ? str : "(none)");
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_IMEI, &str);
- g_message ("%s: IMEI: %s", __func__, str ? str : "(none)");
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_ICCID, &str);
- g_message ("%s: ICCID: %s", __func__, str ? str : "(none)");
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_MCC, &str);
- str2 = NULL;
- wmc_result_get_string (result, WMC_CMD_DEVICE_INFO_ITEM_MNC, &str2);
- g_message ("%s: MCC/MNC: %s %s", __func__,
- str ? str : "(none)",
- str2 ? str2 : "(none)");
-
- wmc_result_unref (result);
-}
-
-static const char *
-service_to_string (u_int8_t service)
-{
- switch (service) {
- case WMC_NETWORK_SERVICE_NONE:
- return "none";
- case WMC_NETWORK_SERVICE_AMPS:
- return "AMPS";
- case WMC_NETWORK_SERVICE_IS95A:
- return "IS95-A";
- case WMC_NETWORK_SERVICE_IS95B:
- return "IS95-B";
- case WMC_NETWORK_SERVICE_GSM:
- return "GSM";
- case WMC_NETWORK_SERVICE_GPRS:
- return "GPRS";
- case WMC_NETWORK_SERVICE_1XRTT:
- return "1xRTT";
- case WMC_NETWORK_SERVICE_EVDO_0:
- return "EVDO r0";
- case WMC_NETWORK_SERVICE_UMTS:
- return "UMTS";
- case WMC_NETWORK_SERVICE_EVDO_A:
- return "EVDO rA";
- case WMC_NETWORK_SERVICE_EDGE:
- return "EDGE";
- case WMC_NETWORK_SERVICE_HSDPA:
- return "HSDPA";
- case WMC_NETWORK_SERVICE_HSUPA:
- return "HSUPA";
- case WMC_NETWORK_SERVICE_HSPA:
- return "HSPA";
- case WMC_NETWORK_SERVICE_LTE:
- return "LTE";
- default:
- return "unknown";
- }
-}
-
-void
-test_com_network_info (void *f, void *data)
-{
- TestComData *d = data;
- wmcbool success;
- char buf[1024];
- const char *str;
- u_int8_t dbm, service;
- gint len;
- WmcResult *result;
- size_t reply_len;
-
- len = wmc_cmd_network_info_new (buf, sizeof (buf));
- g_assert (len == 2);
-
- /* Send the command */
- success = send_command (d, buf, sizeof (buf), len);
- g_assert (success);
-
- /* Get a response */
- reply_len = wait_reply (d, buf, sizeof (buf));
-
- /* Parse the response into a result structure */
- result = wmc_cmd_network_info_result (buf, reply_len);
- g_assert (result);
-
- g_print ("\n");
-
- service = 0;
- wmc_result_get_u8 (result, WMC_CMD_NETWORK_INFO_ITEM_SERVICE, &service);
- g_message ("%s: Service: %d (%s)", __func__, service, service_to_string (service));
-
- dbm = 0;
- wmc_result_get_u8 (result, WMC_CMD_NETWORK_INFO_ITEM_2G_DBM, &dbm);
- g_message ("%s: 2G dBm: -%d", __func__, dbm);
-
- dbm = 0;
- wmc_result_get_u8 (result, WMC_CMD_NETWORK_INFO_ITEM_3G_DBM, &dbm);
- g_message ("%s: 3G dBm: -%d", __func__, dbm);
-
- dbm = 0;
- wmc_result_get_u8 (result, WMC_CMD_NETWORK_INFO_ITEM_LTE_DBM, &dbm);
- g_message ("%s: LTE dBm: -%d", __func__, dbm);
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_NETWORK_INFO_ITEM_OPNAME, &str);
- g_message ("%s: Operator Name: %s", __func__, str ? str : "(none)");
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_NETWORK_INFO_ITEM_MCC, &str);
- g_message ("%s: MCC: %s", __func__, str ? str : "(none)");
-
- str = NULL;
- wmc_result_get_string (result, WMC_CMD_NETWORK_INFO_ITEM_MNC, &str);
- g_message ("%s: MNC: %s", __func__, str ? str : "(none)");
-
- wmc_result_unref (result);
-}
-
-static const char *
-mode_to_string (u_int8_t service)
-{
- switch (service) {
- case WMC_NETWORK_MODE_AUTO_CDMA:
- return "CDMA/EVDO";
- case WMC_NETWORK_MODE_CDMA_ONLY:
- return "CDMA only";
- case WMC_NETWORK_MODE_EVDO_ONLY:
- return "EVDO only";
- case WMC_NETWORK_MODE_AUTO_GSM:
- return "GSM/UMTS";
- case WMC_NETWORK_MODE_GPRS_ONLY:
- return "GSM/GPRS/EDGE only";
- case WMC_NETWORK_MODE_UMTS_ONLY:
- return "UMTS/HSPA only";
- case WMC_NETWORK_MODE_AUTO:
- return "Auto";
- case WMC_NETWORK_MODE_LTE_ONLY:
- return "LTE only";
- default:
- return "unknown";
- }
-}
-
-void
-test_com_get_global_mode (void *f, void *data)
-{
- TestComData *d = data;
- wmcbool success;
- char buf[1024];
- u_int8_t mode;
- gint len;
- WmcResult *result;
- size_t reply_len;
-
- len = wmc_cmd_get_global_mode_new (buf, sizeof (buf));
- g_assert (len == 3);
-
- /* Send the command */
- success = send_command (d, buf, sizeof (buf), len);
- g_assert (success);
-
- /* Get a response */
- reply_len = wait_reply (d, buf, sizeof (buf));
-
- /* Parse the response into a result structure */
- result = wmc_cmd_get_global_mode_result (buf, reply_len);
- g_assert (result);
-
- g_print ("\n");
-
- mode = 0;
- wmc_result_get_u8 (result, WMC_CMD_GET_GLOBAL_MODE_ITEM_MODE, &mode);
- g_message ("%s: Mode: %d (%s)", __func__, mode, mode_to_string (mode));
-
- wmc_result_unref (result);
-}
-
diff --git a/libwmc/tests/test-wmc-com.h b/libwmc/tests/test-wmc-com.h
deleted file mode 100644
index 3dc0a0dd..00000000
--- a/libwmc/tests/test-wmc-com.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2011 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef TEST_WMC_COM_H
-#define TEST_WMC_COM_H
-
-#include "utils.h"
-
-gpointer test_com_setup (const char *port, wmcbool uml290, wmcbool debug);
-void test_com_teardown (gpointer d);
-
-void test_com_port_init (void *f, void *data);
-
-void test_com_init (void *f, void *data);
-
-void test_com_device_info (void *f, void *data);
-
-void test_com_network_info (void *f, void *data);
-
-void test_com_get_global_mode (void *f, void *data);
-
-#endif /* TEST_WMC_COM_H */
-
diff --git a/libwmc/tests/test-wmc-crc.c b/libwmc/tests/test-wmc-crc.c
deleted file mode 100644
index 29650113..00000000
--- a/libwmc/tests/test-wmc-crc.c
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <string.h>
-
-#include "test-wmc-crc.h"
-#include "utils.h"
-
-void
-test_crc16_2 (void *f, void *data)
-{
- static const char buf[] = {
- 0x26, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00
- };
- guint16 crc;
- guint16 expected = 0x6D69;
-
- /* CRC check */
- crc = wmc_crc16 (buf, sizeof (buf), 0);
- g_assert (crc == expected);
-}
-
-void
-test_crc16_1 (void *f, void *data)
-{
- static const char buf[] = {
- 0x4b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f,
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
- };
- guint16 crc;
- guint16 expected = 0x097A;
-
- /* CRC check */
- crc = wmc_crc16 (buf, sizeof (buf), 0);
- g_assert (crc == expected);
-}
-
diff --git a/libwmc/tests/test-wmc-crc.h b/libwmc/tests/test-wmc-crc.h
deleted file mode 100644
index 4ce2871b..00000000
--- a/libwmc/tests/test-wmc-crc.h
+++ /dev/null
@@ -1,25 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef TEST_WMC_CRC_H
-#define TEST_WMC_CRC_H
-
-void test_crc16_2 (void *f, void *data);
-void test_crc16_1 (void *f, void *data);
-
-#endif /* TEST_WMC_CRC_H */
-
diff --git a/libwmc/tests/test-wmc-escaping.c b/libwmc/tests/test-wmc-escaping.c
deleted file mode 100644
index 3c4cd21f..00000000
--- a/libwmc/tests/test-wmc-escaping.c
+++ /dev/null
@@ -1,165 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <string.h>
-
-#include "test-wmc-escaping.h"
-#include "utils.h"
-
-static const char data1[] = {
- 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
- 0x0a, 0x6e, 0x6f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
- 0x6e, 0x74, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0a, 0x70, 0x68, 0x6f,
- 0x6e, 0x7e, 0x7e, 0x7e, 0x7d, 0x7d, 0x7e, 0x7d, 0x7e, 0x7d, 0x7e, 0x6e,
- 0x6b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x0a, 0x6f, 0x70,
- 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x6c,
- 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74,
- 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
- 0x74, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e,
- 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66,
- 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
- 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b,
- 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d,
- 0x6e, 0x6f, 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x0a,
- 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72,
- 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71,
- 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x69,
- 0x6c, 0x75, 0x72, 0x65, 0x0a, 0x73, 0x69, 0x6d, 0x62, 0x75, 0x73, 0x79,
- 0x0a, 0x73, 0x69, 0x6d, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x0a, 0x69, 0x6e,
- 0x63, 0x6f, 0x72, 0x72, 0x65, 0x63, 0x74, 0x70, 0x61, 0x73, 0x73, 0x77,
- 0x6f, 0x72, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x32, 0x72,
- 0x65, 0x71, 0x75, 0x69
-};
-
-static const char expected1[] = {
- 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65,
- 0x0a, 0x6e, 0x6f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
- 0x6e, 0x74, 0x6f, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x0a, 0x70, 0x68, 0x6f,
- 0x6e, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5d, 0x7d,
- 0x5e, 0x7d, 0x5d, 0x7d, 0x5e, 0x7d, 0x5d, 0x7d, 0x5e, 0x6e, 0x6b, 0x72,
- 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x6c, 0x6c, 0x6f,
- 0x77, 0x65, 0x64, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x6e, 0x6f, 0x74, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
- 0x64, 0x0a, 0x70, 0x68, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65,
- 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69,
- 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
- 0x0a, 0x70, 0x68, 0x66, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65,
- 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x6e, 0x6f,
- 0x74, 0x69, 0x6e, 0x73, 0x65, 0x72, 0x74, 0x65, 0x64, 0x0a, 0x73, 0x69,
- 0x6d, 0x70, 0x69, 0x6e, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
- 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x75, 0x6b, 0x72, 0x65, 0x71, 0x75, 0x69,
- 0x72, 0x65, 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x66, 0x61, 0x69, 0x6c, 0x75,
- 0x72, 0x65, 0x0a, 0x73, 0x69, 0x6d, 0x62, 0x75, 0x73, 0x79, 0x0a, 0x73,
- 0x69, 0x6d, 0x77, 0x72, 0x6f, 0x6e, 0x67, 0x0a, 0x69, 0x6e, 0x63, 0x6f,
- 0x72, 0x72, 0x65, 0x63, 0x74, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
- 0x64, 0x0a, 0x73, 0x69, 0x6d, 0x70, 0x69, 0x6e, 0x32, 0x72, 0x65, 0x71,
- 0x75, 0x69
-};
-
-void
-test_escape1 (void *f, void *data)
-{
- char escaped[1024];
- size_t len;
-
- /* Ensure that escaping in general works */
- len = hdlc_escape (data1, sizeof (data1), FALSE, escaped, sizeof (escaped));
- g_assert (len == 266);
- g_assert (len == sizeof (expected1));
- g_assert (memcmp (escaped, expected1, len) == 0);
-}
-
-static const char data2[] = {
- 0x4b, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x3f,
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff
-};
-
-void
-test_escape2 (void *f, void *data)
-{
- char escaped[1024];
- size_t len;
-
- /* Ensure that escaping data that doesn't need escaping works */
- len = hdlc_escape (data2, sizeof (data2), FALSE, escaped, sizeof (escaped));
- g_assert (len == sizeof (data2));
- g_assert (memcmp (escaped, data2, len) == 0);
-}
-
-static const char data_ctrl_src[] = {
- 0xc8, 0x0d, 0xda, 0x07, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x19, 0x00,
- 0x04, 0x00, 0x01, 0x00, 0x07, 0x88
-};
-
-static const char data_ctrl_expected[] = {
- 0xc8, 0x7d, 0x2d, 0xda, 0x7d, 0x27, 0x7d, 0x2c, 0x7d, 0x20, 0x7d, 0x34,
- 0x7d, 0x20, 0x7d, 0x30, 0x7d, 0x20, 0x7d, 0x39, 0x7d, 0x20, 0x7d, 0x24,
- 0x7d, 0x20, 0x7d, 0x21, 0x7d, 0x20, 0x7d, 0x27, 0x88
-};
-
-void
-test_escape_ctrl (void *f, void *data)
-{
- char escaped[1024];
- size_t len;
-
- len = hdlc_escape (data_ctrl_src, sizeof (data_ctrl_src), TRUE, escaped, sizeof (escaped));
- g_assert (len == sizeof (data_ctrl_expected));
- g_assert (memcmp (escaped, data_ctrl_expected, len) == 0);
-}
-
-void
-test_escape_unescape (void *f, void *data)
-{
- char escaped[512];
- char unescaped[512];
- size_t len, unlen;
- wmcbool escaping = FALSE;
-
- /* Ensure that escaping data that needs escaping, and then unescaping it,
- * produces the exact same data as was originally escaped.
- */
- len = hdlc_escape (data1, sizeof (data1), FALSE, escaped, sizeof (escaped));
- unlen = hdlc_unescape (escaped, len, unescaped, sizeof (unescaped), &escaping);
-
- g_assert (unlen == sizeof (data1));
- g_assert (memcmp (unescaped, data1, unlen) == 0);
-}
-
-void
-test_escape_unescape_ctrl (void *f, void *data)
-{
- char escaped[512];
- char unescaped[512];
- size_t len, unlen;
- wmcbool escaping = FALSE;
-
- /* Ensure that escaping data that needs escaping, and then unescaping it,
- * produces the exact same data as was originally escaped.
- */
- len = hdlc_escape (data_ctrl_src, sizeof (data_ctrl_src), TRUE, escaped, sizeof (escaped));
- g_assert (memcmp (escaped, data_ctrl_expected, len) == 0);
-
- unlen = hdlc_unescape (escaped, len, unescaped, sizeof (unescaped), &escaping);
- g_assert (unlen == sizeof (data_ctrl_src));
- g_assert (memcmp (unescaped, data_ctrl_src, unlen) == 0);
-}
-
diff --git a/libwmc/tests/test-wmc-escaping.h b/libwmc/tests/test-wmc-escaping.h
deleted file mode 100644
index 144c37bf..00000000
--- a/libwmc/tests/test-wmc-escaping.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef TEST_WMC_ESCAPING_H
-#define TEST_WMC_ESCAPING_H
-
-void test_escape1 (void *f, void *data);
-void test_escape2 (void *f, void *data);
-void test_escape_ctrl (void *f, void *data);
-void test_escape_unescape (void *f, void *data);
-void test_escape_unescape_ctrl (void *f, void *data);
-
-#endif /* TEST_WMC_ESCAPING_H */
-
diff --git a/libwmc/tests/test-wmc-utils.c b/libwmc/tests/test-wmc-utils.c
deleted file mode 100644
index 49d7351d..00000000
--- a/libwmc/tests/test-wmc-utils.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <string.h>
-
-#include "test-wmc-utils.h"
-#include "utils.h"
-
-static const char decap_inbuf[] = {
- 0x40, 0x03, 0x00, 0x01, 0x00, 0x19, 0xf0, 0x00, 0x16, 0x00, 0x21, 0x00,
- 0x1c, 0x00, 0xd8, 0x00, 0x3f, 0x00, 0x56, 0x01, 0x3f, 0x00, 0x15, 0x00,
- 0x1a, 0x00, 0x11, 0x01, 0x3f, 0x00, 0x92, 0x01, 0x3f, 0x00, 0x39, 0x00,
- 0x3f, 0x00, 0x95, 0x01, 0x3f, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x23, 0x01,
- 0x3f, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x0b, 0x01, 0x3f, 0x00, 0xae, 0x00,
- 0x3f, 0x00, 0x02, 0x01, 0x3f, 0x00, 0xa8, 0x00, 0x3f, 0x00, 0x50, 0x01,
- 0x3f, 0x00, 0xf8, 0x01, 0x3f, 0x00, 0x57, 0x00, 0x3f, 0x00, 0x7d, 0x5e,
- 0x00, 0x3f, 0x00, 0x93, 0x00, 0x3f, 0x00, 0xbd, 0x00, 0x3f, 0x00, 0x77,
- 0x01, 0x3f, 0x00, 0xb7, 0x00, 0x3f, 0x00, 0xab, 0x00, 0x3f, 0x00, 0x33,
- 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13,
- 0x13, 0x50, 0x1f, 0x00, 0x00, 0xff, 0xff, 0x00, 0xaa, 0x19, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0xc4, 0x7d, 0x5e,
- 0x7d, 0x5e, 0x7d, 0x5d, 0x5d, 0x04, 0x58, 0x1b, 0x5b, 0x1b, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x65, 0x69, 0x7e
-};
-
-void
-test_utils_decapsulate_basic_buffer (void *f, void *data)
-{
- wmcbool success;
- char outbuf[512];
- size_t decap_len = 0;
- size_t used = 0;
- wmcbool more = FALSE;
-
- success = hdlc_decapsulate_buffer (decap_inbuf, sizeof (decap_inbuf),
- FALSE, 0, outbuf, sizeof (outbuf),
- &decap_len, &used, &more);
- g_assert (success);
- g_assert (decap_len == 214);
- g_assert (used == 221);
- g_assert (more == FALSE);
-}
-
-
-static const char encap_outbuf[] = {
- 0x4b, 0x05, 0x08, 0x00, 0x01, 0xdd, 0x7e
-};
-
-void
-test_utils_encapsulate_basic_buffer (void *f, void *data)
-{
- char cmdbuf[10];
- char outbuf[512];
- size_t encap_len = 0;
-
- cmdbuf[0] = 0x4B; /* DIAG_CMD_SUBSYS */
- cmdbuf[1] = 0x05; /* DIAG_SUBSYS_HDR */
- cmdbuf[2] = 0x08; /* first byte of DIAG_SUBSYS_HDR_STATE_INFO in LE */
- cmdbuf[3] = 0x00; /* second byte of DIAG_SUBSYS_HDR_STATE_INFO in LE */
-
- encap_len = hdlc_encapsulate_buffer (cmdbuf, 4, sizeof (cmdbuf),
- 0, TRUE, FALSE,
- &outbuf[0], sizeof (outbuf));
- g_assert (encap_len == sizeof (encap_outbuf));
- g_assert (memcmp (outbuf, encap_outbuf, encap_len) == 0);
-}
-
-static const char cns_inbuf[] = {
- 0x00, 0x0a, 0x6b, 0x74, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x7e
-};
-
-void
-test_utils_decapsulate_sierra_cns (void *f, void *data)
-{
- wmcbool success;
- char outbuf[512];
- size_t decap_len = 0;
- size_t used = 0;
- wmcbool more = FALSE;
-
- success = hdlc_decapsulate_buffer (cns_inbuf, sizeof (cns_inbuf),
- FALSE, 0, outbuf, sizeof (outbuf),
- &decap_len, &used, &more);
- g_assert (success == FALSE);
-}
-
-
-static const char uml290_encap_src[] = {
- 0xc8, 0x0d, 0xda, 0x07, 0x0c, 0x00, 0x14, 0x00, 0x10, 0x00, 0x19, 0x00,
- 0x04, 0x00, 0x01, 0x00, 0x07, 0x88
-};
-
-static const char uml290_encap_outbuf[] = {
- 0x41, 0x54, 0x2a, 0x57, 0x4d, 0x43, 0x3d, 0xc8, 0x7d, 0x2d, 0xda, 0x7d,
- 0x27, 0x7d, 0x2c, 0x7d, 0x20, 0x7d, 0x34, 0x7d, 0x20, 0x7d, 0x30, 0x7d,
- 0x20, 0x7d, 0x39, 0x7d, 0x20, 0x7d, 0x24, 0x7d, 0x20, 0x7d, 0x21, 0x7d,
- 0x20, 0x7d, 0x27, 0x88, 0x0d
-};
-
-void
-test_utils_encapsulate_uml290_wmc1 (void *f, void *data)
-{
- char inbuf[512];
- char outbuf[512];
- size_t encap_len = 0;
-
- memcpy (inbuf, uml290_encap_src, sizeof (uml290_encap_src));
- encap_len = wmc_encapsulate (inbuf, sizeof (uml290_encap_src),
- sizeof (inbuf), outbuf, sizeof (outbuf), TRUE);
- g_assert (encap_len == sizeof (encap_outbuf));
- g_assert (memcmp (outbuf, encap_outbuf, encap_len) == 0);
-}
-
-static const char uml290_src[] = {
- 0xc8, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xda, 0x07, 0x0c, 0x00,
- 0x14, 0x00, 0x12, 0x00, 0x19, 0x00, 0x06, 0x00, 0xc2, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x7d, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x5d, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x00, 0x00, 0x01, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39,
- 0x00, 0x00, 0x00, 0x40, 0x06, 0x00, 0x00, 0x30, 0x30, 0x7e
-};
-
-static const char uml290_expected[] = {
- 0xc8, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0xda, 0x07, 0x0c, 0x00,
- 0x14, 0x00, 0x12, 0x00, 0x19, 0x00, 0x06, 0x00, 0xc2, 0x02, 0x00, 0x00,
- 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x56, 0x65, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x20, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00,
- 0x00, 0x40, 0x06, 0x00, 0x00
-};
-
-void
-test_utils_decapsulate_uml290_wmc1 (void *f, void *data)
-{
- wmcbool success;
- char outbuf[512];
- size_t decap_len = 0;
- size_t used = 0;
- wmcbool more = FALSE;
-
- success = hdlc_decapsulate_buffer (uml290_src, sizeof (uml290_src),
- TRUE, 0x3030, outbuf, sizeof (outbuf),
- &decap_len, &used, &more);
- g_assert (success == TRUE);
- g_assert (more == 0);
- g_assert_cmpint (used, ==, sizeof (uml290_src));
- g_assert_cmpint (decap_len, ==, sizeof (uml290_expected));
- g_assert_cmpint (memcmp (outbuf, uml290_expected, decap_len), ==, 0);
-}
-
-
-static const char pc5740_src[] = {
- 0xc8, 0x0b, 0x17, 0x00, 0x00, 0x00, 0x06, 0x00, 0xdb, 0x07, 0x06, 0x00,
- 0x11, 0x00, 0x0d, 0x00, 0x2d, 0x00, 0x10, 0x00, 0xe4, 0x03, 0xd4, 0xfe,
- 0xff, 0xff, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0e, 0x92, 0x7e
-};
-
-static const char pc5740_expected[] = {
- 0xc8, 0x0b, 0x17, 0x00, 0x00, 0x00, 0x06, 0x00, 0xdb, 0x07, 0x06, 0x00,
- 0x11, 0x00, 0x0d, 0x00, 0x2d, 0x00, 0x10, 0x00, 0xe4, 0x03, 0xd4, 0xfe,
- 0xff, 0xff, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00
-};
-
-void
-test_utils_decapsulate_pc5740_wmc1 (void *f, void *data)
-{
- wmcbool success;
- char outbuf[512];
- size_t decap_len = 0;
- size_t used = 0;
- wmcbool more = FALSE;
-
- success = hdlc_decapsulate_buffer (pc5740_src, sizeof (pc5740_src),
- FALSE, 0, outbuf, sizeof (outbuf),
- &decap_len, &used, &more);
- g_assert (success == TRUE);
- g_assert (more == 0);
- g_assert_cmpint (used, ==, sizeof (pc5740_src));
- g_assert_cmpint (decap_len, ==, sizeof (pc5740_expected));
- g_assert_cmpint (memcmp (outbuf, pc5740_expected, decap_len), ==, 0);
-}
-
diff --git a/libwmc/tests/test-wmc-utils.h b/libwmc/tests/test-wmc-utils.h
deleted file mode 100644
index 96427068..00000000
--- a/libwmc/tests/test-wmc-utils.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifndef TEST_WMC_UTILS_H
-#define TEST_WMC_UTILS_H
-
-void test_utils_decapsulate_basic_buffer (void *f, void *data);
-
-void test_utils_encapsulate_basic_buffer (void *f, void *data);
-
-void test_utils_decapsulate_sierra_cns (void *f, void *data);
-
-void test_utils_encapsulate_uml290_wmc1 (void *f, void *data);
-
-void test_utils_decapsulate_uml290_wmc1 (void *f, void *data);
-
-void test_utils_decapsulate_pc5740_wmc1 (void *f, void *data);
-
-#endif /* TEST_WMC_UTILS_H */
-
diff --git a/libwmc/tests/test-wmc.c b/libwmc/tests/test-wmc.c
deleted file mode 100644
index e1027b2e..00000000
--- a/libwmc/tests/test-wmc.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * Copyright (C) 2010 Red Hat, Inc.
- *
- * This program is free software: you can redistribute it and/or
- * modify it under the terms of version 2 of the GNU General Public
- * License as published by the Free Software Foundation
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <glib.h>
-#include <string.h>
-
-#include "test-wmc-crc.h"
-#include "test-wmc-escaping.h"
-#include "test-wmc-utils.h"
-#include "test-wmc-com.h"
-
-typedef struct {
- gpointer com_data;
-} TestData;
-
-typedef GTestFixtureFunc TCFunc;
-
-#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
-
-static TestData *
-test_data_new (const char *port, gboolean uml290, gboolean debug)
-{
- TestData *d;
-
- d = g_malloc0 (sizeof (TestData));
- g_assert (d);
-
- if (port)
- d->com_data = test_com_setup (port, uml290, debug);
-
- return d;
-}
-
-static void
-test_data_free (TestData *d)
-{
- if (d->com_data)
- test_com_teardown (d->com_data);
-
- g_free (d);
-}
-
-int main (int argc, char **argv)
-{
- GTestSuite *suite;
- TestData *data;
- int i;
- const char *port = NULL;
- gint result;
- gboolean uml290 = FALSE, debug = FALSE;
-
- g_test_init (&argc, &argv, NULL);
-
- /* See if we got passed a serial port for live testing */
- for (i = 0; i < argc; i++) {
- if (!strcmp (argv[i], "--port")) {
- /* Make sure there's actually a port in the next arg */
- g_assert (argc > i + 1);
- port = argv[++i];
- } else if (!strcmp (argv[i], "--uml290"))
- uml290 = TRUE;
- else if (!strcmp (argv[i], "--debug"))
- debug = TRUE;
- }
-
- data = test_data_new (port, uml290, debug);
-
- suite = g_test_get_root ();
- g_test_suite_add (suite, TESTCASE (test_crc16_1, NULL));
- g_test_suite_add (suite, TESTCASE (test_crc16_2, NULL));
- g_test_suite_add (suite, TESTCASE (test_escape1, NULL));
- g_test_suite_add (suite, TESTCASE (test_escape2, NULL));
- g_test_suite_add (suite, TESTCASE (test_escape_ctrl, NULL));
- g_test_suite_add (suite, TESTCASE (test_escape_unescape, NULL));
- g_test_suite_add (suite, TESTCASE (test_escape_unescape_ctrl, NULL));
- g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_basic_buffer, NULL));
- g_test_suite_add (suite, TESTCASE (test_utils_encapsulate_basic_buffer, NULL));
- g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_sierra_cns, NULL));
- g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_uml290_wmc1, NULL));
- g_test_suite_add (suite, TESTCASE (test_utils_decapsulate_pc5740_wmc1, NULL));
-
- /* Live tests */
- if (port) {
- g_test_suite_add (suite, TESTCASE (test_com_port_init, data->com_data));
- g_test_suite_add (suite, TESTCASE (test_com_init, data->com_data));
- g_test_suite_add (suite, TESTCASE (test_com_device_info, data->com_data));
- g_test_suite_add (suite, TESTCASE (test_com_network_info, data->com_data));
- g_test_suite_add (suite, TESTCASE (test_com_get_global_mode, data->com_data));
- }
-
- result = g_test_run ();
-
- test_data_free (data);
-
- return result;
-}
diff --git a/libwmc/uml290.txt b/libwmc/uml290.txt
deleted file mode 100644
index 5a6113a6..00000000
--- a/libwmc/uml290.txt
+++ /dev/null
@@ -1,206 +0,0 @@
-This document describes information about the Pantech UML290 and the WMC
-protocol observed through USB packet capture and other investigation.
-
-
-Pantech UML290 Notes
---------------------------------------
-
-This device exposes 4 USB interfaces. They are, in no particular order, a
-CDC-ACM compatible AT command port, a QCDM/Diag port, an WMC port, and a
-raw IP network port. The modem's native command interface is the WMC port
-which the Windows driver uses for all normal communication.
-
-
-CDC-ACM AT Port
-----------------
-
-The modem's +GCAP response reports:
-
-+GCAP: +CIS707-A, CIS-856, CIS-856-A, +CGSM, +CLTE1
-
-and with recent firmware updates (L0290VWB333F.230 [Mar 15 2011 15:03:20] or
-later) the device does, in fact, appear to support common IS-707-A and ETSI
-27.007 GSM and LTE AT commands. This interface does support PPP data but when
-PPP is used the device does not support handoffs between LTE and EVDO.
-
-To support seamless operation of devices between LTE and EVDO Verizon has
-upgraded their network to support the eHRPD protocol. Older, non-LTE capable
-devices usually do not include support for eHRPD and use the standard HRPD
-protocols. LTE-capable devices support both eHRPD and standard HRPD, but at
-least with the UML290, connections to the 3G EVDO network using direct PPP over
-the AT modem port do not use eHRPD. Thus to successfully connect to the 3G
-EVDO network, the modem must be switched into standard HRPD mode by changing
-the value of the NV_HDRSCP_FORCE_AT_CONFIG_I NVRAM item using the the QCDM/Diag
-port and the DIAG protocol. Use of HRPD only prevents connections to the LTE
-network. It is possible that connections initiated using the WMC port and
-utilizing the "raw IP" network interface do not have this problem.
-
-
-QCDM/Diag Port
-----------------
-
-This port is a normal QCDM/Diag port and responds to DIAG commands.
-
-
-Raw IP Network Port
--------------------
-
-This USB interface is the normal network interface port the device uses in
-Windows for network communication. It appears to operate in a "raw IP" mode
-where raw IP packets are sent and received over USB with no additional framing
-or encapsulation. The IPv4 and IPv6 addresses for the interface are determined
-using WMC commands on the WMC port, not through the AT command port. The AT
-command port only supports PPP-based communication. More information about the
-"raw IP" mode may be available in the Qualcomm CodeAurora SMD/QMI drivers which
-implement a "raw IP" network communication mode for various MSM7xxx chipsets
-used in Android devices.
-
-
-WMC Port
------------
-
-This port accepts and responds to WMC protocol requests. Instead of using plain
-WMC however, requests are prefixed with the string "AT*WMC=" and terminated with
-a newline (0x0D) character instead of the normal WMC frame termination
-character (0x7E). The data in between is normal binary WMC data, except that
-all bytes less than 0x20 are escaped using normal HDLC/PPP escaping while a
-normal WMC request would only escape the special HDLC/PPP characters of 0x7E and
-0x7D. Thus a UML290 request looks like this in hexadecimal:
-
-41542a574d433dc87d2a87b80d
-
-This "AT"-style framing has not been observed on other devices.
-
-
-
-WMC Protocol Framing
---------------------
-
-The protocol is a request/response style protocol though unsolicited responses
-are sometimes sent from the modem to the host. There does not appear to be any
-sequence numbering in either the request or response packets. WMC packets
-always begin with the frame start marker (0xC8). The second byte is the command
-number, followed by the frame's data. The frame ends with a three-byte trailer
-including a CRC-16 and a frame termination marker which differs between devices.
-Most older devices use the standard HDLC/PPP frame termination marker (0x7E),
-while some others (UML290) use a different termination marker as described
-below. Thus a normal WMC packet looks like this:
-
-0xC8 <command number> <data> <CRC-16> 0x7E
-
-The entire frame (exclusive of the termination marker) is escaped using standard
-HDLC/PPP escaping mechanisms to ensure that the bytes 0x7E and 0x7D never appear
-in the packet. Some devices (UML290) escape more than the standard HDLC escape
-characters.
-
-Frames can span multiple USB packets. This behavior has been observed with the
-PC5740 in various responses, in which case the frame is simply split up between
-USB packets. It has also been observed with some UML290 requests, where it
-appears that in addition to splitting the frame, each segment after the first
-is prefixed with 0x20.
-
-For example, a split response from a PC5740:
-
-* c8 06 0f 0f 0f 0f 0f 0f 0f 0f 0f 0f 00 00 00 00 ................
- ...
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
-
-* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- ...
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
-
-* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- ...
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
-
-* 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- ...
- 00 00 00 01 ....
-
-
-and an split request from a UML290:
-
-* c8 56 86 02 00 00 00 00 00 00 39 30 30 30 38 30 .V........900080
- ...
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..............
-
-* 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
- ...
- 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
-
-* 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ...............
- ...
- 00 00 00 00 00 00 00 00 00 00 5b 1c 0d ..........[..
-
-
-
-Requests
----------------
-
-Requests are usually short, often only 4 or 5 bytes long, including the frame
-start marker (0xC8), the command number, the 16-bit CRC, and the frame termination
-marker. Requests almost always receive a response from the modem containing the
-same command number.
-
-The UML290 uses different "AT"-style framing of requests, prefixing the
-request with "AT*WMC=" and using a frame termination marker of 0x0D instead
-of the standard 0x7E. The UML290 also uses a different CRC-16 initial seed of
-0xAAFE instead of the standard 0xFFFF used on other devices and with HDLC
-framing in general. For added lolz the UML290 HDLC-escapes all control
-characters in the request in addition to the standard 0x7D and 0x7E characters
-escaped in HDLC.
-
-Thus a normal WMC request (from a PC5740) looks like this:
-
- c8 0a 77 a4 7e
-<frame start> <cmd no> <CRC-16> <terminator>
-
-while the same request from the UML290 looks like this:
-
-41542a574d433d c8 7d2a 87 b8 0d
- <AT*WMC=> <frame start> <cmd no> <CRC-16> <terminator>
-
-Thus after removing all framing and escaping, this WMC request is a single
-byte (0x0A) which indicates the command number of the request.
-
-
-WMC Responses and Unsolicited Messages
---------------------------------------
-
-Responses begin with the WMC frame start marker (0xC8) and end with the standard
-HDLC/PPP frame terminator (0x7E) in all observed cases, even on the UML290.
-The data in between is HDLC/PPP escaped and there is a CRC-16 before the frame
-terminator. Not all devices use the same CRC-16 calculation however; the
-UML290 always uses a CRC-16 of 0x3030, while the PC5740 includes a valid CRC-16
-using a standard polynomial of 0x8408 and an initial seed of 0xFFFF. Responses
-may span multiple USB packets if they are large enough, but the frame terminator
-provides a convenient mechanism for detecting when the frame is complete.
-
-A standard WMC response (from a UML290) looks like this:
-
-c80d0000000030307e
-
-which translates to:
-
-c8 0d 00 00 00 00
-
-
-WMC Command Numbers
--------------------
-
-These command numbers have been observed and minimally investigated:
-
-0x06: request device information, including manufacturer, model, firmware
- revision, hardware revision, MCC/MNC, serial number
-
-0x0A: request IP configuration when connected (IPv4 and IPv6) on the UML290; may
- include elapsed connected time or traffic byte counts too. Request has
- been observed on the PC5740 but is significantly shorter.
-
-0x0B: get status including operator name, RSSI dBm, and possibly registration
- status
-
-0x13: retrieve SMS messages
-
-0x4D: appears to request EPS bearer configuration; response includes the APN
-
diff --git a/m4/ax_append_compile_flags.m4 b/m4/ax_append_compile_flags.m4
new file mode 100644
index 00000000..9c856356
--- /dev/null
+++ b/m4/ax_append_compile_flags.m4
@@ -0,0 +1,46 @@
+# ============================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# For every FLAG1, FLAG2 it is checked whether the compiler works with the
+# flag. If it does, the flag is added FLAGS-VARIABLE
+#
+# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+# CFLAGS) is used. During the check the flag is always added to the
+# current language's flags.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+# NOTE: This macro depends on the AX_APPEND_FLAG and
+# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
+# AX_APPEND_LINK_FLAGS.
+#
+# LICENSE
+#
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 7
+
+AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
+[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
+AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+for flag in $1; do
+ AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4])
+done
+])dnl AX_APPEND_COMPILE_FLAGS
diff --git a/m4/ax_append_flag.m4 b/m4/ax_append_flag.m4
new file mode 100644
index 00000000..dd6d8b61
--- /dev/null
+++ b/m4/ax_append_flag.m4
@@ -0,0 +1,50 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_append_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
+#
+# DESCRIPTION
+#
+# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
+# added in between.
+#
+# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
+# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
+# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
+# FLAG.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 8
+
+AC_DEFUN([AX_APPEND_FLAG],
+[dnl
+AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
+AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
+AS_VAR_SET_IF(FLAGS,[
+ AS_CASE([" AS_VAR_GET(FLAGS) "],
+ [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
+ [
+ AS_VAR_APPEND(FLAGS,[" $1"])
+ AC_RUN_LOG([: FLAGS="$FLAGS"])
+ ])
+ ],
+ [
+ AS_VAR_SET(FLAGS,[$1])
+ AC_RUN_LOG([: FLAGS="$FLAGS"])
+ ])
+AS_VAR_POPDEF([FLAGS])dnl
+])dnl AX_APPEND_FLAG
diff --git a/m4/ax_append_link_flags.m4 b/m4/ax_append_link_flags.m4
new file mode 100644
index 00000000..99b9fa5b
--- /dev/null
+++ b/m4/ax_append_link_flags.m4
@@ -0,0 +1,44 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_append_link_flags.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_APPEND_LINK_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# For every FLAG1, FLAG2 it is checked whether the linker works with the
+# flag. If it does, the flag is added FLAGS-VARIABLE
+#
+# If FLAGS-VARIABLE is not specified, the linker's flags (LDFLAGS) is
+# used. During the check the flag is always added to the linker's flags.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+# NOTE: This macro depends on the AX_APPEND_FLAG and AX_CHECK_LINK_FLAG.
+# Please keep this macro in sync with AX_APPEND_COMPILE_FLAGS.
+#
+# LICENSE
+#
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 7
+
+AC_DEFUN([AX_APPEND_LINK_FLAGS],
+[AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+for flag in $1; do
+ AX_CHECK_LINK_FLAG([$flag], [AX_APPEND_FLAG([$flag], [m4_default([$2], [LDFLAGS])])], [], [$3], [$4])
+done
+])dnl AX_APPEND_LINK_FLAGS
diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4
new file mode 100644
index 00000000..bd753b34
--- /dev/null
+++ b/m4/ax_check_compile_flag.m4
@@ -0,0 +1,53 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the current language's compiler
+# or gives an error. (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the current language's default
+# flags (e.g. CFLAGS) when the check is done. The check is thus made with
+# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
+# force the compiler to issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_COMPILE_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
+AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
+ ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
+ _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
+ AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_COMPILE_FLAGS
diff --git a/m4/ax_check_link_flag.m4 b/m4/ax_check_link_flag.m4
new file mode 100644
index 00000000..03a30ce4
--- /dev/null
+++ b/m4/ax_check_link_flag.m4
@@ -0,0 +1,53 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_check_link_flag.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CHECK_LINK_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
+#
+# DESCRIPTION
+#
+# Check whether the given FLAG works with the linker or gives an error.
+# (Warnings, however, are ignored)
+#
+# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
+# success/failure.
+#
+# If EXTRA-FLAGS is defined, it is added to the linker's default flags
+# when the check is done. The check is thus made with the flags: "LDFLAGS
+# EXTRA-FLAGS FLAG". This can for example be used to force the linker to
+# issue an error when a bad flag is given.
+#
+# INPUT gives an alternative input source to AC_LINK_IFELSE.
+#
+# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
+# macro in sync with AX_CHECK_{PREPROC,COMPILE}_FLAG.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
+# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 6
+
+AC_DEFUN([AX_CHECK_LINK_FLAG],
+[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
+AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_ldflags_$4_$1])dnl
+AC_CACHE_CHECK([whether the linker accepts $1], CACHEVAR, [
+ ax_check_save_flags=$LDFLAGS
+ LDFLAGS="$LDFLAGS $4 $1"
+ AC_LINK_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
+ [AS_VAR_SET(CACHEVAR,[yes])],
+ [AS_VAR_SET(CACHEVAR,[no])])
+ LDFLAGS=$ax_check_save_flags])
+AS_VAR_IF(CACHEVAR,yes,
+ [m4_default([$2], :)],
+ [m4_default([$3], :)])
+AS_VAR_POPDEF([CACHEVAR])dnl
+])dnl AX_CHECK_LINK_FLAGS
diff --git a/m4/ax_code_coverage.m4 b/m4/ax_code_coverage.m4
new file mode 100644
index 00000000..2751459c
--- /dev/null
+++ b/m4/ax_code_coverage.m4
@@ -0,0 +1,229 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_code_coverage.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CODE_COVERAGE()
+#
+# DESCRIPTION
+#
+# Defines CODE_COVERAGE_CFLAGS and CODE_COVERAGE_LDFLAGS which should be
+# included in the CFLAGS and LIBS/LDFLAGS variables of every build target
+# (program or library) which should be built with code coverage support.
+# Also defines CODE_COVERAGE_RULES which should be substituted in your
+# Makefile; and $enable_code_coverage which can be used in subsequent
+# configure output. CODE_COVERAGE_ENABLED is defined and substituted, and
+# corresponds to the value of the --enable-code-coverage option, which
+# defaults to being disabled.
+#
+# Test also for gcov program and create GCOV variable that could be
+# substituted.
+#
+# Note that all optimisation flags in CFLAGS must be disabled when code
+# coverage is enabled.
+#
+# Usage example:
+#
+# configure.ac:
+#
+# AX_CODE_COVERAGE
+#
+# Makefile.am:
+#
+# @CODE_COVERAGE_RULES@
+# my_program_LIBS = ... $(CODE_COVERAGE_LDFLAGS) ...
+# my_program_CFLAGS = ... $(CODE_COVERAGE_CFLAGS) ...
+#
+# This results in a "check-code-coverage" rule being added to any
+# Makefile.am which includes "@CODE_COVERAGE_RULES@" (assuming the module
+# has been configured with --enable-code-coverage). Running `make
+# check-code-coverage` in that directory will run the module's test suite
+# (`make check`) and build a code coverage report detailing the code which
+# was touched, then print the URI for the report.
+#
+# This code was derived from Makefile.decl in GLib, originally licenced
+# under LGPLv2.1+.
+#
+# LICENSE
+#
+# Copyright (c) 2012 Philip Withnall
+# Copyright (c) 2012 Xan Lopez
+# Copyright (c) 2012 Christian Persch
+# Copyright (c) 2012 Paolo Borelli
+# Copyright (c) 2012 Dan Winship
+# Copyright (c) 2015 Bastien ROUCARIES
+#
+# This library is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or (at
+# your option) any later version.
+#
+# This library is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
+# General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+#serial 5
+
+AC_DEFUN([AX_CODE_COVERAGE],[
+ dnl Check for --enable-code-coverage
+ AC_REQUIRE([AC_PROG_SED])
+
+ # allow to override gcov location
+ AC_ARG_WITH([gcov],
+ [AS_HELP_STRING([--with-gcov[=GCOV]], [use given GCOV for coverage (GCOV=gcov).])],
+ [_AX_CODE_COVERAGE_GCOV_PROG_WITH=$with_gcov],
+ [_AX_CODE_COVERAGE_GCOV_PROG_WITH=gcov])
+
+ AC_MSG_CHECKING([whether to build with code coverage support])
+ AC_ARG_ENABLE([code-coverage],
+ AS_HELP_STRING([--enable-code-coverage],
+ [Whether to enable code coverage support]),,
+ enable_code_coverage=no)
+
+ AM_CONDITIONAL([CODE_COVERAGE_ENABLED], [test x$enable_code_coverage = xyes])
+ AC_SUBST([CODE_COVERAGE_ENABLED], [$enable_code_coverage])
+ AC_MSG_RESULT($enable_code_coverage)
+
+ AS_IF([ test "$enable_code_coverage" = "yes" ], [
+ # check for gcov
+ AC_CHECK_TOOL([GCOV],
+ [$_AX_CODE_COVERAGE_GCOV_PROG_WITH],
+ [:])
+ AS_IF([test "X$GCOV" = "X:"],
+ [AC_MSG_ERROR([gcov is needed to do coverage])])
+ AC_SUBST([GCOV])
+
+ dnl Check if gcc is being used
+ AS_IF([ test "$GCC" = "no" ], [
+ AC_MSG_ERROR([not compiling with gcc, which is required for gcov code coverage])
+ ])
+
+ # List of supported lcov versions.
+ lcov_version_list="1.6 1.7 1.8 1.9 1.10 1.11"
+
+ AC_CHECK_PROG([LCOV], [lcov], [lcov])
+ AC_CHECK_PROG([GENHTML], [genhtml], [genhtml])
+
+ AS_IF([ test "$LCOV" ], [
+ AC_CACHE_CHECK([for lcov version], ax_cv_lcov_version, [
+ ax_cv_lcov_version=invalid
+ lcov_version=`$LCOV -v 2>/dev/null | $SED -e 's/^.* //'`
+ for lcov_check_version in $lcov_version_list; do
+ if test "$lcov_version" = "$lcov_check_version"; then
+ ax_cv_lcov_version="$lcov_check_version (ok)"
+ fi
+ done
+ ])
+ ], [
+ lcov_msg="To enable code coverage reporting you must have one of the following lcov versions installed: $lcov_version_list"
+ AC_MSG_ERROR([$lcov_msg])
+ ])
+
+ case $ax_cv_lcov_version in
+ ""|invalid[)]
+ lcov_msg="You must have one of the following versions of lcov: $lcov_version_list (found: $lcov_version)."
+ AC_MSG_ERROR([$lcov_msg])
+ LCOV="exit 0;"
+ ;;
+ esac
+
+ AS_IF([ test -z "$GENHTML" ], [
+ AC_MSG_ERROR([Could not find genhtml from the lcov package])
+ ])
+
+ dnl Build the code coverage flags
+ CODE_COVERAGE_CFLAGS="-O0 -g -fprofile-arcs -ftest-coverage"
+ CODE_COVERAGE_LDFLAGS="-lgcov"
+
+ AC_SUBST([CODE_COVERAGE_CFLAGS])
+ AC_SUBST([CODE_COVERAGE_LDFLAGS])
+ ])
+
+CODE_COVERAGE_RULES='
+# Code coverage
+#
+# Optional:
+# - CODE_COVERAGE_DIRECTORY: Top-level directory for code coverage reporting.
+# (Default: $(top_builddir))
+# - CODE_COVERAGE_OUTPUT_FILE: Filename and path for the .info file generated
+# by lcov for code coverage. (Default:
+# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info)
+# - CODE_COVERAGE_OUTPUT_DIRECTORY: Directory for generated code coverage
+# reports to be created. (Default:
+# $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage)
+# - CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH: --gcov-tool pathtogcov
+# - CODE_COVERAGE_LCOV_OPTIONS_DEFAULT: Extra options to pass to the lcov instance.
+# (Default: $CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
+# - CODE_COVERAGE_LCOV_OPTIONS: Extra options to pass to the lcov instance.
+# (Default: $CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
+# - CODE_COVERAGE_GENHTML_OPTIONS: Extra options to pass to the genhtml
+# instance. (Default: empty)
+# - CODE_COVERAGE_IGNORE_PATTERN: Extra glob pattern of files to ignore
+#
+# The generated report will be titled using the $(PACKAGE_NAME) and
+# $(PACKAGE_VERSION). In order to add the current git hash to the title,
+# use the git-version-gen script, available online.
+
+# Optional variables
+CODE_COVERAGE_DIRECTORY ?= $(top_builddir)
+CODE_COVERAGE_OUTPUT_FILE ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage.info
+CODE_COVERAGE_OUTPUT_DIRECTORY ?= $(PACKAGE_NAME)-$(PACKAGE_VERSION)-coverage
+CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH ?= --gcov-tool "$(GCOV)"
+CODE_COVERAGE_LCOV_OPTIONS_DEFAULT ?= $(CODE_COVERAGE_LCOV_OPTIONS_GCOVPATH)
+CODE_COVERAGE_LCOV_OPTIONS ?= $(CODE_COVERAGE_LCOV_OPTIONS_DEFAULT)
+CODE_COVERAGE_GENHTML_OPTIONS ?=
+CODE_COVERAGE_IGNORE_PATTERN ?=
+
+code_coverage_quiet = $(code_coverage_quiet_$(V))
+code_coverage_quiet_ = $(code_coverage_quiet_$(AM_DEFAULT_VERBOSITY))
+code_coverage_quiet_0 = --quiet
+
+# Use recursive makes in order to ignore errors during check
+check-code-coverage:
+ifeq ($(CODE_COVERAGE_ENABLED),yes)
+ -$(MAKE) $(AM_MAKEFLAGS) -k check
+ $(MAKE) $(AM_MAKEFLAGS) code-coverage-capture
+else
+ @echo "Need to reconfigure with --enable-code-coverage"
+endif
+
+# Capture code coverage data
+code-coverage-capture: code-coverage-capture-hook
+ifeq ($(CODE_COVERAGE_ENABLED),yes)
+ $(LCOV) $(code_coverage_quiet) --directory $(CODE_COVERAGE_DIRECTORY) --capture --output-file "$(CODE_COVERAGE_OUTPUT_FILE).tmp" --test-name "$(PACKAGE_NAME)-$(PACKAGE_VERSION)" --no-checksum --compat-libtool $(CODE_COVERAGE_LCOV_OPTIONS)
+ $(LCOV) $(code_coverage_quiet) --directory $(CODE_COVERAGE_DIRECTORY) --remove "$(CODE_COVERAGE_OUTPUT_FILE).tmp" "/tmp/*" $(CODE_COVERAGE_IGNORE_PATTERN) --output-file "$(CODE_COVERAGE_OUTPUT_FILE)"
+ -@rm -f $(CODE_COVERAGE_OUTPUT_FILE).tmp
+ LANG=C $(GENHTML) $(code_coverage_quiet) --prefix $(CODE_COVERAGE_DIRECTORY) --output-directory "$(CODE_COVERAGE_OUTPUT_DIRECTORY)" --title "$(PACKAGE_NAME)-$(PACKAGE_VERSION) Code Coverage" --legend --show-details "$(CODE_COVERAGE_OUTPUT_FILE)" $(CODE_COVERAGE_GENHTML_OPTIONS)
+ @echo "file://$(abs_builddir)/$(CODE_COVERAGE_OUTPUT_DIRECTORY)/index.html"
+else
+ @echo "Need to reconfigure with --enable-code-coverage"
+endif
+
+# Hook rule executed before code-coverage-capture, overridable by the user
+code-coverage-capture-hook:
+
+ifeq ($(CODE_COVERAGE_ENABLED),yes)
+clean: code-coverage-clean
+code-coverage-clean:
+ -$(LCOV) --directory $(top_builddir) -z
+ -rm -rf $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_FILE).tmp $(CODE_COVERAGE_OUTPUT_DIRECTORY)
+ -find . -name "*.gcda" -o -name "*.gcov" -delete
+endif
+
+GITIGNOREFILES ?=
+GITIGNOREFILES += $(CODE_COVERAGE_OUTPUT_FILE) $(CODE_COVERAGE_OUTPUT_DIRECTORY)
+
+DISTCHECK_CONFIGURE_FLAGS ?=
+DISTCHECK_CONFIGURE_FLAGS += --disable-code-coverage
+
+.PHONY: check-code-coverage code-coverage-capture code-coverage-capture-hook code-coverage-clean
+'
+
+ AC_SUBST([CODE_COVERAGE_RULES])
+ m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([CODE_COVERAGE_RULES])])
+])
diff --git a/m4/ax_compiler_flags.m4 b/m4/ax_compiler_flags.m4
new file mode 100644
index 00000000..ddb0456c
--- /dev/null
+++ b/m4/ax_compiler_flags.m4
@@ -0,0 +1,158 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_COMPILER_FLAGS([CFLAGS-VARIABLE], [LDFLAGS-VARIABLE], [IS-RELEASE], [EXTRA-BASE-CFLAGS], [EXTRA-YES-CFLAGS], [UNUSED], [UNUSED], [UNUSED], [EXTRA-BASE-LDFLAGS], [EXTRA-YES-LDFLAGS], [UNUSED], [UNUSED], [UNUSED])
+#
+# DESCRIPTION
+#
+# Check for the presence of an --enable-compile-warnings option to
+# configure, defaulting to "error" in normal operation, or "yes" if
+# IS-RELEASE is equal to "yes". Return the value in the variable
+# $ax_enable_compile_warnings.
+#
+# Depending on the value of --enable-compile-warnings, different compiler
+# warnings are checked to see if they work with the current compiler and,
+# if so, are appended to CFLAGS-VARIABLE and LDFLAGS-VARIABLE. This
+# allows a consistent set of baseline compiler warnings to be used across
+# a code base, irrespective of any warnings enabled locally by individual
+# developers. By standardising the warnings used by all developers of a
+# project, the project can commit to a zero-warnings policy, using -Werror
+# to prevent compilation if new warnings are introduced. This makes
+# catching bugs which are flagged by warnings a lot easier.
+#
+# By providing a consistent --enable-compile-warnings argument across all
+# projects using this macro, continuous integration systems can easily be
+# configured the same for all projects. Automated systems or build
+# systems aimed at beginners may want to pass the --disable-Werror
+# argument to unconditionally prevent warnings being fatal.
+#
+# --enable-compile-warnings can take the values:
+#
+# * no: Base compiler warnings only; not even -Wall.
+# * yes: The above, plus a broad range of useful warnings.
+# * error: The above, plus -Werror so that all warnings are fatal.
+# Use --disable-Werror to override this and disable fatal
+# warnings.
+#
+# The set of base and enabled flags can be augmented using the
+# EXTRA-*-CFLAGS and EXTRA-*-LDFLAGS variables, which are tested and
+# appended to the output variable if --enable-compile-warnings is not
+# "no". Flags should not be disabled using these arguments, as the entire
+# point of AX_COMPILER_FLAGS is to enforce a consistent set of useful
+# compiler warnings on code, using warnings which have been chosen for low
+# false positive rates. If a compiler emits false positives for a
+# warning, a #pragma should be used in the code to disable the warning
+# locally. See:
+#
+# https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Diagnostic-Pragmas.html#Diagnostic-Pragmas
+#
+# The EXTRA-* variables should only be used to supply extra warning flags,
+# and not general purpose compiler flags, as they are controlled by
+# configure options such as --disable-Werror.
+#
+# IS-RELEASE can be used to disable -Werror when making a release, which
+# is useful for those hairy moments when you just want to get the release
+# done as quickly as possible. Set it to "yes" to disable -Werror. By
+# default, it uses the value of $ax_is_release, so if you are using the
+# AX_IS_RELEASE macro, there is no need to pass this parameter. For
+# example:
+#
+# AX_IS_RELEASE([git-directory])
+# AX_COMPILER_FLAGS()
+#
+# CFLAGS-VARIABLE defaults to WARN_CFLAGS, and LDFLAGS-VARIABLE defaults
+# to WARN_LDFLAGS. Both variables are AC_SUBST-ed by this macro, but must
+# be manually added to the CFLAGS and LDFLAGS variables for each target in
+# the code base.
+#
+# If C++ language support is enabled with AC_PROG_CXX, which must occur
+# before this macro in configure.ac, warning flags for the C++ compiler
+# are AC_SUBST-ed as WARN_CXXFLAGS, and must be manually added to the
+# CXXFLAGS variables for each target in the code base. EXTRA-*-CFLAGS can
+# be used to augment the base and enabled flags.
+#
+# Warning flags for g-ir-scanner (from GObject Introspection) are
+# AC_SUBST-ed as WARN_SCANNERFLAGS. This variable must be manually added
+# to the SCANNERFLAGS variable for each GIR target in the code base. If
+# extra g-ir-scanner flags need to be enabled, the AX_COMPILER_FLAGS_GIR
+# macro must be invoked manually.
+#
+# AX_COMPILER_FLAGS may add support for other tools in future, in addition
+# to the compiler and linker. No extra EXTRA-* variables will be added
+# for those tools, and all extra support will still use the single
+# --enable-compile-warnings configure option. For finer grained control
+# over the flags for individual tools, use AX_COMPILER_FLAGS_CFLAGS,
+# AX_COMPILER_FLAGS_LDFLAGS and AX_COMPILER_FLAGS_* for new tools.
+#
+# The UNUSED variables date from a previous version of this macro, and are
+# automatically appended to the preceding non-UNUSED variable. They should
+# be left empty in new uses of the macro.
+#
+# LICENSE
+#
+# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
+# Copyright (c) 2015 David King <amigadave@amigadave.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 14
+
+# _AX_COMPILER_FLAGS_LANG([LANGNAME])
+m4_defun([_AX_COMPILER_FLAGS_LANG],
+[m4_ifdef([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [],
+ [m4_define([_AX_COMPILER_FLAGS_LANG_]$1[_enabled], [])dnl
+ AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_]$1[FLAGS])])dnl
+])
+
+AC_DEFUN([AX_COMPILER_FLAGS],[
+ # C support is enabled by default.
+ _AX_COMPILER_FLAGS_LANG([C])
+ # Only enable C++ support if AC_PROG_CXX is called. The redefinition of
+ # AC_PROG_CXX is so that a fatal error is emitted if this macro is called
+ # before AC_PROG_CXX, which would otherwise cause no C++ warnings to be
+ # checked.
+ AC_PROVIDE_IFELSE([AC_PROG_CXX],
+ [_AX_COMPILER_FLAGS_LANG([CXX])],
+ [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[_AX_COMPILER_FLAGS_LANG([CXX])])])
+ AX_REQUIRE_DEFINED([AX_COMPILER_FLAGS_LDFLAGS])
+
+ # Default value for IS-RELEASE is $ax_is_release
+ ax_compiler_flags_is_release=m4_tolower(m4_normalize(ifelse([$3],,
+ [$ax_is_release],
+ [$3])))
+
+ AC_ARG_ENABLE([compile-warnings],
+ AS_HELP_STRING([--enable-compile-warnings=@<:@no/yes/error@:>@],
+ [Enable compiler warnings and errors]),,
+ [AS_IF([test "$ax_compiler_flags_is_release" = "yes"],
+ [enable_compile_warnings="yes"],
+ [enable_compile_warnings="error"])])
+ AC_ARG_ENABLE([Werror],
+ AS_HELP_STRING([--disable-Werror],
+ [Unconditionally make all compiler warnings non-fatal]),,
+ [enable_Werror=maybe])
+
+ # Return the user's chosen warning level
+ AS_IF([test "$enable_Werror" = "no" -a \
+ "$enable_compile_warnings" = "error"],[
+ enable_compile_warnings="yes"
+ ])
+
+ ax_enable_compile_warnings=$enable_compile_warnings
+
+ AX_COMPILER_FLAGS_CFLAGS([$1],[$ax_compiler_flags_is_release],
+ [$4],[$5 $6 $7 $8])
+ m4_ifdef([_AX_COMPILER_FLAGS_LANG_CXX_enabled],
+ [AX_COMPILER_FLAGS_CXXFLAGS([WARN_CXXFLAGS],
+ [$ax_compiler_flags_is_release],
+ [$4],[$5 $6 $7 $8])])
+ AX_COMPILER_FLAGS_LDFLAGS([$2],[$ax_compiler_flags_is_release],
+ [$9],[$10 $11 $12 $13])
+ AX_COMPILER_FLAGS_GIR([WARN_SCANNERFLAGS],[$ax_compiler_flags_is_release])
+])dnl AX_COMPILER_FLAGS
diff --git a/m4/ax_compiler_flags_cflags.m4 b/m4/ax_compiler_flags_cflags.m4
new file mode 100644
index 00000000..916f9183
--- /dev/null
+++ b/m4/ax_compiler_flags_cflags.m4
@@ -0,0 +1,161 @@
+# =============================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_cflags.html
+# =============================================================================
+#
+# SYNOPSIS
+#
+# AX_COMPILER_FLAGS_CFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
+#
+# DESCRIPTION
+#
+# Add warning flags for the C compiler to VARIABLE, which defaults to
+# WARN_CFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be
+# manually added to the CFLAGS variable for each target in the code base.
+#
+# This macro depends on the environment set up by AX_COMPILER_FLAGS.
+# Specifically, it uses the value of $ax_enable_compile_warnings to decide
+# which flags to enable.
+#
+# LICENSE
+#
+# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
+# Copyright (c) 2017, 2018 Reini Urban <rurban@cpan.org>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 17
+
+AC_DEFUN([AX_COMPILER_FLAGS_CFLAGS],[
+ AC_REQUIRE([AC_PROG_SED])
+ AX_REQUIRE_DEFINED([AX_APPEND_COMPILE_FLAGS])
+ AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+ AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
+
+ # Variable names
+ m4_define([ax_warn_cflags_variable],
+ [m4_normalize(ifelse([$1],,[WARN_CFLAGS],[$1]))])
+
+ AC_LANG_PUSH([C])
+
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
+ [#ifndef __cplusplus
+ #error "no C++"
+ #endif]])],
+ [ax_compiler_cxx=yes;],
+ [ax_compiler_cxx=no;])
+
+ # Always pass -Werror=unknown-warning-option to get Clang to fail on bad
+ # flags, otherwise they are always appended to the warn_cflags variable, and
+ # Clang warns on them for every compilation unit.
+ # If this is passed to GCC, it will explode, so the flag must be enabled
+ # conditionally.
+ AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
+ ax_compiler_flags_test="-Werror=unknown-warning-option"
+ ],[
+ ax_compiler_flags_test=""
+ ])
+
+ # Check that -Wno-suggest-attribute=format is supported
+ AX_CHECK_COMPILE_FLAG([-Wno-suggest-attribute=format],[
+ ax_compiler_no_suggest_attribute_flags="-Wno-suggest-attribute=format"
+ ],[
+ ax_compiler_no_suggest_attribute_flags=""
+ ])
+
+ # Base flags
+ AX_APPEND_COMPILE_FLAGS([ dnl
+ -fno-strict-aliasing dnl
+ $3 dnl
+ ],ax_warn_cflags_variable,[$ax_compiler_flags_test])
+
+ AS_IF([test "$ax_enable_compile_warnings" != "no"],[
+ if test "$ax_compiler_cxx" = "no" ; then
+ # C-only flags. Warn in C++
+ AX_APPEND_COMPILE_FLAGS([ dnl
+ -Wnested-externs dnl
+ -Wmissing-prototypes dnl
+ -Wstrict-prototypes dnl
+ -Wdeclaration-after-statement dnl
+ -Wimplicit-function-declaration dnl
+ -Wold-style-definition dnl
+ -Wjump-misses-init dnl
+ ],ax_warn_cflags_variable,[$ax_compiler_flags_test])
+ fi
+
+ # "yes" flags
+ AX_APPEND_COMPILE_FLAGS([ dnl
+ -Wall dnl
+ -Wextra dnl
+ -Wundef dnl
+ -Wwrite-strings dnl
+ -Wpointer-arith dnl
+ -Wmissing-declarations dnl
+ -Wredundant-decls dnl
+ -Wno-unused-parameter dnl
+ -Wno-missing-field-initializers dnl
+ -Wformat=2 dnl
+ -Wcast-align dnl
+ -Wformat-nonliteral dnl
+ -Wformat-security dnl
+ -Wsign-compare dnl
+ -Wstrict-aliasing dnl
+ -Wshadow dnl
+ -Winline dnl
+ -Wpacked dnl
+ -Wmissing-format-attribute dnl
+ -Wmissing-noreturn dnl
+ -Winit-self dnl
+ -Wredundant-decls dnl
+ -Wmissing-include-dirs dnl
+ -Wunused-but-set-variable dnl
+ -Warray-bounds dnl
+ -Wreturn-type dnl
+ -Wswitch-enum dnl
+ -Wswitch-default dnl
+ -Wduplicated-cond dnl
+ -Wduplicated-branches dnl
+ -Wlogical-op dnl
+ -Wrestrict dnl
+ -Wnull-dereference dnl
+ -Wdouble-promotion dnl
+ $4 dnl
+ $5 dnl
+ $6 dnl
+ $7 dnl
+ ],ax_warn_cflags_variable,[$ax_compiler_flags_test])
+ ])
+ AS_IF([test "$ax_enable_compile_warnings" = "error"],[
+ # "error" flags; -Werror has to be appended unconditionally because
+ # it's not possible to test for
+ #
+ # suggest-attribute=format is disabled because it gives too many false
+ # positives
+ AX_APPEND_FLAG([-Werror],ax_warn_cflags_variable)
+
+ AX_APPEND_COMPILE_FLAGS([ dnl
+ [$ax_compiler_no_suggest_attribute_flags] dnl
+ ],ax_warn_cflags_variable,[$ax_compiler_flags_test])
+ ])
+
+ # In the flags below, when disabling specific flags, always add *both*
+ # -Wno-foo and -Wno-error=foo. This fixes the situation where (for example)
+ # we enable -Werror, disable a flag, and a build bot passes CFLAGS=-Wall,
+ # which effectively turns that flag back on again as an error.
+ for flag in $ax_warn_cflags_variable; do
+ AS_CASE([$flag],
+ [-Wno-*=*],[],
+ [-Wno-*],[
+ AX_APPEND_COMPILE_FLAGS([-Wno-error=$(AS_ECHO([$flag]) | $SED 's/^-Wno-//')],
+ ax_warn_cflags_variable,
+ [$ax_compiler_flags_test])
+ ])
+ done
+
+ AC_LANG_POP([C])
+
+ # Substitute the variables
+ AC_SUBST(ax_warn_cflags_variable)
+])dnl AX_COMPILER_FLAGS
diff --git a/m4/ax_compiler_flags_gir.m4 b/m4/ax_compiler_flags_gir.m4
new file mode 100644
index 00000000..5b4924a2
--- /dev/null
+++ b/m4/ax_compiler_flags_gir.m4
@@ -0,0 +1,60 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_gir.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_COMPILER_FLAGS_GIR([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
+#
+# DESCRIPTION
+#
+# Add warning flags for the g-ir-scanner (from GObject Introspection) to
+# VARIABLE, which defaults to WARN_SCANNERFLAGS. VARIABLE is AC_SUBST-ed
+# by this macro, but must be manually added to the SCANNERFLAGS variable
+# for each GIR target in the code base.
+#
+# This macro depends on the environment set up by AX_COMPILER_FLAGS.
+# Specifically, it uses the value of $ax_enable_compile_warnings to decide
+# which flags to enable.
+#
+# LICENSE
+#
+# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 6
+
+AC_DEFUN([AX_COMPILER_FLAGS_GIR],[
+ AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+
+ # Variable names
+ m4_define([ax_warn_scannerflags_variable],
+ [m4_normalize(ifelse([$1],,[WARN_SCANNERFLAGS],[$1]))])
+
+ # Base flags
+ AX_APPEND_FLAG([$3],ax_warn_scannerflags_variable)
+
+ AS_IF([test "$ax_enable_compile_warnings" != "no"],[
+ # "yes" flags
+ AX_APPEND_FLAG([ dnl
+ --warn-all dnl
+ $4 dnl
+ $5 dnl
+ $6 dnl
+ $7 dnl
+ ],ax_warn_scannerflags_variable)
+ ])
+ AS_IF([test "$ax_enable_compile_warnings" = "error"],[
+ # "error" flags
+ AX_APPEND_FLAG([ dnl
+ --warn-error dnl
+ ],ax_warn_scannerflags_variable)
+ ])
+
+ # Substitute the variables
+ AC_SUBST(ax_warn_scannerflags_variable)
+])dnl AX_COMPILER_FLAGS
diff --git a/m4/ax_compiler_flags_ldflags.m4 b/m4/ax_compiler_flags_ldflags.m4
new file mode 100644
index 00000000..976d1198
--- /dev/null
+++ b/m4/ax_compiler_flags_ldflags.m4
@@ -0,0 +1,111 @@
+# ==============================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_compiler_flags_ldflags.html
+# ==============================================================================
+#
+# SYNOPSIS
+#
+# AX_COMPILER_FLAGS_LDFLAGS([VARIABLE], [IS-RELEASE], [EXTRA-BASE-FLAGS], [EXTRA-YES-FLAGS])
+#
+# DESCRIPTION
+#
+# Add warning flags for the linker to VARIABLE, which defaults to
+# WARN_LDFLAGS. VARIABLE is AC_SUBST-ed by this macro, but must be
+# manually added to the LDFLAGS variable for each target in the code base.
+#
+# This macro depends on the environment set up by AX_COMPILER_FLAGS.
+# Specifically, it uses the value of $ax_enable_compile_warnings to decide
+# which flags to enable.
+#
+# LICENSE
+#
+# Copyright (c) 2014, 2015 Philip Withnall <philip@tecnocode.co.uk>
+# Copyright (c) 2017, 2018 Reini Urban <rurban@cpan.org>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 9
+
+AC_DEFUN([AX_COMPILER_FLAGS_LDFLAGS],[
+ AX_REQUIRE_DEFINED([AX_APPEND_LINK_FLAGS])
+ AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
+ AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
+ AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+
+ # Variable names
+ m4_define([ax_warn_ldflags_variable],
+ [m4_normalize(ifelse([$1],,[WARN_LDFLAGS],[$1]))])
+
+ # Always pass -Werror=unknown-warning-option to get Clang to fail on bad
+ # flags, otherwise they are always appended to the warn_ldflags variable,
+ # and Clang warns on them for every compilation unit.
+ # If this is passed to GCC, it will explode, so the flag must be enabled
+ # conditionally.
+ AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
+ ax_compiler_flags_test="-Werror=unknown-warning-option"
+ ],[
+ ax_compiler_flags_test=""
+ ])
+
+ AX_CHECK_LINK_FLAG([-Wl,--as-needed], [
+ AX_APPEND_LINK_FLAGS([-Wl,--as-needed],
+ [AM_LDFLAGS],[$ax_compiler_flags_test])
+ ])
+ AX_CHECK_LINK_FLAG([-Wl,-z,relro], [
+ AX_APPEND_LINK_FLAGS([-Wl,-z,relro],
+ [AM_LDFLAGS],[$ax_compiler_flags_test])
+ ])
+ AX_CHECK_LINK_FLAG([-Wl,-z,now], [
+ AX_APPEND_LINK_FLAGS([-Wl,-z,now],
+ [AM_LDFLAGS],[$ax_compiler_flags_test])
+ ])
+ AX_CHECK_LINK_FLAG([-Wl,-z,noexecstack], [
+ AX_APPEND_LINK_FLAGS([-Wl,-z,noexecstack],
+ [AM_LDFLAGS],[$ax_compiler_flags_test])
+ ])
+ # textonly, retpolineplt not yet
+
+ # macOS and cygwin linker do not have --as-needed
+ AX_CHECK_LINK_FLAG([-Wl,--no-as-needed], [
+ ax_compiler_flags_as_needed_option="-Wl,--no-as-needed"
+ ], [
+ ax_compiler_flags_as_needed_option=""
+ ])
+
+ # macOS linker speaks with a different accent
+ ax_compiler_flags_fatal_warnings_option=""
+ AX_CHECK_LINK_FLAG([-Wl,--fatal-warnings], [
+ ax_compiler_flags_fatal_warnings_option="-Wl,--fatal-warnings"
+ ])
+ AX_CHECK_LINK_FLAG([-Wl,-fatal_warnings], [
+ ax_compiler_flags_fatal_warnings_option="-Wl,-fatal_warnings"
+ ])
+
+ # Base flags
+ AX_APPEND_LINK_FLAGS([ dnl
+ $ax_compiler_flags_as_needed_option dnl
+ $3 dnl
+ ],ax_warn_ldflags_variable,[$ax_compiler_flags_test])
+
+ AS_IF([test "$ax_enable_compile_warnings" != "no"],[
+ # "yes" flags
+ AX_APPEND_LINK_FLAGS([$4 $5 $6 $7],
+ ax_warn_ldflags_variable,
+ [$ax_compiler_flags_test])
+ ])
+ AS_IF([test "$ax_enable_compile_warnings" = "error"],[
+ # "error" flags; -Werror has to be appended unconditionally because
+ # it's not possible to test for
+ #
+ # suggest-attribute=format is disabled because it gives too many false
+ # positives
+ AX_APPEND_LINK_FLAGS([ dnl
+ $ax_compiler_flags_fatal_warnings_option dnl
+ ],ax_warn_ldflags_variable,[$ax_compiler_flags_test])
+ ])
+
+ # Substitute the variables
+ AC_SUBST(ax_warn_ldflags_variable)
+])dnl AX_COMPILER_FLAGS
diff --git a/m4/ax_is_release.m4 b/m4/ax_is_release.m4
new file mode 100644
index 00000000..9097ddb6
--- /dev/null
+++ b/m4/ax_is_release.m4
@@ -0,0 +1,80 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_is_release.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_IS_RELEASE(POLICY)
+#
+# DESCRIPTION
+#
+# Determine whether the code is being configured as a release, or from
+# git. Set the ax_is_release variable to 'yes' or 'no'.
+#
+# If building a release version, it is recommended that the configure
+# script disable compiler errors and debug features, by conditionalising
+# them on the ax_is_release variable. If building from git, these
+# features should be enabled.
+#
+# The POLICY parameter specifies how ax_is_release is determined. It can
+# take the following values:
+#
+# * git-directory: ax_is_release will be 'no' if a '.git' directory exists
+# * minor-version: ax_is_release will be 'no' if the minor version number
+# in $PACKAGE_VERSION is odd; this assumes
+# $PACKAGE_VERSION follows the 'major.minor.micro' scheme
+# * micro-version: ax_is_release will be 'no' if the micro version number
+# in $PACKAGE_VERSION is odd; this assumes
+# $PACKAGE_VERSION follows the 'major.minor.micro' scheme
+# * dash-version: ax_is_release will be 'no' if there is a dash '-'
+# in $PACKAGE_VERSION, for example 1.2-pre3, 1.2.42-a8b9
+# or 2.0-dirty (in particular this is suitable for use
+# with git-version-gen)
+# * always: ax_is_release will always be 'yes'
+# * never: ax_is_release will always be 'no'
+#
+# Other policies may be added in future.
+#
+# LICENSE
+#
+# Copyright (c) 2015 Philip Withnall <philip@tecnocode.co.uk>
+# Copyright (c) 2016 Collabora Ltd.
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved.
+
+#serial 7
+
+AC_DEFUN([AX_IS_RELEASE],[
+ AC_BEFORE([AC_INIT],[$0])
+
+ m4_case([$1],
+ [git-directory],[
+ # $is_release = (.git directory does not exist)
+ AS_IF([test -d ${srcdir}/.git],[ax_is_release=no],[ax_is_release=yes])
+ ],
+ [minor-version],[
+ # $is_release = ($minor_version is even)
+ minor_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]][[^.]]*.\([[^.]][[^.]]*\).*/\1/'`
+ AS_IF([test "$(( $minor_version % 2 ))" -ne 0],
+ [ax_is_release=no],[ax_is_release=yes])
+ ],
+ [micro-version],[
+ # $is_release = ($micro_version is even)
+ micro_version=`echo "$PACKAGE_VERSION" | sed 's/[[^.]]*\.[[^.]]*\.\([[^.]]*\).*/\1/'`
+ AS_IF([test "$(( $micro_version % 2 ))" -ne 0],
+ [ax_is_release=no],[ax_is_release=yes])
+ ],
+ [dash-version],[
+ # $is_release = ($PACKAGE_VERSION has a dash)
+ AS_CASE([$PACKAGE_VERSION],
+ [*-*], [ax_is_release=no],
+ [*], [ax_is_release=yes])
+ ],
+ [always],[ax_is_release=yes],
+ [never],[ax_is_release=no],
+ [
+ AC_MSG_ERROR([Invalid policy. Valid policies: git-directory, minor-version, micro-version, dash-version, always, never.])
+ ])
+])
diff --git a/m4/ax_require_defined.m4 b/m4/ax_require_defined.m4
new file mode 100644
index 00000000..17c3eab7
--- /dev/null
+++ b/m4/ax_require_defined.m4
@@ -0,0 +1,37 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_require_defined.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_REQUIRE_DEFINED(MACRO)
+#
+# DESCRIPTION
+#
+# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
+# been defined and thus are available for use. This avoids random issues
+# where a macro isn't expanded. Instead the configure script emits a
+# non-fatal:
+#
+# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
+#
+# It's like AC_REQUIRE except it doesn't expand the required macro.
+#
+# Here's an example:
+#
+# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
+#
+# LICENSE
+#
+# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 2
+
+AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
+ m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
+])dnl AX_REQUIRE_DEFINED
diff --git a/m4/compiler_warnings.m4 b/m4/compiler_warnings.m4
deleted file mode 100644
index c592eb98..00000000
--- a/m4/compiler_warnings.m4
+++ /dev/null
@@ -1,32 +0,0 @@
-AC_DEFUN([NM_COMPILER_WARNINGS],
-[AC_ARG_ENABLE(more-warnings,
- AS_HELP_STRING([--enable-more-warnings], [Maximum compiler warnings]),
- set_more_warnings="$enableval",set_more_warnings=yes)
-AC_MSG_CHECKING(for more warnings, including -Werror)
-if test "$GCC" = "yes" -a "$set_more_warnings" != "no"; then
- AC_MSG_RESULT(yes)
- CFLAGS="-Wall -Werror -std=gnu89 $CFLAGS"
-
- for option in -Wmissing-declarations -Wmissing-prototypes \
- -Wdeclaration-after-statement -Wstrict-prototypes \
- -Wno-unused-parameter -Wno-sign-compare \
- -fno-strict-aliasing -Wno-deprecated-declarations \
- -Wno-unused-but-set-variable -Wformat-security; do
- SAVE_CFLAGS="$CFLAGS"
- CFLAGS="$CFLAGS $option"
- AC_MSG_CHECKING([whether gcc understands $option])
- AC_TRY_COMPILE([], [],
- has_option=yes,
- has_option=no,)
- if test $has_option = no; then
- CFLAGS="$SAVE_CFLAGS"
- fi
- AC_MSG_RESULT($has_option)
- unset has_option
- unset SAVE_CFLAGS
- done
- unset option
-else
- AC_MSG_RESULT(no)
-fi
-])
diff --git a/m4/gtk-doc.m4 b/m4/gtk-doc.m4
new file mode 100644
index 00000000..2d12f01f
--- /dev/null
+++ b/m4/gtk-doc.m4
@@ -0,0 +1,113 @@
+# -*- mode: autoconf -*-
+#
+# gtk-doc.m4 - configure macro to check for gtk-doc
+# Copyright (C) 2003 James Henstridge
+# 2007-2017 Stefan Sauer
+#
+# 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 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the above copyright owner gives unlimited
+# permission to copy, distribute and modify the configure scripts that
+# are the output of Autoconf when processing the Macro. You need not
+# follow the terms of the GNU General Public License when using or
+# distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+
+# serial 2
+
+dnl Usage:
+dnl GTK_DOC_CHECK([minimum-gtk-doc-version])
+AC_DEFUN([GTK_DOC_CHECK],
+[
+ AC_REQUIRE([PKG_PROG_PKG_CONFIG])
+ AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
+ AC_BEFORE([AM_PROG_LIBTOOL],[$0])dnl setup libtool first
+
+ ifelse([$1],[],[gtk_doc_requires="gtk-doc"],[gtk_doc_requires="gtk-doc >= $1"])
+ AC_MSG_CHECKING([for gtk-doc])
+ PKG_CHECK_EXISTS([$gtk_doc_requires],[have_gtk_doc=yes],[have_gtk_doc=no])
+ AC_MSG_RESULT($have_gtk_doc)
+
+ if test "$have_gtk_doc" = "no"; then
+ AC_MSG_WARN([
+ You will not be able to create source packages with 'make dist'
+ because $gtk_doc_requires is not found.])
+ fi
+
+ dnl check for tools we added during development
+ dnl Use AC_CHECK_PROG to avoid the check target using an absolute path that
+ dnl may not be writable by the user. Currently, automake requires that the
+ dnl test name must end in '.test'.
+ dnl https://bugzilla.gnome.org/show_bug.cgi?id=701638
+ AC_CHECK_PROG([GTKDOC_CHECK],[gtkdoc-check],[gtkdoc-check.test])
+ AC_PATH_PROG([GTKDOC_CHECK_PATH],[gtkdoc-check])
+ AC_PATH_PROGS([GTKDOC_REBASE],[gtkdoc-rebase],[true])
+ AC_PATH_PROG([GTKDOC_MKPDF],[gtkdoc-mkpdf])
+
+ dnl for overriding the documentation installation directory
+ AC_ARG_WITH([html-dir],
+ AS_HELP_STRING([--with-html-dir=PATH], [path to installed docs]),,
+ [with_html_dir='${datadir}/gtk-doc/html'])
+ HTML_DIR="$with_html_dir"
+ AC_SUBST([HTML_DIR])
+
+ dnl enable/disable documentation building
+ AC_ARG_ENABLE([gtk-doc],
+ AS_HELP_STRING([--enable-gtk-doc],
+ [use gtk-doc to build documentation [[default=no]]]),,
+ [enable_gtk_doc=no])
+
+ AC_MSG_CHECKING([whether to build gtk-doc documentation])
+ AC_MSG_RESULT($enable_gtk_doc)
+
+ if test "x$enable_gtk_doc" = "xyes" && test "$have_gtk_doc" = "no"; then
+ AC_MSG_ERROR([
+ You must have $gtk_doc_requires installed to build documentation for
+ $PACKAGE_NAME. Please install gtk-doc or disable building the
+ documentation by adding '--disable-gtk-doc' to '[$]0'.])
+ fi
+
+ dnl don't check for glib if we build glib
+ if test "x$PACKAGE_NAME" != "xglib"; then
+ dnl don't fail if someone does not have glib
+ PKG_CHECK_MODULES(GTKDOC_DEPS, glib-2.0 >= 2.10.0 gobject-2.0 >= 2.10.0,,[:])
+ fi
+
+ dnl enable/disable output formats
+ AC_ARG_ENABLE([gtk-doc-html],
+ AS_HELP_STRING([--enable-gtk-doc-html],
+ [build documentation in html format [[default=yes]]]),,
+ [enable_gtk_doc_html=yes])
+ AC_ARG_ENABLE([gtk-doc-pdf],
+ AS_HELP_STRING([--enable-gtk-doc-pdf],
+ [build documentation in pdf format [[default=no]]]),,
+ [enable_gtk_doc_pdf=no])
+
+ if test -z "$GTKDOC_MKPDF"; then
+ enable_gtk_doc_pdf=no
+ fi
+
+ if test -z "$AM_DEFAULT_VERBOSITY"; then
+ AM_DEFAULT_VERBOSITY=1
+ fi
+ AC_SUBST([AM_DEFAULT_VERBOSITY])
+
+ AM_CONDITIONAL([HAVE_GTK_DOC], [test x$have_gtk_doc = xyes])
+ AM_CONDITIONAL([ENABLE_GTK_DOC], [test x$enable_gtk_doc = xyes])
+ AM_CONDITIONAL([GTK_DOC_BUILD_HTML], [test x$enable_gtk_doc_html = xyes])
+ AM_CONDITIONAL([GTK_DOC_BUILD_PDF], [test x$enable_gtk_doc_pdf = xyes])
+ AM_CONDITIONAL([GTK_DOC_USE_LIBTOOL], [test -n "$LIBTOOL"])
+ AM_CONDITIONAL([GTK_DOC_USE_REBASE], [test -n "$GTKDOC_REBASE"])
+])
diff --git a/m4/introspection.m4 b/m4/introspection.m4
index d89c3d90..b0ccd689 100644
--- a/m4/introspection.m4
+++ b/m4/introspection.m4
@@ -8,6 +8,47 @@ dnl
# serial 1
+dnl This is a copy of AS_AC_EXPAND
+dnl
+dnl (C) 2003, 2004, 2005 Thomas Vander Stichele <thomas at apestaart dot org>
+dnl Copying and distribution of this file, with or without modification,
+dnl are permitted in any medium without royalty provided the copyright
+dnl notice and this notice are preserved.
+m4_define([_GOBJECT_INTROSPECTION_AS_AC_EXPAND],
+[
+ EXP_VAR=[$1]
+ FROM_VAR=[$2]
+
+ dnl first expand prefix and exec_prefix if necessary
+ prefix_save=$prefix
+ exec_prefix_save=$exec_prefix
+
+ dnl if no prefix given, then use /usr/local, the default prefix
+ if test "x$prefix" = "xNONE"; then
+ prefix="$ac_default_prefix"
+ fi
+ dnl if no exec_prefix given, then use prefix
+ if test "x$exec_prefix" = "xNONE"; then
+ exec_prefix=$prefix
+ fi
+
+ full_var="$FROM_VAR"
+ dnl loop until it doesn't change anymore
+ while true; do
+ new_full_var="`eval echo $full_var`"
+ if test "x$new_full_var" = "x$full_var"; then break; fi
+ full_var=$new_full_var
+ done
+
+ dnl clean up
+ full_var=$new_full_var
+ AC_SUBST([$1], "$full_var")
+
+ dnl restore prefix and exec_prefix
+ prefix=$prefix_save
+ exec_prefix=$exec_prefix_save
+])
+
m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL],
[
AC_BEFORE([AC_PROG_LIBTOOL],[$0])dnl setup libtool first
@@ -50,20 +91,25 @@ m4_define([_GOBJECT_INTROSPECTION_CHECK_INTERNAL],
AC_MSG_RESULT([$found_introspection])
+ dnl expand datadir/libdir so we can pass them to pkg-config
+ dnl and get paths relative to our target directories
+ _GOBJECT_INTROSPECTION_AS_AC_EXPAND(_GI_EXP_DATADIR, "$datadir")
+ _GOBJECT_INTROSPECTION_AS_AC_EXPAND(_GI_EXP_LIBDIR, "$libdir")
+
INTROSPECTION_SCANNER=
INTROSPECTION_COMPILER=
INTROSPECTION_GENERATE=
INTROSPECTION_GIRDIR=
INTROSPECTION_TYPELIBDIR=
if test "x$found_introspection" = "xyes"; then
- INTROSPECTION_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
- INTROSPECTION_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
- INTROSPECTION_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
- INTROSPECTION_GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
- INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
+ INTROSPECTION_SCANNER=$PKG_CONFIG_SYSROOT_DIR`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
+ INTROSPECTION_COMPILER=$PKG_CONFIG_SYSROOT_DIR`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
+ INTROSPECTION_GENERATE=$PKG_CONFIG_SYSROOT_DIR`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
+ INTROSPECTION_GIRDIR=`$PKG_CONFIG --define-variable=datadir="${_GI_EXP_DATADIR}" --variable=girdir gobject-introspection-1.0`
+ INTROSPECTION_TYPELIBDIR="$($PKG_CONFIG --define-variable=libdir="${_GI_EXP_LIBDIR}" --variable=typelibdir gobject-introspection-1.0)"
INTROSPECTION_CFLAGS=`$PKG_CONFIG --cflags gobject-introspection-1.0`
INTROSPECTION_LIBS=`$PKG_CONFIG --libs gobject-introspection-1.0`
- INTROSPECTION_MAKEFILE=`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection
+ INTROSPECTION_MAKEFILE=$PKG_CONFIG_SYSROOT_DIR`$PKG_CONFIG --variable=datadir gobject-introspection-1.0`/gobject-introspection-1.0/Makefile.introspection
fi
AC_SUBST(INTROSPECTION_SCANNER)
AC_SUBST(INTROSPECTION_COMPILER)
diff --git a/m4/mm-enable-plugin.m4 b/m4/mm-enable-plugin.m4
new file mode 100644
index 00000000..774c881d
--- /dev/null
+++ b/m4/mm-enable-plugin.m4
@@ -0,0 +1,71 @@
+dnl -*- mode: autoconf -*-
+dnl Copyright 2019 Aleksander Morgado
+dnl
+dnl This file is free software; the author(s) gives unlimited
+dnl permission to copy and/or distribute it, with or without
+dnl modifications, as long as this notice is preserved.
+dnl
+
+# serial 1
+
+dnl Usage:
+dnl MM_ENABLE_ALL_PLUGINS
+AC_DEFUN([MM_ENABLE_ALL_PLUGINS],
+[dnl
+AC_ARG_ENABLE(all-plugins,
+ AS_HELP_STRING([--enable-all-plugins],
+ [Build all plugins [[default=yes]]]),
+ [],
+ [enable_all_plugins=yes])
+])
+
+dnl Usage:
+dnl MM_ENABLE_PLUGIN_FULL([NAME],[DEFAULT],[WITH_SHARED_NAME_1,WITH_SHARED_NAME_2,...])
+AC_DEFUN([MM_ENABLE_PLUGIN_FULL],
+[dnl
+m4_pushdef([var_enable_plugin], patsubst([enable_plugin_$1], -, _))dnl
+m4_pushdef([VAR_ENABLE_PLUGIN], patsubst(translit([enable_plugin_$1], [a-z], [A-Z]), -, _))dnl
+AC_ARG_ENABLE(plugin-$1,
+ AS_HELP_STRING([--enable-plugin-$1], [Build $1 plugin]),
+ [],
+ [var_enable_plugin=$2])
+if test "x$var_enable_plugin" = "xyes"; then
+ AC_DEFINE([VAR_ENABLE_PLUGIN], 1, [Define if $1 plugin is enabled])
+m4_ifval([$3],[m4_foreach(with_shared,[$3],[dnl
+ with_shared="yes"
+])])dnl
+fi
+AM_CONDITIONAL(VAR_ENABLE_PLUGIN, [test "x$var_enable_plugin" = "xyes"])
+m4_popdef([VAR_ENABLE_PLUGIN])dnl
+m4_popdef([var_enable_plugin])dnl
+])
+
+dnl Usage:
+dnl MM_ENABLE_PLUGIN([NAME],[WITH_SHARED_NAME_1,WITH_SHARED_NAME_2,...])
+AC_DEFUN([MM_ENABLE_PLUGIN],
+[dnl
+MM_ENABLE_PLUGIN_FULL([$1],[$enable_all_plugins],[$2])
+])
+
+dnl Usage:
+dnl MM_ENABLE_PLUGIN_DISABLED([NAME],[WITH_SHARED_NAME_1,WITH_SHARED_NAME_2,...])
+AC_DEFUN([MM_ENABLE_PLUGIN_DISABLED],
+[dnl
+MM_ENABLE_PLUGIN_FULL([$1],["no"],[$2])
+])
+
+dnl Usage:
+dnl MM_BUILD_SHARED([NAME])
+AC_DEFUN([MM_BUILD_SHARED],
+[dnl
+m4_pushdef([with_shared], patsubst([with_shared_$1], -, _))dnl
+m4_pushdef([WITH_SHARED], patsubst(translit([with_shared_$1], [a-z], [A-Z]), -, _))dnl
+AM_CONDITIONAL(WITH_SHARED, test "x$with_shared" = "xyes")
+if test "x$with_shared" = "xyes"; then
+ AC_DEFINE([WITH_SHARED], 1, [Define if $1 utils are built])
+else
+ with_shared="no"
+fi
+m4_popdef([WITH_SHARED])dnl
+m4_popdef([with_shared])dnl
+])
diff --git a/meson.build b/meson.build
new file mode 100644
index 00000000..29088311
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,419 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+project(
+ 'ModemManager', 'c',
+ version: '1.19.0',
+ license: 'GPL2',
+ default_options: [
+ 'buildtype=debugoptimized',
+ 'c_std=gnu89',
+ 'warning_level=2',
+ ],
+ meson_version: '>= 0.53.0',
+)
+
+mm_name = meson.project_name()
+mm_version = meson.project_version()
+version_array = mm_version.split('.')
+mm_major_version = version_array[0].to_int()
+mm_minor_version = version_array[1].to_int()
+mm_micro_version = version_array[2].to_int()
+
+mm_prefix = get_option('prefix')
+mm_datadir = get_option('datadir')
+mm_includedir = get_option('includedir')
+mm_libdir = get_option('libdir')
+mm_sbindir = get_option('sbindir')
+mm_sysconfdir = get_option('sysconfdir')
+
+mm_pkgdatadir = mm_datadir / mm_name
+mm_pkgincludedir = mm_includedir / mm_name
+mm_pkglibdir = mm_libdir / mm_name
+
+mm_glib_name = 'libmm-glib'
+mm_glib_pkgincludedir = mm_includedir / mm_glib_name
+
+# libtool versioning for libmm-glib (-version-info c:r:a)
+# - If the interface is unchanged, but the implementation has changed or been fixed, then increment r
+# - Otherwise, increment c and zero r.
+# - If the interface has grown (that is, the new library is compatible with old code), increment a.
+# - If the interface has changed in an incompatible way (that is, functions have changed or been removed), then zero a.
+current = 8
+revision = 0
+age = 8
+mm_glib_version = '@0@.@1@.@2@'.format(current - age, age, revision)
+
+mm_gir_version = '1.0'
+
+gnome = import('gnome')
+i18n = import('i18n')
+pkg = import('pkgconfig')
+
+source_root = meson.current_source_dir()
+build_root = meson.current_build_dir()
+
+build_aux_dir = source_root / 'build-aux'
+plugins_dir = source_root / 'plugins'
+po_dir = source_root / 'po'
+src_dir = source_root / 'src'
+
+top_inc = include_directories('.')
+
+cc = meson.get_compiler('c')
+
+config_h = configuration_data()
+config_h.set_quoted('PACKAGE_VERSION', mm_version)
+config_h.set_quoted('VERSION', mm_version)
+
+# Globally define_GNU_SOURCE and therefore enable the GNU extensions
+config_h.set('_GNU_SOURCE', true)
+
+# compiler flags
+common_args = ['-DHAVE_CONFIG_H']
+
+# compiler flags that are always enabled, even in release builds
+cc_args = cc.get_supported_arguments([
+ # warning on unused parameters is overkill, never do that
+ '-Wno-unused-parameter',
+ # function type cast disabled: used throughout the code especially to
+ # cast GAsyncReadyCallbacks with the real object type instead of GObject
+ '-Wno-cast-function-type',
+ # all message protocol structs are packed, never complain about it
+ '-Wno-packed',
+ # we use some floating point ids as unknown, so we want to compare with them
+ '-Wno-float-equal',
+ # avoid warning if we're ignoring fields on purpose
+ '-Wno-missing-field-initializers',
+])
+
+# strict flags to use in debug builds
+if get_option('buildtype').contains('debug')
+ cc_args += cc.get_supported_arguments([
+ '-fno-strict-aliasing',
+ '-Waggregate-return',
+ '-Wcast-align',
+ '-Wdeclaration-after-statement',
+ '-Wdouble-promotion',
+ '-Wduplicated-branches',
+ '-Wduplicated-cond',
+ '-Wformat=2',
+ '-Wformat-nonliteral',
+ '-Wformat-security',
+ '-Winit-self',
+ '-Winline',
+ '-Wjump-misses-init',
+ '-Wlogical-op',
+ '-Wnested-externs',
+ '-Wmissing-declarations',
+ '-Wmissing-format-attribute',
+ '-Wmissing-include-dirs',
+ '-Wmissing-noreturn',
+ '-Wmissing-prototypes',
+ '-Wnull-dereference',
+ '-Wpointer-arith',
+ '-Wredundant-decls',
+ '-Wrestrict',
+ '-Wreturn-type',
+ '-Wshadow',
+ '-Wstrict-prototypes',
+ '-Wsuggest-attribute=format',
+ '-Wswitch-default',
+ '-Wswitch-enum',
+ '-Wundef',
+ '-Wunused-but-set-variable',
+ '-Wwrite-strings',
+ ])
+endif
+
+add_project_arguments(common_args + cc_args, language: 'c')
+
+glib_version = '2.56'
+
+gio_unix_dep = dependency('gio-unix-2.0')
+glib_dep = dependency('glib-2.0', version: '>= ' + glib_version)
+gmodule_dep = dependency('gmodule-2.0')
+
+deps = [
+ glib_dep,
+ dependency('gio-2.0'),
+ dependency('gobject-2.0'),
+]
+
+c_args = [
+ '-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_' + glib_version.underscorify(),
+ '-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_' + glib_version.underscorify(),
+ '-DGLIB_DISABLE_DEPRECATION_WARNINGS',
+]
+
+glib_deps = declare_dependency(
+ dependencies: deps,
+ compile_args: c_args,
+)
+
+# DBus system directory
+dbus_dep = dependency('dbus-1')
+dbus_interfaces_dir = dbus_dep.get_pkgconfig_variable('interfaces_dir', define_variable: ['datadir', mm_datadir])
+dbus_system_bus_services_dir = dbus_dep.get_pkgconfig_variable('system_bus_services_dir', define_variable: ['datadir', mm_datadir])
+
+dbus_policy_dir = get_option('dbus_policy_dir')
+if dbus_policy_dir == ''
+ dbus_policy_dir = dbus_dep.get_pkgconfig_variable('sysconfdir', define_variable: ['sysconfdir', mm_sysconfdir]) / 'dbus-1/system.d'
+endif
+
+enable_bash_completion = get_option('bash_completion')
+if enable_bash_completion
+ bash_completion_dep = dependency('bash-completion')
+ bash_completion_completionsdir = bash_completion_dep.get_pkgconfig_variable(
+ 'completionsdir',
+ # bash-completion 2.10 changed the substitutions
+ define_variable: bash_completion_dep.version().version_compare('>= 2.10') ? ['datadir', mm_datadir] : ['prefix', mm_prefix],
+ )
+endif
+
+# udev support (enabled by default)
+gudev_dep = dependency('gudev-1.0', version: '>= 232', required: get_option('udev'))
+enable_udev = gudev_dep.found()
+config_h.set('WITH_UDEV', enable_udev)
+
+# udev base directory
+udev_udevdir = get_option('udevdir')
+if udev_udevdir == ''
+ udev_udevdir = dependency('udev').get_pkgconfig_variable('udevdir')
+endif
+udev_rulesdir = udev_udevdir / 'rules.d'
+
+# systemd unit / service files
+systemd_systemdsystemunitdir = get_option('systemdsystemunitdir')
+install_systemdunitdir = (systemd_systemdsystemunitdir != 'no')
+
+if install_systemdunitdir and systemd_systemdsystemunitdir == ''
+ systemd_dep = dependency('systemd', not_found_message: 'systemd required but not found, please provide a valid systemd user unit dir or disable it')
+ systemd_systemdsystemunitdir = systemd_dep.get_pkgconfig_variable('systemdsystemunitdir', define_variable: ['root_prefix', mm_prefix])
+endif
+
+# Suspend/resume support
+enable_systemd_suspend_resume = get_option('systemd_suspend_resume')
+# systemd journal support
+enable_systemd_journal = get_option('systemd_journal')
+
+if enable_systemd_suspend_resume or enable_systemd_journal
+ libsystemd_dep = dependency('libsystemd', version: '>= 209', required: false)
+ if not libsystemd_dep.found()
+ libsystemd_dep = dependency('libsystemd-login', version: '>= 183', required: false)
+ if not libsystemd_dep.found()
+ libsystemd_dep = dependency(
+ 'libelogind',
+ version: '>= 209',
+ not_found_message: 'libsystemd, libsystemd-login or elogind must be available at runtime for suspend/resume or systemd journal support',
+ )
+ endif
+ endif
+endif
+config_h.set('WITH_SYSTEMD_SUSPEND_RESUME', enable_systemd_suspend_resume)
+config_h.set('WITH_SYSTEMD_JOURNAL', enable_systemd_journal)
+
+# PolicyKit
+polkit_opt = get_option('polkit')
+enable_polkit = (polkit_opt != 'no')
+if enable_polkit
+ polkit_gobject_dep = dependency('polkit-gobject-1', version: '>= 0.97', not_found_message: 'PolicyKit development headers are required')
+
+ polkit_gobject_policydir = polkit_gobject_dep.get_pkgconfig_variable('policydir', define_variable: ['prefix', mm_prefix])
+
+ policy_conf = {'MM_DEFAULT_USER_POLICY': (polkit_opt == 'permissive' ? 'yes' : 'auth_self_keep')}
+endif
+config_h.set('WITH_POLKIT', enable_polkit)
+
+# AT command via DBus support (disabled by default unless running in --debug)
+# It is suggested that this option is only enabled in custom built systems and
+# only if truly required.
+enable_at_command_via_dbus = get_option('at_command_via_dbus')
+config_h.set('WITH_AT_COMMAND_VIA_DBUS', enable_at_command_via_dbus)
+
+# MBIM support (enabled by default)
+mbim_glib_dep = dependency('mbim-glib', version: '>= 1.27.3', required: get_option('mbim'))
+enable_mbim = mbim_glib_dep.found()
+config_h.set('WITH_MBIM', enable_mbim)
+
+# QMI support (enabled by default)
+qmi_glib_dep = dependency('qmi-glib', version: '>= 1.31.1', required: get_option('qmi'))
+enable_qmi = qmi_glib_dep.found()
+config_h.set('WITH_QMI', enable_qmi)
+
+# QRTR support (both as qrtr-glib and qmi-glib apis)
+qrtr_glib_dep = dependency('qrtr-glib', version: '>= 1.0.0', required: get_option('qrtr'))
+enable_qrtr = qrtr_glib_dep.found()
+assert(not enable_qrtr or enable_qmi, 'QRTR support requires QMI enabled')
+assert(not enable_qrtr or qmi_glib_dep.get_pkgconfig_variable('qmi_qrtr_supported').to_int().is_odd(), 'Couldn\'t find QRTR support in qmi-glib.')
+config_h.set('WITH_QRTR', enable_qrtr)
+
+# Distribution version string
+dist_version = get_option('dist_version')
+if dist_version != ''
+ config_h.set('MM_DIST_VERSION', dist_version)
+endif
+
+util_dep = cc.find_library('util')
+
+# introspection support
+enable_gir = dependency('gobject-introspection-1.0', version: '>= 0.9.6', required: get_option('introspection')).found()
+
+# vala support
+enable_vapi = get_option('vapi')
+
+# gtkdoc support
+enable_gtk_doc = get_option('gtk_doc')
+
+enable_plugins = not get_option('auto_features').disabled()
+
+plugins_shared_reqs = {
+ 'foxconn': enable_mbim,
+ 'icera': true,
+ 'novatel': true,
+ 'option': true,
+ 'sierra': true,
+ 'telit': true,
+ 'xmm': true,
+}
+
+plugins_options_reqs = {
+ 'altair-lte': {'available': true, 'shared': []},
+ 'anydata': {'available': true, 'shared': []},
+ 'broadmobi': {'available': true, 'shared': []},
+ 'cinterion': {'available': true, 'shared': []},
+ 'dell': {'available': true, 'shared': ['foxconn', 'novatel', 'sierra', 'telit', 'xmm']},
+ 'dlink': {'available': true, 'shared': []},
+ 'fibocom': {'available': true, 'shared': ['xmm']},
+ 'foxconn': {'available': true, 'shared': ['foxconn']},
+ 'generic': {'available': true, 'shared': []},
+ 'gosuncn': {'available': true, 'shared': []},
+ 'haier': {'available': true, 'shared': []},
+ 'huawei': {'available': true, 'shared': []},
+ 'iridium': {'available': true, 'shared': []},
+ 'linktop': {'available': true, 'shared': []},
+ 'longcheer': {'available': true, 'shared': []},
+ 'mbm': {'available': true, 'shared': []},
+ 'motorola': {'available': true, 'shared': []},
+ 'mtk': {'available': true, 'shared': []},
+ 'nokia': {'available': true, 'shared': []},
+ 'nokia-icera': {'available': true, 'shared': ['icera']},
+ 'novatel': {'available': true, 'shared': ['novatel']},
+ 'novatel-lte': {'available': true, 'shared': []},
+ 'option': {'available': true, 'shared': ['option']},
+ 'option-hso': {'available': true, 'shared': ['option']},
+ 'pantech': {'available': true, 'shared': []},
+ 'qcom-soc': {'available': enable_qmi, 'shared': []},
+ 'quectel': {'available': true, 'shared': []},
+ 'samsung': {'available': true, 'shared': ['icera']},
+ 'sierra-legacy': {'available': true, 'shared': ['icera', 'sierra']},
+ 'sierra': {'available': true, 'shared': ['xmm']},
+ 'simtech': {'available': true, 'shared': []},
+ 'telit': {'available': true, 'shared': ['telit']},
+ 'thuraya': {'available': true, 'shared': []},
+ 'tplink': {'available': true, 'shared': []},
+ 'ublox': {'available': true, 'shared': []},
+ 'via': {'available': true, 'shared': []},
+ 'wavecom': {'available': true, 'shared': []},
+ 'x22x': {'available': true, 'shared': []},
+ 'zte': {'available': true, 'shared': ['icera']},
+}
+
+plugins_shared = {}
+foreach plugin_name, _: plugins_shared_reqs
+ plugins_shared += {plugin_name: false}
+endforeach
+
+plugins_options = {}
+foreach plugin_name, plugin_reqs: plugins_options_reqs
+ plugin_opt = get_option('plugin_' + plugin_name.underscorify())
+ assert(plugin_reqs['available'] or not plugin_opt.enabled(), '@0@ is not available'.format(plugin_name))
+ plugin_enabled = not plugin_opt.disabled() and plugin_reqs['available']
+ if plugin_enabled
+ foreach plugin_req: plugin_reqs['shared']
+ if plugins_shared_reqs[plugin_req]
+ plugins_shared += {plugin_req: true}
+ else
+ assert(plugin_opt.enabled(), '@0@ required @1@ but is not available'.format(plugin_name, plugin_req))
+ plugin_enabled = false
+ break
+ endif
+ endforeach
+ endif
+ plugins_options += {plugin_name: plugin_enabled}
+endforeach
+
+version_conf = {
+ 'MM_MAJOR_VERSION': mm_major_version,
+ 'MM_MINOR_VERSION': mm_minor_version,
+ 'MM_MICRO_VERSION': mm_micro_version,
+ 'VERSION': mm_version,
+}
+
+subdir('po')
+subdir('data')
+subdir('introspection')
+subdir('include')
+
+subdir('libqcdm/src')
+subdir('libqcdm/tests')
+
+subdir('libmm-glib')
+subdir('src')
+subdir('plugins')
+subdir('cli')
+subdir('test')
+subdir('tools/tests')
+
+subdir('examples/sms-c')
+
+enable_man = get_option('man')
+if enable_man
+ subdir('docs/man')
+endif
+
+if enable_gtk_doc
+ subdir('docs/reference/api')
+ subdir('docs/reference/libmm-glib')
+endif
+
+configure_file(
+ output: 'config.h',
+ configuration: config_h,
+)
+
+summary({
+ 'compiler': cc.get_id(),
+ 'cflags': cc_args,
+}, section: 'Build')
+
+summary({
+ 'prefix': mm_prefix,
+ 'D-Bus policy directory': dbus_policy_dir,
+ 'udev base directory': udev_udevdir,
+ 'systemd user unit directory': systemd_systemdsystemunitdir,
+}, section: 'System paths')
+
+summary({
+ 'udev': enable_udev,
+ 'policykit': polkit_opt,
+ 'mbim': enable_mbim,
+ 'qmi': enable_qmi,
+ 'qrtr': enable_qrtr,
+ 'systemd suspend/resume': enable_systemd_suspend_resume,
+ 'systemd journal': enable_systemd_journal,
+ 'at command via dbus': enable_at_command_via_dbus,
+}, section: 'Features')
+
+summary(plugins_shared, section: 'Shared utils')
+
+summary(plugins_options, section: 'Plugins')
+
+summary({
+ 'gobject introspection': enable_gir,
+ 'Man': enable_man,
+ 'Documentation': enable_gtk_doc,
+ 'bash completion': enable_bash_completion,
+ 'vala bindings': enable_vapi,
+ 'code coverage': get_option('b_coverage'),
+}, section: 'Miscellaneous')
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 00000000..cef6d089
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,95 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+option('udev', type: 'feature', value: 'auto', description: 'enable udev support')
+option('udevdir', type: 'string', value: '', description: 'udev base directory')
+
+option('dbus_policy_dir', type: 'string', value: '', description: 'd-bus system policy directory')
+
+option('systemdsystemunitdir', type: 'string', value: '', description: 'systemd system units directory')
+option('systemd_suspend_resume', type: 'boolean', value: true, description: 'enable suspend/resume support')
+option('systemd_journal', type: 'boolean', value: true, description: 'enable systemd journal support')
+
+option('polkit', type: 'combo', choices: ['strict', 'permissive', 'no'], value: 'strict', description: 'User auth-polkit configuration option.')
+
+option('at_command_via_dbus', type: 'boolean', value: false, description: 'enable at commands vida d-bus')
+
+option('mbim', type: 'feature', value: 'auto', description: 'enable MBIM support')
+option('qmi', type: 'feature', value: 'auto', description: 'enable QMI support')
+option('qrtr', type: 'feature', value: 'auto', description: 'enable QRTR support')
+
+option('dist_version', type: 'string', value: '', description: 'define the custom version (like distribution package name and revision')
+
+option('plugin_generic', type: 'feature', value: 'auto', description: 'enable generic plugin support')
+option('plugin_altair_lte', type: 'feature', value: 'auto', description: 'enable altair lte plugin support')
+option('plugin_anydata', type: 'feature', value: 'auto', description: 'enable anydata plugin support')
+option('plugin_broadmobi', type: 'feature', value: 'auto', description: 'enable broadmobi plugin support')
+option('plugin_cinterion', type: 'feature', value: 'auto', description: 'enable cinterion plugin support')
+
+# shared_sierra, shared_novatel, shared_xmm, shared_telit, shared_foxonn
+option('plugin_dell', type: 'feature', value: 'auto', description: 'enable dell plugin support')
+
+option('plugin_dlink', type: 'feature', value: 'auto', description: 'enable dlink plugin support')
+
+# shared_xmm
+option('plugin_fibocom', type: 'feature', value: 'auto', description: 'enable fibocom plugin support')
+
+# shared_foxconn
+option('plugin_foxconn', type: 'feature', value: 'auto', description: 'enable foxconn plugin support')
+option('plugin_gosuncn', type: 'feature', value: 'auto', description: 'enable gosuncn plugin support')
+option('plugin_haier', type: 'feature', value: 'auto', description: 'enable haier plugin support')
+option('plugin_huawei', type: 'feature', value: 'auto', description: 'enable huawei plugin support')
+option('plugin_iridium', type: 'feature', value: 'auto', description: 'enable iridium plugin support')
+option('plugin_linktop', type: 'feature', value: 'auto', description: 'enable linktop plugin support')
+option('plugin_longcheer', type: 'feature', value: 'auto', description: 'enable longcheer plugin support')
+option('plugin_mbm', type: 'feature', value: 'auto', description: 'enable mbm plugin support')
+option('plugin_motorola', type: 'feature', value: 'auto', description: 'enable motorola plugin support')
+option('plugin_mtk', type: 'feature', value: 'auto', description: 'enable mtk plugin support')
+option('plugin_nokia', type: 'feature', value: 'auto', description: 'enable nokia plugin support')
+
+# shared_icera
+option('plugin_nokia_icera', type: 'feature', value: 'auto', description: 'enable nokia icera plugin support')
+
+# shared_novatel
+option('plugin_novatel', type: 'feature', value: 'auto', description: 'enable novatel plugin support')
+option('plugin_novatel_lte', type: 'feature', value: 'auto', description: 'enable novatel lte plugin support')
+
+# shared_option
+option('plugin_option', type: 'feature', value: 'auto', description: 'enable option plugin support')
+
+# shared_option
+option('plugin_option_hso', type: 'feature', value: 'auto', description: 'enable option hso plugin support')
+option('plugin_pantech', type: 'feature', value: 'auto', description: 'enable pantech plugin support')
+
+option('plugin_qcom_soc', type: 'feature', value: 'auto', description: 'enable qcom soc plugin support')
+option('plugin_quectel', type: 'feature', value: 'auto', description: 'enable quectel plugin support')
+
+# shared_icera
+option('plugin_samsung', type: 'feature', value: 'auto', description: 'enable samsung plugin support')
+
+# shared_icera, shared_sierra
+option('plugin_sierra_legacy', type: 'feature', value: 'auto', description: 'enable sierra legacy plugin support')
+
+# shared_xmm
+option('plugin_sierra', type: 'feature', value: 'auto', description: 'enable sierra plugin support')
+option('plugin_simtech', type: 'feature', value: 'auto', description: 'enable simtech plugin support')
+
+# shared_telit
+option('plugin_telit', type: 'feature', value: 'auto', description: 'enable telit plugin support')
+option('plugin_thuraya', type: 'feature', value: 'auto', description: 'enable thuraya plugin support')
+option('plugin_tplink', type: 'feature', value: 'auto', description: 'enable tplink plugin support')
+option('plugin_ublox', type: 'feature', value: 'auto', description: 'enable ublox plugin support')
+option('plugin_via', type: 'feature', value: 'auto', description: 'enable via plugin support')
+option('plugin_wavecom', type: 'feature', value: 'auto', description: 'enable wavecom plugin support')
+option('plugin_x22x', type: 'feature', value: 'auto', description: 'enable x22x plugin support')
+
+# shared_icera
+option('plugin_zte', type: 'feature', value: 'auto', description: 'enable zte plugin support')
+
+option('introspection', type: 'feature', value: 'auto', description: 'build introspection support')
+option('vapi', type: 'boolean', value: false, description: 'build vala bindings')
+
+option('man', type: 'boolean', value: true, description: 'build manual pages')
+option('gtk_doc', type: 'boolean', value: false, description: 'use gtk-doc to build documentation')
+
+option('bash_completion', type: 'boolean', value: true, description: 'install bash completion files')
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
index d2cbcb1c..5e2d4a14 100644
--- a/plugins/Makefile.am
+++ b/plugins/Makefile.am
@@ -1,39 +1,64 @@
include $(top_srcdir)/gtester.make
-# Common CPPFLAGS and LDFLAGS
+################################################################################
+# common
+################################################################################
-PLUGIN_COMMON_COMPILER_FLAGS = \
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
$(MM_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
$(GUDEV_CFLAGS) \
+ -DPKGDATADIR=\"${pkgdatadir}\" \
-I$(top_srcdir) \
-I$(top_srcdir)/src \
-I$(top_builddir)/src \
+ -I$(top_srcdir)/src/kerneldevice \
-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
+ -I$(top_builddir)/libmm-glib/generated \
+ $(NULL)
-PLUGIN_COMMON_LINKER_FLAGS = \
- $(GUDEV_LIBS) \
- $(MM_LIBS) \
- -module \
- -avoid-version
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(MM_LIBS) \
+ $(CODE_COVERAGE_LDFLAGS) \
+ $(GUDEV_LIBS) \
+ $(NULL)
if WITH_QMI
-PLUGIN_COMMON_COMPILER_FLAGS += $(QMI_CFLAGS)
-PLUGIN_COMMON_LINKER_FLAGS += $(QMI_LIBS)
+AM_CFLAGS += $(QMI_CFLAGS)
+AM_LDFLAGS += $(QMI_LIBS)
endif
if WITH_MBIM
-PLUGIN_COMMON_COMPILER_FLAGS += $(MBIM_CFLAGS)
-PLUGIN_COMMON_LINKER_FLAGS += $(MBIM_LIBS)
+AM_CFLAGS += $(MBIM_CFLAGS)
+AM_LDFLAGS += $(MBIM_LIBS)
endif
+# Common compiler/linker flags for shared utils
+SHARED_COMMON_COMPILER_FLAGS = \
+ $(NULL)
+SHARED_COMMON_LINKER_FLAGS = \
+ -module \
+ -avoid-version \
+ $(NULL)
+
+# Common compiler/linker flags for plugins
+PLUGIN_COMMON_COMPILER_FLAGS = \
+ $(NULL)
+PLUGIN_COMMON_LINKER_FLAGS = \
+ -module \
+ -avoid-version \
+ -export-symbols-regex '^mm_plugin_major_version$$|^mm_plugin_minor_version$$|^mm_plugin_create$$' \
+ $(NULL)
+
# UDev rules
udevrulesdir = $(UDEV_BASE_DIR)/rules.d
-udevrules_DATA =
+dist_udevrules_DATA =
# Unit tests
noinst_PROGRAMS =
@@ -41,32 +66,41 @@ noinst_PROGRAMS =
# Helper libs
noinst_LTLIBRARIES =
-########################################
+# Plugins
+pkglib_LTLIBRARIES =
-# Common service test support
+# Built sources
+BUILT_SOURCES =
-noinst_LTLIBRARIES += libmm-test-common.la
+# Clean files
+CLEANFILES =
+
+# Data files
+dist_pkgdata_DATA =
+################################################################################
+# common service test support
+################################################################################
+
+noinst_LTLIBRARIES += libmm-test-common.la
libmm_test_common_la_SOURCES = \
tests/test-fixture.h \
tests/test-fixture.c \
tests/test-port-context.h \
- tests/test-port-context.c
-
+ tests/test-port-context.c \
+ tests/test-helpers.h \
+ tests/test-helpers.c \
+ $(NULL)
libmm_test_common_la_CPPFLAGS = \
- $(MM_CFLAGS) \
- -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 \
-I$(top_builddir)/libmm-glib/generated/tests \
- -DTEST_SERVICES=\""$(abs_top_builddir)/data/tests"\"
-
+ -DTEST_SERVICES=\""$(abs_top_builddir)/data/tests"\" \
+ $(NULL)
libmm_test_common_la_LIBADD = \
${top_builddir}/libmm-glib/generated/tests/libmm-test-generated.la \
$(top_builddir)/libmm-glib/libmm-glib.la
+EXTRA_DIST += tests/gsm-port.conf
+
TEST_COMMON_COMPILER_FLAGS = \
$(MM_CFLAGS) \
-I$(top_srcdir)/plugins/tests \
@@ -76,446 +110,1631 @@ TEST_COMMON_COMPILER_FLAGS = \
-I$(top_srcdir)/libmm-glib/generated \
-I$(top_builddir)/libmm-glib/generated \
-I$(top_builddir)/libmm-glib/generated/tests \
- -DCOMMON_GSM_PORT_CONF=\""$(abs_top_srcdir)/plugins/tests/gsm-port.conf"\"
+ -DCOMMON_GSM_PORT_CONF=\""$(abs_top_srcdir)/plugins/tests/gsm-port.conf"\" \
+ $(NULL)
TEST_COMMON_LIBADD_FLAGS = \
$(builddir)/libmm-test-common.la \
- $(top_builddir)/libmm-glib/libmm-glib.la
+ $(NULL)
+################################################################################
+# common icera support
+################################################################################
-########################################
+if WITH_SHARED_ICERA
-# Icera-specific support
-noinst_LTLIBRARIES += libmm-utils-icera.la
-libmm_utils_icera_la_SOURCES = \
- icera/mm-broadband-modem-icera.h \
- icera/mm-broadband-modem-icera.c \
- icera/mm-broadband-bearer-icera.h \
- icera/mm-broadband-bearer-icera.c \
+noinst_LTLIBRARIES += libhelpers-icera.la
+libhelpers_icera_la_SOURCES = \
icera/mm-modem-helpers-icera.c \
- icera/mm-modem-helpers-icera.h
-libmm_utils_icera_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_utils_icera_la_LIBADD = $(GUDEV_LIBS) $(MM_LIBS)
-
-ICERA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/icera
-ICERA_COMMON_LIBADD_FLAGS = $(builddir)/libmm-utils-icera.la
+ icera/mm-modem-helpers-icera.h \
+ $(NULL)
+libhelpers_icera_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"shared-icera\" \
+ $(NULL)
noinst_PROGRAMS += test-modem-helpers-icera
test_modem_helpers_icera_SOURCES = \
- icera/mm-modem-helpers-icera.c \
- icera/mm-modem-helpers-icera.h \
- icera/tests/test-modem-helpers-icera.c
+ icera/tests/test-modem-helpers-icera.c \
+ $(NULL)
test_modem_helpers_icera_CPPFLAGS = \
-I$(top_srcdir)/plugins/icera \
- $(PLUGIN_COMMON_COMPILER_FLAGS)
+ $(NULL)
test_modem_helpers_icera_LDADD = \
- $(top_builddir)/libmm-glib/libmm-glib.la \
- $(top_builddir)/src/libmodem-helpers.la
-test_modem_helpers_icera_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-
-########################################
-
-pkglib_LTLIBRARIES = \
- libmm-plugin-generic.la \
- libmm-plugin-nokia.la \
- libmm-plugin-nokia-icera.la \
- libmm-plugin-cinterion.la \
- libmm-plugin-iridium.la \
- libmm-plugin-gobi.la \
- libmm-plugin-motorola.la \
- libmm-plugin-novatel.la \
- libmm-plugin-novatel-lte.la \
- libmm-plugin-altair-lte.la \
- libmm-plugin-samsung.la \
- libmm-plugin-option.la \
- libmm-plugin-hso.la \
- libmm-plugin-anydata.la \
- libmm-plugin-linktop.la \
- libmm-plugin-simtech.la \
- libmm-plugin-wavecom.la \
- libmm-plugin-huawei.la \
- libmm-plugin-longcheer.la \
- libmm-plugin-x22x.la \
- libmm-plugin-pantech.la \
- libmm-plugin-zte.la \
- libmm-plugin-sierra.la \
- libmm-plugin-mbm.la \
- libmm-plugin-via.la \
- libmm-plugin-telit.la \
- libmm-plugin-mtk.la
-
-# Generic
+ $(builddir)/libhelpers-icera.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-shared-icera.la
+libmm_shared_icera_la_SOURCES = \
+ icera/mm-shared.c \
+ icera/mm-broadband-modem-icera.h \
+ icera/mm-broadband-modem-icera.c \
+ icera/mm-broadband-bearer-icera.h \
+ icera/mm-broadband-bearer-icera.c \
+ $(NULL)
+libmm_shared_icera_la_CPPFLAGS = \
+ $(SHARED_COMMON_COMPILER_FLAGS)
+ -DMM_MODULE_NAME=\"shared-icera\" \
+ $(NULL)
+libmm_shared_icera_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+libmm_shared_icera_la_LIBADD = \
+ $(builddir)/libhelpers-icera.la \
+ $(NULL)
+
+ICERA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/icera
+
+endif
+
+################################################################################
+# common sierra support
+################################################################################
+
+if WITH_SHARED_SIERRA
+
+noinst_LTLIBRARIES += libhelpers-sierra.la
+libhelpers_sierra_la_SOURCES = \
+ sierra/mm-modem-helpers-sierra.c \
+ sierra/mm-modem-helpers-sierra.h \
+ $(NULL)
+libhelpers_sierra_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"shared-sierra\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-sierra
+test_modem_helpers_sierra_SOURCES = \
+ sierra/tests/test-modem-helpers-sierra.c \
+ $(NULL)
+test_modem_helpers_sierra_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/sierra \
+ $(NULL)
+test_modem_helpers_sierra_LDADD = \
+ $(builddir)/libhelpers-sierra.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-shared-sierra.la
+libmm_shared_sierra_la_SOURCES = \
+ sierra/mm-shared.c \
+ sierra/mm-common-sierra.c \
+ sierra/mm-common-sierra.h \
+ sierra/mm-sim-sierra.c \
+ sierra/mm-sim-sierra.h \
+ sierra/mm-broadband-bearer-sierra.c \
+ sierra/mm-broadband-bearer-sierra.h \
+ sierra/mm-broadband-modem-sierra.c \
+ sierra/mm-broadband-modem-sierra.h \
+ $(NULL)
+libmm_shared_sierra_la_CPPFLAGS = \
+ $(SHARED_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"shared-sierra\" \
+ $(NULL)
+libmm_shared_sierra_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+libmm_shared_sierra_la_LIBADD = \
+ $(builddir)/libhelpers-sierra.la \
+ $(NULL)
+
+SIERRA_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/sierra
+
+endif
+
+################################################################################
+# common option support
+################################################################################
+
+if WITH_SHARED_OPTION
+
+pkglib_LTLIBRARIES += libmm-shared-option.la
+libmm_shared_option_la_SOURCES = \
+ option/mm-shared.c \
+ option/mm-shared-option.h \
+ option/mm-shared-option.c \
+ option/mm-sim-option.c \
+ option/mm-sim-option.h \
+ option/mm-broadband-modem-option.c \
+ option/mm-broadband-modem-option.h \
+ $(NULL)
+libmm_shared_option_la_CPPFLAGS = $(SHARED_COMMON_COMPILER_FLAGS)
+libmm_shared_option_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+
+OPTION_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/option
+
+endif
+
+################################################################################
+# common novatel support
+################################################################################
+
+if WITH_SHARED_NOVATEL
+
+# Common Novatel modem support library
+pkglib_LTLIBRARIES += libmm-shared-novatel.la
+libmm_shared_novatel_la_SOURCES = \
+ novatel/mm-shared.c \
+ novatel/mm-common-novatel.c \
+ novatel/mm-common-novatel.h \
+ novatel/mm-broadband-modem-novatel.c \
+ novatel/mm-broadband-modem-novatel.h \
+ $(NULL)
+libmm_shared_novatel_la_CPPFLAGS = \
+ $(SHARED_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"shared-novatel\" \
+ $(NULL)
+libmm_shared_novatel_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+
+NOVATEL_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/novatel
+
+endif
+
+################################################################################
+# common xmm support
+################################################################################
+
+if WITH_SHARED_XMM
+
+noinst_LTLIBRARIES += libhelpers-xmm.la
+libhelpers_xmm_la_SOURCES = \
+ xmm/mm-modem-helpers-xmm.c \
+ xmm/mm-modem-helpers-xmm.h \
+ $(NULL)
+libhelpers_xmm_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"shared-xmm\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-xmm
+test_modem_helpers_xmm_SOURCES = \
+ xmm/tests/test-modem-helpers-xmm.c \
+ $(NULL)
+test_modem_helpers_xmm_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/xmm \
+ $(NULL)
+test_modem_helpers_xmm_LDADD = \
+ $(builddir)/libhelpers-xmm.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-shared-xmm.la
+libmm_shared_xmm_la_SOURCES = \
+ xmm/mm-shared.c \
+ xmm/mm-shared-xmm.h \
+ xmm/mm-shared-xmm.c \
+ xmm/mm-broadband-modem-xmm.h \
+ xmm/mm-broadband-modem-xmm.c \
+ $(NULL)
+
+if WITH_MBIM
+libmm_shared_xmm_la_SOURCES += \
+ xmm/mm-broadband-modem-mbim-xmm.h \
+ xmm/mm-broadband-modem-mbim-xmm.c \
+ $(NULL)
+endif
+
+libmm_shared_xmm_la_CPPFLAGS = \
+ $(SHARED_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"shared-xmm\" \
+ $(NULL)
+libmm_shared_xmm_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+libmm_shared_xmm_la_LIBADD = \
+ $(builddir)/libhelpers-xmm.la \
+ $(NULL)
+
+XMM_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/xmm
+
+endif
+
+################################################################################
+# common telit support
+################################################################################
+
+if WITH_SHARED_TELIT
+
+TELIT_ENUMS_INPUTS = \
+ $(top_srcdir)/plugins/telit/mm-modem-helpers-telit.h \
+ $(NULL)
+
+TELIT_ENUMS_GENERATED = \
+ telit/mm-telit-enums-types.h \
+ telit/mm-telit-enums-types.c \
+ $(NULL)
+
+BUILT_SOURCES += $(TELIT_ENUMS_GENERATED)
+CLEANFILES += $(TELIT_ENUMS_GENERATED)
+
+telit/mm-telit-enums-types.h: Makefile.am $(TELIT_ENUMS_INPUTS) $(top_srcdir)/build-aux/mm-enums-types.h.template
+ $(AM_V_GEN) \
+ $(MKDIR_P) telit; \
+ $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-modem-helpers-telit.h\"\n#ifndef __MM_TELIT_ENUMS_TYPES_H__\n#define __MM_TELIT_ENUMS_TYPES_H__\n" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
+ --ftail "#endif /* __MM_TELIT_ENUMS_TYPES_H__ */\n" \
+ $(TELIT_ENUMS_INPUTS) > $@
+
+telit/mm-telit-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-types.c.template telit/mm-telit-enums-types.h
+ $(AM_V_GEN) \
+ $(MKDIR_P) telit; \
+ $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-telit-enums-types.h\"" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
+ $(TELIT_ENUMS_INPUTS) > $@
+
+noinst_LTLIBRARIES += libhelpers-telit.la
+libhelpers_telit_la_SOURCES = \
+ telit/mm-modem-helpers-telit.c \
+ telit/mm-modem-helpers-telit.h \
+ $(NULL)
+libhelpers_telit_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"shared-telit\" \
+ -I$(top_srcdir)/plugins/telit \
+ -I$(top_builddir)/plugins/telit \
+ $(NULL)
+nodist_libhelpers_telit_la_SOURCES = $(TELIT_ENUMS_GENERATED)
+
+noinst_PROGRAMS += test-modem-helpers-telit
+test_modem_helpers_telit_SOURCES = \
+ telit/tests/test-mm-modem-helpers-telit.c \
+ $(NULL)
+test_modem_helpers_telit_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/telit \
+ -I$(top_builddir)/plugins/telit \
+ $(TEST_COMMON_COMPILER_FLAGS) \
+ $(NULL)
+test_modem_helpers_telit_LDADD = \
+ $(TEST_COMMON_LIBADD_FLAGS) \
+ $(builddir)/libhelpers-telit.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+# Common telit modem support library
+pkglib_LTLIBRARIES += libmm-shared-telit.la
+libmm_shared_telit_la_SOURCES = \
+ telit/mm-shared.c \
+ telit/mm-common-telit.c \
+ telit/mm-common-telit.h \
+ telit/mm-shared-telit.c \
+ telit/mm-shared-telit.h \
+ telit/mm-broadband-modem-telit.c \
+ telit/mm-broadband-modem-telit.h \
+ $(NULL)
+if WITH_MBIM
+libmm_shared_telit_la_SOURCES += \
+ telit/mm-broadband-modem-mbim-telit.h \
+ telit/mm-broadband-modem-mbim-telit.c \
+ $(NULL)
+endif
+
+libmm_shared_telit_la_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/telit \
+ -I$(top_builddir)/plugins/telit \
+ $(SHARED_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"shared-telit\" \
+ $(NULL)
+libmm_shared_telit_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+libmm_shared_telit_la_LIBADD = \
+ $(builddir)/libhelpers-telit.la \
+ $(NULL)
+
+TELIT_COMMON_COMPILER_FLAGS = \
+ -I$(top_srcdir)/plugins/telit \
+ -I$(top_builddir)/plugins/telit \
+ $(NULL)
+
+endif
+
+################################################################################
+# common foxconn support
+################################################################################
+
+# Common Foxconn modem support library (MBIM only)
+if WITH_SHARED_FOXCONN
+if WITH_MBIM
+pkglib_LTLIBRARIES += libmm-shared-foxconn.la
+libmm_shared_foxconn_la_SOURCES = \
+ foxconn/mm-shared.c \
+ foxconn/mm-broadband-modem-mbim-foxconn.c \
+ foxconn/mm-broadband-modem-mbim-foxconn.h \
+ $(NULL)
+libmm_shared_foxconn_la_CPPFLAGS = \
+ $(SHARED_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"shared-foxconn\" \
+ $(NULL)
+libmm_shared_foxconn_la_LDFLAGS = $(SHARED_COMMON_LINKER_FLAGS)
+
+FOXCONN_COMMON_COMPILER_FLAGS = -I$(top_srcdir)/plugins/foxconn
+endif
+endif
+
+################################################################################
+# plugin: generic
+################################################################################
+
+if ENABLE_PLUGIN_GENERIC
+
+pkglib_LTLIBRARIES += libmm-plugin-generic.la
libmm_plugin_generic_la_SOURCES = \
generic/mm-plugin-generic.c \
- generic/mm-plugin-generic.h
-libmm_plugin_generic_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_generic_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+ generic/mm-plugin-generic.h \
+ $(NULL)
+libmm_plugin_generic_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"generic\" \
+ $(NULL)
+libmm_plugin_generic_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
noinst_PROGRAMS += test-service-generic
-test_service_generic_SOURCES = generic/tests/test-service-generic.c
+test_service_generic_SOURCES = generic/tests/test-service-generic.c
test_service_generic_CPPFLAGS = $(TEST_COMMON_COMPILER_FLAGS)
-test_service_generic_LDADD = $(TEST_COMMON_LIBADD_FLAGS)
-test_service_generic_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+test_service_generic_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(TEST_COMMON_LIBADD_FLAGS) \
+ $(NULL)
-## Motorola
-libmm_plugin_motorola_la_SOURCES = \
- motorola/mm-plugin-motorola.c \
- motorola/mm-plugin-motorola.h \
- motorola/mm-broadband-modem-motorola.c \
- motorola/mm-broadband-modem-motorola.h
-libmm_plugin_motorola_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_motorola_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+endif
+
+################################################################################
+# plugin: altair lte
+################################################################################
+
+if ENABLE_PLUGIN_ALTAIR_LTE
+
+noinst_LTLIBRARIES += libhelpers-altair-lte.la
+libhelpers_altair_lte_la_SOURCES = \
+ altair/mm-modem-helpers-altair-lte.c \
+ altair/mm-modem-helpers-altair-lte.h \
+ $(NULL)
+libhelpers_altair_lte_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"altair-lte\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-altair-lte
+test_modem_helpers_altair_lte_SOURCES = \
+ altair/tests/test-modem-helpers-altair-lte.c \
+ $(NULL)
+test_modem_helpers_altair_lte_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/altair \
+ $(NULL)
+test_modem_helpers_altair_lte_LDADD = \
+ $(builddir)/libhelpers-altair-lte.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-altair-lte.la
+libmm_plugin_altair_lte_la_SOURCES = \
+ altair/mm-plugin-altair-lte.c \
+ altair/mm-plugin-altair-lte.h \
+ altair/mm-broadband-modem-altair-lte.c \
+ altair/mm-broadband-modem-altair-lte.h \
+ altair/mm-broadband-bearer-altair-lte.c \
+ altair/mm-broadband-bearer-altair-lte.h \
+ $(NULL)
+libmm_plugin_altair_lte_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"altair-lte\" \
+ $(NULL)
+libmm_plugin_altair_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_altair_lte_la_LIBADD = $(builddir)/libhelpers-altair-lte.la
+
+endif
+
+################################################################################
+# plugin: anydata
+################################################################################
+
+if ENABLE_PLUGIN_ANYDATA
+
+pkglib_LTLIBRARIES += libmm-plugin-anydata.la
+libmm_plugin_anydata_la_SOURCES = \
+ anydata/mm-plugin-anydata.c \
+ anydata/mm-plugin-anydata.h \
+ anydata/mm-broadband-modem-anydata.h \
+ anydata/mm-broadband-modem-anydata.c \
+ $(NULL)
+libmm_plugin_anydata_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"anydata\" \
+ $(NULL)
+libmm_plugin_anydata_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: broadmobi
+################################################################################
+
+if ENABLE_PLUGIN_BROADMOBI
+
+pkglib_LTLIBRARIES += libmm-plugin-broadmobi.la
+libmm_plugin_broadmobi_la_SOURCES = \
+ broadmobi/mm-plugin-broadmobi.c \
+ broadmobi/mm-plugin-broadmobi.h \
+ $(NULL)
+libmm_plugin_broadmobi_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"broadmobi\" \
+ $(NULL)
+libmm_plugin_broadmobi_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += broadmobi/77-mm-broadmobi-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_BROADMOBI=\"${srcdir}/broadmobi\"
+
+endif
+
+################################################################################
+# plugin: cinterion (previously siemens)
+################################################################################
+
+if ENABLE_PLUGIN_CINTERION
+
+noinst_LTLIBRARIES += libhelpers-cinterion.la
+libhelpers_cinterion_la_SOURCES = \
+ cinterion/mm-modem-helpers-cinterion.c \
+ cinterion/mm-modem-helpers-cinterion.h \
+ $(NULL)
+libhelpers_cinterion_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"cinterion\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-cinterion
+test_modem_helpers_cinterion_SOURCES = \
+ cinterion/tests/test-modem-helpers-cinterion.c \
+ $(NULL)
+test_modem_helpers_cinterion_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/cinterion \
+ $(NULL)
+test_modem_helpers_cinterion_LDADD = \
+ $(builddir)/libhelpers-cinterion.la \
+ $(top_builddir)/src/libport.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-cinterion.la
+libmm_plugin_cinterion_la_SOURCES = \
+ cinterion/mm-plugin-cinterion.c \
+ cinterion/mm-plugin-cinterion.h \
+ cinterion/mm-shared-cinterion.c \
+ cinterion/mm-shared-cinterion.h \
+ cinterion/mm-broadband-modem-cinterion.c \
+ cinterion/mm-broadband-modem-cinterion.h \
+ cinterion/mm-broadband-bearer-cinterion.c \
+ cinterion/mm-broadband-bearer-cinterion.h \
+ $(NULL)
+if WITH_QMI
+libmm_plugin_cinterion_la_SOURCES += \
+ cinterion/mm-broadband-modem-qmi-cinterion.c \
+ cinterion/mm-broadband-modem-qmi-cinterion.h \
+ $(NULL)
+endif
+if WITH_MBIM
+libmm_plugin_cinterion_la_SOURCES += \
+ cinterion/mm-broadband-modem-mbim-cinterion.c \
+ cinterion/mm-broadband-modem-mbim-cinterion.h \
+ $(NULL)
+endif
+libmm_plugin_cinterion_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"cinterion\" \
+ $(NULL)
+libmm_plugin_cinterion_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_cinterion_la_LIBADD = $(builddir)/libhelpers-cinterion.la
+
+dist_udevrules_DATA += cinterion/77-mm-cinterion-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_CINTERION=\"${srcdir}/cinterion\"
+
+endif
+
+################################################################################
+# plugin: dell (novatel, sierra, telit or foxconn)
+################################################################################
+
+if ENABLE_PLUGIN_DELL
+
+pkglib_LTLIBRARIES += libmm-plugin-dell.la
+libmm_plugin_dell_la_SOURCES = \
+ dell/mm-plugin-dell.c \
+ dell/mm-plugin-dell.h \
+ $(NULL)
+
+libmm_plugin_dell_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(NOVATEL_COMMON_COMPILER_FLAGS) \
+ $(SIERRA_COMMON_COMPILER_FLAGS) \
+ $(TELIT_COMMON_COMPILER_FLAGS) \
+ $(XMM_COMMON_COMPILER_FLAGS) \
+ $(FOXCONN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"dell\" \
+ $(NULL)
+libmm_plugin_dell_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += dell/77-mm-dell-port-types.rules
+
+AM_CFLAGS += \
+ -DTESTUDEVRULESDIR_DELL=\"${srcdir}/dell\" \
+ $(NULL)
+
+endif
+
+################################################################################
+# plugin: dlink
+################################################################################
+
+if ENABLE_PLUGIN_DLINK
+
+pkglib_LTLIBRARIES += libmm-plugin-dlink.la
+libmm_plugin_dlink_la_SOURCES = \
+ dlink/mm-plugin-dlink.c \
+ dlink/mm-plugin-dlink.h \
+ $(NULL)
+libmm_plugin_dlink_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"d-link\" \
+ $(NULL)
+libmm_plugin_dlink_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += dlink/77-mm-dlink-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_DLINK=\"${srcdir}/dlink\"
+
+endif
+
+################################################################################
+# plugin: fibocom
+################################################################################
+
+if ENABLE_PLUGIN_FIBOCOM
+
+pkglib_LTLIBRARIES += libmm-plugin-fibocom.la
+libmm_plugin_fibocom_la_SOURCES = \
+ fibocom/mm-plugin-fibocom.c \
+ fibocom/mm-plugin-fibocom.h \
+ $(NULL)
+libmm_plugin_fibocom_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(XMM_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"fibocom\" \
+ $(NULL)
+libmm_plugin_fibocom_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += fibocom/77-mm-fibocom-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_FIBOCOM=\"${srcdir}/fibocom\"
+
+endif
+
+################################################################################
+# plugin: foxconn
+################################################################################
-# Gobi
-libmm_plugin_gobi_la_SOURCES = \
- gobi/mm-plugin-gobi.c \
- gobi/mm-plugin-gobi.h \
- gobi/mm-broadband-modem-gobi.c \
- gobi/mm-broadband-modem-gobi.h
-libmm_plugin_gobi_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_gobi_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+if ENABLE_PLUGIN_FOXCONN
-# Huawei
+pkglib_LTLIBRARIES += libmm-plugin-foxconn.la
+libmm_plugin_foxconn_la_SOURCES = \
+ foxconn/mm-plugin-foxconn.c \
+ foxconn/mm-plugin-foxconn.h \
+ $(NULL)
+libmm_plugin_foxconn_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(FOXCONN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"foxconn\" \
+ $(NULL)
+libmm_plugin_foxconn_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += foxconn/77-mm-foxconn-port-types.rules
+
+dist_pkgdata_DATA += \
+ foxconn/mm-foxconn-t77w968-carrier-mapping.conf \
+ $(NULL)
+
+AM_CFLAGS += \
+ -DTESTUDEVRULESDIR_FOXCONN=\"${srcdir}/foxconn\" \
+ -DTESTKEYFILE_FOXCONN_T77W968=\"${srcdir}/foxconn/mm-foxconn-t77w968-carrier-mapping.conf\" \
+ $(NULL)
+
+endif
+
+################################################################################
+# plugin: gosuncn
+################################################################################
+
+if ENABLE_PLUGIN_GOSUNCN
+
+pkglib_LTLIBRARIES += libmm-plugin-gosuncn.la
+libmm_plugin_gosuncn_la_SOURCES = \
+ gosuncn/mm-plugin-gosuncn.c \
+ gosuncn/mm-plugin-gosuncn.h \
+ $(NULL)
+libmm_plugin_gosuncn_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"gosuncn\" \
+ $(NULL)
+libmm_plugin_gosuncn_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += gosuncn/77-mm-gosuncn-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_GOSUNCN=\"${srcdir}/gosuncn\"
+
+endif
+
+################################################################################
+# plugin: haier
+################################################################################
+
+if ENABLE_PLUGIN_HAIER
+
+pkglib_LTLIBRARIES += libmm-plugin-haier.la
+libmm_plugin_haier_la_SOURCES = \
+ haier/mm-plugin-haier.c \
+ haier/mm-plugin-haier.h \
+ $(NULL)
+libmm_plugin_haier_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"haier\" \
+ $(NULL)
+libmm_plugin_haier_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += haier/77-mm-haier-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_HAIER=\"${srcdir}/haier\"
+
+endif
+
+################################################################################
+# plugin: huawei
+################################################################################
+
+if ENABLE_PLUGIN_HUAWEI
+
+HUAWEI_ENUMS_INPUTS = \
+ $(top_srcdir)/plugins/huawei/mm-modem-helpers-huawei.h \
+ $(NULL)
+
+HUAWEI_ENUMS_GENERATED = \
+ huawei/mm-huawei-enums-types.h \
+ huawei/mm-huawei-enums-types.c \
+ $(NULL)
+
+BUILT_SOURCES += $(HUAWEI_ENUMS_GENERATED)
+CLEANFILES += $(HUAWEI_ENUMS_GENERATED)
+
+huawei/mm-huawei-enums-types.h: Makefile.am $(HUAWEI_ENUMS_INPUTS) $(top_srcdir)/build-aux/mm-enums-types.h.template
+ $(AM_V_GEN) \
+ $(MKDIR_P) huawei; \
+ $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-modem-helpers-huawei.h\"\n#ifndef __MM_HUAWEI_ENUMS_TYPES_H__\n#define __MM_HUAWEI_ENUMS_TYPES_H__\n" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
+ --ftail "#endif /* __MM_HUAWEI_ENUMS_TYPES_H__ */\n" \
+ $(HUAWEI_ENUMS_INPUTS) > $@
+
+huawei/mm-huawei-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-types.c.template huawei/mm-huawei-enums-types.h
+ $(AM_V_GEN) \
+ $(MKDIR_P) huawei; \
+ $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-huawei-enums-types.h\"" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
+ $(HUAWEI_ENUMS_INPUTS) > $@
+
+noinst_LTLIBRARIES += libhelpers-huawei.la
+libhelpers_huawei_la_SOURCES = \
+ huawei/mm-modem-helpers-huawei.c \
+ huawei/mm-modem-helpers-huawei.h \
+ $(NULL)
+libhelpers_huawei_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"huawei\" \
+ -I$(top_srcdir)/plugins/huawei \
+ -I$(top_builddir)/plugins/huawei \
+ $(NULL)
+nodist_libhelpers_huawei_la_SOURCES = $(HUAWEI_ENUMS_GENERATED)
+
+noinst_PROGRAMS += test-modem-helpers-huawei
+test_modem_helpers_huawei_SOURCES = \
+ huawei/tests/test-modem-helpers-huawei.c \
+ $(NULL)
+test_modem_helpers_huawei_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/huawei \
+ -I$(top_builddir)/plugins/huawei \
+ $(NULL)
+test_modem_helpers_huawei_LDADD = \
+ $(builddir)/libhelpers-huawei.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-huawei.la
libmm_plugin_huawei_la_SOURCES = \
huawei/mm-plugin-huawei.c \
huawei/mm-plugin-huawei.h \
- huawei/mm-modem-helpers-huawei.c \
- huawei/mm-modem-helpers-huawei.h \
huawei/mm-sim-huawei.c \
huawei/mm-sim-huawei.h \
huawei/mm-broadband-modem-huawei.c \
huawei/mm-broadband-modem-huawei.h \
huawei/mm-broadband-bearer-huawei.c \
- huawei/mm-broadband-bearer-huawei.h
-libmm_plugin_huawei_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+ huawei/mm-broadband-bearer-huawei.h \
+ $(NULL)
+libmm_plugin_huawei_la_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/huawei \
+ -I$(top_builddir)/plugins/huawei \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"huawei\" \
+ $(NULL)
libmm_plugin_huawei_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_huawei_la_LIBADD = $(builddir)/libhelpers-huawei.la
-udevrules_DATA += huawei/77-mm-huawei-net-port-types.rules
+dist_udevrules_DATA += huawei/77-mm-huawei-net-port-types.rules
-noinst_PROGRAMS += test-modem-helpers-huawei
-test_modem_helpers_huawei_SOURCES = \
- huawei/mm-modem-helpers-huawei.c \
- huawei/mm-modem-helpers-huawei.h \
- huawei/tests/test-modem-helpers-huawei.c
-test_modem_helpers_huawei_CPPFLAGS = \
- -I$(top_srcdir)/plugins/huawei \
- $(PLUGIN_COMMON_COMPILER_FLAGS)
-test_modem_helpers_huawei_LDADD = \
- $(top_builddir)/libmm-glib/libmm-glib.la \
- $(top_builddir)/src/libmodem-helpers.la
-test_modem_helpers_huawei_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+AM_CFLAGS += -DTESTUDEVRULESDIR_HUAWEI=\"${srcdir}/huawei\"
-# MBM
-libmm_plugin_mbm_la_SOURCES = \
- mbm/mm-plugin-mbm.c \
- mbm/mm-plugin-mbm.h \
- mbm/mm-broadband-modem-mbm.c \
- mbm/mm-broadband-modem-mbm.h \
- mbm/mm-broadband-bearer-mbm.c \
- mbm/mm-broadband-bearer-mbm.h \
+endif
+
+################################################################################
+# plugin: iridium
+################################################################################
+
+if ENABLE_PLUGIN_IRIDIUM
+
+pkglib_LTLIBRARIES += libmm-plugin-iridium.la
+libmm_plugin_iridium_la_SOURCES = \
+ iridium/mm-plugin-iridium.c \
+ iridium/mm-plugin-iridium.h \
+ iridium/mm-broadband-modem-iridium.c \
+ iridium/mm-broadband-modem-iridium.h \
+ iridium/mm-bearer-iridium.c \
+ iridium/mm-bearer-iridium.h \
+ iridium/mm-sim-iridium.c \
+ iridium/mm-sim-iridium.h \
+ $(NULL)
+libmm_plugin_iridium_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"iridium\" \
+ $(NULL)
+libmm_plugin_iridium_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: linktop
+################################################################################
+
+if ENABLE_PLUGIN_LINKTOP
+
+noinst_LTLIBRARIES += libhelpers-linktop.la
+libhelpers_linktop_la_SOURCES = \
+ linktop/mm-modem-helpers-linktop.c \
+ linktop/mm-modem-helpers-linktop.h \
+ $(NULL)
+libhelpers_linktop_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"linktop\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-linktop
+test_modem_helpers_linktop_SOURCES = \
+ linktop/tests/test-modem-helpers-linktop.c \
+ $(NULL)
+test_modem_helpers_linktop_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/linktop \
+ $(NULL)
+test_modem_helpers_linktop_LDADD = \
+ $(builddir)/libhelpers-linktop.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-linktop.la
+libmm_plugin_linktop_la_SOURCES = \
+ linktop/mm-plugin-linktop.c \
+ linktop/mm-plugin-linktop.h \
+ linktop/mm-broadband-modem-linktop.h \
+ linktop/mm-broadband-modem-linktop.c \
+ $(NULL)
+libmm_plugin_linktop_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"linktop\" \
+ $(NULL)
+libmm_plugin_linktop_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_linktop_la_LIBADD = $(builddir)/libhelpers-linktop.la
+
+endif
+
+################################################################################
+# plugin: longcheer (and rebranded dongles)
+################################################################################
+
+if ENABLE_PLUGIN_LONGCHEER
+
+pkglib_LTLIBRARIES += libmm-plugin-longcheer.la
+libmm_plugin_longcheer_la_SOURCES = \
+ longcheer/mm-plugin-longcheer.c \
+ longcheer/mm-plugin-longcheer.h \
+ longcheer/mm-broadband-modem-longcheer.h \
+ longcheer/mm-broadband-modem-longcheer.c \
+ $(NULL)
+libmm_plugin_longcheer_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"longcheer\" \
+ $(NULL)
+libmm_plugin_longcheer_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += longcheer/77-mm-longcheer-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_LONGCHEER=\"${srcdir}/longcheer\"
+
+endif
+
+################################################################################
+# plugin: ericsson mbm
+################################################################################
+
+if ENABLE_PLUGIN_MBM
+
+noinst_LTLIBRARIES += libhelpers-mbm.la
+libhelpers_mbm_la_SOURCES = \
mbm/mm-modem-helpers-mbm.c \
mbm/mm-modem-helpers-mbm.h \
- mbm/mm-sim-mbm.c \
- mbm/mm-sim-mbm.h
-libmm_plugin_mbm_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_mbm_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-udevrules_DATA += mbm/77-mm-ericsson-mbm.rules
+ $(NULL)
+libhelpers_mbm_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"ericsson-mbm\" \
+ $(NULL)
noinst_PROGRAMS += test-modem-helpers-mbm
test_modem_helpers_mbm_SOURCES = \
- mbm/mm-modem-helpers-mbm.c \
- mbm/mm-modem-helpers-mbm.h \
- mbm/tests/test-modem-helpers-mbm.c
+ mbm/tests/test-modem-helpers-mbm.c \
+ $(NULL)
test_modem_helpers_mbm_CPPFLAGS = \
-I$(top_srcdir)/plugins/mbm \
- $(PLUGIN_COMMON_COMPILER_FLAGS)
+ $(NULL)
test_modem_helpers_mbm_LDADD = \
- $(top_builddir)/libmm-glib/libmm-glib.la \
- $(top_builddir)/src/libmodem-helpers.la
-test_modem_helpers_mbm_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+ $(builddir)/libhelpers-mbm.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
-# Option
-libmm_plugin_option_la_SOURCES = \
- option/mm-plugin-option.c \
- option/mm-plugin-option.h \
- option/mm-broadband-modem-option.c \
- option/mm-broadband-modem-option.h
-libmm_plugin_option_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_option_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+pkglib_LTLIBRARIES += libmm-plugin-ericsson-mbm.la
+libmm_plugin_ericsson_mbm_la_SOURCES = \
+ mbm/mm-broadband-modem-mbm.c \
+ mbm/mm-broadband-modem-mbm.h \
+ mbm/mm-broadband-bearer-mbm.c \
+ mbm/mm-broadband-bearer-mbm.h \
+ mbm/mm-sim-mbm.c \
+ mbm/mm-sim-mbm.h \
+ mbm/mm-plugin-mbm.c \
+ mbm/mm-plugin-mbm.h \
+ $(NULL)
+libmm_plugin_ericsson_mbm_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"ericsson-mbm\" \
+ $(NULL)
+libmm_plugin_ericsson_mbm_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_ericsson_mbm_la_LIBADD = $(builddir)/libhelpers-mbm.la
-# HSO
-libmm_plugin_hso_la_SOURCES = \
- option/mm-plugin-hso.c \
- option/mm-plugin-hso.h \
- option/mm-broadband-modem-option.c \
- option/mm-broadband-modem-option.h \
- option/mm-broadband-bearer-hso.c \
- option/mm-broadband-bearer-hso.h \
- option/mm-broadband-modem-hso.c \
- option/mm-broadband-modem-hso.h
-libmm_plugin_hso_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_hso_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+dist_udevrules_DATA += mbm/77-mm-ericsson-mbm.rules
-# Sierra
-libmm_plugin_sierra_la_SOURCES = \
- sierra/mm-plugin-sierra.c \
- sierra/mm-plugin-sierra.h \
- sierra/mm-common-sierra.c \
- sierra/mm-common-sierra.h \
- sierra/mm-sim-sierra.c \
- sierra/mm-sim-sierra.h \
- sierra/mm-broadband-bearer-sierra.c \
- sierra/mm-broadband-bearer-sierra.h \
- sierra/mm-broadband-modem-sierra.c \
- sierra/mm-broadband-modem-sierra.h \
- sierra/mm-broadband-modem-sierra-icera.c \
- sierra/mm-broadband-modem-sierra-icera.h
-libmm_plugin_sierra_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS)
-libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-libmm_plugin_sierra_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
+AM_CFLAGS += -DTESTUDEVRULESDIR_MBM=\"${srcdir}/mbm\"
-# Wavecom (Sierra Airlink)
-libmm_plugin_wavecom_la_SOURCES = \
- wavecom/mm-plugin-wavecom.c \
- wavecom/mm-plugin-wavecom.h \
- wavecom/mm-broadband-modem-wavecom.c \
- wavecom/mm-broadband-modem-wavecom.h
-libmm_plugin_wavecom_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_wavecom_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+endif
+
+################################################################################
+# plugin: motorola
+################################################################################
+
+if ENABLE_PLUGIN_MOTOROLA
+
+pkglib_LTLIBRARIES += libmm-plugin-motorola.la
+libmm_plugin_motorola_la_SOURCES = \
+ motorola/mm-plugin-motorola.c \
+ motorola/mm-plugin-motorola.h \
+ motorola/mm-broadband-modem-motorola.c \
+ motorola/mm-broadband-modem-motorola.h \
+ $(NULL)
+libmm_plugin_motorola_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"motorola\" \
+ $(NULL)
+libmm_plugin_motorola_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: mtk
+################################################################################
+
+if ENABLE_PLUGIN_MTK
+
+pkglib_LTLIBRARIES += libmm-plugin-mtk.la
+libmm_plugin_mtk_la_SOURCES = \
+ mtk/mm-plugin-mtk.c \
+ mtk/mm-plugin-mtk.h \
+ mtk/mm-broadband-modem-mtk.h \
+ mtk/mm-broadband-modem-mtk.c \
+ $(NULL)
+libmm_plugin_mtk_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"mtk\" \
+ $(NULL)
+libmm_plugin_mtk_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += mtk/77-mm-mtk-port-types.rules
-# Nokia
+AM_CFLAGS += -DTESTUDEVRULESDIR_MTK=\"${srcdir}/mtk\"
+
+endif
+
+################################################################################
+# plugin: nokia
+################################################################################
+
+if ENABLE_PLUGIN_NOKIA
+
+pkglib_LTLIBRARIES += libmm-plugin-nokia.la
libmm_plugin_nokia_la_SOURCES = \
nokia/mm-plugin-nokia.c \
nokia/mm-plugin-nokia.h \
nokia/mm-sim-nokia.c \
nokia/mm-sim-nokia.h \
nokia/mm-broadband-modem-nokia.c \
- nokia/mm-broadband-modem-nokia.h
-libmm_plugin_nokia_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+ nokia/mm-broadband-modem-nokia.h \
+ $(NULL)
+libmm_plugin_nokia_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"nokia\" \
+ $(NULL)
libmm_plugin_nokia_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Nokia (Icera)
+endif
+
+################################################################################
+# plugin: nokia (icera)
+################################################################################
+
+if ENABLE_PLUGIN_NOKIA_ICERA
+
+pkglib_LTLIBRARIES += libmm-plugin-nokia-icera.la
libmm_plugin_nokia_icera_la_SOURCES = \
nokia/mm-plugin-nokia-icera.c \
- nokia/mm-plugin-nokia-icera.h
-libmm_plugin_nokia_icera_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS)
+ nokia/mm-plugin-nokia-icera.h \
+ $(NULL)
+libmm_plugin_nokia_icera_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(ICERA_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"nokia-icera\" \
+ $(NULL)
libmm_plugin_nokia_icera_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-libmm_plugin_nokia_icera_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
-udevrules_DATA += nokia/77-mm-nokia-port-types.rules
-# Zte
-libmm_plugin_zte_la_SOURCES = \
- zte/mm-plugin-zte.c \
- zte/mm-plugin-zte.h \
- zte/mm-common-zte.h \
- zte/mm-common-zte.c \
- zte/mm-broadband-modem-zte.h \
- zte/mm-broadband-modem-zte.c \
- zte/mm-broadband-modem-zte-icera.h \
- zte/mm-broadband-modem-zte-icera.c
-libmm_plugin_zte_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS)
-libmm_plugin_zte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-libmm_plugin_zte_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
-udevrules_DATA += zte/77-mm-zte-port-types.rules
+dist_udevrules_DATA += nokia/77-mm-nokia-port-types.rules
-# Longcheer (and rebranded dongles)
-libmm_plugin_longcheer_la_SOURCES = \
- longcheer/mm-plugin-longcheer.c \
- longcheer/mm-plugin-longcheer.h \
- longcheer/mm-broadband-modem-longcheer.h \
- longcheer/mm-broadband-modem-longcheer.c
-libmm_plugin_longcheer_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_longcheer_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-udevrules_DATA += longcheer/77-mm-longcheer-port-types.rules
+AM_CFLAGS += -DTESTUDEVRULESDIR_NOKIA_ICERA=\"${srcdir}/nokia\"
-# AnyData CDMA
-libmm_plugin_anydata_la_SOURCES = \
- anydata/mm-plugin-anydata.c \
- anydata/mm-plugin-anydata.h \
- anydata/mm-broadband-modem-anydata.h \
- anydata/mm-broadband-modem-anydata.c
-libmm_plugin_anydata_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_anydata_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+endif
-# Linktop CDMA
-libmm_plugin_linktop_la_SOURCES = \
- linktop/mm-plugin-linktop.c \
- linktop/mm-plugin-linktop.h \
- linktop/mm-broadband-modem-linktop.h \
- linktop/mm-broadband-modem-linktop.c
-libmm_plugin_linktop_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_linktop_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+################################################################################
+# plugin: novatel non-lte
+################################################################################
-# SimTech
-libmm_plugin_simtech_la_SOURCES = \
- simtech/mm-plugin-simtech.c \
- simtech/mm-plugin-simtech.h \
- simtech/mm-broadband-modem-simtech.h \
- simtech/mm-broadband-modem-simtech.c
-libmm_plugin_simtech_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_simtech_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-udevrules_DATA += simtech/77-mm-simtech-port-types.rules
+if ENABLE_PLUGIN_NOVATEL
-# Alcatel/TCT/JRD x220D and possibly others
-libmm_plugin_x22x_la_SOURCES = \
- x22x/mm-plugin-x22x.c \
- x22x/mm-plugin-x22x.h \
- x22x/mm-broadband-modem-x22x.h \
- x22x/mm-broadband-modem-x22x.c
-libmm_plugin_x22x_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_x22x_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-udevrules_DATA += x22x/77-mm-x22x-port-types.rules
+pkglib_LTLIBRARIES += libmm-plugin-novatel.la
+libmm_plugin_novatel_la_SOURCES = \
+ novatel/mm-plugin-novatel.c \
+ novatel/mm-plugin-novatel.h \
+ $(NULL)
+libmm_plugin_novatel_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(NOVATEL_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"novatel\" \
+ $(NULL)
+libmm_plugin_novatel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: novatel lte
+################################################################################
+
+if ENABLE_PLUGIN_NOVATEL_LTE
+
+pkglib_LTLIBRARIES += libmm-plugin-novatel-lte.la
+libmm_plugin_novatel_lte_la_SOURCES = \
+ novatel/mm-plugin-novatel-lte.c \
+ novatel/mm-plugin-novatel-lte.h \
+ novatel/mm-broadband-modem-novatel-lte.c \
+ novatel/mm-broadband-modem-novatel-lte.h \
+ novatel/mm-broadband-bearer-novatel-lte.c \
+ novatel/mm-broadband-bearer-novatel-lte.h \
+ novatel/mm-sim-novatel-lte.c \
+ novatel/mm-sim-novatel-lte.h \
+ $(NULL)
+libmm_plugin_novatel_lte_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"novatel-lte\" \
+ $(NULL)
+libmm_plugin_novatel_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: option
+################################################################################
+
+if ENABLE_PLUGIN_OPTION
+
+pkglib_LTLIBRARIES += libmm-plugin-option.la
+libmm_plugin_option_la_SOURCES = \
+ option/mm-plugin-option.c \
+ option/mm-plugin-option.h \
+ $(NULL)
+libmm_plugin_option_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(OPTION_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"option\" \
+ $(NULL)
+libmm_plugin_option_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Pantech
+endif
+
+################################################################################
+# plugin: option hso
+################################################################################
+
+if ENABLE_PLUGIN_OPTION_HSO
+
+pkglib_LTLIBRARIES += libmm-plugin-option-hso.la
+libmm_plugin_option_hso_la_SOURCES = \
+ option/mm-plugin-hso.c \
+ option/mm-plugin-hso.h \
+ option/mm-broadband-bearer-hso.c \
+ option/mm-broadband-bearer-hso.h \
+ option/mm-broadband-modem-hso.c \
+ option/mm-broadband-modem-hso.h \
+ $(NULL)
+libmm_plugin_option_hso_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(OPTION_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"option-hso\" \
+ $(NULL)
+libmm_plugin_option_hso_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: pantech
+################################################################################
+
+if ENABLE_PLUGIN_PANTECH
+
+pkglib_LTLIBRARIES += libmm-plugin-pantech.la
libmm_plugin_pantech_la_SOURCES = \
pantech/mm-plugin-pantech.c \
pantech/mm-plugin-pantech.h \
pantech/mm-sim-pantech.c \
pantech/mm-sim-pantech.h \
pantech/mm-broadband-modem-pantech.c \
- pantech/mm-broadband-modem-pantech.h
-libmm_plugin_pantech_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+ pantech/mm-broadband-modem-pantech.h \
+ $(NULL)
+libmm_plugin_pantech_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"pantech\" \
+ $(NULL)
libmm_plugin_pantech_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Samsung modem
+endif
+
+################################################################################
+# plugin: qcom-soc
+################################################################################
+
+if ENABLE_PLUGIN_QCOM_SOC
+if WITH_QMI
+pkglib_LTLIBRARIES += libmm-plugin-qcom-soc.la
+libmm_plugin_qcom_soc_la_SOURCES = \
+ qcom-soc/mm-plugin-qcom-soc.c \
+ qcom-soc/mm-plugin-qcom-soc.h \
+ qcom-soc/mm-broadband-modem-qmi-qcom-soc.c \
+ qcom-soc/mm-broadband-modem-qmi-qcom-soc.h \
+ $(NULL)
+libmm_plugin_qcom_soc_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"qcom-soc\" \
+ $(NULL)
+libmm_plugin_qcom_soc_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += qcom-soc/77-mm-qcom-soc.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_QCOM_SOC=\"${srcdir}/qcom-soc/\"
+endif
+endif
+
+################################################################################
+# plugin: quectel
+################################################################################
+
+if ENABLE_PLUGIN_QUECTEL
+
+noinst_LTLIBRARIES += libhelpers-quectel.la
+libhelpers_quectel_la_SOURCES = \
+ quectel/mm-modem-helpers-quectel.c \
+ quectel/mm-modem-helpers-quectel.h \
+ $(NULL)
+libhelpers_quectel_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"quectel\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-quectel
+test_modem_helpers_quectel_SOURCES = \
+ quectel/tests/test-modem-helpers-quectel.c \
+ $(NULL)
+test_modem_helpers_quectel_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/quectel \
+ $(NULL)
+test_modem_helpers_quectel_LDADD = \
+ $(builddir)/libhelpers-quectel.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-quectel.la
+libmm_plugin_quectel_la_SOURCES = \
+ quectel/mm-plugin-quectel.c \
+ quectel/mm-plugin-quectel.h \
+ quectel/mm-shared-quectel.c \
+ quectel/mm-shared-quectel.h \
+ quectel/mm-broadband-modem-quectel.c \
+ quectel/mm-broadband-modem-quectel.h \
+ $(NULL)
+if WITH_QMI
+libmm_plugin_quectel_la_SOURCES += \
+ quectel/mm-broadband-modem-qmi-quectel.c \
+ quectel/mm-broadband-modem-qmi-quectel.h \
+ $(NULL)
+endif
+if WITH_MBIM
+libmm_plugin_quectel_la_SOURCES += \
+ quectel/mm-broadband-modem-mbim-quectel.c \
+ quectel/mm-broadband-modem-mbim-quectel.h \
+ $(NULL)
+endif
+libmm_plugin_quectel_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"quectel\" \
+ $(NULL)
+libmm_plugin_quectel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_quectel_la_LIBADD = $(builddir)/libhelpers-quectel.la
+
+dist_udevrules_DATA += quectel/77-mm-quectel-port-types.rules
+AM_CFLAGS += -DTESTUDEVRULESDIR_QUECTEL=\"${srcdir}/quectel\"
+
+endif
+
+################################################################################
+# plugin: samsung
+################################################################################
+
+if ENABLE_PLUGIN_SAMSUNG
+
+pkglib_LTLIBRARIES += libmm-plugin-samsung.la
libmm_plugin_samsung_la_SOURCES = \
samsung/mm-plugin-samsung.c \
samsung/mm-plugin-samsung.h \
samsung/mm-broadband-modem-samsung.c \
- samsung/mm-broadband-modem-samsung.h
-libmm_plugin_samsung_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(ICERA_COMMON_COMPILER_FLAGS)
-libmm_plugin_samsung_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-libmm_plugin_samsung_la_LIBADD = $(ICERA_COMMON_LIBADD_FLAGS)
+ samsung/mm-broadband-modem-samsung.h \
+ $(NULL)
+libmm_plugin_samsung_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(ICERA_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"samsung\" \
+ $(NULL)
+libmm_plugin_samsung_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Cinterion (previously Siemens) modem
-libmm_plugin_cinterion_la_SOURCES = \
- cinterion/mm-plugin-cinterion.c \
- cinterion/mm-plugin-cinterion.h \
- cinterion/mm-modem-helpers-cinterion.c \
- cinterion/mm-modem-helpers-cinterion.h \
- cinterion/mm-common-cinterion.c \
- cinterion/mm-common-cinterion.h \
- cinterion/mm-broadband-modem-cinterion.c \
- cinterion/mm-broadband-modem-cinterion.h
+endif
+
+################################################################################
+# plugin: sierra (legacy)
+################################################################################
+
+if ENABLE_PLUGIN_SIERRA_LEGACY
+
+pkglib_LTLIBRARIES += libmm-plugin-sierra-legacy.la
+libmm_plugin_sierra_legacy_la_SOURCES = \
+ sierra/mm-plugin-sierra-legacy.c \
+ sierra/mm-plugin-sierra-legacy.h \
+ sierra/mm-broadband-modem-sierra-icera.c \
+ sierra/mm-broadband-modem-sierra-icera.h \
+ $(NULL)
+libmm_plugin_sierra_legacy_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(ICERA_COMMON_COMPILER_FLAGS) \
+ $(SIERRA_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"sierra-legacy\" \
+ $(NULL)
+libmm_plugin_sierra_legacy_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+
+################################################################################
+# plugin: sierra (new QMI or MBIM modems)
+################################################################################
+
+if ENABLE_PLUGIN_SIERRA
+
+dist_udevrules_DATA += sierra/77-mm-sierra.rules
+
+pkglib_LTLIBRARIES += libmm-plugin-sierra.la
+libmm_plugin_sierra_la_SOURCES = \
+ sierra/mm-plugin-sierra.c \
+ sierra/mm-plugin-sierra.h \
+ $(NULL)
+libmm_plugin_sierra_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(XMM_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"sierra\" \
+ $(NULL)
+libmm_plugin_sierra_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+endif
+################################################################################
+# plugin: simtech
+################################################################################
+
+if ENABLE_PLUGIN_SIMTECH
+
+noinst_LTLIBRARIES += libhelpers-simtech.la
+libhelpers_simtech_la_SOURCES = \
+ simtech/mm-modem-helpers-simtech.c \
+ simtech/mm-modem-helpers-simtech.h \
+ $(NULL)
+libhelpers_simtech_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"simtech\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-simtech
+test_modem_helpers_simtech_SOURCES = \
+ simtech/tests/test-modem-helpers-simtech.c \
+ $(NULL)
+test_modem_helpers_simtech_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/simtech \
+ $(NULL)
+test_modem_helpers_simtech_LDADD = \
+ $(builddir)/libhelpers-simtech.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-simtech.la
+libmm_plugin_simtech_la_SOURCES = \
+ simtech/mm-plugin-simtech.c \
+ simtech/mm-plugin-simtech.h \
+ simtech/mm-shared-simtech.c \
+ simtech/mm-shared-simtech.h \
+ simtech/mm-broadband-modem-simtech.h \
+ simtech/mm-broadband-modem-simtech.c \
+ $(NULL)
if WITH_QMI
-libmm_plugin_cinterion_la_SOURCES += \
- cinterion/mm-broadband-modem-qmi-cinterion.c \
- cinterion/mm-broadband-modem-qmi-cinterion.h
+libmm_plugin_simtech_la_SOURCES += \
+ simtech/mm-broadband-modem-qmi-simtech.c \
+ simtech/mm-broadband-modem-qmi-simtech.h \
+ $(NULL)
endif
+libmm_plugin_simtech_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"simtech\" \
+ $(NULL)
+libmm_plugin_simtech_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_simtech_la_LIBADD = $(builddir)/libhelpers-simtech.la
-libmm_plugin_cinterion_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_cinterion_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+dist_udevrules_DATA += simtech/77-mm-simtech-port-types.rules
-udevrules_DATA += cinterion/77-mm-cinterion-port-types.rules
+AM_CFLAGS += -DTESTUDEVRULESDIR_SIMTECH=\"${srcdir}/simtech\"
-noinst_PROGRAMS += test-modem-helpers-cinterion
-test_modem_helpers_cinterion_SOURCES = \
- cinterion/mm-modem-helpers-cinterion.c \
- cinterion/mm-modem-helpers-cinterion.h \
- cinterion/tests/test-modem-helpers-cinterion.c
-test_modem_helpers_cinterion_CPPFLAGS = \
- -I$(top_srcdir)/plugins/cinterion \
- $(PLUGIN_COMMON_COMPILER_FLAGS)
-test_modem_helpers_cinterion_LDADD = \
- $(top_builddir)/libmm-glib/libmm-glib.la \
- $(top_builddir)/src/libmodem-helpers.la
-test_modem_helpers_cinterion_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+endif
-# Iridium modem
-libmm_plugin_iridium_la_SOURCES = \
- iridium/mm-plugin-iridium.c \
- iridium/mm-plugin-iridium.h \
- iridium/mm-broadband-modem-iridium.c \
- iridium/mm-broadband-modem-iridium.h \
- iridium/mm-bearer-iridium.c \
- iridium/mm-bearer-iridium.h \
- iridium/mm-sim-iridium.c \
- iridium/mm-sim-iridium.h
-libmm_plugin_iridium_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_iridium_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+################################################################################
+# plugin: telit
+################################################################################
-# Novatel LTE modem
-libmm_plugin_novatel_lte_la_SOURCES = \
- novatel/mm-plugin-novatel-lte.c \
- novatel/mm-plugin-novatel-lte.h \
- novatel/mm-broadband-modem-novatel-lte.c \
- novatel/mm-broadband-modem-novatel-lte.h \
- novatel/mm-broadband-bearer-novatel-lte.c \
- novatel/mm-broadband-bearer-novatel-lte.h \
- novatel/mm-sim-novatel-lte.c \
- novatel/mm-sim-novatel-lte.h
-libmm_plugin_novatel_lte_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_novatel_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+if ENABLE_PLUGIN_TELIT
-# Novatel non-LTE modem
-libmm_plugin_novatel_la_SOURCES = \
- novatel/mm-plugin-novatel.c \
- novatel/mm-plugin-novatel.h \
- novatel/mm-broadband-modem-novatel.c \
- novatel/mm-broadband-modem-novatel.h
-libmm_plugin_novatel_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_novatel_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+pkglib_LTLIBRARIES += libmm-plugin-telit.la
+libmm_plugin_telit_la_SOURCES = \
+ telit/mm-plugin-telit.c \
+ telit/mm-plugin-telit.h \
+ $(NULL)
+libmm_plugin_telit_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(TELIT_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"telit\" \
+ $(NULL)
+libmm_plugin_telit_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Altair LTE modem
-libmm_plugin_altair_lte_la_SOURCES = \
- altair/mm-modem-helpers-altair-lte.c \
- altair/mm-modem-helpers-altair-lte.h \
- altair/mm-plugin-altair-lte.c \
- altair/mm-plugin-altair-lte.h \
- altair/mm-broadband-modem-altair-lte.c \
- altair/mm-broadband-modem-altair-lte.h \
- altair/mm-broadband-bearer-altair-lte.c \
- altair/mm-broadband-bearer-altair-lte.h
-libmm_plugin_altair_lte_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_altair_lte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+dist_udevrules_DATA += telit/77-mm-telit-port-types.rules
-noinst_PROGRAMS += test-modem-helpers-altair-lte
-test_modem_helpers_altair_lte_SOURCES = \
- altair/mm-modem-helpers-altair-lte.c \
- altair/mm-modem-helpers-altair-lte.h \
- altair/tests/test-modem-helpers-altair-lte.c
-test_modem_helpers_altair_lte_CPPFLAGS = \
- -I$(top_srcdir)/plugins/altair \
- $(PLUGIN_COMMON_COMPILER_FLAGS)
-test_modem_helpers_altair_lte_LDADD = $(top_builddir)/libmm-glib/libmm-glib.la
-test_modem_helpers_altair_lte_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+AM_CFLAGS += -DTESTUDEVRULESDIR_TELIT=\"${srcdir}/telit\"
+
+endif
+
+################################################################################
+# plugin: thuraya xt
+################################################################################
+
+if ENABLE_PLUGIN_THURAYA
+
+noinst_LTLIBRARIES += libhelpers-thuraya.la
+libhelpers_thuraya_la_SOURCES = \
+ thuraya/mm-modem-helpers-thuraya.c \
+ thuraya/mm-modem-helpers-thuraya.h \
+ $(NULL)
+libhelpers_thuraya_la_CPPFLAGS = \
+ -DMM_MODULE_NAME=\"thuraya\" \
+ $(NULL)
+
+noinst_PROGRAMS += test-modem-helpers-thuraya
+test_modem_helpers_thuraya_SOURCES = \
+ thuraya/tests/test-mm-modem-helpers-thuraya.c \
+ $(NULL)
+test_modem_helpers_thuraya_CPPFLAGS = \
+ -I$(top_srcdir)/plugins/thuraya \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(NULL)
+test_modem_helpers_thuraya_LDADD = \
+ $(builddir)/libhelpers-thuraya.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-thuraya.la
+libmm_plugin_thuraya_la_SOURCES = \
+ thuraya/mm-plugin-thuraya.c \
+ thuraya/mm-plugin-thuraya.h \
+ thuraya/mm-broadband-modem-thuraya.c \
+ thuraya/mm-broadband-modem-thuraya.h \
+ $(NULL)
+libmm_plugin_thuraya_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"thuraya\" \
+ $(NULL)
+libmm_plugin_thuraya_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_thuraya_la_LIBADD = $(builddir)/libhelpers-thuraya.la
+
+endif
+
+################################################################################
+# plugin: tplink
+################################################################################
+
+if ENABLE_PLUGIN_TPLINK
+
+pkglib_LTLIBRARIES += libmm-plugin-tplink.la
+libmm_plugin_tplink_la_SOURCES = \
+ tplink/mm-plugin-tplink.c \
+ tplink/mm-plugin-tplink.h \
+ $(NULL)
+libmm_plugin_tplink_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"tp-link\" \
+ $(NULL)
+libmm_plugin_tplink_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += tplink/77-mm-tplink-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_TPLINK=\"${srcdir}/tplink\"
+
+endif
+################################################################################
+# plugin: u-blox
+################################################################################
+
+if ENABLE_PLUGIN_UBLOX
+
+dist_udevrules_DATA += ublox/77-mm-ublox-port-types.rules
+
+PLUGIN_UBLOX_COMPILER_FLAGS = \
+ -I$(top_srcdir)/plugins/ublox \
+ -I$(top_builddir)/plugins/ublox \
+ $(NULL)
+
+noinst_LTLIBRARIES += libhelpers-ublox.la
+
+UBLOX_ENUMS_INPUTS = \
+ $(top_srcdir)/plugins/ublox/mm-modem-helpers-ublox.h \
+ $(NULL)
+
+UBLOX_ENUMS_GENERATED = \
+ ublox/mm-ublox-enums-types.h \
+ ublox/mm-ublox-enums-types.c \
+ $(NULL)
+
+ublox/mm-ublox-enums-types.h: Makefile.am $(UBLOX_ENUMS_INPUTS) $(top_srcdir)/build-aux/mm-enums-types.h.template
+ $(AM_V_GEN) \
+ $(MKDIR_P) ublox; \
+ $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-modem-helpers-ublox.h\"\n#ifndef __MM_UBLOX_ENUMS_TYPES_H__\n#define __MM_UBLOX_ENUMS_TYPES_H__\n" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
+ --ftail "#endif /* __MM_UBLOX_ENUMS_TYPES_H__ */\n" \
+ $(UBLOX_ENUMS_INPUTS) > $@
+
+ublox/mm-ublox-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-types.c.template ublox/mm-ublox-enums-types.h
+ $(AM_V_GEN) \
+ $(MKDIR_P) ublox; \
+ $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-ublox-enums-types.h\"" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
+ $(UBLOX_ENUMS_INPUTS) > $@
+
+libhelpers_ublox_la_SOURCES = \
+ ublox/mm-modem-helpers-ublox.c \
+ ublox/mm-modem-helpers-ublox.h \
+ $(NULL)
+
+nodist_libhelpers_ublox_la_SOURCES = $(UBLOX_ENUMS_GENERATED)
+
+libhelpers_ublox_la_CPPFLAGS = \
+ $(PLUGIN_UBLOX_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"u-blox\" \
+ $(NULL)
+
+BUILT_SOURCES += $(UBLOX_ENUMS_GENERATED)
+CLEANFILES += $(UBLOX_ENUMS_GENERATED)
+
+noinst_PROGRAMS += test-modem-helpers-ublox
+test_modem_helpers_ublox_SOURCES = \
+ ublox/tests/test-modem-helpers-ublox.c \
+ $(NULL)
+test_modem_helpers_ublox_CPPFLAGS = \
+ $(PLUGIN_UBLOX_COMPILER_FLAGS) \
+ $(TEST_COMMON_COMPILER_FLAGS) \
+ $(NULL)
+test_modem_helpers_ublox_LDADD = \
+ $(TEST_COMMON_LIBADD_FLAGS) \
+ $(builddir)/libhelpers-ublox.la \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+pkglib_LTLIBRARIES += libmm-plugin-ublox.la
+libmm_plugin_ublox_la_SOURCES = \
+ ublox/mm-plugin-ublox.c \
+ ublox/mm-plugin-ublox.h \
+ ublox/mm-broadband-bearer-ublox.h \
+ ublox/mm-broadband-bearer-ublox.c \
+ ublox/mm-broadband-modem-ublox.h \
+ ublox/mm-broadband-modem-ublox.c \
+ ublox/mm-sim-ublox.c \
+ ublox/mm-sim-ublox.h \
+ $(NULL)
+libmm_plugin_ublox_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(PLUGIN_UBLOX_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"u-blox\" \
+ $(NULL)
+libmm_plugin_ublox_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+libmm_plugin_ublox_la_LIBADD = $(builddir)/libhelpers-ublox.la
-# VIA modem
+endif
+
+################################################################################
+# plugin: via
+################################################################################
+
+if ENABLE_PLUGIN_VIA
+
+pkglib_LTLIBRARIES += libmm-plugin-via.la
libmm_plugin_via_la_SOURCES = \
via/mm-plugin-via.c \
via/mm-plugin-via.h \
via/mm-broadband-modem-via.c \
- via/mm-broadband-modem-via.h
-libmm_plugin_via_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
+ via/mm-broadband-modem-via.h \
+ $(NULL)
+libmm_plugin_via_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"via\" \
+ $(NULL)
libmm_plugin_via_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-# Telit modem
-libmm_plugin_telit_la_SOURCES = \
- telit/mm-plugin-telit.c \
- telit/mm-plugin-telit.h \
- telit/mm-broadband-modem-telit.c \
- telit/mm-broadband-modem-telit.h
-libmm_plugin_telit_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_telit_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-udevrules_DATA += telit/77-mm-telit-port-types.rules
+endif
-# MTK
-libmm_plugin_mtk_la_SOURCES = \
- mtk/mm-plugin-mtk.c \
- mtk/mm-plugin-mtk.h \
- mtk/mm-broadband-modem-mtk.h \
- mtk/mm-broadband-modem-mtk.c
-libmm_plugin_mtk_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS)
-libmm_plugin_mtk_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
-udevrules_DATA += mtk/77-mm-mtk-port-types.rules
+################################################################################
+# plugin: wavecom (now sierra airlink)
+################################################################################
+
+if ENABLE_PLUGIN_WAVECOM
+
+pkglib_LTLIBRARIES += libmm-plugin-wavecom.la
+libmm_plugin_wavecom_la_SOURCES = \
+ wavecom/mm-plugin-wavecom.c \
+ wavecom/mm-plugin-wavecom.h \
+ wavecom/mm-broadband-modem-wavecom.c \
+ wavecom/mm-broadband-modem-wavecom.h \
+ $(NULL)
+libmm_plugin_wavecom_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"wavecom\" \
+ $(NULL)
+libmm_plugin_wavecom_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+endif
-# Additional files to include in the distribution
-EXTRA_DIST += \
- $(udevrules_DATA) \
- tests/gsm-port.conf
+################################################################################
+# plugin: alcatel/TCT/JRD x220D and possibly others
+################################################################################
+
+if ENABLE_PLUGIN_X22X
+
+pkglib_LTLIBRARIES += libmm-plugin-x22x.la
+libmm_plugin_x22x_la_SOURCES = \
+ x22x/mm-plugin-x22x.c \
+ x22x/mm-plugin-x22x.h \
+ x22x/mm-broadband-modem-x22x.h \
+ x22x/mm-broadband-modem-x22x.c \
+ $(NULL)
+libmm_plugin_x22x_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"x22x\" \
+ $(NULL)
+libmm_plugin_x22x_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += x22x/77-mm-x22x-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_X22X=\"${srcdir}/x22x\"
+
+endif
+
+################################################################################
+# plugin: zte
+################################################################################
+
+if ENABLE_PLUGIN_ZTE
+
+pkglib_LTLIBRARIES += libmm-plugin-zte.la
+libmm_plugin_zte_la_SOURCES = \
+ zte/mm-plugin-zte.c \
+ zte/mm-plugin-zte.h \
+ zte/mm-common-zte.h \
+ zte/mm-common-zte.c \
+ zte/mm-broadband-modem-zte.h \
+ zte/mm-broadband-modem-zte.c \
+ zte/mm-broadband-modem-zte-icera.h \
+ zte/mm-broadband-modem-zte-icera.c \
+ $(NULL)
+libmm_plugin_zte_la_CPPFLAGS = \
+ $(PLUGIN_COMMON_COMPILER_FLAGS) \
+ $(ICERA_COMMON_COMPILER_FLAGS) \
+ -DMM_MODULE_NAME=\"zte\" \
+ $(NULL)
+libmm_plugin_zte_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS)
+
+dist_udevrules_DATA += zte/77-mm-zte-port-types.rules
+
+AM_CFLAGS += -DTESTUDEVRULESDIR_ZTE=\"${srcdir}/zte\"
+
+endif
+
+################################################################################
+# udev rules tester
+################################################################################
+
+noinst_PROGRAMS += test-udev-rules
+test_udev_rules_SOURCES = \
+ tests/test-udev-rules.c \
+ $(NULL)
+test_udev_rules_LDADD = \
+ $(top_builddir)/src/libkerneldevice.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+################################################################################
+# keyfile tester
+################################################################################
+
+noinst_PROGRAMS += test-keyfiles
+test_keyfiles_SOURCES = \
+ tests/test-keyfiles.c \
+ $(NULL)
+test_keyfiles_LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+################################################################################
-# Unit tests
TEST_PROGS += $(noinst_PROGRAMS)
diff --git a/plugins/README.txt b/plugins/README.txt
new file mode 100644
index 00000000..ff36dc41
--- /dev/null
+++ b/plugins/README.txt
@@ -0,0 +1,160 @@
+
+The following list shows the relationship among the different plugins provided
+by default by ModemManager. For each of the plugin types, the list of modem
+objects created by the plugin is given.
+
+ * Altair:
+ ** MMBroadbandModemAltairLte
+
+ * Anydata:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemAnydata
+
+ * Cinterion:
+ ** MMBroadbandModemQmiCinterion
+ ** MMBroadbandModemCinterion
+
+ * Dell:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemFoxconnT77w968 (from the Foxconn utils)
+ ** MMBroadbandModemMbimXmm (from the XMM utils)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModemNovatel (from the Novatel utils)
+ ** MMBroadbandModemSierra (from the Sierra Legacy utils)
+ ** MMBroadbandModemTelit (from the Telit utils)
+ ** MMBroadbandModemXmm (from the XMM utils)
+ ** MMBroadbandModem (generic)
+
+ * D-Link:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModem (generic)
+
+ * Fibocom:
+ ** MMBroadbandModemMbimXmm (from the XMM utils)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModemXmm (from the XMM utils)
+ ** MMBroadbandModem (generic)
+
+ * Foxconn:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemFoxconnT77w968
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModem (generic)
+
+ * Generic:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModem (generic)
+
+ * Gosuncn:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModem (generic)
+
+ * Haier:
+ ** MMBroadbandModem (generic)
+
+ * Huawei:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModemHuawei
+
+ * Icera (no explicit plugin):
+ ** MMBroadbandModemIcera
+
+ * Iridium:
+ ** MMBroadbandModemIridium
+
+ * Linktop:
+ ** MMBroadbandModemLinktop
+
+ * Longcheer:
+ ** MMBroadbandModemLongcheer
+
+ * MBM:
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModemMbm
+
+ * Motorola:
+ ** MMBroadbandModemMotorola
+
+ * Mtk:
+ ** MMBroadbandModemMtk
+
+ * Nokia:
+ ** MMBroadbandModemNokia
+
+ * Nokia Icera:
+ ** MMBroadbandModemIcera (from the Icera utils)
+
+ * Novatel:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemNovatel
+
+ * Novatel LTE:
+ ** MMBroadbandModemNovatelLte
+
+ * Option:
+ ** MMBroadbandModemOption
+
+ * Option HSO:
+ ** MMBroadbandModemHso
+
+ * Pantech:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemPantech
+
+ * Quectel:
+ ** MMBroadbandModemQmiQuectel
+ ** MMBroadbandModemQuectel
+
+ * Samsung:
+ ** MMBroadbandModemSamsung (subclassed from the Icera utils)
+
+ * Sierra Legacy:
+ ** MMBroadbandModemSierraIcera (subclassed from the Icera utils)
+ ** MMBroadbandModemSierra
+
+ * Sierra:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModem (generic)
+
+ * Simtech:
+ ** MMBroadbandModemQmiSimtech
+ ** MMBroadbandModemSimtech
+
+ * Telit:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemMbimTelit
+ ** MMBroadbandModemTelit
+
+ * Thuraya
+ ** MMBroadbandModemThuraya
+
+ * TP-Link:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModem (generic)
+
+ * u-blox:
+ ** MMBroadbandModemUblox
+
+ * via:
+ ** MMBroadbandModemVia
+
+ * wavecom:
+ ** MMBroadbandModemWavecom
+
+ * x22x:
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemX22x
+
+ * XMM (no explicit plugin):
+ ** MMBroadbandModemMbimXmm
+ ** MMBroadbandModemXmm
+
+ * ZTE
+ ** MMBroadbandModemQmi (generic)
+ ** MMBroadbandModemMbim (generic)
+ ** MMBroadbandModemZteIcera (subclassed from the Icera utils)
+ ** MMBroadbandModemZte
diff --git a/plugins/altair/mm-broadband-bearer-altair-lte.c b/plugins/altair/mm-broadband-bearer-altair-lte.c
index 1d188d58..52403418 100644
--- a/plugins/altair/mm-broadband-bearer-altair-lte.c
+++ b/plugins/altair/mm-broadband-bearer-altair-lte.c
@@ -42,51 +42,17 @@ G_DEFINE_TYPE (MMBroadbandBearerAltairLte, mm_broadband_bearer_altair_lte, MM_TY
/* 3GPP Connect sequence */
typedef struct {
- MMBroadbandBearerAltairLte *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
} DetailedConnectContext;
-static DetailedConnectContext *
-detailed_connect_context_new (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
- MMPortSerialAt *primary,
- MMPort *data,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- DetailedConnectContext *ctx;
-
- ctx = g_new0 (DetailedConnectContext, 1);
- ctx->self = g_object_ref (self);
- ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
- ctx->primary = g_object_ref (primary);
- ctx->data = g_object_ref (data);
- /* NOTE:
- * We don't currently support cancelling AT commands, so we'll just check
- * whether the operation is to be cancelled at each step. */
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- detailed_connect_context_new);
- return ctx;
-}
-
static void
-detailed_connect_context_complete_and_free (DetailedConnectContext *ctx)
+detailed_connect_context_free (DetailedConnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -95,74 +61,70 @@ connect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
connect_3gpp_connect_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
+ DetailedConnectContext *ctx;
const gchar *result;
GError *error = NULL;
MMBearerIpConfig *config;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
- mm_warn ("connect failed: %s", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("Connected");
+ ctx = g_task_get_task_data (task);
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
/* Set operation result */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- mm_bearer_connect_result_new (ctx->data,
- config,
- config),
+ g_task_return_pointer (
+ task,
+ mm_bearer_connect_result_new (ctx->data, config, config),
(GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
g_object_unref (config);
-
- detailed_connect_context_complete_and_free (ctx);
}
static void
connect_3gpp_apnsettings_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
+ DetailedConnectContext *ctx;
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
- mm_warn ("setting APN failed: %s", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ g_prefix_error (&error, "setting APN failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("APN set - connecting bearer");
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"%DPDNACT=1",
- 20, /* timeout */
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_3gpp_connect_ready,
- ctx); /* user_data */
+ task); /* user_data */
}
static void
@@ -179,6 +141,7 @@ connect_3gpp (MMBroadbandBearer *self,
MMBearerProperties *config;
MMModem3gppRegistrationState registration_state;
MMPort *data;
+ GTask *task;
/* There is a known firmware bug that can leave the modem unusable if a
* connect attempt is made when out of coverage. So, fail without trying.
@@ -187,12 +150,13 @@ connect_3gpp (MMBroadbandBearer *self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &registration_state,
NULL);
if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
- "Out of coverage, can't connect.");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ connect_3gpp,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
+ "Out of coverage, can't connect.");
return;
}
@@ -200,38 +164,40 @@ connect_3gpp (MMBroadbandBearer *self,
* refresh.
* */
if (mm_broadband_modem_altair_lte_is_sim_refresh_detach_in_progress (modem)) {
- mm_dbg ("Detached from network to process SIM refresh, failing connect request");
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_RETRY,
- "Detached from network to process SIM refresh, can't connect.");
+ mm_obj_dbg (self, "detached from network to process SIM refresh, failing connect request");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ connect_3gpp,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Detached from network to process SIM refresh, can't connect.");
return;
}
data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
if (!data) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CONNECTED,
- "Couldn't connect: no available net port available");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ connect_3gpp,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CONNECTED,
+ "Couldn't connect: no available net port available");
return;
}
- ctx = detailed_connect_context_new (self,
- modem,
- primary,
- data,
- cancellable,
- callback,
- user_data);
+ ctx = g_new0 (DetailedConnectContext, 1);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+ ctx->primary = g_object_ref (primary);
+ ctx->data = g_object_ref (data);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free);
config = mm_base_bearer_peek_config (MM_BASE_BEARER (self));
apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (config));
- command = g_strdup_printf ("%%APNN=%s",apn);
+ command = g_strdup_printf ("%%APNN=%s", apn);
g_free (apn);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
@@ -239,9 +205,9 @@ connect_3gpp (MMBroadbandBearer *self,
10, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
- ctx->cancellable,
+ cancellable,
(GAsyncReadyCallback)connect_3gpp_apnsettings_ready,
- ctx); /* user_data */
+ task); /* user_data */
g_free (command);
}
@@ -249,73 +215,43 @@ connect_3gpp (MMBroadbandBearer *self,
/* 3GPP Disconnect sequence */
typedef struct {
- MMBroadbandBearer *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
- GSimpleAsyncResult *result;
} DetailedDisconnectContext;
-static DetailedDisconnectContext *
-detailed_disconnect_context_new (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
- MMPortSerialAt *primary,
- MMPort *data,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- DetailedDisconnectContext *ctx;
-
- ctx = g_new0 (DetailedDisconnectContext, 1);
- ctx->self = g_object_ref (self);
- ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
- ctx->primary = g_object_ref (primary);
- ctx->data = g_object_ref (data);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- detailed_disconnect_context_new);
- return ctx;
-}
-
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-detailed_disconnect_context_complete_and_free (DetailedDisconnectContext *ctx)
+detailed_disconnect_context_free (DetailedDisconnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx);
}
static void
disconnect_3gpp_check_status (MMBaseModem *modem,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
- if (!result) {
- mm_warn ("Disconnect failed: %s", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- }
+ if (!result)
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -330,6 +266,7 @@ disconnect_3gpp (MMBroadbandBearer *self,
{
DetailedDisconnectContext *ctx;
MMModem3gppRegistrationState registration_state;
+ GTask *task;
/* There is a known firmware bug that can leave the modem unusable if a
* disconnect attempt is made when out of coverage. So, fail without trying.
@@ -338,26 +275,33 @@ disconnect_3gpp (MMBroadbandBearer *self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &registration_state,
NULL);
if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
- "Out of coverage, can't disconnect.");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ disconnect_3gpp,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK,
+ "Out of coverage, can't disconnect.");
return;
}
- ctx = detailed_disconnect_context_new (self, modem, primary, data, callback, user_data);
+ ctx = g_new0 (DetailedDisconnectContext, 1);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+ ctx->primary = g_object_ref (primary);
+ ctx->data = g_object_ref (data);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"%DPDNACT=0",
- 20, /* timeout */
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_3gpp_check_status,
- ctx); /* user_data */
+ task); /* user_data */
}
/*****************************************************************************/
@@ -409,8 +353,16 @@ mm_broadband_bearer_altair_lte_init (MMBroadbandBearerAltairLte *self)
static void
mm_broadband_bearer_altair_lte_class_init (MMBroadbandBearerAltairLteClass *klass)
{
+ MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = NULL;
+ base_bearer_class->reload_connection_status_finish = NULL;
+#endif
+
broadband_bearer_class->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
diff --git a/plugins/altair/mm-broadband-modem-altair-lte.c b/plugins/altair/mm-broadband-modem-altair-lte.c
index d58b6839..551a7cac 100644
--- a/plugins/altair/mm-broadband-modem-altair-lte.c
+++ b/plugins/altair/mm-broadband-modem-altair-lte.c
@@ -35,7 +35,7 @@
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-3gpp-ussd.h"
#include "mm-iface-modem-messaging.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-altair-lte.h"
#include "mm-serial-parsers.h"
@@ -50,7 +50,7 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemAltairLte, mm_broadband_modem_altair_lte
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init));
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init))
struct _MMBroadbandModemAltairLtePrivate {
/* Regex for SIM refresh notifications */
@@ -67,6 +67,8 @@ struct _MMBroadbandModemAltairLtePrivate {
GRegex *statcm_regex;
/* Regex for PCO notifications */
GRegex *pcoinfo_regex;
+
+ GList *pco_list;
};
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
@@ -104,33 +106,23 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_altair_lte_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
static void
@@ -139,20 +131,16 @@ modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- /* Set a new ref to the bearer object as result */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
/* We just create a MMBroadbandBearer */
mm_broadband_bearer_altair_lte_new (MM_BROADBAND_MODEM_ALTAIR_LTE (self),
properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -163,16 +151,13 @@ load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_unlock_retries_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -180,10 +165,8 @@ load_unlock_retries_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -196,18 +179,15 @@ load_unlock_retries_ready (MMBaseModem *self,
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- retries,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, retries, g_object_unref);
} else {
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid unlock retries response: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid unlock retries response: '%s'",
+ response);
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -215,16 +195,16 @@ load_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- "%CPININFO",
- 3,
- FALSE,
- (GAsyncReadyCallback)load_unlock_retries_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_unlock_retries));
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "%CPININFO",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)load_unlock_retries_ready,
+ task);
}
/*****************************************************************************/
@@ -235,15 +215,15 @@ load_current_capabilities_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMModemCapability caps;
- gchar *caps_str;
+ GError *inner_error = NULL;
+ gssize value;
- /* This modem is LTE only.*/
- caps = MM_MODEM_CAPABILITY_LTE;
- caps_str = mm_modem_capability_build_string_from_mask (caps);
- mm_dbg ("Loaded current capabilities: %s", caps_str);
- g_free (caps_str);
- return caps;
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_CAPABILITY_NONE;
+ }
+ return (MMModemCapability)value;
}
static void
@@ -251,16 +231,12 @@ load_current_capabilities (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- mm_dbg ("Loading (Altair LTE) current capabilities...");
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_capabilities);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ /* This modem is LTE only.*/
+ g_task_return_int (task, MM_MODEM_CAPABILITY_LTE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -271,11 +247,7 @@ load_supported_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
#define BANDCAP_TAG "%BANDCAP: "
@@ -283,7 +255,7 @@ load_supported_bands_finish (MMIfaceModem *self,
static void
load_supported_bands_done (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GArray *bands;
const gchar *response;
@@ -291,10 +263,8 @@ load_supported_bands_done (MMIfaceModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query supported bands: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -305,22 +275,17 @@ load_supported_bands_done (MMIfaceModem *self,
bands = mm_altair_parse_bands_response (response);
if (!bands) {
- mm_dbg ("Failed to parse supported bands response");
- g_simple_async_result_set_error (
- operation_result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse supported bands response");
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_op_res_gpointer (operation_result,
- bands,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
}
static void
@@ -328,12 +293,9 @@ load_supported_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_bands);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
@@ -341,7 +303,7 @@ load_supported_bands (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_supported_bands_done,
- result);
+ task);
}
/*****************************************************************************/
@@ -352,11 +314,7 @@ load_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
#define CFGBANDS_TAG "Bands: "
@@ -364,7 +322,7 @@ load_current_bands_finish (MMIfaceModem *self,
static void
load_current_bands_done (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GArray *bands;
const gchar *response;
@@ -372,10 +330,8 @@ load_current_bands_done (MMIfaceModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query current bands: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -386,22 +342,17 @@ load_current_bands_done (MMIfaceModem *self,
bands = mm_altair_parse_bands_response (response);
if (!bands) {
- mm_dbg ("Failed to parse current bands response");
- g_simple_async_result_set_error (
- operation_result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse current bands response");
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_op_res_gpointer (operation_result,
- bands,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
}
static void
@@ -409,12 +360,9 @@ load_current_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_bands);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
@@ -422,7 +370,7 @@ load_current_bands (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_current_bands_done,
- result);
+ task);
}
/*****************************************************************************/
@@ -457,14 +405,36 @@ modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
- error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+simulate_unprovisioned_subscription_pco_update (MMBroadbandModemAltairLte *self)
+{
+ MMPco *pco;
+
+ /* Simulate a %PCOINFO notification issued to the IMS PDN that indicates an
+ * unprovisioned Verizon SIM. See mm_altair_parse_vendor_pco_info() for the
+ * detailed format of a %PCOINFO response.
+ *
+ * 1,FF00,13018405 is constructed as follows:
+ *
+ * 1: CID for IMS PDN
+ * FF 00: Container ID for the Verizon-specific PCO content
+ * 13 01 84: Binary coded decimal representation of Verizon MCC/MNC 311/084
+ * 05: Value indicating an unprovisioned SIM
+ */
+ pco = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,FF00,13018405", NULL);
+ g_assert (pco != NULL);
+ self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco);
+ mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (self), self->priv->pco_list);
+ g_object_unref (pco);
}
static void
run_registration_checks_subscription_state_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
const gchar *at_response;
@@ -474,44 +444,43 @@ run_registration_checks_subscription_state_ready (MMIfaceModem3gpp *self,
* ignore the error. This allows the registration attempt to continue.
* So, the async response from this function is *always* True.
*/
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
at_response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!at_response) {
g_assert (error);
- mm_warn ("AT+CEER failed: %s", error->message);
+ mm_obj_warn (self, "AT+CEER failed: %s", error->message);
g_error_free (error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
ceer_response = mm_altair_parse_ceer_response (at_response, &error);
if (!ceer_response) {
g_assert (error);
- mm_warn ("Failed to parse AT+CEER response: %s", error->message);
+ mm_obj_warn (self, "Failed to parse AT+CEER response: %s", error->message);
g_error_free (error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
if (g_strcmp0 ("EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", ceer_response) == 0) {
- mm_dbg ("Registration failed due to unprovisioned SIM.");
- mm_iface_modem_3gpp_update_subscription_state (self, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED);
+ mm_obj_dbg (self, "registration failed due to unprovisioned SIM");
+ simulate_unprovisioned_subscription_pco_update (MM_BROADBAND_MODEM_ALTAIR_LTE (self));
} else {
- mm_dbg ("Failed to find a better reason for registration failure.");
+ mm_obj_dbg (self, "failed to find a better reason for registration failure");
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
g_free (ceer_response);
}
static void
run_registration_checks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
gboolean success;
@@ -520,83 +489,95 @@ run_registration_checks_ready (MMIfaceModem3gpp *self,
success = iface_modem_3gpp_parent->run_registration_checks_finish (self, res, &error);
if (!success) {
g_assert (error);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("Checking if SIM is unprovisioned (ignoring registration state).");
+ mm_obj_dbg (self, "checking if SIM is unprovisioned (ignoring registration state)");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CEER",
6,
FALSE,
(GAsyncReadyCallback) run_registration_checks_subscription_state_ready,
- operation_result);
+ task);
}
static void
-modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
+ gboolean is_cs_supported,
+ gboolean is_ps_supported,
+ gboolean is_eps_supported,
+ gboolean is_5gs_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *operation_result;
+ GTask *task;
- operation_result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_run_registration_checks);
+ task = g_task_new (self, NULL, callback, user_data);
g_assert (iface_modem_3gpp_parent->run_registration_checks);
iface_modem_3gpp_parent->run_registration_checks (self,
- cs_supported,
- ps_supported,
- eps_supported,
+ is_cs_supported,
+ is_ps_supported,
+ is_eps_supported,
+ is_5gs_supported,
(GAsyncReadyCallback) run_registration_checks_ready,
- operation_result);
+ task);
}
/*****************************************************************************/
/* Register in network (3GPP interface) */
+static gboolean
+modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
static void
-modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+cmatt_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
if (operator_id) {
/* Currently only VZW is supported */
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting a specific operator Id is not supported");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Setting a specific operator ID is not supported");
+ g_object_unref (task);
return;
}
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
- "%CMATT=1",
- 3,
- FALSE,
- FALSE, /* raw */
- cancellable,
- callback,
- user_data);
-}
-
-static gboolean
-modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
-{
- return !!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, error);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "%CMATT=1",
+ 3,
+ FALSE, /* allow cached */
+ (GAsyncReadyCallback)cmatt_ready,
+ task);
}
/*****************************************************************************/
@@ -607,11 +588,10 @@ altair_reregister_ready (MMBaseModem *self,
GAsyncResult *res,
gpointer user_data)
{
- if (!mm_base_modem_at_command_finish (self, res, NULL)) {
- mm_dbg ("Failed to reregister modem");
- } else {
- mm_dbg ("Modem reregistered successfully");
- }
+ if (!mm_base_modem_at_command_finish (self, res, NULL))
+ mm_obj_dbg (self, "failed to reregister modem");
+ else
+ mm_obj_dbg (self, "modem reregistered successfully");
MM_BROADBAND_MODEM_ALTAIR_LTE (self)->priv->sim_refresh_detach_in_progress = FALSE;
}
@@ -621,12 +601,12 @@ altair_deregister_ready (MMBaseModem *self,
gpointer user_data)
{
if (!mm_base_modem_at_command_finish (self, res, NULL)) {
- mm_dbg ("Deregister modem failed");
+ mm_obj_dbg (self, "deregister modem failed");
MM_BROADBAND_MODEM_ALTAIR_LTE (self)->priv->sim_refresh_detach_in_progress = FALSE;
return;
}
- mm_dbg ("Deregistered modem, now reregistering");
+ mm_obj_dbg (self, "deregistered modem, now reregistering");
/* Register */
mm_base_modem_at_command (
@@ -648,7 +628,7 @@ altair_load_own_numbers_ready (MMIfaceModem *iface_modem,
str_list = MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish (MM_IFACE_MODEM (self), res, &error);
if (error) {
- mm_warn ("Couldn't reload Own Numbers: '%s'", error->message);
+ mm_obj_warn (self, "Couldn't reload Own Numbers: '%s'", error->message);
g_error_free (error);
}
if (str_list) {
@@ -661,7 +641,7 @@ altair_load_own_numbers_ready (MMIfaceModem *iface_modem,
self->priv->sim_refresh_detach_in_progress = TRUE;
/* Deregister */
- mm_dbg ("Reregistering modem");
+ mm_obj_dbg (self, "reregistering modem");
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"%CMATT=0",
@@ -674,7 +654,7 @@ altair_load_own_numbers_ready (MMIfaceModem *iface_modem,
static gboolean
altair_sim_refresh_timer_expired (MMBroadbandModemAltairLte *self)
{
- mm_dbg ("No more SIM refreshes, reloading Own Numbers and reregistering modem");
+ mm_obj_dbg (self, "no more SIM refreshes, reloading own numbers and reregistering modem");
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers);
g_assert (MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish);
@@ -684,7 +664,7 @@ altair_sim_refresh_timer_expired (MMBroadbandModemAltairLte *self)
self);
self->priv->sim_refresh_timer_id = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -692,7 +672,7 @@ altair_sim_refresh_changed (MMPortSerialAt *port,
GMatchInfo *match_info,
MMBroadbandModemAltairLte *self)
{
- mm_dbg ("Received SIM refresh notification");
+ mm_obj_dbg (self, "received SIM refresh notification");
if (self->priv->sim_refresh_timer_id) {
g_source_remove (self->priv->sim_refresh_timer_id);
}
@@ -730,7 +710,7 @@ altair_statcm_changed (MMPortSerialAt *port,
mm_get_int_from_match_info (match_info, 1, &pdn_event);
- mm_dbg ("altair_statcm_changed %d", pdn_event);
+ mm_obj_dbg (self, "PDN event detected: %d", pdn_event);
/* Currently we only care about bearer disconnection */
@@ -770,7 +750,7 @@ set_3gpp_unsolicited_events_handlers (MMBroadbandModemAltairLte *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable/disable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -805,26 +785,24 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_3gpp_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_ALTAIR_LTE (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -832,33 +810,29 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_3gpp_setup_unsolicited_events_ready,
- result);
+ task);
}
static void
parent_3gpp_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -866,12 +840,9 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own cleanup first */
set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_ALTAIR_LTE (self), FALSE);
@@ -880,36 +851,17 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_3gpp_cleanup_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Enabling unsolicited events (3GPP interface) */
-static gboolean
-response_processor_no_result_stop_on_error (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
-{
- if (error) {
- *result_error = g_error_copy (error);
- return TRUE;
- }
-
- *result = NULL;
- return FALSE;
-}
-
static const MMBaseModemAtCommand unsolicited_events_enable_sequence[] = {
- { "%STATCM=1", 10, FALSE, response_processor_no_result_stop_on_error },
- { "%NOTIFYEV=\"SIMREFRESH\",1", 10, FALSE, NULL },
- { "%PCOINFO=1", 10, FALSE, NULL },
- { NULL }
+ { "%STATCM=1", 10, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { "%NOTIFYEV=\"SIMREFRESH\",1", 10, FALSE, NULL },
+ { "%PCOINFO=1", 10, FALSE, NULL },
+ { NULL }
};
static gboolean
@@ -917,36 +869,34 @@ modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
own_enable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_finish (self, res, NULL, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -957,7 +907,7 @@ parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
(GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -965,18 +915,15 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's enable */
iface_modem_3gpp_parent->enable_unsolicited_events (
self,
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -994,36 +941,34 @@ modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
own_disable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_finish (self, res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1031,7 +976,7 @@ own_disable_unsolicited_events_ready (MMBaseModem *self,
iface_modem_3gpp_parent->disable_unsolicited_events (
MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -1039,12 +984,9 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own disable first */
mm_base_modem_at_sequence (
@@ -1053,7 +995,7 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
(GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1065,15 +1007,19 @@ modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self,
GError **error)
{
const gchar *result;
- gchar *operator_code;
+ gchar *operator_code = NULL;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (!result)
return NULL;
- operator_code = mm_3gpp_parse_operator (result, MM_MODEM_CHARSET_UNKNOWN);
- if (operator_code)
- mm_dbg ("loaded Operator Code: %s", operator_code);
+ if (!mm_3gpp_parse_cops_read_response (result,
+ NULL, /* mode */
+ NULL, /* format */
+ &operator_code,
+ NULL, /* act */
+ error))
+ return NULL;
return operator_code;
}
@@ -1083,8 +1029,6 @@ modem_3gpp_load_operator_code (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading Operator Code...");
-
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+COPS=3,2",
6,
@@ -1109,16 +1053,21 @@ modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self,
GError **error)
{
const gchar *result;
- gchar *operator_name;
+ gchar *operator_name = NULL;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (!result)
return NULL;
- operator_name = mm_3gpp_parse_operator (result, MM_MODEM_CHARSET_UNKNOWN);
- if (operator_name)
- mm_dbg ("loaded Operator Name: %s", operator_name);
+ if (!mm_3gpp_parse_cops_read_response (result,
+ NULL, /* mode */
+ NULL, /* format */
+ &operator_name,
+ NULL, /* act */
+ error))
+ return NULL;
+ mm_3gpp_normalize_operator (&operator_name, MM_MODEM_CHARSET_UNKNOWN, self);
return operator_name;
}
@@ -1127,8 +1076,6 @@ modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("Loading Operator Name...");
-
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+COPS=3,0",
6,
@@ -1145,154 +1092,33 @@ modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
}
/*****************************************************************************/
-/* Subscription State loading (3GPP interface) */
-
-typedef struct {
- MMIfaceModem3gpp *self;
- GSimpleAsyncResult *result;
- gchar *pco_info;
-} LoadSubscriptionStateContext;
-
-static void
-load_subscription_state_context_complete_and_free (LoadSubscriptionStateContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_free (ctx->pco_info);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LoadSubscriptionStateContext, ctx);
-}
-
-static MMModem3gppSubscriptionState
-altair_vzw_pco_value_to_mm_modem_3gpp_subscription_state (guint pco_value)
-{
- switch (pco_value) {
- case 0:
- return MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED;
- case 3:
- return MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA;
- case 5:
- return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED;
- default:
- return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN;
- }
-}
-
-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 GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
-}
+/* PCOINFO unsolicited event handler */
static void
-altair_get_subscription_state (MMIfaceModem3gpp *self,
- LoadSubscriptionStateContext *ctx)
+altair_pco_info_changed (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemAltairLte *self)
{
- guint pco_value = -1;
- GError *error = NULL;
- MMModem3gppSubscriptionState subscription_state;
+ const gchar *pco_info;
+ MMPco *pco;
+ g_autoptr(GError) error = NULL;
- mm_dbg ("Parsing vendor PCO info: %s", ctx->pco_info);
- pco_value = mm_altair_parse_vendor_pco_info (ctx->pco_info, &error);
- if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- load_subscription_state_context_complete_and_free (ctx);
- return;
- }
- mm_dbg ("PCO value = %d", pco_value);
-
- subscription_state = altair_vzw_pco_value_to_mm_modem_3gpp_subscription_state (pco_value);
- g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (subscription_state), NULL);
- load_subscription_state_context_complete_and_free (ctx);
-}
-
-static void
-altair_load_vendor_pco_info_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- LoadSubscriptionStateContext *ctx)
-{
- const gchar *response;
- GError *error = NULL;
+ pco_info = g_match_info_fetch (match_info, 0);
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (error) {
- mm_dbg ("Failed to load vendor PCO info.");
- g_simple_async_result_take_error (ctx->result, error);
- load_subscription_state_context_complete_and_free (ctx);
+ /* ignore if empty */
+ if (!pco_info || !pco_info[0])
return;
- }
- g_assert (response);
- ctx->pco_info = g_strdup (response);
- altair_get_subscription_state (self, ctx);
-}
-
-static void
-modem_3gpp_load_subscription_state (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- LoadSubscriptionStateContext *ctx;
-
- ctx = g_slice_new0 (LoadSubscriptionStateContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_subscription_state);
-
- mm_dbg ("Loading vendor PCO info...");
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "%PCOINFO?",
- 6,
- FALSE,
- (GAsyncReadyCallback)altair_load_vendor_pco_info_ready,
- ctx);
-}
-
-/*****************************************************************************/
-/* PCOINFO unsolicited event handler */
-static void
-altair_get_subscription_state_ready (MMBroadbandModemAltairLte *self,
- GAsyncResult *res,
- gpointer *user_data)
-{
- GError *error = NULL;
- MMModem3gppSubscriptionState subscription_state;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), &error)) {
- mm_warn ("Couldn't load Subscription State: '%s'", error->message);
- g_error_free (error);
+ mm_obj_dbg (self, "parsing vendor PCO info: %s", pco_info);
+ pco = mm_altair_parse_vendor_pco_info (pco_info, &error);
+ if (!pco) {
+ mm_obj_warn (self, "error parsing vendor PCO info: %s", error->message);
return;
}
- subscription_state = GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- if (subscription_state != MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN)
- mm_iface_modem_3gpp_update_subscription_state (MM_IFACE_MODEM_3GPP (self), subscription_state);
-}
-
-static void
-altair_pco_info_changed (MMPortSerialAt *port,
- GMatchInfo *match_info,
- MMBroadbandModemAltairLte *self)
-{
- LoadSubscriptionStateContext *ctx;
- const gchar *response;
-
- ctx = g_slice_new0 (LoadSubscriptionStateContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- (GAsyncReadyCallback)altair_get_subscription_state_ready,
- NULL,
- altair_pco_info_changed);
- response = g_match_info_fetch (match_info, 0);
- ctx->pco_info = g_strdup (response);
- altair_get_subscription_state (MM_IFACE_MODEM_3GPP (self), ctx);
+ self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco);
+ mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (self), self->priv->pco_list);
+ g_object_unref (pco);
}
/*****************************************************************************/
@@ -1340,6 +1166,9 @@ mm_broadband_modem_altair_lte_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Altair bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
/* Since this is an LTE-only modem - don't bother query
* anything else */
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
@@ -1452,8 +1281,6 @@ 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;
}
static void
diff --git a/plugins/altair/mm-modem-helpers-altair-lte.c b/plugins/altair/mm-modem-helpers-altair-lte.c
index 3e52ee9c..d2fd9af7 100644
--- a/plugins/altair/mm-modem-helpers-altair-lte.c
+++ b/plugins/altair/mm-modem-helpers-altair-lte.c
@@ -50,12 +50,12 @@ mm_altair_parse_bands_response (const gchar *response)
MMModemBand band;
band_value = (guint32)strtoul (split[i], NULL, 10);
- band = MM_MODEM_BAND_EUTRAN_I - 1 + band_value;
+ band = MM_MODEM_BAND_EUTRAN_1 - 1 + band_value;
/* Due to a firmware issue, the modem may incorrectly includes 0 in the
* bands response. We thus ignore any band value outside the range of
* E-UTRAN operating bands. */
- if (band >= MM_MODEM_BAND_EUTRAN_I && band <= MM_MODEM_BAND_EUTRAN_XLIV)
+ if (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_44)
g_array_append_val (bands, band);
}
@@ -71,13 +71,13 @@ gchar *
mm_altair_parse_ceer_response (const gchar *response,
GError **error)
{
- GRegex *r;
- GMatchInfo *match_info = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
gchar *ceer_response = NULL;
/* First accept an empty response as the no error case. Sometimes, the only
- * respone to the AT+CEER query is an OK.
+ * response to the AT+CEER query is an OK.
*/
if (g_strcmp0 ("", response) == 0) {
return g_strdup ("");
@@ -93,8 +93,6 @@ mm_altair_parse_ceer_response (const gchar *response,
if (!g_regex_match (r, response, 0, &match_info)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Could not parse +CEER response");
- g_match_info_free (match_info);
- g_regex_unref (r);
return NULL;
}
@@ -104,28 +102,23 @@ mm_altair_parse_ceer_response (const gchar *response,
ceer_response = g_strdup ("");
}
- g_match_info_free (match_info);
- g_regex_unref (r);
return ceer_response;
}
/*****************************************************************************/
/* %CGINFO="cid",1 response parser */
-guint
+gint
mm_altair_parse_cid (const gchar *response, GError **error)
{
- GRegex *regex;
- GMatchInfo *match_info;
+ g_autoptr(GRegex) regex = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
guint cid = -1;
regex = g_regex_new ("\\%CGINFO:\\s*(\\d+)", G_REGEX_RAW, 0, NULL);
g_assert (regex);
- if (!g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, error)) {
- g_match_info_free (match_info);
- g_regex_unref (regex);
+ if (!g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, error))
return -1;
- }
if (!mm_get_uint_from_match_info (match_info, 1, &cid))
g_set_error (error,
@@ -133,70 +126,25 @@ mm_altair_parse_cid (const gchar *response, GError **error)
MM_CORE_ERROR_FAILED,
"Failed to parse %%CGINFO=\"cid\",1 response");
- g_match_info_free (match_info);
- g_regex_unref (regex);
return cid;
}
/*****************************************************************************/
/* %PCOINFO response parser */
-typedef enum {
- MM_VZW_PCO_PROVISIONED = 0,
- MM_VZW_PCO_LIMIT_REACHED = 1,
- MM_VZW_PCO_OUT_OF_DATA = 3,
- MM_VZW_PCO_UNPROVISIONED = 5
-} MMVzwPco;
-
-static guint
-altair_extract_vzw_pco_value (const gchar *pco_payload, GError **error)
-{
- GRegex *regex;
- GMatchInfo *match_info;
- guint pco_value = -1;
-
- /* Extract PCO value from PCO payload.
- * The PCO value in the VZW network is after the VZW PLMN (MCC+MNC 311-480).
- */
- regex = g_regex_new ("130184(\\d+)", G_REGEX_RAW, 0, NULL);
- g_assert (regex);
- if (!g_regex_match_full (regex,
- pco_payload,
- strlen (pco_payload),
- 0,
- 0,
- &match_info,
- error)) {
- g_match_info_free (match_info);
- g_regex_unref (regex);
- return -1;
- }
-
- if (!g_match_info_matches (match_info) ||
- !mm_get_uint_from_match_info (match_info, 1, &pco_value))
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse PCO value from PCO payload: '%s'",
- pco_payload);
-
- g_match_info_free (match_info);
- g_regex_unref (regex);
-
- return pco_value;
-}
-
-guint
+MMPco *
mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error)
{
- GRegex *regex;
- GMatchInfo *match_info;
- guint pco_value = -1;
+ g_autoptr(GRegex) regex = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ MMPco *pco = NULL;
gint num_matches;
- if (!pco_info[0])
- /* No APNs configured, all done */
- return -1;
+ if (!pco_info || !pco_info[0]) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "No PCO info given");
+ return NULL;
+ }
/* Expected %PCOINFO response:
*
@@ -207,11 +155,9 @@ mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error)
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, NULL);
g_assert (regex);
- if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error)) {
- g_match_info_free (match_info);
- g_regex_unref (regex);
- return -1;
- }
+
+ if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error))
+ return NULL;
num_matches = g_match_info_get_match_count (match_info);
if (num_matches != 5) {
@@ -220,65 +166,94 @@ mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error)
MM_CORE_ERROR_FAILED,
"Failed to parse substrings, number of matches: %d",
num_matches);
- g_match_info_free (match_info);
- g_regex_unref (regex);
- return -1;
+ return NULL;
}
while (g_match_info_matches (match_info)) {
- guint pco_cid;
- gchar *pco_id;
- gchar *pco_payload;
+ guint pco_cid;
+ g_autofree gchar *pco_id = NULL;
+ g_autofree gchar *pco_payload = NULL;
+ g_autofree guint8 *pco_payload_bytes = NULL;
+ gsize pco_payload_bytes_len;
+ guint8 pco_prefix[6];
+ GByteArray *pco_raw;
+ gsize pco_raw_len;
if (!mm_get_uint_from_match_info (match_info, 1, &pco_cid)) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse CID from PCO info: '%s'",
- pco_info);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse CID from PCO info: '%s'", pco_info);
break;
}
+ /* We are only interested in IMS and Internet PDN PCO. */
+ if (pco_cid != MM_ALTAIR_IMS_PDN_CID && pco_cid != MM_ALTAIR_INTERNET_PDN_CID) {
+ g_match_info_next (match_info, error);
+ continue;
+ }
+
pco_id = mm_get_string_unquoted_from_match_info (match_info, 3);
if (!pco_id) {
- g_set_error (error,
- MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Couldn't parse PCO ID from PCO info: '%s'",
- pco_info);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse PCO ID from PCO info: '%s'", pco_info);
break;
}
if (g_strcmp0 (pco_id, "FF00")) {
- g_free (pco_id);
g_match_info_next (match_info, error);
continue;
}
- g_free (pco_id);
pco_payload = mm_get_string_unquoted_from_match_info (match_info, 4);
if (!pco_payload) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse PCO payload from PCO info: '%s'",
- pco_info);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse PCO payload from PCO info: '%s'", pco_info);
break;
}
- pco_value = altair_extract_vzw_pco_value (pco_payload, error);
- g_free (pco_payload);
-
- /* We are only interested in IMS and Internet PDN PCO. */
- if (pco_cid == MM_ALTAIR_IMS_PDN_CID || pco_cid == MM_ALTAIR_INTERNET_PDN_CID) {
+ pco_payload_bytes = mm_utils_hexstr2bin (pco_payload, -1, &pco_payload_bytes_len, error);
+ if (!pco_payload_bytes) {
+ g_prefix_error (error, "Invalid PCO payload from PCO info '%s': ", pco_info);
break;
}
- pco_value = -1;
- g_match_info_next (match_info, error);
+ /* Protocol Configuration Options (PCO) is an information element with an
+ * identifier (IEI) 0x27 and contains between 3 and 253 octets. See 3GPP TS
+ * 24.008 for more details on PCO.
+ *
+ * NOTE: The standard uses one-based indexing, but to better correlate to the
+ * code, zero-based indexing is used in the description hereinafter.
+ *
+ * Octet | Value
+ * --------+--------------------------------------------
+ * 0 | PCO IEI (= 0x27)
+ * 1 | Length of PCO contents (= total length - 2)
+ * 2 | bit 7 : ext
+ * | bit 6 to 3 : spare (= 0b0000)
+ * | bit 2 to 0 : Configuration protocol
+ * 3 to 4 | Element 1 ID
+ * 5 | Length of element 1 contents
+ * 6 to m | Element 1 contents
+ * ... |
+ */
+ pco_raw_len = sizeof (pco_prefix) + pco_payload_bytes_len;
+ pco_prefix[0] = 0x27;
+ pco_prefix[1] = pco_raw_len - 2;
+ pco_prefix[2] = 0x80;
+ /* Verizon uses element ID 0xFF00 for carrier-specific PCO content. */
+ pco_prefix[3] = 0xFF;
+ pco_prefix[4] = 0x00;
+ pco_prefix[5] = pco_payload_bytes_len;
+
+ pco_raw = g_byte_array_sized_new (pco_raw_len);
+ g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix));
+ g_byte_array_append (pco_raw, pco_payload_bytes, pco_payload_bytes_len);
+
+ pco = mm_pco_new ();
+ mm_pco_set_session_id (pco, pco_cid);
+ mm_pco_set_complete (pco, TRUE);
+ mm_pco_set_data (pco, pco_raw->data, pco_raw->len);
+ break;
}
- g_match_info_free (match_info);
- g_regex_unref (regex);
-
- return pco_value;
+ return pco;
}
diff --git a/plugins/altair/mm-modem-helpers-altair-lte.h b/plugins/altair/mm-modem-helpers-altair-lte.h
index b3199978..ff7f64b0 100644
--- a/plugins/altair/mm-modem-helpers-altair-lte.h
+++ b/plugins/altair/mm-modem-helpers-altair-lte.h
@@ -19,6 +19,9 @@
#include <glib.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
/* Bands response parser */
GArray *mm_altair_parse_bands_response (const gchar *response);
@@ -27,9 +30,9 @@ gchar *mm_altair_parse_ceer_response (const gchar *response,
GError **error);
/* %CGINFO="cid",1 response parser */
-guint mm_altair_parse_cid (const gchar *response, GError **error);
+gint mm_altair_parse_cid (const gchar *response, GError **error);
/* %PCOINFO response parser */
-guint mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error);
+MMPco *mm_altair_parse_vendor_pco_info (const gchar *pco_info, GError **error);
#endif /* MM_MODEM_HELPERS_ALTAIR_H */
diff --git a/plugins/altair/mm-plugin-altair-lte.c b/plugins/altair/mm-plugin-altair-lte.c
index 1e1ecef0..054a600f 100644
--- a/plugins/altair/mm-plugin-altair-lte.c
+++ b/plugins/altair/mm-plugin-altair-lte.c
@@ -31,19 +31,34 @@
G_DEFINE_TYPE (MMPluginAltairLte, mm_plugin_altair_lte, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+/* Custom commands for AT probing */
+
+/* Increase the response timeout for probe commands since some altair modems
+ take longer to respond after a reset.
+ */
+static const MMPortProbeAtCommand custom_at_probe[] = {
+ { "AT", 7, mm_port_probe_response_processor_is_at },
+ { "AT", 7, mm_port_probe_response_processor_is_at },
+ { "AT", 7, mm_port_probe_response_processor_is_at },
+ { NULL }
+};
+
+/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_altair_lte_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_altair_lte_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -62,9 +77,10 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_ALTAIR_LTE,
- MM_PLUGIN_NAME, "Altair LTE",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_PRODUCT_IDS, products,
+ MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe,
MM_PLUGIN_ALLOWED_SINGLE_AT, TRUE,
MM_PLUGIN_SEND_LF, TRUE,
NULL));
diff --git a/plugins/altair/tests/test-modem-helpers-altair-lte.c b/plugins/altair/tests/test-modem-helpers-altair-lte.c
index d83d3e50..da9eaf32 100644
--- a/plugins/altair/tests/test-modem-helpers-altair-lte.c
+++ b/plugins/altair/tests/test-modem-helpers-altair-lte.c
@@ -16,6 +16,7 @@
#include <stdarg.h>
#include <stdio.h>
+#include <string.h>
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
@@ -43,10 +44,10 @@ test_parse_bands (void)
bands = mm_altair_parse_bands_response ("0, 0, 1, 4,13,44,45");
g_assert (bands != NULL);
g_assert_cmpuint (bands->len, ==, 4);
- g_assert_cmpuint (g_array_index (bands, MMModemBand, 0), ==, MM_MODEM_BAND_EUTRAN_I);
- g_assert_cmpuint (g_array_index (bands, MMModemBand, 1), ==, MM_MODEM_BAND_EUTRAN_IV);
- g_assert_cmpuint (g_array_index (bands, MMModemBand, 2), ==, MM_MODEM_BAND_EUTRAN_XIII);
- g_assert_cmpuint (g_array_index (bands, MMModemBand, 3), ==, MM_MODEM_BAND_EUTRAN_XLIV);
+ g_assert_cmpuint (g_array_index (bands, MMModemBand, 0), ==, MM_MODEM_BAND_EUTRAN_1);
+ g_assert_cmpuint (g_array_index (bands, MMModemBand, 1), ==, MM_MODEM_BAND_EUTRAN_4);
+ g_assert_cmpuint (g_array_index (bands, MMModemBand, 2), ==, MM_MODEM_BAND_EUTRAN_13);
+ g_assert_cmpuint (g_array_index (bands, MMModemBand, 3), ==, MM_MODEM_BAND_EUTRAN_44);
g_array_free (bands, TRUE);
}
@@ -59,7 +60,7 @@ typedef struct {
} CeerTest;
static const CeerTest ceer_tests[] = {
- { "", "" }, /* Special case, sometimes the response is empty, treat it as a valid response. */
+ { "", "" }, /* Special case, sometimes the response is empty, treat it as a good response. */
{ "+CEER:", "" },
{ "+CEER: EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED", "EPS_AND_NON_EPS_SERVICES_NOT_ALLOWED" },
{ "+CEER: NO_SUITABLE_CELLS_IN_TRACKING_AREA", "NO_SUITABLE_CELLS_IN_TRACKING_AREA" },
@@ -97,52 +98,86 @@ test_parse_cid (void)
g_assert (mm_altair_parse_cid ("%CGINFO:blah", NULL) == -1);
}
-static void
-test_parse_vendor_pco_info (void)
-{
- guint pco_value;
+/*****************************************************************************/
+/* Test %PCOINFO responses */
+
+typedef struct {
+ const gchar *pco_info;
+ guint32 session_id;
+ gsize pco_data_size;
+ guint8 pco_data[50];
+} TestValidPcoInfo;
+static const TestValidPcoInfo good_pco_infos[] = {
/* Valid PCO values */
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,1,FF00,13018400", NULL);
- g_assert (pco_value == 0);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,1,FF00,13018403", NULL);
- g_assert (pco_value == 3);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,1,FF00,13018405", NULL);
- g_assert (pco_value == 5);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018400", NULL);
- g_assert (pco_value == 0);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018403", NULL);
- g_assert (pco_value == 3);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018405", NULL);
- g_assert (pco_value == 5);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO:1,FF00,13018400", NULL);
- g_assert (pco_value == 0);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO:1,FF00,13018403", NULL);
- g_assert (pco_value == 3);
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO:1,FF00,13018405", NULL);
- g_assert (pco_value == 5);
- /* Different container */
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,3,F000,13018401", NULL);
- g_assert (pco_value == -1);
- /* Invalid CID */
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,2,FF00,13018401", NULL);
- g_assert (pco_value == -1);
+ { "%PCOINFO: 1,1,FF00,13018400", 1, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } },
+ { "%PCOINFO: 1,1,FF00,13018403", 1, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
+ { "%PCOINFO: 1,1,FF00,13018405", 1, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
+ { "%PCOINFO: 1,3,FF00,13018400", 3, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } },
+ { "%PCOINFO: 1,3,FF00,13018403", 3, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
+ { "%PCOINFO: 1,3,FF00,13018405", 3, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
+ { "%PCOINFO:1,FF00,13018400", 1, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x00 } },
+ { "%PCOINFO:1,FF00,13018403", 1, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
+ { "%PCOINFO:1,FF00,13018405", 1, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x05 } },
/* Different payload */
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018501", NULL);
- g_assert (pco_value == -1);
- /* Bad PCO info */
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: blah,blah,FF00,13018401", NULL);
- g_assert (pco_value == -1);
+ { "%PCOINFO: 1,3,FF00,130185", 3, 9,
+ { 0x27, 0x07, 0x80, 0xFF, 0x00, 0x03, 0x13, 0x01, 0x85 } },
/* Multiline PCO info */
- pco_value = mm_altair_parse_vendor_pco_info ("%PCOINFO: 1,2,FF00,13018400\r\n%PCOINFO: 1,3,FF00,13018403", NULL);
- g_assert (pco_value == 3);
+ { "%PCOINFO: 1,2,FF00,13018400\r\n%PCOINFO: 1,3,FF00,13018403", 3, 10,
+ { 0x27, 0x08, 0x80, 0xFF, 0x00, 0x04, 0x13, 0x01, 0x84, 0x03 } },
+};
+
+static const gchar *bad_pco_infos[] = {
+ /* Different container */
+ "%PCOINFO: 1,3,F000,13018401",
+ /* Ingood CID */
+ "%PCOINFO: 1,2,FF00,13018401",
+ /* Bad PCO info */
+ "%PCOINFO: blah,blah,FF00,13018401",
+ /* Bad PCO payload */
+ "%PCOINFO: 1,1,FF00,130184011",
+};
+
+static void
+test_parse_vendor_pco_info (void)
+{
+ MMPco *pco;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (good_pco_infos); ++i) {
+ const guint8 *pco_data;
+ gsize pco_data_size;
+
+ pco = mm_altair_parse_vendor_pco_info (good_pco_infos[i].pco_info, NULL);
+ g_assert (pco != NULL);
+ g_assert_cmpuint (mm_pco_get_session_id (pco), ==, good_pco_infos[i].session_id);
+ g_assert (mm_pco_is_complete (pco));
+ pco_data = mm_pco_get_data (pco, &pco_data_size);
+ g_assert (pco_data != NULL);
+ g_assert_cmpuint (pco_data_size, ==, good_pco_infos[i].pco_data_size);
+ g_assert_cmpint (memcmp (pco_data, good_pco_infos[i].pco_data, pco_data_size), ==, 0);
+ g_object_unref (pco);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (bad_pco_infos); ++i) {
+ pco = mm_altair_parse_vendor_pco_info (bad_pco_infos[i], NULL);
+ g_assert (pco == NULL);
+ }
}
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/altair/parse_bands", test_parse_bands);
diff --git a/plugins/anydata/mm-broadband-modem-anydata.c b/plugins/anydata/mm-broadband-modem-anydata.c
index de804c10..95f8cbd2 100644
--- a/plugins/anydata/mm-broadband-modem-anydata.c
+++ b/plugins/anydata/mm-broadband-modem-anydata.c
@@ -25,7 +25,7 @@
#include "ModemManager.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-errors-types.h"
#include "mm-base-modem-at.h"
@@ -38,7 +38,7 @@ static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemAnydata, mm_broadband_modem_anydata, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init));
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init))
/*****************************************************************************/
/* Detailed registration state (CDMA interface) */
@@ -47,36 +47,6 @@ typedef struct {
MMModemCdmaRegistrationState detailed_evdo_state;
} DetailedRegistrationStateResults;
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- MMPortSerialAt *port;
- MMModemCdmaRegistrationState cdma1x_state;
- MMModemCdmaRegistrationState evdo_state;
- GError *error;
-} DetailedRegistrationStateContext;
-
-static void
-detailed_registration_state_context_complete_and_free (DetailedRegistrationStateContext *ctx)
-{
- if (ctx->error)
- g_simple_async_result_take_error (ctx->result, ctx->error);
- else {
- DetailedRegistrationStateResults *results;
-
- results = g_new (DetailedRegistrationStateResults, 1);
- results->detailed_cdma1x_state = ctx->cdma1x_state;
- results->detailed_evdo_state = ctx->evdo_state;
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, g_free);
- }
-
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->port);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
get_detailed_registration_state_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
@@ -86,31 +56,36 @@ get_detailed_registration_state_finish (MMIfaceModemCdma *self,
{
DetailedRegistrationStateResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*detailed_cdma1x_state = results->detailed_cdma1x_state;
*detailed_evdo_state = results->detailed_evdo_state;
+ g_free (results);
return TRUE;
}
static void
hstate_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
+ DetailedRegistrationStateResults *results;
GError *error = NULL;
const gchar *response;
GRegex *r;
GMatchInfo *match_info;
+ results = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
/* Leave superclass' reg state alone if AT*HSTATE isn't supported */
g_error_free (error);
- /* Result is set here when completing */
- detailed_registration_state_context_complete_and_free (ctx);
+
+ g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free);
+ g_object_unref (task);
return;
}
@@ -137,14 +112,14 @@ hstate_ready (MMIfaceModemCdma *self,
* It may be that IDLE actually means NO SERVICE too; not sure.
*/
if (dbm > -105)
- ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ results->detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
case 4: /* ACCESS */
case 5: /* CONNECT */
- ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ results->detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
default:
- mm_warn ("ANYDATA: unknown *STATE (%d); assuming no service.", val);
+ mm_obj_warn (self, "unknown *HSTATE (%d); assuming no service", val);
/* fall through */
case 0: /* NO SERVICE */
case 1: /* ACQUISITION */
@@ -157,25 +132,29 @@ hstate_ready (MMIfaceModemCdma *self,
g_match_info_free (match_info);
g_regex_unref (r);
- /* Result is set here when completing */
- detailed_registration_state_context_complete_and_free (ctx);
+ g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free);
+ g_object_unref (task);
}
static void
state_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
+ DetailedRegistrationStateResults *results;
+ GError *error = NULL;
const gchar *response;
GRegex *r;
GMatchInfo *match_info;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &ctx->error);
- if (ctx->error) {
- detailed_registration_state_context_complete_and_free (ctx);
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ results = g_task_get_task_data (task);
response = mm_strip_tag (response, "*STATE:");
/* Format is "<channel>,<pn>,<sid>,<nid>,<state>,<rssi>,..." */
@@ -199,15 +178,15 @@ state_ready (MMIfaceModemCdma *self,
* It may be that IDLE actually means NO SERVICE too; not sure.
*/
if (dbm > -105)
- ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ results->detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
case 2: /* ACCESS */
case 3: /* PAGING */
case 4: /* TRAFFIC */
- ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+ results->detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
break;
default:
- mm_warn ("ANYDATA: unknown *STATE (%d); assuming no service.", val);
+ mm_obj_warn (self, "unknown *HSTATE (%d); assuming no service", val);
/* fall through */
case 0: /* NO SERVICE */
break;
@@ -224,7 +203,7 @@ state_ready (MMIfaceModemCdma *self,
3,
FALSE,
(GAsyncReadyCallback)hstate_ready,
- ctx);
+ task);
}
static void
@@ -234,24 +213,22 @@ get_detailed_registration_state (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- DetailedRegistrationStateContext *ctx;
-
- /* Setup context */
- ctx = g_new0 (DetailedRegistrationStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_detailed_registration_state);
- ctx->cdma1x_state = cdma1x_state;
- ctx->evdo_state = evdo_state;
+ DetailedRegistrationStateResults *results;
+ GTask *task;
+
+ results = g_new (DetailedRegistrationStateResults, 1);
+ results->detailed_cdma1x_state = cdma1x_state;
+ results->detailed_evdo_state = evdo_state;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, results, g_free);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"*STATE?",
3,
FALSE,
(GAsyncReadyCallback)state_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -295,7 +272,7 @@ setup_ports (MMBroadbandModem *self)
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Now reset the unsolicited messages */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -354,6 +331,9 @@ mm_broadband_modem_anydata_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/anydata/mm-plugin-anydata.c b/plugins/anydata/mm-plugin-anydata.c
index 73df9e08..f8c0c30e 100644
--- a/plugins/anydata/mm-plugin-anydata.c
+++ b/plugins/anydata/mm-plugin-anydata.c
@@ -21,7 +21,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-plugin-anydata.h"
#include "mm-broadband-modem-anydata.h"
@@ -31,14 +31,14 @@
G_DEFINE_TYPE (MMPluginAnydata, mm_plugin_anydata, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -47,8 +47,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered AnyDATA modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered AnyDATA modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -56,7 +56,7 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_anydata_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_anydata_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -68,12 +68,12 @@ create_modem (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendor_ids[] = { 0x16d5, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_ANYDATA,
- MM_PLUGIN_NAME, "AnyDATA",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/broadmobi/77-mm-broadmobi-port-types.rules b/plugins/broadmobi/77-mm-broadmobi-port-types.rules
new file mode 100644
index 00000000..685e1467
--- /dev/null
+++ b/plugins/broadmobi/77-mm-broadmobi-port-types.rules
@@ -0,0 +1,16 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_broadmobi_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2020", GOTO="mm_broadmobi_port_types"
+GOTO="mm_broadmobi_port_types_end"
+
+LABEL="mm_broadmobi_port_types"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# BroadMobi BM818
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2020", ATTRS{idProduct}=="2060", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+LABEL="mm_broadmobi_port_types_end" \ No newline at end of file
diff --git a/plugins/gobi/mm-plugin-gobi.c b/plugins/broadmobi/mm-plugin-broadmobi.c
index 823b95db..6df926d6 100644
--- a/plugins/gobi/mm-plugin-gobi.c
+++ b/plugins/broadmobi/mm-plugin-broadmobi.c
@@ -10,8 +10,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <string.h>
@@ -20,34 +19,35 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-plugin-gobi.h"
-#include "mm-broadband-modem-gobi.h"
-#include "mm-log.h"
+#include "mm-port-enums-types.h"
+#include "mm-log-object.h"
+#include "mm-plugin-broadmobi.h"
+#include "mm-broadband-modem.h"
#if defined WITH_QMI
-#include "mm-broadband-modem-qmi.h"
+# include "mm-broadband-modem-qmi.h"
#endif
-G_DEFINE_TYPE (MMPluginGobi, mm_plugin_gobi, MM_TYPE_PLUGIN)
+G_DEFINE_TYPE (MMPluginBroadmobi, mm_plugin_broadmobi, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
-create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+create_modem (MMPlugin *self,
+ const gchar *uid,
const gchar **drivers,
- guint16 vendor,
- guint16 product,
- GList *probes,
- GError **error)
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Gobi modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered BroadMobi modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -55,11 +55,11 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_gobi_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
}
/*****************************************************************************/
@@ -67,14 +67,14 @@ create_modem (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
- static const gchar *drivers[] = { "qcserial", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendor_ids[] = { 0x2020, 0 };
return MM_PLUGIN (
- g_object_new (MM_TYPE_PLUGIN_GOBI,
- MM_PLUGIN_NAME, "Gobi",
+ g_object_new (MM_TYPE_PLUGIN_BROADMOBI,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
- MM_PLUGIN_ALLOWED_DRIVERS, drivers,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
@@ -82,12 +82,12 @@ mm_plugin_create (void)
}
static void
-mm_plugin_gobi_init (MMPluginGobi *self)
+mm_plugin_broadmobi_init (MMPluginBroadmobi *self)
{
}
static void
-mm_plugin_gobi_class_init (MMPluginGobiClass *klass)
+mm_plugin_broadmobi_class_init (MMPluginBroadmobiClass *klass)
{
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
diff --git a/plugins/broadmobi/mm-plugin-broadmobi.h b/plugins/broadmobi/mm-plugin-broadmobi.h
new file mode 100644
index 00000000..1f46cfce
--- /dev/null
+++ b/plugins/broadmobi/mm-plugin-broadmobi.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_BROADMOBI_H
+#define MM_PLUGIN_BROADMOBI_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_BROADMOBI (mm_plugin_broadmobi_get_type ())
+#define MM_PLUGIN_BROADMOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobi))
+#define MM_PLUGIN_BROADMOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobiClass))
+#define MM_IS_PLUGIN_BROADMOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_BROADMOBI))
+#define MM_IS_PLUGIN_BROADMOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_BROADMOBI))
+#define MM_PLUGIN_BROADMOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_BROADMOBI, MMPluginBroadmobiClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginBroadmobi;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginBroadmobiClass;
+
+GType mm_plugin_broadmobi_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_BROADMOBI_H */
diff --git a/plugins/cinterion/77-mm-cinterion-port-types.rules b/plugins/cinterion/77-mm-cinterion-port-types.rules
index 09de7428..5ad47ceb 100644
--- a/plugins/cinterion/77-mm-cinterion-port-types.rules
+++ b/plugins/cinterion/77-mm-cinterion-port-types.rules
@@ -1,11 +1,61 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_cinterion_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_cinterion_port_types_end"
-ENV{ID_VENDOR_ID}!="1e2d", GOTO="mm_cinterion_port_types_end"
+ACTION!="add|change|move|bind", GOTO="mm_cinterion_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e2d", GOTO="mm_cinterion_port_types"
+GOTO="mm_cinterion_port_types_end"
+LABEL="mm_cinterion_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
-ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0053", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_CINTERION_PORT_TYPE_GPS}="1"
+# PHS8
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0053", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+
+# PLS8 port types
+# ttyACM0 (if #0): AT port
+# ttyACM1 (if #2): AT port
+# ttyACM2 (if #4): GPS data port
+# ttyACM3 (if #6): unknown
+# ttyACM4 (if #8): unknown
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+
+# PLS62 family non-mbim enumeration uses alternate settings for 2G band management
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{ID_MM_CINTERION_MODEM_FAMILY}="imt"
+# PLS62 family non-mbim enumeration
+# ttyACM0 (if #0): AT port
+# ttyACM1 (if #2): AT port
+# ttyACM2 (if #4): can be AT or GNSS in some models
+# ttyACM3 (if #6): AT port (but just ignore)
+# ttyACM4 (if #8): DIAG/QCDM
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005b", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+
+# PLS62 family mbim enumeration
+# ttyACM0 (if #0): AT port
+# ttyACM1 (if #2): AT port
+# ttyACM2 (if #4): can be AT or GNSS in some models
+# ttyACM3 (if #6): AT port (but just ignore)
+# ttyACM4 (if #8): DIAG/QCDM
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="005d", ENV{.MM_USBIFNUM}=="08", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+
+# PLS83
+# ttyACM0 (if #0): AT port
+# ttyACM1 (if #2): AT port
+# ttyACM2 (if #4): GPS data port
+# ttyACM3 (if #6): DIAG/QCDM
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
LABEL="mm_cinterion_port_types_end"
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.c b/plugins/cinterion/mm-broadband-bearer-cinterion.c
new file mode 100644
index 00000000..dcc79a9f
--- /dev/null
+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.c
@@ -0,0 +1,687 @@
+/* -*- 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) 2016 Trimble Navigation Limited
+ * Author: Matthew Stanger <matthew_stanger@trimble.com>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+#include <ModemManager.h>
+#include "mm-base-modem-at.h"
+#include "mm-broadband-bearer-cinterion.h"
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-cinterion.h"
+#include "mm-daemon-enums-types.h"
+
+G_DEFINE_TYPE (MMBroadbandBearerCinterion, mm_broadband_bearer_cinterion, MM_TYPE_BROADBAND_BEARER)
+
+/*****************************************************************************/
+/* WWAN interface mapping */
+
+typedef struct {
+ guint swwan_index;
+ guint usb_iface_num;
+} UsbInterfaceConfig;
+
+/* Map SWWAN index, USB interface number and preferred PDP context.
+ *
+ * The expected USB interface mapping is:
+ * INTERFACE=usb0 -> ID_USB_INTERFACE_NUM=0a
+ * INTERFACE=usb1 -> ID_USB_INTERFACE_NUM=0c
+ */
+static const UsbInterfaceConfig usb_interface_configs[] = {
+ {
+ .swwan_index = 1,
+ .usb_iface_num = 0x0a,
+ },
+ {
+ .swwan_index = 2,
+ .usb_iface_num = 0x0c,
+ },
+};
+
+static gint
+get_usb_interface_config_index (MMPort *data,
+ GError **error)
+{
+ guint usb_iface_num;
+ guint i;
+
+ usb_iface_num = (guint) mm_kernel_device_get_interface_number (mm_port_peek_kernel_device (data));
+
+ for (i = 0; i < G_N_ELEMENTS (usb_interface_configs); i++) {
+ if (usb_interface_configs[i].usb_iface_num == usb_iface_num)
+ return (gint) i;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported WWAN interface: unexpected interface number: 0x%02x", usb_iface_num);
+ return -1;
+}
+
+/*****************************************************************************/
+/* Connection status loading
+ * NOTE: only CONNECTED or DISCONNECTED should be reported here.
+ */
+
+static MMBearerConnectionStatus
+load_connection_status_finish (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize aux;
+
+ aux = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ }
+ return (MMBearerConnectionStatus) aux;
+}
+
+static void
+swwan_check_status_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerCinterion *self;
+ const gchar *response;
+ GError *error = NULL;
+ MMBearerConnectionStatus status;
+ guint cid;
+
+ self = g_task_get_source_object (task);
+ cid = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ status = mm_cinterion_parse_swwan_response (response, cid, self, &error);
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTED);
+ g_task_return_int (task, (gssize) status);
+
+out:
+ g_object_unref (task);
+}
+
+static void
+load_connection_status_by_cid (MMBroadbandBearerCinterion *bearer,
+ gint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ g_autoptr(MMBaseModem) modem = NULL;
+
+ task = g_task_new (bearer, NULL, callback, user_data);
+ if (cid == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown profile id to check connection status");
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_set_task_data (task, GUINT_TO_POINTER (cid), NULL);
+
+ g_object_get (bearer,
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+
+ mm_base_modem_at_command (modem,
+ "^SWWAN?",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback) swwan_check_status_ready,
+ task);
+}
+
+static void
+load_connection_status (MMBaseBearer *bearer,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (bearer),
+ mm_base_bearer_get_profile_id (bearer),
+ callback,
+ user_data);
+}
+
+/******************************************************************************/
+/* Dial 3GPP */
+
+typedef enum {
+ DIAL_3GPP_CONTEXT_STEP_FIRST = 0,
+ DIAL_3GPP_CONTEXT_STEP_AUTH,
+ DIAL_3GPP_CONTEXT_STEP_START_SWWAN,
+ DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION,
+ DIAL_3GPP_CONTEXT_STEP_LAST,
+} Dial3gppContextStep;
+
+typedef struct {
+ MMBroadbandBearerCinterion *self;
+ MMBaseModem *modem;
+ MMPortSerialAt *primary;
+ guint cid;
+ MMPort *data;
+ gint usb_interface_config_index;
+ Dial3gppContextStep step;
+} Dial3gppContext;
+
+static void
+dial_3gpp_context_free (Dial3gppContext *ctx)
+{
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->primary);
+ g_clear_object (&ctx->data);
+ g_slice_free (Dial3gppContext, ctx);
+}
+
+static MMPort *
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void dial_3gpp_context_step (GTask *task);
+
+static void
+dial_connection_status_ready (MMBroadbandBearerCinterion *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerConnectionStatus status;
+ Dial3gppContext *ctx;
+ GError *error = NULL;
+
+ ctx = (Dial3gppContext *) g_task_get_task_data (task);
+
+ status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error);
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "CID %u is reported disconnected", ctx->cid);
+ g_object_unref (task);
+ return;
+ }
+
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED);
+
+ /* Go to next step */
+ ctx->step++;
+ dial_3gpp_context_step (task);
+}
+
+static void
+common_dial_operation_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Dial3gppContext *ctx;
+ GError *error = NULL;
+
+ ctx = (Dial3gppContext *) g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go to next step */
+ ctx->step++;
+ dial_3gpp_context_step (task);
+}
+
+static void
+handle_cancel_dial (GTask *task)
+{
+ Dial3gppContext *ctx;
+ gchar *command;
+
+ ctx = (Dial3gppContext *) g_task_get_task_data (task);
+
+ /* Disconnect, may not succeed. Will not check response on cancel */
+ command = g_strdup_printf ("^SWWAN=0,%u,%u",
+ ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ NULL,
+ NULL);
+ g_free (command);
+}
+
+static void
+dial_3gpp_context_step (GTask *task)
+{
+ MMBroadbandBearerCinterion *self;
+ Dial3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* Check for cancellation */
+ if (g_task_return_error_if_cancelled (task)) {
+ handle_cancel_dial (task);
+ g_object_unref (task);
+ return;
+ }
+
+ switch (ctx->step) {
+ case DIAL_3GPP_CONTEXT_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case DIAL_3GPP_CONTEXT_STEP_AUTH: {
+ gchar *command;
+
+ command = mm_cinterion_build_auth_string (self,
+ mm_broadband_modem_cinterion_get_family (MM_BROADBAND_MODEM_CINTERION (ctx->modem)),
+ mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)),
+ ctx->cid);
+
+ if (command) {
+ mm_obj_dbg (self, "dial step %u/%u: authenticating...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST);
+ /* Send SGAUTH write, if User & Pass are provided.
+ * advance to next state by callback */
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 10,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback) common_dial_operation_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ mm_obj_dbg (self, "dial step %u/%u: authentication not required", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST);
+ ctx->step++;
+ } /* fall through */
+
+ case DIAL_3GPP_CONTEXT_STEP_START_SWWAN: {
+ gchar *command;
+
+ mm_obj_dbg (self, "dial step %u/%u: starting SWWAN interface %u connection...",
+ ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+ command = g_strdup_printf ("^SWWAN=1,%u,%u",
+ ctx->cid,
+ usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback) common_dial_operation_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ case DIAL_3GPP_CONTEXT_STEP_VALIDATE_CONNECTION:
+ mm_obj_dbg (self, "dial step %u/%u: checking SWWAN interface %u status...",
+ ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+ load_connection_status_by_cid (ctx->self,
+ (gint) ctx->cid,
+ (GAsyncReadyCallback) dial_connection_status_ready,
+ task);
+ return;
+
+ case DIAL_3GPP_CONTEXT_STEP_LAST:
+ mm_obj_dbg (self, "dial step %u/%u: finished", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST);
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+dial_3gpp (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Dial3gppContext *ctx;
+ GError *error = NULL;
+
+ g_assert (primary != NULL);
+
+ /* Setup task and create connection context */
+ task = g_task_new (self, cancellable, callback, user_data);
+ ctx = g_slice_new0 (Dial3gppContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) dial_3gpp_context_free);
+
+ /* Setup context */
+ ctx->self = MM_BROADBAND_BEARER_CINTERION (g_object_ref (self));
+ ctx->modem = g_object_ref (modem);
+ ctx->primary = g_object_ref (primary);
+ ctx->cid = cid;
+ ctx->step = DIAL_3GPP_CONTEXT_STEP_FIRST;
+
+ /* Get a net port to setup the connection on */
+ ctx->data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
+ if (!ctx->data) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
+ return;
+ }
+ g_object_ref (ctx->data);
+
+ /* Validate configuration */
+ ctx->usb_interface_config_index = get_usb_interface_config_index (ctx->data, &error);
+ if (ctx->usb_interface_config_index < 0) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Run! */
+ dial_3gpp_context_step (task);
+}
+
+/*****************************************************************************/
+/* Disconnect 3GPP */
+
+typedef enum {
+ DISCONNECT_3GPP_CONTEXT_STEP_FIRST,
+ DISCONNECT_3GPP_CONTEXT_STEP_STOP_SWWAN,
+ DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS,
+ DISCONNECT_3GPP_CONTEXT_STEP_LAST,
+} Disconnect3gppContextStep;
+
+typedef struct {
+ MMBroadbandBearerCinterion *self;
+ MMBaseModem *modem;
+ MMPortSerialAt *primary;
+ MMPort *data;
+ guint cid;
+ gint usb_interface_config_index;
+ Disconnect3gppContextStep step;
+} Disconnect3gppContext;
+
+static void
+disconnect_3gpp_context_free (Disconnect3gppContext *ctx)
+{
+ g_object_unref (ctx->data);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->modem);
+ g_slice_free (Disconnect3gppContext, ctx);
+}
+
+static gboolean
+disconnect_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void disconnect_3gpp_context_step (GTask *task);
+
+static void
+disconnect_connection_status_ready (MMBroadbandBearerCinterion *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerConnectionStatus status;
+ Disconnect3gppContext *ctx;
+ GError *error = NULL;
+
+ ctx = (Disconnect3gppContext *) g_task_get_task_data (task);
+
+ status = load_connection_status_finish (MM_BASE_BEARER (self), res, &error);
+ switch (status) {
+ case MM_BEARER_CONNECTION_STATUS_UNKNOWN:
+ /* Assume disconnected */
+ mm_obj_dbg (self, "couldn't get CID %u status, assume disconnected: %s", ctx->cid, error->message);
+ g_clear_error (&error);
+ break;
+ case MM_BEARER_CONNECTION_STATUS_DISCONNECTED:
+ break;
+ case MM_BEARER_CONNECTION_STATUS_CONNECTED:
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "CID %u is reported connected", ctx->cid);
+ g_object_unref (task);
+ return;
+ case MM_BEARER_CONNECTION_STATUS_DISCONNECTING:
+ case MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED:
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ disconnect_3gpp_context_step (task);
+}
+
+static void
+swwan_disconnect_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Disconnect3gppContext *ctx;
+
+ ctx = (Disconnect3gppContext *) g_task_get_task_data (task);
+
+ /* We don't bother to check error or response here since, ctx flow's
+ * next step checks it */
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+
+ /* Go on to next step */
+ ctx->step++;
+ disconnect_3gpp_context_step (task);
+}
+
+static void
+disconnect_3gpp_context_step (GTask *task)
+{
+ MMBroadbandBearerCinterion *self;
+ Disconnect3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case DISCONNECT_3GPP_CONTEXT_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case DISCONNECT_3GPP_CONTEXT_STEP_STOP_SWWAN: {
+ gchar *command;
+
+ command = g_strdup_printf ("^SWWAN=0,%u,%u",
+ ctx->cid, usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+ mm_obj_dbg (self, "disconnect step %u/%u: disconnecting PDP CID %u...",
+ ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST, ctx->cid);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback) swwan_disconnect_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ case DISCONNECT_3GPP_CONTEXT_STEP_CONNECTION_STATUS:
+ mm_obj_dbg (self, "disconnect step %u/%u: checking SWWAN interface %u status...",
+ ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST,
+ usb_interface_configs[ctx->usb_interface_config_index].swwan_index);
+ load_connection_status_by_cid (MM_BROADBAND_BEARER_CINTERION (ctx->self),
+ (gint) ctx->cid,
+ (GAsyncReadyCallback) disconnect_connection_status_ready,
+ task);
+ return;
+
+ case DISCONNECT_3GPP_CONTEXT_STEP_LAST:
+ mm_obj_dbg (self, "disconnect step %u/%u: finished",
+ ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+disconnect_3gpp (MMBroadbandBearer *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Disconnect3gppContext *ctx;
+ GError *error = NULL;
+
+ g_assert (primary != NULL);
+ g_assert (data != NULL);
+
+ /* Setup task and create connection context */
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (Disconnect3gppContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_3gpp_context_free);
+
+ /* Setup context */
+ ctx->self = MM_BROADBAND_BEARER_CINTERION (g_object_ref (self));
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+ ctx->primary = g_object_ref (primary);
+ ctx->data = g_object_ref (data);
+ ctx->cid = cid;
+ ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST;
+
+ /* Validate configuration */
+ ctx->usb_interface_config_index = get_usb_interface_config_index (data, &error);
+ if (ctx->usb_interface_config_index < 0) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Start */
+ disconnect_3gpp_context_step (task);
+}
+
+/*****************************************************************************/
+/* Setup and Init Bearers */
+
+MMBaseBearer *
+mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,
+ GError **error)
+{
+ GObject *bearer;
+ GObject *source;
+
+ source = g_async_result_get_source_object (res);
+ bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+ g_object_unref (source);
+
+ if (!bearer)
+ return NULL;
+
+ /* Only export valid bearers */
+ mm_base_bearer_export (MM_BASE_BEARER (bearer));
+
+ return MM_BASE_BEARER (bearer);
+}
+
+void
+mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (
+ MM_TYPE_BROADBAND_BEARER_CINTERION,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_BASE_BEARER_MODEM, modem,
+ MM_BASE_BEARER_CONFIG, config,
+ NULL);
+}
+
+static void
+mm_broadband_bearer_cinterion_init (MMBroadbandBearerCinterion *self)
+{
+}
+
+static void
+mm_broadband_bearer_cinterion_class_init (MMBroadbandBearerCinterionClass *klass)
+{
+ MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
+ MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+
+ base_bearer_class->load_connection_status = load_connection_status;
+ base_bearer_class->load_connection_status_finish = load_connection_status_finish;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = load_connection_status;
+ base_bearer_class->reload_connection_status_finish = load_connection_status_finish;
+#endif
+
+ broadband_bearer_class->dial_3gpp = dial_3gpp;
+ broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
+ broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
+ broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
+}
diff --git a/plugins/cinterion/mm-broadband-bearer-cinterion.h b/plugins/cinterion/mm-broadband-bearer-cinterion.h
new file mode 100644
index 00000000..d514759d
--- /dev/null
+++ b/plugins/cinterion/mm-broadband-bearer-cinterion.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) 2016 Trimble Navigation Limited
+ * Author: Matthew Stanger <Matthew_Stanger@trimble.com>
+ */
+
+#ifndef MM_BROADBAND_BEARER_CINTERION_H
+#define MM_BROADBAND_BEARER_CINTERION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-broadband-bearer.h"
+#include "mm-broadband-modem-cinterion.h"
+
+#define MM_TYPE_BROADBAND_BEARER_CINTERION (mm_broadband_bearer_cinterion_get_type ())
+#define MM_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterion))
+#define MM_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))
+#define MM_IS_BROADBAND_BEARER_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION))
+#define MM_IS_BROADBAND_BEARER_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_CINTERION))
+#define MM_BROADBAND_BEARER_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_CINTERION, MMBroadbandBearerCinterionClass))
+
+typedef struct _MMBroadbandBearerCinterion MMBroadbandBearerCinterion;
+typedef struct _MMBroadbandBearerCinterionClass MMBroadbandBearerCinterionClass;
+
+struct _MMBroadbandBearerCinterion {
+ MMBroadbandBearer parent;
+};
+
+struct _MMBroadbandBearerCinterionClass {
+ MMBroadbandBearerClass parent;
+};
+
+GType mm_broadband_bearer_cinterion_get_type (void);
+
+void mm_broadband_bearer_cinterion_new (MMBroadbandModemCinterion *modem,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseBearer *mm_broadband_bearer_cinterion_new_finish (GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_BROADBAND_BEARER_CINTERION_H */
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index 0135c8d9..575ab484 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -12,7 +12,9 @@
*
* Copyright (C) 2011 Ammonit Measurement GmbH
* Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2016 Trimble Navigation Limited
* Author: Aleksander Morgado <aleksander@lanedo.com>
+ * Contributor: Matthew Stanger <matthew_stanger@trimble.com>
*/
#include <config.h>
@@ -26,42 +28,58 @@
#include "ModemManager.h"
#include "mm-modem-helpers.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-messaging.h"
#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
#include "mm-base-modem-at.h"
#include "mm-broadband-modem-cinterion.h"
#include "mm-modem-helpers-cinterion.h"
-#include "mm-common-cinterion.h"
+#include "mm-shared-cinterion.h"
+#include "mm-broadband-bearer-cinterion.h"
+#include "mm-iface-modem-signal.h"
-static void iface_modem_init (MMIfaceModem *iface);
-static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
-static void iface_modem_location_init (MMIfaceModemLocation *iface);
-
-static MMIfaceModem *iface_modem_parent;
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
+static void shared_cinterion_init (MMSharedCinterion *iface);
+
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
+static MMIfaceModemTime *iface_modem_time_parent;
+static MMIfaceModemSignal *iface_modem_signal_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_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_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_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_SHARED_CINTERION, shared_cinterion_init))
-struct _MMBroadbandModemCinterionPrivate {
- /* Flag to know if we should try AT^SIND or not to get psinfo */
- gboolean sind_psinfo;
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED,
+} FeatureSupport;
+struct _MMBroadbandModemCinterionPrivate {
/* Command to go into sleep mode */
gchar *sleep_mode_cmd;
- /* Cached manual selection attempt */
- gchar *manual_operator_id;
-
/* Cached supported bands in Cinterion format */
- guint supported_bands;
+ guint supported_bands[MM_CINTERION_RB_BLOCK_N];
/* Cached supported modes for SMS setup */
GArray *cnmi_supported_mode;
@@ -69,85 +87,182 @@ struct _MMBroadbandModemCinterionPrivate {
GArray *cnmi_supported_bm;
GArray *cnmi_supported_ds;
GArray *cnmi_supported_bfr;
-};
-/* Setup relationship between the band bitmask in the modem and the bitmask
- * in ModemManager. */
-typedef struct {
- gchar *cinterion_band;
- guint n_mm_bands;
- MMModemBand mm_bands [4];
-} CinterionBand2G;
-
-/* Table checked in both MC75i (GPRS/EDGE) and EGS5 (GPRS) references.
- * Note that the modem's configuration is also based on a bitmask, but as we
- * will just support some of the combinations, we just use strings for them.
- */
-static const CinterionBand2G bands_2g[] = {
- { "1", 1, { MM_MODEM_BAND_EGSM, 0, 0, 0 }},
- { "2", 1, { MM_MODEM_BAND_DCS, 0, 0, 0 }},
- { "4", 1, { MM_MODEM_BAND_PCS, 0, 0, 0 }},
- { "8", 1, { MM_MODEM_BAND_G850, 0, 0, 0 }},
- { "3", 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, 0, 0 }},
- { "5", 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_PCS, 0, 0 }},
- { "10", 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_DCS, 0, 0 }},
- { "12", 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS, 0, 0 }},
- { "15", 4, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850 }}
+ /* ignore regex */
+ GRegex *sysstart_regex;
+ /* +CIEV indications as configured via AT^SIND */
+ GRegex *ciev_regex;
+ /* Ignore SIM hotswap SCKS msg, until ready */
+ GRegex *scks_regex;
+
+ /* Flags for feature support checks */
+ FeatureSupport swwan_support;
+ FeatureSupport sind_psinfo_support;
+ FeatureSupport smoni_support;
+ FeatureSupport sind_simstatus_support;
+
+ /* Flags for model-based behaviors */
+ MMCinterionModemFamily modem_family;
+ MMCinterionRadioBandFormat rb_format;
+
+ /* Initial EPS bearer context number */
+ gint initial_eps_bearer_cid;
};
/*****************************************************************************/
-/* Unsolicited events enabling */
+
+MMCinterionModemFamily
+mm_broadband_modem_cinterion_get_family (MMBroadbandModemCinterion *self)
+{
+ return self->priv->modem_family;
+}
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
static gboolean
-enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-enable_unsolicited_events (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+parent_signal_check_support_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GTask *task)
{
- /* AT=CMER=[<mode>[,<keyp>[,<disp>[,<ind>[,<bfr>]]]]]
- * but <ind> should be either not set, or equal to 0 or 2.
- * Enabled with 2.
- */
+ GError *error = NULL;
+
+ if (!iface_modem_signal_parent->check_support_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+check_smoni_support (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+ /* Fetch the result to the SMONI test. If no response given (error triggered), assume unsupported */
+ if (mm_base_modem_at_command_finish (_self, res, NULL)) {
+ mm_obj_dbg (self, "SMONI supported");
+ self->priv->smoni_support = FEATURE_SUPPORTED;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "SMONI unsupported");
+ self->priv->smoni_support = FEATURE_NOT_SUPPORTED;
+
+ /* Otherwise, check if the parent CESQ-based implementation works */
+ g_assert (iface_modem_signal_parent->check_support && iface_modem_signal_parent->check_support_finish);
+ iface_modem_signal_parent->check_support (MM_IFACE_MODEM_SIGNAL (self),
+ (GAsyncReadyCallback) parent_signal_check_support_ready,
+ task);
+}
+
+static void
+signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+CMER=3,0,0,2",
+ "^SMONI=?",
3,
- FALSE,
- callback,
- user_data);
+ TRUE,
+ (GAsyncReadyCallback) check_smoni_support,
+ task);
+}
+
+/*****************************************************************************/
+/* Load extended signal information (Signal interface) */
+
+static gboolean
+signal_load_values_finish (MMIfaceModemSignal *_self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ const gchar *response;
+
+ if (self->priv->smoni_support == FEATURE_NOT_SUPPORTED)
+ return iface_modem_signal_parent->load_values_finish (_self, res, cdma, evdo, gsm, umts, lte, nr5g, error);
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, error);
+ if (!response || !mm_cinterion_smoni_response_to_signal_info (response, gsm, umts, lte, error))
+ return FALSE;
+
+ if (cdma)
+ *cdma = NULL;
+ if (evdo)
+ *evdo = NULL;
+ if (nr5g)
+ *nr5g = NULL;
+
+ return TRUE;
+}
+
+static void
+signal_load_values (MMIfaceModemSignal *_self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+ if (self->priv->smoni_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SMONI",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+ return;
+ }
+
+ /* ^SMONI not supported, fallback to the parent */
+ iface_modem_signal_parent->load_values (_self, cancellable, callback, user_data);
}
/*****************************************************************************/
/* Enable unsolicited events (SMS indications) (Messaging interface) */
static gboolean
-messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self,
- GAsyncResult *res,
- GError **error)
+messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-cnmi_test_ready (MMBaseModem *self,
+cnmi_test_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (error)
- g_simple_async_result_take_error (simple, error);
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static gboolean
@@ -168,84 +283,79 @@ value_supported (const GArray *array,
static void
messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
- GString *cmd;
- GError *error = NULL;
- GSimpleAsyncResult *simple;
+ GString *cmd;
+ GError *error = NULL;
+ GTask *task;
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_enable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* AT+CNMI=<mode>,[<mt>[,<bm>[,<ds>[,<bfr>]]]] */
cmd = g_string_new ("+CNMI=");
/* Mode 2 or 1 */
- if (!error) {
- if (value_supported (self->priv->cnmi_supported_mode, 2))
- g_string_append_printf (cmd, "%u,", 2);
- else if (value_supported (self->priv->cnmi_supported_mode, 1))
- g_string_append_printf (cmd, "%u,", 1);
- else
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SMS settings don't accept [2,1] <mode>");
+ if (value_supported (self->priv->cnmi_supported_mode, 2))
+ g_string_append_printf (cmd, "%u,", 2);
+ else if (value_supported (self->priv->cnmi_supported_mode, 1))
+ g_string_append_printf (cmd, "%u,", 1);
+ else {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SMS settings don't accept [2,1] <mode>");
+ goto out;
}
/* mt 2 or 1 */
- if (!error) {
- if (value_supported (self->priv->cnmi_supported_mt, 2))
- g_string_append_printf (cmd, "%u,", 2);
- else if (value_supported (self->priv->cnmi_supported_mt, 1))
- g_string_append_printf (cmd, "%u,", 1);
- else
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SMS settings don't accept [2,1] <mt>");
+ if (value_supported (self->priv->cnmi_supported_mt, 2))
+ g_string_append_printf (cmd, "%u,", 2);
+ else if (value_supported (self->priv->cnmi_supported_mt, 1))
+ g_string_append_printf (cmd, "%u,", 1);
+ else {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SMS settings don't accept [2,1] <mt>");
+ goto out;
}
/* bm 2 or 0 */
- if (!error) {
- if (value_supported (self->priv->cnmi_supported_bm, 2))
- g_string_append_printf (cmd, "%u,", 2);
- else if (value_supported (self->priv->cnmi_supported_bm, 0))
- g_string_append_printf (cmd, "%u,", 0);
- else
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SMS settings don't accept [2,0] <bm>");
+ if (value_supported (self->priv->cnmi_supported_bm, 2))
+ g_string_append_printf (cmd, "%u,", 2);
+ else if (value_supported (self->priv->cnmi_supported_bm, 0))
+ g_string_append_printf (cmd, "%u,", 0);
+ else {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SMS settings don't accept [2,0] <bm>");
+ goto out;
}
/* ds 2, 1 or 0 */
- if (!error) {
- if (value_supported (self->priv->cnmi_supported_ds, 2))
- g_string_append_printf (cmd, "%u,", 2);
- if (value_supported (self->priv->cnmi_supported_ds, 1))
- g_string_append_printf (cmd, "%u,", 1);
- else if (value_supported (self->priv->cnmi_supported_ds, 0))
- g_string_append_printf (cmd, "%u,", 0);
- else
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SMS settings don't accept [2,1,0] <ds>");
+ if (value_supported (self->priv->cnmi_supported_ds, 2))
+ g_string_append_printf (cmd, "%u,", 2);
+ else if (value_supported (self->priv->cnmi_supported_ds, 1))
+ g_string_append_printf (cmd, "%u,", 1);
+ else if (value_supported (self->priv->cnmi_supported_ds, 0))
+ g_string_append_printf (cmd, "%u,", 0);
+ else {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SMS settings don't accept [2,1,0] <ds>");
+ goto out;
}
/* bfr 1 */
- if (!error) {
- if (value_supported (self->priv->cnmi_supported_bfr, 1))
- g_string_append_printf (cmd, "%u", 1);
- /* otherwise, skip setting it */
- }
+ if (value_supported (self->priv->cnmi_supported_bfr, 1))
+ g_string_append_printf (cmd, "%u", 1);
+ /* otherwise, skip setting it */
+out:
/* Early error report */
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_string_free (cmd, TRUE);
return;
}
@@ -255,7 +365,7 @@ messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self,
3,
FALSE,
(GAsyncReadyCallback)cnmi_test_ready,
- simple);
+ task);
g_string_free (cmd, TRUE);
}
@@ -263,26 +373,26 @@ messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self,
/* Check if Messaging supported (Messaging interface) */
static gboolean
-messaging_check_support_finish (MMIfaceModemMessaging *self,
- GAsyncResult *res,
- GError **error)
+messaging_check_support_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-cnmi_format_check_ready (MMBroadbandModemCinterion *self,
+cnmi_format_check_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- GError *error = NULL;
- const gchar *response;
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GError *error = NULL;
+ const gchar *response;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (_self, res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -294,37 +404,31 @@ cnmi_format_check_ready (MMBroadbandModemCinterion *self,
&self->priv->cnmi_supported_ds,
&self->priv->cnmi_supported_bfr,
&error)) {
- mm_warn ("error reading SMS setup: %s", error->message);
+ mm_obj_warn (self, "error reading SMS setup: %s", error->message);
g_error_free (error);
}
/* CNMI command is supported; assume we have full messaging capabilities */
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
messaging_check_support (MMIfaceModemMessaging *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
/* We assume that CDMA-only modems don't have messaging capabilities */
if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
- g_simple_async_result_set_error (
- result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "CDMA-only modems don't have messaging capabilities");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "CDMA-only modems don't have messaging capabilities");
+ g_object_unref (task);
return;
}
@@ -334,76 +438,70 @@ messaging_check_support (MMIfaceModemMessaging *self,
3,
TRUE,
(GAsyncReadyCallback)cnmi_format_check_ready,
- result);
+ task);
}
/*****************************************************************************/
-/* MODEM POWER DOWN */
+/* Power down */
static gboolean
-modem_power_down_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-sleep_ready (MMBaseModem *self,
+sleep_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
- GError *error = NULL;
-
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ g_autoptr(GError) error = NULL;
- /* Ignore errors */
- if (error) {
- mm_dbg ("Couldn't send power down command: '%s'", error->message);
- g_error_free (error);
- }
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+ mm_obj_dbg (self, "couldn't send power down command: %s", error->message);
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-send_sleep_mode_command (MMBroadbandModemCinterion *self,
- GSimpleAsyncResult *operation_result)
+send_sleep_mode_command (GTask *task)
{
- if (self->priv->sleep_mode_cmd &&
- self->priv->sleep_mode_cmd[0]) {
+ MMBroadbandModemCinterion *self;
+
+ self = g_task_get_source_object (task);
+
+ if (self->priv->sleep_mode_cmd && self->priv->sleep_mode_cmd[0]) {
mm_base_modem_at_command (MM_BASE_MODEM (self),
self->priv->sleep_mode_cmd,
5,
FALSE,
(GAsyncReadyCallback)sleep_ready,
- operation_result);
+ task);
return;
}
/* No default command; just finish without sending anything */
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-supported_functionality_status_query_ready (MMBroadbandModemCinterion *self,
+supported_functionality_status_query_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
- const gchar *response;
- GError *error = NULL;
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ const gchar *response;
+ g_autoptr(GError) error = NULL;
g_assert (self->priv->sleep_mode_cmd == NULL);
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (_self, res, &error);
if (!response) {
- mm_warn ("Couldn't query supported functionality status: '%s'",
- error->message);
- g_error_free (error);
+ mm_obj_warn (self, "couldn't query supported functionality status: %s", error->message);
self->priv->sleep_mode_cmd = g_strdup ("");
} else {
/* We need to get which power-off command to use to put the modem in low
@@ -417,37 +515,33 @@ supported_functionality_status_query_ready (MMBroadbandModemCinterion *self,
* not found, report warning and don't use any.
*/
if (strstr (response, "4") != NULL) {
- mm_dbg ("Device supports CFUN=4 sleep mode");
+ mm_obj_dbg (self, "device supports CFUN=4 sleep mode");
self->priv->sleep_mode_cmd = g_strdup ("+CFUN=4");
} else if (strstr (response, "7") != NULL) {
- mm_dbg ("Device supports CFUN=7 sleep mode");
+ mm_obj_dbg (self, "device supports CFUN=7 sleep mode");
self->priv->sleep_mode_cmd = g_strdup ("+CFUN=7");
} else {
- mm_warn ("Unknown functionality mode to go into sleep mode");
+ mm_obj_warn (self, "unknown functionality mode to go into sleep mode");
self->priv->sleep_mode_cmd = g_strdup ("");
}
}
- send_sleep_mode_command (self, operation_result);
+ send_sleep_mode_command (task);
}
static void
-modem_power_down (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_power_down (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- MMBroadbandModemCinterion *cinterion = MM_BROADBAND_MODEM_CINTERION (self);
- GSimpleAsyncResult *result;
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_power_down);
+ task = g_task_new (self, NULL, callback, user_data);
/* If sleep command already decided, use it. */
- if (cinterion->priv->sleep_mode_cmd)
- send_sleep_mode_command (MM_BROADBAND_MODEM_CINTERION (self),
- result);
+ if (self->priv->sleep_mode_cmd)
+ send_sleep_mode_command (task);
else
mm_base_modem_at_command (
MM_BASE_MODEM (self),
@@ -455,7 +549,7 @@ modem_power_down (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)supported_functionality_status_query_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -464,18 +558,16 @@ modem_power_down (MMIfaceModem *self,
#define MAX_POWER_OFF_WAIT_TIME_SECS 20
typedef struct {
- MMBroadbandModemCinterion *self;
MMPortSerialAt *port;
- GSimpleAsyncResult *result;
- GRegex *shutdown_regex;
- gboolean shutdown_received;
- gboolean smso_replied;
- gboolean serial_open;
- guint timeout_id;
+ GRegex *shutdown_regex;
+ gboolean shutdown_received;
+ gboolean smso_replied;
+ gboolean serial_open;
+ guint timeout_id;
} PowerOffContext;
static void
-power_off_context_complete_and_free (PowerOffContext *ctx)
+power_off_context_free (PowerOffContext *ctx)
{
if (ctx->serial_open)
mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
@@ -483,99 +575,118 @@ power_off_context_complete_and_free (PowerOffContext *ctx)
g_source_remove (ctx->timeout_id);
mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL);
g_object_unref (ctx->port);
- g_object_unref (ctx->self);
g_regex_unref (ctx->shutdown_regex);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_slice_free (PowerOffContext, ctx);
}
static gboolean
-modem_power_off_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_power_off_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-complete_power_off (PowerOffContext *ctx)
+complete_power_off (GTask *task)
{
+ PowerOffContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
if (!ctx->shutdown_received || !ctx->smso_replied)
return;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- power_off_context_complete_and_free (ctx);
+ /* remove timeout right away */
+ g_assert (ctx->timeout_id);
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-smso_ready (MMBaseModem *self,
+smso_ready (MMBaseModem *self,
GAsyncResult *res,
- PowerOffContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ PowerOffContext *ctx;
+ GError *error = NULL;
- mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error);
- if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- power_off_context_complete_and_free (ctx);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* Set as replied */
+ /* Set as replied and see if we can complete */
ctx->smso_replied = TRUE;
- complete_power_off (ctx);
+ complete_power_off (task);
}
static void
shutdown_received (MMPortSerialAt *port,
- GMatchInfo *match_info,
- PowerOffContext *ctx)
+ GMatchInfo *match_info,
+ GTask *task)
{
- /* Cleanup handler */
+ PowerOffContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* Cleanup handler right away, we don't want it called any more */
mm_port_serial_at_add_unsolicited_msg_handler (port, ctx->shutdown_regex, NULL, NULL, NULL);
- /* Set as received */
+
+ /* Set as received and see if we can complete */
ctx->shutdown_received = TRUE;
- complete_power_off (ctx);
+ complete_power_off (task);
}
static gboolean
-power_off_timeout_cb (PowerOffContext *ctx)
+power_off_timeout_cb (GTask *task)
{
+ PowerOffContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
ctx->timeout_id = 0;
/* The SMSO reply should have come earlier */
g_warn_if_fail (ctx->smso_replied == TRUE);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Power off operation timed out");
- power_off_context_complete_and_free (ctx);
+ /* Cleanup handler right away, we no longer want to receive it */
+ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->shutdown_regex, NULL, NULL, NULL);
+
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Power off operation timed out");
+ g_object_unref (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
-modem_power_off (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
PowerOffContext *ctx;
- GError *error = NULL;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (PowerOffContext);
- ctx->self = g_object_ref (self);
ctx->port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_power_off);
ctx->shutdown_regex = g_regex_new ("\\r\\n\\^SHUTDOWN\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
ctx->timeout_id = g_timeout_add_seconds (MAX_POWER_OFF_WAIT_TIME_SECS,
(GSourceFunc)power_off_timeout_cb,
- ctx);
+ task);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) power_off_context_free);
/* We'll need to wait for a ^SHUTDOWN before returning the action, which is
* when the modem tells us that it is ready to be shutdown */
@@ -583,15 +694,15 @@ modem_power_off (MMIfaceModem *self,
ctx->port,
ctx->shutdown_regex,
(MMPortSerialAtUnsolicitedMsgFn)shutdown_received,
- ctx,
+ task,
NULL);
/* In order to get the ^SHUTDOWN notification, we must keep the port open
* during the wait time */
ctx->serial_open = mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error);
if (G_UNLIKELY (error)) {
- g_simple_async_result_take_error (ctx->result, error);
- power_off_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -607,271 +718,1033 @@ modem_power_off (MMIfaceModem *self,
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)smso_ready,
- ctx);
+ task);
}
/*****************************************************************************/
-/* ACCESS TECHNOLOGIES */
+/* Access technologies polling */
static gboolean
-load_access_technologies_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemAccessTechnology *access_technologies,
- guint *mask,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+load_access_technologies_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemAccessTechnology *access_technologies,
+ guint *mask,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize val;
+
+ val = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
- *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ *access_technologies = (MMModemAccessTechnology) val;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
}
-static MMModemAccessTechnology
-get_access_technology_from_smong_gprs_status (const gchar *gprs_status,
- GError **error)
-{
- if (strlen (gprs_status) == 1) {
- switch (gprs_status[0]) {
- case '0':
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- case '1':
- case '2':
- return MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
- case '3':
- case '4':
- return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
- default:
- break;
- }
+static void
+smong_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMModemAccessTechnology access_tech;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_cinterion_parse_smong_response (response, &access_tech, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, (gssize) access_tech);
+ g_object_unref (task);
+}
+
+static void
+load_access_technologies (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Abort access technology polling if ^SIND psinfo URCs are enabled */
+ if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "No need to poll access technologies");
+ g_object_unref (task);
+ return;
}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't get network capabilities, "
- "invalid GPRS status value: '%s'",
- gprs_status);
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "^SMONG",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)smong_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Disable unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-smong_query_ready (MMBroadbandModemCinterion *self,
- GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
- const gchar *response;
- GError *error = NULL;
- GMatchInfo *match_info = NULL;
- GRegex *regex;
+ g_autoptr(GError) error = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't disable parent 3GPP unsolicited events: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_disable_unsolicited_messages (GTask *task)
+{
+ /* Chain up parent's disable */
+ iface_modem_3gpp_parent->disable_unsolicited_events (
+ MM_IFACE_MODEM_3GPP (g_task_get_source_object (task)),
+ (GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
+ task);
+}
+
+static void
+sind_psinfo_disable_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't disable ^SIND psinfo notifications: %s", error->message);
+
+ parent_disable_unsolicited_messages (task);
+}
+
+static void
+modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemCinterion *self;
+ GTask *task;
+
+ self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
+ /* Disable access technology update reporting */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SIND=\"psinfo\",0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)sind_psinfo_disable_ready,
+ task);
return;
}
- /* The AT^SMONG command returns a cell info table, where the second
- * column identifies the "GPRS status", which is exactly what we want.
- * So we'll try to read that second number in the values row.
- *
- * AT^SMONG
- * GPRS Monitor
- * BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell #
- * 0776 1 - - 214 03 2 00 01
- * OK
- */
- regex = g_regex_new (".*GPRS Monitor\\r\\n"
- "BCCH\\s*G.*\\r\\n"
- "(\\d*)\\s*(\\d*)\\s*", 0, 0, NULL);
- if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) {
- gchar *gprs_status;
- MMModemAccessTechnology act;
-
- gprs_status = g_match_info_fetch (match_info, 2);
- act = get_access_technology_from_smong_gprs_status (gprs_status, &error);
- g_free (gprs_status);
-
- if (error)
- g_simple_async_result_take_error (operation_result, error);
- else {
- /* We'll default to use SMONG then */
- self->priv->sind_psinfo = FALSE;
- g_simple_async_result_set_op_res_gpointer (operation_result,
- GUINT_TO_POINTER (act),
- NULL);
- }
+ parent_disable_unsolicited_messages (task);
+}
+
+/*****************************************************************************/
+/* Enable unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sind_psinfo_enable_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self;
+ g_autoptr(GError) error = NULL;
+ const gchar *response;
+ guint mode;
+ guint val;
+
+ self = MM_BROADBAND_MODEM_CINTERION (_self);
+ if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
+ /* something went wrong, disable indicator */
+ self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_warn (self, "couldn't enable ^SIND psinfo notifications: %s", error->message);
+ } else if (!mm_cinterion_parse_sind_response (response, NULL, &mode, &val, &error)) {
+ /* problem with parsing, disable indicator */
+ self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_warn (self, "couldn't parse ^SIND psinfo response: %s", error->message);
} else {
- /* We'll reset here the flag to try to use SIND/psinfo the next time */
- self->priv->sind_psinfo = TRUE;
-
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't get network capabilities, "
- "invalid SMONG reply: '%s'",
- response);
+ /* Report initial access technology gathered right away */
+ mm_obj_dbg (self, "reporting initial access technologies...");
+ mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
+ mm_cinterion_get_access_technology_from_sind_psinfo (val, self),
+ MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
}
- g_match_info_free (match_info);
- g_regex_unref (regex);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+static void
+set_urc_dest_port_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self;
+ g_autoptr(GError) error = NULL;
+
+ self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (_self), res, &error))
+ mm_obj_dbg (self, "couldn't guarantee unsolicited events are sent to the correct port: %s", error->message);
+
+ if (self->priv->sind_psinfo_support == FEATURE_SUPPORTED) {
+ /* Enable access technology update reporting */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SIND=\"psinfo\",1",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)sind_psinfo_enable_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
-static MMModemAccessTechnology
-get_access_technology_from_psinfo (const gchar *psinfo,
- GError **error)
+static void
+parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
- guint psinfoval;
+ g_autoptr(GError) error = NULL;
- if (mm_get_uint_from_str (psinfo, &psinfoval)) {
- switch (psinfoval) {
- case 0:
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- case 1:
- case 2:
- return MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
- case 3:
- case 4:
- return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
- case 5:
- case 6:
- return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
- case 7:
- case 8:
- return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
- case 9:
- case 10:
- return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
- default:
- break;
+ if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't enable parent 3GPP unsolicited events: %s", error->message);
+
+ /* Make sure unsolicited events are sent to an AT port (PLS9 can default to DATA port) */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SCFG=\"URC/DstIfc\",\"app\"",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback)set_urc_dest_port_ready,
+ task);
+}
+
+static void
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's enable */
+ iface_modem_3gpp_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup/Cleanup unsolicited events (3GPP interface) */
+
+static void
+sind_ciev_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemCinterion *self)
+{
+ guint val = 0;
+ gchar *indicator;
+
+ indicator = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (!mm_get_uint_from_match_info (match_info, 2, &val))
+ mm_obj_dbg (self, "couldn't parse indicator '%s' value", indicator);
+ else {
+ mm_obj_dbg (self, "received indicator '%s' update: %u", indicator, val);
+ if (g_strcmp0 (indicator, "psinfo") == 0) {
+ mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
+ mm_cinterion_get_access_technology_from_sind_psinfo (val, self),
+ MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
}
}
+ g_free (indicator);
+}
+
+static void
+set_unsolicited_events_handlers (MMBroadbandModemCinterion *self,
+ gboolean enable)
+{
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Enable unsolicited events in given port */
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->ciev_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)sind_ciev_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't get network capabilities, "
- "invalid psinfo value: '%s'",
- psinfo);
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+static gboolean
+modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-sind_query_ready (MMBroadbandModemCinterion *self,
- GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
- const gchar *response;
GError *error = NULL;
- GMatchInfo *match_info = NULL;
- GRegex *regex;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
- return;
+ if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else {
+ /* Our own setup now */
+ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), TRUE);
+ g_task_return_boolean (task, TRUE);
}
+ g_object_unref (task);
+}
- /* The AT^SIND? command replies a list of several different indicators.
- * We will only look for 'psinfo' which is the one which may tell us
- * the available network access technology. Note that only 3G-enabled
- * devices seem to have this indicator.
- *
- * AT+SIND?
- * ^SIND: battchg,1,1
- * ^SIND: signal,1,99
- * ...
- */
- regex = g_regex_new ("\\r\\n\\^SIND:\\s*psinfo,\\s*(\\d*),\\s*(\\d*)", 0, 0, NULL);
- if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) {
- MMModemAccessTechnology act;
- gchar *ind_value;
-
- ind_value = g_match_info_fetch (match_info, 2);
- act = get_access_technology_from_psinfo (ind_value, &error);
- g_free (ind_value);
- g_simple_async_result_set_op_res_gpointer (operation_result, GUINT_TO_POINTER (act), NULL);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+static void
+modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's setup */
+ iface_modem_3gpp_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
+ task);
+}
+
+static void
+parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Our own cleanup first */
+ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), FALSE);
+
+ /* And now chain up parent's cleanup */
+ iface_modem_3gpp_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common operation to load expected CID for the initial EPS bearer */
+
+static gboolean
+load_initial_eps_bearer_cid_finish (MMBroadbandModemCinterion *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+scfg_prov_cfg_query_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ g_autoptr(GError) error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response)
+ mm_obj_dbg (self, "couldn't query MNO profiles: %s", error->message);
+
+ else if (!mm_cinterion_provcfg_response_to_cid (response,
+ MM_BROADBAND_MODEM_CINTERION (self)->priv->modem_family,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ self,
+ &self->priv->initial_eps_bearer_cid,
+ &error))
+ mm_obj_dbg (self, "failed processing list of MNO profiles: %s", error->message);
+
+ if (self->priv->initial_eps_bearer_cid < 0) {
+ mm_obj_dbg (self, "using default EPS bearer context id: 1");
+ self->priv->initial_eps_bearer_cid = 1;
+ } else
+ mm_obj_dbg (self, "loaded EPS bearer context id from list of MNO profiles: %d", self->priv->initial_eps_bearer_cid);
+
+ /* This operation really never fails */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+load_initial_eps_bearer_cid (MMBroadbandModemCinterion *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_assert (self->priv->initial_eps_bearer_cid < 0);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SCFG=\"MEopMode/Prov/Cfg\"",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)scfg_prov_cfg_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set initial EPS bearer settings */
+
+typedef enum {
+ SET_INITIAL_EPS_STEP_FIRST = 0,
+ SET_INITIAL_EPS_STEP_CHECK_MODE,
+ SET_INITIAL_EPS_STEP_RF_OFF,
+ SET_INITIAL_EPS_STEP_APN,
+ SET_INITIAL_EPS_STEP_AUTH,
+ SET_INITIAL_EPS_STEP_RF_ON,
+ SET_INITIAL_EPS_STEP_LAST,
+} SetInitialEpsStep;
+
+typedef struct {
+ MMBearerProperties *properties;
+ SetInitialEpsStep step;
+ guint initial_cfun_mode;
+ GError *saved_error;
+} SetInitialEpsContext;
+
+static void
+set_initial_eps_context_free (SetInitialEpsContext *ctx)
+{
+ g_assert (!ctx->saved_error);
+ g_object_unref (ctx->properties);
+ g_slice_free (SetInitialEpsContext, ctx);
+}
+
+static gboolean
+modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void set_initial_eps_step (GTask *task);
+
+static void
+set_initial_eps_rf_on_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ SetInitialEpsContext *ctx;
+
+ ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't set RF back on: %s", error->message);
+ if (!ctx->saved_error)
+ ctx->saved_error = g_steal_pointer (&error);
+ }
+
+ /* Go to next step */
+ ctx->step++;
+ set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_auth_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ SetInitialEpsContext *ctx;
+
+ ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (_self, res, &ctx->saved_error)) {
+ mm_obj_warn (self, "couldn't configure context %d auth settings: %s",
+ self->priv->initial_eps_bearer_cid, ctx->saved_error->message);
+ /* Fallback to recover RF before returning the error */
+ ctx->step = SET_INITIAL_EPS_STEP_RF_ON;
+ } else {
+ /* Go to next step */
+ ctx->step++;
+ }
+ set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_cgdcont_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ SetInitialEpsContext *ctx;
+
+ ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (_self, res, &ctx->saved_error)) {
+ mm_obj_warn (self, "couldn't configure context %d settings: %s",
+ self->priv->initial_eps_bearer_cid, ctx->saved_error->message);
+ /* Fallback to recover RF before returning the error */
+ ctx->step = SET_INITIAL_EPS_STEP_RF_ON;
} else {
- /* If there was no 'psinfo' indicator, we'll try AT^SMONG and read the cell
- * info table. */
+ /* Go to next step */
+ ctx->step++;
+ }
+ set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_rf_off_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ SetInitialEpsContext *ctx;
+
+ ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't set RF off: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go to next step */
+ ctx->step++;
+ set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_cfun_mode_load_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ SetInitialEpsContext *ctx;
+ guint mode;
+
+ ctx = (SetInitialEpsContext *) g_task_get_task_data (task);
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_3gpp_parse_cfun_query_response (response, &mode, &error)) {
+ mm_obj_warn (self, "couldn't load initial functionality mode: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "current functionality mode: %u", mode);
+ if (mode != 1 && mode != 4) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "cannot setup the default LTE bearer settings: "
+ "the SIM must be powered");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->initial_cfun_mode = mode;
+ ctx->step++;
+ set_initial_eps_step (task);
+}
+
+static void
+set_initial_eps_step (GTask *task)
+{
+ MMBroadbandModemCinterion *self;
+ SetInitialEpsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_INITIAL_EPS_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SET_INITIAL_EPS_STEP_CHECK_MODE:
mm_base_modem_at_command (
MM_BASE_MODEM (self),
- "^SMONG",
- 3,
+ "+CFUN?",
+ 5,
FALSE,
- (GAsyncReadyCallback)smong_query_ready,
- operation_result);
+ (GAsyncReadyCallback)set_initial_eps_cfun_mode_load_ready,
+ task);
+ return;
+
+ case SET_INITIAL_EPS_STEP_RF_OFF:
+ if (ctx->initial_cfun_mode != 4) {
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CFUN=4",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback)set_initial_eps_rf_off_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_INITIAL_EPS_STEP_APN: {
+ const gchar *apn;
+ g_autofree gchar *quoted_apn = NULL;
+ g_autofree gchar *apn_cmd = NULL;
+ const gchar *ip_family_str;
+ MMBearerIpFamily ip_family;
+
+ ip_family = mm_bearer_properties_get_ip_type (ctx->properties);
+ if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY)
+ ip_family = MM_BEARER_IP_FAMILY_IPV4;
+
+ ip_family_str = mm_3gpp_get_pdp_type_from_ip_family (ip_family);
+ apn = mm_bearer_properties_get_apn (ctx->properties);
+ mm_obj_dbg (self, "context %d with APN '%s' and PDP type '%s'",
+ self->priv->initial_eps_bearer_cid, apn, ip_family_str);
+ quoted_apn = mm_port_serial_at_quote_string (apn);
+ apn_cmd = g_strdup_printf ("+CGDCONT=%u,\"%s\",%s",
+ self->priv->initial_eps_bearer_cid, ip_family_str, quoted_apn);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ apn_cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_initial_eps_cgdcont_ready,
+ task);
+ return;
+ }
+
+ case SET_INITIAL_EPS_STEP_AUTH: {
+ g_autofree gchar *auth_cmd = NULL;
+
+ auth_cmd = mm_cinterion_build_auth_string (self,
+ MM_BROADBAND_MODEM_CINTERION (self)->priv->modem_family,
+ ctx->properties,
+ self->priv->initial_eps_bearer_cid);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ auth_cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_initial_eps_auth_ready,
+ task);
+ return;
}
- g_match_info_free (match_info);
- g_regex_unref (regex);
+ case SET_INITIAL_EPS_STEP_RF_ON:
+ if (ctx->initial_cfun_mode == 1) {
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CFUN=1",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback)set_initial_eps_rf_on_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_INITIAL_EPS_STEP_LAST:
+ if (ctx->saved_error)
+ g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
}
static void
-load_access_technologies (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self);
- GSimpleAsyncResult *result;
+ GTask *task;
+ SetInitialEpsContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* The initial EPS bearer settings should have already been loaded */
+ g_assert (MM_BROADBAND_MODEM_CINTERION (self)->priv->initial_eps_bearer_cid >= 0);
+
+ /* Setup context */
+ ctx = g_slice_new0 (SetInitialEpsContext);
+ ctx->properties = g_object_ref (properties);
+ ctx->step = SET_INITIAL_EPS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_context_free);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_access_technologies);
+ set_initial_eps_step (task);
+}
+
+/*****************************************************************************/
+/* Common initial EPS bearer info loading for both:
+ * - runtime status
+ * - configuration settings
+ */
+
+typedef enum {
+ COMMON_LOAD_INITIAL_EPS_STEP_FIRST = 0,
+ COMMON_LOAD_INITIAL_EPS_STEP_PROFILE,
+ COMMON_LOAD_INITIAL_EPS_STEP_APN,
+ COMMON_LOAD_INITIAL_EPS_STEP_AUTH,
+ COMMON_LOAD_INITIAL_EPS_STEP_LAST,
+} CommonLoadInitialEpsStep;
+
+typedef struct {
+ MMBearerProperties *properties;
+ CommonLoadInitialEpsStep step;
+ gboolean runtime;
+} CommonLoadInitialEpsContext;
+
+static void
+common_load_initial_eps_context_free (CommonLoadInitialEpsContext *ctx)
+{
+ g_clear_object (&ctx->properties);
+ g_slice_free (CommonLoadInitialEpsContext, ctx);
+}
+
+static MMBearerProperties *
+common_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void common_load_initial_eps_step (GTask *task);
+
+static void
+common_load_initial_eps_auth_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ const gchar *response;
+ CommonLoadInitialEpsContext *ctx;
+ g_autoptr(GError) error = NULL;
+ MMBearerAllowedAuth auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ g_autofree gchar *username = NULL;
+
+ ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response)
+ mm_obj_dbg (self, "couldn't load context %d auth settings: %s",
+ self->priv->initial_eps_bearer_cid, error->message);
+ else if (!mm_cinterion_parse_sgauth_response (response, self->priv->initial_eps_bearer_cid, &auth, &username, &error))
+ mm_obj_dbg (self, "couldn't parse context %d auth settings: %s", self->priv->initial_eps_bearer_cid, error->message);
+ else {
+ mm_bearer_properties_set_allowed_auth (ctx->properties, auth);
+ mm_bearer_properties_set_user (ctx->properties, username);
+ }
- if (broadband->priv->sind_psinfo) {
+ /* Go to next step */
+ ctx->step++;
+ common_load_initial_eps_step (task);
+}
+
+static void
+common_load_initial_eps_load_cid_ready (MMBroadbandModemCinterion *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ CommonLoadInitialEpsContext *ctx;
+
+ ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
+
+ load_initial_eps_bearer_cid_finish (self, res, NULL);
+ g_assert (self->priv->initial_eps_bearer_cid >= 0);
+
+ /* Go to next step */
+ ctx->step++;
+ common_load_initial_eps_step (task);
+}
+
+static void
+common_load_initial_eps_cgcontrdp_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ const gchar *response;
+ CommonLoadInitialEpsContext *ctx;
+ g_autofree gchar *apn = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
+
+ /* errors aren't fatal */
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response)
+ mm_obj_dbg (self, "couldn't load context %d settings: %s",
+ self->priv->initial_eps_bearer_cid, error->message);
+ else if (!mm_3gpp_parse_cgcontrdp_response (response, NULL, NULL, &apn, NULL, NULL, NULL, NULL, NULL, &error))
+ mm_obj_dbg (self, "couldn't parse CGDCONTRDP response: %s", error->message);
+ else
+ mm_bearer_properties_set_apn (ctx->properties, apn);
+
+ /* Go to next step */
+ ctx->step++;
+ common_load_initial_eps_step (task);
+}
+
+static void
+common_load_initial_eps_cgdcont_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ const gchar *response;
+ CommonLoadInitialEpsContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = (CommonLoadInitialEpsContext *) g_task_get_task_data (task);
+
+ /* errors aren't fatal */
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response)
+ mm_obj_dbg (self, "couldn't load context %d status: %s",
+ self->priv->initial_eps_bearer_cid, error->message);
+ else {
+ GList *context_list;
+
+ context_list = mm_3gpp_parse_cgdcont_read_response (response, &error);
+ if (!context_list)
+ mm_obj_dbg (self, "couldn't parse CGDCONT response: %s", error->message);
+ else {
+ GList *l;
+
+ for (l = context_list; l; l = g_list_next (l)) {
+ MM3gppPdpContext *pdp = l->data;
+
+ if (pdp->cid == (guint) self->priv->initial_eps_bearer_cid) {
+ mm_bearer_properties_set_ip_type (ctx->properties, pdp->pdp_type);
+ mm_bearer_properties_set_apn (ctx->properties, pdp->apn ? pdp->apn : "");
+ break;
+ }
+ }
+ if (!l)
+ mm_obj_dbg (self, "no status reported for context %d", self->priv->initial_eps_bearer_cid);
+ mm_3gpp_pdp_context_list_free (context_list);
+ }
+ }
+
+ /* Go to next step */
+ ctx->step++;
+ common_load_initial_eps_step (task);
+}
+
+static void
+common_load_initial_eps_step (GTask *task)
+{
+ MMBroadbandModemCinterion *self;
+ CommonLoadInitialEpsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case COMMON_LOAD_INITIAL_EPS_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case COMMON_LOAD_INITIAL_EPS_STEP_PROFILE:
+ /* Initial EPS bearer CID initialization run once only */
+ if (G_UNLIKELY (self->priv->initial_eps_bearer_cid < 0)) {
+ load_initial_eps_bearer_cid (
+ self,
+ (GAsyncReadyCallback)common_load_initial_eps_load_cid_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case COMMON_LOAD_INITIAL_EPS_STEP_APN:
+ if (ctx->runtime) {
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CGDCONT?",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)common_load_initial_eps_cgdcont_ready,
+ task);
+ } else {
+ g_autofree gchar *cmd = NULL;
+
+ cmd = g_strdup_printf ("+CGCONTRDP=%u", self->priv->initial_eps_bearer_cid);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CGCONTRDP",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)common_load_initial_eps_cgcontrdp_ready,
+ task);
+ }
+ return;
+
+ case COMMON_LOAD_INITIAL_EPS_STEP_AUTH:
mm_base_modem_at_command (
MM_BASE_MODEM (self),
- "^SIND?",
- 3,
+ "^SGAUTH?",
+ 20,
FALSE,
- (GAsyncReadyCallback)sind_query_ready,
- result);
+ (GAsyncReadyCallback)common_load_initial_eps_auth_ready,
+ task);
return;
+
+ case COMMON_LOAD_INITIAL_EPS_STEP_LAST:
+ g_task_return_pointer (task, g_steal_pointer (&ctx->properties), g_object_unref);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
}
+}
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- "^SMONG",
- 3,
- FALSE,
- (GAsyncReadyCallback)smong_query_ready,
- result);
+static void
+common_load_initial_eps_bearer (MMIfaceModem3gpp *self,
+ gboolean runtime,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CommonLoadInitialEpsContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Setup context */
+ ctx = g_slice_new0 (CommonLoadInitialEpsContext);
+ ctx->runtime = runtime;
+ ctx->properties = mm_bearer_properties_new ();
+ ctx->step = COMMON_LOAD_INITIAL_EPS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) common_load_initial_eps_context_free);
+
+ common_load_initial_eps_step (task);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer runtime status loading */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_load_initial_eps_bearer_finish (self, res, error);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_load_initial_eps_bearer (self, TRUE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer settings loading -> set configuration */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_load_initial_eps_bearer_finish (self, res, error);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_load_initial_eps_bearer (self, FALSE, callback, user_data);
}
/*****************************************************************************/
/* Load supported modes (Modem interface) */
static GArray *
-load_supported_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -881,9 +1754,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -898,19 +1770,30 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
mode.allowed = MM_MODEM_MODE_3G;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
- /* 2G and 3G */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
+
+ if (mm_iface_modem_is_4g (self)) {
+ /* 4G only */
+ mode.allowed = MM_MODEM_MODE_4G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G, 3G and 4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ } else {
+ /* 2G and 3G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -922,186 +1805,100 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Set current modes (Modem interface) */
static gboolean
-set_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-allowed_access_technology_update_ready (MMBroadbandModemCinterion *self,
- GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+set_current_modes_reregister_in_network_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (error)
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- else {
- /* Request immediate access tech update */
- mm_iface_modem_refresh_access_technologies (MM_IFACE_MODEM (self));
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- }
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ if (!mm_iface_modem_3gpp_reregister_in_network_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-set_current_modes (MMIfaceModem *_self,
- MMModemMode allowed,
- MMModemMode preferred,
- GAsyncReadyCallback callback,
- gpointer user_data)
+allowed_access_technology_update_ready (MMBroadbandModemCinterion *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GSimpleAsyncResult *result;
- MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
-
- g_assert (preferred == MM_MODEM_MODE_NONE);
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
-
- /* For dual 2G/3G devices... */
- if (mm_iface_modem_is_2g (_self) &&
- mm_iface_modem_is_3g (_self)) {
- gchar *command;
-
- /* We will try to simulate the possible allowed modes here. The
- * Cinterion devices do not seem to allow setting preferred access
- * technology in 3G devices, but they allow restricting to a given
- * one:
- * - 2G-only is forced by forcing GERAN RAT (AcT=0)
- * - 3G-only is forced by forcing UTRAN RAT (AcT=2)
- * - for the remaining ones, we default to automatic selection of RAT,
- * which is based on the quality of the connection.
- */
-
- if (allowed == MM_MODEM_MODE_3G)
- command = g_strdup ("+COPS=,,,2");
- else if (allowed == MM_MODEM_MODE_2G)
- command = g_strdup ("+COPS=,,,0");
- else if (allowed == (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G)) {
- /* no AcT given, defaults to Auto. For this case, we cannot provide
- * AT+COPS=,,, (i.e. just without a last value). Instead, we need to
- * re-run the last manual/automatic selection command which succeeded,
- * (or auto by default if none was launched) */
- if (self->priv->manual_operator_id)
- command = g_strdup_printf ("+COPS=1,2,\"%s\"", self->priv->manual_operator_id);
- else
- command = g_strdup ("+COPS=0");
- } else
- g_assert_not_reached ();
-
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- command,
- 20,
- FALSE,
- (GAsyncReadyCallback)allowed_access_technology_update_ready,
- result);
- g_free (command);
- return;
- }
+ GError *error = NULL;
- /* For 2G-only and 3G-only devices, we already stated that we don't
- * support mode switching. */
- g_assert_not_reached ();
+ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
-/*****************************************************************************/
-/* Register in network (3GPP interface) */
-
-typedef struct {
- MMBroadbandModemCinterion *self;
- GSimpleAsyncResult *result;
- gchar *operator_id;
-} RegisterInNetworkContext;
-
static void
-register_in_network_context_complete_and_free (RegisterInNetworkContext *ctx)
+set_current_modes (MMIfaceModem *_self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx->operator_id);
- g_slice_free (RegisterInNetworkContext, ctx);
-}
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ gchar *command;
+ GTask *task;
-static gboolean
-register_in_network_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
+ g_assert (preferred == MM_MODEM_MODE_NONE);
-static void
-cops_write_ready (MMBaseModem *self,
- GAsyncResult *res,
- RegisterInNetworkContext *ctx)
-{
- GError *error = NULL;
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* We will try to simulate the possible allowed modes here. The
+ * Cinterion devices do not seem to allow setting preferred access
+ * technology in devices, but they allow restricting to a given
+ * one:
+ * - 2G-only is forced by forcing GERAN RAT (AcT=0)
+ * - 3G-only is forced by forcing UTRAN RAT (AcT=2)
+ * - 4G-only is forced by forcing E-UTRAN RAT (AcT=7)
+ * - for the remaining ones, we default to automatic selection of RAT,
+ * which is based on the quality of the connection.
+ */
- if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error))
- g_simple_async_result_take_error (ctx->result, error);
+ if (mm_iface_modem_is_4g (_self) && allowed == MM_MODEM_MODE_4G)
+ command = g_strdup ("+COPS=,,,7");
+ else if (mm_iface_modem_is_3g (_self) && allowed == MM_MODEM_MODE_3G)
+ command = g_strdup ("+COPS=,,,2");
+ else if (mm_iface_modem_is_2g (_self) && allowed == MM_MODEM_MODE_2G)
+ command = g_strdup ("+COPS=,,,0");
else {
- /* Update cached */
- g_free (ctx->self->priv->manual_operator_id);
- ctx->self->priv->manual_operator_id = g_strdup (ctx->operator_id);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ /* For any other combination (e.g. ANY or no AcT given, defaults to Auto. For this case, we cannot provide
+ * AT+COPS=,,, (i.e. just without a last value). Instead, we need to
+ * re-run the last manual/automatic selection command which succeeded,
+ * (or auto by default if none was launched) */
+ mm_iface_modem_3gpp_reregister_in_network (MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback) set_current_modes_reregister_in_network_ready,
+ task);
+ return;
}
- register_in_network_context_complete_and_free (ctx);
-}
-
-static void
-register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- RegisterInNetworkContext *ctx;
- gchar *command;
-
- ctx = g_slice_new (RegisterInNetworkContext);
- ctx->self = g_object_ref (self);
- ctx->operator_id = g_strdup (operator_id);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- register_in_network);
-
- /* If the user sent a specific network to use, lock it in. */
- if (operator_id)
- command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id);
- else
- command = g_strdup ("+COPS=0");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)allowed_access_technology_update_ready,
+ task);
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
- command,
- 120,
- FALSE,
- FALSE, /* raw */
- cancellable,
- (GAsyncReadyCallback)cops_write_ready,
- ctx);
g_free (command);
}
@@ -1109,227 +1906,357 @@ register_in_network (MMIfaceModem3gpp *self,
/* Supported bands (Modem interface) */
static GArray *
-load_supported_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-scfg_test_ready (MMBaseModem *_self,
+scfg_test_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
- const gchar *response;
- GError *error = NULL;
- GArray *bands;
+ const gchar *response;
+ GError *error = NULL;
+ GArray *bands;
response = mm_base_modem_at_command_finish (_self, res, &error);
- if (!response)
- g_simple_async_result_take_error (simple, error);
- else if (!mm_cinterion_parse_scfg_test (response,
- mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
- &bands,
- &error))
- g_simple_async_result_take_error (simple, error);
+ if (!response ||
+ !mm_cinterion_parse_scfg_test (response,
+ self->priv->modem_family,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ &bands,
+ &self->priv->rb_format,
+ &error))
+ g_task_return_error (task, error);
else {
- mm_cinterion_build_band (bands, 0, FALSE, &self->priv->supported_bands, NULL);
- g_assert (self->priv->supported_bands != 0);
- g_simple_async_result_set_op_res_gpointer (simple, bands, (GDestroyNotify)g_array_unref);
+ if (!mm_cinterion_build_band (bands,
+ NULL,
+ FALSE,
+ self->priv->rb_format,
+ self->priv->modem_family,
+ self->priv->supported_bands,
+ &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-load_supported_bands (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_supported_bands (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *simple;
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GTask *task;
+ MMPort *primary;
+ MMKernelDevice *port;
+ const gchar *family = NULL;
+
+ /* Lookup for the tag specifying which modem family the current device belongs */
+ primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
+ port = mm_port_peek_kernel_device (primary);
+ family = mm_kernel_device_get_global_property (port, "ID_MM_CINTERION_MODEM_FAMILY");
+
+ /* if the property is not set, default family */
+ self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT;
+
+ /* set used family also in the string for mm_obj_dbg */
+ if (!family)
+ family = "default";
+
+ if (g_ascii_strcasecmp (family, "imt") == 0)
+ self->priv->modem_family = MM_CINTERION_MODEM_FAMILY_IMT;
+ else if (g_ascii_strcasecmp (family, "default") != 0) {
+ mm_obj_dbg (self, "cinterion modem family '%s' unknown", family);
+ family = "default";
+ }
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_bands);
+ mm_obj_dbg (self, "Using cinterion %s modem family", family);
- mm_base_modem_at_command (MM_BASE_MODEM (self),
+ task = g_task_new (_self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (_self),
"AT^SCFG=?",
3,
FALSE,
(GAsyncReadyCallback)scfg_test_ready,
- simple);
+ task);
}
/*****************************************************************************/
/* Load current bands (Modem interface) */
static GArray *
-load_current_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-get_band_ready (MMBroadbandModemCinterion *self,
+get_band_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
const gchar *response;
- GError *error = NULL;
- GArray *bands = NULL;
+ GError *error = NULL;
+ GArray *bands = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!response)
- g_simple_async_result_take_error (simple, error);
- else if (!mm_cinterion_parse_scfg_response (response,
- mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
- &bands,
- &error))
- g_simple_async_result_take_error (simple, error);
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response ||
+ !mm_cinterion_parse_scfg_response (response,
+ self->priv->modem_family,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ &bands,
+ self->priv->rb_format,
+ &error))
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple, bands, (GDestroyNotify)g_array_unref);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
-load_current_bands (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_bands);
+ task = g_task_new (self, NULL, callback, user_data);
+ /* The timeout in this command is extremely large, because there are some
+ * modules like the EGS5 that build the response based on the current network
+ * registration, and that implies the module needs to be registered. If for
+ * any reason there is no serving network where to register, the response
+ * comes after a very long time, up to 100s. */
mm_base_modem_at_command (MM_BASE_MODEM (self),
- "AT^SCFG=\"Radio/Band\"",
- 3,
+ "AT^SCFG?",
+ 120,
FALSE,
(GAsyncReadyCallback)get_band_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Set current bands (Modem interface) */
+typedef struct {
+ MMBaseModemAtCommandAlloc *cmds;
+} SetCurrentBandsContext;
+
+static void
+set_current_bands_context_free (SetCurrentBandsContext *ctx)
+{
+ if (ctx->cmds) {
+ guint i;
+
+ for (i = 0; ctx->cmds[i].command; i++)
+ mm_base_modem_at_command_alloc_clear (&ctx->cmds[i]);
+ g_free (ctx->cmds);
+ }
+ g_slice_free (SetCurrentBandsContext, ctx);
+}
+
static gboolean
-set_current_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-scfg_set_ready (MMBaseModem *self,
+scfg_set_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
- /* Let the error be critical */
- g_simple_async_result_take_error (operation_result, error);
- else {
- /* Request immediate access tech update */
- mm_iface_modem_refresh_access_technologies (MM_IFACE_MODEM (self));
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- }
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-set_bands_3g (MMIfaceModem *_self,
- GArray *bands_array,
- GSimpleAsyncResult *simple)
+scfg_set_ready_sequence (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
GError *error = NULL;
- guint band = 0;
- gchar *cmd;
+
+ mm_base_modem_at_sequence_finish (self, res, NULL, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+set_bands_3g (GTask *task,
+ GArray *bands_array)
+{
+ MMBroadbandModemCinterion *self;
+ GError *error = NULL;
+ guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
+
+ self = g_task_get_source_object (task);
if (!mm_cinterion_build_band (bands_array,
self->priv->supported_bands,
FALSE, /* 2G and 3G */
- &band,
+ self->priv->rb_format,
+ self->priv->modem_family,
+ band,
&error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* Following the setup:
- * AT^SCFG="Radion/Band",<rba>
- * We will set the preferred band equal to the allowed band, so that we force
- * the modem to connect at that specific frequency only. Note that we will be
- * passing a number here!
- *
- * The optional <rbe> field is set to 1, so that changes take effect
- * immediately.
- */
- cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u,1", band);
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- cmd,
- 15,
- FALSE,
- (GAsyncReadyCallback)scfg_set_ready,
- simple);
- g_free (cmd);
+ if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
+ g_autofree gchar *cmd = NULL;
+
+ /* Following the setup:
+ * AT^SCFG="Radion/Band",<rba>
+ * We will set the preferred band equal to the allowed band, so that we force
+ * the modem to connect at that specific frequency only. Note that we will be
+ * passing a number here!
+ *
+ * The optional <rbe> field is set to 1, so that changes take effect
+ * immediately.
+ */
+ cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u,1", band[MM_CINTERION_RB_BLOCK_LEGACY]);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 15,
+ FALSE,
+ (GAsyncReadyCallback)scfg_set_ready,
+ task);
+ return;
+ }
+
+ if (self->priv->rb_format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) {
+ SetCurrentBandsContext *ctx;
+
+ ctx = g_slice_new0 (SetCurrentBandsContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free);
+
+ if (self->priv->modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+ g_autofree gchar *bandstr2G = NULL;
+ g_autofree gchar *bandstr3G = NULL;
+ g_autofree gchar *bandstr4G = NULL;
+ g_autofree gchar *bandstr2G_enc = NULL;
+ g_autofree gchar *bandstr3G_enc = NULL;
+ g_autofree gchar *bandstr4G_enc = NULL;
+
+ bandstr2G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_GSM]);
+ bandstr3G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_UMTS]);
+ bandstr4G = g_strdup_printf ("0x%08X", band[MM_CINTERION_RB_BLOCK_LTE_LOW]);
+
+ bandstr2G_enc = mm_modem_charset_str_from_utf8 (bandstr2G,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr2G_enc) {
+ g_prefix_error (&error, "Couldn't convert 2G band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ bandstr3G_enc = mm_modem_charset_str_from_utf8 (bandstr3G,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr3G_enc) {
+ g_prefix_error (&error, "Couldn't convert 3G band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ bandstr4G_enc = mm_modem_charset_str_from_utf8 (bandstr4G,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr4G_enc) {
+ g_prefix_error (&error, "Couldn't convert 4G band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
+ ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%s\"", bandstr2G_enc);
+ ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%s\"", bandstr3G_enc);
+ ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%s\"", bandstr4G_enc);
+ ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 60;
+ } else {
+ ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, 3 + 1);
+ ctx->cmds[0].command = g_strdup_printf ("^SCFG=\"Radio/Band/2G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_GSM]);
+ ctx->cmds[1].command = g_strdup_printf ("^SCFG=\"Radio/Band/3G\",\"%08x\",,1", band[MM_CINTERION_RB_BLOCK_UMTS]);
+ ctx->cmds[2].command = g_strdup_printf ("^SCFG=\"Radio/Band/4G\",\"%08x\",\"%08x\",1", band[MM_CINTERION_RB_BLOCK_LTE_LOW], band[MM_CINTERION_RB_BLOCK_LTE_HIGH]);
+ ctx->cmds[0].timeout = ctx->cmds[1].timeout = ctx->cmds[2].timeout = 15;
+ }
+
+ mm_base_modem_at_sequence (MM_BASE_MODEM (self),
+ (const MMBaseModemAtCommand *)ctx->cmds,
+ NULL,
+ NULL,
+ (GAsyncReadyCallback)scfg_set_ready_sequence,
+ task);
+ return;
+ }
+
+ g_assert_not_reached ();
}
static void
-set_bands_2g (MMIfaceModem *_self,
- GArray *bands_array,
- GSimpleAsyncResult *simple)
+set_bands_2g (GTask *task,
+ GArray *bands_array)
{
- MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
- GError *error = NULL;
- guint band = 0;
- gchar *cmd;
- gchar *bandstr;
+ MMBroadbandModemCinterion *self;
+ GError *error = NULL;
+ guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
+ g_autofree gchar *cmd = NULL;
+ g_autofree gchar *bandstr = NULL;
+ g_autofree gchar *bandstr_enc = NULL;
+
+ self = g_task_get_source_object (task);
if (!mm_cinterion_build_band (bands_array,
self->priv->supported_bands,
TRUE, /* 2G only */
- &band,
+ MM_CINTERION_RADIO_BAND_FORMAT_SINGLE,
+ 0,
+ band,
&error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Build string with the value, in the proper charset */
- bandstr = g_strdup_printf ("%u", band);
- bandstr = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (self), bandstr);
- if (!bandstr) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Couldn't convert band set to current charset");
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ bandstr = g_strdup_printf ("%u", band[MM_CINTERION_RB_BLOCK_LEGACY]);
+ bandstr_enc = mm_modem_charset_str_from_utf8 (bandstr,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (self)),
+ FALSE,
+ &error);
+ if (!bandstr_enc) {
+ g_prefix_error (&error, "Couldn't convert band string to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1339,26 +2266,22 @@ set_bands_2g (MMIfaceModem *_self,
* the modem to connect at that specific frequency only. Note that we will be
* passing double-quote enclosed strings here!
*/
- cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", bandstr, bandstr);
-
+ cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", bandstr_enc, bandstr_enc);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
15,
FALSE,
(GAsyncReadyCallback)scfg_set_ready,
- simple);
-
- g_free (cmd);
- g_free (bandstr);
+ task);
}
static void
-set_current_bands (MMIfaceModem *self,
- GArray *bands_array,
- GAsyncReadyCallback callback,
- gpointer user_data)
+set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
/* The bands that we get here are previously validated by the interface, and
* that means that ALL the bands given here were also given in the list of
@@ -1366,79 +2289,69 @@ set_current_bands (MMIfaceModem *self,
* will end up being valid, as not all combinations are possible. E.g,
* Cinterion modems supporting only 2G have specific combinations allowed.
*/
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_bands);
-
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_3g (self))
- set_bands_3g (self, bands_array, result);
+ set_bands_3g (task, bands_array);
else
- set_bands_2g (self, bands_array, result);
+ set_bands_2g (task, bands_array);
}
/*****************************************************************************/
-/* FLOW CONTROL */
+/* Flow control */
static gboolean
-setup_flow_control_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+setup_flow_control_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-setup_flow_control_ready (MMBroadbandModemCinterion *self,
+setup_flow_control_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
- if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+ if (!mm_base_modem_at_command_finish (self, res, &error))
/* Let the error be critical. We DO need RTS/CTS in order to have
* proper modem disabling. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-setup_flow_control (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+setup_flow_control (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- setup_flow_control);
+ task = g_task_new (self, NULL, callback, user_data);
/* We need to enable RTS/CTS so that CYCLIC SLEEP mode works */
+ g_object_set (self, MM_BROADBAND_MODEM_FLOW_CONTROL, MM_FLOW_CONTROL_RTS_CTS, NULL);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"\\Q3",
3,
FALSE,
(GAsyncReadyCallback)setup_flow_control_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Load unlock retries (Modem interface) */
typedef struct {
- MMBroadbandModemCinterion *self;
- GSimpleAsyncResult *result;
MMUnlockRetries *retries;
- guint i;
+ guint i;
} LoadUnlockRetriesContext;
typedef struct {
- MMModemLock lock;
+ MMModemLock lock;
const gchar *command;
} UnlockRetriesMap;
@@ -1454,95 +2367,94 @@ static const UnlockRetriesMap unlock_retries_map [] = {
};
static void
-load_unlock_retries_context_complete_and_free (LoadUnlockRetriesContext *ctx)
+load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->retries);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (LoadUnlockRetriesContext, ctx);
}
static MMUnlockRetries *
-load_unlock_retries_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void load_unlock_retries_context_step (LoadUnlockRetriesContext *ctx);
+static void load_unlock_retries_context_step (GTask *task);
static void
-spic_ready (MMBaseModem *self,
+spic_ready (MMBaseModem *self,
GAsyncResult *res,
- LoadUnlockRetriesContext *ctx)
+ GTask *task)
{
- const gchar *response;
- GError *error = NULL;
+ LoadUnlockRetriesContext *ctx;
+ const gchar *response;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- mm_dbg ("Couldn't load retry count for lock '%s': %s",
- mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock),
- error->message);
- g_error_free (error);
+ mm_obj_dbg (self, "Couldn't load retry count for lock '%s': %s",
+ mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock),
+ error->message);
} else {
guint val;
response = mm_strip_tag (response, "^SPIC:");
if (!mm_get_uint_from_str (response, &val))
- mm_dbg ("Couldn't parse retry count value for lock '%s'",
- mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock));
+ mm_obj_dbg (self, "couldn't parse retry count value for lock '%s'",
+ mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock));
else
mm_unlock_retries_set (ctx->retries, unlock_retries_map[ctx->i].lock, val);
}
/* Go to next lock value */
ctx->i++;
- load_unlock_retries_context_step (ctx);
+ load_unlock_retries_context_step (task);
}
static void
-load_unlock_retries_context_step (LoadUnlockRetriesContext *ctx)
+load_unlock_retries_context_step (GTask *task)
{
+ MMBroadbandModemCinterion *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (ctx->i == G_N_ELEMENTS (unlock_retries_map)) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->retries),
- (GDestroyNotify)g_object_unref);
- load_unlock_retries_context_complete_and_free (ctx);
+ g_task_return_pointer (task, g_object_ref (ctx->retries), g_object_unref);
+ g_object_unref (task);
return;
}
mm_base_modem_at_command (
- MM_BASE_MODEM (ctx->self),
+ MM_BASE_MODEM (self),
unlock_retries_map[ctx->i].command,
3,
FALSE,
(GAsyncReadyCallback)spic_ready,
- ctx);
+ task);
}
static void
-load_unlock_retries (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_unlock_retries (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
LoadUnlockRetriesContext *ctx;
+ task = g_task_new (self, NULL, callback, user_data);
+
ctx = g_slice_new0 (LoadUnlockRetriesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_unlock_retries);
ctx->retries = mm_unlock_retries_new ();
ctx->i = 0;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free);
- load_unlock_retries_context_step (ctx);
+ load_unlock_retries_context_step (task);
}
/*****************************************************************************/
@@ -1557,48 +2469,40 @@ typedef enum {
} CinterionSimStatus;
typedef struct {
- MMBroadbandModemCinterion *self;
- GSimpleAsyncResult *result;
guint retries;
guint timeout_id;
} AfterSimUnlockContext;
-static void
-after_sim_unlock_context_complete_and_free (AfterSimUnlockContext *ctx)
-{
- g_assert (ctx->timeout_id == 0);
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (AfterSimUnlockContext, ctx);
-}
-
static gboolean
-after_sim_unlock_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+after_sim_unlock_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void after_sim_unlock_context_step (AfterSimUnlockContext *ctx);
+static void after_sim_unlock_context_step (GTask *task);
static gboolean
-simstatus_timeout_cb (AfterSimUnlockContext *ctx)
+simstatus_timeout_cb (GTask *task)
{
+ AfterSimUnlockContext *ctx;
+
+ ctx = g_task_get_task_data (task);
ctx->timeout_id = 0;
- after_sim_unlock_context_step (ctx);
- return FALSE;
+ after_sim_unlock_context_step (task);
+ return G_SOURCE_REMOVE;
}
static void
-simstatus_check_ready (MMBaseModem *self,
+simstatus_check_ready (MMBaseModem *self,
GAsyncResult *res,
- AfterSimUnlockContext *ctx)
+ GTask *task)
{
- const gchar *response;
+ AfterSimUnlockContext *ctx;
+ const gchar *response;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+ response = mm_base_modem_at_command_finish (self, res, NULL);
if (response) {
gchar *descr = NULL;
guint val = 0;
@@ -1608,8 +2512,8 @@ simstatus_check_ready (MMBaseModem *self,
val == CINTERION_SIM_STATUS_INIT_COMPLETED) {
/* SIM ready! */
g_free (descr);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- after_sim_unlock_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -1617,58 +2521,352 @@ simstatus_check_ready (MMBaseModem *self,
}
/* Need to retry after 1 sec */
+ ctx = g_task_get_task_data (task);
g_assert (ctx->timeout_id == 0);
- ctx->timeout_id = g_timeout_add_seconds (1, (GSourceFunc)simstatus_timeout_cb, ctx);
+ ctx->timeout_id = g_timeout_add_seconds (1, (GSourceFunc)simstatus_timeout_cb, task);
}
static void
-after_sim_unlock_context_step (AfterSimUnlockContext *ctx)
+after_sim_unlock_context_step (GTask *task)
{
- if (ctx->retries == 0) {
- /* Too much wait, go on anyway */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- after_sim_unlock_context_complete_and_free (ctx);
+ MMBroadbandModemCinterion *self;
+ AfterSimUnlockContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* if not supported or too much wait, skip */
+ if (self->priv->sind_simstatus_support != FEATURE_SUPPORTED || ctx->retries == 0) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Recheck */
ctx->retries--;
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
- "^SIND=\"simstatus\",1",
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SIND=\"simstatus\",2",
3,
FALSE,
(GAsyncReadyCallback)simstatus_check_ready,
- ctx);
+ task);
}
static void
-after_sim_unlock (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+sind_indicators_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
{
+ MMBroadbandModemCinterion *self;
+ g_autoptr(GError) error = NULL;
+ const gchar *response;
+
+ self = MM_BROADBAND_MODEM_CINTERION (_self);
+ if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
+ self->priv->sind_psinfo_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_dbg (self, "psinfo support? no");
+
+ self->priv->sind_simstatus_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_dbg (self, "simstatus support? no");
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ return;
+ }
+
+ if (g_regex_match_simple ("\\(\\s*psinfo\\s*,", response, 0, 0))
+ self->priv->sind_psinfo_support = FEATURE_SUPPORTED;
+ mm_obj_dbg (self, "psinfo support? %s", self->priv->sind_psinfo_support == FEATURE_SUPPORTED ? "yes":"no");
+
+ if (g_regex_match_simple ("\\(\\s*simstatus\\s*,", response, 0, 0))
+ self->priv->sind_simstatus_support = FEATURE_SUPPORTED;
+ mm_obj_dbg (self, "simstatus support? %s", self->priv->sind_simstatus_support == FEATURE_SUPPORTED ? "yes":"no");
+
+ after_sim_unlock_context_step (task);
+}
+
+static void
+after_sim_unlock (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
AfterSimUnlockContext *ctx;
- ctx = g_slice_new0 (AfterSimUnlockContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- after_sim_unlock);
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_new0 (AfterSimUnlockContext, 1);
ctx->retries = MAX_AFTER_SIM_UNLOCK_RETRIES;
+ g_task_set_task_data (task, ctx, g_free);
- after_sim_unlock_context_step (ctx);
+ /* check which indicators are available */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SIND=?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)sind_indicators_ready,
+ task);
}
/*****************************************************************************/
-/* Setup ports (Broadband modem class) */
+/* Setup SIM hot swap (Modem interface) */
static void
-setup_ports (MMBroadbandModem *self)
+cinterion_scks_unsolicited_handler (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemCinterion *self)
{
- /* Call parent's setup ports first always */
- MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_cinterion_parent_class)->setup_ports (self);
+ guint scks;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &scks))
+ return;
+
+ switch (scks) {
+ case 0:
+ mm_obj_info (self, "SIM removal detected");
+ break;
+ case 1:
+ mm_obj_info (self, "SIM insertion detected");
+ break;
+ case 2:
+ mm_obj_info (self, "SIM interface hardware deactivated (Potentially non-electrically compatible SIM inserted)");
+ break;
+ case 3:
+ mm_obj_info (self, "SIM interface hardware deactivated (Technical problem, no precise diagnosis)");
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
- mm_common_cinterion_setup_gps_port (self);
+ mm_broadband_modem_sim_hot_swap_detected (MM_BROADBAND_MODEM (self));
+}
+
+static gboolean
+modem_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cinterion_hot_swap_init_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GError *error = NULL;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+
+ if (!mm_base_modem_at_command_finish (_self, res, &error)) {
+ g_prefix_error (&error, "Could not enable SCKS: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "SIM hot swap detect successfully enabled");
+
+ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ primary,
+ self->priv->scks_regex,
+ (MMPortSerialAtUnsolicitedMsgFn) cinterion_scks_unsolicited_handler,
+ self,
+ NULL);
+
+ secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+ if (secondary)
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ secondary,
+ self->priv->scks_regex,
+ (MMPortSerialAtUnsolicitedMsgFn) cinterion_scks_unsolicited_handler,
+ self,
+ NULL);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ mm_obj_dbg (self, "Enabling SCKS URCs for SIM hot swap detection");
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SCKS=1",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) cinterion_hot_swap_init_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Create Bearer (Modem interface) */
+
+static MMBaseBearer *
+cinterion_modem_create_bearer_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+broadband_bearer_cinterion_new_ready (GObject *unused,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseBearer *bearer;
+ GError *error = NULL;
+
+ bearer = mm_broadband_bearer_cinterion_new_finish (res, &error);
+ if (!bearer)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+broadband_bearer_new_ready (GObject *unused,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseBearer *bearer;
+ GError *error = NULL;
+
+ bearer = mm_broadband_bearer_new_finish (res, &error);
+ if (!bearer)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+common_create_bearer (GTask *task)
+{
+ MMBroadbandModemCinterion *self;
+
+ self = g_task_get_source_object (task);
+
+ switch (self->priv->swwan_support) {
+ case FEATURE_NOT_SUPPORTED:
+ mm_obj_dbg (self, "^SWWAN not supported, creating default bearer...");
+ mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
+ g_task_get_task_data (task),
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)broadband_bearer_new_ready,
+ task);
+ return;
+ case FEATURE_SUPPORTED:
+ mm_obj_dbg (self, "^SWWAN supported, creating cinterion bearer...");
+ mm_broadband_bearer_cinterion_new (MM_BROADBAND_MODEM_CINTERION (self),
+ g_task_get_task_data (task),
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)broadband_bearer_cinterion_new_ready,
+ task);
+ return;
+ case FEATURE_SUPPORT_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+swwan_test_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+
+ /* Fetch the result to the SWWAN test. If no response given (error triggered),
+ * assume unsupported */
+ if (!mm_base_modem_at_command_finish (_self, res, NULL)) {
+ mm_obj_dbg (self, "SWWAN unsupported");
+ self->priv->swwan_support = FEATURE_NOT_SUPPORTED;
+ } else {
+ mm_obj_dbg (self, "SWWAN supported");
+ self->priv->swwan_support = FEATURE_SUPPORTED;
+ }
+
+ /* Go on and create the bearer */
+ common_create_bearer (task);
+}
+
+static void
+cinterion_modem_create_bearer (MMIfaceModem *_self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, g_object_ref (properties), g_object_unref);
+
+ /* Newer Cinterion modems may support SWWAN, which is the same as WWAN.
+ * Check to see if current modem supports it.*/
+ if (self->priv->swwan_support != FEATURE_SUPPORT_UNKNOWN) {
+ common_create_bearer (task);
+ return;
+ }
+
+ /* If we don't have a data port, don't even bother checking for ^SWWAN
+ * support. */
+ if (!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) {
+ mm_obj_dbg (self, "skipping ^SWWAN check as no data port is available");
+ self->priv->swwan_support = FEATURE_NOT_SUPPORTED;
+ common_create_bearer (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "checking ^SWWAN support...");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SWWAN=?",
+ 6,
+ TRUE, /* may be cached */
+ (GAsyncReadyCallback) swwan_test_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static void
+setup_ports (MMBroadbandModem *_self)
+{
+ MMBroadbandModemCinterion *self = (MM_BROADBAND_MODEM_CINTERION (_self));
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ /* Call parent's setup ports first always */
+ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_cinterion_parent_class)->setup_ports (_self);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->sysstart_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->scks_regex,
+ NULL, NULL, NULL);
+ }
}
/*****************************************************************************/
@@ -1686,6 +2884,11 @@ mm_broadband_modem_cinterion_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (TTY) or Cinterion bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
NULL);
}
@@ -1697,8 +2900,19 @@ mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self)
MM_TYPE_BROADBAND_MODEM_CINTERION,
MMBroadbandModemCinterionPrivate);
- /* Set defaults */
- self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */
+ /* Initialize private variables */
+ self->priv->initial_eps_bearer_cid = -1;
+ self->priv->sind_psinfo_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->swwan_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->smoni_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->sind_simstatus_support = FEATURE_SUPPORT_UNKNOWN;
+
+ self->priv->ciev_regex = g_regex_new ("\\r\\n\\+CIEV:\\s*([a-z]+),(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->sysstart_regex = g_regex_new ("\\r\\n\\^SYSSTART.*\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->scks_regex = g_regex_new ("\\^SCKS:\\s*([0-3])\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
}
static void
@@ -1707,7 +2921,6 @@ finalize (GObject *object)
MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (object);
g_free (self->priv->sleep_mode_cmd);
- g_free (self->priv->manual_operator_id);
if (self->priv->cnmi_supported_mode)
g_array_unref (self->priv->cnmi_supported_mode);
@@ -1720,6 +2933,10 @@ finalize (GObject *object)
if (self->priv->cnmi_supported_bfr)
g_array_unref (self->priv->cnmi_supported_bfr);
+ g_regex_unref (self->priv->ciev_regex);
+ g_regex_unref (self->priv->sysstart_regex);
+ g_regex_unref (self->priv->scks_regex);
+
G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object);
}
@@ -1728,6 +2945,8 @@ iface_modem_init (MMIfaceModem *iface)
{
iface_modem_parent = g_type_interface_peek_parent (iface);
+ iface->create_bearer = cinterion_modem_create_bearer;
+ iface->create_bearer_finish = cinterion_modem_create_bearer_finish;
iface->load_supported_modes = load_supported_modes;
iface->load_supported_modes_finish = load_supported_modes_finish;
iface->set_current_modes = set_current_modes;
@@ -1746,19 +2965,44 @@ iface_modem_init (MMIfaceModem *iface)
iface->modem_after_sim_unlock_finish = after_sim_unlock_finish;
iface->load_unlock_retries = load_unlock_retries;
iface->load_unlock_retries_finish = load_unlock_retries_finish;
+ iface->reset = mm_shared_cinterion_modem_reset;
+ iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
iface->modem_power_down = modem_power_down;
iface->modem_power_down_finish = modem_power_down_finish;
iface->modem_power_off = modem_power_off;
iface->modem_power_off_finish = modem_power_off_finish;
+ iface->setup_sim_hot_swap = modem_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish;
+}
+
+static MMIfaceModem *
+peek_parent_interface (MMSharedCinterion *self)
+{
+ return iface_modem_parent;
}
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
- iface->enable_unsolicited_events = enable_unsolicited_events;
- iface->enable_unsolicited_events_finish = enable_unsolicited_events_finish;
- iface->register_in_network = register_in_network;
- iface->register_in_network_finish = register_in_network_finish;
+ iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+
+ iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish;
+
+ iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+
+ iface->load_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer;
+ iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish;
+ iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
+ iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
+ iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
+ iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish;
+
}
static void
@@ -1773,14 +3017,82 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)
static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
- mm_common_cinterion_peek_parent_location_interface (iface);
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_cinterion_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedCinterion *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->create_call = mm_shared_cinterion_create_call;
+
+ iface->check_support = mm_shared_cinterion_voice_check_support;
+ iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish;
+ iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish;
+ iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish;
+}
+
+static MMIfaceModemVoice *
+peek_parent_voice_interface (MMSharedCinterion *self)
+{
+ return iface_modem_voice_parent;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+ iface_modem_time_parent = g_type_interface_peek_parent (iface);
+
+ iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish;
+}
+
+static MMIfaceModemTime *
+peek_parent_time_interface (MMSharedCinterion *self)
+{
+ return iface_modem_time_parent;
+}
+
+static void
+shared_cinterion_init (MMSharedCinterion *iface)
+{
+ iface->peek_parent_interface = peek_parent_interface;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+ iface->peek_parent_voice_interface = peek_parent_voice_interface;
+ iface->peek_parent_time_interface = peek_parent_time_interface;
+}
+
+static void
+iface_modem_signal_init (MMIfaceModemSignal *iface)
+{
+ iface_modem_signal_parent = g_type_interface_peek_parent (iface);
- iface->load_capabilities = mm_common_cinterion_location_load_capabilities;
- iface->load_capabilities_finish = mm_common_cinterion_location_load_capabilities_finish;
- iface->enable_location_gathering = mm_common_cinterion_enable_location_gathering;
- iface->enable_location_gathering_finish = mm_common_cinterion_enable_location_gathering_finish;
- iface->disable_location_gathering = mm_common_cinterion_disable_location_gathering;
- iface->disable_location_gathering_finish = mm_common_cinterion_disable_location_gathering_finish;
+ 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
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.h b/plugins/cinterion/mm-broadband-modem-cinterion.h
index 47f0dcb4..555ee084 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.h
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.h
@@ -19,6 +19,7 @@
#define MM_BROADBAND_MODEM_CINTERION_H
#include "mm-broadband-modem.h"
+#include "mm-modem-helpers-cinterion.h"
#define MM_TYPE_BROADBAND_MODEM_CINTERION (mm_broadband_modem_cinterion_get_type ())
#define MM_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterion))
@@ -48,4 +49,6 @@ MMBroadbandModemCinterion *mm_broadband_modem_cinterion_new (const gchar *device
guint16 vendor_id,
guint16 product_id);
+MMCinterionModemFamily mm_broadband_modem_cinterion_get_family (MMBroadbandModemCinterion * modem);
+
#endif /* MM_BROADBAND_MODEM_CINTERION_H */
diff --git a/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c b/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c
new file mode 100644
index 00000000..255af0ce
--- /dev/null
+++ b/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c
@@ -0,0 +1,168 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log.h"
+#include "mm-errors-types.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-broadband-modem-mbim-cinterion.h"
+#include "mm-shared-cinterion.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void shared_cinterion_init (MMSharedCinterion *iface);
+
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
+static MMIfaceModemTime *iface_modem_time_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimCinterion, mm_broadband_modem_mbim_cinterion, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init))
+
+/*****************************************************************************/
+
+MMBroadbandModemMbimCinterion *
+mm_broadband_modem_mbim_cinterion_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED, TRUE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_mbim_cinterion_init (MMBroadbandModemMbimCinterion *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->reset = mm_shared_cinterion_modem_reset;
+ iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
+}
+
+static MMIfaceModem *
+peek_parent_interface (MMSharedCinterion *self)
+{
+ return iface_modem_parent;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_cinterion_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedCinterion *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->create_call = mm_shared_cinterion_create_call;
+
+ iface->check_support = mm_shared_cinterion_voice_check_support;
+ iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish;
+ iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish;
+ iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish;
+}
+
+static MMIfaceModemVoice *
+peek_parent_voice_interface (MMSharedCinterion *self)
+{
+ return iface_modem_voice_parent;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+ iface_modem_time_parent = g_type_interface_peek_parent (iface);
+
+ iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish;
+}
+
+static MMIfaceModemTime *
+peek_parent_time_interface (MMSharedCinterion *self)
+{
+ return iface_modem_time_parent;
+}
+
+static void
+shared_cinterion_init (MMSharedCinterion *iface)
+{
+ iface->peek_parent_interface = peek_parent_interface;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+ iface->peek_parent_voice_interface = peek_parent_voice_interface;
+ iface->peek_parent_time_interface = peek_parent_time_interface;
+}
+
+static void
+mm_broadband_modem_mbim_cinterion_class_init (MMBroadbandModemMbimCinterionClass *klass)
+{
+}
diff --git a/plugins/cinterion/mm-broadband-modem-mbim-cinterion.h b/plugins/cinterion/mm-broadband-modem-mbim-cinterion.h
new file mode 100644
index 00000000..a2f2ef68
--- /dev/null
+++ b/plugins/cinterion/mm-broadband-modem-mbim-cinterion.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_MBIM_CINTERION_MBIM_H
+#define MM_BROADBAND_MODEM_MBIM_CINTERION_MBIM_H
+
+#include "mm-broadband-modem-mbim.h"
+
+#define MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION (mm_broadband_modem_mbim_cinterion_get_type ())
+#define MM_BROADBAND_MODEM_MBIM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterion))
+#define MM_BROADBAND_MODEM_MBIM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterionClass))
+#define MM_IS_BROADBAND_MODEM_MBIM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION))
+#define MM_IS_BROADBAND_MODEM_MBIM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION))
+#define MM_BROADBAND_MODEM_MBIM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_CINTERION, MMBroadbandModemMbimCinterionClass))
+
+typedef struct _MMBroadbandModemMbimCinterion MMBroadbandModemMbimCinterion;
+typedef struct _MMBroadbandModemMbimCinterionClass MMBroadbandModemMbimCinterionClass;
+
+struct _MMBroadbandModemMbimCinterion {
+ MMBroadbandModemMbim parent;
+};
+
+struct _MMBroadbandModemMbimCinterionClass{
+ MMBroadbandModemMbimClass parent;
+};
+
+GType mm_broadband_modem_mbim_cinterion_get_type (void);
+
+MMBroadbandModemMbimCinterion *mm_broadband_modem_mbim_cinterion_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_MBIM_CINTERION_H */
diff --git a/plugins/cinterion/mm-broadband-modem-qmi-cinterion.c b/plugins/cinterion/mm-broadband-modem-qmi-cinterion.c
index 05f92e4f..c3f5e05c 100644
--- a/plugins/cinterion/mm-broadband-modem-qmi-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-qmi-cinterion.c
@@ -25,26 +25,29 @@
#include "ModemManager.h"
#include "mm-log.h"
#include "mm-errors-types.h"
+#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
#include "mm-broadband-modem-qmi-cinterion.h"
-#include "mm-common-cinterion.h"
+#include "mm-shared-cinterion.h"
+static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void shared_cinterion_init (MMSharedCinterion *iface);
-G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiCinterion, mm_broadband_modem_qmi_cinterion, MM_TYPE_BROADBAND_MODEM_QMI, 0,
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
-
-/*****************************************************************************/
-/* Setup ports (Broadband modem class) */
-
-static void
-setup_ports (MMBroadbandModem *self)
-{
- /* Call parent's setup ports first always */
- MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_cinterion_parent_class)->setup_ports (self);
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
+static MMIfaceModemTime *iface_modem_time_parent;
- mm_common_cinterion_setup_gps_port (self);
-}
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiCinterion, mm_broadband_modem_qmi_cinterion, MM_TYPE_BROADBAND_MODEM_QMI, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init))
/*****************************************************************************/
@@ -61,6 +64,11 @@ mm_broadband_modem_qmi_cinterion_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* QMI bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
NULL);
}
@@ -70,23 +78,91 @@ mm_broadband_modem_qmi_cinterion_init (MMBroadbandModemQmiCinterion *self)
}
static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->reset = mm_shared_cinterion_modem_reset;
+ iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
+}
+
+static MMIfaceModem *
+peek_parent_interface (MMSharedCinterion *self)
+{
+ return iface_modem_parent;
+}
+
+static void
iface_modem_location_init (MMIfaceModemLocation *iface)
{
- mm_common_cinterion_peek_parent_location_interface (iface);
-
- iface->load_capabilities = mm_common_cinterion_location_load_capabilities;
- iface->load_capabilities_finish = mm_common_cinterion_location_load_capabilities_finish;
- iface->enable_location_gathering = mm_common_cinterion_enable_location_gathering;
- iface->enable_location_gathering_finish = mm_common_cinterion_enable_location_gathering_finish;
- iface->disable_location_gathering = mm_common_cinterion_disable_location_gathering;
- iface->disable_location_gathering_finish = mm_common_cinterion_disable_location_gathering_finish;
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_cinterion_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_cinterion_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_cinterion_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_cinterion_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_cinterion_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_cinterion_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedCinterion *self)
+{
+ return iface_modem_location_parent;
}
static void
-mm_broadband_modem_qmi_cinterion_class_init (MMBroadbandModemQmiCinterionClass *klass)
+iface_modem_voice_init (MMIfaceModemVoice *iface)
{
- MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
- /* Virtual methods */
- broadband_modem_class->setup_ports = setup_ports;
+ iface->create_call = mm_shared_cinterion_create_call;
+
+ iface->check_support = mm_shared_cinterion_voice_check_support;
+ iface->check_support_finish = mm_shared_cinterion_voice_check_support_finish;
+ iface->enable_unsolicited_events = mm_shared_cinterion_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = mm_shared_cinterion_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = mm_shared_cinterion_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = mm_shared_cinterion_voice_disable_unsolicited_events_finish;
+ iface->setup_unsolicited_events = mm_shared_cinterion_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_cinterion_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_cinterion_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_voice_cleanup_unsolicited_events_finish;
+}
+
+static MMIfaceModemVoice *
+peek_parent_voice_interface (MMSharedCinterion *self)
+{
+ return iface_modem_voice_parent;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+ iface_modem_time_parent = g_type_interface_peek_parent (iface);
+
+ iface->setup_unsolicited_events = mm_shared_cinterion_time_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_cinterion_time_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_cinterion_time_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_cinterion_time_cleanup_unsolicited_events_finish;
+}
+
+static MMIfaceModemTime *
+peek_parent_time_interface (MMSharedCinterion *self)
+{
+ return iface_modem_time_parent;
+}
+
+static void
+shared_cinterion_init (MMSharedCinterion *iface)
+{
+ iface->peek_parent_interface = peek_parent_interface;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+ iface->peek_parent_voice_interface = peek_parent_voice_interface;
+ iface->peek_parent_time_interface = peek_parent_time_interface;
+}
+
+static void
+mm_broadband_modem_qmi_cinterion_class_init (MMBroadbandModemQmiCinterionClass *klass)
+{
}
diff --git a/plugins/cinterion/mm-common-cinterion.c b/plugins/cinterion/mm-common-cinterion.c
deleted file mode 100644
index 6e4da70c..00000000
--- a/plugins/cinterion/mm-common-cinterion.c
+++ /dev/null
@@ -1,466 +0,0 @@
-/* -*- 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) 2014 Ammonit Measurement GmbH
- * Author: Aleksander Morgado <aleksander@aleksander.es>
- */
-
-#include "mm-common-cinterion.h"
-#include "mm-base-modem-at.h"
-
-static MMIfaceModemLocation *iface_modem_location_parent;
-
-/*****************************************************************************/
-
-#define CINTERION_LOCATION_CONTEXT_TAG "cinterion-location-tag"
-static GQuark cinterion_location_context_quark;
-
-/*****************************************************************************/
-
-typedef struct {
- MMModemLocationSource enabled_sources;
-} LocationContext;
-
-static void
-location_context_free (LocationContext *ctx)
-{
- g_slice_free (LocationContext, ctx);
-}
-
-static LocationContext *
-get_location_context (MMBaseModem *self)
-{
- LocationContext *ctx;
-
- if (G_UNLIKELY (!cinterion_location_context_quark))
- cinterion_location_context_quark = (g_quark_from_static_string (
- CINTERION_LOCATION_CONTEXT_TAG));
-
- ctx = g_object_get_qdata (G_OBJECT (self), cinterion_location_context_quark);
- if (!ctx) {
- /* Create context and keep it as object data */
- ctx = g_slice_new (LocationContext);
- ctx->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE;
-
- g_object_set_qdata_full (
- G_OBJECT (self),
- cinterion_location_context_quark,
- ctx,
- (GDestroyNotify)location_context_free);
- }
-
- return ctx;
-}
-
-
-/*****************************************************************************/
-/* Location capabilities loading (Location interface) */
-
-MMModemLocationSource
-mm_common_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_LOCATION_SOURCE_NONE;
-
- return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
-}
-
-static void
-parent_load_capabilities_ready (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- MMModemLocationSource sources;
- GError *error = NULL;
-
- sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
- if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- /* Now our own check. */
- if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)))
- sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED);
-
- /* So we're done, complete */
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (sources),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-void
-mm_common_cinterion_location_load_capabilities (MMIfaceModemLocation *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_common_cinterion_location_load_capabilities);
-
- /* Chain up parent's setup */
- iface_modem_location_parent->load_capabilities (
- self,
- (GAsyncReadyCallback)parent_load_capabilities_ready,
- result);
-}
-
-/*****************************************************************************/
-/* Enable/Disable location gathering (Location interface) */
-
-typedef struct {
- MMBaseModem *self;
- GSimpleAsyncResult *result;
- MMModemLocationSource source;
-} LocationGatheringContext;
-
-static void
-location_gathering_context_complete_and_free (LocationGatheringContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LocationGatheringContext, ctx);
-}
-
-/******************************/
-/* Disable location gathering */
-
-gboolean
-mm_common_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-gps_disabled_ready (MMBaseModem *self,
- GAsyncResult *res,
- LocationGatheringContext *ctx)
-{
- GError *error = NULL;
-
- if (!mm_base_modem_at_command_full_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-
- /* Only use the GPS port in NMEA/RAW setups */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
- MMPortSerialGps *gps_port;
-
- /* Even if we get an error here, we try to close the GPS port */
- gps_port = mm_base_modem_peek_port_gps (self);
- if (gps_port)
- mm_port_serial_close (MM_PORT_SERIAL (gps_port));
- }
-
- location_gathering_context_complete_and_free (ctx);
-}
-
-static void
-internal_disable_location_gathering (LocationGatheringContext *ctx)
-{
- LocationContext *location_ctx;
- gboolean stop_gps = FALSE;
-
- location_ctx = get_location_context (MM_BASE_MODEM (ctx->self));
-
- /* Only stop GPS engine if no GPS-related sources enabled */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- location_ctx->enabled_sources &= ~ctx->source;
-
- if (!(location_ctx->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
- stop_gps = TRUE;
- }
-
- if (stop_gps) {
- /* We disable continuous GPS fixes */
- mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->self),
- mm_base_modem_peek_best_at_port (MM_BASE_MODEM (ctx->self), NULL),
- "AT^SGPSS=0",
- 3,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_disabled_ready,
- ctx);
- return;
- }
-
- /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- location_gathering_context_complete_and_free (ctx);
-}
-
-static void
-parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
- GAsyncResult *res,
- LocationGatheringContext *ctx)
-{
- GError *error = NULL;
-
- if (!iface_modem_location_parent->disable_location_gathering_finish (self, res, &error)) {
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- /* Ignore errors when disabling GPS, we can try with AT commands */
- g_error_free (error);
- } else {
- /* Fatal */
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
- return;
- }
- }
-
- internal_disable_location_gathering (ctx);
-}
-
-void
-mm_common_cinterion_disable_location_gathering (MMIfaceModemLocation *self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- LocationGatheringContext *ctx;
-
- ctx = g_slice_new (LocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_common_cinterion_disable_location_gathering);
- ctx->source = source;
-
- /* Chain up parent's gathering enable */
- if (iface_modem_location_parent->disable_location_gathering) {
- iface_modem_location_parent->disable_location_gathering (
- self,
- source,
- (GAsyncReadyCallback)parent_disable_location_gathering_ready,
- ctx);
- return;
- }
-
- internal_disable_location_gathering (ctx);
-}
-
-/*****************************************************************************/
-/* Enable location gathering (Location interface) */
-
-gboolean
-mm_common_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-gps_enabled_ready (MMBaseModem *self,
- GAsyncResult *res,
- LocationGatheringContext *ctx)
-{
- GError *error = NULL;
-
- if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- /* Only use the GPS port in NMEA/RAW setups */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
- MMPortSerialGps *gps_port;
-
- gps_port = mm_base_modem_peek_port_gps (self);
- if (!gps_port ||
- !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &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,
- "Couldn't open raw GPS serial port");
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-
- location_gathering_context_complete_and_free (ctx);
-}
-
-static void
-parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
- GAsyncResult *res,
- LocationGatheringContext *ctx)
-{
- gboolean start_gps = FALSE;
- GError *error = NULL;
- LocationContext *location_ctx;
-
- if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- /* Ignore errors when enabling GPS, we can try with AT commands */
- g_error_free (error);
- } else {
- /* Fatal */
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
- return;
- }
- }
-
- /* Now our own enabling */
-
- location_ctx = get_location_context (MM_BASE_MODEM (self));
-
- /* NMEA and RAW are both enabled in the same way */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- /* Only start GPS engine if not done already */
- if (!(location_ctx->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
- start_gps = TRUE;
- location_ctx->enabled_sources |= ctx->source;
- }
-
- if (start_gps) {
- /* We enable continuous GPS fixes */
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
- "AT^SGPSS=4",
- 3,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_enabled_ready,
- ctx);
- return;
- }
-
- /* For any other location (e.g. 3GPP), or if GPS already running just return */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- location_gathering_context_complete_and_free (ctx);
-}
-
-void
-mm_common_cinterion_enable_location_gathering (MMIfaceModemLocation *self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- LocationGatheringContext *ctx;
-
- ctx = g_slice_new (LocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_common_cinterion_enable_location_gathering);
- ctx->source = source;
-
- /* Chain up parent's gathering enable */
- iface_modem_location_parent->enable_location_gathering (
- self,
- source,
- (GAsyncReadyCallback)parent_enable_location_gathering_ready,
- ctx);
-}
-
-/*****************************************************************************/
-/* Setup ports (Broadband modem class) */
-
-static void
-trace_received (MMPortSerialGps *port,
- const gchar *trace,
- MMIfaceModemLocation *self)
-{
- /* Helper to debug GPS location related issues. Don't depend on a real GPS
- * fix for debugging, just use some random values to update */
-#if 0
- if (g_str_has_prefix (trace, "$GPGGA")) {
- GString *str;
- GDateTime *now;
-
- now = g_date_time_new_now_utc ();
- str = g_string_new ("");
- g_string_append_printf (str,
- "$GPGGA,%02u%02u%02u,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47",
- g_date_time_get_hour (now),
- g_date_time_get_minute (now),
- g_date_time_get_second (now));
- mm_iface_modem_location_gps_update (self, str->str);
- g_string_free (str, TRUE);
- g_date_time_unref (now);
- return;
- }
-#endif
-
- mm_iface_modem_location_gps_update (self, trace);
-}
-
-void
-mm_common_cinterion_setup_gps_port (MMBroadbandModem *self)
-{
- MMPortSerialGps *gps_data_port;
-
- gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
- if (gps_data_port) {
- /* It may happen that the modem was started with GPS already enabled, or
- * maybe ModemManager got rebooted and it was left enabled before. We'll make
- * sure that it is disabled when we initialize the modem */
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
- "AT^SGPSS=0",
- 3, FALSE, FALSE, NULL, NULL, NULL);
-
- /* Add handler for the NMEA traces */
- mm_port_serial_gps_add_trace_handler (gps_data_port,
- (MMPortSerialGpsTraceFn)trace_received,
- self,
- NULL);
- }
-}
-
-/*****************************************************************************/
-
-void
-mm_common_cinterion_peek_parent_location_interface (MMIfaceModemLocation *iface)
-{
- iface_modem_location_parent = g_type_interface_peek_parent (iface);
-}
diff --git a/plugins/cinterion/mm-common-cinterion.h b/plugins/cinterion/mm-common-cinterion.h
deleted file mode 100644
index 02b01751..00000000
--- a/plugins/cinterion/mm-common-cinterion.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- 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) 2014 Ammonit Measurement GmbH
- * Author: Aleksander Morgado <aleksander@aleksander.es>
- */
-
-#ifndef MM_COMMON_CINTERION_H
-#define MM_COMMON_CINTERION_H
-
-#include "glib.h"
-#include "mm-broadband-modem.h"
-#include "mm-iface-modem-location.h"
-
-void mm_common_cinterion_location_load_capabilities (MMIfaceModemLocation *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMModemLocationSource mm_common_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error);
-
-void mm_common_cinterion_enable_location_gathering (MMIfaceModemLocation *self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_common_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error);
-
-void mm_common_cinterion_disable_location_gathering (MMIfaceModemLocation *self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_common_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error);
-
-void mm_common_cinterion_setup_gps_port (MMBroadbandModem *self);
-
-void mm_common_cinterion_peek_parent_location_interface (MMIfaceModemLocation *iface);
-
-#endif /* MM_COMMON_CINTERION_H */
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index bffe00a9..f8cec825 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -11,42 +11,131 @@
* GNU General Public License for more details:
*
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2016 Trimble Navigation Limited
+ * Copyright (C) 2016 Matthew Stanger <matthew_stanger@trimble.com>
+ * Copyright (C) 2019 Purism SPC
*/
#include <config.h>
#include <string.h>
#include <stdlib.h>
+#include <stdio.h>
#include "ModemManager.h"
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-charsets.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers-cinterion.h"
+#include "mm-modem-helpers.h"
+#include "mm-common-helpers.h"
+#include "mm-port-serial-at.h"
/* Setup relationship between the 3G band bitmask in the modem and the bitmask
* in ModemManager. */
typedef struct {
- guint32 cinterion_band_flag;
+ guint32 cinterion_band_flag;
MMModemBand mm_band;
} CinterionBand;
-/* Table checked in HC25 and PHS8 references. This table includes both 2G and 3G
+typedef struct {
+ MMCinterionRbBlock cinterion_band_block;
+ guint32 cinterion_band_flag;
+ MMModemBand mm_band;
+} CinterionBandEx;
+
+/* Table checked in PLS8-X/E/J/V/US, HC25 & PHS8 references. The table includes 2/3/4G
* frequencies. Depending on which one is configured, one access technology or
* the other will be used. This may conflict with the allowed mode configuration
* set, so you shouldn't for example set 3G frequency bands, and then use a
* 2G-only allowed mode. */
static const CinterionBand cinterion_bands[] = {
- { (1 << 0), MM_MODEM_BAND_EGSM },
- { (1 << 1), MM_MODEM_BAND_DCS },
- { (1 << 2), MM_MODEM_BAND_PCS },
- { (1 << 3), MM_MODEM_BAND_G850 },
- { (1 << 4), MM_MODEM_BAND_U2100 },
- { (1 << 5), MM_MODEM_BAND_U1900 },
- { (1 << 6), MM_MODEM_BAND_U850 },
- { (1 << 7), MM_MODEM_BAND_U900 },
- { (1 << 8), MM_MODEM_BAND_U800 }
+ { (1 << 0), MM_MODEM_BAND_EGSM },
+ { (1 << 1), MM_MODEM_BAND_DCS },
+ { (1 << 2), MM_MODEM_BAND_G850 },
+ { (1 << 3), MM_MODEM_BAND_PCS },
+ { (1 << 4), MM_MODEM_BAND_UTRAN_1 },
+ { (1 << 5), MM_MODEM_BAND_UTRAN_2 },
+ { (1 << 6), MM_MODEM_BAND_UTRAN_5 },
+ { (1 << 7), MM_MODEM_BAND_UTRAN_8 },
+ { (1 << 8), MM_MODEM_BAND_UTRAN_6 },
+ { (1 << 9), MM_MODEM_BAND_UTRAN_4 },
+ { (1 << 10), MM_MODEM_BAND_UTRAN_19 },
+ { (1 << 12), MM_MODEM_BAND_UTRAN_3 },
+ { (1 << 13), MM_MODEM_BAND_EUTRAN_1 },
+ { (1 << 14), MM_MODEM_BAND_EUTRAN_2 },
+ { (1 << 15), MM_MODEM_BAND_EUTRAN_3 },
+ { (1 << 16), MM_MODEM_BAND_EUTRAN_4 },
+ { (1 << 17), MM_MODEM_BAND_EUTRAN_5 },
+ { (1 << 18), MM_MODEM_BAND_EUTRAN_7 },
+ { (1 << 19), MM_MODEM_BAND_EUTRAN_8 },
+ { (1 << 20), MM_MODEM_BAND_EUTRAN_17 },
+ { (1 << 21), MM_MODEM_BAND_EUTRAN_20 },
+ { (1 << 22), MM_MODEM_BAND_EUTRAN_13 },
+ { (1 << 24), MM_MODEM_BAND_EUTRAN_19 }
+};
+
+static const CinterionBandEx cinterion_bands_ex[] = {
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000001, MM_MODEM_BAND_EGSM },
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000002, MM_MODEM_BAND_DCS },
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000004, MM_MODEM_BAND_G850 },
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000008, MM_MODEM_BAND_PCS },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000001, MM_MODEM_BAND_UTRAN_1 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000002, MM_MODEM_BAND_UTRAN_2 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000004, MM_MODEM_BAND_UTRAN_3 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000008, MM_MODEM_BAND_UTRAN_4 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000010, MM_MODEM_BAND_UTRAN_5 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000020, MM_MODEM_BAND_UTRAN_6 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000080, MM_MODEM_BAND_UTRAN_8 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000100, MM_MODEM_BAND_UTRAN_9 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00040000, MM_MODEM_BAND_UTRAN_19 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000001, MM_MODEM_BAND_EUTRAN_1 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000002, MM_MODEM_BAND_EUTRAN_2 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000004, MM_MODEM_BAND_EUTRAN_3 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000008, MM_MODEM_BAND_EUTRAN_4 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000010, MM_MODEM_BAND_EUTRAN_5 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000040, MM_MODEM_BAND_EUTRAN_7 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000080, MM_MODEM_BAND_EUTRAN_8 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000800, MM_MODEM_BAND_EUTRAN_12 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00001000, MM_MODEM_BAND_EUTRAN_13 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00010000, MM_MODEM_BAND_EUTRAN_17 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00020000, MM_MODEM_BAND_EUTRAN_18 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00040000, MM_MODEM_BAND_EUTRAN_19 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00080000, MM_MODEM_BAND_EUTRAN_20 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x02000000, MM_MODEM_BAND_EUTRAN_26 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x08000000, MM_MODEM_BAND_EUTRAN_28 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x10000000, MM_MODEM_BAND_EUTRAN_29 },
+ { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000020, MM_MODEM_BAND_EUTRAN_38 },
+ { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000040, MM_MODEM_BAND_EUTRAN_39 },
+ { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000080, MM_MODEM_BAND_EUTRAN_40 },
+ { MM_CINTERION_RB_BLOCK_LTE_HIGH, 0x00000100, MM_MODEM_BAND_EUTRAN_41 }
+};
+
+static const CinterionBandEx cinterion_bands_imt[] = {
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000004, MM_MODEM_BAND_EGSM },
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000010, MM_MODEM_BAND_DCS },
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000020, MM_MODEM_BAND_PCS },
+ { MM_CINTERION_RB_BLOCK_GSM, 0x00000040, MM_MODEM_BAND_G850 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000001, MM_MODEM_BAND_UTRAN_1 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000002, MM_MODEM_BAND_UTRAN_2 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000008, MM_MODEM_BAND_UTRAN_4 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000010, MM_MODEM_BAND_UTRAN_5 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000080, MM_MODEM_BAND_UTRAN_8 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00000100, MM_MODEM_BAND_UTRAN_9 },
+ { MM_CINTERION_RB_BLOCK_UMTS, 0x00040000, MM_MODEM_BAND_UTRAN_19 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000001, MM_MODEM_BAND_EUTRAN_1 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000002, MM_MODEM_BAND_EUTRAN_2 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000004, MM_MODEM_BAND_EUTRAN_3 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000008, MM_MODEM_BAND_EUTRAN_4 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000010, MM_MODEM_BAND_EUTRAN_5 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000040, MM_MODEM_BAND_EUTRAN_7 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000080, MM_MODEM_BAND_EUTRAN_8 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00000800, MM_MODEM_BAND_EUTRAN_12 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00020000, MM_MODEM_BAND_EUTRAN_18 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00040000, MM_MODEM_BAND_EUTRAN_19 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x00080000, MM_MODEM_BAND_EUTRAN_20 },
+ { MM_CINTERION_RB_BLOCK_LTE_LOW, 0x08000000, MM_MODEM_BAND_EUTRAN_28 }
};
/* Check valid combinations in 2G-only devices */
@@ -62,52 +151,143 @@ static const CinterionBand cinterion_bands[] = {
cinterion_mask == 15)
/*****************************************************************************/
-/* ^SCFG (3G) test parser
+/* ^SCFG (3G+LTE) test parser
*
- * Example:
+ * Example 3G:
* AT^SCFG=?
* ...
* ^SCFG: "MEShutdown/OnIgnition",("on","off")
* ^SCFG: "Radio/Band",("1-511","0-1")
* ^SCFG: "Radio/NWSM",("0","1","2")
* ...
+ * ^SCFG: "Radio/Band\",("1"-"147")
*
+ * Example LTE1 (GSM charset):
+ * AT^SCFG=?
+ * ...
+ * ^SCFG: "Radio/Band/2G",("0x00000004"-"0x00000074")
+ * ^SCFG: "Radio/Band/3G",("0x00000001"-"0x0004019B")
+ * ^SCFG: "Radio/Band/4G",("0x00000001"-"0x080E08DF")
+ * ...
+ *
+ * Example LTE1 (UCS2 charset):
+ * AT^SCFG=?
+ * ...
+ * ^SCFG: "Radio/Band/2G",("0030007800300030003000300030003000300034"-"0030007800300030003000300030003000370034")
+ * ^SCFG: "Radio/Band/3G",("0030007800300030003000300030003000300031"-"0030007800300030003000340030003100390042")
+ * ^SCFG: "Radio/Band/4G",("0030007800300030003000300030003000300031"-"0030007800300038003000450030003800440046")
+ * ...
+ *
+ * Example LTE2 (all charsets):
+ * AT^SCFG=?
+ * ...
+ * ^SCFG: "Radio/Band/2G",("00000001-0000000f"),,("0","1")
+ * ^SCFG: "Radio/Band/3G",("00000001-000400b5"),,("0","1")
+ * ^SCFG: "Radio/Band/4G",("00000001-8a0e00d5"),("00000002-000001e2"),("0","1")
+ * ...
*/
+static void
+parse_bands (guint bandlist,
+ GArray **bands,
+ MMCinterionRbBlock block,
+ MMCinterionModemFamily modem_family)
+{
+ guint i;
+ const CinterionBandEx *ref_bands;
+ guint nb_ref_bands;
+
+ if (!bandlist)
+ return;
+
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+ ref_bands = cinterion_bands_imt;
+ nb_ref_bands = G_N_ELEMENTS (cinterion_bands_imt);
+ } else {
+ ref_bands = cinterion_bands_ex;
+ nb_ref_bands = G_N_ELEMENTS (cinterion_bands_ex);
+ }
+
+ for (i = 0; i < nb_ref_bands; i++) {
+ if (block == ref_bands[i].cinterion_band_block && (bandlist & ref_bands[i].cinterion_band_flag)) {
+ if (G_UNLIKELY (!*bands))
+ *bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+ g_array_append_val (*bands, ref_bands[i].mm_band);
+ }
+ }
+}
+
+static guint
+take_and_convert_from_matched_string (gchar *str,
+ MMModemCharset charset,
+ MMCinterionModemFamily modem_family,
+ GError **error)
+{
+ guint val = 0;
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *taken_str = str;
+
+ if (!taken_str) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't convert to integer number: no input string");
+ return 0;
+ }
+
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+ utf8 = mm_modem_charset_str_to_utf8 (taken_str, -1, charset, FALSE, error);
+ if (!utf8) {
+ g_prefix_error (error, "Couldn't convert to integer number: ");
+ return 0;
+ }
+ }
+
+ if (!mm_get_uint_from_hex_str (utf8 ? utf8 : taken_str, &val)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't convert to integer number: wrong hex encoding: %s", utf8 ? utf8 : taken_str);
+ return 0;
+ }
+
+ return val;
+}
+
gboolean
-mm_cinterion_parse_scfg_test (const gchar *response,
- MMModemCharset charset,
- GArray **supported_bands,
- GError **error)
+mm_cinterion_parse_scfg_test (const gchar *response,
+ MMCinterionModemFamily modem_family,
+ MMModemCharset charset,
+ GArray **supported_bands,
+ MMCinterionRadioBandFormat *format,
+ GError **error)
{
- GRegex *r;
- GMatchInfo *match_info;
- GError *inner_error = NULL;
- GArray *bands = NULL;
+ g_autoptr(GRegex) r1 = NULL;
+ g_autoptr(GMatchInfo) match_info1 = NULL;
+ g_autoptr(GRegex) r2 = NULL;
+ g_autoptr(GMatchInfo) match_info2 = NULL;
+ GError *inner_error = NULL;
+ GArray *bands = NULL;
+
+ g_assert (format);
if (!response) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
return FALSE;
}
- r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\(\"([0-9a-fA-F]*)-([0-9a-fA-F]*)\",.*\\)",
- G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
- 0, NULL);
- g_assert (r != NULL);
+ r1 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\((?:\")?([0-9]*)(?:\")?-(?:\")?([0-9]*)(?:\")?.*\\)",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r1 != NULL);
- g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
- if (!inner_error && g_match_info_matches (match_info)) {
- gchar *maxbandstr;
- guint maxband = 0;
+ g_regex_match_full (r1, response, strlen (response), 0, 0, &match_info1, &inner_error);
+ if (inner_error)
+ goto finish;
+ if (g_match_info_matches (match_info1)) {
+ g_autofree gchar *maxbandstr = NULL;
+ guint maxband = 0;
- maxbandstr = mm_get_string_unquoted_from_match_info (match_info, 2);
- if (maxbandstr) {
- /* Handle charset conversion if the number is given in UCS2 */
- if (charset != MM_MODEM_CHARSET_UNKNOWN)
- maxbandstr = mm_charset_take_and_convert_to_utf8 (maxbandstr, charset);
+ *format = MM_CINTERION_RADIO_BAND_FORMAT_SINGLE;
+ maxbandstr = mm_get_string_unquoted_from_match_info (match_info1, 2);
+ if (maxbandstr)
mm_get_uint_from_str (maxbandstr, &maxband);
- }
if (maxband == 0) {
inner_error = g_error_new (MM_CORE_ERROR,
@@ -124,15 +304,64 @@ mm_cinterion_parse_scfg_test (const gchar *response,
}
}
}
-
- g_free (maxbandstr);
+ goto finish;
}
- if (match_info)
- g_match_info_free (match_info);
- g_regex_unref (r);
+ r2 = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\","
+ "\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\)"
+ "(,*\\(\"?([0-9A-Fa-fx]*)\"?-\"?([0-9A-Fa-fx]*)\"?\\))?",
+ 0, 0, NULL);
+ g_assert (r2 != NULL);
+
+ g_regex_match_full (r2, response, strlen (response), 0, 0, &match_info2, &inner_error);
+ if (inner_error)
+ goto finish;
+
+ while (g_match_info_matches (match_info2)) {
+ g_autofree gchar *techstr = NULL;
+ guint maxband;
+
+ *format = MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE;
+
+ techstr = mm_get_string_unquoted_from_match_info (match_info2, 1);
+ if (g_strcmp0 (techstr, "2G") == 0) {
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family);
+ } else if (g_strcmp0 (techstr, "3G") == 0) {
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family);
+ } else if (g_strcmp0 (techstr, "4G") == 0) {
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family);
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) {
+ maxband = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info2, 6),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (maxband, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family);
+ }
+ } else {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^SCFG=? response");
+ break;
+ }
- if (!bands)
+ g_match_info_next (match_info2, NULL);
+ }
+
+finish:
+ /* set error only if not already given */
+ if (!bands && !inner_error)
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"No valid bands found in ^SCFG=? response");
@@ -149,7 +378,7 @@ mm_cinterion_parse_scfg_test (const gchar *response,
}
/*****************************************************************************/
-/* ^SCFG response parser
+/* ^SCFG response parser (2 types: 2G/3G and LTE)
*
* Example (3G):
* AT^SCFG="Radio/Band"
@@ -162,64 +391,137 @@ mm_cinterion_parse_scfg_test (const gchar *response,
* Example (2G):
* AT+SCFG="Radio/Band"
* ^SCFG: "Radio/Band","3","3"
+ *
+ * Example LTE1 (GSM charset):
+ * AT^SCFG=?
+ * ...
+ * ^SCFG: "Radio/Band/2G","0x00000074"
+ * ^SCFG: "Radio/Band/3G","0x0004019B"
+ * ^SCFG: "Radio/Band/4G","0x080E08DF"
+ * ...
+ * AT^SCFG=?
+ * ...
+ * Example LTE1 (UCS2 charset):
+ * AT^SCFG=?
+ * ...
+ * ^SCFG: "Radio/Band/2G","0030007800300030003000300030003000370034"
+ * ^SCFG: "Radio/Band/3G","0030007800300030003000340030003100390042"
+ * ^SCFG: "Radio/Band/4G","0030007800300038003000450030003800440046"
+ * ...
+ * Example LTE2 (all charsets):
+ * AT^SCFG=?
+ * ...
+ * ^SCFG: "Radio/Band/2G","0000000f"
+ * ^SCFG: "Radio/Band/3G","000400b5"
+ * ^SCFG: "Radio/Band/4G","8a0e00d5","000000e2"
+ * ...
*/
gboolean
-mm_cinterion_parse_scfg_response (const gchar *response,
- MMModemCharset charset,
- GArray **current_bands,
- GError **error)
+mm_cinterion_parse_scfg_response (const gchar *response,
+ MMCinterionModemFamily modem_family,
+ MMModemCharset charset,
+ GArray **current_bands,
+ MMCinterionRadioBandFormat format,
+ GError **error)
{
- GRegex *r;
- GMatchInfo *match_info;
- GError *inner_error = NULL;
- GArray *bands = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ GError *inner_error = NULL;
+ GArray *bands = NULL;
if (!response) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
return FALSE;
}
- r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"?([0-9a-fA-F]*)\"?", 0, 0, NULL);
- g_assert (r != NULL);
-
- if (g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
- gchar *currentstr;
- guint current = 0;
-
- currentstr = mm_get_string_unquoted_from_match_info (match_info, 1);
- if (currentstr) {
- /* Handle charset conversion if the number is given in UCS2 */
- if (charset != MM_MODEM_CHARSET_UNKNOWN)
- currentstr = mm_charset_take_and_convert_to_utf8 (currentstr, charset);
-
- mm_get_uint_from_str (currentstr, &current);
- }
-
- if (current == 0) {
- inner_error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse ^SCFG response");
- } else {
- guint i;
-
- for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
- if (current & cinterion_bands[i].cinterion_band_flag) {
- if (G_UNLIKELY (!bands))
- bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
- g_array_append_val (bands, cinterion_bands[i].mm_band);
+ if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
+ r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"?([0-9a-fA-F]*)\"?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto finish;
+
+ if (g_match_info_matches (match_info)) {
+ g_autofree gchar *currentstr = NULL;
+ guint current = 0;
+
+ currentstr = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (currentstr)
+ mm_get_uint_from_str (currentstr, &current);
+
+ if (current == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^SCFG? response");
+ } else {
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
+ if (current & cinterion_bands[i].cinterion_band_flag) {
+ if (G_UNLIKELY (!bands))
+ bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
+ g_array_append_val (bands, cinterion_bands[i].mm_band);
+ }
}
}
}
+ } else if (format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE) {
+ r = g_regex_new ("\\^SCFG:\\s*\"Radio/Band/([234]G)\",\"?([0-9A-Fa-fx]*)\"?,?\"?([0-9A-Fa-fx]*)?\"?",
+ 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto finish;
+
+ while (g_match_info_matches (match_info)) {
+ g_autofree gchar *techstr = NULL;
+ guint current;
+
+ techstr = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (g_strcmp0 (techstr, "2G") == 0) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_GSM, modem_family);
- g_free (currentstr);
- }
+ } else if (g_strcmp0 (techstr, "3G") == 0) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_UMTS, modem_family);
+ } else if (g_strcmp0 (techstr, "4G") == 0) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 2),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_LOW, modem_family);
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_DEFAULT) {
+ current = take_and_convert_from_matched_string (mm_get_string_unquoted_from_match_info (match_info, 3),
+ charset, modem_family, &inner_error);
+ if (inner_error)
+ break;
+ parse_bands (current, &bands, MM_CINTERION_RB_BLOCK_LTE_HIGH, modem_family);
+ }
+ } else {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^SCFG? response");
+ break;
+ }
- if (match_info)
- g_match_info_free (match_info);
- g_regex_unref (r);
+ g_match_info_next (match_info, NULL);
+ }
+ } else
+ g_assert_not_reached ();
- if (!bands)
+finish:
+ /* set error only if not already given */
+ if (!bands && !inner_error)
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"No valid bands found in ^SCFG response");
@@ -243,43 +545,6 @@ mm_cinterion_parse_scfg_response (const gchar *response,
* +CNMI: (0,1,2),(0,1),(0,2),(0),(1)
*/
-static GArray *
-read_number_list (const gchar *str)
-{
- GError *inner_error = NULL;
- GArray *out = NULL;
- GRegex *r;
- GMatchInfo *match_info;
-
- if (!str)
- return NULL;
-
- r = g_regex_new ("(\\d),?", G_REGEX_UNGREEDY, 0, NULL);
- g_assert (r != NULL);
-
- g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
- while (!inner_error && g_match_info_matches (match_info)) {
- guint aux;
-
- if (mm_get_uint_from_match_info (match_info, 1, &aux)) {
- if (!out)
- out = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
- g_array_append_val (out, aux);
- }
- g_match_info_next (match_info, &inner_error);
- }
-
- if (inner_error) {
- mm_warn ("Unexpected error matching +CNMI response: '%s'", inner_error->message);
- g_error_free (inner_error);
- }
-
- g_match_info_free (match_info);
- g_regex_unref (r);
-
- return out;
-}
-
gboolean
mm_cinterion_parse_cnmi_test (const gchar *response,
GArray **supported_mode,
@@ -292,6 +557,11 @@ mm_cinterion_parse_cnmi_test (const gchar *response,
GRegex *r;
GMatchInfo *match_info;
GError *inner_error = NULL;
+ GArray *tmp_supported_mode = NULL;
+ GArray *tmp_supported_mt = NULL;
+ GArray *tmp_supported_bm = NULL;
+ GArray *tmp_supported_ds = NULL;
+ GArray *tmp_supported_bfr = NULL;
if (!response) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing response");
@@ -309,77 +579,75 @@ mm_cinterion_parse_cnmi_test (const gchar *response,
gchar *str;
str = mm_get_string_unquoted_from_match_info (match_info, 1);
- *supported_mode = read_number_list (str);
+ tmp_supported_mode = mm_parse_uint_list (str, &inner_error);
g_free (str);
+ if (inner_error)
+ goto out;
}
if (supported_mt) {
gchar *str;
str = mm_get_string_unquoted_from_match_info (match_info, 2);
- *supported_mt = read_number_list (str);
+ tmp_supported_mt = mm_parse_uint_list (str, &inner_error);
g_free (str);
+ if (inner_error)
+ goto out;
}
if (supported_bm) {
gchar *str;
str = mm_get_string_unquoted_from_match_info (match_info, 3);
- *supported_bm = read_number_list (str);
+ tmp_supported_bm = mm_parse_uint_list (str, &inner_error);
g_free (str);
+ if (inner_error)
+ goto out;
}
if (supported_ds) {
gchar *str;
str = mm_get_string_unquoted_from_match_info (match_info, 4);
- *supported_ds = read_number_list (str);
+ tmp_supported_ds = mm_parse_uint_list (str, &inner_error);
g_free (str);
+ if (inner_error)
+ goto out;
}
if (supported_bfr) {
gchar *str;
str = mm_get_string_unquoted_from_match_info (match_info, 5);
- *supported_bfr = read_number_list (str);
+ tmp_supported_bfr = mm_parse_uint_list (str, &inner_error);
g_free (str);
+ if (inner_error)
+ goto out;
}
}
- if (match_info)
- g_match_info_free (match_info);
- g_regex_unref (r);
+out:
- if ((supported_mode && *supported_mode == NULL) ||
- (supported_mt && *supported_mt == NULL) ||
- (supported_bm && *supported_bm == NULL) ||
- (supported_ds && *supported_ds == NULL) ||
- (supported_bfr && *supported_bfr == NULL))
- inner_error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Error parsing +CNMI=? response");
+ g_match_info_free (match_info);
+ g_regex_unref (r);
if (inner_error) {
- if (supported_mode && *supported_mode) {
- g_array_unref (*supported_mode);
- *supported_mode = NULL;
- }
- if (supported_mt && *supported_mt) {
- g_array_unref (*supported_mt);
- *supported_mt = NULL;
- }
- if (supported_bm && *supported_bm) {
- g_array_unref (*supported_bm);
- *supported_bm = NULL;
- }
- if (supported_ds && *supported_ds) {
- g_array_unref (*supported_ds);
- *supported_ds = NULL;
- }
- if (supported_bfr && *supported_bfr) {
- g_array_unref (*supported_bfr);
- *supported_bfr = NULL;
- }
+ g_clear_pointer (&tmp_supported_mode, g_array_unref);
+ g_clear_pointer (&tmp_supported_mt, g_array_unref);
+ g_clear_pointer (&tmp_supported_bm, g_array_unref);
+ g_clear_pointer (&tmp_supported_ds, g_array_unref);
+ g_clear_pointer (&tmp_supported_bfr, g_array_unref);
g_propagate_error (error, inner_error);
return FALSE;
}
+ if (supported_mode)
+ *supported_mode = tmp_supported_mode;
+ if (supported_mt)
+ *supported_mt = tmp_supported_mt;
+ if (supported_bm)
+ *supported_bm = tmp_supported_bm;
+ if (supported_ds)
+ *supported_ds = tmp_supported_ds;
+ if (supported_bfr)
+ *supported_bfr = tmp_supported_bfr;
+
return TRUE;
}
@@ -387,52 +655,101 @@ mm_cinterion_parse_cnmi_test (const gchar *response,
/* Build Cinterion-specific band value */
gboolean
-mm_cinterion_build_band (GArray *bands,
- guint supported,
- gboolean only_2g,
- guint *out_band,
- GError **error)
+mm_cinterion_build_band (GArray *bands,
+ guint *supported,
+ gboolean only_2g,
+ MMCinterionRadioBandFormat format,
+ MMCinterionModemFamily modem_family,
+ guint *out_band,
+ GError **error)
{
- guint band = 0;
+ guint band[MM_CINTERION_RB_BLOCK_N] = { 0 };
- /* The special case of ANY should be treated separately. */
- if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
- band = supported;
- } else {
- guint i;
+ if (format == MM_CINTERION_RADIO_BAND_FORMAT_SINGLE) {
+ /* The special case of ANY should be treated separately. */
+ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ if (supported)
+ band[MM_CINTERION_RB_BLOCK_LEGACY] = supported[MM_CINTERION_RB_BLOCK_LEGACY];
+ } else {
+ guint i;
- for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
- guint j;
+ for (i = 0; i < G_N_ELEMENTS (cinterion_bands); i++) {
+ guint j;
- for (j = 0; j < bands->len; j++) {
- if (g_array_index (bands, MMModemBand, j) == cinterion_bands[i].mm_band) {
- band |= cinterion_bands[i].cinterion_band_flag;
- break;
+ for (j = 0; j < bands->len; j++) {
+ if (g_array_index (bands, MMModemBand, j) == cinterion_bands[i].mm_band) {
+ band[MM_CINTERION_RB_BLOCK_LEGACY] |= cinterion_bands[i].cinterion_band_flag;
+ break;
+ }
}
}
+
+ /* 2G-only modems only support a subset of the possible band
+ * combinations. Detect it early and error out.
+ */
+ if (only_2g && !VALIDATE_2G_BAND (band[MM_CINTERION_RB_BLOCK_LEGACY]))
+ band[MM_CINTERION_RB_BLOCK_LEGACY] = 0;
}
- /* 2G-only modems only support a subset of the possible band
- * combinations. Detect it early and error out.
- */
- if (only_2g && !VALIDATE_2G_BAND (band))
- band = 0;
- }
+ if (band[MM_CINTERION_RB_BLOCK_LEGACY] == 0) {
+ g_autofree gchar *bands_string = NULL;
- if (band == 0) {
- gchar *bands_string;
+ bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands->data, bands->len);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "The given band combination is not supported: '%s'",
+ bands_string);
+ return FALSE;
+ }
- bands_string = mm_common_build_bands_string ((MMModemBand *)bands->data, bands->len);
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "The given band combination is not supported: '%s'",
- bands_string);
- g_free (bands_string);
- return FALSE;
+ } else { /* format == MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE */
+ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ if (supported)
+ memcpy (band, supported, sizeof (guint) * MM_CINTERION_RB_BLOCK_N);
+ } else {
+ guint i;
+ const CinterionBandEx *ref_bands;
+ guint nb_ref_bands;
+
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+ ref_bands = cinterion_bands_imt;
+ nb_ref_bands = G_N_ELEMENTS (cinterion_bands_imt);
+ } else {
+ ref_bands = cinterion_bands_ex;
+ nb_ref_bands = G_N_ELEMENTS (cinterion_bands_ex);
+ }
+
+ for (i = 0; i < nb_ref_bands; i++) {
+ guint j;
+
+ for (j = 0; j < bands->len; j++) {
+ if (g_array_index (bands, MMModemBand, j) == ref_bands[i].mm_band) {
+ band[ref_bands[i].cinterion_band_block] |= ref_bands[i].cinterion_band_flag;
+ break;
+ }
+ }
+ }
+ }
+
+ /* this modem family does not allow disabling all bands in a given technology through this command */
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT &&
+ (!band[MM_CINTERION_RB_BLOCK_GSM] ||
+ !band[MM_CINTERION_RB_BLOCK_UMTS] ||
+ !band[MM_CINTERION_RB_BLOCK_LTE_LOW])) {
+ g_autofree gchar *bands_string = NULL;
+
+ bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands->data, bands->len);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "The given band combination is not supported: '%s'",
+ bands_string);
+ return FALSE;
+ }
}
- *out_band = band;
+ memcpy (out_band, band, sizeof (guint) * MM_CINTERION_RB_BLOCK_N);
return TRUE;
}
@@ -458,7 +775,7 @@ mm_cinterion_parse_sind_response (const gchar *response,
r = g_regex_new ("\\^SIND:\\s*(.*),(\\d+),(\\d+)(\\r\\n)?", 0, 0, NULL);
g_assert (r != NULL);
- if (g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
+ if (g_regex_match (r, response, 0, &match_info)) {
if (description) {
*description = mm_get_string_unquoted_from_match_info (match_info, 1);
if (*description == NULL)
@@ -471,8 +788,7 @@ mm_cinterion_parse_sind_response (const gchar *response,
} else
errors++;
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
if (errors > 0) {
@@ -482,3 +798,876 @@ mm_cinterion_parse_sind_response (const gchar *response,
return TRUE;
}
+
+/*****************************************************************************/
+/* ^SWWAN read parser
+ *
+ * Description: Parses <cid>, <state>[, <WWAN adapter>] or CME ERROR from SWWAN.
+ *
+ * The method returns a MMSwwanState with the connection status of a single
+ * PDP context, the one being queried via the cid given as input.
+ *
+ * Note that we use CID for matching because the WWAN adapter field is optional
+ * it seems.
+ *
+ * Read Command
+ * AT^SWWAN?
+ * Response(s)
+ * [^SWWAN: <cid>, <state>[, <WWAN adapter>]]
+ * [^SWWAN: ...]
+ * OK
+ * ERROR
+ * +CME ERROR: <err>
+ *
+ * Examples:
+ * OK - If no WWAN connection is active, then read command just returns OK
+ * ^SWWAN: 3,1,1 - 3rd PDP Context, Activated, First WWAN Adaptor
+ * +CME ERROR: ? -
+ */
+
+enum {
+ MM_SWWAN_STATE_DISCONNECTED = 0,
+ MM_SWWAN_STATE_CONNECTED = 1,
+};
+
+MMBearerConnectionStatus
+mm_cinterion_parse_swwan_response (const gchar *response,
+ guint cid,
+ gpointer log_object,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ MMBearerConnectionStatus status;
+
+ g_assert (response);
+
+ /* If no WWAN connection is active, then ^SWWAN read command just returns OK
+ * (which we receive as an empty string) */
+ if (!response[0])
+ return MM_BEARER_CONNECTION_STATUS_DISCONNECTED;
+
+ if (!g_str_has_prefix (response, "^SWWAN:")) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^SWWAN response: '%s'", response);
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ }
+
+ r = g_regex_new ("\\^SWWAN:\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d+))?(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ status = MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ while (!inner_error && g_match_info_matches (match_info)) {
+ guint read_state;
+ guint read_cid;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &read_cid))
+ mm_obj_warn (log_object, "couldn't read cid in ^SWWAN response: %s", response);
+ else if (!mm_get_uint_from_match_info (match_info, 2, &read_state))
+ mm_obj_warn (log_object, "couldn't read state in ^SWWAN response: %s", response);
+ else if (read_cid == cid) {
+ if (read_state == MM_SWWAN_STATE_CONNECTED) {
+ status = MM_BEARER_CONNECTION_STATUS_CONNECTED;
+ break;
+ }
+ if (read_state == MM_SWWAN_STATE_DISCONNECTED) {
+ status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED;
+ break;
+ }
+ mm_obj_warn (log_object, "invalid state read in ^SWWAN response: %u", read_state);
+ break;
+ }
+ g_match_info_next (match_info, &inner_error);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No state returned for CID %u", cid);
+
+ return status;
+}
+
+/*****************************************************************************/
+/* ^SGAUTH response parser */
+
+/* at^sgauth?
+ * ^SGAUTH: 1,2,"vf"
+ * ^SGAUTH: 3,0,""
+ * ^SGAUTH: 4,0
+ *
+ * OK
+ */
+
+gboolean
+mm_cinterion_parse_sgauth_response (const gchar *response,
+ guint cid,
+ MMBearerAllowedAuth *out_auth,
+ gchar **out_username,
+ GError **error)
+{
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+
+ r = g_regex_new ("\\^SGAUTH:\\s*(\\d+),(\\d+),?\"?([a-zA-Z0-9_-]+)?\"?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL);
+ while (g_match_info_matches (match_info)) {
+ guint sgauth_cid = 0;
+
+ if (mm_get_uint_from_match_info (match_info, 1, &sgauth_cid) &&
+ (sgauth_cid == cid)) {
+ guint cinterion_auth_type = 0;
+
+ mm_get_uint_from_match_info (match_info, 2, &cinterion_auth_type);
+ *out_auth = mm_auth_type_from_cinterion_auth_type (cinterion_auth_type);
+ *out_username = mm_get_string_unquoted_from_match_info (match_info, 3);
+ return TRUE;
+ }
+ g_match_info_next (match_info, NULL);
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Auth settings for context %u not found", cid);
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* ^SMONG response parser */
+
+static gboolean
+get_access_technology_from_smong_gprs_status (guint gprs_status,
+ MMModemAccessTechnology *out,
+ GError **error)
+{
+ switch (gprs_status) {
+ case 0:
+ *out = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ return TRUE;
+ case 1:
+ case 2:
+ *out = MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
+ return TRUE;
+ case 3:
+ case 4:
+ *out = MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
+ return TRUE;
+ default:
+ break;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't get network capabilities, "
+ "unsupported GPRS status value: '%u'",
+ gprs_status);
+ return FALSE;
+}
+
+gboolean
+mm_cinterion_parse_smong_response (const gchar *response,
+ MMModemAccessTechnology *access_tech,
+ GError **error)
+{
+ guint value = 0;
+ GError *inner_error = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GRegex) regex = NULL;
+
+ /* The AT^SMONG command returns a cell info table, where the second
+ * column identifies the "GPRS status", which is exactly what we want.
+ * So we'll try to read that second number in the values row.
+ *
+ * AT^SMONG
+ * GPRS Monitor
+ * BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell #
+ * 0776 1 - - 214 03 2 00 01
+ * OK
+ */
+ regex = g_regex_new (".*GPRS Monitor(?:\r\n)*"
+ "BCCH\\s*G.*\\r\\n"
+ "\\s*(\\d+)\\s*(\\d+)\\s*",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (regex);
+
+ g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, &inner_error);
+
+ if (inner_error) {
+ g_prefix_error (&inner_error, "Failed to match AT^SMONG response: ");
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (!g_match_info_matches (match_info) || !mm_get_uint_from_match_info (match_info, 2, &value)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't read 'GPRS status' field from AT^SMONG response");
+ return FALSE;
+ }
+
+ return get_access_technology_from_smong_gprs_status (value, access_tech, error);
+}
+
+/*****************************************************************************/
+/* ^SIND psinfo helper */
+
+MMModemAccessTechnology
+mm_cinterion_get_access_technology_from_sind_psinfo (guint val,
+ gpointer log_object)
+{
+ switch (val) {
+ case 0:
+ return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ case 1:
+ case 2:
+ return MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
+ case 3:
+ case 4:
+ return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
+ case 5:
+ case 6:
+ return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ case 7:
+ case 8:
+ return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
+ case 9:
+ case 10:
+ return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
+ case 16:
+ case 17:
+ return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ default:
+ mm_obj_dbg (log_object, "unable to identify access technology from psinfo reported value: %u", val);
+ return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+/* ^SLCC psinfo helper */
+
+GRegex *
+mm_cinterion_get_slcc_regex (void)
+{
+ /* The list of active calls displayed with this URC will always be terminated
+ * with an empty line preceded by prefix "^SLCC: ", in order to indicate the end
+ * of the list.
+ */
+ return g_regex_new ("\\r\\n(\\^SLCC: .*\\r\\n)*\\^SLCC: \\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+static void
+cinterion_call_info_free (MMCallInfo *info)
+{
+ if (!info)
+ return;
+ g_free (info->number);
+ g_slice_free (MMCallInfo, info);
+}
+
+gboolean
+mm_cinterion_parse_slcc_list (const gchar *str,
+ gpointer log_object,
+ GList **out_list,
+ GError **error)
+{
+ GRegex *r;
+ GList *list = NULL;
+ GError *inner_error = NULL;
+ GMatchInfo *match_info = NULL;
+
+ static const MMCallDirection cinterion_call_direction[] = {
+ [0] = MM_CALL_DIRECTION_OUTGOING,
+ [1] = MM_CALL_DIRECTION_INCOMING,
+ };
+
+ static const MMCallState cinterion_call_state[] = {
+ [0] = MM_CALL_STATE_ACTIVE,
+ [1] = MM_CALL_STATE_HELD,
+ [2] = MM_CALL_STATE_DIALING, /* Dialing (MOC) */
+ [3] = MM_CALL_STATE_RINGING_OUT, /* Alerting (MOC) */
+ [4] = MM_CALL_STATE_RINGING_IN, /* Incoming (MTC) */
+ [5] = MM_CALL_STATE_WAITING, /* Waiting (MTC) */
+ };
+
+ g_assert (out_list);
+
+ /*
+ * 1 2 3 4 5 6 7 8 9
+ * ^SLCC: <idx>, <dir>, <stat>, <mode>, <mpty>, <Reserved>[, <number>, <type>[,<alpha>]]
+ * [^SLCC: <idx>, <dir>, <stat>, <mode>, <mpty>, <Reserved>[, <number>, <type>[,<alpha>]]]
+ * [... ]
+ * ^SLCC :
+ */
+
+ r = g_regex_new ("\\^SLCC:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)" /* mandatory fields */
+ "(?:,\\s*([^,]*),\\s*(\\d+)" /* number and type */
+ "(?:,\\s*([^,]*)" /* alpha */
+ ")?)?$",
+ G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF,
+ G_REGEX_MATCH_NEWLINE_CRLF,
+ NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ /* Parse the results */
+ while (g_match_info_matches (match_info)) {
+ MMCallInfo *call_info;
+ guint aux;
+
+ call_info = g_slice_new0 (MMCallInfo);
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &call_info->index)) {
+ mm_obj_warn (log_object, "couldn't parse call index from ^SLCC line");
+ goto next;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &aux) ||
+ (aux >= G_N_ELEMENTS (cinterion_call_direction))) {
+ mm_obj_warn (log_object, "couldn't parse call direction from ^SLCC line");
+ goto next;
+ }
+ call_info->direction = cinterion_call_direction[aux];
+
+ if (!mm_get_uint_from_match_info (match_info, 3, &aux) ||
+ (aux >= G_N_ELEMENTS (cinterion_call_state))) {
+ mm_obj_warn (log_object, "couldn't parse call state from ^SLCC line");
+ goto next;
+ }
+ call_info->state = cinterion_call_state[aux];
+
+ if (g_match_info_get_match_count (match_info) >= 8)
+ call_info->number = mm_get_string_unquoted_from_match_info (match_info, 7);
+
+ list = g_list_append (list, call_info);
+ call_info = NULL;
+
+ next:
+ cinterion_call_info_free (call_info);
+ g_match_info_next (match_info, NULL);
+ }
+
+out:
+ g_clear_pointer (&match_info, g_match_info_free);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ mm_cinterion_call_info_list_free (list);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *out_list = list;
+
+ return TRUE;
+}
+
+void
+mm_cinterion_call_info_list_free (GList *call_info_list)
+{
+ g_list_free_full (call_info_list, (GDestroyNotify) cinterion_call_info_free);
+}
+
+/*****************************************************************************/
+/* +CTZU URC helpers */
+
+GRegex *
+mm_cinterion_get_ctzu_regex (void)
+{
+ /*
+ * From PLS-8 AT command spec:
+ * +CTZU:<nitzUT>, <nitzTZ>[, <nitzDST>]
+ * E.g.:
+ * +CTZU: "19/07/09,10:19:15",+08,1
+ */
+
+ return g_regex_new ("\\r\\n\\+CTZU:\\s*\"(\\d+)\\/(\\d+)\\/(\\d+),(\\d+):(\\d+):(\\d+)\",([\\-\\+\\d]+)(?:,(\\d+))?(?:\\r\\n)?",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+gboolean
+mm_cinterion_parse_ctzu_urc (GMatchInfo *match_info,
+ gchar **iso8601p,
+ MMNetworkTimezone **tzp,
+ GError **error)
+{
+ guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0, dst = 0;
+ gint tz = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &year) ||
+ !mm_get_uint_from_match_info (match_info, 2, &month) ||
+ !mm_get_uint_from_match_info (match_info, 3, &day) ||
+ !mm_get_uint_from_match_info (match_info, 4, &hour) ||
+ !mm_get_uint_from_match_info (match_info, 5, &minute) ||
+ !mm_get_uint_from_match_info (match_info, 6, &second) ||
+ !mm_get_int_from_match_info (match_info, 7, &tz)) {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse +CTZU URC");
+ return FALSE;
+ }
+
+ /* adjust year */
+ if (year < 100)
+ year += 2000;
+
+ /*
+ * tz = timezone offset in 15 minute intervals
+ */
+ if (iso8601p) {
+ /* Return ISO-8601 format date/time string */
+ *iso8601p = mm_new_iso8601_time (year, month, day, hour,
+ minute, second,
+ TRUE, tz * 15);
+ }
+
+ if (tzp) {
+ *tzp = mm_network_timezone_new ();
+ mm_network_timezone_set_offset (*tzp, tz * 15);
+ }
+
+ /* dst flag is optional in the URC
+ *
+ * tz = timezone offset in 15 minute intervals
+ * dst = daylight adjustment, 0 = none, 1 = 1 hour, 2 = 2 hours
+ */
+ if (tzp && mm_get_uint_from_match_info (match_info, 8, &dst))
+ mm_network_timezone_set_dst_offset (*tzp, dst * 60);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* ^SMONI response parser */
+
+gboolean
+mm_cinterion_parse_smoni_query_response (const gchar *response,
+ MMCinterionRadioGen *out_tech,
+ gdouble *out_rssi,
+ gdouble *out_ecn0,
+ gdouble *out_rscp,
+ gdouble *out_rsrp,
+ gdouble *out_rsrq,
+ GError **error)
+{
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GRegex) pre = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GMatchInfo) match_info_pre = NULL;
+ GError *inner_error = NULL;
+ MMCinterionRadioGen tech = MM_CINTERION_RADIO_GEN_NONE;
+ gdouble rssi = -G_MAXDOUBLE;
+ gdouble ecn0 = -G_MAXDOUBLE;
+ gdouble rscp = -G_MAXDOUBLE;
+ gdouble rsrq = -G_MAXDOUBLE;
+ gdouble rsrp = -G_MAXDOUBLE;
+ gboolean success = FALSE;
+
+ g_assert (out_tech);
+ g_assert (out_rssi);
+ g_assert (out_ecn0);
+ g_assert (out_rscp);
+ g_assert (out_rsrp);
+ g_assert (out_rsrq);
+ g_assert (out_rssi);
+
+ /* Possible Responses:
+ * 2G
+ * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,Conn_state // registered
+ * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod // searching
+ * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,PWR,RXLev,ARFCN,TS,timAdv,dBm,Q,ChMod // limsrv
+ * ^SMONI: 2G,ARFCN,BCCH,MCC,MNC,LAC,cell,C1,C2,NCC,BCC,GPRS,ARFCN,TS,timAdv,dBm,Q,ChMod // dedicated channel
+ *
+ * ^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN
+ * ^^^
+ * ^SMONI: 2G,SEARCH,SEARCH
+ * ^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV
+ * ^^^ ^^^^ RXLev dBm
+ * ^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR
+ * ^^^ ^^^ dBm: Receiving level of the traffic channel carrier in dBm
+ * BCCH: Receiving level of the BCCH carrier in dBm (level is limited from -110dBm to -47dBm)
+ * -> rssi for 2G, directly without mm_3gpp_rxlev_to_rssi
+ *
+ *
+ * 3G
+ * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,,Conn_state",
+ * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA",
+ * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA",
+ * ^SMONI: 3G,UARFCN,PSC,EC/n0,RSCP,MCC,MNC,LAC,cell,SQual,SRxLev,PhysCh, SF,Slot,EC/n0,RSCP,ComMod,HSUPA,HSDPA",
+ *
+ * ^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN
+ * ^^^^ ^^^
+ * ^SMONI: 3G,SEARCH,SEARCH
+ * ^SMONI: 3G,10564,96,-7.5,-79,262,02,0143,00228FF,-92,-78,LIMSRV
+ * ^^^^ ^^^
+ * ^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06
+ * ^^ ^^^
+ * RSCP: Received Signal Code Power in dBm -> no need for mm_3gpp_rscp_level_to_rscp
+ * EC/n0: EC/n0 Carrier to noise ratio in dB = measured Ec/Io value in dB. Please refer to 3GPP 25.133, section 9.1.2.3, Table 9.9 for details on the mapping from EC/n0 to EC/Io.
+ * -> direct value, without need for mm_3gpp_ecn0_level_to_ecio
+ *
+ *
+ * 4G
+ * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+ * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+ * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,Srxlev,RSRP,RSRQ,Conn_state
+ * ^SMONI: 4G,EARFCN,Band,DL bandwidth,UL bandwidth,Mode,MCC,MNC,TAC,Global Cell ID,Physical Cell ID,TX_power,RSRP,RSRQ,Conn_state
+ *
+ * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN
+ * ^^^ ^^
+ * ^SMONI: 4G,SEARCH
+ * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,LIMSRV
+ * ^^^ ^^
+ * ^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-94,-7,CONN
+ * ^^^ ^^
+ * RSRP Reference Signal Received Power (see 3GPP 36.214 Section 5.1.1.) -> directly the value without mm_3gpp_rsrq_level_to_rsrp
+ * RSRQ Reference Signal Received Quality (see 3GPP 36.214 Section 5.1.2.) -> directly the value without mm_3gpp_rsrq_level_to_rsrq
+ */
+ if (g_regex_match_simple ("\\^SMONI:\\s*[234]G,SEARCH", response, 0, 0)) {
+ success = TRUE;
+ goto out;
+ }
+ pre = g_regex_new ("\\^SMONI:\\s*([234])", 0, 0, NULL);
+ g_assert (pre != NULL);
+ g_regex_match_full (pre, response, strlen (response), 0, 0, &match_info_pre, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info_pre)) {
+ if (!mm_get_uint_from_match_info (match_info_pre, 1, &tech)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read tech");
+ goto out;
+ }
+ #define FLOAT "([-+]?[0-9]+\\.?[0-9]*)"
+ switch (tech) {
+ case MM_CINTERION_RADIO_GEN_2G:
+ r = g_regex_new ("\\^SMONI:\\s*2G,(\\d+),"FLOAT, 0, 0, NULL);
+ g_assert (r != NULL);
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ /* skip ARFCN */
+ if (!mm_get_double_from_match_info (match_info, 2, &rssi)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BCCH=rssi");
+ goto out;
+ }
+ }
+ break;
+ case MM_CINTERION_RADIO_GEN_3G:
+ r = g_regex_new ("\\^SMONI:\\s*3G,(\\d+),(\\d+),"FLOAT","FLOAT, 0, 0, NULL);
+ g_assert (r != NULL);
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ /* skip UARFCN */
+ /* skip PSC (Primary scrambling code) */
+ if (!mm_get_double_from_match_info (match_info, 3, &ecn0)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read EcN0");
+ goto out;
+ }
+ if (!mm_get_double_from_match_info (match_info, 4, &rscp)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP");
+ goto out;
+ }
+ }
+ break;
+ case MM_CINTERION_RADIO_GEN_4G:
+ r = g_regex_new ("\\^SMONI:\\s*4G,(\\d+),(\\d+),(\\d+),(\\d+),(\\w+),(\\d+),(\\d+),(\\w+),(\\w+),(\\d+),([^,]*),"FLOAT","FLOAT, 0, 0, NULL);
+ g_assert (r != NULL);
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ /* skip EARFCN */
+ /* skip Band */
+ /* skip DL bandwidth */
+ /* skip UL bandwidth */
+ /* skip Mode */
+ /* skip MCC */
+ /* skip MNC */
+ /* skip TAC */
+ /* skip Global Cell ID */
+ /* skip Physical Cell ID */
+ /* skip Srxlev/TX_power */
+ if (!mm_get_double_from_match_info (match_info, 12, &rsrp)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ");
+ goto out;
+ }
+ if (!mm_get_double_from_match_info (match_info, 13, &rsrq)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP");
+ goto out;
+ }
+ }
+ break;
+ case MM_CINTERION_RADIO_GEN_NONE:
+ default:
+ goto out;
+ }
+ #undef FLOAT
+ success = TRUE;
+ }
+
+out:
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (!success) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^SMONI response: %s", response);
+ return FALSE;
+ }
+
+ *out_tech = tech;
+ *out_rssi = rssi;
+ *out_rscp = rscp;
+ *out_ecn0 = ecn0;
+ *out_rsrq = rsrq;
+ *out_rsrp = rsrp;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Get extended signal information */
+
+gboolean
+mm_cinterion_smoni_response_to_signal_info (const gchar *response,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ GError **error)
+{
+ MMCinterionRadioGen tech = MM_CINTERION_RADIO_GEN_NONE;
+ gdouble rssi = MM_SIGNAL_UNKNOWN;
+ gdouble ecn0 = MM_SIGNAL_UNKNOWN;
+ gdouble rscp = MM_SIGNAL_UNKNOWN;
+ gdouble rsrq = MM_SIGNAL_UNKNOWN;
+ gdouble rsrp = MM_SIGNAL_UNKNOWN;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+
+ if (!mm_cinterion_parse_smoni_query_response (response,
+ &tech, &rssi,
+ &ecn0, &rscp,
+ &rsrp, &rsrq,
+ error))
+ return FALSE;
+
+ switch (tech) {
+ case MM_CINTERION_RADIO_GEN_2G:
+ gsm = mm_signal_new ();
+ mm_signal_set_rssi (gsm, rssi);
+ break;
+ case MM_CINTERION_RADIO_GEN_3G:
+ umts = mm_signal_new ();
+ mm_signal_set_rscp (umts, rscp);
+ mm_signal_set_ecio (umts, ecn0); /* UMTS EcIo (assumed EcN0) */
+ break;
+ case MM_CINTERION_RADIO_GEN_4G:
+ lte = mm_signal_new ();
+ mm_signal_set_rsrp (lte, rsrp);
+ mm_signal_set_rsrq (lte, rsrq);
+ break;
+ case MM_CINTERION_RADIO_GEN_NONE: /* not registered, searching */
+ break; /* no error case */
+ default: /* should not happen, so if it does, error */
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't build detailed signal info");
+ return FALSE;
+ }
+
+ if (out_gsm)
+ *out_gsm = gsm;
+ if (out_umts)
+ *out_umts = umts;
+ if (out_lte)
+ *out_lte = lte;
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* provider cfg information to CID number for EPS initial settings */
+
+/*
+ * at^scfg="MEopMode/Prov/Cfg"
+ * ^SCFG: "MEopMode/Prov/Cfg","vdfde"
+ * ^SCFG: "MEopMode/Prov/Cfg","attus"
+ * ^SCFG: "MEopMode/Prov/Cfg","2" -> PLS8-X vzw
+ * ^SCFG: "MEopMode/Prov/Cfg","vzwdcus" -> PLAS9-x vzw
+ * ^SCFG: "MEopMode/Prov/Cfg","tmode" -> t-mob germany
+ * OK
+ */
+gboolean
+mm_cinterion_provcfg_response_to_cid (const gchar *response,
+ MMCinterionModemFamily modem_family,
+ MMModemCharset charset,
+ gpointer log_object,
+ gint *cid,
+ GError **error)
+{
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autofree gchar *mno = NULL;
+ GError *inner_error = NULL;
+
+ r = g_regex_new ("\\^SCFG:\\s*\"MEopMode/Prov/Cfg\",\\s*\"([0-9a-zA-Z*]*)\"", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+
+ if (inner_error) {
+ g_prefix_error (&inner_error, "Failed to match Prov/Cfg response: ");
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't match Prov/Cfg response");
+ return FALSE;
+ }
+
+ mno = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (mno && modem_family == MM_CINTERION_MODEM_FAMILY_IMT) {
+ gchar *mno_utf8;
+
+ mno_utf8 = mm_modem_charset_str_to_utf8 (mno, -1, charset, FALSE, error);
+ if (!mno_utf8)
+ return FALSE;
+ g_free (mno);
+ mno = mno_utf8;
+ }
+ mm_obj_dbg (log_object, "current mno: %s", mno ? mno : "none");
+
+ /* for Cinterion LTE modules, some CID numbers have special meaning.
+ * This is dictated by the chipset and by the MNO:
+ * - the chipset uses a special one, CID 1, as a LTE combined attach chipset
+ * - the MNOs can define the sequence and number of APN to be used for their network.
+ * This takes priority over the chipset preferences, and therefore for some of them
+ * the CID for the initial EPS context must be changed.
+ */
+ if (g_strcmp0 (mno, "2") == 0 || g_strcmp0 (mno, "vzwdcus") == 0)
+ *cid = 3;
+ else if (g_strcmp0 (mno, "tmode") == 0)
+ *cid = 2;
+ else
+ *cid = 1;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Auth related helpers */
+
+typedef enum {
+ BEARER_CINTERION_AUTH_UNKNOWN = -1,
+ BEARER_CINTERION_AUTH_NONE = 0,
+ BEARER_CINTERION_AUTH_PAP = 1,
+ BEARER_CINTERION_AUTH_CHAP = 2,
+ BEARER_CINTERION_AUTH_MSCHAPV2 = 3,
+} BearerCinterionAuthType;
+
+static BearerCinterionAuthType
+parse_auth_type (MMBearerAllowedAuth mm_auth)
+{
+ switch (mm_auth) {
+ case MM_BEARER_ALLOWED_AUTH_NONE:
+ return BEARER_CINTERION_AUTH_NONE;
+ case MM_BEARER_ALLOWED_AUTH_PAP:
+ return BEARER_CINTERION_AUTH_PAP;
+ case MM_BEARER_ALLOWED_AUTH_CHAP:
+ return BEARER_CINTERION_AUTH_CHAP;
+ case MM_BEARER_ALLOWED_AUTH_MSCHAPV2:
+ return BEARER_CINTERION_AUTH_MSCHAPV2;
+ case MM_BEARER_ALLOWED_AUTH_UNKNOWN:
+ case MM_BEARER_ALLOWED_AUTH_MSCHAP:
+ case MM_BEARER_ALLOWED_AUTH_EAP:
+ default:
+ return BEARER_CINTERION_AUTH_UNKNOWN;
+ }
+}
+
+MMBearerAllowedAuth
+mm_auth_type_from_cinterion_auth_type (guint cinterion_auth)
+{
+ switch (cinterion_auth) {
+ case BEARER_CINTERION_AUTH_NONE:
+ return MM_BEARER_ALLOWED_AUTH_NONE;
+ case BEARER_CINTERION_AUTH_PAP:
+ return MM_BEARER_ALLOWED_AUTH_PAP;
+ case BEARER_CINTERION_AUTH_CHAP:
+ return MM_BEARER_ALLOWED_AUTH_CHAP;
+ default:
+ return MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ }
+}
+
+/* Cinterion authentication is done with the command AT^SGAUTH,
+ whose syntax depends on the modem family, as follow:
+ - AT^SGAUTH=<cid>[, <auth_type>[, <user>, <passwd>]] for the IMT family
+ - AT^SGAUTH=<cid>[, <auth_type>[, <passwd>, <user>]] for the rest */
+gchar *
+mm_cinterion_build_auth_string (gpointer log_object,
+ MMCinterionModemFamily modem_family,
+ MMBearerProperties *config,
+ guint cid)
+{
+ MMBearerAllowedAuth auth;
+ BearerCinterionAuthType encoded_auth = BEARER_CINTERION_AUTH_UNKNOWN;
+ gboolean has_user;
+ gboolean has_passwd;
+ const gchar *user;
+ const gchar *passwd;
+ g_autofree gchar *quoted_user = NULL;
+ g_autofree gchar *quoted_passwd = NULL;
+
+ user = mm_bearer_properties_get_user (config);
+ passwd = mm_bearer_properties_get_password (config);
+ auth = mm_bearer_properties_get_allowed_auth (config);
+
+ has_user = (user && user[0]);
+ has_passwd = (passwd && passwd[0]);
+ encoded_auth = parse_auth_type (auth);
+
+ /* When 'none' requested, we won't require user/password */
+ if (encoded_auth == BEARER_CINTERION_AUTH_NONE) {
+ if (has_user || has_passwd)
+ mm_obj_warn (log_object, "APN user/password given but 'none' authentication requested");
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT)
+ return g_strdup_printf ("^SGAUTH=%u,%d,\"\",\"\"", cid, encoded_auth);
+ return g_strdup_printf ("^SGAUTH=%u,%d", cid, encoded_auth);
+ }
+
+ /* No explicit auth type requested? */
+ if (encoded_auth == BEARER_CINTERION_AUTH_UNKNOWN) {
+ /* If no user/passwd given, do nothing */
+ if (!has_user && !has_passwd)
+ return NULL;
+
+ /* If user/passwd given, default to CHAP (more common than PAP) */
+ mm_obj_dbg (log_object, "APN user/password given but no authentication type explicitly requested: defaulting to 'CHAP'");
+ encoded_auth = BEARER_CINTERION_AUTH_CHAP;
+ }
+
+ quoted_user = mm_port_serial_at_quote_string (user ? user : "");
+ quoted_passwd = mm_port_serial_at_quote_string (passwd ? passwd : "");
+
+ if (modem_family == MM_CINTERION_MODEM_FAMILY_IMT)
+ return g_strdup_printf ("^SGAUTH=%u,%d,%s,%s",
+ cid,
+ encoded_auth,
+ quoted_user,
+ quoted_passwd);
+
+ return g_strdup_printf ("^SGAUTH=%u,%d,%s,%s",
+ cid,
+ encoded_auth,
+ quoted_passwd,
+ quoted_user);
+}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h
index d37bcb07..7bbd789a 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.h
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.h
@@ -11,28 +11,66 @@
* GNU General Public License for more details:
*
* Copyright (C) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2016 Trimble Navigation Limited
+ * Copyright (C) 2016 Matthew Stanger <matthew_stanger@trimble.com>
+ * Copyright (C) 2019 Purism SPC
*/
#ifndef MM_MODEM_HELPERS_CINTERION_H
#define MM_MODEM_HELPERS_CINTERION_H
-#include "glib.h"
+#include <glib.h>
+
+#include <ModemManager.h>
+#include <mm-base-bearer.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+typedef enum {
+ MM_CINTERION_MODEM_FAMILY_DEFAULT = 0,
+ MM_CINTERION_MODEM_FAMILY_IMT = 1,
+} MMCinterionModemFamily;
+
+typedef enum {
+ MM_CINTERION_RADIO_BAND_FORMAT_SINGLE = 0,
+ MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE = 1,
+} MMCinterionRadioBandFormat;
+
+typedef enum {
+ MM_CINTERION_RB_BLOCK_LEGACY = 0,
+ MM_CINTERION_RB_BLOCK_GSM = 0,
+ MM_CINTERION_RB_BLOCK_UMTS = 1,
+ MM_CINTERION_RB_BLOCK_LTE_LOW = 2,
+ MM_CINTERION_RB_BLOCK_LTE_HIGH = 3,
+ MM_CINTERION_RB_BLOCK_N = 4
+} MMCinterionRbBlock;
+
+typedef enum {
+ MM_CINTERION_RADIO_GEN_NONE = 0,
+ MM_CINTERION_RADIO_GEN_2G = 2,
+ MM_CINTERION_RADIO_GEN_3G = 3,
+ MM_CINTERION_RADIO_GEN_4G = 4,
+} MMCinterionRadioGen;
/*****************************************************************************/
/* ^SCFG test parser */
-gboolean mm_cinterion_parse_scfg_test (const gchar *response,
- MMModemCharset charset,
- GArray **supported_bands,
- GError **error);
+gboolean mm_cinterion_parse_scfg_test (const gchar *response,
+ MMCinterionModemFamily modem_family,
+ MMModemCharset charset,
+ GArray **supported_bands,
+ MMCinterionRadioBandFormat *format,
+ GError **error);
/*****************************************************************************/
/* ^SCFG response parser */
-gboolean mm_cinterion_parse_scfg_response (const gchar *response,
- MMModemCharset charset,
- GArray **bands,
- GError **error);
+gboolean mm_cinterion_parse_scfg_response (const gchar *response,
+ MMCinterionModemFamily modem_family,
+ MMModemCharset charset,
+ GArray **bands,
+ MMCinterionRadioBandFormat format,
+ GError **error);
/*****************************************************************************/
/* +CNMI test parser */
@@ -48,11 +86,13 @@ gboolean mm_cinterion_parse_cnmi_test (const gchar *response,
/*****************************************************************************/
/* Build Cinterion-specific band value */
-gboolean mm_cinterion_build_band (GArray *bands,
- guint supported,
- gboolean only_2g,
- guint *out_band,
- GError **error);
+gboolean mm_cinterion_build_band (GArray *bands,
+ guint *supported,
+ gboolean only_2g,
+ MMCinterionRadioBandFormat format,
+ MMCinterionModemFamily modem_family,
+ guint *out_band,
+ GError **error);
/*****************************************************************************/
/* Single ^SIND response parser */
@@ -63,4 +103,93 @@ gboolean mm_cinterion_parse_sind_response (const gchar *response,
guint *value,
GError **error);
+/*****************************************************************************/
+/* ^SWWAN response parser */
+
+MMBearerConnectionStatus mm_cinterion_parse_swwan_response (const gchar *response,
+ guint swwan_index,
+ gpointer log_object,
+ GError **error);
+
+/*****************************************************************************/
+/* ^SGAUTH response parser */
+
+gboolean mm_cinterion_parse_sgauth_response (const gchar *response,
+ guint cid,
+ MMBearerAllowedAuth *out_auth,
+ gchar **out_username,
+ GError **error);
+
+/*****************************************************************************/
+/* ^SMONG response parser */
+
+gboolean mm_cinterion_parse_smong_response (const gchar *response,
+ MMModemAccessTechnology *access_tech,
+ GError **error);
+
+/*****************************************************************************/
+/* ^SIND psinfo helper */
+
+MMModemAccessTechnology mm_cinterion_get_access_technology_from_sind_psinfo (guint val,
+ gpointer log_object);
+
+/*****************************************************************************/
+/* ^SLCC URC helpers */
+
+GRegex *mm_cinterion_get_slcc_regex (void);
+
+/* MMCallInfo list management */
+gboolean mm_cinterion_parse_slcc_list (const gchar *str,
+ gpointer log_object,
+ GList **out_list,
+ GError **error);
+void mm_cinterion_call_info_list_free (GList *call_info_list);
+
+/*****************************************************************************/
+/* +CTZU URC helpers */
+
+GRegex *mm_cinterion_get_ctzu_regex (void);
+gboolean mm_cinterion_parse_ctzu_urc (GMatchInfo *match_info,
+ gchar **iso8601p,
+ MMNetworkTimezone **tzp,
+ GError **error);
+
+/*****************************************************************************/
+/* ^SMONI helper */
+
+gboolean mm_cinterion_parse_smoni_query_response (const gchar *response,
+ MMCinterionRadioGen *out_tech,
+ gdouble *out_rssi,
+ gdouble *out_ecn0,
+ gdouble *out_rscp,
+ gdouble *out_rsrp,
+ gdouble *out_rsrq,
+ GError **error);
+
+gboolean mm_cinterion_smoni_response_to_signal_info (const gchar *response,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ GError **error);
+
+/*****************************************************************************/
+/* ^SCFG="MEopMode/Prov/Cfg" helper */
+
+gboolean mm_cinterion_provcfg_response_to_cid (const gchar *response,
+ MMCinterionModemFamily modem_family,
+ MMModemCharset charset,
+ gpointer log_object,
+ gint *cid,
+ GError **error);
+
+/*****************************************************************************/
+/* Auth related helpers */
+
+MMBearerAllowedAuth mm_auth_type_from_cinterion_auth_type (guint cinterion_auth);
+
+gchar *mm_cinterion_build_auth_string (gpointer log_object,
+ MMCinterionModemFamily modem_family,
+ MMBearerProperties *config,
+ guint cid);
+
#endif /* MM_MODEM_HELPERS_CINTERION_H */
diff --git a/plugins/cinterion/mm-plugin-cinterion.c b/plugins/cinterion/mm-plugin-cinterion.c
index 8af0a2ac..48fbb1ac 100644
--- a/plugins/cinterion/mm-plugin-cinterion.c
+++ b/plugins/cinterion/mm-plugin-cinterion.c
@@ -29,16 +29,20 @@
#include "mm-plugin-cinterion.h"
#include "mm-broadband-modem-cinterion.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi-cinterion.h"
#endif
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim-cinterion.h"
+#endif
+
G_DEFINE_TYPE (MMPluginCinterion, mm_plugin_cinterion, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
@@ -46,91 +50,67 @@ int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
#define TAG_CINTERION_APP_PORT "cinterion-app-port"
#define TAG_CINTERION_MODEM_PORT "cinterion-modem-port"
-typedef struct {
- MMPortProbe *probe;
- MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
-} CinterionCustomInitContext;
-
-static void
-cinterion_custom_init_context_complete_and_free (CinterionCustomInitContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
- g_slice_free (CinterionCustomInitContext, ctx);
-}
-
static gboolean
-cinterion_custom_init_finish (MMPortProbe *probe,
- GAsyncResult *result,
- GError **error)
+cinterion_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
sqport_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- CinterionCustomInitContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
+ MMPortProbe *probe;
const gchar *response;
+ probe = g_task_get_source_object (task);
+
/* Ignore errors, just avoid tagging */
response = mm_port_serial_at_command_finish (port, res, NULL);
if (response) {
/* A valid reply to AT^SQPORT tells us this is an AT port already */
- mm_port_probe_set_result_at (ctx->probe, TRUE);
+ mm_port_probe_set_result_at (probe, TRUE);
if (strstr (response, "Application"))
- g_object_set_data (G_OBJECT (ctx->probe), TAG_CINTERION_APP_PORT, GUINT_TO_POINTER (TRUE));
+ g_object_set_data (G_OBJECT (probe), TAG_CINTERION_APP_PORT, GUINT_TO_POINTER (TRUE));
else if (strstr (response, "Modem"))
- g_object_set_data (G_OBJECT (ctx->probe), TAG_CINTERION_MODEM_PORT, GUINT_TO_POINTER (TRUE));
+ g_object_set_data (G_OBJECT (probe), TAG_CINTERION_MODEM_PORT, GUINT_TO_POINTER (TRUE));
}
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- cinterion_custom_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-cinterion_custom_init (MMPortProbe *probe,
- MMPortSerialAt *port,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+cinterion_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- CinterionCustomInitContext *ctx;
+ GTask *task;
- ctx = g_slice_new (CinterionCustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- cinterion_custom_init);
- ctx->probe = g_object_ref (probe);
- ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ task = g_task_new (probe, cancellable, callback, user_data);
mm_port_serial_at_command (
- ctx->port,
+ port,
"AT^SQPORT?",
3,
FALSE, /* raw */
FALSE, /* allow cached */
- ctx->cancellable,
- (GAsyncReadyCallback)sqport_ready,
- ctx);
+ cancellable,
+ (GAsyncReadyCallback) sqport_ready,
+ task);
}
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -139,8 +119,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Cinterion modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_cinterion_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered Cinterion modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_cinterion_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -148,7 +128,18 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_cinterion_new (sysfs_path,
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ mm_obj_dbg (self, "MBIM-powered Cinterion modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_cinterion_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_cinterion_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -167,29 +158,19 @@ grab_port (MMPlugin *self,
ptype = mm_port_probe_get_port_type (probe);
if (g_object_get_data (G_OBJECT (probe), TAG_CINTERION_APP_PORT)) {
- mm_dbg ("(%s/%s)' Port flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port '%s/%s' flagged as primary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
} else if (g_object_get_data (G_OBJECT (probe), TAG_CINTERION_MODEM_PORT)) {
- mm_dbg ("(%s/%s)' Port flagged as PPP",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port '%s/%s' flagged as PPP",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
- } else if (g_udev_device_get_property_as_boolean (mm_port_probe_peek_port (probe),
- "ID_MM_CINTERION_PORT_TYPE_GPS")) {
- mm_dbg ("(%s/%s)' Port flagged as GPS",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- /* Not an AT port, but the port to grab GPS traces */
- g_warn_if_fail (ptype == MM_PORT_TYPE_UNKNOWN);
- ptype = MM_PORT_TYPE_GPS;
}
return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ mm_port_probe_peek_port (probe),
ptype,
pflags,
error);
@@ -200,7 +181,7 @@ grab_port (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const gchar *vendor_strings[] = { "cinterion", "siemens", NULL };
static const guint16 vendor_ids[] = { 0x1e2d, 0x0681, 0 };
static const MMAsyncMethod custom_init = {
@@ -210,12 +191,13 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_CINTERION,
- MM_PLUGIN_NAME, "Cinterion",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
MM_PLUGIN_CUSTOM_INIT, &custom_init,
NULL));
}
diff --git a/plugins/cinterion/mm-shared-cinterion.c b/plugins/cinterion/mm-shared-cinterion.c
new file mode 100644
index 00000000..36cf60c9
--- /dev/null
+++ b/plugins/cinterion/mm-shared-cinterion.c
@@ -0,0 +1,1601 @@
+/* -*- 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) 2014 Ammonit Measurement GmbH
+ * Copyright (C) 2014 - 2018 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#include <config.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-shared-cinterion.h"
+#include "mm-modem-helpers-cinterion.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "shared-cinterion-private-tag"
+static GQuark private_quark;
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED,
+} FeatureSupport;
+
+typedef struct {
+ /* modem */
+ MMIfaceModem *iface_modem_parent;
+ /* location */
+ MMIfaceModemLocation *iface_modem_location_parent;
+ MMModemLocationSource supported_sources;
+ MMModemLocationSource enabled_sources;
+ FeatureSupport sgpss_support;
+ FeatureSupport sgpsc_support;
+ /* voice */
+ MMIfaceModemVoice *iface_modem_voice_parent;
+ FeatureSupport slcc_support;
+ GRegex *slcc_regex;
+ /* time */
+ MMIfaceModemTime *iface_modem_time_parent;
+ GRegex *ctzu_regex;
+} Private;
+
+static void
+private_free (Private *ctx)
+{
+ g_regex_unref (ctx->ctzu_regex);
+ g_regex_unref (ctx->slcc_regex);
+ g_slice_free (Private, ctx);
+}
+
+static Private *
+get_private (MMSharedCinterion *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = (g_quark_from_static_string (PRIVATE_TAG));
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new (Private);
+
+ priv->supported_sources = MM_MODEM_LOCATION_SOURCE_NONE;
+ priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE;
+ priv->sgpss_support = FEATURE_SUPPORT_UNKNOWN;
+ priv->sgpsc_support = FEATURE_SUPPORT_UNKNOWN;
+ priv->slcc_support = FEATURE_SUPPORT_UNKNOWN;
+ priv->slcc_regex = mm_cinterion_get_slcc_regex ();
+ priv->ctzu_regex = mm_cinterion_get_ctzu_regex ();
+
+ /* Setup parent class' MMIfaceModem, MMIfaceModemLocation, MMIfaceModemVoice
+ * and MMIfaceModemTime */
+
+ g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_interface);
+ priv->iface_modem_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_interface (self);
+
+ g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_location_interface);
+ priv->iface_modem_location_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_location_interface (self);
+
+ g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_voice_interface);
+ priv->iface_modem_voice_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_voice_interface (self);
+
+ g_assert (MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_time_interface);
+ priv->iface_modem_time_parent = MM_SHARED_CINTERION_GET_INTERFACE (self)->peek_parent_time_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+/*****************************************************************************/
+/* Modem interface */
+
+gboolean
+mm_shared_cinterion_modem_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_reset_at_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_reset_at (GTask *task)
+{
+ MMSharedCinterion *self;
+
+ self = g_task_get_source_object (task);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN=1,1",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) modem_reset_at_ready,
+ task);
+}
+
+static void
+parent_modem_reset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ if (!priv->iface_modem_parent->reset_finish (self, res, NULL)) {
+ modem_reset_at (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_cinterion_modem_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (priv->iface_modem_parent->reset &&
+ priv->iface_modem_parent->reset_finish) {
+ priv->iface_modem_parent->reset (self,
+ (GAsyncReadyCallback) parent_modem_reset_ready,
+ task);
+ return;
+ }
+
+ modem_reset_at (task);
+}
+
+/*****************************************************************************/
+/* GPS trace received */
+
+static void
+trace_received (MMPortSerialGps *port,
+ const gchar *trace,
+ MMIfaceModemLocation *self)
+{
+ mm_iface_modem_location_gps_update (self, trace);
+}
+
+/*****************************************************************************/
+/* Location capabilities loading (Location interface) */
+
+MMModemLocationSource
+mm_shared_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize aux;
+
+ aux = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource) aux;
+}
+
+static void probe_gps_features (GTask *task);
+
+static void
+sgpsc_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!mm_base_modem_at_command_finish (self, res, NULL))
+ priv->sgpsc_support = FEATURE_NOT_SUPPORTED;
+ else {
+ /* ^SGPSC supported! */
+ priv->sgpsc_support = FEATURE_SUPPORTED;
+ /* It may happen that the modem was started with GPS already enabled, or
+ * maybe ModemManager got rebooted and it was left enabled before. We'll
+ * make sure that it is disabled when we initialize the modem. */
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Engine\",\"0\"", 3, FALSE, NULL, NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"Power/Antenna\",\"off\"", 3, FALSE, NULL, NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=\"NMEA/Output\",\"off\"", 3, FALSE, NULL, NULL);
+ }
+
+ probe_gps_features (task);
+}
+
+static void
+sgpss_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!mm_base_modem_at_command_finish (self, res, NULL))
+ priv->sgpss_support = FEATURE_NOT_SUPPORTED;
+ else {
+ /* ^SGPSS supported! */
+ priv->sgpss_support = FEATURE_SUPPORTED;
+
+ /* Flag ^SGPSC as unsupported, even if it may be supported, so that we
+ * only use one set of commands to enable/disable GPS. */
+ priv->sgpsc_support = FEATURE_NOT_SUPPORTED;
+
+ /* It may happen that the modem was started with GPS already enabled, or
+ * maybe ModemManager got rebooted and it was left enabled before. We'll
+ * make sure that it is disabled when we initialize the modem. */
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSS=0", 3, FALSE, NULL, NULL);
+ }
+
+ probe_gps_features (task);
+}
+
+static void
+probe_gps_features (GTask *task)
+{
+ MMSharedCinterion *self;
+ MMModemLocationSource sources;
+ Private *priv;
+
+ self = MM_SHARED_CINTERION (g_task_get_source_object (task));
+ priv = get_private (self);
+
+ /* Need to check if SGPSS supported... */
+ if (priv->sgpss_support == FEATURE_SUPPORT_UNKNOWN) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSS=?", 3, TRUE, (GAsyncReadyCallback) sgpss_test_ready, task);
+ return;
+ }
+
+ /* Need to check if SGPSC supported... */
+ if (priv->sgpsc_support == FEATURE_SUPPORT_UNKNOWN) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "AT^SGPSC=?", 3, TRUE, (GAsyncReadyCallback) sgpsc_test_ready, task);
+ return;
+ }
+
+ /* All GPS features probed */
+
+ /* Recover parent sources */
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ if (priv->sgpss_support == FEATURE_SUPPORTED || priv->sgpsc_support == FEATURE_SUPPORTED) {
+ mm_obj_dbg (self, "GPS commands supported: GPS capabilities enabled");
+
+ /* We only flag as supported by this implementation those sources not already
+ * supported by the parent implementation */
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA))
+ priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW))
+ priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW;
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))
+ priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
+
+ sources |= priv->supported_sources;
+
+ /* Add handler for the NMEA traces in the GPS data port */
+ mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)),
+ (MMPortSerialGpsTraceFn)trace_received,
+ self,
+ NULL);
+ } else
+ mm_obj_dbg (self, "no GPS command supported: no GPS capabilities");
+
+ g_task_return_int (task, (gssize) sources);
+ g_object_unref (task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Now our own check. If we don't have any GPS port, we're done */
+ if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) {
+ mm_obj_dbg (self, "no GPS data port found: no GPS capabilities");
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Cache sources supported by the parent */
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+
+ /* Probe all GPS features */
+ probe_gps_features (task);
+}
+
+void
+mm_shared_cinterion_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->load_capabilities);
+ g_assert (priv->iface_modem_location_parent->load_capabilities_finish);
+
+ priv->iface_modem_location_parent->load_capabilities (self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Disable location gathering (Location interface) */
+
+typedef enum {
+ DISABLE_LOCATION_GATHERING_GPS_STEP_FIRST,
+ DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSS,
+ DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE,
+ DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA,
+ DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT,
+ DISABLE_LOCATION_GATHERING_GPS_STEP_LAST,
+} DisableLocationGatheringGpsStep;
+
+typedef struct {
+ MMModemLocationSource source;
+ DisableLocationGatheringGpsStep gps_step;
+ GError *sgpss_error;
+ GError *sgpsc_error;
+} DisableLocationGatheringContext;
+
+static void
+disable_location_gathering_context_free (DisableLocationGatheringContext *ctx)
+{
+ if (ctx->sgpss_error)
+ g_error_free (ctx->sgpss_error);
+ if (ctx->sgpsc_error)
+ g_error_free (ctx->sgpsc_error);
+ g_slice_free (DisableLocationGatheringContext, ctx);
+}
+
+gboolean
+mm_shared_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void disable_location_gathering_context_gps_step (GTask *task);
+
+static void
+disable_sgpsc_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisableLocationGatheringContext *ctx;
+ GError *error = NULL;
+
+ ctx = (DisableLocationGatheringContext *) g_task_get_task_data (task);
+
+ /* Store error, if not one available already, and continue */
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ if (!ctx->sgpsc_error)
+ ctx->sgpsc_error = error;
+ else
+ g_error_free (error);
+ }
+
+ ctx->gps_step++;
+ disable_location_gathering_context_gps_step (task);
+}
+
+static void
+disable_sgpss_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisableLocationGatheringContext *ctx;
+
+ ctx = (DisableLocationGatheringContext *) g_task_get_task_data (task);
+
+ /* Store error, if any, and continue */
+ g_assert (!ctx->sgpss_error);
+ mm_base_modem_at_command_finish (self, res, &ctx->sgpss_error);
+
+ ctx->gps_step++;
+ disable_location_gathering_context_gps_step (task);
+}
+
+static void
+disable_location_gathering_context_gps_step (GTask *task)
+{
+ DisableLocationGatheringContext *ctx;
+ MMSharedCinterion *self;
+ Private *priv;
+
+ self = MM_SHARED_CINTERION (g_task_get_source_object (task));
+ priv = get_private (self);
+ ctx = (DisableLocationGatheringContext *) g_task_get_task_data (task);
+
+ /* Only one of both supported */
+ g_assert ((priv->sgpss_support == FEATURE_SUPPORTED) || (priv->sgpsc_support == FEATURE_SUPPORTED));
+ g_assert (!((priv->sgpss_support == FEATURE_SUPPORTED) && (priv->sgpsc_support == FEATURE_SUPPORTED)));
+
+ switch (ctx->gps_step) {
+ case DISABLE_LOCATION_GATHERING_GPS_STEP_FIRST:
+ ctx->gps_step++;
+ /* fall through */
+
+ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSS:
+ if (priv->sgpss_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSS=0",
+ 3, FALSE, (GAsyncReadyCallback) disable_sgpss_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE:
+ if (priv->sgpsc_support == FEATURE_SUPPORTED) {
+ /* Engine off */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSC=\"Engine\",\"0\"",
+ 3, FALSE, (GAsyncReadyCallback) disable_sgpsc_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA:
+ if (priv->sgpsc_support == FEATURE_SUPPORTED) {
+ /* Antenna off */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSC=\"Power/Antenna\",\"off\"",
+ 3, FALSE, (GAsyncReadyCallback) disable_sgpsc_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case DISABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT:
+ if (priv->sgpsc_support == FEATURE_SUPPORTED) {
+ /* NMEA output off */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSC=\"NMEA/Output\",\"off\"",
+ 3, FALSE, (GAsyncReadyCallback) disable_sgpsc_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case DISABLE_LOCATION_GATHERING_GPS_STEP_LAST:
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ /* Even if we get an error here, we try to close the GPS port */
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ if (ctx->sgpss_error) {
+ g_task_return_error (task, ctx->sgpss_error);
+ g_clear_error (&ctx->sgpss_error);
+ } else if (ctx->sgpsc_error) {
+ g_task_return_error (task, ctx->sgpsc_error);
+ g_clear_error (&ctx->sgpsc_error);
+ } else {
+ priv->enabled_sources &= ~ctx->source;
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_cinterion_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisableLocationGatheringContext *ctx;
+ MMModemLocationSource enabled_sources;
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent disable */
+ if (!(priv->supported_sources & source)) {
+ /* If disabling implemented by the parent, run it. */
+ if (priv->iface_modem_location_parent->disable_location_gathering &&
+ priv->iface_modem_location_parent->disable_location_gathering_finish) {
+ priv->iface_modem_location_parent->disable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_disable_location_gathering_ready,
+ task);
+ return;
+ }
+ /* Otherwise, we're done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED));
+
+ /* Flag as disabled to see how many others we would have left enabled */
+ enabled_sources = priv->enabled_sources;
+ enabled_sources &= ~source;
+
+ /* If there are still GPS-related sources enabled, do nothing else */
+ if (enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ priv->enabled_sources &= ~source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Stop GPS engine if all GPS-related sources are disabled */
+ ctx = g_slice_new0 (DisableLocationGatheringContext);
+ ctx->source = source;
+ ctx->gps_step = DISABLE_LOCATION_GATHERING_GPS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) disable_location_gathering_context_free);
+ disable_location_gathering_context_gps_step (task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+/* We will retry the SGPSC command that enables the Engine */
+#define MAX_SGPSC_ENGINE_RETRIES 3
+
+/* Cinterion asks for 100ms some time between GPS commands, but we'll give up
+ * to 2000ms before setting the Engine configuration as 100ms didn't seem always
+ * enough (we would get +CME ERROR: 767 errors reported). */
+#define GPS_COMMAND_TIMEOUT_DEFAULT_MS 100
+#define GPS_COMMAND_TIMEOUT_ENGINE_MS 2000
+
+typedef enum {
+ ENABLE_LOCATION_GATHERING_GPS_STEP_FIRST,
+ ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSS,
+ ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT,
+ ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA,
+ ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE,
+ ENABLE_LOCATION_GATHERING_GPS_STEP_LAST,
+} EnableLocationGatheringGpsStep;
+
+typedef struct {
+ MMModemLocationSource source;
+ EnableLocationGatheringGpsStep gps_step;
+ guint sgpsc_engine_retries;
+} EnableLocationGatheringContext;
+
+static void
+enable_location_gathering_context_free (EnableLocationGatheringContext *ctx)
+{
+ g_slice_free (EnableLocationGatheringContext, ctx);
+}
+
+gboolean
+mm_shared_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void enable_location_gathering_context_gps_step (GTask *task);
+
+static gboolean
+enable_location_gathering_context_gps_step_schedule_cb (GTask *task)
+{
+ /* Run the scheduled step */
+ enable_location_gathering_context_gps_step (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+enable_sgpsc_or_sgpss_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnableLocationGatheringContext *ctx;
+ GError *error = NULL;
+
+ ctx = (EnableLocationGatheringContext *) g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ /* The GPS setup may sometimes report "+CME ERROR 767" when enabling the
+ * Engine; so we'll run some retries of the same command ourselves. */
+ if (ctx->gps_step == ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE) {
+ ctx->sgpsc_engine_retries++;
+ mm_obj_dbg (self, "GPS engine setup failed (%u/%u)", ctx->sgpsc_engine_retries, MAX_SGPSC_ENGINE_RETRIES);
+ if (ctx->sgpsc_engine_retries < MAX_SGPSC_ENGINE_RETRIES) {
+ g_clear_error (&error);
+ goto schedule;
+ }
+ }
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->gps_step++;
+
+schedule:
+ g_timeout_add (ctx->gps_step == ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE ? GPS_COMMAND_TIMEOUT_ENGINE_MS : GPS_COMMAND_TIMEOUT_DEFAULT_MS,
+ (GSourceFunc) enable_location_gathering_context_gps_step_schedule_cb, task);
+}
+
+static void
+enable_location_gathering_context_gps_step (GTask *task)
+{
+ EnableLocationGatheringContext *ctx;
+ MMSharedCinterion *self;
+ Private *priv;
+
+ self = MM_SHARED_CINTERION (g_task_get_source_object (task));
+ priv = get_private (self);
+ ctx = (EnableLocationGatheringContext *) g_task_get_task_data (task);
+
+ /* Only one of both supported */
+ g_assert ((priv->sgpss_support == FEATURE_SUPPORTED) || (priv->sgpsc_support == FEATURE_SUPPORTED));
+ g_assert (!((priv->sgpss_support == FEATURE_SUPPORTED) && (priv->sgpsc_support == FEATURE_SUPPORTED)));
+
+ switch (ctx->gps_step) {
+ case ENABLE_LOCATION_GATHERING_GPS_STEP_FIRST:
+ ctx->gps_step++;
+ /* fall through */
+
+ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSS:
+ if (priv->sgpss_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSS=4",
+ 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_OUTPUT:
+ if (priv->sgpsc_support == FEATURE_SUPPORTED) {
+ /* NMEA output off */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSC=\"NMEA/Output\",\"on\"",
+ 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ANTENNA:
+ if (priv->sgpsc_support == FEATURE_SUPPORTED) {
+ /* Antenna off */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSC=\"Power/Antenna\",\"on\"",
+ 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case ENABLE_LOCATION_GATHERING_GPS_STEP_SGPSC_ENGINE:
+ if (priv->sgpsc_support == FEATURE_SUPPORTED) {
+ /* Engine off */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SGPSC=\"Engine\",\"1\"",
+ 3, FALSE, (GAsyncReadyCallback) enable_sgpsc_or_sgpss_ready, task);
+ return;
+ }
+ ctx->gps_step++;
+ /* fall through */
+
+ case ENABLE_LOCATION_GATHERING_GPS_STEP_LAST:
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+ GError *error = NULL;
+
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ /* Success */
+ priv->enabled_sources |= ctx->source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_cinterion_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+ EnableLocationGatheringContext *ctx;
+
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent enable */
+ if (!(priv->supported_sources & source)) {
+ priv->iface_modem_location_parent->enable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED));
+
+ /* If GPS already started, store new flag and we're done */
+ if (priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (EnableLocationGatheringContext);
+ ctx->source = source;
+ ctx->gps_step = ENABLE_LOCATION_GATHERING_GPS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) enable_location_gathering_context_free);
+
+ enable_location_gathering_context_gps_step (task);
+}
+
+/*****************************************************************************/
+
+MMBaseCall *
+mm_shared_cinterion_create_call (MMIfaceModemVoice *self,
+ MMCallDirection direction,
+ const gchar *number)
+{
+ Private *priv;
+
+ /* If ^SLCC is supported create a cinterion call object */
+ priv = get_private (MM_SHARED_CINTERION (self));
+ if (priv->slcc_support == FEATURE_SUPPORTED) {
+ mm_obj_dbg (self, "created new call with ^SLCC support");
+ return mm_base_call_new (MM_BASE_MODEM (self),
+ direction,
+ number,
+ /* When SLCC is supported we have support for detailed
+ * call list events via call list report URCs */
+ TRUE, /* incoming timeout not required */
+ TRUE, /* dialing->ringing supported */
+ TRUE); /* ringing->active supported */
+ }
+
+ /* otherwise, run parent's generic base call logic */
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->create_call);
+ return priv->iface_modem_voice_parent->create_call (self, direction, number);
+}
+
+/*****************************************************************************/
+/* Common enable/disable voice unsolicited events */
+
+typedef struct {
+ gboolean enable;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ gchar *slcc_command;
+ gboolean slcc_primary_done;
+ gboolean slcc_secondary_done;
+} VoiceUnsolicitedEventsContext;
+
+static void
+voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx)
+{
+ g_clear_object (&ctx->secondary);
+ g_clear_object (&ctx->primary);
+ g_free (ctx->slcc_command);
+ g_slice_free (VoiceUnsolicitedEventsContext, ctx);
+}
+
+static gboolean
+common_voice_enable_disable_unsolicited_events_finish (MMSharedCinterion *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void run_voice_enable_disable_unsolicited_events (GTask *task);
+
+static void
+slcc_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (self, res, &error))
+ mm_obj_dbg (self, "couldn't %s ^SLCC reporting: %s",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+
+ /* Continue on next port */
+ run_voice_enable_disable_unsolicited_events (task);
+}
+
+static void
+run_voice_enable_disable_unsolicited_events (GTask *task)
+{
+ MMSharedCinterion *self;
+ Private *priv;
+ VoiceUnsolicitedEventsContext *ctx;
+ MMPortSerialAt *port = NULL;
+
+ self = MM_SHARED_CINTERION (g_task_get_source_object (task));
+ priv = get_private (self);
+ ctx = g_task_get_task_data (task);
+
+ /* If not ^SLCC supported, we're done */
+ if (priv->slcc_support == FEATURE_NOT_SUPPORTED) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!ctx->slcc_primary_done && ctx->primary) {
+ mm_obj_dbg (self, "%s ^SLCC extended list of current calls reporting in primary port...",
+ ctx->enable ? "enabling" : "disabling");
+ ctx->slcc_primary_done = TRUE;
+ port = ctx->primary;
+ } else if (!ctx->slcc_secondary_done && ctx->secondary) {
+ mm_obj_dbg (self, "%s ^SLCC extended list of current calls reporting in secondary port...",
+ ctx->enable ? "enabling" : "disabling");
+ ctx->slcc_secondary_done = TRUE;
+ port = ctx->secondary;
+ }
+
+ if (port) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ port,
+ ctx->slcc_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)slcc_command_ready,
+ task);
+ return;
+ }
+
+ /* Fully done now */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_voice_enable_disable_unsolicited_events (MMSharedCinterion *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (VoiceUnsolicitedEventsContext);
+ ctx->enable = enable;
+ if (enable)
+ ctx->slcc_command = g_strdup ("^SLCC=1");
+ else
+ ctx->slcc_command = g_strdup ("^SLCC=0");
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free);
+
+ run_voice_enable_disable_unsolicited_events (task);
+}
+
+/*****************************************************************************/
+/* Disable unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_cinterion_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!priv->iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't disable parent voice unsolicited events: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+voice_disable_unsolicited_events_ready (MMSharedCinterion *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ g_autoptr(GError) error = NULL;
+
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't disable Cinterion-specific voice unsolicited events: %s", error->message);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events_finish);
+
+ /* Chain up parent's disable */
+ priv->iface_modem_voice_parent->disable_unsolicited_events (
+ MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready,
+ task);
+}
+
+void
+mm_shared_cinterion_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* our own disabling first */
+ common_voice_enable_disable_unsolicited_events (MM_SHARED_CINTERION (self),
+ FALSE,
+ (GAsyncReadyCallback) voice_disable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_cinterion_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_enable_unsolicited_events_ready (MMSharedCinterion *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't enable Cinterion-specific voice unsolicited events: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!priv->iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't enable parent voice unsolicited events: %s", error->message);
+
+ /* our own enabling next */
+ common_voice_enable_disable_unsolicited_events (MM_SHARED_CINTERION (self),
+ TRUE,
+ (GAsyncReadyCallback) voice_enable_unsolicited_events_ready,
+ task);
+}
+
+void
+mm_shared_cinterion_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events_finish);
+
+ /* chain up parent's enable first */
+ priv->iface_modem_voice_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common setup/cleanup voice unsolicited events */
+
+static void
+slcc_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMSharedCinterion *self)
+{
+ g_autofree gchar *full = NULL;
+ g_autoptr(GError) error = NULL;
+ GList *call_info_list = NULL;
+
+ full = g_match_info_fetch (match_info, 0);
+ if (!mm_cinterion_parse_slcc_list (full, self, &call_info_list, &error))
+ mm_obj_warn (self, "couldn't parse ^SLCC list: %s", error->message);
+ else
+ mm_iface_modem_voice_report_all_calls (MM_IFACE_MODEM_VOICE (self), call_info_list);
+ mm_cinterion_call_info_list_free (call_info_list);
+}
+
+static void
+common_voice_setup_cleanup_unsolicited_events (MMSharedCinterion *self,
+ gboolean enable)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->slcc_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)slcc_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_cinterion_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't cleanup parent voice unsolicited events: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_cinterion_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish);
+
+ /* our own cleanup first */
+ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), FALSE);
+
+ /* Chain up parent's cleanup */
+ priv->iface_modem_voice_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_cinterion_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!priv->iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't setup parent voice unsolicited events: %s", error->message);
+
+ /* our own setup next */
+ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_cinterion_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events_finish);
+
+ /* chain up parent's setup first */
+ priv->iface_modem_voice_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Check if Voice supported (Voice interface) */
+
+gboolean
+mm_shared_cinterion_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+slcc_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ /* ^SLCC supported unless we got any error response */
+ priv->slcc_support = (!!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ?
+ FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED);
+
+ /* If ^SLCC supported we won't need polling in the parent */
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, (priv->slcc_support == FEATURE_SUPPORTED),
+ NULL);
+
+ /* ^SLCC command is supported; assume we have full voice capabilities */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_check_support_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ GError *error = NULL;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ if (!priv->iface_modem_voice_parent->check_support_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* voice is supported, check if ^SLCC is available */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^SLCC=?",
+ 3,
+ /* Do NOT cache as the reply may be different if PIN locked
+ * or unlocked. E.g. we may not support ^SLCC for emergency
+ * voice calls. */
+ FALSE,
+ (GAsyncReadyCallback) slcc_format_check_ready,
+ task);
+}
+
+void
+mm_shared_cinterion_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->check_support);
+ g_assert (priv->iface_modem_voice_parent->check_support_finish);
+
+ /* chain up parent's setup first */
+ priv->iface_modem_voice_parent->check_support (
+ self,
+ (GAsyncReadyCallback)parent_voice_check_support_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common setup/cleanup time unsolicited events */
+
+static void
+ctzu_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMSharedCinterion *self)
+{
+ g_autofree gchar *iso8601 = NULL;
+ g_autoptr(MMNetworkTimezone) tz = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_cinterion_parse_ctzu_urc (match_info, &iso8601, &tz, &error)) {
+ mm_obj_dbg (self, "couldn't process +CTZU URC: %s", error->message);
+ return;
+ }
+
+ mm_obj_dbg (self, "+CTZU URC received: %s", iso8601);
+ mm_iface_modem_time_update_network_time (MM_IFACE_MODEM_TIME (self), iso8601);
+ mm_iface_modem_time_update_network_timezone (MM_IFACE_MODEM_TIME (self), tz);
+}
+
+static void
+common_time_setup_cleanup_unsolicited_events (MMSharedCinterion *self,
+ gboolean enable)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ mm_obj_dbg (self, "%s up time unsolicited events...",
+ enable ? "setting" : "cleaning");
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->ctzu_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)ctzu_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited events (Time interface) */
+
+gboolean
+mm_shared_cinterion_time_cleanup_unsolicited_events_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_time_cleanup_unsolicited_events_ready (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!priv->iface_modem_time_parent->cleanup_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't cleanup parent time unsolicited events: %s", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_cinterion_time_cleanup_unsolicited_events (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_time_parent);
+
+ /* our own cleanup first */
+ common_time_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), FALSE);
+
+ if (priv->iface_modem_time_parent->cleanup_unsolicited_events &&
+ priv->iface_modem_time_parent->cleanup_unsolicited_events_finish) {
+ /* Chain up parent's cleanup */
+ priv->iface_modem_time_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_time_cleanup_unsolicited_events_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Setup unsolicited events (Time interface) */
+
+gboolean
+mm_shared_cinterion_time_setup_unsolicited_events_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+own_time_setup_unsolicited_events (GTask *task)
+{
+ MMSharedCinterion *self;
+
+ self = g_task_get_source_object (task);
+
+ /* our own setup next */
+ common_time_setup_cleanup_unsolicited_events (MM_SHARED_CINTERION (self), TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_time_setup_unsolicited_events_ready (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+
+ if (!priv->iface_modem_time_parent->cleanup_unsolicited_events_finish (self, res, &error))
+ mm_obj_warn (self, "Couldn't cleanup parent time unsolicited events: %s", error->message);
+
+ own_time_setup_unsolicited_events (task);
+}
+
+void
+mm_shared_cinterion_time_setup_unsolicited_events (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_CINTERION (self));
+ g_assert (priv->iface_modem_time_parent);
+
+ if (priv->iface_modem_time_parent->setup_unsolicited_events &&
+ priv->iface_modem_time_parent->setup_unsolicited_events_finish) {
+ /* chain up parent's setup first */
+ priv->iface_modem_time_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_time_setup_unsolicited_events_ready,
+ task);
+ return;
+ }
+
+ own_time_setup_unsolicited_events (task);
+}
+
+/*****************************************************************************/
+
+static void
+shared_cinterion_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_cinterion_get_type (void)
+{
+ static GType shared_cinterion_type = 0;
+
+ if (!G_UNLIKELY (shared_cinterion_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedCinterion), /* class_size */
+ shared_cinterion_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_cinterion_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedCinterion", &info, 0);
+ g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM);
+ g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM_VOICE);
+ g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM_TIME);
+ g_type_interface_add_prerequisite (shared_cinterion_type, MM_TYPE_IFACE_MODEM_LOCATION);
+ }
+
+ return shared_cinterion_type;
+}
diff --git a/plugins/cinterion/mm-shared-cinterion.h b/plugins/cinterion/mm-shared-cinterion.h
new file mode 100644
index 00000000..eb6beac8
--- /dev/null
+++ b/plugins/cinterion/mm-shared-cinterion.h
@@ -0,0 +1,153 @@
+/* -*- 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) 2014 Ammonit Measurement GmbH
+ * Copyright (C) 2014 - 2018 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#ifndef MM_SHARED_CINTERION_H
+#define MM_SHARED_CINTERION_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-iface-modem-time.h"
+
+#define MM_TYPE_SHARED_CINTERION (mm_shared_cinterion_get_type ())
+#define MM_SHARED_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_CINTERION, MMSharedCinterion))
+#define MM_IS_SHARED_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_CINTERION))
+#define MM_SHARED_CINTERION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_CINTERION, MMSharedCinterion))
+
+typedef struct _MMSharedCinterion MMSharedCinterion;
+
+struct _MMSharedCinterion {
+ GTypeInterface g_iface;
+
+ /* Peek modem interface of the parent class of the object */
+ MMIfaceModem * (* peek_parent_interface) (MMSharedCinterion *self);
+
+ /* Peek location interface of the parent class of the object */
+ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedCinterion *self);
+
+ /* Peek voice interface of the parent class of the object */
+ MMIfaceModemVoice * (* peek_parent_voice_interface) (MMSharedCinterion *self);
+
+ /* Peek time interface of the parent class of the object */
+ MMIfaceModemTime * (* peek_parent_time_interface) (MMSharedCinterion *self);
+};
+
+GType mm_shared_cinterion_get_type (void);
+
+/*****************************************************************************/
+/* Modem interface */
+
+void mm_shared_cinterion_modem_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_modem_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+/*****************************************************************************/
+/* Location interface */
+
+void mm_shared_cinterion_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemLocationSource mm_shared_cinterion_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+/*****************************************************************************/
+/* Voice interface */
+
+MMBaseCall *mm_shared_cinterion_create_call (MMIfaceModemVoice *self,
+ MMCallDirection direction,
+ const gchar *number);
+
+void mm_shared_cinterion_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+/*****************************************************************************/
+/* Time interface */
+
+void mm_shared_cinterion_time_setup_unsolicited_events (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_time_setup_unsolicited_events_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_cinterion_time_cleanup_unsolicited_events (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_cinterion_time_cleanup_unsolicited_events_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_CINTERION_H */
diff --git a/plugins/cinterion/tests/test-modem-helpers-cinterion.c b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
index 26b07ab2..c3e7e9e5 100644
--- a/plugins/cinterion/tests/test-modem-helpers-cinterion.c
+++ b/plugins/cinterion/tests/test-modem-helpers-cinterion.c
@@ -20,44 +20,47 @@
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include <math.h>
-#include "mm-log.h"
+#include "mm-log-test.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-cinterion.h"
-static gint
-sort_band (MMModemBand a, MMModemBand b)
-{
- return a - b;
-}
+#define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \
+ g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
/*****************************************************************************/
/* Test ^SCFG test responses */
static void
common_test_scfg (const gchar *response,
- GArray *expected_bands)
+ GArray *expected_bands,
+ MMModemCharset charset,
+ MMCinterionModemFamily modem_family)
{
GArray *bands = NULL;
gchar *expected_bands_str;
gchar *bands_str;
GError *error = NULL;
gboolean res;
+ MMCinterionRadioBandFormat format;
res = mm_cinterion_parse_scfg_test (response,
- MM_MODEM_CHARSET_UNKNOWN,
+ modem_family,
+ charset,
&bands,
+ &format,
&error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert (bands != NULL);
- g_array_sort (bands, (GCompareFunc)sort_band);
- g_array_sort (expected_bands, (GCompareFunc)sort_band);
+ mm_common_bands_garray_sort (bands);
+ mm_common_bands_garray_sort (expected_bands);
- expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)expected_bands->data,
+ expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)expected_bands->data,
expected_bands->len);
- bands_str = mm_common_build_bands_string ((const MMModemBand *)bands->data,
+ bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)bands->data,
bands->len);
/* Instead of comparing the array one by one, compare the strings built from the mask
@@ -103,17 +106,349 @@ test_scfg (void)
"^SCFG: \"URC/Ringline/ActiveTime\",(\"0\",\"1\",\"2\",\"keep\")\r\n";
expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
- single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U2100, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U1900, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U850, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U900, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U800, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single);
+
+ common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UNKNOWN, MM_CINTERION_MODEM_FAMILY_DEFAULT);
+
+ g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_ehs5 (void)
+{
+ GArray *expected_bands;
+ MMModemBand single;
+ const gchar *response =
+ "^SCFG: \"Audio/Loop\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Call/ECC\",(\"0\"-\"255\")\r\n"
+ "^SCFG: \"Call/Ecall/AckTimeout\",(\"0-2147483646\")\r\n"
+ "^SCFG: \"Call/Ecall/Callback\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Call/Ecall/CallbackTimeout\",(\"0-2147483646\")\r\n"
+ "^SCFG: \"Call/Ecall/Msd\",(\"280\")\r\n"
+ "^SCFG: \"Call/Ecall/Pullmode\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Call/Ecall/SessionTimeout\",(\"0-2147483646\")\r\n"
+ "^SCFG: \"Call/Ecall/StartTimeout\",(\"0-2147483646\")\r\n"
+ "^SCFG: \"Call/Speech/Codec\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n"
+ "^SCFG: \"Gpio/mode/ASC1\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DAI\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DCD0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DSR0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DTR0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/FSR\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/PULSE\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/PWM\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/RING0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/SPI\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/SYNC\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Ident/Manufacturer\",(25)\r\n"
+ "^SCFG: \"Ident/Product\",(25)\r\n"
+ "^SCFG: \"MEShutdown/Fso\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEShutdown/sVsup/threshold\",(\"-4\",\"-3\",\"-2\",\"-1\",\"0\",\"1\",\"2\",\"3\",\"4\"),(\"0\")\r\n"
+ "^SCFG: \"MEopMode/CFUN\",(\"0\",\"1\"),(\"1\",\"4\")\r\n"
+ "^SCFG: \"MEopMode/Dormancy\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/SoR\",(\"off\",\"on\")\r\n"
+ "^SCFG: \"Radio/Band\",(\"1\"-\"147\")\r\n"
+ "^SCFG: \"Radio/Mtpl\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"1\",\"8\"),(\"18\"-\"33\"),(\"18\"-\"27\")\r\n"
+ "^SCFG: \"Radio/Mtpl\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"16\",\"32\",\"64\",\"128\",\"256\"),(\"18\"-\"24\")\r\n"
+ "^SCFG: \"Radio/Mtpl\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"2\",\"4\"),(\"18\"-\"30\"),(\"18\"-\"26\")\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",(\"0\",\"1\",\"2\",\"3\",\"4\")\r\n"
+ "^SCFG: \"Serial/Interface/Allocation\",(\"0\",\"1\",\"2\"),(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"Serial/USB/DDD\",(\"0\",\"1\"),(\"0\"),(4),(4),(4),(63),(63),(4)\r\n"
+ "^SCFG: \"Tcp/IRT\",(\"1\"-\"60\")\r\n"
+ "^SCFG: \"Tcp/MR\",(\"1\"-\"30\")\r\n"
+ "^SCFG: \"Tcp/OT\",(\"1\"-\"6000\")\r\n"
+ "^SCFG: \"Tcp/WithURCs\",(\"on\",\"off\")\r\n"
+ "^SCFG: \"Trace/Syslog/OTAP\",(\"0\",\"1\"),(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"usb3\",\"usb4\",\"usb5\",\"file\",\"udp\",\"system\"),(\"1\"-\"65535\"),(125),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n"
+ "^SCFG: \"URC/Ringline\",(\"off\",\"local\",\"asc0\")\r\n"
+ "^SCFG: \"URC/Ringline/ActiveTime\",(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"Userware/Autostart\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Userware/Autostart/Delay\",(\"0\"-\"10000\")\r\n"
+ "^SCFG: \"Userware/DebugInterface\",(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\",\"1\")\r\n"
+ "^SCFG: \"Userware/DebugMode\",(\"off\",\"on\")\r\n"
+ "^SCFG: \"Userware/Passwd\",(\"0\"-\"8\")\r\n"
+ "^SCFG: \"Userware/Stdout\",(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"usb3\",\"usb4\",\"usb5\",\"file\",\"udp\",\"system\"),(\"1\"-\"65535\"),(\"0\"-\"125\"),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n"
+ "^SCFG: \"Userware/Watchdog\",(\"0\",\"1\",\"2\")\r\n";
+
+ expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+
+ common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UNKNOWN, MM_CINTERION_MODEM_FAMILY_DEFAULT);
+
+ g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_pls62_gsm (void)
+{
+ GArray *expected_bands;
+ MMModemBand single;
+ const gchar *response =
+ "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"off\",\"on\")\r\n"
+ "^SCFG: \"MEopMode/Prov/Cfg\",(\"fallback\",\"attus\")\r\n"
+ "^SCFG: \"Serial/Ifc\",(\"Current\",\"ASC0\",\"USB0\",\"USB1\",\"USB2\",\"MUX1\",\"MUX2\",\"MUX3\",\"0\"),(\"0\",\"3\"),(\"1200\",\"2400\",\"4800\",\"9600\",\"19200\",\"38400\",\"57600\",\"115200\",\"230400\",\"460800\",\"500000\",\"750000\",\"921600\"),(\"0)\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",(\"current\",\"powerup\"),(\"asc0\",\"acm1\",\"acm2\",\"acm3\",\"rmnet0\",\"rmnet1\")\r\n"
+ "^SCFG: \"Gpio/mode/ASC1\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DCD0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DSR0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/DTR0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/FSR\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/PULSE\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/PWM\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/HWAKEUP\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/RING0\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/SPI\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"Gpio/mode/SYNC\",(\"std\",\"gpio\",\"rsv\")\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n"
+ "^SCFG: \"Ident/Manufacturer\",(25)\r\n"
+ "^SCFG: \"Ident/Product\",(25)\r\n"
+ "^SCFG: \"MEopMode/SoR\",(\"off\",\"on\")\r\n"
+ "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MeOpMode/SRPOM\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/RingOnData\",(\"off\",\"on\")\r\n"
+ "^SCFG: \"MEShutdown/Fso\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEShutdown/sVsup/threshold\",(\"-4\",\"-3\",\"-2\",\"-1\",\"0\",\"1\",\"2\",\"3\",\"4\"),(\"0\")\r\n"
+ "^SCFG: \"Radio/Band/2G\",(\"0x00000004\"-\"0x00000074\")\r\n"
+ "^SCFG: \"Radio/Band/3G\",(\"0x00000001\"-\"0x0004019B\")\r\n"
+ "^SCFG: \"Radio/Band/4G\",(\"0x00000001\"-\"0x080E08DF\")\r\n"
+ "^SCFG: \"Radio/Mtpl/2G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000004\",\"0x00000010\",\"0x00000020\",\"0x00000040\"),,(\"18\"-\"33\"),(\"18\"-\"27\")\r\n"
+ "^SCFG: \"Radio/Mtpl/3G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000001\",\"0x00000002\",\"0x00000008\",\"0x00000010\",\"0x00000080\",\"0x00000100\",\"0x00040000\"),,(\"18\"-\"24\")\r\n"
+ "^SCFG: \"Radio/Mtpl/4G\",(\"0\"-\"3\"),(\"1\"-\"8\"),(\"0x00000001\",\"0x00000002\",\"0x00000004\",\"0x00000008\",\"0x00000010\",\"0x00000040\",\"0x00000080\",\"0x00000800\",\"0x00020000\",\"0x00040000\",\"0x00080000\",\"0x08000000\"),,(\"18)\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",(\"0\",\"1\",\"2\",\"3\",\"4\")\r\n"
+ "^SCFG: \"Serial/Interface/Allocation\",(\"0\",\"1\"),(\"0\",\"1\")\r\n"
+ "^SCFG: \"Serial/USB/DDD\",(\"0\",\"1\"),(\"0\"),(4),(4),(4),(63),(63),(4)\r\n"
+ "^SCFG: \"Tcp/IRT\",(\"1\"-\"60\")\r\n"
+ "^SCFG: \"Tcp/MR\",(\"2\"-\"30\")\r\n"
+ "^SCFG: \"Tcp/OT\",(\"1\"-\"6000\")\r\n"
+ "^SCFG: \"Tcp/WithURCs\",(\"on\",\"off\")\r\n"
+ "^SCFG: \"Trace/Syslog/OTAP\",(\"0\",\"1\"),(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"file\",\"system\"),(\"1\"-\"65535\"),(125),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n"
+ "^SCFG: \"Urc/Ringline\",(\"off\",\"local\",\"asc0\",\"wakeup\")\r\n"
+ "^SCFG: \"Urc/Ringline/ActiveTime\",(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"Userware/Autostart\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Userware/Autostart/Delay\",(\"0\"-\"10000\")\r\n"
+ "^SCFG: \"Userware/DebugInterface\",(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\"-\"255\")|(\"FE80::\"-\"FE80::FFFFFFFFFFFFFFFF\"),(\"0\",\"1\")\r\n"
+ "^SCFG: \"Userware/DebugMode\",(\"off\",\"on\")\r\n"
+ "^SCFG: \"Userware/Passwd\",(\"0\"-\"8\")\r\n"
+ "^SCFG: \"Userware/Stdout\",(\"null\",\"asc0\",\"asc1\",\"usb\",\"usb1\",\"usb2\",\"file\",\"system\"),(\"1\"-\"65535\"),(\"0\"-\"125\"),(\"buffered\",\"secure\"),(\"off\",\"on\")\r\n"
+ "^SCFG: \"Userware/Watchdog\",(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",(\"current\",\"powerup\"),(\"asc0\",\"acm1\",\"acm2\",\"acm3\")\r\n";
+
+ expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_4, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_4, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_12, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+
+ common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_GSM, MM_CINTERION_MODEM_FAMILY_IMT);
+
+ g_array_unref (expected_bands);
+}
- common_test_scfg (response, expected_bands);
+static void
+test_scfg_pls62_ucs2 (void)
+{
+ GArray *expected_bands;
+ MMModemBand single;
+ const gchar *response =
+ "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"006F00660066\",\"006F006E\")\r\n"
+ "^SCFG: \"MEopMode/Prov/Cfg\",(\"fallback\",\"attus\")\r\n"
+ "^SCFG: \"Serial/Ifc\",(\"00430075007200720065006E0074\",\"0041005300430030\",\"0055005300420030\",\"0055005300420031\",\"0055005300420032\",\"004D005500580031\",\"004D005500580032\",\"004D005500580033\",\"0030\"),(\"0030\",\"0033)\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",(\"00630075007200720065006E0074\",\"0070006F00770065007200750070\"),(\"0061007300630030\",\"00610063006D0031\",\"00610063006D0032\",\"00610063006D0033\",\"0072006D006E006500740030\",\"0072006D0)\r\n"
+ "^SCFG: \"Gpio/mode/ASC1\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/DCD0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/DSR0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/DTR0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/FSR\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/PULSE\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/PWM\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/HWAKEUP\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/RING0\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/SPI\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"Gpio/mode/SYNC\",(\"007300740064\",\"006700700069006F\",\"007200730076\")\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",(\"00640069007300610062006C00650064\",\"0065006E00610062006C00650064\")\r\n"
+ "^SCFG: \"Ident/Manufacturer\",(25)\r\n"
+ "^SCFG: \"Ident/Product\",(25)\r\n"
+ "^SCFG: \"MEopMode/SoR\",(\"006F00660066\",\"006F006E\")\r\n"
+ "^SCFG: \"MEopMode/CregRoam\",(\"0030\",\"0031\")\r\n"
+ "^SCFG: \"MeOpMode/SRPOM\",(\"0030\",\"0031\")\r\n"
+ "^SCFG: \"MEopMode/RingOnData\",(\"006F00660066\",\"006F006E\")\r\n"
+ "^SCFG: \"MEShutdown/Fso\",(\"0030\",\"0031\")\r\n"
+ "^SCFG: \"MEShutdown/sVsup/threshold\",(\"002D0034\",\"002D0033\",\"002D0032\",\"002D0031\",\"0030\",\"0031\",\"0032\",\"0033\",\"0034\"),(\"0030\")\r\n"
+ "^SCFG: \"Radio/Band/2G\",(\"0030007800300030003000300030003000300034\"-\"0030007800300030003000300030003000370034\")\r\n"
+ "^SCFG: \"Radio/Band/3G\",(\"0030007800300030003000300030003000300031\"-\"0030007800300030003000340030003100390042\")\r\n"
+ "^SCFG: \"Radio/Band/4G\",(\"0030007800300030003000300030003000300031\"-\"0030007800300038003000450030003800440046\")\r\n"
+ "^SCFG: \"Radio/Mtpl/2G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00300078003000300030003000300030003000340022002C002200300078003000300030003000300030003100300022002C0022003000780030003000300030003)\r\n"
+ "^SCFG: \"Radio/Mtpl/3G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00300078003000300030003000300030003000310022002C002200300078003000300030003000300030003000320022002C0022003000780030003000300030003)\r\n"
+ "^SCFG: \"Radio/Mtpl/4G\",(\"00300022002D00220033\"),(\"00310022002D00220038\"),(\"00310022002D00220038\"),,(\"003100380022002D002200320033\")\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",(\"0030\",\"0031\",\"0032\",\"0033\",\"0034\")\r\n"
+ "^SCFG: \"Serial/Interface/Allocation\",(\"0030\",\"0031\"),(\"0030\",\"0031\")\r\n"
+ "^SCFG: \"Serial/USB/DDD\",(\"0030\",\"0031\"),(\"0030\"),(4),(4),(4),(63),(63),(4)\r\n"
+ "^SCFG: \"Tcp/IRT\",(\"0031\"-\"00360030\")\r\n"
+ "^SCFG: \"Tcp/MR\",(\"0032\"-\"00330030\")\r\n"
+ "^SCFG: \"Tcp/OT\",(\"0031\"-\"0036003000300030\")\r\n"
+ "^SCFG: \"Tcp/WithURCs\",(\"006F006E\",\"006F00660066\")\r\n"
+ "^SCFG: \"Trace/Syslog/OTAP\",(\"0030\",\"0031\"),(\"006E0075006C006C\",\"0061007300630030\",\"0061007300630031\",\"007500730062\",\"0075007300620031\",\"0075007300620032\",\"00660069006C0065\",\"00730079007300740065006D\"),(\"003)\r\n"
+ "^SCFG: \"Urc/Ringline\",(\"006F00660066\",\"006C006F00630061006C\",\"0061007300630030\",\"00770061006B006500750070\")\r\n"
+ "^SCFG: \"Urc/Ringline/ActiveTime\",(\"0030\",\"0031\",\"0032\")\r\n"
+ "^SCFG: \"Userware/Autostart\",(\"0030\",\"0031\")\r\n"
+ "^SCFG: \"Userware/Autostart/Delay\",(\"00300022002D002200310030003000300030\")\r\n"
+ "^SCFG: \"Userware/DebugInterface\",(\"0030\"-\"003200350035\")|(\"0046004500380030003A003A\"-\"0046004500380030003A003A0046004600460046004600460046004600460046004600460046004600460046\"),(\"0030\"-\"003200350035\")|(\"004)\r\n"
+ "^SCFG: \"Userware/DebugMode\",(\"006F00660066\",\"006F006E\")\r\n"
+ "^SCFG: \"Userware/Passwd\",(\"0030\"-\"0038\")\r\n"
+ "^SCFG: \"Userware/Stdout\",(\"006E0075006C006C\",\"0061007300630030\",\"0061007300630031\",\"007500730062\",\"0075007300620031\",\"0075007300620032\",\"00660069006C0065\",\"00730079007300740065006D\"),(\"0031\"-\"00360035003500)\r\n"
+ "^SCFG: \"Userware/Watchdog\",(\"0030\",\"0031\",\"0032\")\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",(\"00630075007200720065006E0074\",\"0070006F00770065007200750070\"),(\"0061007300630030\",\"00610063006D0031\",\"00610063006D0032\",\"00610063006D0033\")\r\n";
+
+ expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_4, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_4, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_12, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+
+ common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_UCS2, MM_CINTERION_MODEM_FAMILY_IMT);
+
+ g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_alas5 (void)
+{
+ GArray *expected_bands;
+ MMModemBand single;
+ const gchar *response =
+ "^SCFG: \"Audio/Loop\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Audio/SvTone\",(\"0-2047\")\r\n"
+ "^SCFG: \"Call/Ecall/AckTimeout\",(\"0-60000\")\r\n"
+ "^SCFG: \"Call/Ecall/BlockSMSPP\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Call/Ecall/Callback\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Call/Ecall/CallbackTimeout\",(\"0-86400000\")\r\n"
+ "^SCFG: \"Call/Ecall/Force\",(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"Call/Ecall/Msd\",(280)\r\n"
+ "^SCFG: \"Call/Ecall/Pullmode\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Call/Ecall/SessionTimeout\",(\"0-300000\")\r\n"
+ "^SCFG: \"Call/Ecall/StartTimeout\",(\"0-600000\")\r\n"
+ "^SCFG: \"Call/ECC\",(\"0\"-\"255\")\r\n"
+ "^SCFG: \"Call/Speech/Codec\",(\"0\",\"2\")\r\n"
+ "^SCFG: \"GPRS/Auth\",(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",(\"disabled\",\"enabled\")\r\n"
+ "^SCFG: \"GPRS/MTU/Mode\",(\"0-1\")\r\n"
+ "^SCFG: \"GPRS/MTU/Size\",(\"1280-4096\")\r\n"
+ "^SCFG: \"MEopMode/CFUN\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/CregRoam\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/Dormancy\",(\"0\",\"1\",\"9\")\r\n"
+ "^SCFG: \"MEopMode/DTM/Mode\",(\"0\",\"1\",\"2\")\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n"
+ "^SCFG: \"MEopMode/FGI/Split\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/IMS\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/NonBlock/Cops\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/PowerMgmt/LCI\",(\"disabled\",\"enabled\"),(\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+ "^SCFG: \"MEopMode/Prov/AutoFallback\",(\"on\",\"off\")\r\n"
+ "^SCFG: \"MEopMode/Prov/AutoSelect\",(\"on\",\"off\")\r\n"
+ "^SCFG: \"MEopMode/Prov/Cfg\",(\"vdfde\",\"tmode\",\"clarobr\",\"telenorno\",\"telenorse\",\"vdfpt\",\"fallb3gpp*\",\"vdfww\",\"vdfes\",\"swisscomch\",\"eeuk\",\"orangero\",\"orangees\",\"tefde\",\"telenordk\",\"timit\",\"tn1de\",\"tefes\",\"tels)\r\n"
+ "^SCFG: \"MEopMode/PwrSave\",(\"disabled\",\"enabled\"),(\"0-36000\"),(\"0-36000\"),(\"CPU-A\",\"CPU-M\"),(\"powerup\",\"current\")\r\n"
+ "^SCFG: \"MEopMode/SRPOM\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"MEopMode/USB/KeepData\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n"
+ "^SCFG: \"MEShutdown/OnIgnition\",(\"on\",\"off\")\r\n"
+ "^SCFG: \"MEShutdown/Timer\",(\"off\",\"0\"-\"525600\")\r\n"
+ "^SCFG: \"Misc/CId\",(290)\r\n"
+ "^SCFG: \"Radio/Band/2G\",(\"00000001-0000000f\"),,(\"0\",\"1\")\r\n"
+ "^SCFG: \"Radio/Band/3G\",(\"00000001-000400b5\"),,(\"0\",\"1\")\r\n"
+ "^SCFG: \"Radio/Band/4G\",(\"00000001-8a0e00d5\"),(\"00000002-000001e2\"),(\"0\",\"1\")\r\n"
+ "^SCFG: \"Radio/CNS\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"Radio/Mtpl\",(\"0-1\"),(\"1-8\")\r\n"
+ "^SCFG: \"Radio/Mtpl/2G\",(\"2-3\"),(\"1-8\"),(\"00000001-0000000f\"),,(\"18-33\"),(\"18-27\")\r\n"
+ "^SCFG: \"Radio/Mtpl/3G\",(\"2-3\"),(\"1-8\"),(\"00000001-000000b5\"),,(\"18-24\")\r\n"
+ "^SCFG: \"Radio/Mtpl/4G\",(\"2-3\"),(\"1-8\"),(\"00000001-8a0e00d5\"),(\"00000002-000000e2\"),(\"18-24\")\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",(\"4\"-\"8\")\r\n"
+ "^SCFG: \"RemoteWakeUp/Event/ASC\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+ "^SCFG: \"RemoteWakeUp/Event/URC\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+ "^SCFG: \"RemoteWakeUp/Event/USB\",(\"none\",\"GPIO1\",\"GPIO3\",\"GPIO4\",\"GPIO5\",\"GPIO6\",\"GPIO7\",\"GPIO8\",\"GPIO11\",\"GPIO12\",\"GPIO13\",\"GPIO14\",\"GPIO15\",\"GPIO16\",\"GPIO17\",\"GPIO22\")\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",(\"current\",\"powerup\"),(\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\")\r\n"
+ "^SCFG: \"RemoteWakeUp/Pulse\",(\"1\"-\"100\")\r\n"
+ "^SCFG: \"Serial/USB/DDD\",(\"0-1\"),(\"0\"),(\"0001-ffff\"),(\"0000-ffff\"),(\"0000-ffff\"),(63),(63),(4)\r\n"
+ "^SCFG: \"SIM/CS\",(\"NOSIM\",\"SIM1\",\"SIM2\")\r\n"
+ "^SCFG: \"SMS/4GPREF\",(\"IMS\",\"CSPS\")\r\n"
+ "^SCFG: \"SMS/AutoAck\",(\"0\",\"1\")\r\n"
+ "^SCFG: \"SMS/RETRM\",(\"1-45\")\r\n"
+ "^SCFG: \"URC/Ringline\",(\"off\",\"local\",\"asc0\")\r\n"
+ "^SCFG: \"URC/Ringline/ActiveTime\",(\"2\",\"on\",\"off\")\r\n";
+
+ expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 23);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_3, g_array_append_val (expected_bands, single); //
+ single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single); //
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_26, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_38, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_39, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_40, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_41, g_array_append_val (expected_bands, single);
+
+ common_test_scfg (response, expected_bands, MM_MODEM_CHARSET_GSM, MM_CINTERION_MODEM_FAMILY_DEFAULT);
g_array_unref (expected_bands);
}
@@ -124,7 +459,9 @@ test_scfg (void)
static void
common_test_scfg_response (const gchar *response,
MMModemCharset charset,
- GArray *expected_bands)
+ GArray *expected_bands,
+ MMCinterionModemFamily modem_family,
+ MMCinterionRadioBandFormat rbf)
{
GArray *bands = NULL;
gchar *expected_bands_str;
@@ -132,17 +469,17 @@ common_test_scfg_response (const gchar *response,
GError *error = NULL;
gboolean res;
- res = mm_cinterion_parse_scfg_response (response, charset, &bands, &error);
+ res = mm_cinterion_parse_scfg_response (response, modem_family, charset, &bands, rbf, &error);
g_assert_no_error (error);
g_assert (res == TRUE);
g_assert (bands != NULL);
- g_array_sort (bands, (GCompareFunc)sort_band);
- g_array_sort (expected_bands, (GCompareFunc)sort_band);
+ mm_common_bands_garray_sort (bands);
+ mm_common_bands_garray_sort (expected_bands);
- expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)expected_bands->data,
+ expected_bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)expected_bands->data,
expected_bands->len);
- bands_str = mm_common_build_bands_string ((const MMModemBand *)bands->data,
+ bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)bands->data,
bands->len);
/* Instead of comparing the array one by one, compare the strings built from the mask
@@ -167,47 +504,275 @@ test_scfg_response_2g (void)
single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
- common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands);
+ common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE);
g_array_unref (expected_bands);
}
static void
-test_scfg_response_2g_ucs2 (void)
+test_scfg_response_3g (void)
{
GArray *expected_bands;
MMModemBand single;
const gchar *response =
- "^SCFG: \"Radio/Band\",\"0031\",\"0031\"\r\n"
+ "^SCFG: \"Radio/Band\",127\r\n"
"\r\n";
expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
- single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
- common_test_scfg_response (response, MM_MODEM_CHARSET_UCS2, expected_bands);
+ common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_SINGLE);
g_array_unref (expected_bands);
}
static void
-test_scfg_response_3g (void)
+test_scfg_response_pls62_gsm (void)
{
GArray *expected_bands;
MMModemBand single;
- const gchar *response =
- "^SCFG: \"Radio/Band\",127\r\n"
- "\r\n";
+ const gchar *response =
+ "^SCFG: \"MEopMode/Prov/AutoSelect\",\"off\"\r\n"
+ "^SCFG: \"MEopMode/Prov/Cfg\",\"attus\"\r\n"
+ "^SCFG: \"Serial/Ifc\",\"0\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",\"current\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",\"powerup\"\r\n"
+ "^SCFG: \"Gpio/mode/ASC1\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/DCD0\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/DSR0\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/DTR0\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/FSR\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/PULSE\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/PWM\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/HWAKEUP\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/RING0\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/SPI\",\"gpio\"\r\n"
+ "^SCFG: \"Gpio/mode/SYNC\",\"gpio\"\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",\"enabled\"\r\n"
+ "^SCFG: \"Ident/Manufacturer\",\"Cinterion\"\r\n"
+ "^SCFG: \"Ident/Product\",\"PLS62-W\"\r\n"
+ "^SCFG: \"MEopMode/SoR\",\"off\"\r\n"
+ "^SCFG: \"MEopMode/CregRoam\",\"0\"\r\n"
+ "^SCFG: \"MeOpMode/SRPOM\",\"0\"\r\n"
+ "^SCFG: \"MEopMode/RingOnData\",\"off\"\r\n"
+ "^SCFG: \"MEShutdown/Fso\",\"0\"\r\n"
+ "^SCFG: \"MEShutdown/sVsup/threshold\",\"0\",\"0\"\r\n"
+ "^SCFG: \"Radio/Band/2G\",\"0x00000014\"\r\n"
+ "^SCFG: \"Radio/Band/3G\",\"0x00000182\"\r\n"
+ "^SCFG: \"Radio/Band/4G\",\"0x080E0000\"\r\n"
+ "^SCFG: \"Radio/Mtpl/2G\",\"0\"\r\n"
+ "^SCFG: \"Radio/Mtpl/3G\",\"0\"\r\n"
+ "^SCFG: \"Radio/Mtpl/4G\",\"0\"\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",\"4\"\r\n"
+ "^SCFG: \"Serial/Interface/Allocation\",\"0\",\"0\"\r\n"
+ "^SCFG: \"Serial/USB/DDD\",\"0\",\"0\",\"0409\",\"1E2D\",\"005B\",\"Cinterion Wireless Modules\",\"PLSx\",\"\"\r\n"
+ "^SCFG: \"Tcp/IRT\",\"3\"\r\n"
+ "^SCFG: \"Tcp/MR\",\"10\"\r\n"
+ "^SCFG: \"Tcp/OT\",\"6000\"\r\n"
+ "^SCFG: \"Tcp/WithURCs\",\"on\"\r\n"
+ "^SCFG: \"Trace/Syslog/OTAP\",\"0\"\r\n"
+ "^SCFG: \"Urc/Ringline\",\"local\"\r\n"
+ "^SCFG: \"Urc/Ringline/ActiveTime\",\"2\"\r\n"
+ "^SCFG: \"Userware/Autostart\",\"0\"\r\n"
+ "^SCFG: \"Userware/Autostart/Delay\",\"0\"\r\n"
+ "^SCFG: \"Userware/DebugInterface\",\"0.0.0.0\",\"0.0.0.0\",\"0\"\r\n"
+ "^SCFG: \"Userware/DebugMode\",\"off\"\r\n"
+ "^SCFG: \"Userware/Passwd\",\r\n"
+ "^SCFG: \"Userware/Stdout\",\"null\",,,,\"off\"\r\n"
+ "^SCFG: \"Userware/Watchdog\",\"0\"\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",\"current\"\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",\"powerup\"\r\n";
expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
- single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U2100, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U1900, g_array_append_val (expected_bands, single);
- single = MM_MODEM_BAND_U850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+
+ common_test_scfg_response (response, MM_MODEM_CHARSET_GSM, expected_bands, MM_CINTERION_MODEM_FAMILY_IMT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE);
+
+ g_array_unref (expected_bands);
+}
+
+static void
+test_scfg_response_pls62_ucs2 (void)
+{
+ GArray *expected_bands;
+ MMModemBand single;
+ const gchar *response =
+ "^SCFG: \"MEopMode/Prov/AutoSelect\",\"006F00660066\"\r\n"
+ "^SCFG: \"MEopMode/Prov/Cfg\",\"00610074007400750073\"\r\n"
+ "^SCFG: \"Serial/Ifc\",\"0\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",\"00630075007200720065006E0074\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",\"0070006F00770065007200750070\"\r\n"
+ "^SCFG: \"Gpio/mode/ASC1\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/DCD0\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/DSR0\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/DTR0\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/FSR\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/PULSE\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/PWM\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/HWAKEUP\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/RING0\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/SPI\",\"006700700069006F\"\r\n"
+ "^SCFG: \"Gpio/mode/SYNC\",\"006700700069006F\"\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",\"0065006E00610062006C00650064\"\r\n"
+ "^SCFG: \"Ident/Manufacturer\",\"Cinterion\"\r\n"
+ "^SCFG: \"Ident/Product\",\"PLS62-W\"\r\n"
+ "^SCFG: \"MEopMode/SoR\",\"006F00660066\"\r\n"
+ "^SCFG: \"MEopMode/CregRoam\",\"0030\"\r\n"
+ "^SCFG: \"MeOpMode/SRPOM\",\"0030\"\r\n"
+ "^SCFG: \"MEopMode/RingOnData\",\"006F00660066\"\r\n"
+ "^SCFG: \"MEShutdown/Fso\",\"0030\"\r\n"
+ "^SCFG: \"MEShutdown/sVsup/threshold\",\"0030\",\"0030\"\r\n"
+ "^SCFG: \"Radio/Band/2G\",\"0030007800300030003000300030003000310034\"\r\n"
+ "^SCFG: \"Radio/Band/3G\",\"0030007800300030003000300030003100380032\"\r\n"
+ "^SCFG: \"Radio/Band/4G\",\"0030007800300038003000450030003000300030\"\r\n"
+ "^SCFG: \"Radio/Mtpl/2G\",\"0030\"\r\n"
+ "^SCFG: \"Radio/Mtpl/3G\",\"0030\"\r\n"
+ "^SCFG: \"Radio/Mtpl/4G\",\"0030\"\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",\"0034\"\r\n"
+ "^SCFG: \"Serial/Interface/Allocation\",\"0030\",\"0030\"\r\n"
+ "^SCFG: \"Serial/USB/DDD\",\"0030\",\"0030\",\"0030003400300039\",\"0031004500320044\",\"0030003000350042\",\"00430069006E0074006500720069006F006E00200057006900720065006C0065007300730020004D006F00640075006C00650073\",\"005\"\r\n"
+ "^SCFG: \"Tcp/IRT\",\"0033\"\r\n"
+ "^SCFG: \"Tcp/MR\",\"00310030\"\r\n"
+ "^SCFG: \"Tcp/OT\",\"0036003000300030\"\r\n"
+ "^SCFG: \"Tcp/WithURCs\",\"006F006E\"\r\n"
+ "^SCFG: \"Trace/Syslog/OTAP\",\"0030\"\r\n"
+ "^SCFG: \"Urc/Ringline\",\"006C006F00630061006C\"\r\n"
+ "^SCFG: \"Urc/Ringline/ActiveTime\",\"0032\"\r\n"
+ "^SCFG: \"Userware/Autostart\",\"0030\"\r\n"
+ "^SCFG: \"Userware/Autostart/Delay\",\"0030\"\r\n"
+ "^SCFG: \"Userware/DebugInterface\",\"0030002E0030002E0030002E0030\",\"0030002E0030002E0030002E0030\",\"0030\"\r\n"
+ "^SCFG: \"Userware/DebugMode\",\"006F00660066\"\r\n"
+ "^SCFG: \"Userware/Passwd\",\r\n"
+ "^SCFG: \"Userware/Stdout\",\"006E0075006C006C\",,,,\"006F00660066\"\r\n"
+ "^SCFG: \"Userware/Watchdog\",\"0030\"\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",\"00630075007200720065006E0074\"\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",\"0070006F00770065007200750070\"\r\n";
+
+ expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 9);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_2, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_9, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+
+ common_test_scfg_response (response, MM_MODEM_CHARSET_UCS2, expected_bands, MM_CINTERION_MODEM_FAMILY_IMT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE);
+
+ g_array_unref (expected_bands);
+}
- common_test_scfg_response (response, MM_MODEM_CHARSET_UNKNOWN, expected_bands);
+static void
+test_scfg_response_alas5 (void)
+{
+ GArray *expected_bands;
+ MMModemBand single;
+ const gchar *response =
+ "^SCFG: \"Audio/Loop\",\"0\"\r\n"
+ "^SCFG: \"Audio/SvTone\",\"0\"\r\n"
+ "^SCFG: \"Call/Ecall/AckTimeout\",\"5000\"\r\n"
+ "^SCFG: \"Call/Ecall/BlockSMSPP\",\"0\"\r\n"
+ "^SCFG: \"Call/Ecall/Callback\",\"0\"\r\n"
+ "^SCFG: \"Call/Ecall/CallbackTimeout\",\"43200000\"\r\n"
+ "^SCFG: \"Call/Ecall/Force\",\"1\"\r\n"
+ "^SCFG: \"Call/Ecall/Msd\",\"\"\r\n"
+ "^SCFG: \"Call/Ecall/Pullmode\",\"0\"\r\n"
+ "^SCFG: \"Call/Ecall/SessionTimeout\",\"20000\"\r\n"
+ "^SCFG: \"Call/Ecall/StartTimeout\",\"5000\"\r\n"
+ "^SCFG: \"Call/ECC\",\"0\"\r\n"
+ "^SCFG: \"Call/Speech/Codec\",\"0\"\r\n"
+ "^SCFG: \"GPRS/Auth\",\"2\"\r\n"
+ "^SCFG: \"GPRS/AutoAttach\",\"enabled\"\r\n"
+ "^SCFG: \"GPRS/MTU/Mode\",\"0\"\r\n"
+ "^SCFG: \"GPRS/MTU/Size\",1500\r\n"
+ "^SCFG: \"MEopMode/CFUN\",\"1\",\"1\"\r\n"
+ "^SCFG: \"MEopMode/CregRoam\",\"0\"\r\n"
+ "^SCFG: \"MEopMode/Dormancy\",\"0\",\"0\"\r\n"
+ "^SCFG: \"MEopMode/DTM/Mode\",\"2\"\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"mbim\",\"asc0\"\r\n"
+ "^SCFG: \"MEopMode/ExpectDTR\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"mbim\",\"asc0\"\r\n"
+ "^SCFG: \"MEopMode/FGI/Split\",\"1\"\r\n"
+ "^SCFG: \"MEopMode/IMS\",\"1\"\r\n"
+ "^SCFG: \"MEopMode/NonBlock/Cops\",\"0\"\r\n"
+ "^SCFG: \"MEopMode/PowerMgmt/LCI\",\"disabled\"\r\n"
+ "^SCFG: \"MEopMode/Prov/AutoFallback\",\"off\"\r\n"
+ "^SCFG: \"MEopMode/Prov/AutoSelect\",\"on\"\r\n"
+ "^SCFG: \"MEopMode/Prov/Cfg\",\"vdfde\"\r\n"
+ "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"52\",\"50\",\"CPU-A\",\"powerup\"\r\n"
+ "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"52\",\"50\",\"CPU-A\",\"current\"\r\n"
+ "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"0\",\"0\",\"CPU-M\",\"powerup\"\r\n"
+ "^SCFG: \"MEopMode/PwrSave\",\"enabled\",\"0\",\"0\",\"CPU-M\",\"current\"\r\n"
+ "^SCFG: \"MEopMode/SRPOM\",\"0\"\r\n"
+ "^SCFG: \"MEopMode/USB/KeepData\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+ "^SCFG: \"MEopMode/USB/KeepData\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+ "^SCFG: \"MEShutdown/OnIgnition\",\"off\"\r\n"
+ "^SCFG: \"MEShutdown/Timer\",\"off\"\r\n"
+ "^SCFG: \"Misc/CId\",\"\"\r\n"
+ "^SCFG: \"Radio/Band/2G\",\"0000000f\"\r\n"
+ "^SCFG: \"Radio/Band/3G\",\"000400b5\"\r\n"
+ "^SCFG: \"Radio/Band/4G\",\"8a0e00d5\",\"000000e2\"\r\n"
+ "^SCFG: \"Radio/CNS\",\"0\"\r\n"
+ "^SCFG: \"Radio/Mtpl\",\"0\"\r\n"
+ "^SCFG: \"Radio/Mtpl/2G\",\"0\"\r\n"
+ "^SCFG: \"Radio/Mtpl/3G\",\"0\"\r\n"
+ "^SCFG: \"Radio/Mtpl/4G\",\"0\"\r\n"
+ "^SCFG: \"Radio/OutputPowerReduction\",\"4\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Event/ASC\",\"none\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Event/URC\",\"none\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Event/USB\",\"GPIO4\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",\"current\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Ports\",\"powerup\",\"acm0\",\"acm1\",\"acm2\",\"acm3\",\"diag\",\"mbim\",\"asc0\"\r\n"
+ "^SCFG: \"RemoteWakeUp/Pulse\",\"10\"\r\n"
+ "^SCFG: \"Serial/USB/DDD\",\"0\",\"0\",\"0409\",\"1e2d\",\"0065\",\"Cinterion\",\"LTE Modem\",\"8d8f\"\r\n"
+ "^SCFG: \"SIM/CS\",\"SIM1\"\r\n"
+ "^SCFG: \"SMS/4GPREF\",\"IMS\"\r\n"
+ "^SCFG: \"SMS/AutoAck\",\"0\"\r\n"
+ "^SCFG: \"SMS/RETRM\",\"30\"\r\n"
+ "^SCFG: \"URC/Ringline\",\"local\"\r\n"
+ "^SCFG: \"URC/Ringline/ActiveTime\",\"2\"\r\n";
+
+ expected_bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 25);
+ single = MM_MODEM_BAND_EGSM, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_DCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_PCS, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_G850, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_3, g_array_append_val (expected_bands, single); //
+ single = MM_MODEM_BAND_UTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_6, g_array_append_val (expected_bands, single); //
+ single = MM_MODEM_BAND_UTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_UTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_1, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_3, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_5, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_7, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_8, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_18, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_19, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_20, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_26, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_28, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_38, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_39, g_array_append_val (expected_bands, single);
+ single = MM_MODEM_BAND_EUTRAN_40, g_array_append_val (expected_bands, single);
+
+ common_test_scfg_response (response, MM_MODEM_CHARSET_GSM, expected_bands, MM_CINTERION_MODEM_FAMILY_DEFAULT, MM_CINTERION_RADIO_BAND_FORMAT_MULTIPLE);
g_array_unref (expected_bands);
}
@@ -327,7 +892,206 @@ test_cnmi_phs8 (void)
g_array_unref (expected_mt);
g_array_unref (expected_bm);
g_array_unref (expected_ds);
- g_array_unref (expected_bfr);}
+ g_array_unref (expected_bfr);
+}
+
+static void
+test_cnmi_other (void)
+{
+ GArray *expected_mode;
+ GArray *expected_mt;
+ GArray *expected_bm;
+ GArray *expected_ds;
+ GArray *expected_bfr;
+ guint val;
+ const gchar *response =
+ "+CNMI: (0-3),(0,1),(0,2,3),(0,2),(1)\r\n"
+ "\r\n";
+
+ expected_mode = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
+ val = 0, g_array_append_val (expected_mode, val);
+ val = 1, g_array_append_val (expected_mode, val);
+ val = 2, g_array_append_val (expected_mode, val);
+ val = 3, g_array_append_val (expected_mode, val);
+
+ expected_mt = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = 0, g_array_append_val (expected_mt, val);
+ val = 1, g_array_append_val (expected_mt, val);
+
+ expected_bm = g_array_sized_new (FALSE, FALSE, sizeof (guint), 2);
+ val = 0, g_array_append_val (expected_bm, val);
+ val = 2, g_array_append_val (expected_bm, val);
+ val = 3, g_array_append_val (expected_bm, val);
+
+ expected_ds = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+ val = 0, g_array_append_val (expected_ds, val);
+ val = 2, g_array_append_val (expected_ds, val);
+
+ expected_bfr = g_array_sized_new (FALSE, FALSE, sizeof (guint), 1);
+ val = 1, g_array_append_val (expected_bfr, val);
+
+ common_test_cnmi (response,
+ expected_mode,
+ expected_mt,
+ expected_bm,
+ expected_ds,
+ expected_bfr);
+
+ g_array_unref (expected_mode);
+ g_array_unref (expected_mt);
+ g_array_unref (expected_bm);
+ g_array_unref (expected_ds);
+ g_array_unref (expected_bfr);
+}
+
+/*****************************************************************************/
+/* Test ^SWWAN read */
+
+#define SWWAN_TEST_MAX_CIDS 2
+
+typedef struct {
+ guint cid;
+ MMBearerConnectionStatus state;
+} PdpContextState;
+
+typedef struct {
+ const gchar *response;
+ PdpContextState expected_items[SWWAN_TEST_MAX_CIDS];
+ gboolean skip_test_other_cids;
+} SwwanTest;
+
+/* Note: all tests are based on checking CIDs 2 and 3 */
+static const SwwanTest swwan_tests[] = {
+ /* No active PDP context reported (all disconnected) */
+ {
+ .response = "",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }
+ },
+ /* Don't test other CIDs because for those we would also return
+ * DISCONNECTED, not UNKNOWN. */
+ .skip_test_other_cids = TRUE
+ },
+ /* Single PDP context active (short version without interface index) */
+ {
+ .response = "^SWWAN: 3,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }
+ }
+ },
+ /* Single PDP context active (long version with interface index) */
+ {
+ .response = "^SWWAN: 3,1,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }
+ }
+ },
+ /* Single PDP context inactive (short version without interface index) */
+ {
+ .response = "^SWWAN: 3,0\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }
+ }
+ },
+ /* Single PDP context inactive (long version with interface index) */
+ {
+ .response = "^SWWAN: 3,0,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_UNKNOWN },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }
+ }
+ },
+ /* Multiple PDP contexts active (short version without interface index) */
+ {
+ .response = "^SWWAN: 2,1\r\n^SWWAN: 3,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }
+ }
+ },
+ /* Multiple PDP contexts active (long version with interface index) */
+ {
+ .response = "^SWWAN: 2,1,3\r\n^SWWAN: 3,1,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }
+ }
+ },
+ /* Multiple PDP contexts inactive (short version without interface index) */
+ {
+ .response = "^SWWAN: 2,0\r\n^SWWAN: 3,0\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }
+ }
+ },
+ /* Multiple PDP contexts inactive (long version with interface index) */
+ {
+ .response = "^SWWAN: 2,0,3\r\n^SWWAN: 3,0,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED }
+ }
+ },
+ /* Multiple PDP contexts active/inactive (short version without interface index) */
+ {
+ .response = "^SWWAN: 2,0\r\n^SWWAN: 3,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }
+ }
+ },
+ /* Multiple PDP contexts active/inactive (long version with interface index) */
+ {
+ .response = "^SWWAN: 2,0,3\r\n^SWWAN: 3,1,1\r\n",
+ .expected_items = {
+ { .cid = 2, .state = MM_BEARER_CONNECTION_STATUS_DISCONNECTED },
+ { .cid = 3, .state = MM_BEARER_CONNECTION_STATUS_CONNECTED }
+ }
+ }
+};
+
+static void
+test_swwan_pls8 (void)
+{
+ MMBearerConnectionStatus read_state;
+ GError *error = NULL;
+ guint i;
+
+ /* Base tests for successful responses */
+ for (i = 0; i < G_N_ELEMENTS (swwan_tests); i++) {
+ guint j;
+
+ /* Query for the expected items (CIDs 2 and 3) */
+ for (j = 0; j < SWWAN_TEST_MAX_CIDS; j++) {
+ read_state = mm_cinterion_parse_swwan_response (swwan_tests[i].response, swwan_tests[i].expected_items[j].cid, NULL, &error);
+ if (swwan_tests[i].expected_items[j].state == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+ g_clear_error (&error);
+ } else
+ g_assert_no_error (error);
+ g_assert_cmpint (read_state, ==, swwan_tests[i].expected_items[j].state);
+ }
+
+ /* Query for a CID which isn't replied (e.g. 12) */
+ if (!swwan_tests[i].skip_test_other_cids) {
+ read_state = mm_cinterion_parse_swwan_response (swwan_tests[i].response, 12, NULL, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+ g_assert_cmpint (read_state, ==, MM_BEARER_CONNECTION_STATUS_UNKNOWN);
+ g_clear_error (&error);
+ }
+ }
+
+ /* Additional tests for errors */
+ read_state = mm_cinterion_parse_swwan_response ("^GARBAGE", 2, NULL, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+ g_assert_cmpint (read_state, ==, MM_BEARER_CONNECTION_STATUS_UNKNOWN);
+ g_clear_error (&error);
+}
/*****************************************************************************/
/* Test ^SIND responses */
@@ -366,40 +1130,662 @@ test_sind_response_simstatus (void)
}
/*****************************************************************************/
+/* Test ^SMONG responses */
+
+static void
+common_test_smong_response (const gchar *response,
+ gboolean success,
+ MMModemAccessTechnology expected_access_tech)
+{
+ GError *error = NULL;
+ gboolean res;
+ MMModemAccessTechnology access_tech;
+
+ res = mm_cinterion_parse_smong_response (response, &access_tech, &error);
+
+ if (success) {
+ g_assert_no_error (error);
+ g_assert (res);
+ g_assert_cmpuint (access_tech, ==, expected_access_tech);
+ } else {
+ g_assert (error);
+ g_assert (!res);
+ }
+}
+
+static void
+test_smong_response_tc63i (void)
+{
+ const gchar *response =
+ "\r\n"
+ "GPRS Monitor\r\n"
+ "BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell #\r\n"
+ "0073 1 - - 262 02 2 00 01\r\n";
+ common_test_smong_response (response, TRUE, MM_MODEM_ACCESS_TECHNOLOGY_GPRS);
+}
+
+static void
+test_smong_response_other (void)
+{
+ const gchar *response =
+ "\r\n"
+ "GPRS Monitor\r\n"
+ "\r\n"
+ "BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell #\r\n"
+ " 44 1 - - 234 10 - - - \r\n";
+ common_test_smong_response (response, TRUE, MM_MODEM_ACCESS_TECHNOLOGY_GPRS);
+}
+
+static void
+test_smong_response_no_match (void)
+{
+ const gchar *response =
+ "\r\n"
+ "GPRS Monitor\r\n"
+ "\r\n"
+ "BCCH K PBCCH PAT MCC MNC NOM TA RAC # Cell #\r\n"
+ " 44 1 - - 234 10 - - - \r\n";
+ common_test_smong_response (response, FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+}
+
+/*****************************************************************************/
+/* Test ^SLCC URCs */
+
+static void
+common_test_slcc_urc (const gchar *urc,
+ const MMCallInfo *expected_call_info_list,
+ guint expected_call_info_list_size)
+{
+ GError *error = NULL;
+ GRegex *slcc_regex = NULL;
+ gboolean result;
+ GMatchInfo *match_info = NULL;
+ gchar *str;
+ GList *call_info_list = NULL;
+ GList *l;
+
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+ slcc_regex = mm_cinterion_get_slcc_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (slcc_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ /* read full matched content */
+ str = g_match_info_fetch (match_info, 0);
+ g_assert (str);
+
+ result = mm_cinterion_parse_slcc_list (str, NULL, &call_info_list, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ g_debug ("found %u calls", g_list_length (call_info_list));
+
+ if (expected_call_info_list) {
+ g_assert (call_info_list);
+ g_assert_cmpuint (g_list_length (call_info_list), ==, expected_call_info_list_size);
+ } else
+ g_assert (!call_info_list);
+
+ for (l = call_info_list; l; l = g_list_next (l)) {
+ const MMCallInfo *call_info = (const MMCallInfo *)(l->data);
+ gboolean found = FALSE;
+ guint i;
+
+ g_debug ("call at index %u: direction %s, state %s, number %s",
+ call_info->index,
+ mm_call_direction_get_string (call_info->direction),
+ mm_call_state_get_string (call_info->state),
+ call_info->number ? call_info->number : "n/a");
+
+ for (i = 0; !found && i < expected_call_info_list_size; i++)
+ found = ((call_info->index == expected_call_info_list[i].index) &&
+ (call_info->direction == expected_call_info_list[i].direction) &&
+ (call_info->state == expected_call_info_list[i].state) &&
+ (g_strcmp0 (call_info->number, expected_call_info_list[i].number) == 0));
+
+ g_assert (found);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (slcc_regex);
+ g_free (str);
+
+ mm_cinterion_call_info_list_free (call_info_list);
+}
+
+static void
+test_slcc_urc_empty (void)
{
-#if defined ENABLE_TEST_MESSAGE_TRACES
- /* Dummy log function */
- va_list args;
- gchar *msg;
+ const gchar *urc = "\r\n^SLCC: \r\n";
- va_start (args, fmt);
- msg = g_strdup_vprintf (fmt, args);
- va_end (args);
- g_print ("%s\n", msg);
- g_free (msg);
-#endif
+ common_test_slcc_urc (urc, NULL, 0);
}
+static void
+test_slcc_urc_single (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }
+ };
+
+ const gchar *urc =
+ "\r\n^SLCC: 1,1,0,0,0,0,\"123456789\",161"
+ "\r\n^SLCC: \r\n";
+
+ common_test_slcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_slcc_urc_multiple (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, NULL },
+ { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" },
+ { 3, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "987654321" },
+ };
+
+ const gchar *urc =
+ "\r\n^SLCC: 1,1,0,0,1,0" /* number unknown */
+ "\r\n^SLCC: 2,1,0,0,1,0,\"123456789\",161"
+ "\r\n^SLCC: 3,1,0,0,1,0,\"987654321\",161,\"Alice\""
+ "\r\n^SLCC: \r\n";
+
+ common_test_slcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_slcc_urc_complex (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" },
+ { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_WAITING, (gchar *) "987654321" },
+ };
+
+ const gchar *urc =
+ "\r\n^CIEV: 1,0" /* some different URC before our match */
+ "\r\n^SLCC: 1,1,0,0,0,0,\"123456789\",161"
+ "\r\n^SLCC: 2,1,5,0,0,0,\"987654321\",161"
+ "\r\n^SLCC: \r\n"
+ "\r\n^CIEV: 1,0" /* some different URC after our match */;
+
+ common_test_slcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+/*****************************************************************************/
+/* Test +CTZU URCs */
+
+static void
+common_test_ctzu_urc (const gchar *urc,
+ const gchar *expected_iso8601,
+ gint expected_offset,
+ gint expected_dst_offset)
+{
+ GError *error = NULL;
+ GRegex *ctzu_regex = NULL;
+ gboolean result;
+ GMatchInfo *match_info = NULL;
+ gchar *iso8601;
+ MMNetworkTimezone *tz = NULL;
+
+ ctzu_regex = mm_cinterion_get_ctzu_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (ctzu_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ result = mm_cinterion_parse_ctzu_urc (match_info, &iso8601, &tz, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ g_assert (iso8601);
+ g_assert_cmpstr (expected_iso8601, ==, iso8601);
+ g_free (iso8601);
+
+ g_assert (tz);
+ g_assert_cmpint (expected_offset, ==, mm_network_timezone_get_offset (tz));
+
+ if (expected_dst_offset >= 0)
+ g_assert_cmpuint ((guint)expected_dst_offset, ==, mm_network_timezone_get_dst_offset (tz));
+
+ g_object_unref (tz);
+ g_match_info_free (match_info);
+ g_regex_unref (ctzu_regex);
+}
+
+static void
+test_ctzu_urc_simple (void)
+{
+ const gchar *urc = "\r\n+CTZU: \"19/07/09,11:15:40\",+08\r\n";
+ const gchar *expected_iso8601 = "2019-07-09T11:15:40+02";
+ gint expected_offset = 120;
+ gint expected_dst_offset = -1; /* not given */
+
+ common_test_ctzu_urc (urc, expected_iso8601, expected_offset, expected_dst_offset);
+}
+
+static void
+test_ctzu_urc_full (void)
+{
+ const gchar *urc = "\r\n+CTZU: \"19/07/09,11:15:40\",+08,1\r\n";
+ const gchar *expected_iso8601 = "2019-07-09T11:15:40+02";
+ gint expected_offset = 120;
+ gint expected_dst_offset = 60;
+
+ common_test_ctzu_urc (urc, expected_iso8601, expected_offset, expected_dst_offset);
+}
+
+/*****************************************************************************/
+/* Test ^SMONI responses */
+
+typedef struct {
+ const gchar *str;
+ MMCinterionRadioGen tech;
+ gdouble rssi;
+ gdouble ecn0;
+ gdouble rscp;
+ gdouble rsrp;
+ gdouble rsrq;
+} SMoniResponseTest;
+
+static const SMoniResponseTest smoni_response_tests[] = {
+ {
+ .str = "^SMONI: 2G,71,-61,262,02,0143,83BA,33,33,3,6,G,NOCONN",
+ .tech = MM_CINTERION_RADIO_GEN_2G,
+ .rssi = -61.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 2G,SEARCH,SEARCH",
+ .tech = MM_CINTERION_RADIO_GEN_NONE,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 2G,673,-89,262,07,4EED,A500,16,16,7,4,G,5,-107,LIMSRV",
+ .tech = MM_CINTERION_RADIO_GEN_2G,
+ .rssi = -89.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 2G,673,-80,262,07,4EED,A500,35,35,7,4,G,643,4,0,-80,0,S_FR",
+ .tech = MM_CINTERION_RADIO_GEN_2G,
+ .rssi = -80.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 3G,10564,296,-7.5,-79,262,02,0143,00228FF,-92,-78,NOCONN",
+ .tech = MM_CINTERION_RADIO_GEN_3G,
+ .rssi = 0.0,
+ .ecn0 = -7.5,
+ .rscp = -79.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 3G,SEARCH,SEARCH",
+ .tech = MM_CINTERION_RADIO_GEN_NONE,
+ .rssi = 0.0,
+ .ecn0 = 0,
+ .rscp = 0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 3G,10564,96,-6.5,-77,262,02,0143,00228FF,-92,-78,LIMSRV",
+ .tech = MM_CINTERION_RADIO_GEN_3G,
+ .rssi = 0.0,
+ .ecn0 = -6.5,
+ .rscp = -77.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 3G,10737,131,-5,-93,260,01,7D3D,C80BC9A,--,--,----,---,-,-5,-93,0,01,06",
+ .tech = MM_CINTERION_RADIO_GEN_3G,
+ .rssi = 0.0,
+ .ecn0 = -5.0,
+ .rscp = -93.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-94,-7,NOCONN",
+ .tech = MM_CINTERION_RADIO_GEN_4G,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = -94.0,
+ .rsrq = -7.0
+ },
+ {
+ .str = "^SMONI: 4G,SEARCH",
+ .tech = MM_CINTERION_RADIO_GEN_NONE,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = 0.0,
+ .rsrq = 0.0
+ },
+ {
+ .str = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,33,-90,-6,LIMSRV",
+ .tech = MM_CINTERION_RADIO_GEN_4G,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = -90.0,
+ .rsrq = -6.0
+ },
+ {
+ .str = "^SMONI: 4G,6300,20,10,10,FDD,262,02,BF75,0345103,350,90,-101,-7,CONN",
+ .tech = MM_CINTERION_RADIO_GEN_4G,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = -101.0,
+ .rsrq = -7.0
+ },
+ {
+ .str = "^SMONI: 4G,2850,7,20,20,FDD,262,02,C096,027430F,275,11,-114,-9,NOCONN",
+ .tech = MM_CINTERION_RADIO_GEN_4G,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = -114.0,
+ .rsrq = -9.0
+ },
+ {
+ .str = "^SMONI: 4G,2850,7,20,20,FDD,262,02,C096,027430F,275,-,-113,-8,CONN",
+ .tech = MM_CINTERION_RADIO_GEN_4G,
+ .rssi = 0.0,
+ .ecn0 = 0.0,
+ .rscp = 0.0,
+ .rsrp = -113.0,
+ .rsrq = -8.0
+ }
+};
+
+static void
+test_smoni_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (smoni_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMCinterionRadioGen tech = MM_CINTERION_RADIO_GEN_NONE;
+ gdouble rssi = MM_SIGNAL_UNKNOWN;
+ gdouble ecn0 = MM_SIGNAL_UNKNOWN;
+ gdouble rscp = MM_SIGNAL_UNKNOWN;
+ gdouble rsrp = MM_SIGNAL_UNKNOWN;
+ gdouble rsrq = MM_SIGNAL_UNKNOWN;
+
+ success = mm_cinterion_parse_smoni_query_response (smoni_response_tests[i].str,
+ &tech, &rssi,
+ &ecn0, &rscp,
+ &rsrp, &rsrq,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_assert_cmpuint (smoni_response_tests[i].tech, ==, tech);
+ switch (smoni_response_tests[i].tech) {
+ case MM_CINTERION_RADIO_GEN_2G:
+ g_assert_cmpfloat_tolerance (rssi, smoni_response_tests[i].rssi, 0.1);
+ break;
+ case MM_CINTERION_RADIO_GEN_3G:
+ g_assert_cmpfloat_tolerance (ecn0, smoni_response_tests[i].ecn0, 0.1);
+ g_assert_cmpfloat_tolerance (rscp, smoni_response_tests[i].rscp, 0.1);
+ break;
+ case MM_CINTERION_RADIO_GEN_4G:
+ g_assert_cmpfloat_tolerance (rsrp, smoni_response_tests[i].rsrp, 0.1);
+ g_assert_cmpfloat_tolerance (rsrq, smoni_response_tests[i].rsrq, 0.1);
+ break;
+ case MM_CINTERION_RADIO_GEN_NONE:
+ default:
+ break;
+ }
+ }
+}
+
+static void
+test_smoni_response_to_signal (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (smoni_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+
+ success = mm_cinterion_smoni_response_to_signal_info (smoni_response_tests[i].str,
+ &gsm, &umts, &lte,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ switch (smoni_response_tests[i].tech) {
+ case MM_CINTERION_RADIO_GEN_2G:
+ g_assert (gsm);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), smoni_response_tests[i].rssi, 0.1);
+ g_object_unref (gsm);
+ g_assert (!umts);
+ g_assert (!lte);
+ break;
+ case MM_CINTERION_RADIO_GEN_3G:
+ g_assert (umts);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), smoni_response_tests[i].rscp, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), smoni_response_tests[i].ecn0, 0.1);
+ g_object_unref (umts);
+ g_assert (!gsm);
+ g_assert (!lte);
+ break;
+ case MM_CINTERION_RADIO_GEN_4G:
+ g_assert (lte);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), smoni_response_tests[i].rsrp, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), smoni_response_tests[i].rsrq, 0.1);
+ g_object_unref (lte);
+ g_assert (!gsm);
+ g_assert (!umts);
+ break;
+ case MM_CINTERION_RADIO_GEN_NONE:
+ default:
+ g_assert (!gsm);
+ g_assert (!umts);
+ g_assert (!lte);
+ break;
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Test ^SCFG="MEopMode/Prov/Cfg" responses */
+
+typedef struct {
+ const gchar *str;
+ MMCinterionModemFamily modem_family;
+ gboolean success;
+ guint expected_cid;
+} ProvcfgResponseTest;
+
+static const ProvcfgResponseTest provcfg_response_tests[] = {
+ {
+
+ .str = "^SCFG: \"MEopMode/Prov/Cfg\",\"vdfde\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT,
+ .success = TRUE,
+ .expected_cid = 1,
+ },
+ {
+
+ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"attus\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_IMT,
+ .success = TRUE,
+ .expected_cid = 1,
+ },
+ {
+
+ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"2\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT,
+ .success = TRUE,
+ .expected_cid = 3,
+ },
+ {
+
+ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"vzwdcus\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT,
+ .success = TRUE,
+ .expected_cid = 3,
+ },
+ {
+
+ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"tmode\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT,
+ .success = TRUE,
+ .expected_cid = 2,
+ },
+ {
+ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"fallback*\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT,
+ .success = TRUE,
+ .expected_cid = 1,
+ },
+ {
+ /* commas not allowed by the regex */
+ .str = "* ^SCFG: \"MEopMode/Prov/Cfg\",\"something,with,commas\"",
+ .modem_family = MM_CINTERION_MODEM_FAMILY_DEFAULT,
+ .success = FALSE,
+ }
+};
+
+static void
+test_provcfg_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (provcfg_response_tests); i++) {
+ gint cid = -1;
+ gboolean result;
+ GError *error = NULL;
+
+ result = mm_cinterion_provcfg_response_to_cid (provcfg_response_tests[i].str,
+ provcfg_response_tests[i].modem_family,
+ MM_MODEM_CHARSET_GSM,
+ NULL,
+ &cid,
+ &error);
+ if (provcfg_response_tests[i].success) {
+ g_assert_no_error (error);
+ g_assert (result);
+ g_assert_cmpuint (cid, ==, provcfg_response_tests[i].expected_cid);
+ } else {
+ g_assert (error);
+ g_assert (!result);
+ }
+ }
+}
+
+/*****************************************************************************/
+/* Test ^SGAUTH responses */
+
+static void
+test_sgauth_response (void)
+{
+ gboolean result;
+ MMBearerAllowedAuth auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ gchar *username = NULL;
+ GError *error = NULL;
+
+ const gchar *response =
+ "^SGAUTH: 1,2,\"vf\"\r\n"
+ "^SGAUTH: 2,1,\"\"\r\n"
+ "^SGAUTH: 3,0\r\n";
+
+ /* CID 1 */
+ result = mm_cinterion_parse_sgauth_response (response, 1, &auth, &username, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+ g_assert_cmpuint (auth, ==, MM_BEARER_ALLOWED_AUTH_CHAP);
+ g_assert_cmpstr (username, ==, "vf");
+
+ auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ g_clear_pointer (&username, g_free);
+
+ /* CID 2 */
+ result = mm_cinterion_parse_sgauth_response (response, 2, &auth, &username, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+ g_assert_cmpuint (auth, ==, MM_BEARER_ALLOWED_AUTH_PAP);
+ g_assert_null (username);
+
+ auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+
+ /* CID 3 */
+ result = mm_cinterion_parse_sgauth_response (response, 3, &auth, &username, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+ g_assert_cmpuint (auth, ==, MM_BEARER_ALLOWED_AUTH_NONE);
+ g_assert_null (username);
+
+ auth = MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+
+ /* CID 4 */
+ result = mm_cinterion_parse_sgauth_response (response, 4, &auth, &username, &error);
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND);
+ g_assert (!result);
+}
+
+/*****************************************************************************/
+
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/cinterion/scfg", test_scfg);
+ g_test_add_func ("/MM/cinterion/scfg/ehs5", test_scfg_ehs5);
+ g_test_add_func ("/MM/cinterion/scfg/pls62/gsm", test_scfg_pls62_gsm);
+ g_test_add_func ("/MM/cinterion/scfg/pls62/ucs2", test_scfg_pls62_ucs2);
+ g_test_add_func ("/MM/cinterion/scfg/alas5", test_scfg_alas5);
g_test_add_func ("/MM/cinterion/scfg/response/3g", test_scfg_response_3g);
g_test_add_func ("/MM/cinterion/scfg/response/2g", test_scfg_response_2g);
- g_test_add_func ("/MM/cinterion/scfg/response/2g/ucs2", test_scfg_response_2g_ucs2);
+ g_test_add_func ("/MM/cinterion/scfg/response/pls62/gsm", test_scfg_response_pls62_gsm);
+ g_test_add_func ("/MM/cinterion/scfg/response/pls62/ucs2",test_scfg_response_pls62_ucs2);
+ g_test_add_func ("/MM/cinterion/scfg/response/alas5", test_scfg_response_alas5);
g_test_add_func ("/MM/cinterion/cnmi/phs8", test_cnmi_phs8);
+ g_test_add_func ("/MM/cinterion/cnmi/other", test_cnmi_other);
+ g_test_add_func ("/MM/cinterion/swwan/pls8", test_swwan_pls8);
g_test_add_func ("/MM/cinterion/sind/response/simstatus", test_sind_response_simstatus);
+ g_test_add_func ("/MM/cinterion/smong/response/tc63i", test_smong_response_tc63i);
+ g_test_add_func ("/MM/cinterion/smong/response/other", test_smong_response_other);
+ g_test_add_func ("/MM/cinterion/smong/response/no-match", test_smong_response_no_match);
+ g_test_add_func ("/MM/cinterion/slcc/urc/empty", test_slcc_urc_empty);
+ g_test_add_func ("/MM/cinterion/slcc/urc/single", test_slcc_urc_single);
+ g_test_add_func ("/MM/cinterion/slcc/urc/multiple", test_slcc_urc_multiple);
+ g_test_add_func ("/MM/cinterion/slcc/urc/complex", test_slcc_urc_complex);
+ g_test_add_func ("/MM/cinterion/ctzu/urc/simple", test_ctzu_urc_simple);
+ g_test_add_func ("/MM/cinterion/ctzu/urc/full", test_ctzu_urc_full);
+ g_test_add_func ("/MM/cinterion/smoni/query_response", test_smoni_response);
+ g_test_add_func ("/MM/cinterion/smoni/query_response_to_signal", test_smoni_response_to_signal);
+ g_test_add_func ("/MM/cinterion/scfg/provcfg", test_provcfg_response);
+ g_test_add_func ("/MM/cinterion/sgauth", test_sgauth_response);
return g_test_run ();
}
diff --git a/plugins/dell/77-mm-dell-port-types.rules b/plugins/dell/77-mm-dell-port-types.rules
new file mode 100644
index 00000000..fa01c116
--- /dev/null
+++ b/plugins/dell/77-mm-dell-port-types.rules
@@ -0,0 +1,32 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_dell_port_types_end"
+
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", GOTO="mm_dell_vendorcheck"
+GOTO="mm_dell_port_types_end"
+
+LABEL="mm_dell_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Dell DW5821e (default 0x81d7, with esim support 0x81e0)
+# if 02: primary port
+# if 03: secondary port
+# if 04: raw NMEA port
+# if 05: diag/qcdm port
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d7", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81e0", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+
+# Dell DW5820e
+# if 02: AT port
+# if 04: debug port (ignore)
+# if 06: AT port
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="81d9", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+
+GOTO="mm_dell_port_types_end"
+LABEL="mm_dell_port_types_end"
diff --git a/plugins/dell/mm-plugin-dell.c b/plugins/dell/mm-plugin-dell.c
new file mode 100644
index 00000000..01914fb8
--- /dev/null
+++ b/plugins/dell/mm-plugin-dell.c
@@ -0,0 +1,527 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2015-2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-dell.h"
+#include "mm-common-novatel.h"
+#include "mm-private-boxed-types.h"
+#include "mm-broadband-modem.h"
+#include "mm-broadband-modem-novatel.h"
+#include "mm-common-novatel.h"
+#include "mm-broadband-modem-sierra.h"
+#include "mm-common-sierra.h"
+#include "mm-broadband-modem-telit.h"
+#include "mm-broadband-modem-xmm.h"
+#include "mm-common-telit.h"
+#include "mm-log-object.h"
+
+#if defined WITH_QMI
+#include "mm-broadband-modem-qmi.h"
+#endif
+
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim.h"
+#include "mm-broadband-modem-mbim-xmm.h"
+#include "mm-broadband-modem-mbim-foxconn.h"
+#endif
+
+#define MAX_PORT_PROBE_TIMEOUTS 3
+
+G_DEFINE_TYPE (MMPluginDell, mm_plugin_dell, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+#define TAG_DELL_MANUFACTURER "dell-manufacturer"
+typedef enum {
+ DELL_MANUFACTURER_UNKNOWN = 0,
+ DELL_MANUFACTURER_NOVATEL = 1,
+ DELL_MANUFACTURER_SIERRA = 2,
+ DELL_MANUFACTURER_ERICSSON = 3,
+ DELL_MANUFACTURER_TELIT = 4
+} DellManufacturer;
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortSerialAt *port;
+ guint gmi_retries;
+ guint cgmi_retries;
+ guint ati_retries;
+ guint timeouts;
+} CustomInitContext;
+
+static void
+custom_init_context_free (CustomInitContext *ctx)
+{
+ g_object_unref (ctx->port);
+ g_slice_free (CustomInitContext, ctx);
+}
+
+static gboolean
+dell_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+novatel_custom_init_ready (MMPortProbe *probe,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_common_novatel_custom_init_finish (probe, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+sierra_custom_init_ready (MMPortProbe *probe,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_common_sierra_custom_init_finish (probe, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+telit_custom_init_ready (MMPortProbe *probe,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!telit_custom_init_finish (probe, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void custom_init_step (GTask *task);
+
+static void
+custom_init_step_next_command (GTask *task)
+{
+ CustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->timeouts = 0;
+ if (ctx->gmi_retries > 0)
+ ctx->gmi_retries = 0;
+ else if (ctx->cgmi_retries > 0)
+ ctx->cgmi_retries = 0;
+ else if (ctx->ati_retries > 0)
+ ctx->ati_retries = 0;
+ custom_init_step (task);
+}
+
+static void
+response_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+ const gchar *response;
+ GError *error = NULL;
+ gchar *lower;
+ DellManufacturer manufacturer;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* Non-timeout error, jump to next command */
+ if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ mm_obj_dbg (probe, "error probing AT port: %s", error->message);
+ g_error_free (error);
+ custom_init_step_next_command (task);
+ return;
+ }
+ /* Directly retry same command on timeout */
+ ctx->timeouts++;
+ custom_init_step (task);
+ g_error_free (error);
+ return;
+ }
+
+ /* Guess manufacturer from response */
+ lower = g_ascii_strdown (response, -1);
+ if (strstr (lower, "novatel"))
+ manufacturer = DELL_MANUFACTURER_NOVATEL;
+ else if (strstr (lower, "sierra"))
+ manufacturer = DELL_MANUFACTURER_SIERRA;
+ else if (strstr (lower, "ericsson"))
+ manufacturer = DELL_MANUFACTURER_ERICSSON;
+ else if (strstr (lower, "telit"))
+ manufacturer = DELL_MANUFACTURER_TELIT;
+ else
+ manufacturer = DELL_MANUFACTURER_UNKNOWN;
+ g_free (lower);
+
+ /* Tag based on manufacturer */
+ if (manufacturer != DELL_MANUFACTURER_UNKNOWN) {
+ g_object_set_data (G_OBJECT (probe), TAG_DELL_MANUFACTURER, GUINT_TO_POINTER (manufacturer));
+
+ /* Run additional custom init, if needed */
+
+ if (manufacturer == DELL_MANUFACTURER_NOVATEL) {
+ mm_common_novatel_custom_init (probe,
+ ctx->port,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) novatel_custom_init_ready,
+ task);
+ return;
+ }
+
+ if (manufacturer == DELL_MANUFACTURER_SIERRA) {
+ mm_common_sierra_custom_init (probe,
+ ctx->port,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) sierra_custom_init_ready,
+ task);
+ return;
+ }
+
+ if (manufacturer == DELL_MANUFACTURER_TELIT) {
+ telit_custom_init (probe,
+ ctx->port,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) telit_custom_init_ready,
+ task);
+ return;
+ }
+
+ /* Finish custom_init */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If we got a response, but we didn't get an expected string, try with next command */
+ custom_init_step_next_command (task);
+}
+
+static void
+custom_init_step (GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* If cancelled, end without error right away */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ mm_obj_dbg (probe, "no need to keep on running custom init: cancelled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+#if defined WITH_QMI
+ /* If device has a QMI port, don't run anything else, as we don't care */
+ if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) {
+ mm_obj_dbg (probe, "no need to run custom init: device has QMI port");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+#endif
+
+#if defined WITH_MBIM
+ /* If device has a MBIM port, don't run anything else, as we don't care */
+ if (mm_port_probe_list_has_mbim_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) {
+ mm_obj_dbg (probe, "no need to run custom init: device has MBIM port");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+#endif
+
+ if (ctx->timeouts >= MAX_PORT_PROBE_TIMEOUTS) {
+ mm_obj_dbg (probe, "couldn't detect real manufacturer: too many timeouts");
+ mm_port_probe_set_result_at (probe, FALSE);
+ goto out;
+ }
+
+ if (ctx->gmi_retries > 0) {
+ ctx->gmi_retries--;
+ mm_port_serial_at_command (ctx->port,
+ "AT+GMI",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)response_ready,
+ task);
+ return;
+ }
+
+ if (ctx->cgmi_retries > 0) {
+ ctx->cgmi_retries--;
+ mm_port_serial_at_command (ctx->port,
+ "AT+CGMI",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)response_ready,
+ task);
+ return;
+ }
+
+ if (ctx->ati_retries > 0) {
+ ctx->ati_retries--;
+ /* Note: in Ericsson devices, ATI3 seems to reply the vendor string */
+ mm_port_serial_at_command (ctx->port,
+ "ATI1I2I3",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)response_ready,
+ task);
+ return;
+ }
+
+ mm_obj_dbg (probe, "couldn't detect real manufacturer: all retries consumed");
+out:
+ /* Finish custom_init */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+dell_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CustomInitContext *ctx;
+
+ ctx = g_slice_new0 (CustomInitContext);
+ ctx->port = g_object_ref (port);
+ ctx->gmi_retries = 3;
+ ctx->cgmi_retries = 1;
+ ctx->ati_retries = 1;
+
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) custom_init_context_free);
+
+ custom_init_step (task);
+}
+
+/*****************************************************************************/
+
+static gboolean
+port_probe_list_has_manufacturer_port (GList *probes,
+ DellManufacturer manufacturer)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (l->data), TAG_DELL_MANUFACTURER)) == manufacturer)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ /* Note: at this point we don't make any difference between different
+ * Dell-branded QMI or MBIM modems; they may come from Novatel, Ericsson or
+ * Sierra. */
+
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ /* Specific implementation for the DW5821e */
+ if (vendor == 0x413c && (product == 0x81d7 || product == 0x81e0)) {
+ mm_obj_dbg (self, "MBIM-powered DW5821e (T77W968) modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_foxconn_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ if (mm_port_probe_list_is_xmm (probes)) {
+ mm_obj_dbg (self, "MBIM-powered XMM-based modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ mm_obj_dbg (self, "MBIM-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_NOVATEL)) {
+ mm_obj_dbg (self, "Novatel-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_novatel_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_SIERRA)) {
+ mm_obj_dbg (self, "Sierra-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_sierra_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ if (port_probe_list_has_manufacturer_port (probes, DELL_MANUFACTURER_TELIT)) {
+ mm_obj_dbg (self, "Telit-powered Dell-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ if (mm_port_probe_list_is_xmm (probes)) {
+ mm_obj_dbg (self, "XMM-based modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ mm_obj_dbg (self, "Dell-branded generic modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+static gboolean
+grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error)
+{
+ if (MM_IS_BROADBAND_MODEM_SIERRA (modem))
+ return mm_common_sierra_grab_port (self, modem, probe, error);
+
+ if (MM_IS_BROADBAND_MODEM_TELIT (modem))
+ return telit_grab_port (self, modem, probe, error);
+
+ return mm_base_modem_grab_port (modem,
+ mm_port_probe_peek_port (probe),
+ mm_port_probe_get_port_type (probe),
+ MM_PORT_SERIAL_AT_FLAG_NONE,
+ error);
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendors[] = { 0x413c, 0 };
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (dell_custom_init),
+ .finish = G_CALLBACK (dell_custom_init_finish),
+ };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_DELL,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ MM_PLUGIN_XMM_PROBE, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_dell_init (MMPluginDell *self)
+{
+}
+
+static void
+mm_plugin_dell_class_init (MMPluginDellClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+ plugin_class->grab_port = grab_port;
+}
diff --git a/plugins/dell/mm-plugin-dell.h b/plugins/dell/mm-plugin-dell.h
new file mode 100644
index 00000000..cc1a539e
--- /dev/null
+++ b/plugins/dell/mm-plugin-dell.h
@@ -0,0 +1,46 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_DELL_H
+#define MM_PLUGIN_DELL_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_DELL (mm_plugin_dell_get_type ())
+#define MM_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDell))
+#define MM_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_DELL, MMPluginDellClass))
+#define MM_IS_PLUGIN_DELL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_DELL))
+#define MM_IS_PLUGIN_DELL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_DELL))
+#define MM_PLUGIN_DELL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_DELL, MMPluginDellClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginDell;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginDellClass;
+
+GType mm_plugin_dell_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_DELL_H */
diff --git a/plugins/dlink/77-mm-dlink-port-types.rules b/plugins/dlink/77-mm-dlink-port-types.rules
new file mode 100644
index 00000000..0dc7afce
--- /dev/null
+++ b/plugins/dlink/77-mm-dlink-port-types.rules
@@ -0,0 +1,16 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_dlink_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2001", GOTO="mm_dlink_port_types"
+GOTO="mm_dlink_port_types_end"
+
+LABEL="mm_dlink_port_types"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# D-Link DWM-222
+ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7e35", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+
+LABEL="mm_dlink_port_types_end"
diff --git a/plugins/dlink/mm-plugin-dlink.c b/plugins/dlink/mm-plugin-dlink.c
new file mode 100644
index 00000000..7fb0e962
--- /dev/null
+++ b/plugins/dlink/mm-plugin-dlink.c
@@ -0,0 +1,94 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-port-enums-types.h"
+#include "mm-log-object.h"
+#include "mm-plugin-dlink.h"
+#include "mm-broadband-modem.h"
+
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginDlink, mm_plugin_dlink, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered D-Link modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendor_ids[] = { 0x2001, 0 };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_DLINK,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_dlink_init (MMPluginDlink *self)
+{
+}
+
+static void
+mm_plugin_dlink_class_init (MMPluginDlinkClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/dlink/mm-plugin-dlink.h b/plugins/dlink/mm-plugin-dlink.h
new file mode 100644
index 00000000..13453cb0
--- /dev/null
+++ b/plugins/dlink/mm-plugin-dlink.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_DLINK_H
+#define MM_PLUGIN_DLINK_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_DLINK (mm_plugin_dlink_get_type ())
+#define MM_PLUGIN_DLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_DLINK, MMPluginDlink))
+#define MM_PLUGIN_DLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_DLINK, MMPluginDlinkClass))
+#define MM_IS_PLUGIN_DLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_DLINK))
+#define MM_IS_PLUGIN_DLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_DLINK))
+#define MM_PLUGIN_DLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_DLINK, MMPluginDlinkClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginDlink;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginDlinkClass;
+
+GType mm_plugin_dlink_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_DLINK_H */
diff --git a/plugins/fibocom/77-mm-fibocom-port-types.rules b/plugins/fibocom/77-mm-fibocom-port-types.rules
new file mode 100644
index 00000000..81fcd900
--- /dev/null
+++ b/plugins/fibocom/77-mm-fibocom-port-types.rules
@@ -0,0 +1,34 @@
+# do not edit this file, it will be overwritten on update
+ACTION!="add|change|move|bind", GOTO="mm_fibocom_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2cb7", GOTO="mm_fibocom_port_types"
+GOTO="mm_fibocom_port_types_end"
+
+LABEL="mm_fibocom_port_types"
+
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Fibocom L850-GL
+# ttyACM0 (if #2): AT port
+# ttyACM1 (if #4): debug port (ignore)
+# ttyACM2 (if #6): AT port
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+
+# Fibocom NL668-AM
+# ttyACM0 (if #2): AT port
+# ttyACM1 (if #3): AT port
+# ttyACM2 (if #4): debug port (ignore)
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="01a0", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+
+# Fibocom FM150
+# ttyUSB0 (if #0): QCDM port
+# ttyUSB1 (if #1): AT port
+# ttyUSB2 (if #2): AT port
+# ttyUSB2 (if #3): Ignore
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="2cb7", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+
+LABEL="mm_fibocom_port_types_end"
diff --git a/plugins/fibocom/mm-plugin-fibocom.c b/plugins/fibocom/mm-plugin-fibocom.c
new file mode 100644
index 00000000..1ff9f17c
--- /dev/null
+++ b/plugins/fibocom/mm-plugin-fibocom.c
@@ -0,0 +1,132 @@
+/* -*- 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) 2018-2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-plugin-fibocom.h"
+#include "mm-broadband-modem.h"
+#include "mm-broadband-modem-xmm.h"
+
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim.h"
+#include "mm-broadband-modem-mbim-xmm.h"
+#endif
+
+#if defined WITH_QMI
+#include "mm-broadband-modem-qmi.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginFibocom, mm_plugin_fibocom, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ if (mm_port_probe_list_is_xmm (probes)) {
+ mm_obj_dbg (self, "MBIM-powered XMM-based Fibocom modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+ mm_obj_dbg (self, "MBIM-powered Fibocom modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Fibocom modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ if (mm_port_probe_list_is_xmm (probes)) {
+ mm_obj_dbg (self, "XMM-based Fibocom modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+
+ mm_obj_dbg (self, "Fibocom modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendor_ids[] = { 0x2cb7, 0 };
+ static const gchar *drivers[] = { "cdc_mbim", "qmi_wwan", NULL };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_FIBOCOM,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_DRIVERS, drivers,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_XMM_PROBE, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_fibocom_init (MMPluginFibocom *self)
+{
+}
+
+static void
+mm_plugin_fibocom_class_init (MMPluginFibocomClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/fibocom/mm-plugin-fibocom.h b/plugins/fibocom/mm-plugin-fibocom.h
new file mode 100644
index 00000000..e5289979
--- /dev/null
+++ b/plugins/fibocom/mm-plugin-fibocom.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_FIBOCOM_H
+#define MM_PLUGIN_FIBOCOM_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_FIBOCOM (mm_plugin_fibocom_get_type ())
+#define MM_PLUGIN_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_FIBOCOM, MMPluginFibocom))
+#define MM_PLUGIN_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_FIBOCOM, MMPluginFibocomClass))
+#define MM_IS_PLUGIN_FIBOCOM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_FIBOCOM))
+#define MM_IS_PLUGIN_FIBOCOM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_FIBOCOM))
+#define MM_PLUGIN_FIBOCOM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_FIBOCOM, MMPluginFibocomClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginFibocom;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginFibocomClass;
+
+GType mm_plugin_fibocom_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_FIBOCOM_H */
diff --git a/plugins/foxconn/77-mm-foxconn-port-types.rules b/plugins/foxconn/77-mm-foxconn-port-types.rules
new file mode 100644
index 00000000..344df152
--- /dev/null
+++ b/plugins/foxconn/77-mm-foxconn-port-types.rules
@@ -0,0 +1,26 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_foxconn_port_types_end"
+
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0489", GOTO="mm_foxconn_vendorcheck"
+GOTO="mm_foxconn_port_types_end"
+
+LABEL="mm_foxconn_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Foxconn T77w968 (default 0xe0b4, with esim support 0xe0b5)
+# if 02: primary port
+# if 03: secondary port
+# if 04: raw NMEA port
+# if 05: diag/qcdm port
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b4", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="0489", ATTRS{idProduct}=="e0b5", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+
+GOTO="mm_foxconn_port_types_end"
+LABEL="mm_foxconn_port_types_end"
diff --git a/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c b/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c
new file mode 100644
index 00000000..cd1aeb84
--- /dev/null
+++ b/plugins/foxconn/mm-broadband-modem-mbim-foxconn.c
@@ -0,0 +1,579 @@
+/* -*- 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) 2018-2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-errors-types.h"
+#include "mm-modem-helpers.h"
+#include "mm-base-modem-at.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-broadband-modem-mbim-foxconn.h"
+
+#if defined WITH_QMI
+# include "mm-iface-modem-firmware.h"
+# include "mm-shared-qmi.h"
+#endif
+
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+
+#if defined WITH_QMI
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
+#endif
+
+static MMIfaceModemLocation *iface_modem_location_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimFoxconn, mm_broadband_modem_mbim_foxconn, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
+#if defined WITH_QMI
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
+#endif
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED
+} FeatureSupport;
+
+struct _MMBroadbandModemMbimFoxconnPrivate {
+ FeatureSupport unmanaged_gps_support;
+};
+
+
+#if defined WITH_QMI
+
+/*****************************************************************************/
+/* FCC unlock (Modem interface) */
+
+static gboolean
+fcc_unlock_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+dms_foxconn_set_fcc_authentication_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationOutput) output = NULL;
+
+ output = qmi_client_dms_foxconn_set_fcc_authentication_finish (client, res, &error);
+ if (!output || !qmi_message_dms_foxconn_set_fcc_authentication_output_get_result (output, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+fcc_unlock (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+ g_autoptr(QmiMessageDmsFoxconnSetFccAuthenticationInput) input = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ input = qmi_message_dms_foxconn_set_fcc_authentication_input_new ();
+ qmi_message_dms_foxconn_set_fcc_authentication_input_set_value (input, 0x00, NULL);
+ qmi_client_dms_foxconn_set_fcc_authentication (QMI_CLIENT_DMS (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_foxconn_set_fcc_authentication_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Firmware update settings
+ *
+ * We only support reporting firmware update settings when QMI support is built,
+ * because this is the only clean way to get the expected firmware version to
+ * report.
+ */
+
+static MMFirmwareUpdateSettings *
+firmware_load_update_settings_finish (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+foxconn_get_firmware_version_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageDmsFoxconnGetFirmwareVersionOutput *output;
+ GError *error = NULL;
+ MMFirmwareUpdateSettings *update_settings = NULL;
+ const gchar *str;
+ MMIfaceModemFirmware *self;
+ guint vendor_id;
+ guint product_id;
+
+ output = qmi_client_dms_foxconn_get_firmware_version_finish (client, res, &error);
+ if (!output || !qmi_message_dms_foxconn_get_firmware_version_output_get_result (output, &error))
+ goto out;
+
+ /* Create update settings now:
+ * 0x105b is the T99W175 module, T99W175 supports QDU,
+ * T99W265(0x0489:0xe0da ; 0x0489:0xe0db): supports QDU
+ * else support FASTBOOT and QMI PDC.
+ */
+ self = g_task_get_source_object (task);
+ vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self));
+ product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self));
+ if (vendor_id == 0x105b || (vendor_id == 0x0489 && (product_id == 0xe0da || product_id == 0xe0db)))
+ update_settings = mm_firmware_update_settings_new (MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU);
+ else {
+ update_settings = mm_firmware_update_settings_new (MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT |
+ MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC);
+ mm_firmware_update_settings_set_fastboot_at (update_settings, "AT^FASTBOOT");
+ }
+
+ qmi_message_dms_foxconn_get_firmware_version_output_get_version (output, &str, NULL);
+ mm_firmware_update_settings_set_version (update_settings, str);
+
+ out:
+ if (error)
+ g_task_return_error (task, error);
+ else {
+ g_assert (update_settings);
+ g_task_return_pointer (task, update_settings, g_object_unref);
+ }
+ g_object_unref (task);
+ if (output)
+ qmi_message_dms_foxconn_get_firmware_version_output_unref (output);
+}
+
+static void
+firmware_load_update_settings (MMIfaceModemFirmware *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiMessageDmsFoxconnGetFirmwareVersionInput *input = NULL;
+ QmiClient *client = NULL;
+ guint vendor_id;
+ guint product_id;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (!client) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unable to load version info: no QMI DMS client available");
+ g_object_unref (task);
+ return;
+ }
+
+ vendor_id = mm_base_modem_get_vendor_id (MM_BASE_MODEM (self));
+ product_id = mm_base_modem_get_product_id (MM_BASE_MODEM (self));
+ input = qmi_message_dms_foxconn_get_firmware_version_input_new ();
+ /* 0x105b is the T99W175 module, T99W175/T99W265 need to compare the apps version. */
+ if (vendor_id == 0x105b || (vendor_id == 0x0489 && (product_id == 0xe0da || product_id == 0xe0db)))
+ qmi_message_dms_foxconn_get_firmware_version_input_set_version_type (
+ input,
+ QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG_APPS,
+ NULL);
+ else
+ qmi_message_dms_foxconn_get_firmware_version_input_set_version_type (
+ input,
+ QMI_DMS_FOXCONN_FIRMWARE_VERSION_TYPE_FIRMWARE_MCFG,
+ NULL);
+ qmi_client_dms_foxconn_get_firmware_version (
+ QMI_CLIENT_DMS (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)foxconn_get_firmware_version_ready,
+ task);
+ qmi_message_dms_foxconn_get_firmware_version_input_unref (input);
+}
+
+#endif
+
+/*****************************************************************************/
+/* Location capabilities loading (Location interface) */
+
+static MMModemLocationSource
+location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+custom_location_load_capabilities (GTask *task,
+ MMModemLocationSource sources)
+{
+ MMBroadbandModemMbimFoxconn *self;
+
+ self = g_task_get_source_object (task);
+
+ /* If we have a GPS port and an AT port, enable unmanaged GPS support */
+ if (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)) &&
+ mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) {
+ self->priv->unmanaged_gps_support = FEATURE_SUPPORTED;
+ sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
+ }
+
+ /* So we're done, complete */
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+
+ sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ custom_location_load_capabilities (task, sources);
+}
+
+static void
+location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's setup, if any. If MM is built without QMI support,
+ * the MBIM modem won't have any location capabilities. */
+ if (iface_modem_location_parent &&
+ iface_modem_location_parent->load_capabilities &&
+ iface_modem_location_parent->load_capabilities_finish) {
+ iface_modem_location_parent->load_capabilities (self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+ return;
+ }
+
+ custom_location_load_capabilities (task, MM_MODEM_LOCATION_SOURCE_NONE);
+}
+
+/*****************************************************************************/
+/* Disable location gathering (Location interface) */
+
+static gboolean
+disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_disable_location_gathering (GTask *task)
+{
+ MMIfaceModemLocation *self;
+ MMModemLocationSource source;
+
+ self = MM_IFACE_MODEM_LOCATION (g_task_get_source_object (task));
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ if (iface_modem_location_parent &&
+ iface_modem_location_parent->disable_location_gathering &&
+ iface_modem_location_parent->disable_location_gathering_finish) {
+ iface_modem_location_parent->disable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)parent_disable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+unmanaged_gps_disabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ parent_disable_location_gathering (task);
+}
+
+static void
+disable_location_gathering (MMIfaceModemLocation *_self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbimFoxconn *self = MM_BROADBAND_MODEM_MBIM_FOXCONN (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ /* We only support Unmanaged GPS at this level */
+ if ((self->priv->unmanaged_gps_support != FEATURE_SUPPORTED) ||
+ (source != MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ parent_disable_location_gathering (task);
+ return;
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (_self),
+ "^NV=30007,01,\"00\"",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)unmanaged_gps_disabled_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+static gboolean
+enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+unmanaged_gps_enabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+custom_enable_location_gathering (GTask *task)
+{
+ MMBroadbandModemMbimFoxconn *self;
+ MMModemLocationSource source;
+
+ self = g_task_get_source_object (task);
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* We only support Unmanaged GPS at this level */
+ if ((self->priv->unmanaged_gps_support != FEATURE_SUPPORTED) ||
+ (source != MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^NV=30007,01,\"01\"",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)unmanaged_gps_enabled_ready,
+ task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ custom_enable_location_gathering (task);
+}
+
+static void
+enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ /* Chain up parent's gathering enable */
+ if (iface_modem_location_parent &&
+ iface_modem_location_parent->enable_location_gathering &&
+ iface_modem_location_parent->enable_location_gathering_finish) {
+ iface_modem_location_parent->enable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ custom_enable_location_gathering (task);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemMbimFoxconn *
+mm_broadband_modem_mbim_foxconn_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ const gchar *carrier_config_mapping = NULL;
+
+ /* T77W968 (DW5821e is also T77W968) modules use t77w968 carrier mapping table. */
+ if ((vendor_id == 0x0489 && (product_id == 0xe0b4 || product_id == 0xe0b5)) ||
+ (vendor_id == 0x413c && (product_id == 0x81d7 || product_id == 0x81e0)))
+ carrier_config_mapping = PKGDATADIR "/mm-foxconn-t77w968-carrier-mapping.conf";
+
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
+ MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, TRUE,
+ MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, carrier_config_mapping,
+ NULL);
+}
+
+static void
+mm_broadband_modem_mbim_foxconn_init (MMBroadbandModemMbimFoxconn *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnPrivate);
+ self->priv->unmanaged_gps_support = FEATURE_SUPPORT_UNKNOWN;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = location_load_capabilities;
+ iface->load_capabilities_finish = location_load_capabilities_finish;
+ iface->enable_location_gathering = enable_location_gathering;
+ iface->enable_location_gathering_finish = enable_location_gathering_finish;
+ iface->disable_location_gathering = disable_location_gathering;
+ iface->disable_location_gathering_finish = disable_location_gathering_finish;
+}
+
+#if defined WITH_QMI
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface->fcc_unlock = fcc_unlock;
+ iface->fcc_unlock_finish = fcc_unlock_finish;
+}
+
+static void
+iface_modem_firmware_init (MMIfaceModemFirmware *iface)
+{
+ iface->load_update_settings = firmware_load_update_settings;
+ iface->load_update_settings_finish = firmware_load_update_settings_finish;
+}
+
+#endif
+
+static void
+mm_broadband_modem_mbim_foxconn_class_init (MMBroadbandModemMbimFoxconnClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbimFoxconnPrivate));
+}
diff --git a/plugins/foxconn/mm-broadband-modem-mbim-foxconn.h b/plugins/foxconn/mm-broadband-modem-mbim-foxconn.h
new file mode 100644
index 00000000..374599e4
--- /dev/null
+++ b/plugins/foxconn/mm-broadband-modem-mbim-foxconn.h
@@ -0,0 +1,49 @@
+/* -*- 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) 2018-2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_MBIM_FOXCONN_H
+#define MM_BROADBAND_MODEM_MBIM_FOXCONN_H
+
+#include "mm-broadband-modem-mbim.h"
+
+#define MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN (mm_broadband_modem_mbim_foxconn_get_type ())
+#define MM_BROADBAND_MODEM_MBIM_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconn))
+#define MM_BROADBAND_MODEM_MBIM_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnClass))
+#define MM_IS_BROADBAND_MODEM_MBIM_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN))
+#define MM_IS_BROADBAND_MODEM_MBIM_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN))
+#define MM_BROADBAND_MODEM_MBIM_FOXCONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_FOXCONN, MMBroadbandModemMbimFoxconnClass))
+
+typedef struct _MMBroadbandModemMbimFoxconn MMBroadbandModemMbimFoxconn;
+typedef struct _MMBroadbandModemMbimFoxconnClass MMBroadbandModemMbimFoxconnClass;
+typedef struct _MMBroadbandModemMbimFoxconnPrivate MMBroadbandModemMbimFoxconnPrivate;
+
+struct _MMBroadbandModemMbimFoxconn {
+ MMBroadbandModemMbim parent;
+ MMBroadbandModemMbimFoxconnPrivate *priv;
+};
+
+struct _MMBroadbandModemMbimFoxconnClass{
+ MMBroadbandModemMbimClass parent;
+};
+
+GType mm_broadband_modem_mbim_foxconn_get_type (void);
+
+MMBroadbandModemMbimFoxconn *mm_broadband_modem_mbim_foxconn_new (const gchar *device,
+ const gchar **driver,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_MBIM_FOXCONN_H */
diff --git a/plugins/foxconn/mm-foxconn-t77w968-carrier-mapping.conf b/plugins/foxconn/mm-foxconn-t77w968-carrier-mapping.conf
new file mode 100755
index 00000000..634d2b5f
--- /dev/null
+++ b/plugins/foxconn/mm-foxconn-t77w968-carrier-mapping.conf
@@ -0,0 +1,299 @@
+
+#
+# T77W968 carrier mapping table
+#
+# This table maps the MCCMNC of the SIM card with the corresponding
+# configuration description as reported by the QMI PDC service in
+# this module.
+#
+
+[foxconn t77w968]
+
+# AT&T
+302220=ATT
+302221=ATT
+31030=ATT
+31070=ATT
+31090=ATT
+310150=ATT
+310170=ATT
+310280=ATT
+310380=ATT
+310410=ATT
+310560=ATT
+310680=ATT
+311180=ATT
+
+# FirstNet
+312670=A2
+313100=A2
+313110=A2
+313120=A2
+313130=A2
+313140=A2
+
+# Verizon
+310590=Verizon
+310890=Verizon
+311270=Verizon
+311480=Verizon
+
+# Vodafone
+20205=Vodafone
+20404=Vodafone
+20601=Vodafone
+20810=Vodafone
+21401=Vodafone
+21670=Vodafone
+21910=Vodafone
+22005=Vodafone
+22210=Vodafone
+22601=Vodafone
+23003=Vodafone
+23201=Vodafone
+23415=Vodafone
+23801=Vodafone
+24405=Vodafone
+24602=Vodafone
+24705=Vodafone
+24802=Vodafone
+25001=Vodafone
+26202=Vodafone
+26209=Vodafone
+26801=Vodafone
+27077=Vodafone
+27201=Vodafone
+27402=Vodafone
+27602=Vodafone
+27801=Vodafone
+28001=Vodafone
+28401=Vodafone
+28602=Vodafone
+28802=Vodafone
+29340=Vodafone
+29403=Vodafone
+40004=Vodafone
+40401=Vodafone
+40405=Vodafone
+40411=Vodafone
+40413=Vodafone
+40415=Vodafone
+40420=Vodafone
+40427=Vodafone
+40430=Vodafone
+40443=Vodafone
+40446=Vodafone
+40460=Vodafone
+40484=Vodafone
+40486=Vodafone
+40488=Vodafone
+40566=Vodafone
+40567=Vodafone
+405750=Vodafone
+405751=Vodafone
+405752=Vodafone
+405753=Vodafone
+405754=Vodafone
+405755=Vodafone
+405756=Vodafone
+41302=Vodafone
+42403=Vodafone
+42602=Vodafone
+42702=Vodafone
+50213=Vodafone
+50219=Vodafone
+50503=Vodafone
+52503=Vodafone
+52505=Vodafone
+53001=Vodafone
+54201=Vodafone
+60202=Vodafone
+62002=Vodafone
+63001=Vodafone
+63902=Vodafone
+64004=Vodafone
+64304=Vodafone
+64710=Vodafone
+65101=Vodafone
+65501=Vodafone
+73001=Vodafone
+90128=Vodafone
+
+# Orange
+20610=Orange
+20801=Orange
+20802=Orange
+21403=Orange
+22610=Orange
+23101=Orange
+23430=Orange
+23433=Orange
+23434=Orange
+25901=Orange
+26003=Orange
+27099=Orange
+28310=Orange
+
+# Telefonica Movistar
+21405=Telefonica
+21407=Telefonica
+
+# Swisscom
+22801=Swisscom
+29501=Swisscom
+
+# Telstra
+50501=Telstra
+50506=Telstra
+50571=Telstra
+50572=Telstra
+
+# Sprint
+310120=Sprint
+
+# Optus
+50202=Optus
+
+# NTT DoCoMo
+44002=Docomo
+44003=Docomo
+44009=Docomo
+44010=Docomo
+44011=Docomo
+44012=Docomo
+44013=Docomo
+44014=Docomo
+44015=Docomo
+44016=Docomo
+44017=Docomo
+44018=Docomo
+44019=Docomo
+44022=Docomo
+44023=Docomo
+44024=Docomo
+44025=Docomo
+44026=Docomo
+44027=Docomo
+44028=Docomo
+44029=Docomo
+44030=Docomo
+44031=Docomo
+44032=Docomo
+44033=Docomo
+44034=Docomo
+44035=Docomo
+44036=Docomo
+44037=Docomo
+44038=Docomo
+44039=Docomo
+44049=Docomo
+44058=Docomo
+44060=Docomo
+44061=Docomo
+44062=Docomo
+44063=Docomo
+44064=Docomo
+44065=Docomo
+44066=Docomo
+44067=Docomo
+44068=Docomo
+44069=Docomo
+44087=Docomo
+44099=Docomo
+44140=Docomo
+44141=Docomo
+44142=Docomo
+44143=Docomo
+44144=Docomo
+44145=Docomo
+44190=Docomo
+44101=Docomo
+44192=Docomo
+44193=Docomo
+44194=Docomo
+44198=Docomo
+44199=Docomo
+
+# KDDI
+44007=KDDI
+44008=KDDI
+44050=KDDI
+44051=KDDI
+44052=KDDI
+44053=KDDI
+44054=KDDI
+44055=KDDI
+44056=KDDI
+44070=KDDI
+44071=KDDI
+44072=KDDI
+44073=KDDI
+44074=KDDI
+44075=KDDI
+44076=KDDI
+44077=KDDI
+44078=KDDI
+44079=KDDI
+44080=KDDI
+44081=KDDI
+44082=KDDI
+44083=KDDI
+44084=KDDI
+44085=KDDI
+44086=KDDI
+44088=KDDI
+44089=KDDI
+44150=KDDI
+44151=KDDI
+44170=KDDI
+
+# SoftBank
+44000=SBM
+44004=SBM
+44006=SBM
+44020=SBM
+44021=SBM
+44040=SBM
+44041=SBM
+44042=SBM
+44043=SBM
+44044=SBM
+44045=SBM
+44046=SBM
+44047=SBM
+44048=SBM
+44090=SBM
+44092=SBM
+44093=SBM
+44094=SBM
+44095=SBM
+44096=SBM
+44097=SBM
+44098=SBM
+44101=SBM
+44161=SBM
+44162=SBM
+44163=SBM
+44164=SBM
+44165=SBM
+
+# Deutsche Telekom
+20201=DT
+20416=DT
+20420=DT
+21630=DT
+21901=DT
+22603=DT
+22606=DT
+23001=DT
+23102=DT
+23203=DT
+23207=DT
+26002=DT
+26201=DT
+27601=DT
+29401=DT
+29702=DT
+
+# Others
+generic=GCF
diff --git a/plugins/foxconn/mm-plugin-foxconn.c b/plugins/foxconn/mm-plugin-foxconn.c
new file mode 100644
index 00000000..0eef97ef
--- /dev/null
+++ b/plugins/foxconn/mm-plugin-foxconn.c
@@ -0,0 +1,120 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-foxconn.h"
+#include "mm-log-object.h"
+#include "mm-broadband-modem.h"
+
+#if defined WITH_QMI
+#include "mm-broadband-modem-qmi.h"
+#endif
+
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim-foxconn.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginFoxconn, mm_plugin_foxconn, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Foxconn-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ mm_obj_dbg (self, "MBIM-powered Foxconn-branded modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_foxconn_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ mm_obj_dbg (self, "Foxconn-branded generic modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL };
+ static const guint16 vendor_ids[] = {
+ 0x0489, /* usb vid */
+ 0x105b, /* pci vid */
+ 0 };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_FOXCONN,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_foxconn_init (MMPluginFoxconn *self)
+{
+}
+
+static void
+mm_plugin_foxconn_class_init (MMPluginFoxconnClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/foxconn/mm-plugin-foxconn.h b/plugins/foxconn/mm-plugin-foxconn.h
new file mode 100644
index 00000000..4a22ceeb
--- /dev/null
+++ b/plugins/foxconn/mm-plugin-foxconn.h
@@ -0,0 +1,46 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_FOXCONN_H
+#define MM_PLUGIN_FOXCONN_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_FOXCONN (mm_plugin_foxconn_get_type ())
+#define MM_PLUGIN_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_FOXCONN, MMPluginFoxconn))
+#define MM_PLUGIN_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_FOXCONN, MMPluginFoxconnClass))
+#define MM_IS_PLUGIN_FOXCONN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_FOXCONN))
+#define MM_IS_PLUGIN_FOXCONN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_FOXCONN))
+#define MM_PLUGIN_FOXCONN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_FOXCONN, MMPluginFoxconnClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginFoxconn;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginFoxconnClass;
+
+GType mm_plugin_foxconn_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_FOXCONN_H */
diff --git a/src/mm-auth.h b/plugins/foxconn/mm-shared.c
index 27b5daef..3b017574 100644
--- a/src/mm-auth.h
+++ b/plugins/foxconn/mm-shared.c
@@ -10,18 +10,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2010 - 2012 Red Hat, Inc.
- * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
-#ifndef MM_AUTH_H
-#define MM_AUTH_H
+#include "mm-shared.h"
-#include "mm-auth-provider.h"
-
-/* Get the default provider */
-MMAuthProvider *mm_auth_get_provider (void);
-
-void mm_auth_shutdown (void);
-
-#endif /* MM_AUTH_H */
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Foxconn)
diff --git a/plugins/generic/mm-plugin-generic.c b/plugins/generic/mm-plugin-generic.c
index b9bea6ae..e18b8854 100644
--- a/plugins/generic/mm-plugin-generic.c
+++ b/plugins/generic/mm-plugin-generic.c
@@ -31,7 +31,7 @@
#include "mm-plugin-generic.h"
#include "mm-broadband-modem.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
@@ -43,14 +43,14 @@
G_DEFINE_TYPE (MMPluginGeneric, mm_plugin_generic, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -59,8 +59,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered generic modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered generic modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -70,8 +70,8 @@ create_modem (MMPlugin *self,
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
- mm_dbg ("MBIM-powered generic modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path,
+ mm_obj_dbg (self, "MBIM-powered generic modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -79,7 +79,7 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -91,11 +91,12 @@ create_modem (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_GENERIC,
- MM_PLUGIN_NAME, MM_PLUGIN_GENERIC_NAME,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_IS_GENERIC, TRUE,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
diff --git a/plugins/generic/tests/test-service-generic.c b/plugins/generic/tests/test-service-generic.c
index 44164df3..d7bc4e01 100644
--- a/plugins/generic/tests/test-service-generic.c
+++ b/plugins/generic/tests/test-service-generic.c
@@ -10,10 +10,12 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2016 Aleksander Morgado <aleksander@gnu.org>
*/
+#include <sys/types.h>
#include <unistd.h>
+
#include <glib.h>
#include <glib-object.h>
@@ -25,16 +27,18 @@
/*****************************************************************************/
static void
-test_something (TestFixture *fixture)
+test_enable_disable (TestFixture *fixture)
{
GError *error = NULL;
MMObject *obj;
MMModem *modem;
TestPortContext *port0;
- const gchar *ports [] = {
- "abstract:port0",
- NULL
- };
+ gchar *ports [] = { NULL, NULL };
+
+ /* Create port name, and add process ID so that multiple runs of this test
+ * in the same system don't clash with each other */
+ ports[0] = g_strdup_printf ("abstract:port0:%ld", (glong) getpid ());
+ g_debug ("test service generic: using abstract port at '%s'", ports[0]);
/* Setup new port context */
port0 = test_port_context_new (ports[0]);
@@ -46,8 +50,8 @@ test_something (TestFixture *fixture)
/* Set the test profile */
test_fixture_set_profile (fixture,
- "test-something",
- "Generic",
+ "test-enable-disable",
+ "generic",
(const gchar *const *)ports);
/* Wait and get the modem object */
@@ -69,6 +73,8 @@ test_something (TestFixture *fixture)
/* Stop port context */
test_port_context_stop (port0);
test_port_context_free (port0);
+
+ g_free (ports[0]);
}
/*****************************************************************************/
@@ -76,10 +82,9 @@ test_something (TestFixture *fixture)
int main (int argc,
char *argv[])
{
- g_type_init ();
g_test_init (&argc, &argv, NULL);
- TEST_ADD ("/MM/Service/Generic", test_something);
+ TEST_ADD ("/MM/Service/Generic/enable-disable", test_enable_disable);
return g_test_run ();
}
diff --git a/plugins/gobi/mm-broadband-modem-gobi.c b/plugins/gobi/mm-broadband-modem-gobi.c
deleted file mode 100644
index 27aded25..00000000
--- a/plugins/gobi/mm-broadband-modem-gobi.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/* -*- 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) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Google Inc.
- */
-
-#include <config.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <ctype.h>
-
-#include "ModemManager.h"
-#include "mm-modem-helpers.h"
-#include "mm-serial-parsers.h"
-#include "mm-log.h"
-#include "mm-errors-types.h"
-#include "mm-iface-modem.h"
-#include "mm-iface-modem-3gpp.h"
-#include "mm-base-modem-at.h"
-#include "mm-broadband-modem-gobi.h"
-
-static void iface_modem_init (MMIfaceModem *iface);
-
-G_DEFINE_TYPE_EXTENDED (MMBroadbandModemGobi, mm_broadband_modem_gobi, MM_TYPE_BROADBAND_MODEM, 0,
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init));
-
-/*****************************************************************************/
-/* Load access technologies (Modem interface) */
-
-static gboolean
-load_access_technologies_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemAccessTechnology *access_technologies,
- guint *mask,
- GError **error)
-{
- const gchar *p;
- const gchar *response;
-
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
- if (!response)
- return FALSE;
-
- p = mm_strip_tag (response, "*CNTI:");
- p = strchr (p, ',');
- if (p) {
- /* We are reporting ALL 3GPP access technologies here */
- *access_technologies = mm_string_to_access_tech (p + 1);
- *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
- return TRUE;
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse access technologies result: '%s'",
- response);
- return FALSE;
-}
-
-static void
-load_access_technologies (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "*CNTI=0",
- 3,
- FALSE,
- callback,
- user_data);
-}
-
-/*****************************************************************************/
-
-MMBroadbandModemGobi *
-mm_broadband_modem_gobi_new (const gchar *device,
- const gchar **drivers,
- const gchar *plugin,
- guint16 vendor_id,
- guint16 product_id)
-{
- return g_object_new (MM_TYPE_BROADBAND_MODEM_GOBI,
- MM_BASE_MODEM_DEVICE, device,
- MM_BASE_MODEM_DRIVERS, drivers,
- MM_BASE_MODEM_PLUGIN, plugin,
- MM_BASE_MODEM_VENDOR_ID, vendor_id,
- MM_BASE_MODEM_PRODUCT_ID, product_id,
- NULL);
-}
-
-static void
-mm_broadband_modem_gobi_init (MMBroadbandModemGobi *self)
-{
-}
-
-static void
-iface_modem_init (MMIfaceModem *iface)
-{
- iface->load_access_technologies = load_access_technologies;
- iface->load_access_technologies_finish = load_access_technologies_finish;
-
- iface->modem_power_down = NULL;
- iface->modem_power_down_finish = NULL;
-}
-
-static void
-mm_broadband_modem_gobi_class_init (MMBroadbandModemGobiClass *klass)
-{
-}
diff --git a/plugins/gobi/mm-broadband-modem-gobi.h b/plugins/gobi/mm-broadband-modem-gobi.h
deleted file mode 100644
index 4164cfe6..00000000
--- a/plugins/gobi/mm-broadband-modem-gobi.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/* -*- 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) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2011 Google Inc.
- */
-
-#ifndef MM_BROADBAND_MODEM_GOBI_H
-#define MM_BROADBAND_MODEM_GOBI_H
-
-#include "mm-broadband-modem.h"
-
-#define MM_TYPE_BROADBAND_MODEM_GOBI (mm_broadband_modem_gobi_get_type ())
-#define MM_BROADBAND_MODEM_GOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_GOBI, MMBroadbandModemGobi))
-#define MM_BROADBAND_MODEM_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_GOBI, MMBroadbandModemGobiClass))
-#define MM_IS_BROADBAND_MODEM_GOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_GOBI))
-#define MM_IS_BROADBAND_MODEM_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_GOBI))
-#define MM_BROADBAND_MODEM_GOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_GOBI, MMBroadbandModemGobiClass))
-
-typedef struct _MMBroadbandModemGobi MMBroadbandModemGobi;
-typedef struct _MMBroadbandModemGobiClass MMBroadbandModemGobiClass;
-
-struct _MMBroadbandModemGobi {
- MMBroadbandModem parent;
-};
-
-struct _MMBroadbandModemGobiClass{
- MMBroadbandModemClass parent;
-};
-
-GType mm_broadband_modem_gobi_get_type (void);
-
-MMBroadbandModemGobi *mm_broadband_modem_gobi_new (const gchar *device,
- const gchar **drivers,
- const gchar *plugin,
- guint16 vendor_id,
- guint16 product_id);
-
-#endif /* MM_BROADBAND_MODEM_GOBI_H */
diff --git a/plugins/gobi/mm-plugin-gobi.h b/plugins/gobi/mm-plugin-gobi.h
deleted file mode 100644
index c47b172c..00000000
--- a/plugins/gobi/mm-plugin-gobi.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/* -*- 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) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
- */
-
-#ifndef MM_PLUGIN_GOBI_H
-#define MM_PLUGIN_GOBI_H
-
-#include "mm-plugin.h"
-
-#define MM_TYPE_PLUGIN_GOBI (mm_plugin_gobi_get_type ())
-#define MM_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobi))
-#define MM_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass))
-#define MM_IS_PLUGIN_GOBI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GOBI))
-#define MM_IS_PLUGIN_GOBI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GOBI))
-#define MM_PLUGIN_GOBI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GOBI, MMPluginGobiClass))
-
-typedef struct {
- MMPlugin parent;
-} MMPluginGobi;
-
-typedef struct {
- MMPluginClass parent;
-} MMPluginGobiClass;
-
-GType mm_plugin_gobi_get_type (void);
-
-G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
-
-#endif /* MM_PLUGIN_GOBI_H */
diff --git a/plugins/gosuncn/77-mm-gosuncn-port-types.rules b/plugins/gosuncn/77-mm-gosuncn-port-types.rules
new file mode 100644
index 00000000..122c6666
--- /dev/null
+++ b/plugins/gosuncn/77-mm-gosuncn-port-types.rules
@@ -0,0 +1,17 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_gosuncn_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="305a", GOTO="mm_gosuncn_port_types"
+GOTO="mm_gosuncn_port_types_end"
+
+LABEL="mm_gosuncn_port_types"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Gosuncn GM800
+# Interfaces #3 and #4 are MBIM
+ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="305a", ATTRS{idProduct}=="1405", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1"
+
+LABEL="mm_gosuncn_port_types_end"
diff --git a/plugins/gosuncn/mm-plugin-gosuncn.c b/plugins/gosuncn/mm-plugin-gosuncn.c
new file mode 100644
index 00000000..7ac517fa
--- /dev/null
+++ b/plugins/gosuncn/mm-plugin-gosuncn.c
@@ -0,0 +1,113 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-plugin-gosuncn.h"
+#include "mm-broadband-modem.h"
+
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+
+#if defined WITH_MBIM
+# include "mm-broadband-modem-mbim.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginGosuncn, mm_plugin_gosuncn, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Gosuncn modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ mm_obj_dbg (self, "MBIM-powered Gosuncn modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ /* Fallback to default modem in the worst case */
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendor_ids[] = { 0x305a, 0 };
+ static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_GOSUNCN,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_DRIVERS, drivers,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_gosuncn_init (MMPluginGosuncn *self)
+{
+}
+
+static void
+mm_plugin_gosuncn_class_init (MMPluginGosuncnClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/gosuncn/mm-plugin-gosuncn.h b/plugins/gosuncn/mm-plugin-gosuncn.h
new file mode 100644
index 00000000..a50e3089
--- /dev/null
+++ b/plugins/gosuncn/mm-plugin-gosuncn.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_GOSUNCN_H
+#define MM_PLUGIN_GOSUNCN_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_GOSUNCN (mm_plugin_gosuncn_get_type ())
+#define MM_PLUGIN_GOSUNCN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_GOSUNCN, MMPluginGosuncn))
+#define MM_PLUGIN_GOSUNCN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_GOSUNCN, MMPluginGosuncnClass))
+#define MM_IS_PLUGIN_GOSUNCN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_GOSUNCN))
+#define MM_IS_PLUGIN_GOSUNCN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_GOSUNCN))
+#define MM_PLUGIN_GOSUNCN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_GOSUNCN, MMPluginGosuncnClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginGosuncn;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginGosuncnClass;
+
+GType mm_plugin_gosuncn_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_GOSUNCN_H */
diff --git a/plugins/haier/77-mm-haier-port-types.rules b/plugins/haier/77-mm-haier-port-types.rules
new file mode 100644
index 00000000..0d969fca
--- /dev/null
+++ b/plugins/haier/77-mm-haier-port-types.rules
@@ -0,0 +1,13 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_haier_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="201e", GOTO="mm_haier_port_types"
+GOTO="mm_haier_port_types_end"
+
+LABEL="mm_haier_port_types"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Haier CE81B
+ATTRS{idVendor}=="201e", ATTRS{idProduct}=="10f8", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+LABEL="mm_haier_port_types_end"
diff --git a/plugins/haier/mm-plugin-haier.c b/plugins/haier/mm-plugin-haier.c
new file mode 100644
index 00000000..0c11aeb6
--- /dev/null
+++ b/plugins/haier/mm-plugin-haier.c
@@ -0,0 +1,75 @@
+/* -*- 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) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-plugin-haier.h"
+
+G_DEFINE_TYPE (MMPluginHaier, mm_plugin_haier, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", NULL };
+ static const guint16 vendor_ids[] = { 0x201e, 0 };
+
+ return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_HAIER,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_haier_init (MMPluginHaier *self)
+{
+}
+
+static void
+mm_plugin_haier_class_init (MMPluginHaierClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/haier/mm-plugin-haier.h b/plugins/haier/mm-plugin-haier.h
new file mode 100644
index 00000000..e1fdfbb1
--- /dev/null
+++ b/plugins/haier/mm-plugin-haier.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2014 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_HAIER_H
+#define MM_PLUGIN_HAIER_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_HAIER (mm_plugin_haier_get_type ())
+#define MM_PLUGIN_HAIER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_HAIER, MMPluginHaier))
+#define MM_PLUGIN_HAIER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_HAIER, MMPluginHaierClass))
+#define MM_IS_PLUGIN_HAIER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_HAIER))
+#define MM_IS_PLUGIN_HAIER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_HAIER))
+#define MM_PLUGIN_HAIER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_HAIER, MMPluginHaierClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginHaier;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginHaierClass;
+
+GType mm_plugin_haier_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_HAIER_H */
diff --git a/plugins/huawei/77-mm-huawei-net-port-types.rules b/plugins/huawei/77-mm-huawei-net-port-types.rules
index f60f1f8a..214d7bc4 100644
--- a/plugins/huawei/77-mm-huawei-net-port-types.rules
+++ b/plugins/huawei/77-mm-huawei-net-port-types.rules
@@ -1,24 +1,34 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_huawei_port_types_end"
+ACTION!="add|change|move|bind", GOTO="mm_huawei_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="12d1", GOTO="mm_huawei_port_types"
+GOTO="mm_huawei_port_types_end"
-ENV{ID_VENDOR_ID}!="12d1", GOTO="mm_huawei_port_types_end"
+LABEL="mm_huawei_port_types"
# MU609 does not support getportmode (crashes modem with default firmware)
-ATTRS{idProduct}=="1573", ENV{ID_MM_HUAWEI_DISABLE_GETPORTMODE}="1"
+ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1573", ENV{ID_MM_HUAWEI_DISABLE_GETPORTMODE}="1"
# Mark the modem and at port flags for ModemManager
-SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="01", ENV{ID_MM_HUAWEI_MODEM_PORT}="1"
-SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="02", ENV{ID_MM_HUAWEI_AT_PORT}="1"
-SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="01", ENV{ID_MM_HUAWEI_MODEM_PORT}="1"
-SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="02", ENV{ID_MM_HUAWEI_AT_PORT}="1"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="02", ATTRS{bInterfaceProtocol}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
# GPS NMEA port on MU609
-SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="05", ENV{ID_MM_HUAWEI_GPS_PORT}="1"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# GPS NMEA port on MU909
-SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="14", ENV{ID_MM_HUAWEI_GPS_PORT}="1"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="01", ATTRS{bInterfaceProtocol}=="14", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+# GPS NMEA port on MU906e
+SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="ff", ATTRS{bInterfaceSubClass}=="06", ATTRS{bInterfaceProtocol}=="14", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# Only the standard ECM or NCM port can support dial-up with AT NDISDUP through AT port
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="06",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
SUBSYSTEMS=="usb", ATTRS{bInterfaceClass}=="02", ATTRS{bInterfaceSubClass}=="0d",ATTRS{bInterfaceProtocol}=="00", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
+# Airtel branded E3372h-607, using huawei-cdc-ncm driver but with unresponsive cdc-wdm port
+ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1506", ENV{ID_MM_HUAWEI_NDISDUP_SUPPORTED}="1"
+
+# R215, Disable CPOL based features
+ATTRS{idVendor}=="12d1", ATTRS{idProduct}=="1588", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
+
LABEL="mm_huawei_port_types_end"
diff --git a/plugins/huawei/mm-broadband-bearer-huawei.c b/plugins/huawei/mm-broadband-bearer-huawei.c
index 4af5aa4d..45e5a516 100644
--- a/plugins/huawei/mm-broadband-bearer-huawei.c
+++ b/plugins/huawei/mm-broadband-bearer-huawei.c
@@ -27,7 +27,7 @@
#include <ModemManager.h>
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-huawei.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-huawei.h"
#include "mm-daemon-enums-types.h"
@@ -37,8 +37,6 @@ G_DEFINE_TYPE (MMBroadbandBearerHuawei, mm_broadband_bearer_huawei, MM_TYPE_BROA
struct _MMBroadbandBearerHuaweiPrivate {
gpointer connect_pending;
gpointer disconnect_pending;
- /* Tag for the post task for network-initiated disconnect */
- guint network_disconnect_pending_id;
};
/*****************************************************************************/
@@ -67,31 +65,26 @@ typedef enum {
CONNECT_3GPP_CONTEXT_STEP_FIRST = 0,
CONNECT_3GPP_CONTEXT_STEP_NDISDUP,
CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY,
+ CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG,
CONNECT_3GPP_CONTEXT_STEP_LAST
} Connect3gppContextStep;
typedef struct {
- MMBroadbandBearerHuawei *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
Connect3gppContextStep step;
guint check_count;
guint failed_ndisstatqry_count;
+ MMBearerIpConfig *ipv4_config;
} Connect3gppContext;
static void
-connect_3gpp_context_complete_and_free (Connect3gppContext *ctx)
+connect_3gpp_context_free (Connect3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
+ g_clear_object (&ctx->ipv4_config);
g_clear_object (&ctx->data);
g_clear_object (&ctx->primary);
@@ -103,30 +96,108 @@ connect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void connect_3gpp_context_step (Connect3gppContext *ctx);
+static void connect_3gpp_context_step (GTask *task);
+
+static void
+connect_dhcp_check_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerHuawei *self)
+{
+ GTask *task;
+ Connect3gppContext *ctx;
+ const gchar *response;
+ GError *error = NULL;
+
+ task = self->priv->connect_pending;
+ g_assert (task != NULL);
+
+ ctx = g_task_get_task_data (task);
+
+ /* Balance refcount */
+ g_object_unref (self);
+
+ /* Cache IPv4 details if available, otherwise clients will have to use DHCP */
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (response) {
+ guint address = 0;
+ guint prefix = 0;
+ guint gateway = 0;
+ guint dns1 = 0;
+ guint dns2 = 0;
+
+ if (mm_huawei_parse_dhcp_response (response,
+ &address,
+ &prefix,
+ &gateway,
+ &dns1,
+ &dns2,
+ &error)) {
+ GInetAddress *addr;
+ gchar *strarr[3] = { NULL, NULL, NULL };
+ guint n = 0;
+ gchar *str;
+
+ mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_STATIC);
+
+ addr = g_inet_address_new_from_bytes ((guint8 *)&address, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_bearer_ip_config_set_address (ctx->ipv4_config, str);
+ g_free (str);
+ g_object_unref (addr);
+
+ /* Netmask */
+ mm_bearer_ip_config_set_prefix (ctx->ipv4_config, prefix);
+
+ /* Gateway */
+ addr = g_inet_address_new_from_bytes ((guint8 *)&gateway, G_SOCKET_FAMILY_IPV4);
+ str = g_inet_address_to_string (addr);
+ mm_bearer_ip_config_set_gateway (ctx->ipv4_config, str);
+ g_free (str);
+ g_object_unref (addr);
+
+ /* DNS */
+ if (dns1) {
+ addr = g_inet_address_new_from_bytes ((guint8 *)&dns1, G_SOCKET_FAMILY_IPV4);
+ strarr[n++] = g_inet_address_to_string (addr);
+ g_object_unref (addr);
+ }
+ if (dns2) {
+ addr = g_inet_address_new_from_bytes ((guint8 *)&dns2, G_SOCKET_FAMILY_IPV4);
+ strarr[n++] = g_inet_address_to_string (addr);
+ g_object_unref (addr);
+ }
+ mm_bearer_ip_config_set_dns (ctx->ipv4_config, (const gchar **)strarr);
+ g_free (strarr[0]);
+ g_free (strarr[1]);
+ } else {
+ mm_obj_dbg (self, "unexpected response to ^DHCP command: %s", error->message);
+ }
+ }
+
+ g_clear_error (&error);
+ ctx->step++;
+ connect_3gpp_context_step (task);
+}
static gboolean
connect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self)
{
- Connect3gppContext *ctx;
+ GTask *task;
/* Recover context */
- ctx = self->priv->connect_pending;
- g_assert (ctx != NULL);
+ task = self->priv->connect_pending;
+ g_assert (task != NULL);
/* Balance refcount */
g_object_unref (self);
/* Retry same step */
- connect_3gpp_context_step (ctx);
+ connect_3gpp_context_step (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -134,6 +205,7 @@ connect_ndisstatqry_check_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
+ GTask *task;
Connect3gppContext *ctx;
const gchar *response;
GError *error = NULL;
@@ -142,8 +214,10 @@ connect_ndisstatqry_check_ready (MMBaseModem *modem,
gboolean ipv6_available = FALSE;
gboolean ipv6_connected = FALSE;
- ctx = self->priv->connect_pending;
- g_assert (ctx != NULL);
+ task = self->priv->connect_pending;
+ g_assert (task != NULL);
+
+ ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
@@ -157,8 +231,8 @@ connect_ndisstatqry_check_ready (MMBaseModem *modem,
&ipv6_connected,
&error)) {
ctx->failed_ndisstatqry_count++;
- mm_dbg ("Unexpected response to ^NDISSTATQRY command: %s (Attempts so far: %u)",
- error->message, ctx->failed_ndisstatqry_count);
+ mm_obj_dbg (self, "unexpected response to ^NDISSTATQRY command: %s (%u attempts so far)",
+ error->message, ctx->failed_ndisstatqry_count);
g_error_free (error);
}
@@ -166,7 +240,7 @@ connect_ndisstatqry_check_ready (MMBaseModem *modem,
if (ipv4_available && ipv4_connected) {
/* Success! */
ctx->step++;
- connect_3gpp_context_step (ctx);
+ connect_3gpp_context_step (task);
return;
}
@@ -181,26 +255,29 @@ connect_ndisdup_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
+ GTask *task;
Connect3gppContext *ctx;
GError *error = NULL;
- ctx = self->priv->connect_pending;
- g_assert (ctx != NULL);
+ task = self->priv->connect_pending;
+ g_assert (task != NULL);
+
+ ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- /* Clear context */
+ /* Clear task */
self->priv->connect_pending = NULL;
- g_simple_async_result_take_error (ctx->result, error);
- connect_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go to next step */
ctx->step++;
- connect_3gpp_context_step (ctx);
+ connect_3gpp_context_step (task);
}
typedef enum {
@@ -224,74 +301,65 @@ huawei_parse_auth_type (MMBearerAllowedAuth mm_auth)
case MM_BEARER_ALLOWED_AUTH_MSCHAPV2:
return MM_BEARER_HUAWEI_AUTH_MSCHAPV2;
default:
+ case MM_BEARER_ALLOWED_AUTH_UNKNOWN:
+ case MM_BEARER_ALLOWED_AUTH_MSCHAP:
+ case MM_BEARER_ALLOWED_AUTH_EAP:
return MM_BEARER_HUAWEI_AUTH_UNKNOWN;
}
}
static void
-connect_3gpp_context_step (Connect3gppContext *ctx)
+connect_3gpp_context_step (GTask *task)
{
+ MMBroadbandBearerHuawei *self;
+ Connect3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Check for cancellation */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- /* Clear context */
- ctx->self->priv->connect_pending = NULL;
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ /* Clear task */
+ self->priv->connect_pending = NULL;
/* If we already sent the connetion command, send the disconnection one */
if (ctx->step > CONNECT_3GPP_CONTEXT_STEP_NDISDUP)
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"^NDISDUP=1,0",
- 3,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE,
NULL,
NULL, /* Do not care the AT response */
NULL);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Huawei connection operation has been cancelled");
- connect_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "Huawei connection operation has been cancelled");
+ g_object_unref (task);
return;
}
- /* Network-initiated disconnect should not be outstanding at this point,
- * because it interferes with the connect attempt.
- */
- g_assert (ctx->self->priv->network_disconnect_pending_id == 0);
-
switch (ctx->step) {
case CONNECT_3GPP_CONTEXT_STEP_FIRST: {
MMBearerIpFamily ip_family;
- ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- if (ip_family == MM_BEARER_IP_FAMILY_NONE ||
- ip_family == MM_BEARER_IP_FAMILY_ANY) {
- gchar *ip_family_str;
-
- ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (ctx->self));
- ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);
- mm_dbg ("No specific IP family requested, defaulting to %s",
- ip_family_str);
- g_free (ip_family_str);
- }
-
+ ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ mm_3gpp_normalize_ip_family (&ip_family);
if (ip_family != MM_BEARER_IP_FAMILY_IPV4) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Only IPv4 is supported by this modem");
- connect_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Only IPv4 is supported by this modem");
+ g_object_unref (task);
return;
}
- /* Store the context */
- ctx->self->priv->connect_pending = ctx;
+ /* Store the task */
+ self->priv->connect_pending = task;
ctx->step++;
- /* Fall down to the next step */
- }
+ } /* fall through */
case CONNECT_3GPP_CONTEXT_STEP_NDISDUP: {
const gchar *apn;
@@ -301,30 +369,30 @@ connect_3gpp_context_step (Connect3gppContext *ctx)
gint encoded_auth = MM_BEARER_HUAWEI_AUTH_UNKNOWN;
gchar *command;
- apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ passwd = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
encoded_auth = huawei_parse_auth_type (auth);
/* Default to no authentication if not specified */
- if ((encoded_auth = huawei_parse_auth_type (auth)) == MM_BEARER_HUAWEI_AUTH_UNKNOWN)
+ if (encoded_auth == MM_BEARER_HUAWEI_AUTH_UNKNOWN)
encoded_auth = MM_BEARER_HUAWEI_AUTH_NONE;
if (!user && !passwd)
command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\"",
apn == NULL ? "" : apn);
- else if (encoded_auth == MM_BEARER_HUAWEI_AUTH_NONE)
- command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\"",
- apn == NULL ? "" : apn,
- user == NULL ? "" : user,
- passwd == NULL ? "" : passwd);
- else
+ else {
+ if (encoded_auth == MM_BEARER_HUAWEI_AUTH_NONE) {
+ encoded_auth = MM_BEARER_HUAWEI_AUTH_CHAP;
+ mm_obj_dbg (self, "using default (CHAP) authentication method");
+ }
command = g_strdup_printf ("AT^NDISDUP=1,1,\"%s\",\"%s\",\"%s\",%d",
apn == NULL ? "" : apn,
user == NULL ? "" : user,
passwd == NULL ? "" : passwd,
encoded_auth);
+ }
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
@@ -334,36 +402,36 @@ connect_3gpp_context_step (Connect3gppContext *ctx)
FALSE,
NULL,
(GAsyncReadyCallback)connect_ndisdup_ready,
- g_object_ref (ctx->self));
+ g_object_ref (self));
g_free (command);
return;
}
case CONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY:
- /* Wait for dial up timeout, retries for 60 times
- * (1s between the retries, so it means 1 minute).
+ /* Wait for dial up timeout, retries for 180 times
+ * (1s between the retries, so it means 3 minutes).
* If too many retries, failed
*/
- if (ctx->check_count > 60) {
+ if (ctx->check_count > MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT) {
/* Clear context */
- ctx->self->priv->connect_pending = NULL;
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Connection attempt timed out");
- connect_3gpp_context_complete_and_free (ctx);
+ self->priv->connect_pending = NULL;
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Connection attempt timed out");
+ g_object_unref (task);
return;
}
/* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */
if (ctx->failed_ndisstatqry_count > 10) {
/* Clear context */
- ctx->self->priv->connect_pending = NULL;
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
- "Connection attempt not supported.");
- connect_3gpp_context_complete_and_free (ctx);
+ self->priv->connect_pending = NULL;
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
+ "Connection attempt not supported.");
+ g_object_unref (task);
return;
}
@@ -377,33 +445,41 @@ connect_3gpp_context_step (Connect3gppContext *ctx)
FALSE,
NULL,
(GAsyncReadyCallback)connect_ndisstatqry_check_ready,
- g_object_ref (ctx->self));
+ g_object_ref (self));
+ return;
+
+ case CONNECT_3GPP_CONTEXT_STEP_IP_CONFIG:
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ "^DHCP?",
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)connect_dhcp_check_ready,
+ g_object_ref (self));
return;
case CONNECT_3GPP_CONTEXT_STEP_LAST:
/* Clear context */
- ctx->self->priv->connect_pending = NULL;
+ self->priv->connect_pending = NULL;
/* Setup result */
- {
- MMBearerIpConfig *ipv4_config;
-
- ipv4_config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- mm_bearer_connect_result_new (ctx->data, ipv4_config, NULL),
- (GDestroyNotify)mm_bearer_connect_result_unref);
- g_object_unref (ipv4_config);
- }
+ g_task_return_pointer (
+ task,
+ mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, NULL),
+ (GDestroyNotify)mm_bearer_connect_result_unref);
- connect_3gpp_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
}
static void
-connect_3gpp (MMBroadbandBearer *self,
+connect_3gpp (MMBroadbandBearer *_self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
@@ -411,7 +487,9 @@ connect_3gpp (MMBroadbandBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- Connect3gppContext *ctx;
+ MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (_self);
+ Connect3gppContext *ctx;
+ GTask *task;
MMPort *data;
g_assert (primary != NULL);
@@ -419,35 +497,39 @@ connect_3gpp (MMBroadbandBearer *self,
/* We need a net data port */
data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
if (!data) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ connect_3gpp,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
return;
}
/* Setup connection context */
ctx = g_slice_new0 (Connect3gppContext);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->data = g_object_ref (data);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- connect_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
ctx->step = CONNECT_3GPP_CONTEXT_STEP_FIRST;
- g_assert (ctx->self->priv->connect_pending == NULL);
- g_assert (ctx->self->priv->disconnect_pending == NULL);
+ g_assert (self->priv->connect_pending == NULL);
+ g_assert (self->priv->disconnect_pending == NULL);
/* Get correct dial port to use */
ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), ctx->data, primary);
+
+ /* Default to automatic/DHCP addressing */
+ ctx->ipv4_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)connect_3gpp_context_free);
+ g_task_set_check_cancellable (task, FALSE);
+
/* Run! */
- connect_3gpp_context_step (ctx);
+ connect_3gpp_context_step (task);
}
/*****************************************************************************/
@@ -461,22 +543,17 @@ typedef enum {
} Disconnect3gppContextStep;
typedef struct {
- MMBroadbandBearerHuawei *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
- GSimpleAsyncResult *result;
Disconnect3gppContextStep step;
guint check_count;
guint failed_ndisstatqry_count;
} Disconnect3gppContext;
static void
-disconnect_3gpp_context_complete_and_free (Disconnect3gppContext *ctx)
+disconnect_3gpp_context_free (Disconnect3gppContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->primary);
- g_object_unref (ctx->self);
g_object_unref (ctx->modem);
g_slice_free (Disconnect3gppContext, ctx);
}
@@ -486,26 +563,26 @@ disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void disconnect_3gpp_context_step (Disconnect3gppContext *ctx);
+static void disconnect_3gpp_context_step (GTask *task);
static gboolean
disconnect_retry_ndisstatqry_check_cb (MMBroadbandBearerHuawei *self)
{
- Disconnect3gppContext *ctx;
+ GTask *task;
/* Recover context */
- ctx = self->priv->disconnect_pending;
- g_assert (ctx != NULL);
+ task = self->priv->disconnect_pending;
+ g_assert (task != NULL);
/* Balance refcount */
g_object_unref (self);
/* Retry same step */
- disconnect_3gpp_context_step (ctx);
- return FALSE;
+ disconnect_3gpp_context_step (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -513,6 +590,7 @@ disconnect_ndisstatqry_check_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
+ GTask *task;
Disconnect3gppContext *ctx;
const gchar *response;
GError *error = NULL;
@@ -521,8 +599,10 @@ disconnect_ndisstatqry_check_ready (MMBaseModem *modem,
gboolean ipv6_available = FALSE;
gboolean ipv6_connected = FALSE;
- ctx = self->priv->disconnect_pending;
- g_assert (ctx != NULL);
+ task = self->priv->disconnect_pending;
+ g_assert (task != NULL);
+
+ ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
@@ -536,8 +616,8 @@ disconnect_ndisstatqry_check_ready (MMBaseModem *modem,
&ipv6_connected,
&error)) {
ctx->failed_ndisstatqry_count++;
- mm_dbg ("Unexpected response to ^NDISSTATQRY command: %s (Attempts so far: %u)",
- error->message, ctx->failed_ndisstatqry_count);
+ mm_obj_dbg (self, "unexpected response to ^NDISSTATQRY command: %s (%u attempts so far)",
+ error->message, ctx->failed_ndisstatqry_count);
g_error_free (error);
}
@@ -545,7 +625,7 @@ disconnect_ndisstatqry_check_ready (MMBaseModem *modem,
if (ipv4_available && !ipv4_connected) {
/* Success! */
ctx->step++;
- disconnect_3gpp_context_step (ctx);
+ disconnect_3gpp_context_step (task);
return;
}
@@ -560,46 +640,43 @@ disconnect_ndisdup_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerHuawei *self)
{
+ GTask *task;
Disconnect3gppContext *ctx;
- GError *error = NULL;
- ctx = self->priv->disconnect_pending;
- g_assert (ctx != NULL);
+ task = self->priv->disconnect_pending;
+ g_assert (task != NULL);
+
+ ctx = g_task_get_task_data (task);
/* Balance refcount */
g_object_unref (self);
- if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- /* Clear context */
- self->priv->disconnect_pending = NULL;
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_3gpp_context_complete_and_free (ctx);
- return;
- }
+ /* Running NDISDUP=1,0 on an already disconnected bearer/context will
+ * return ERROR! Ignore errors in the NDISDUP disconnection command,
+ * because we're anyway going to check the bearer/context status
+ * afterwards. */
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
/* Go to next step */
ctx->step++;
- disconnect_3gpp_context_step (ctx);
+ disconnect_3gpp_context_step (task);
}
static void
-disconnect_3gpp_context_step (Disconnect3gppContext *ctx)
+disconnect_3gpp_context_step (GTask *task)
{
+ MMBroadbandBearerHuawei *self;
+ Disconnect3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISCONNECT_3GPP_CONTEXT_STEP_FIRST:
- /* Store the context */
- ctx->self->priv->disconnect_pending = ctx;
-
- /* We ignore any pending network-initiated disconnection in order to prevent it
- * from interfering with the client-initiated disconnection, as we would like to
- * proceed with the latter anyway. */
- if (ctx->self->priv->network_disconnect_pending_id != 0) {
- g_source_remove (ctx->self->priv->network_disconnect_pending_id);
- ctx->self->priv->network_disconnect_pending_id = 0;
- }
-
+ /* Store the task */
+ self->priv->disconnect_pending = task;
ctx->step++;
- /* Fall down to the next step */
+ /* fall through */
case DISCONNECT_3GPP_CONTEXT_STEP_NDISDUP:
mm_base_modem_at_command_full (ctx->modem,
@@ -610,31 +687,31 @@ disconnect_3gpp_context_step (Disconnect3gppContext *ctx)
FALSE,
NULL,
(GAsyncReadyCallback)disconnect_ndisdup_ready,
- g_object_ref (ctx->self));
+ g_object_ref (self));
return;
case DISCONNECT_3GPP_CONTEXT_STEP_NDISSTATQRY:
/* If too many retries (1s of wait between the retries), failed */
- if (ctx->check_count > 60) {
- /* Clear context */
- ctx->self->priv->disconnect_pending = NULL;
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Disconnection attempt timed out");
- disconnect_3gpp_context_complete_and_free (ctx);
+ if (ctx->check_count > MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT) {
+ /* Clear task */
+ self->priv->disconnect_pending = NULL;
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Disconnection attempt timed out");
+ g_object_unref (task);
return;
}
/* Give up if too many unexpected responses to NIDSSTATQRY are encountered. */
if (ctx->failed_ndisstatqry_count > 10) {
- /* Clear context */
- ctx->self->priv->disconnect_pending = NULL;
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
- "Disconnection attempt not supported.");
- disconnect_3gpp_context_complete_and_free (ctx);
+ /* Clear task */
+ self->priv->disconnect_pending = NULL;
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
+ "Disconnection attempt not supported.");
+ g_object_unref (task);
return;
}
@@ -648,21 +725,24 @@ disconnect_3gpp_context_step (Disconnect3gppContext *ctx)
FALSE,
NULL,
(GAsyncReadyCallback)disconnect_ndisstatqry_check_ready,
- g_object_ref (ctx->self));
+ g_object_ref (self));
return;
case DISCONNECT_3GPP_CONTEXT_STEP_LAST:
- /* Clear context */
- ctx->self->priv->disconnect_pending = NULL;
+ /* Clear task */
+ self->priv->disconnect_pending = NULL;
/* Set data port as result */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_3gpp_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
}
static void
-disconnect_3gpp (MMBroadbandBearer *self,
+disconnect_3gpp (MMBroadbandBearer *_self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
@@ -671,46 +751,35 @@ disconnect_3gpp (MMBroadbandBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (_self);
Disconnect3gppContext *ctx;
+ GTask *task;
g_assert (primary != NULL);
ctx = g_slice_new0 (Disconnect3gppContext);
- ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect_3gpp);
ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST;
- g_assert (ctx->self->priv->connect_pending == NULL);
- g_assert (ctx->self->priv->disconnect_pending == NULL);
+ g_assert (self->priv->connect_pending == NULL);
+ g_assert (self->priv->disconnect_pending == NULL);
/* Get correct dial port to use */
ctx->primary = get_dial_port (MM_BROADBAND_MODEM_HUAWEI (ctx->modem), data, primary);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_3gpp_context_free);
+
/* Start! */
- disconnect_3gpp_context_step (ctx);
+ disconnect_3gpp_context_step (task);
}
/*****************************************************************************/
-static gboolean
-network_disconnect_3gpp_delayed (MMBroadbandBearerHuawei *self)
-{
- mm_dbg ("Disconnect bearer '%s' on network request.",
- mm_base_bearer_get_path (MM_BASE_BEARER (self)));
-
- self->priv->network_disconnect_pending_id = 0;
- mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
- MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
- return FALSE;
-}
-
static void
-report_connection_status (MMBaseBearer *bearer,
- MMBearerConnectionStatus status)
+report_connection_status (MMBaseBearer *bearer,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (bearer);
@@ -724,35 +793,17 @@ report_connection_status (MMBaseBearer *bearer,
if (self->priv->connect_pending || self->priv->disconnect_pending)
return;
- mm_dbg ("Received spontaneous ^NDISSTAT (%s)",
- mm_bearer_connection_status_get_string (status));
+ mm_obj_dbg (self, "received spontaneous ^NDISSTAT (%s)", mm_bearer_connection_status_get_string (status));
/* Ignore 'CONNECTED' */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED)
return;
- /* We already use ^NDISSTATQRY? to poll the connection status, so only
- * handle network-initiated disconnection here. */
- if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING) {
- /* MM_BEARER_CONNECTION_STATUS_DISCONNECTING is used to indicate that the
- * reporting of disconnection should be delayed. See MMBroadbandModemHuawei's
- * bearer_report_connection_status for details. */
- if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_CONNECTED &&
- self->priv->network_disconnect_pending_id == 0) {
- mm_dbg ("Delay network-initiated disconnection of bearer '%s'",
- mm_base_bearer_get_path (MM_BASE_BEARER (self)));
- self->priv->network_disconnect_pending_id = (g_timeout_add_seconds (
- 4,
- (GSourceFunc) network_disconnect_3gpp_delayed,
- self));
- }
- return;
- }
-
/* Report disconnected right away */
MM_BASE_BEARER_CLASS (mm_broadband_bearer_huawei_parent_class)->report_connection_status (
bearer,
- MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
+ NULL);
}
/*****************************************************************************/
@@ -777,19 +828,6 @@ mm_broadband_bearer_huawei_new_finish (GAsyncResult *res,
return MM_BASE_BEARER (bearer);
}
-static void
-dispose (GObject *object)
-{
- MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (object);
-
- if (self->priv->network_disconnect_pending_id != 0) {
- g_source_remove (self->priv->network_disconnect_pending_id);
- self->priv->network_disconnect_pending_id = 0;
- }
-
- G_OBJECT_CLASS (mm_broadband_bearer_huawei_parent_class)->dispose (object);
-}
-
void
mm_broadband_bearer_huawei_new (MMBroadbandModemHuawei *modem,
MMBearerProperties *config,
@@ -826,8 +864,14 @@ mm_broadband_bearer_huawei_class_init (MMBroadbandBearerHuaweiClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHuaweiPrivate));
- object_class->dispose = dispose;
base_bearer_class->report_connection_status = report_connection_status;
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = NULL;
+ base_bearer_class->reload_connection_status_finish = NULL;
+#endif
+
broadband_bearer_class->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
diff --git a/plugins/huawei/mm-broadband-modem-huawei.c b/plugins/huawei/mm-broadband-modem-huawei.c
index ce1fb6fd..866707d6 100644
--- a/plugins/huawei/mm-broadband-modem-huawei.c
+++ b/plugins/huawei/mm-broadband-modem-huawei.c
@@ -14,7 +14,8 @@
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2011 - 2012 Google Inc.
* Copyright (C) 2012 Huawei Technologies Co., Ltd
- * Copyright (C) 2012 - 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ * Copyright (C) 2012 - 2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -25,13 +26,12 @@
#include <unistd.h>
#include <ctype.h>
#include <time.h>
-#include <gudev/gudev.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-huawei.h"
@@ -42,6 +42,8 @@
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-time.h"
#include "mm-iface-modem-cdma.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-voice.h"
#include "mm-broadband-modem-huawei.h"
#include "mm-broadband-bearer-huawei.h"
#include "mm-broadband-bearer.h"
@@ -54,11 +56,14 @@ static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
static MMIfaceModem *iface_modem_parent;
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
static MMIfaceModemLocation *iface_modem_location_parent;
static MMIfaceModemCdma *iface_modem_cdma_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHuawei, mm_broadband_modem_huawei, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
@@ -66,7 +71,9 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHuawei, mm_broadband_modem_huawei, MM_TY
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init));
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init))
typedef enum {
FEATURE_SUPPORT_UNKNOWN,
@@ -74,6 +81,15 @@ typedef enum {
FEATURE_SUPPORTED
} FeatureSupport;
+typedef struct {
+ MMSignal *cdma;
+ MMSignal *evdo;
+ MMSignal *gsm;
+ MMSignal *umts;
+ MMSignal *lte;
+ MMSignal *nr5g;
+} DetailedSignal;
+
struct _MMBroadbandModemHuaweiPrivate {
/* Regex for signal quality related notifications */
GRegex *rssi_regex;
@@ -87,6 +103,13 @@ struct _MMBroadbandModemHuaweiPrivate {
GRegex *dsflowrpt_regex;
GRegex *ndisstat_regex;
+ /* Regex for voice management notifications */
+ GRegex *orig_regex;
+ GRegex *conf_regex;
+ GRegex *conn_regex;
+ GRegex *cend_regex;
+ GRegex *ddtmf_regex;
+
/* Regex to ignore */
GRegex *boot_regex;
GRegex *connect_regex;
@@ -101,6 +124,14 @@ struct _MMBroadbandModemHuaweiPrivate {
GRegex *pdpdeact_regex;
GRegex *ndisend_regex;
GRegex *rfswitch_regex;
+ GRegex *position_regex;
+ GRegex *posend_regex;
+ GRegex *ecclist_regex;
+ GRegex *ltersrp_regex;
+ GRegex *cschannelinfo_regex;
+ GRegex *ccallstate_regex;
+ GRegex *eons_regex;
+ GRegex *lwurc_regex;
FeatureSupport ndisdup_support;
FeatureSupport rfswitch_support;
@@ -110,18 +141,25 @@ struct _MMBroadbandModemHuaweiPrivate {
FeatureSupport prefmode_support;
FeatureSupport time_support;
FeatureSupport nwtime_support;
+ FeatureSupport cvoice_support;
MMModemLocationSource enabled_sources;
GArray *syscfg_supported_modes;
GArray *syscfgex_supported_modes;
GArray *prefmode_supported_modes;
+
+ DetailedSignal detailed_signal;
+
+ /* Voice call audio related properties */
+ guint audio_hz;
+ guint audio_bits;
};
/*****************************************************************************/
-static GList *
-get_at_port_list (MMBroadbandModemHuawei *self)
+GList *
+mm_broadband_modem_huawei_get_at_port_list (MMBroadbandModemHuawei *self)
{
GList *out = NULL;
MMPortSerialAt *port;
@@ -133,15 +171,14 @@ get_at_port_list (MMBroadbandModemHuawei *self)
out = g_list_append (out, port);
/* Secondary */
- port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
if (port)
out = g_list_append (out, port);
/* Additional cdc-wdm ports used for dialing */
cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
- MM_PORT_SUBSYS_USB,
- MM_PORT_TYPE_AT,
- NULL);
+ MM_PORT_SUBSYS_USBMISC,
+ MM_PORT_TYPE_AT);
return g_list_concat (out, cdc_wdm_at_ports);
}
@@ -174,11 +211,10 @@ sysinfo_finish (MMBroadbandModemHuawei *self,
{
SysinfoResult *result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- result = (SysinfoResult *) g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
if (extended)
*extended = result->extended;
if (srv_status)
@@ -196,13 +232,14 @@ sysinfo_finish (MMBroadbandModemHuawei *self,
if (sys_submode)
*sys_submode = result->sys_submode;
+ g_free (result);
return TRUE;
}
static void
run_sysinfo_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *response;
@@ -210,10 +247,9 @@ run_sysinfo_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- mm_dbg ("^SYSINFO failed: %s", error->message);
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ mm_obj_dbg (self, "^SYSINFO failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -228,35 +264,33 @@ run_sysinfo_ready (MMBaseModem *self,
&result->sys_submode_valid,
&result->sys_submode,
&error)) {
- mm_dbg ("^SYSINFO parsing failed: %s", error->message);
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ mm_obj_dbg (self, "^SYSINFO parsing failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_free (result);
return;
}
- g_simple_async_result_set_op_res_gpointer (simple, result, g_free);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, result, g_free);
+ g_object_unref (task);
}
static void
run_sysinfo (MMBroadbandModemHuawei *self,
- GSimpleAsyncResult *result)
+ GTask *task)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SYSINFO",
3,
FALSE,
(GAsyncReadyCallback)run_sysinfo_ready,
- result);
+ task);
}
static void
run_sysinfoex_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
GError *error = NULL;
@@ -268,17 +302,16 @@ run_sysinfoex_ready (MMBaseModem *_self,
/* First time we try, we fallback to ^SYSINFO */
if (self->priv->sysinfoex_support == FEATURE_SUPPORT_UNKNOWN) {
self->priv->sysinfoex_support = FEATURE_NOT_SUPPORTED;
- mm_dbg ("^SYSINFOEX failed: %s, assuming unsupported", error->message);
+ mm_obj_dbg (self, "^SYSINFOEX failed: %s, assuming unsupported", error->message);
g_error_free (error);
- run_sysinfo (self, simple);
+ run_sysinfo (self, task);
return;
}
/* Otherwise, propagate error */
- mm_dbg ("^SYSINFOEX failed: %s", error->message);
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ mm_obj_dbg (self, "^SYSINFOEX failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -295,31 +328,29 @@ run_sysinfoex_ready (MMBaseModem *_self,
&result->sys_mode,
&result->sys_submode,
&error)) {
- mm_dbg ("^SYSINFOEX parsing failed: %s", error->message);
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ mm_obj_dbg (self, "^SYSINFOEX parsing failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_free (result);
return;
}
/* Submode from SYSINFOEX always valid */
result->sys_submode_valid = TRUE;
- g_simple_async_result_set_op_res_gpointer (simple, result, g_free);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, result, g_free);
+ g_object_unref (task);
}
static void
run_sysinfoex (MMBroadbandModemHuawei *self,
- GSimpleAsyncResult *result)
+ GTask *task)
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SYSINFOEX",
3,
FALSE,
(GAsyncReadyCallback)run_sysinfoex_ready,
- result);
+ task);
}
static void
@@ -327,17 +358,15 @@ sysinfo (MMBroadbandModemHuawei *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sysinfo);
if (self->priv->sysinfoex_support == FEATURE_SUPPORT_UNKNOWN ||
self->priv->sysinfoex_support == FEATURE_SUPPORTED)
- run_sysinfoex (self, result);
+ run_sysinfoex (self, task);
else
- run_sysinfo (self, result);
+ run_sysinfo (self, task);
}
/*****************************************************************************/
@@ -544,7 +573,6 @@ load_access_technologies_finish (MMIfaceModem *self,
guint *mask,
GError **error)
{
- gchar *str;
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
gboolean extended = FALSE;
guint srv_status = 0;
@@ -578,10 +606,6 @@ load_access_technologies_finish (MMIfaceModem *self,
huawei_sysinfo_mode_to_act (sys_mode));
}
- str = mm_modem_access_technology_build_string_from_mask (act);
- mm_dbg ("Access Technology: '%s'", str);
- g_free (str);
-
*access_technologies = act;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
@@ -592,7 +616,6 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading access technology (huawei)...");
sysinfo (MM_BROADBAND_MODEM_HUAWEI (self), callback, user_data);
}
@@ -671,7 +694,6 @@ load_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading unlock retries (huawei)...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^CPIN?",
3,
@@ -688,15 +710,15 @@ modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -704,16 +726,13 @@ modem_after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
+ task = g_task_new (self, NULL, callback, user_data);
/* A 3-second wait is necessary for SIM to become ready, or the firmware may
* fail miserably and reboot itself */
- g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, result);
+ g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
@@ -726,12 +745,12 @@ typedef struct {
static BandTable bands[] = {
/* Sort 3G first since it's preferred */
- { MM_MODEM_BAND_U2100, 0x00400000 },
- { MM_MODEM_BAND_U1900, 0x00800000 },
- { MM_MODEM_BAND_U850, 0x04000000 },
- { MM_MODEM_BAND_U900, 0x00020000 },
- { MM_MODEM_BAND_G850, 0x00080000 },
+ { MM_MODEM_BAND_UTRAN_1, 0x00400000 },
+ { MM_MODEM_BAND_UTRAN_2, 0x00800000 },
+ { MM_MODEM_BAND_UTRAN_5, 0x04000000 },
+ { MM_MODEM_BAND_UTRAN_8, 0x00020000 },
/* 2G second */
+ { MM_MODEM_BAND_G850, 0x00080000 },
{ MM_MODEM_BAND_DCS, 0x00000080 },
{ MM_MODEM_BAND_EGSM, 0x00000100 },
{ MM_MODEM_BAND_PCS, 0x00200000 }
@@ -848,7 +867,6 @@ load_current_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading current bands (huawei)...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SYSCFG?",
3,
@@ -865,24 +883,23 @@ set_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
syscfg_set_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
/* Let the error be critical */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -891,27 +908,23 @@ set_current_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *cmd;
guint32 huawei_band = 0x3FFFFFFF;
gchar *bands_string;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_bands);
+ task = g_task_new (self, NULL, callback, user_data);
- bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array->data,
+ bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array->data,
bands_array->len);
if (!bands_array_to_huawei (bands_array, &huawei_band)) {
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid bands requested: '%s'",
- bands_string);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid bands requested: '%s'",
+ bands_string);
+ g_object_unref (task);
g_free (bands_string);
return;
}
@@ -922,7 +935,7 @@ set_current_bands (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)syscfg_set_ready,
- result);
+ task);
g_free (cmd);
g_free (bands_string);
}
@@ -935,16 +948,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
syscfg_test_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -957,9 +967,9 @@ syscfg_test_ready (MMBroadbandModemHuawei *self,
* string to get parsed. Ugly, ugly, blame Huawei.
*/
if (response[0])
- self->priv->syscfg_supported_modes = mm_huawei_parse_syscfg_test (response, &error);
+ self->priv->syscfg_supported_modes = mm_huawei_parse_syscfg_test (response, self, &error);
else {
- self->priv->syscfg_supported_modes = mm_huawei_parse_syscfg_test (MM_HUAWEI_DEFAULT_SYSCFG_FMT, NULL);
+ self->priv->syscfg_supported_modes = mm_huawei_parse_syscfg_test (MM_HUAWEI_DEFAULT_SYSCFG_FMT, self, NULL);
g_assert (self->priv->syscfg_supported_modes != NULL);
}
}
@@ -986,27 +996,26 @@ syscfg_test_ready (MMBroadbandModemHuawei *self,
}
self->priv->syscfg_support = FEATURE_SUPPORTED;
- g_simple_async_result_set_op_res_gpointer (simple,
- combinations,
- (GDestroyNotify)g_array_unref);
+ g_task_return_pointer (task,
+ combinations,
+ (GDestroyNotify)g_array_unref);
} else {
- g_debug ("Error while checking ^SYSCFG format: %s", error->message);
+ mm_obj_dbg (self, "error while checking ^SYSCFG format: %s", error->message);
/* If SIM-PIN error, don't mark as feature unsupported; we'll retry later */
if (!g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN))
self->priv->syscfg_support = FEATURE_NOT_SUPPORTED;
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
syscfgex_test_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -1038,23 +1047,21 @@ syscfgex_test_ready (MMBroadbandModemHuawei *self,
self->priv->syscfgex_support = FEATURE_SUPPORTED;
- g_simple_async_result_set_op_res_gpointer (simple,
- combinations,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task,
+ combinations,
+ (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
return;
}
/* If SIM-PIN error, don't mark as feature unsupported; we'll retry later */
if (error) {
- g_debug ("Error while checking ^SYSCFGEX format: %s", error->message);
+ mm_obj_dbg (self, "error while checking ^SYSCFGEX format: %s", error->message);
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_error_free (error);
@@ -1068,20 +1075,20 @@ syscfgex_test_ready (MMBroadbandModemHuawei *self,
3,
TRUE,
(GAsyncReadyCallback)syscfg_test_ready,
- simple);
+ task);
}
static void
prefmode_test_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (response)
- self->priv->prefmode_supported_modes = mm_huawei_parse_prefmode_test (response, &error);
+ self->priv->prefmode_supported_modes = mm_huawei_parse_prefmode_test (response, self, &error);
if (self->priv->prefmode_supported_modes) {
MMModemModeCombination mode;
@@ -1105,21 +1112,20 @@ prefmode_test_ready (MMBroadbandModemHuawei *self,
}
self->priv->prefmode_support = FEATURE_SUPPORTED;
- g_simple_async_result_set_op_res_gpointer (simple,
- combinations,
- (GDestroyNotify)g_array_unref);
+ g_task_return_pointer (task,
+ combinations,
+ (GDestroyNotify)g_array_unref);
} else {
- g_debug ("Error while checking ^PREFMODE format: %s", error->message);
+ mm_obj_dbg (self, "error while checking ^PREFMODE format: %s", error->message);
/* If SIM-PIN error, don't mark as feature unsupported; we'll retry later */
if (!g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN))
self->priv->prefmode_support = FEATURE_NOT_SUPPORTED;
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -1128,12 +1134,9 @@ load_supported_modes (MMIfaceModem *_self,
gpointer user_data)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_cdma_only (_self)) {
/* ^PREFMODE only in CDMA-only modems */
@@ -1144,7 +1147,7 @@ load_supported_modes (MMIfaceModem *_self,
3,
TRUE,
(GAsyncReadyCallback)prefmode_test_ready,
- result);
+ task);
return;
}
@@ -1155,7 +1158,7 @@ load_supported_modes (MMIfaceModem *_self,
3,
TRUE,
(GAsyncReadyCallback)syscfgex_test_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1170,19 +1173,21 @@ load_current_modes_finish (MMIfaceModem *self,
{
MMModemModeCombination *out;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ out = g_task_propagate_pointer (G_TASK (res), error);
+ if (!out)
return FALSE;
- out = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*allowed = out->allowed;
*preferred = out->preferred;
+
+ g_free (out);
return TRUE;
}
static void
prefmode_load_current_modes_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -1195,22 +1200,22 @@ prefmode_load_current_modes_ready (MMBroadbandModemHuawei *self,
&error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
- MMModemModeCombination out;
+ MMModemModeCombination *out;
- out.allowed = current->allowed;
- out.preferred = current->preferred;
- g_simple_async_result_set_op_res_gpointer (simple, &out, NULL);
+ out = g_new (MMModemModeCombination, 1);
+ out->allowed = current->allowed;
+ out->preferred = current->preferred;
+ g_task_return_pointer (task, out, g_free);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
syscfg_load_current_modes_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -1223,23 +1228,22 @@ syscfg_load_current_modes_ready (MMBroadbandModemHuawei *self,
&error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
- MMModemModeCombination out;
+ MMModemModeCombination *out;
- out.allowed = current->allowed;
- out.preferred = current->preferred;
- g_simple_async_result_set_op_res_gpointer (simple, &out, NULL);
+ out = g_new (MMModemModeCombination, 1);
+ out->allowed = current->allowed;
+ out->preferred = current->preferred;
+ g_task_return_pointer (task, out, g_free);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
syscfgex_load_current_modes_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -1251,17 +1255,16 @@ syscfgex_load_current_modes_ready (MMBroadbandModemHuawei *self,
self->priv->syscfgex_supported_modes,
&error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
- MMModemModeCombination out;
+ MMModemModeCombination *out;
- out.allowed = current->allowed;
- out.preferred = current->preferred;
- g_simple_async_result_set_op_res_gpointer (simple, &out, NULL);
+ out = g_new (MMModemModeCombination, 1);
+ out->allowed = current->allowed;
+ out->preferred = current->preferred;
+ g_task_return_pointer (task, out, g_free);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -1270,14 +1273,9 @@ load_current_modes (MMIfaceModem *_self,
gpointer user_data)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- mm_dbg ("loading current modes (huawei)...");
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (self->priv->syscfgex_support == FEATURE_SUPPORTED) {
g_assert (self->priv->syscfgex_supported_modes != NULL);
@@ -1287,7 +1285,7 @@ load_current_modes (MMIfaceModem *_self,
3,
FALSE,
(GAsyncReadyCallback)syscfgex_load_current_modes_ready,
- result);
+ task);
return;
}
@@ -1299,7 +1297,7 @@ load_current_modes (MMIfaceModem *_self,
3,
FALSE,
(GAsyncReadyCallback)syscfg_load_current_modes_ready,
- result);
+ task);
return;
}
@@ -1311,16 +1309,15 @@ load_current_modes (MMIfaceModem *_self,
3,
FALSE,
(GAsyncReadyCallback)prefmode_load_current_modes_ready,
- result);
+ task);
return;
}
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unable to load current modes");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unable to load current modes");
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -1331,31 +1328,30 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
set_current_modes_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static gboolean
prefmode_set_current_modes (MMBroadbandModemHuawei *self,
MMModemMode allowed,
MMModemMode preferred,
- GSimpleAsyncResult *simple,
+ GTask *task,
GError **error)
{
guint i;
@@ -1389,7 +1385,7 @@ prefmode_set_current_modes (MMBroadbandModemHuawei *self,
3,
FALSE,
(GAsyncReadyCallback)set_current_modes_ready,
- simple);
+ task);
g_free (command);
return TRUE;
}
@@ -1398,7 +1394,7 @@ static gboolean
syscfg_set_current_modes (MMBroadbandModemHuawei *self,
MMModemMode allowed,
MMModemMode preferred,
- GSimpleAsyncResult *simple,
+ GTask *task,
GError **error)
{
guint i;
@@ -1434,7 +1430,7 @@ syscfg_set_current_modes (MMBroadbandModemHuawei *self,
3,
FALSE,
(GAsyncReadyCallback)set_current_modes_ready,
- simple);
+ task);
g_free (command);
return TRUE;
}
@@ -1443,7 +1439,7 @@ static gboolean
syscfgex_set_current_modes (MMBroadbandModemHuawei *self,
MMModemMode allowed,
MMModemMode preferred,
- GSimpleAsyncResult *simple,
+ GTask *task,
GError **error)
{
guint i;
@@ -1478,7 +1474,7 @@ syscfgex_set_current_modes (MMBroadbandModemHuawei *self,
3,
FALSE,
(GAsyncReadyCallback)set_current_modes_ready,
- simple);
+ task);
g_free (command);
return TRUE;
}
@@ -1491,31 +1487,25 @@ set_current_modes (MMIfaceModem *_self,
gpointer user_data)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
GError *error = NULL;
- mm_dbg ("setting current modes (huawei)...");
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (self->priv->syscfgex_support == FEATURE_SUPPORTED)
- syscfgex_set_current_modes (self, allowed, preferred, result, &error);
+ syscfgex_set_current_modes (self, allowed, preferred, task, &error);
else if (self->priv->syscfg_support == FEATURE_SUPPORTED)
- syscfg_set_current_modes (self, allowed, preferred, result, &error);
+ syscfg_set_current_modes (self, allowed, preferred, task, &error);
else if (self->priv->prefmode_support == FEATURE_SUPPORTED)
- prefmode_set_current_modes (self, allowed, preferred, result, &error);
+ prefmode_set_current_modes (self, allowed, preferred, task, &error);
else
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Setting current modes is not supported");
if (error) {
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
}
@@ -1537,11 +1527,10 @@ huawei_signal_changed (MMPortSerialAt *port,
quality = 0;
} else {
/* Normalize the quality */
- quality = CLAMP (quality, 0, 31) * 100 / 31;
+ quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31;
}
- mm_dbg ("3GPP signal quality: %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -1571,7 +1560,7 @@ huawei_mode_changed (MMPortSerialAt *port,
(act < MM_MODEM_ACCESS_TECHNOLOGY_GSM ||
act > MM_MODEM_ACCESS_TECHNOLOGY_EDGE)) {
str = mm_modem_access_technology_build_string_from_mask (act);
- mm_warn ("Unexpected access technology (%s) in GSM/GPRS mode", str);
+ mm_obj_warn (self, "unexpected access technology (%s) in GSM/GPRS mode", str);
g_free (str);
act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
@@ -1584,7 +1573,7 @@ huawei_mode_changed (MMPortSerialAt *port,
(act < MM_MODEM_ACCESS_TECHNOLOGY_UMTS ||
act > MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS)) {
str = mm_modem_access_technology_build_string_from_mask (act);
- mm_warn ("Unexpected access technology (%s) in WCDMA mode", str);
+ mm_obj_warn (self, "unexpected access technology (%s) in WCDMA mode", str);
g_free (str);
act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
@@ -1596,7 +1585,7 @@ huawei_mode_changed (MMPortSerialAt *port,
if (act != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN &&
act != MM_MODEM_ACCESS_TECHNOLOGY_1XRTT) {
str = mm_modem_access_technology_build_string_from_mask (act);
- mm_warn ("Unexpected access technology (%s) in CDMA mode", str);
+ mm_obj_warn (self, "unexpected access technology (%s) in CDMA mode", str);
g_free (str);
act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
@@ -1611,7 +1600,7 @@ huawei_mode_changed (MMPortSerialAt *port,
(act < MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 ||
act > MM_MODEM_ACCESS_TECHNOLOGY_EVDOB)) {
str = mm_modem_access_technology_build_string_from_mask (act);
- mm_warn ("Unexpected access technology (%s) in EVDO mode", str);
+ mm_obj_warn (self, "unexpected access technology (%s) in EVDO mode", str);
g_free (str);
act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
@@ -1625,14 +1614,10 @@ huawei_mode_changed (MMPortSerialAt *port,
break;
default:
- mm_warn ("Unexpected mode change value reported: '%d'", a);
+ mm_obj_warn (self, "unexpected mode change value reported: '%d'", a);
return;
}
- str = mm_modem_access_technology_build_string_from_mask (act);
- mm_dbg ("Access Technology: '%s'", str);
- g_free (str);
-
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), act, mask);
}
@@ -1645,10 +1630,9 @@ huawei_status_changed (MMPortSerialAt *port,
gint n1, n2, n3, n4, n5, n6, n7;
str = g_match_info_fetch (match_info, 1);
- if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7)) {
- mm_dbg ("Duration: %d Up: %d Kbps Down: %d Kbps Total: %d Total: %d\n",
- n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024);
- }
+ if (sscanf (str, "%x,%x,%x,%x,%x,%x,%x", &n1, &n2, &n3, &n4, &n5, &n6, &n7))
+ mm_obj_dbg (self, "duration: %d up: %d Kbps down: %d Kbps total: %d total: %d\n",
+ n1, n2 * 8 / 1000, n3 * 8 / 1000, n4 / 1024, n5 / 1024);
g_free (str);
}
@@ -1665,14 +1649,11 @@ bearer_report_connection_status (MMBaseBearer *bearer,
{
if (ndisstat_result->ipv4_available) {
/* TODO: MMBroadbandBearerHuawei does not currently support IPv6.
- * When it does, we should check the IP family associated with each bearer.
- *
- * Also, send DISCONNECTING so that we give some time before actually
- * disconnecting the connection */
+ * When it does, we should check the IP family associated with each bearer. */
mm_base_bearer_report_connection_status (bearer,
ndisstat_result->ipv4_connected ?
MM_BEARER_CONNECTION_STATUS_CONNECTED :
- MM_BEARER_CONNECTION_STATUS_DISCONNECTING);
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
}
}
@@ -1693,19 +1674,19 @@ huawei_ndisstat_changed (MMPortSerialAt *port,
&ndisstat_result.ipv6_available,
&ndisstat_result.ipv6_connected,
&error)) {
- mm_dbg ("Ignore invalid ^NDISSTAT unsolicited message: '%s' (error %s)",
- str, error->message);
+ mm_obj_dbg (self, "ignored invalid ^NDISSTAT unsolicited message '%s': %s",
+ str, error->message);
g_error_free (error);
g_free (str);
return;
}
g_free (str);
- mm_dbg ("NDIS status: IPv4 %s, IPv6 %s",
- ndisstat_result.ipv4_available ?
- (ndisstat_result.ipv4_connected ? "connected" : "disconnected") : "not available",
- ndisstat_result.ipv6_available ?
- (ndisstat_result.ipv6_connected ? "connected" : "disconnected") : "not available");
+ mm_obj_dbg (self, "NDIS status: IPv4 %s, IPv6 %s",
+ ndisstat_result.ipv4_available ?
+ (ndisstat_result.ipv4_connected ? "connected" : "disconnected") : "not available",
+ ndisstat_result.ipv6_available ?
+ (ndisstat_result.ipv6_connected ? "connected" : "disconnected") : "not available");
/* If empty bearer list, nothing else to do */
g_object_get (self,
@@ -1722,12 +1703,148 @@ huawei_ndisstat_changed (MMPortSerialAt *port,
}
static void
+detailed_signal_clear (DetailedSignal *signal)
+{
+ g_clear_object (&signal->cdma);
+ g_clear_object (&signal->evdo);
+ g_clear_object (&signal->gsm);
+ g_clear_object (&signal->umts);
+ g_clear_object (&signal->lte);
+}
+
+static gboolean
+get_rssi_dbm (guint rssi, gdouble *out_val)
+{
+ if (rssi <= 96) {
+ *out_val = (double) (-121.0 + rssi);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+get_ecio_db (guint ecio, gdouble *out_val)
+{
+ if (ecio <= 65) {
+ *out_val = -32.5 + ((double) ecio / 2.0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+get_rsrp_dbm (guint rsrp, gdouble *out_val)
+{
+ if (rsrp <= 97) {
+ *out_val = (double) (-141.0 + rsrp);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+get_sinr_db (guint sinr, gdouble *out_val)
+{
+ if (sinr <= 251) {
+ *out_val = -20.2 + (double) (sinr / 5.0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+get_rsrq_db (guint rsrq, gdouble *out_val)
+{
+ if (rsrq <= 34) {
+ *out_val = -20 + (double) (rsrq / 2.0);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+huawei_hcsq_changed (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemHuawei *self)
+{
+ gchar *str;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ guint value1 = 0;
+ guint value2 = 0;
+ guint value3 = 0;
+ guint value4 = 0;
+ guint value5 = 0;
+ gdouble v;
+ GError *error = NULL;
+
+ str = g_match_info_fetch (match_info, 1);
+ if (!mm_huawei_parse_hcsq_response (str,
+ &act,
+ &value1,
+ &value2,
+ &value3,
+ &value4,
+ &value5,
+ &error)) {
+ mm_obj_dbg (self, "ignored invalid ^HCSQ message '%s': %s", str, error->message);
+ g_error_free (error);
+ g_free (str);
+ return;
+ }
+ g_free (str);
+
+ detailed_signal_clear (&self->priv->detailed_signal);
+
+ /* 2G */
+ if (act == MM_MODEM_ACCESS_TECHNOLOGY_GSM) {
+ self->priv->detailed_signal.gsm = mm_signal_new ();
+ /* value1: gsm_rssi */
+ if (get_rssi_dbm (value1, &v))
+ mm_signal_set_rssi (self->priv->detailed_signal.gsm, v);
+ return;
+ }
+
+ /* 3G */
+ if (act == MM_MODEM_ACCESS_TECHNOLOGY_UMTS) {
+ self->priv->detailed_signal.umts = mm_signal_new ();
+ /* value1: wcdma_rssi */
+ if (get_rssi_dbm (value1, &v))
+ mm_signal_set_rssi (self->priv->detailed_signal.umts, v);
+ /* value2: wcdma_rscp; unused */
+ /* value3: wcdma_ecio */
+ if (get_ecio_db (value3, &v))
+ mm_signal_set_ecio (self->priv->detailed_signal.umts, v);
+ return;
+ }
+
+ /* 4G */
+ if (act == MM_MODEM_ACCESS_TECHNOLOGY_LTE) {
+ self->priv->detailed_signal.lte = mm_signal_new ();
+ /* value1: lte_rssi */
+ if (get_rssi_dbm (value1, &v))
+ mm_signal_set_rssi (self->priv->detailed_signal.lte, v);
+ /* value2: lte_rsrp */
+ if (get_rsrp_dbm (value2, &v))
+ mm_signal_set_rsrp (self->priv->detailed_signal.lte, v);
+ /* value3: lte_sinr -> SNR? */
+ if (get_sinr_db (value3, &v))
+ mm_signal_set_snr (self->priv->detailed_signal.lte, v);
+ /* value4: lte_rsrq */
+ if (get_rsrq_db (value4, &v))
+ mm_signal_set_rsrq (self->priv->detailed_signal.lte, v);
+ return;
+ }
+
+ /* CDMA and EVDO not yet supported */
+}
+
+static void
set_3gpp_unsolicited_events_handlers (MMBroadbandModemHuawei *self,
gboolean enable)
{
GList *ports, *l;
- ports = get_at_port_list (self);
+ ports = mm_broadband_modem_huawei_get_at_port_list (self);
/* Enable/disable unsolicited events in given port */
for (l = ports; l; l = g_list_next (l)) {
@@ -1763,9 +1880,16 @@ set_3gpp_unsolicited_events_handlers (MMBroadbandModemHuawei *self,
enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_ndisstat_changed : NULL,
enable ? self : NULL,
NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->hcsq_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)huawei_hcsq_changed : NULL,
+ enable ? self : NULL,
+ NULL);
}
- g_list_free_full (ports, (GDestroyNotify)g_object_unref);
+ g_list_free_full (ports, g_object_unref);
}
static gboolean
@@ -1773,26 +1897,24 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_3gpp_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -1800,33 +1922,29 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_3gpp_setup_unsolicited_events_ready,
- result);
+ task);
}
static void
parent_3gpp_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -1834,12 +1952,9 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own cleanup first */
set_3gpp_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE);
@@ -1848,7 +1963,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_3gpp_cleanup_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1859,23 +1974,22 @@ modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
own_enable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
@@ -1889,14 +2003,13 @@ static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
/* Our own enable now */
@@ -1908,7 +2021,7 @@ parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -1916,18 +2029,15 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's enable */
iface_modem_3gpp_parent->enable_unsolicited_events (
self,
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1938,36 +2048,34 @@ modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
own_disable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_full_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1975,7 +2083,7 @@ own_disable_unsolicited_events_ready (MMBaseModem *self,
iface_modem_3gpp_parent->disable_unsolicited_events (
MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -1983,12 +2091,9 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own disable first */
mm_base_modem_at_command_full (
@@ -2000,138 +2105,81 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- result);
-}
-
-/*****************************************************************************/
-/* Operator Name loading (3GPP interface) */
-
-static gchar *
-modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
-{
- const gchar *result;
- gchar *operator_name;
-
- result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
- if (!result)
- return NULL;
-
- /* Despite +CSCS? may claim supporting UCS2, Huawei modems always report the
- * operator name in ASCII in a +COPS response. Thus, we ignore the current
- * charset claimed by the modem and assume the charset is IRA when parsing
- * the operator name.
- */
- operator_name = mm_3gpp_parse_operator (result, MM_MODEM_CHARSET_IRA);
- if (operator_name)
- mm_dbg ("loaded Operator Name: %s", operator_name);
-
- return operator_name;
-}
-
-static void
-modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- mm_dbg ("loading Operator Name (huawei)...");
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+COPS=3,0;+COPS?",
- 3,
- FALSE,
- callback,
- user_data);
+ task);
}
/*****************************************************************************/
/* Create Bearer (Modem interface) */
-typedef struct {
- MMBroadbandModemHuawei *self;
- GSimpleAsyncResult *result;
- MMBearerProperties *properties;
-} CreateBearerContext;
-
-static void
-create_bearer_context_complete_and_free (CreateBearerContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_object_unref (ctx->properties);
- g_slice_free (CreateBearerContext, ctx);
-}
-
static MMBaseBearer *
huawei_modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New huawei bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_huawei_new_ready (GObject *source,
GAsyncResult *res,
- CreateBearerContext *ctx)
+ GTask *task)
{
MMBaseBearer *bearer;
GError *error = NULL;
bearer = mm_broadband_bearer_huawei_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, (GDestroyNotify)g_object_unref);
- create_bearer_context_complete_and_free (ctx);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
- CreateBearerContext *ctx)
+ GTask *task)
{
MMBaseBearer *bearer;
GError *error = NULL;
bearer = mm_broadband_bearer_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, (GDestroyNotify)g_object_unref);
- create_bearer_context_complete_and_free (ctx);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
static void
-create_bearer_for_net_port (CreateBearerContext *ctx)
+create_bearer_for_net_port (GTask *task)
{
- switch (ctx->self->priv->ndisdup_support) {
- case FEATURE_SUPPORT_UNKNOWN:
- g_assert_not_reached ();
+ MMBroadbandModemHuawei *self;
+ MMBearerProperties *properties;
+
+ self = g_task_get_source_object (task);
+ properties = g_task_get_task_data (task);
+
+ switch (self->priv->ndisdup_support) {
case FEATURE_NOT_SUPPORTED:
- mm_dbg ("^NDISDUP not supported, creating default bearer...");
- mm_broadband_bearer_new (MM_BROADBAND_MODEM (ctx->self),
- ctx->properties,
+ mm_obj_dbg (self, "^NDISDUP not supported, creating default bearer...");
+ mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
+ properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- ctx);
+ task);
return;
case FEATURE_SUPPORTED:
- mm_dbg ("^NDISDUP supported, creating huawei bearer...");
- mm_broadband_bearer_huawei_new (MM_BROADBAND_MODEM_HUAWEI (ctx->self),
- ctx->properties,
+ mm_obj_dbg (self, "^NDISDUP supported, creating huawei bearer...");
+ mm_broadband_bearer_huawei_new (MM_BROADBAND_MODEM_HUAWEI (self),
+ properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_huawei_new_ready,
- ctx);
+ task);
return;
+ case FEATURE_SUPPORT_UNKNOWN:
+ default:
+ g_assert_not_reached ();
}
}
@@ -2141,29 +2189,30 @@ peek_port_at_for_data (MMBroadbandModemHuawei *self,
{
GList *cdc_wdm_at_ports, *l;
const gchar *net_port_parent_path;
+ MMPortSerialAt *found = NULL;
g_warn_if_fail (mm_port_get_subsys (port) == MM_PORT_SUBSYS_NET);
- net_port_parent_path = mm_port_get_parent_path (port);
+ net_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (port));
if (!net_port_parent_path) {
- g_warning ("(%s) no parent path for net port", mm_port_get_device (port));
+ mm_obj_warn (self, "no parent path for net port %s", mm_port_get_device (port));
return NULL;
}
/* Find the CDC-WDM port on the same USB interface as the given net port */
cdc_wdm_at_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
- MM_PORT_SUBSYS_USB,
- MM_PORT_TYPE_AT,
- NULL);
- for (l = cdc_wdm_at_ports; l; l = g_list_next (l)) {
+ MM_PORT_SUBSYS_USBMISC,
+ MM_PORT_TYPE_AT);
+ for (l = cdc_wdm_at_ports; l && !found; l = g_list_next (l)) {
const gchar *wdm_port_parent_path;
g_assert (MM_IS_PORT_SERIAL_AT (l->data));
- wdm_port_parent_path = mm_port_get_parent_path (MM_PORT (l->data));
+ wdm_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (MM_PORT (l->data)));
if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path))
- return MM_PORT_SERIAL_AT (l->data);
+ found = MM_PORT_SERIAL_AT (l->data);
}
- return NULL;
+ g_list_free_full (cdc_wdm_at_ports, g_object_unref);
+ return found;
}
@@ -2177,8 +2226,7 @@ mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self,
found = peek_port_at_for_data (self, port);
if (!found)
- mm_warn ("Couldn't find associated cdc-wdm port for 'net/%s'",
- mm_port_get_device (port));
+ mm_obj_dbg (self, "couldn't find associated cdc-wdm port for %s", mm_port_get_device (port));
return found;
}
@@ -2186,44 +2234,29 @@ static void
ensure_ndisdup_support_checked (MMBroadbandModemHuawei *self,
MMPort *port)
{
- GUdevClient *client;
- GUdevDevice *data_device;
-
/* Check NDISDUP support the first time we need it */
if (self->priv->ndisdup_support != FEATURE_SUPPORT_UNKNOWN)
return;
/* First, check for devices which support NDISDUP on any AT port. These
* devices are tagged by udev */
- client = g_udev_client_new (NULL);
- data_device = (g_udev_client_query_by_subsystem_and_name (
- client,
- "net",
- mm_port_get_device (port)));
- if (data_device && g_udev_device_get_property_as_boolean (data_device, "ID_MM_HUAWEI_NDISDUP_SUPPORTED")) {
- mm_dbg ("This device (%s) can support ndisdup feature", mm_port_get_device (port));
+ if (mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (port), "ID_MM_HUAWEI_NDISDUP_SUPPORTED")) {
+ mm_obj_dbg (self, "^NDISDUP is supported");
self->priv->ndisdup_support = FEATURE_SUPPORTED;
- goto out;
}
-
/* Then, look for devices which have both a net port and a cdc-wdm
* AT-capable port. We assume that these devices allow NDISDUP only
* when issued in the cdc-wdm port. */
- if (peek_port_at_for_data (self, port)) {
- mm_dbg ("This device (%s) can support ndisdup feature on non-serial AT port",
- mm_port_get_device (port));
+ else if (peek_port_at_for_data (self, port)) {
+ mm_obj_dbg (self, "^NDISDUP is supported on non-serial AT port");
self->priv->ndisdup_support = FEATURE_SUPPORTED;
- goto out;
}
- mm_dbg ("This device (%s) can not support ndisdup feature", mm_port_get_device (port));
- self->priv->ndisdup_support = FEATURE_NOT_SUPPORTED;
+ if (self->priv->ndisdup_support != FEATURE_SUPPORT_UNKNOWN)
+ return;
-out:
- if (data_device)
- g_object_unref (data_device);
- if (client)
- g_object_unref (client);
+ mm_obj_dbg (self, "^NDISDUP is not supported");
+ self->priv->ndisdup_support = FEATURE_NOT_SUPPORTED;
}
static void
@@ -2232,34 +2265,34 @@ huawei_modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- CreateBearerContext *ctx;
+ GTask *task;
MMPort *port;
- ctx = g_slice_new0 (CreateBearerContext);
- ctx->self = g_object_ref (self);
- ctx->properties = g_object_ref (properties);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- huawei_modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, g_object_ref (properties), g_object_unref);
port = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET);
if (port) {
- ensure_ndisdup_support_checked (ctx->self, port);
- create_bearer_for_net_port (ctx);
+ ensure_ndisdup_support_checked (MM_BROADBAND_MODEM_HUAWEI (self), port);
+ create_bearer_for_net_port (task);
return;
}
- mm_dbg ("Creating default bearer...");
+ mm_obj_dbg (self, "creating default bearer...");
mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- ctx);
+ task);
}
/*****************************************************************************/
-/* USSD encode/decode (3GPP-USSD interface) */
+/* USSD encode/decode (3GPP-USSD interface)
+ *
+ * Huawei devices don't use the current charset (as per AT+CSCS) in the CUSD
+ * command, they instead expect data encoded in GSM-7 already, given as a
+ * hex string.
+ */
static gchar *
encode (MMIfaceModem3gppUssd *self,
@@ -2267,28 +2300,27 @@ encode (MMIfaceModem3gppUssd *self,
guint *scheme,
GError **error)
{
- gchar *hex;
- guint8 *gsm, *packed;
- guint32 len = 0, packed_len = 0;
+ g_autoptr(GByteArray) gsm = NULL;
+ g_autofree guint8 *packed = NULL;
+ guint32 packed_len = 0;
+
+ gsm = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (!gsm)
+ return NULL;
*scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
- gsm = mm_charset_utf8_to_unpacked_gsm (command, &len);
/* If command is a multiple of 7 characters long, Huawei firmwares
* apparently want that padded. Maybe all modems?
*/
- if (len % 7 == 0) {
- gsm = g_realloc (gsm, len + 1);
- gsm[len] = 0x0d;
- len++;
- }
+ if (gsm->len % 7 == 0) {
+ static const guint8 padding = 0x0d;
- packed = gsm_pack (gsm, len, 0, &packed_len);
- hex = mm_utils_bin2hexstr (packed, packed_len);
- g_free (packed);
- g_free (gsm);
+ g_byte_array_append (gsm, &padding, 1);
+ }
- return hex;
+ packed = mm_charset_gsm_pack (gsm->data, gsm->len, 0, &packed_len);
+ return mm_utils_bin2hexstr (packed, packed_len);
}
static gchar *
@@ -2296,21 +2328,25 @@ decode (MMIfaceModem3gppUssd *self,
const gchar *reply,
GError **error)
{
- gchar *bin, *utf8;
- guint8 *unpacked;
- gsize bin_len;
- guint32 unpacked_len;
+ g_autofree guint8 *bin = NULL;
+ gsize bin_len = 0;
+ g_autofree guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+ g_autoptr(GByteArray) unpacked_array = NULL;
+
+ bin = mm_utils_hexstr2bin (reply, -1, &bin_len, error);
+ if (!bin)
+ return NULL;
- bin = mm_utils_hexstr2bin (reply, &bin_len);
- unpacked = gsm_unpack ((guint8*) bin, (bin_len * 8) / 7, 0, &unpacked_len);
+ unpacked = mm_charset_gsm_unpack (bin, (bin_len * 8) / 7, 0, &unpacked_len);
/* if the last character in a 7-byte block is padding, then drop it */
if ((bin_len % 7 == 0) && (unpacked[unpacked_len - 1] == 0x0d))
unpacked_len--;
- utf8 = (char*) mm_charset_gsm_unpacked_to_utf8 (unpacked, unpacked_len);
- g_free (bin);
- g_free (unpacked);
- return utf8;
+ unpacked_array = g_byte_array_sized_new (unpacked_len);
+ g_byte_array_append (unpacked_array, unpacked, unpacked_len);
+
+ return mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
}
/*****************************************************************************/
@@ -2325,9 +2361,9 @@ huawei_1x_signal_changed (MMPortSerialAt *port,
if (!mm_get_uint_from_match_info (match_info, 1, &quality))
return;
- quality = CLAMP (quality, 0, 100);
- mm_dbg ("1X signal quality: %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ quality = MM_CLAMP_HIGH (quality, 100);
+ mm_obj_dbg (self, "1X signal quality: %u", quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -2340,9 +2376,9 @@ huawei_evdo_signal_changed (MMPortSerialAt *port,
if (!mm_get_uint_from_match_info (match_info, 1, &quality))
return;
- quality = CLAMP (quality, 0, 100);
- mm_dbg ("EVDO signal quality: %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ quality = MM_CLAMP_HIGH (quality, 100);
+ mm_obj_dbg (self, "EVDO signal quality: %u", quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
/* Signal quality loading (Modem interface) */
@@ -2352,36 +2388,37 @@ modem_load_signal_quality_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return 0;
+ GError *inner_error = NULL;
+ gssize value;
- return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return 0;
+ }
+ return (guint)value;
}
static void
parent_load_signal_quality_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
guint signal_quality;
signal_quality = iface_modem_parent->load_signal_quality_finish (self, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (signal_quality),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, signal_quality);
+ g_object_unref (task);
}
static void
signal_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response, *command;
gchar buf[5];
@@ -2393,11 +2430,11 @@ signal_ready (MMBaseModem *self,
iface_modem_parent->load_signal_quality (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_signal_quality_ready,
- simple);
+ task);
return;
}
- command = g_object_get_data (G_OBJECT (simple), "command");
+ command = g_task_get_task_data (task);
g_assert (command);
response = mm_strip_tag (response, command);
/* 'command' won't include the trailing ':' in the response, so strip that */
@@ -2410,20 +2447,17 @@ signal_ready (MMBaseModem *self,
buf[i++] = *response++;
if (mm_get_uint_from_str (buf, &quality)) {
- quality = CLAMP (quality, 0, 100);
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (quality),
- NULL);
+ quality = MM_CLAMP_HIGH (quality, 100);
+ g_task_return_int (task, quality);
} else {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse %s response: '%s'",
- command, response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse %s response: '%s'",
+ command, response);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -2431,22 +2465,18 @@ modem_load_signal_quality (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
const char *command = "^CSQLVL";
- mm_dbg ("loading signal quality...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_signal_quality);
+ task = g_task_new (self, NULL, callback, user_data);
/* 3GPP modems can just run parent's signal quality loading */
if (mm_iface_modem_is_3gpp (self)) {
iface_modem_parent->load_signal_quality (
self,
(GAsyncReadyCallback)parent_load_signal_quality_ready,
- result);
+ task);
return;
}
@@ -2457,7 +2487,8 @@ modem_load_signal_quality (MMIfaceModem *self,
NULL);
if (evdo_state > MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
command = "^HDRCSQLVL";
- g_object_set_data (G_OBJECT (result), "command", (gpointer) command);
+
+ g_task_set_task_data (task, g_strdup (command), g_free);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
@@ -2465,7 +2496,7 @@ modem_load_signal_quality (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)signal_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -2477,7 +2508,7 @@ set_cdma_unsolicited_events_handlers (MMBroadbandModemHuawei *self,
{
GList *ports, *l;
- ports = get_at_port_list (self);
+ ports = mm_broadband_modem_huawei_get_at_port_list (self);
/* Enable/disable unsolicited events in given port */
for (l = ports; l; l = g_list_next (l)) {
@@ -2505,7 +2536,7 @@ set_cdma_unsolicited_events_handlers (MMBroadbandModemHuawei *self,
NULL);
}
- g_list_free_full (ports, (GDestroyNotify)g_object_unref);
+ g_list_free_full (ports, g_object_unref);
}
static gboolean
@@ -2513,26 +2544,24 @@ modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_cdma_setup_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_cdma_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -2540,12 +2569,9 @@ modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_setup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup if needed */
if (iface_modem_cdma_parent->setup_unsolicited_events &&
@@ -2553,30 +2579,28 @@ modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self,
iface_modem_cdma_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cdma_setup_unsolicited_events_ready,
- result);
+ task);
return;
}
/* Otherwise just run our setup and complete */
set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result), TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
parent_cdma_cleanup_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_cdma_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -2584,12 +2608,9 @@ modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_cleanup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own cleanup first */
set_cdma_unsolicited_events_handlers (MM_BROADBAND_MODEM_HUAWEI (self), FALSE);
@@ -2600,14 +2621,13 @@ modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self,
iface_modem_cdma_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cdma_cleanup_unsolicited_events_ready,
- result);
+ task);
return;
}
/* Otherwise we're done */
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result), TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -2633,35 +2653,39 @@ setup_registration_checks_finish (MMIfaceModemCdma *self,
{
SetupRegistrationChecksResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step;
*skip_qcdm_hdr_step = results->skip_qcdm_hdr_step;
*skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step;
*skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step;
*skip_detailed_registration_state = results->skip_detailed_registration_state;
+ g_free (results);
return TRUE;
}
static void
parent_setup_registration_checks_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
+ SetupRegistrationChecksResults *results;
GError *error = NULL;
- SetupRegistrationChecksResults results = { 0 };
+
+ results = g_new0 (SetupRegistrationChecksResults, 1);
if (!iface_modem_cdma_parent->setup_registration_checks_finish (self,
res,
- &results.skip_qcdm_call_manager_step,
- &results.skip_qcdm_hdr_step,
- &results.skip_at_cdma_service_status_step,
- &results.skip_at_cdma1x_serving_system_step,
- &results.skip_detailed_registration_state,
+ &results->skip_qcdm_call_manager_step,
+ &results->skip_qcdm_hdr_step,
+ &results->skip_at_cdma_service_status_step,
+ &results->skip_at_cdma1x_serving_system_step,
+ &results->skip_detailed_registration_state,
&error)) {
- g_simple_async_result_take_error (simple, error);
+ g_free (results);
+ g_task_return_error (task, error);
} else {
gboolean evdo_supported = FALSE;
@@ -2674,18 +2698,16 @@ parent_setup_registration_checks_ready (MMIfaceModemCdma *self,
* AT+CSS won't necessarily report EVDO registration status, only 1X.
*/
if (evdo_supported)
- results.skip_at_cdma1x_serving_system_step = TRUE;
+ results->skip_at_cdma1x_serving_system_step = TRUE;
/* Force to always use the detailed registration checks, as we have
* ^SYSINFO for that */
- results.skip_detailed_registration_state = FALSE;
+ results->skip_detailed_registration_state = FALSE;
- g_simple_async_result_set_op_res_gpointer (simple, &results, NULL);
+ g_task_return_pointer (task, results, g_free);
}
- /* All done. NOTE: complete NOT in idle! */
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -2693,17 +2715,14 @@ setup_registration_checks (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- setup_registration_checks);
+ task = g_task_new (self, NULL, callback, user_data);
/* Run parent's checks first */
iface_modem_cdma_parent->setup_registration_checks (self,
(GAsyncReadyCallback)parent_setup_registration_checks_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -2715,21 +2734,9 @@ typedef struct {
} DetailedRegistrationStateResults;
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
DetailedRegistrationStateResults state;
} DetailedRegistrationStateContext;
-static void
-detailed_registration_state_context_complete_and_free (DetailedRegistrationStateContext *ctx)
-{
- /* Always not in idle! we're passing a struct in stack as result */
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
get_detailed_registration_state_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
@@ -2739,25 +2746,29 @@ get_detailed_registration_state_finish (MMIfaceModemCdma *self,
{
DetailedRegistrationStateResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*detailed_cdma1x_state = results->detailed_cdma1x_state;
*detailed_evdo_state = results->detailed_evdo_state;
+ g_free (results);
return TRUE;
}
static void
registration_state_sysinfo_ready (MMBroadbandModemHuawei *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
+ DetailedRegistrationStateContext *ctx;
gboolean extended = FALSE;
guint srv_status = 0;
guint sys_mode = 0;
guint roam_status = 0;
+ ctx = g_task_get_task_data (task);
+
if (!sysinfo_finish (self,
res,
&extended,
@@ -2769,10 +2780,11 @@ registration_state_sysinfo_ready (MMBroadbandModemHuawei *self,
NULL, /* sys_submode_valid */
NULL, /* sys_submode */
NULL)) {
- /* If error, leave superclass' reg state alone if ^SYSINFO isn't supported.
- * NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
+ /* If error, leave superclass' reg state alone if ^SYSINFO isn't supported. */
+ g_task_return_pointer (task,
+ g_memdup (&ctx->state, sizeof (ctx->state)),
+ g_free);
+ g_object_unref (task);
return;
}
@@ -2807,14 +2819,15 @@ registration_state_sysinfo_ready (MMBroadbandModemHuawei *self,
if (!cdma1x && !evdo) {
/* Say we're registered to something even though sysmode parsing failed */
- mm_dbg ("Assuming registered at least in CDMA1x");
+ mm_obj_dbg (self, "assuming registered at least in CDMA1x");
ctx->state.detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
}
}
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ g_memdup (&ctx->state, sizeof (ctx->state)),
+ g_free);
+ g_object_unref (task);
}
static void
@@ -2825,20 +2838,645 @@ get_detailed_registration_state (MMIfaceModemCdma *self,
gpointer user_data)
{
DetailedRegistrationStateContext *ctx;
+ GTask *task;
/* Setup context */
- ctx = g_new0 (DetailedRegistrationStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_detailed_registration_state);
+ ctx = g_new (DetailedRegistrationStateContext, 1);
ctx->state.detailed_cdma1x_state = cdma1x_state;
ctx->state.detailed_evdo_state = evdo_state;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
sysinfo (MM_BROADBAND_MODEM_HUAWEI (self),
(GAsyncReadyCallback)registration_state_sysinfo_ready,
- ctx);
+ task);
+}
+
+/*****************************************************************************/
+/* Check if Voice supported (Voice interface) */
+
+static gboolean
+modem_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_parent_check_support_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ gboolean parent_support;
+
+ parent_support = iface_modem_voice_parent->check_support_finish (self, res, NULL);
+ g_task_return_boolean (task, parent_support);
+ g_object_unref (task);
+}
+
+static void
+cvoice_check_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ GError *error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response ||
+ !mm_huawei_parse_cvoice_response (response,
+ &self->priv->audio_hz,
+ &self->priv->audio_bits,
+ &error)) {
+ self->priv->cvoice_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_dbg (self, "CVOICE is unsupported: %s", error->message);
+ g_clear_error (&error);
+
+ /* Now check generic support */
+ iface_modem_voice_parent->check_support (MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)voice_parent_check_support_ready,
+ task);
+ return;
+ }
+
+ mm_obj_dbg (self, "CVOICE is supported");
+ self->priv->cvoice_support = FEATURE_SUPPORTED;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ /* Check for Huawei-specific ^CVOICE support */
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^CVOICE?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)cvoice_check_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* In-call audio channel setup/cleanup */
+
+static gboolean
+modem_voice_cleanup_in_call_audio_channel_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_voice_cleanup_in_call_audio_channel (MMIfaceModemVoice *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If there is no CVOICE support, no custom audio setup required
+ * (i.e. audio path is externally managed) */
+ if (self->priv->cvoice_support == FEATURE_SUPPORTED) {
+ MMPort *port;
+
+ /* The QCDM port, if present, switches back from voice to QCDM after
+ * the voice call is dropped. */
+ port = MM_PORT (mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)));
+ if (port) {
+ /* During a voice call, we'll set the QCDM port as connected, and that
+ * will make us ignore all incoming data and avoid sending any outgoing
+ * data. */
+ mm_port_set_connected (port, FALSE);
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static gboolean
+modem_voice_setup_in_call_audio_channel_finish (MMIfaceModemVoice *_self,
+ GAsyncResult *res,
+ MMPort **audio_port,
+ MMCallAudioFormat **audio_format,
+ GError **error)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ if (self->priv->cvoice_support == FEATURE_SUPPORTED) {
+ MMPort *port;
+
+ /* Setup audio format */
+ if (audio_format) {
+ gchar *resolution_str;
+
+ resolution_str = g_strdup_printf ("s%ule", self->priv->audio_bits);
+ *audio_format = mm_call_audio_format_new ();
+ mm_call_audio_format_set_encoding (*audio_format, "pcm");
+ mm_call_audio_format_set_resolution (*audio_format, resolution_str);
+ mm_call_audio_format_set_rate (*audio_format, self->priv->audio_hz);
+ g_free (resolution_str);
+ }
+
+ /* The QCDM port, if present, switches from QCDM to voice while
+ * a voice call is active. */
+ port = MM_PORT (mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)));
+ if (port) {
+ /* During a voice call, we'll set the QCDM port as connected, and that
+ * will make us ignore all incoming data and avoid sending any outgoing
+ * data. */
+ mm_port_set_connected (port, TRUE);
+ }
+
+ if (audio_port)
+ *audio_port = (port ? g_object_ref (port) : NULL);;
+ } else {
+ if (audio_format)
+ *audio_format = NULL;
+ if (audio_port)
+ *audio_port = NULL;
+ }
+
+ return TRUE;
+}
+
+static void
+ddsetex_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_setup_in_call_audio_channel (MMIfaceModemVoice *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If there is no CVOICE support, no custom audio setup required
+ * (i.e. audio path is externally managed) */
+ if (self->priv->cvoice_support != FEATURE_SUPPORTED) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Enable audio streaming on the audio port */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^DDSETEX=2",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback)ddsetex_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common setup/cleanup voice unsolicited events */
+
+typedef enum {
+ HUAWEI_CALL_TYPE_VOICE = 0,
+ HUAWEI_CALL_TYPE_CS_DATA = 1,
+ HUAWEI_CALL_TYPE_PS_DATA = 2,
+ HUAWEI_CALL_TYPE_CDMA_SMS = 3,
+ HUAWEI_CALL_TYPE_OTA_STANDARD_OTASP = 7,
+ HUAWEI_CALL_TYPE_OTA_NON_STANDARD_OTASP = 8,
+ HUAWEI_CALL_TYPE_EMERGENCY = 9,
+} HuaweiCallType;
+
+static void
+orig_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemHuawei *self)
+{
+ MMCallInfo call_info = { 0 };
+ guint aux = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &aux)) {
+ mm_obj_warn (self, "couldn't parse call type from ^ORIG");
+ return;
+ }
+ if (aux != HUAWEI_CALL_TYPE_VOICE && aux != HUAWEI_CALL_TYPE_EMERGENCY) {
+ mm_obj_dbg (self, "ignored ^ORIG for non-voice call");
+ return;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &aux)) {
+ mm_obj_warn (self, "couldn't parse call index from ^ORIG");
+ return;
+ }
+ call_info.index = aux;
+ call_info.state = MM_CALL_STATE_DIALING;
+ call_info.direction = MM_CALL_DIRECTION_OUTGOING;
+
+ mm_obj_dbg (self, "call %u state updated: dialing", call_info.index);
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+conf_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemHuawei *self)
+{
+ MMCallInfo call_info = { 0 };
+ guint aux = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &aux)) {
+ mm_obj_warn (self, "couldn't parse call index from ^CONF");
+ return;
+ }
+ call_info.index = aux;
+ call_info.state = MM_CALL_STATE_RINGING_OUT;
+ call_info.direction = MM_CALL_DIRECTION_OUTGOING;
+
+ mm_obj_dbg (self, "call %u state updated: ringing-out", call_info.index);
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+conn_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemHuawei *self)
+{
+ MMCallInfo call_info = { 0 };
+ guint aux = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &aux)) {
+ mm_obj_warn (self, "couldn't parse call index from ^CONN");
+ return;
+ }
+ call_info.index = aux;
+ call_info.state = MM_CALL_STATE_ACTIVE;
+ call_info.direction = MM_CALL_DIRECTION_UNKNOWN;
+
+ mm_obj_dbg (self, "call %u state updated: active", aux);
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+cend_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemHuawei *self)
+{
+ MMCallInfo call_info = { 0 };
+ guint aux = 0;
+
+ /* only index is mandatory */
+ if (!mm_get_uint_from_match_info (match_info, 1, &aux)) {
+ mm_obj_warn (self, "couldn't parse call index from ^CEND");
+ return;
+ }
+ call_info.index = aux;
+ call_info.state = MM_CALL_STATE_TERMINATED;
+ call_info.direction = MM_CALL_DIRECTION_UNKNOWN;
+
+ mm_obj_dbg (self, "call %u state updated: terminated", call_info.index);
+ if (mm_get_uint_from_match_info (match_info, 2, &aux))
+ mm_obj_dbg (self, " call duration: %u seconds", aux);
+ if (mm_get_uint_from_match_info (match_info, 3, &aux))
+ mm_obj_dbg (self, " end status code: %u", aux);
+ if (mm_get_uint_from_match_info (match_info, 4, &aux))
+ mm_obj_dbg (self, " call control cause: %u", aux);
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+ddtmf_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemHuawei *self)
+{
+ gchar *dtmf;
+
+ dtmf = g_match_info_fetch (match_info, 1);
+ mm_obj_dbg (self, "received DTMF: %s", dtmf);
+ /* call index unknown */
+ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf);
+ g_free (dtmf);
+}
+
+static void
+common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemHuawei *self,
+ gboolean enable)
+{
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->orig_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)orig_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->conf_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)conf_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->conn_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)conn_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->cend_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)cend_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->ddtmf_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)ddtmf_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
+
+/*****************************************************************************/
+/* Setup unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Our own setup now */
+ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_HUAWEI (self), TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's setup */
+ iface_modem_voice_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* cleanup our own */
+ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_HUAWEI (self), FALSE);
+
+ /* Chain up parent's cleanup */
+ iface_modem_voice_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enabling unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+own_voice_enable_unsolicited_events_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static const MMBaseModemAtCommand unsolicited_voice_enable_sequence[] = {
+ /* With ^DDTMFCFG we active the DTMF Decoder */
+ { "^DDTMFCFG=0,1", 3, FALSE, NULL },
+ { NULL }
+};
+
+static void
+parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Our own enable now */
+ mm_base_modem_at_sequence_full (
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+ unsolicited_voice_enable_sequence,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)own_voice_enable_unsolicited_events_ready,
+ task);
+}
+
+static void
+modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's enable */
+ iface_modem_voice_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Disabling unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+own_voice_disable_unsolicited_events_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static const MMBaseModemAtCommand unsolicited_voice_disable_sequence[] = {
+ /* With ^DDTMFCFG we deactivate the DTMF Decoder */
+ { "^DDTMFCFG=1,0", 3, FALSE, NULL },
+ { NULL }
+};
+
+static void
+parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* our own disable now */
+
+ mm_base_modem_at_sequence_full (
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+ unsolicited_voice_disable_sequence,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)own_voice_disable_unsolicited_events_ready,
+ task);
+}
+
+static void
+modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's disable */
+ iface_modem_voice_parent->disable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Create call (Voice interface) */
+
+static MMBaseCall *
+create_call (MMIfaceModemVoice *self,
+ MMCallDirection direction,
+ const gchar *number)
+{
+ return mm_base_call_new (MM_BASE_MODEM (self),
+ direction,
+ number,
+ TRUE, /* skip_incoming_timeout */
+ TRUE, /* supports_dialing_to_ringing */
+ TRUE); /* supports_ringing_to_active) */
}
/*****************************************************************************/
@@ -2923,10 +3561,10 @@ enable_disable_unsolicited_rfswitch_event_handler (MMBroadbandModemHuawei *self,
{
GList *ports, *l;
- ports = get_at_port_list (self);
+ ports = mm_broadband_modem_huawei_get_at_port_list (self);
- mm_dbg ("%s ^RFSWITCH unsolicited event handler",
- enable ? "Enable" : "Disable");
+ mm_obj_dbg (self, "%s ^RFSWITCH unsolicited event handler",
+ enable ? "enable" : "disable");
for (l = ports; l; l = g_list_next (l)) {
MMPortSerialAt *port = MM_PORT_SERIAL_AT (l->data);
@@ -2937,20 +3575,20 @@ enable_disable_unsolicited_rfswitch_event_handler (MMBroadbandModemHuawei *self,
enable);
}
- g_list_free_full (ports, (GDestroyNotify)g_object_unref);
+ g_list_free_full (ports, g_object_unref);
}
static void
parent_load_power_state_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *result)
+ GTask *task)
{
GError *error = NULL;
MMModemPowerState power_state;
power_state = iface_modem_parent->load_power_state_finish (self, res, &error);
if (error)
- g_simple_async_result_take_error (result, error);
+ g_task_return_error (task, error);
else {
/* As modem_power_down uses +CFUN=0 to put the modem in low state, we treat
* CFUN 0 as 'LOW' power state instead of 'OFF'. Otherwise, MMIfaceModem
@@ -2958,17 +3596,16 @@ parent_load_power_state_ready (MMIfaceModem *self,
if (power_state == MM_MODEM_POWER_STATE_OFF)
power_state = MM_MODEM_POWER_STATE_LOW;
- g_simple_async_result_set_op_res_gpointer (result, GUINT_TO_POINTER (power_state), NULL);
+ g_task_return_int (task, power_state);
}
- g_simple_async_result_complete (result);
- g_object_unref (result);
+ g_object_unref (task);
}
static void
huawei_rfswitch_check_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *result)
+ GTask *task)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
GError *error = NULL;
@@ -2983,48 +3620,37 @@ huawei_rfswitch_check_ready (MMBaseModem *_self,
response = mm_strip_tag (response, "^RFSWITCH:");
if (sscanf (response, "%d", &sw_state) != 1 ||
(sw_state != 0 && sw_state != 1)) {
- mm_warn ("Couldn't parse ^RFSWITCH response: '%s'", response);
+ mm_obj_warn (self, "couldn't parse ^RFSWITCH response '%s'", response);
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
- "Couldn't parse ^RFSWITCH response: '%s'",
+ "Couldn't parse ^RFSWITCH response '%s'",
response);
}
}
- switch (self->priv->rfswitch_support) {
- case FEATURE_SUPPORT_UNKNOWN:
+ if (self->priv->rfswitch_support == FEATURE_SUPPORT_UNKNOWN) {
if (error) {
- mm_dbg ("The device does not support ^RFSWITCH");
+ mm_obj_dbg (self, "^RFSWITCH is not supported");
self->priv->rfswitch_support = FEATURE_NOT_SUPPORTED;
g_error_free (error);
/* Fall back to parent's load_power_state */
iface_modem_parent->load_power_state (MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_power_state_ready,
- result);
+ task);
return;
}
- mm_dbg ("The device supports ^RFSWITCH");
+ mm_obj_dbg (self, "^RFSWITCH is supported");
self->priv->rfswitch_support = FEATURE_SUPPORTED;
- break;
- case FEATURE_SUPPORTED:
- break;
- default:
- g_assert_not_reached ();
- break;
}
if (error)
- g_simple_async_result_take_error (result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (result,
- sw_state ?
- GUINT_TO_POINTER (MM_MODEM_POWER_STATE_ON) :
- GUINT_TO_POINTER (MM_MODEM_POWER_STATE_LOW),
- NULL);
+ g_task_return_int (task,
+ sw_state ? MM_MODEM_POWER_STATE_ON : MM_MODEM_POWER_STATE_LOW);
- g_simple_async_result_complete (result);
- g_object_unref (result);
+ g_object_unref (task);
}
static MMModemPowerState
@@ -3032,10 +3658,15 @@ load_power_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_POWER_STATE_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemPowerState)GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+ }
+ return (MMModemPowerState)value;
}
static void
@@ -3043,12 +3674,9 @@ load_power_state (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_power_state);
+ task = g_task_new (self, NULL, callback, user_data);
switch (MM_BROADBAND_MODEM_HUAWEI (self)->priv->rfswitch_support) {
case FEATURE_SUPPORT_UNKNOWN:
@@ -3064,14 +3692,14 @@ load_power_state (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)huawei_rfswitch_check_ready,
- result);
+ task);
break;
}
case FEATURE_NOT_SUPPORTED:
/* Run parent's load_power_state */
iface_modem_parent->load_power_state (self,
(GAsyncReadyCallback)parent_load_power_state_ready,
- result);
+ task);
break;
default:
g_assert_not_reached ();
@@ -3112,6 +3740,7 @@ huawei_modem_power_up (MMIfaceModem *self,
callback,
user_data);
break;
+ case FEATURE_SUPPORT_UNKNOWN:
default:
g_assert_not_reached ();
break;
@@ -3153,6 +3782,7 @@ huawei_modem_power_down (MMIfaceModem *self,
callback,
user_data);
break;
+ case FEATURE_SUPPORT_UNKNOWN:
default:
g_assert_not_reached ();
break;
@@ -3191,25 +3821,29 @@ location_load_capabilities_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_LOCATION_SOURCE_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
}
static void
parent_load_capabilities_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMModemLocationSource sources;
GError *error = NULL;
sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -3220,11 +3854,8 @@ parent_load_capabilities_ready (MMIfaceModemLocation *self,
MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED);
/* So we're done, complete */
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (sources),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, sources);
+ g_object_unref (task);
}
static void
@@ -3232,259 +3863,211 @@ location_load_capabilities (MMIfaceModemLocation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- location_load_capabilities);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup */
iface_modem_location_parent->load_capabilities (self,
(GAsyncReadyCallback)parent_load_capabilities_ready,
- result);
+ task);
}
/*****************************************************************************/
-/* Enable/Disable location gathering (Location interface) */
-
-typedef struct {
- MMBroadbandModemHuawei *self;
- GSimpleAsyncResult *result;
- MMModemLocationSource source;
- int idx;
-} LocationGatheringContext;
-
-static void
-location_gathering_context_complete_and_free (LocationGatheringContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LocationGatheringContext, ctx);
-}
-
-/******************************/
-/* Disable location gathering */
+/* Disable location gathering (Location interface) */
static gboolean
-disable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
+disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-gps_disabled_ready (MMBaseModem *self,
+gps_disabled_ready (MMBaseModem *self,
GAsyncResult *res,
- LocationGatheringContext *ctx)
+ GTask *task)
{
- MMPortSerialGps *gps_port;
GError *error = NULL;
- if (!mm_base_modem_at_command_full_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-
- /* Only use the GPS port in NMEA/RAW setups */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
- /* Even if we get an error here, we try to close the GPS port */
- gps_port = mm_base_modem_peek_port_gps (self);
- if (gps_port)
- mm_port_serial_close (MM_PORT_SERIAL (gps_port));
- }
-
- location_gathering_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-disable_location_gathering (MMIfaceModemLocation *_self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data)
+disable_location_gathering (MMIfaceModemLocation *_self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
- gboolean stop_gps = FALSE;
- LocationGatheringContext *ctx;
+ GTask *task;
- ctx = g_slice_new (LocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disable_location_gathering);
- ctx->source = source;
+ /* NOTE: no parent disable_location_gathering() implementation */
- /* Only stop GPS engine if no GPS-related sources enabled */
- if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- self->priv->enabled_sources &= ~source;
+ task = g_task_new (self, NULL, callback, user_data);
- if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
- stop_gps = TRUE;
- }
+ self->priv->enabled_sources &= ~source;
- if (stop_gps) {
- mm_base_modem_at_command_full (MM_BASE_MODEM (_self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (_self)),
- "^WPEND",
- 3,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_disabled_ready,
- ctx);
+ /* Only stop GPS engine if no GPS-related sources enabled */
+ if ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) &&
+ !(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) {
+ MMPortSerialGps *gps_port;
+
+ /* Close the data port if we don't need it anymore */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) {
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (_self),
+ "^WPEND",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_disabled_ready,
+ task);
return;
}
/* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- location_gathering_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Enable location gathering (Location interface) */
+static const MMBaseModemAtCommand gps_startup[] = {
+ { "^WPDOM=0", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { "^WPDST=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { "^WPDFR=65535,30", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { "^WPDGP", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { NULL }
+};
+
static gboolean
-enable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
+enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static char *gps_startup[] = {
- "^WPDOM=0",
- "^WPDST=1",
- "^WPDFR=65535,30",
- "^WPDGP",
- NULL
-};
-
static void
-gps_enabled_ready (MMBaseModem *self,
+gps_startup_ready (MMBaseModem *_self,
GAsyncResult *res,
- LocationGatheringContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MMPortSerialGps *gps_port;
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ MMModemLocationSource source;
+ GError *error = NULL;
- if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
- ctx->idx = 0;
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
+ mm_base_modem_at_sequence_finish (_self, res, NULL, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* ctx->idx++; make sure ctx->idx is a valid command */
- if (gps_startup[ctx->idx++] && gps_startup[ctx->idx]) {
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- gps_startup[ctx->idx],
- 3,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_enabled_ready,
- ctx);
- return;
- }
-
- /* Only use the GPS port in NMEA/RAW setups */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
- gps_port = mm_base_modem_peek_port_gps (self);
- if (!gps_port ||
- !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* Only open the GPS port in NMEA/RAW setups */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't open raw GPS serial port");
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ } else {
+ /* GPS port was successfully opened */
+ self->priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ }
+ } else {
+ /* No need to open GPS port */
+ self->priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ }
- location_gathering_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
-parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
- GAsyncResult *res,
- LocationGatheringContext *ctx)
+parent_enable_location_gathering_ready (MMIfaceModemLocation *_self,
+ GAsyncResult *res,
+ GTask *task)
{
- gboolean start_gps = FALSE;
- GError *error = NULL;
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ GError *error = NULL;
+ MMModemLocationSource source;
+ gboolean start_gps = FALSE;
- if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
+ if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Now our own enabling */
- /* NMEA and RAW are both enabled in the same way */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- /* Only start GPS engine if not done already */
- if (!(ctx->self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* Only start GPS engine if not done already */
+ start_gps = ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) &&
+ !(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
- start_gps = TRUE;
- ctx->self->priv->enabled_sources |= ctx->source;
- }
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)));
if (start_gps) {
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- gps_startup[ctx->idx],
- 3,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_enabled_ready,
- ctx);
+ mm_base_modem_at_sequence (
+ MM_BASE_MODEM (self),
+ gps_startup,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ (GAsyncReadyCallback)gps_startup_ready,
+ task);
return;
}
/* For any other location (e.g. 3GPP), or if GPS already running just return */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- location_gathering_context_complete_and_free (ctx);
+ self->priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-enable_location_gathering (MMIfaceModemLocation *self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data)
+enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- LocationGatheringContext *ctx;
+ GTask *task;
- ctx = g_slice_new (LocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enable_location_gathering);
- ctx->source = source;
- ctx->idx = 0;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
/* Chain up parent's gathering enable */
iface_modem_location_parent->enable_location_gathering (self,
source,
(GAsyncReadyCallback)parent_enable_location_gathering_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -3495,35 +4078,34 @@ modem_time_check_support_finish (MMIfaceModemTime *_self,
GAsyncResult *res,
GError **error)
{
- MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
-
- if (self->priv->nwtime_support == FEATURE_SUPPORTED)
- return TRUE;
- if (self->priv->time_support == FEATURE_SUPPORTED)
- return TRUE;
- return FALSE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-modem_time_check_ready (MMBaseModem *self,
+modem_time_check_ready (MMBaseModem *_self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+
/* Responses are checked in the sequence parser, ignore overall result */
- mm_base_modem_at_sequence_finish (self, res, NULL, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ mm_base_modem_at_sequence_finish (_self, res, NULL, NULL);
+
+ g_task_return_boolean (task,
+ (self->priv->nwtime_support == FEATURE_SUPPORTED ||
+ self->priv->time_support == FEATURE_SUPPORTED));
+ g_object_unref (task);
}
-static gboolean
-modem_check_time_reply (MMBaseModem *_self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+modem_check_time_reply (MMBaseModem *_self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
@@ -3539,7 +4121,9 @@ modem_check_time_reply (MMBaseModem *_self,
self->priv->time_support = FEATURE_NOT_SUPPORTED;
}
- return FALSE;
+ *result = NULL;
+ *result_error = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
static const MMBaseModemAtCommand time_cmd_sequence[] = {
@@ -3553,19 +4137,152 @@ modem_time_check_support (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_time_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
time_cmd_sequence,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
(GAsyncReadyCallback)modem_time_check_ready,
- result);
+ task);
+}
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
+
+static void
+hcsq_check_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (response)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static gboolean
+signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+signal_check_support (MMIfaceModemSignal *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^HCSQ?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)hcsq_check_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Load extended signal information */
+
+static void
+detailed_signal_free (DetailedSignal *signal)
+{
+ detailed_signal_clear (signal);
+ g_slice_free (DetailedSignal, signal);
+}
+
+static gboolean
+signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
+{
+ DetailedSignal *signals;
+
+ signals = g_task_propagate_pointer (G_TASK (res), error);
+ if (!signals)
+ return FALSE;
+
+ *cdma = signals->cdma ? g_object_ref (signals->cdma) : NULL;
+ *evdo = signals->evdo ? g_object_ref (signals->evdo) : NULL;
+ *gsm = signals->gsm ? g_object_ref (signals->gsm) : NULL;
+ *umts = signals->umts ? g_object_ref (signals->umts) : NULL;
+ *lte = signals->lte ? g_object_ref (signals->lte) : NULL;
+ *nr5g = signals->nr5g ? g_object_ref (signals->nr5g) : NULL;
+
+ detailed_signal_free (signals);
+ return TRUE;
+}
+
+static void
+hcsq_get_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ DetailedSignal *signals;
+ GError *error = NULL;
+
+ /* Don't care about the response; it will have been parsed by the HCSQ
+ * unsolicited event handler and self->priv->detailed_signal will already
+ * be updated.
+ */
+ if (!mm_base_modem_at_command_finish (_self, res, &error)) {
+ mm_obj_dbg (self, "^HCSQ failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ signals = g_slice_new0 (DetailedSignal);
+ signals->cdma = self->priv->detailed_signal.cdma ? g_object_ref (self->priv->detailed_signal.cdma) : NULL;
+ signals->evdo = self->priv->detailed_signal.evdo ? g_object_ref (self->priv->detailed_signal.evdo) : NULL;
+ signals->gsm = self->priv->detailed_signal.gsm ? g_object_ref (self->priv->detailed_signal.gsm) : NULL;
+ signals->umts = self->priv->detailed_signal.umts ? g_object_ref (self->priv->detailed_signal.umts) : NULL;
+ signals->lte = self->priv->detailed_signal.lte ? g_object_ref (self->priv->detailed_signal.lte) : NULL;
+
+ g_task_return_pointer (task, signals, (GDestroyNotify)detailed_signal_free);
+ g_object_unref (task);
+}
+
+static void
+signal_load_values (MMIfaceModemSignal *_self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (_self);
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ /* Clear any previous detailed signal values to get new ones */
+ detailed_signal_clear (&self->priv->detailed_signal);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "^HCSQ?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)hcsq_get_ready,
+ task);
}
/*****************************************************************************/
@@ -3576,7 +4293,7 @@ set_ignored_unsolicited_events_handlers (MMBroadbandModemHuawei *self)
{
GList *ports, *l;
- ports = get_at_port_list (self);
+ ports = mm_broadband_modem_huawei_get_at_port_list (self);
/* Enable/disable unsolicited events in given port */
for (l = ports; l; l = g_list_next (l)) {
@@ -3620,10 +4337,6 @@ set_ignored_unsolicited_events_handlers (MMBroadbandModemHuawei *self)
NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (
port,
- self->priv->hcsq_regex,
- NULL, NULL, NULL);
- mm_port_serial_at_add_unsolicited_msg_handler (
- port,
self->priv->pdpdeact_regex,
NULL, NULL, NULL);
mm_port_serial_at_add_unsolicited_msg_handler (
@@ -3634,9 +4347,41 @@ set_ignored_unsolicited_events_handlers (MMBroadbandModemHuawei *self)
port,
self->priv->rfswitch_regex,
NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->position_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->posend_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->ecclist_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->ltersrp_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->cschannelinfo_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->ccallstate_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->eons_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ self->priv->lwurc_regex,
+ NULL, NULL, NULL);
}
- g_list_free_full (ports, (GDestroyNotify)g_object_unref);
+ g_list_free_full (ports, g_object_unref);
}
static void
@@ -3692,6 +4437,9 @@ mm_broadband_modem_huawei_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (TTY) or Huawei bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -3719,6 +4467,18 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self)
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->ndisstat_regex = g_regex_new ("\\r\\n(\\^NDISSTAT:.+)\\r+\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ self->priv->orig_regex = g_regex_new ("\\r\\n\\^ORIG:\\s*(\\d+),\\s*(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->conf_regex = g_regex_new ("\\r\\n\\^CONF:\\s*(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->conn_regex = g_regex_new ("\\r\\n\\^CONN:\\s*(\\d+),\\s*(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->cend_regex = g_regex_new ("\\r\\n\\^CEND:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(\\d*))?\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->ddtmf_regex = g_regex_new ("\\r\\n\\^DDTMF:\\s*([0-9A-D\\*\\#])\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
self->priv->boot_regex = g_regex_new ("\\r\\n\\^BOOT:.+\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->connect_regex = g_regex_new ("\\r\\n\\^CONNECT .+\\r\\n",
@@ -3737,7 +4497,7 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self)
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->stin_regex = g_regex_new ("\\r\\n\\^STIN:.+\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- self->priv->hcsq_regex = g_regex_new ("\\r\\n\\^HCSQ:.+\\r+\\n",
+ self->priv->hcsq_regex = g_regex_new ("\\r\\n(\\^HCSQ:.+)\\r+\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->pdpdeact_regex = g_regex_new ("\\r\\n\\^PDPDEACT:.+\\r+\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
@@ -3745,6 +4505,22 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self)
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->rfswitch_regex = g_regex_new ("\\r\\n\\^RFSWITCH:.+\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->position_regex = g_regex_new ("\\r\\n\\^POSITION:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->posend_regex = g_regex_new ("\\r\\n\\^POSEND:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->ecclist_regex = g_regex_new ("\\r\\n\\^ECCLIST:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->ltersrp_regex = g_regex_new ("\\r\\n\\^LTERSRP:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->cschannelinfo_regex = g_regex_new ("\\r\\n\\^CSCHANNELINFO:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->ccallstate_regex = g_regex_new ("\\r\\n\\^CCALLSTATE:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->eons_regex = g_regex_new ("\\r\\n\\^EONS:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->lwurc_regex = g_regex_new ("\\r\\n\\^LWURC:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
self->priv->ndisdup_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->rfswitch_support = FEATURE_SUPPORT_UNKNOWN;
@@ -3754,6 +4530,17 @@ mm_broadband_modem_huawei_init (MMBroadbandModemHuawei *self)
self->priv->prefmode_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->nwtime_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->time_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->cvoice_support = FEATURE_SUPPORT_UNKNOWN;
+}
+
+static void
+dispose (GObject *object)
+{
+ MMBroadbandModemHuawei *self = MM_BROADBAND_MODEM_HUAWEI (object);
+
+ detailed_signal_clear (&self->priv->detailed_signal);
+
+ G_OBJECT_CLASS (mm_broadband_modem_huawei_parent_class)->dispose (object);
}
static void
@@ -3767,6 +4554,12 @@ finalize (GObject *object)
g_regex_unref (self->priv->mode_regex);
g_regex_unref (self->priv->dsflowrpt_regex);
g_regex_unref (self->priv->ndisstat_regex);
+ g_regex_unref (self->priv->orig_regex);
+ g_regex_unref (self->priv->conf_regex);
+ g_regex_unref (self->priv->conn_regex);
+ g_regex_unref (self->priv->cend_regex);
+ g_regex_unref (self->priv->ddtmf_regex);
+
g_regex_unref (self->priv->boot_regex);
g_regex_unref (self->priv->connect_regex);
g_regex_unref (self->priv->csnr_regex);
@@ -3780,6 +4573,14 @@ finalize (GObject *object)
g_regex_unref (self->priv->pdpdeact_regex);
g_regex_unref (self->priv->ndisend_regex);
g_regex_unref (self->priv->rfswitch_regex);
+ g_regex_unref (self->priv->position_regex);
+ g_regex_unref (self->priv->posend_regex);
+ g_regex_unref (self->priv->ecclist_regex);
+ g_regex_unref (self->priv->ltersrp_regex);
+ g_regex_unref (self->priv->cschannelinfo_regex);
+ g_regex_unref (self->priv->ccallstate_regex);
+ g_regex_unref (self->priv->eons_regex);
+ g_regex_unref (self->priv->lwurc_regex);
if (self->priv->syscfg_supported_modes)
g_array_unref (self->priv->syscfg_supported_modes);
@@ -3841,8 +4642,6 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events;
iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish;
- iface->load_operator_name = modem_3gpp_load_operator_name;
- iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish;
}
static void
@@ -3892,6 +4691,38 @@ iface_modem_time_init (MMIfaceModemTime *iface)
}
static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->check_support = modem_voice_check_support;
+ iface->check_support_finish = modem_voice_check_support_finish;
+ iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_voice_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_voice_disable_unsolicited_events_finish;
+ iface->setup_in_call_audio_channel = modem_voice_setup_in_call_audio_channel;
+ iface->setup_in_call_audio_channel_finish = modem_voice_setup_in_call_audio_channel_finish;
+ iface->cleanup_in_call_audio_channel = modem_voice_cleanup_in_call_audio_channel;
+ iface->cleanup_in_call_audio_channel_finish = modem_voice_cleanup_in_call_audio_channel_finish;
+
+ iface->create_call = create_call;
+}
+
+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
mm_broadband_modem_huawei_class_init (MMBroadbandModemHuaweiClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -3899,6 +4730,7 @@ mm_broadband_modem_huawei_class_init (MMBroadbandModemHuaweiClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandModemHuaweiPrivate));
+ object_class->dispose = dispose;
object_class->finalize = finalize;
broadband_modem_class->setup_ports = setup_ports;
diff --git a/plugins/huawei/mm-broadband-modem-huawei.h b/plugins/huawei/mm-broadband-modem-huawei.h
index 85bfcdf2..9fb16811 100644
--- a/plugins/huawei/mm-broadband-modem-huawei.h
+++ b/plugins/huawei/mm-broadband-modem-huawei.h
@@ -50,5 +50,6 @@ MMBroadbandModemHuawei *mm_broadband_modem_huawei_new (const gchar *device,
MMPortSerialAt *mm_broadband_modem_huawei_peek_port_at_for_data (MMBroadbandModemHuawei *self,
MMPort *port);
+GList *mm_broadband_modem_huawei_get_at_port_list (MMBroadbandModemHuawei *self);
#endif /* MM_BROADBAND_MODEM_HUAWEI_H */
diff --git a/plugins/huawei/mm-modem-helpers-huawei.c b/plugins/huawei/mm-modem-helpers-huawei.c
index 7612e64d..b8548418 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.c
+++ b/plugins/huawei/mm-modem-helpers-huawei.c
@@ -22,9 +22,11 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
+#include "mm-common-helpers.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-huawei.h"
+#include "mm-huawei-enums-types.h"
/*****************************************************************************/
/* ^NDISSTAT / ^NDISSTATQRY response parser */
@@ -42,8 +44,8 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
GError *inner_error = NULL;
if (!response ||
- !(g_str_has_prefix (response, "^NDISSTAT:") ||
- g_str_has_prefix (response, "^NDISSTATQRY:"))) {
+ !(g_ascii_strncasecmp (response, "^NDISSTAT:", strlen ("^NDISSTAT:")) == 0 ||
+ g_ascii_strncasecmp (response, "^NDISSTATQRY:", strlen ("^NDISSTATQRY:")) == 0)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing ^NDISSTAT / ^NDISSTATQRY prefix");
return FALSE;
}
@@ -61,47 +63,81 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
* Or, in newer firmwares:
* ^NDISSTATQRY:0,,,"IPV4",0,,,"IPV6"
* OK
+ *
+ * Or, even (handled separately):
+ * ^NDISSTATQry:1
+ * OK
*/
- r = g_regex_new ("\\^NDISSTAT(?:QRY)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?"
- "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?",
- G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
- 0, NULL);
- g_assert (r != NULL);
- g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
- if (!inner_error && g_match_info_matches (match_info)) {
- guint ip_type_field = 4;
+ /* If multiple fields available, try first parsing method */
+ if (strchr (response, ',')) {
+ r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d),([^,]*),([^,]*),([^,\\r\\n]*)(?:\\r\\n)?"
+ "(?:\\^NDISSTAT:|\\^NDISSTATQRY:)?\\s*,?(\\d)?,?([^,]*)?,?([^,]*)?,?([^,\\r\\n]*)?(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ guint ip_type_field = 4;
+
+ /* IPv4 and IPv6 are fields 4 and (if available) 8 */
+
+ while (!inner_error && ip_type_field <= 8) {
+ gchar *ip_type_str;
+ guint connected;
+
+ ip_type_str = mm_get_string_unquoted_from_match_info (match_info, ip_type_field);
+ if (!ip_type_str)
+ break;
+
+ if (!mm_get_uint_from_match_info (match_info, (ip_type_field - 3), &connected) ||
+ (connected != 0 && connected != 1)) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields");
+ } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) {
+ *ipv4_available = TRUE;
+ *ipv4_connected = (gboolean)connected;
+ } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) {
+ *ipv6_available = TRUE;
+ *ipv6_connected = (gboolean)connected;
+ }
- /* IPv4 and IPv6 are fields 4 and (if available) 8 */
+ g_free (ip_type_str);
+ ip_type_field += 4;
+ }
+ }
- while (!inner_error && ip_type_field <= 8) {
- gchar *ip_type_str;
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
+ /* No separate IPv4/IPv6 info given just connected/not connected */
+ else {
+ r = g_regex_new ("\\^NDISSTAT(?:QRY)?(?:Qry)?:\\s*(\\d)(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
guint connected;
- ip_type_str = mm_get_string_unquoted_from_match_info (match_info, ip_type_field);
- if (!ip_type_str)
- break;
-
- if (!mm_get_uint_from_match_info (match_info, (ip_type_field - 3), &connected) ||
+ if (!mm_get_uint_from_match_info (match_info, 1, &connected) ||
(connected != 0 && connected != 1)) {
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't parse ^NDISSTAT / ^NDISSTATQRY fields");
- } else if (g_ascii_strcasecmp (ip_type_str, "IPV4") == 0) {
+ } else {
+ /* We'll assume IPv4 */
*ipv4_available = TRUE;
*ipv4_connected = (gboolean)connected;
- } else if (g_ascii_strcasecmp (ip_type_str, "IPV6") == 0) {
- *ipv6_available = TRUE;
- *ipv6_connected = (gboolean)connected;
}
-
- g_free (ip_type_str);
- ip_type_field += 4;
}
- }
- g_match_info_free (match_info);
- g_regex_unref (r);
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ }
if (!ipv4_available && !ipv6_available) {
inner_error = g_error_new (MM_CORE_ERROR,
@@ -118,6 +154,115 @@ mm_huawei_parse_ndisstatqry_response (const gchar *response,
}
/*****************************************************************************/
+/* ^DHCP response parser */
+
+static gboolean
+match_info_to_ip4_addr (GMatchInfo *match_info,
+ guint match_index,
+ guint *out_addr)
+{
+ g_autofree gchar *s = NULL;
+ g_autofree guint8 *bin = NULL;
+ gchar buf[9];
+ gsize len;
+ gsize bin_len;
+ guint32 aux;
+
+ s = g_match_info_fetch (match_info, match_index);
+ g_return_val_if_fail (s != NULL, FALSE);
+
+ len = strlen (s);
+ if (len == 1 && s[0] == '0') {
+ *out_addr = 0;
+ return TRUE;
+ }
+
+ if (len < 7 || len > 8)
+ return FALSE;
+
+ /* Handle possibly missing leading zero */
+ memset (buf, 0, sizeof (buf));
+ if (len == 7) {
+ strcpy (&buf[1], s);
+ buf[0] = '0';
+ } else if (len == 8)
+ strcpy (buf, s);
+ else
+ g_assert_not_reached ();
+
+ bin = mm_utils_hexstr2bin (buf, -1, &bin_len, NULL);
+ if (!bin || bin_len != 4)
+ return FALSE;
+
+ memcpy (&aux, bin, 4);
+ *out_addr = GUINT32_SWAP_LE_BE (aux);
+ return TRUE;
+}
+
+gboolean
+mm_huawei_parse_dhcp_response (const char *reply,
+ guint *out_address,
+ guint *out_prefix,
+ guint *out_gateway,
+ guint *out_dns1,
+ guint *out_dns2,
+ GError **error)
+{
+ gboolean matched;
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+
+ g_assert (reply != NULL);
+ g_assert (out_address != NULL);
+ g_assert (out_prefix != NULL);
+ g_assert (out_gateway != NULL);
+ g_assert (out_dns1 != NULL);
+ g_assert (out_dns2 != NULL);
+
+ /* Format:
+ *
+ * ^DHCP: <address>,<netmask>,<gateway>,<?>,<dns1>,<dns2>,<uplink>,<downlink>
+ *
+ * All numbers are hexadecimal representations of IPv4 addresses, with
+ * least-significant byte first. eg, 192.168.50.32 is expressed as
+ * "2032A8C0". Sometimes leading zeros are stripped, so "1010A0A" is
+ * actually 10.10.1.1.
+ */
+
+ r = g_regex_new ("\\^DHCP:\\s*(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),(?:0[xX])?([0-9a-fA-F]+),.*$", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ matched = g_regex_match_full (r, reply, -1, 0, 0, &match_info, &match_error);
+ if (!matched) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse ^DHCP results: ");
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match ^DHCP reply");
+ }
+ } else {
+ guint netmask;
+
+ if (match_info_to_ip4_addr (match_info, 1, out_address) &&
+ match_info_to_ip4_addr (match_info, 2, &netmask) &&
+ match_info_to_ip4_addr (match_info, 3, out_gateway) &&
+ match_info_to_ip4_addr (match_info, 5, out_dns1) &&
+ match_info_to_ip4_addr (match_info, 6, out_dns2)) {
+ *out_prefix = mm_count_bits_set (netmask);
+ matched = TRUE;
+ }
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ return matched;
+}
+
+/*****************************************************************************/
/* ^SYSINFO response parser */
gboolean
@@ -178,8 +323,7 @@ mm_huawei_parse_sysinfo_response (const char *reply,
}
}
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return matched;
}
@@ -243,8 +387,7 @@ mm_huawei_parse_sysinfoex_response (const char *reply,
mm_get_uint_from_match_info (match_info, 8, out_sys_submode);
}
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return matched;
}
@@ -286,8 +429,9 @@ mode_from_prefmode (guint huawei_mode,
}
GArray *
-mm_huawei_parse_prefmode_test (const gchar *response,
- GError **error)
+mm_huawei_parse_prefmode_test (const gchar *response,
+ gpointer log_object,
+ GError **error)
{
gchar **split;
guint i;
@@ -318,12 +462,12 @@ mm_huawei_parse_prefmode_test (const gchar *response,
continue;
if (!mm_get_uint_from_str (split[i], &val)) {
- mm_dbg ("Error parsing ^PREFMODE value: %s", split[i]);
+ mm_obj_dbg (log_object, "error parsing ^PREFMODE value '%s'", split[i]);
continue;
}
if (!mode_from_prefmode (val, &preferred, &inner_error)) {
- mm_dbg ("Unhandled ^PREFMODE: %s", inner_error->message);
+ mm_obj_dbg (log_object, "unhandled ^PREFMODE value: %s", inner_error->message);
g_error_free (inner_error);
continue;
}
@@ -378,7 +522,7 @@ mm_huawei_parse_prefmode_response (const gchar *response,
const GArray *supported_mode_combinations,
GError **error)
{
- gint mode;
+ guint mode;
guint i;
/* Format:
@@ -387,7 +531,7 @@ mm_huawei_parse_prefmode_response (const gchar *response,
*/
response = mm_strip_tag (response, "^PREFMODE:");
- if (!sscanf (response, "%d", &mode)) {
+ if (!mm_get_uint_from_str (response, &mode)) {
/* Dump error to upper layer */
g_set_error (error,
MM_CORE_ERROR,
@@ -565,9 +709,10 @@ mode_from_syscfg (guint huawei_mode,
}
static GArray *
-parse_syscfg_modes (const gchar *modes_str,
- const gchar *acqorder_str,
- GError **error)
+parse_syscfg_modes (const gchar *modes_str,
+ const gchar *acqorder_str,
+ gpointer log_object,
+ GError **error)
{
GArray *out;
gchar **split;
@@ -577,7 +722,7 @@ parse_syscfg_modes (const gchar *modes_str,
/* Start parsing acquisition order */
if (!sscanf (acqorder_str, "%d-%d", &min_acqorder, &max_acqorder))
- mm_dbg ("Error parsing ^SYSCFG acquisition order range (%s)", acqorder_str);
+ mm_obj_dbg (log_object, "error parsing ^SYSCFG acquisition order range '%s'", acqorder_str);
/* Just in case, we default to supporting only auto */
if (max_acqorder < min_acqorder) {
@@ -598,13 +743,13 @@ parse_syscfg_modes (const gchar *modes_str,
MMHuaweiSyscfgCombination combination;
if (!mm_get_uint_from_str (mm_strip_quotes (split[i]), &val)) {
- mm_dbg ("Error parsing ^SYSCFG mode value: %s", split[i]);
+ mm_obj_dbg (log_object, "error parsing ^SYSCFG mode value: %s", split[i]);
continue;
}
if (!mode_from_syscfg (val, &allowed, &inner_error)) {
if (inner_error) {
- mm_dbg ("Unhandled ^SYSCFG: %s", inner_error->message);
+ mm_obj_dbg (log_object, "unhandled ^SYSCFG: %s", inner_error->message);
g_error_free (inner_error);
}
continue;
@@ -665,8 +810,9 @@ parse_syscfg_modes (const gchar *modes_str,
}
GArray *
-mm_huawei_parse_syscfg_test (const gchar *response,
- GError **error)
+mm_huawei_parse_syscfg_test (const gchar *response,
+ gpointer log_object,
+ GError **error)
{
gchar **split;
GError *inner_error = NULL;
@@ -703,7 +849,7 @@ mm_huawei_parse_syscfg_test (const gchar *response,
}
/* Parse supported mode combinations */
- out = parse_syscfg_modes (split[0], split[1], &inner_error);
+ out = parse_syscfg_modes (split[0], split[1], log_object, &inner_error);
g_strfreev (split);
@@ -758,6 +904,12 @@ mm_huawei_parse_syscfg_response (const gchar *response,
return NULL;
}
+ /* Fix invalid modes with non-sensical acquisition orders */
+ if (mode == 14 && acqorder != 0) /* WCDMA only but acqorder != "Automatic" */
+ acqorder = 0;
+ else if (mode == 13 && acqorder != 0) /* GSM only but acqorder != "Automatic" */
+ acqorder = 0;
+
/* Look for current modes among the supported ones */
for (i = 0; i < supported_mode_combinations->len; i++) {
const MMHuaweiSyscfgCombination *combination;
@@ -840,7 +992,7 @@ parse_mode_combination_string (const gchar *mode_str,
case 1:
/* If only one mode allowed, NONE preferred */
*preferred = MM_MODEM_MODE_NONE;
- /* fall down */
+ /* fall through */
default:
return TRUE;
}
@@ -1105,8 +1257,7 @@ gboolean mm_huawei_parse_nwtime_response (const gchar *response,
}
}
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return ret;
@@ -1177,10 +1328,237 @@ gboolean mm_huawei_parse_time_response (const gchar *response,
}
}
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return ret;
}
+/*****************************************************************************/
+/* ^HCSQ response parser */
+
+gboolean
+mm_huawei_parse_hcsq_response (const gchar *response,
+ MMModemAccessTechnology *out_act,
+ guint *out_value1,
+ guint *out_value2,
+ guint *out_value3,
+ guint *out_value4,
+ guint *out_value5,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+ gboolean ret = FALSE;
+ char *s;
+
+ r = g_regex_new ("\\^HCSQ:\\s*\"?([a-zA-Z]*)\"?,(\\d+),?(\\d+)?,?(\\d+)?,?(\\d+)?,?(\\d+)?$", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse ^HCSQ results: ");
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match ^HCSQ reply");
+ }
+ goto done;
+ }
+
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ if (g_match_info_get_match_count (match_info) < 3) {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Not enough elements in ^HCSQ reply");
+ goto done;
+ }
+
+ if (out_act) {
+ s = g_match_info_fetch (match_info, 1);
+ *out_act = mm_string_to_access_tech (s);
+ g_free (s);
+ }
+
+ if (out_value1)
+ mm_get_uint_from_match_info (match_info, 2, out_value1);
+ if (out_value2)
+ mm_get_uint_from_match_info (match_info, 3, out_value2);
+ if (out_value3)
+ mm_get_uint_from_match_info (match_info, 4, out_value3);
+ if (out_value4)
+ mm_get_uint_from_match_info (match_info, 5, out_value4);
+ if (out_value5)
+ mm_get_uint_from_match_info (match_info, 6, out_value5);
+
+ ret = TRUE;
+
+done:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* ^CVOICE response parser */
+
+gboolean
+mm_huawei_parse_cvoice_response (const gchar *response,
+ guint *out_hz,
+ guint *out_bits,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+ guint supported = 0, hz = 0, bits = 0;
+ gboolean ret = FALSE;
+
+ /* ^CVOICE: <0=supported,1=unsupported>,<hz>,<bits>,<unknown> */
+ r = g_regex_new ("\\^CVOICE:\\s*(\\d)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)$", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse ^CVOICE results: ");
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match ^CVOICE reply");
+ }
+ } else {
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ g_assert (g_match_info_get_match_count (match_info) >= 5);
+
+ if (mm_get_uint_from_match_info (match_info, 1, &supported) &&
+ mm_get_uint_from_match_info (match_info, 2, &hz) &&
+ mm_get_uint_from_match_info (match_info, 3, &bits)) {
+ if (supported == 0) {
+ if (out_hz)
+ *out_hz = hz;
+ if (out_bits)
+ *out_bits = bits;
+ ret = TRUE;
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "^CVOICE not supported by this device");
+ }
+ } else {
+ g_set_error_literal (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse ^CVOICE reply");
+ }
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* ^GETPORTMODE response parser */
+
+#define GETPORTMODE_PREFIX "^GETPORTMODE:"
+
+GArray *
+mm_huawei_parse_getportmode_response (const gchar *response,
+ gpointer log_object,
+ GError **error)
+{
+ g_autoptr(GArray) modes = NULL;
+ g_auto(GStrv) split = NULL;
+ guint i;
+ gint n_items;
+
+ split = g_strsplit (response, ",", -1);
+ n_items = g_strv_length (split) - 1;
+ if (n_items < 1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected number of items in response");
+ return NULL;
+ }
+
+ /* validate response prefix */
+ if (g_ascii_strncasecmp (split[0], GETPORTMODE_PREFIX, strlen (GETPORTMODE_PREFIX)) != 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected response prefix");
+ return NULL;
+ }
+
+ mm_obj_dbg (log_object, "processing ^GETPORTMODE response...");
+
+ modes = g_array_sized_new (FALSE, FALSE, sizeof (MMHuaweiPortMode), n_items);
+
+ /* iterate all port items found */
+ for (i = 1; split[i]; i++) {
+ MMHuaweiPortMode mode = MM_HUAWEI_PORT_MODE_NONE;
+ gchar *separator;
+ guint port_number;
+
+ separator = strchr (split[i], ':');
+ if (!separator)
+ continue;
+
+ /* the reported port number may start either by 0 or by 1; the important
+ * thing is therefore no the number itself, only that it's a number */
+ g_strstrip (&separator[1]);
+ if (!mm_get_uint_from_str (&separator[1], &port_number)) {
+ mm_obj_warn (log_object, " couldn't parse port number: %s", split[i]);
+ break;
+ }
+
+ *separator = '\0';
+ g_strstrip (split[i]);
+
+ if (g_ascii_strcasecmp (split[i], "pcui") == 0)
+ mode = MM_HUAWEI_PORT_MODE_PCUI;
+ else if ((g_ascii_strcasecmp (split[i], "mdm") == 0) ||
+ (g_ascii_strcasecmp (split[i], "modem") == 0) ||
+ (g_ascii_strcasecmp (split[i], "3g_modem") == 0))
+ mode = MM_HUAWEI_PORT_MODE_MODEM;
+ else if ((g_ascii_strcasecmp (split[i], "diag") == 0) ||
+ (g_ascii_strcasecmp (split[i], "3g_diag") == 0) ||
+ (g_ascii_strcasecmp (split[i], "4g_diag") == 0))
+ mode = MM_HUAWEI_PORT_MODE_DIAG;
+ else if (g_ascii_strcasecmp (split[i], "gps") == 0)
+ mode = MM_HUAWEI_PORT_MODE_GPS;
+ else if ((g_ascii_strcasecmp (split[i], "ndis") == 0) ||
+ (g_ascii_strcasecmp (split[i], "rndis") == 0) ||
+ (g_ascii_strcasecmp (split[i], "ncm") == 0) ||
+ (g_ascii_strcasecmp (split[i], "ecm") == 0))
+ mode = MM_HUAWEI_PORT_MODE_NET;
+ else if (g_ascii_strcasecmp (split[i], "cdrom") == 0)
+ mode = MM_HUAWEI_PORT_MODE_CDROM;
+ else if ((g_ascii_strcasecmp (split[i], "sd") == 0) ||
+ (g_ascii_strncasecmp (split[i], "mass", 4) == 0))
+ mode = MM_HUAWEI_PORT_MODE_SD;
+ else if (g_ascii_strcasecmp (split[i], "bt") == 0)
+ mode = MM_HUAWEI_PORT_MODE_BT;
+ else if ((g_ascii_strcasecmp (split[i], "a_shell") == 0) ||
+ (g_ascii_strcasecmp (split[i], "c_shell") == 0))
+ mode = MM_HUAWEI_PORT_MODE_SHELL;
+
+ mm_obj_dbg (log_object, " port mode %s reported at port number %u",
+ mm_huawei_port_mode_get_string (mode), port_number);
+ g_array_append_val (modes, mode);
+ }
+
+ if (!modes->len) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No port modes loaded");
+ return NULL;
+ }
+
+ return g_steal_pointer (&modes);
+}
diff --git a/plugins/huawei/mm-modem-helpers-huawei.h b/plugins/huawei/mm-modem-helpers-huawei.h
index e225e908..3d1a4b22 100644
--- a/plugins/huawei/mm-modem-helpers-huawei.h
+++ b/plugins/huawei/mm-modem-helpers-huawei.h
@@ -17,7 +17,9 @@
#ifndef MM_MODEM_HELPERS_HUAWEI_H
#define MM_MODEM_HELPERS_HUAWEI_H
-#include "glib.h"
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
/*****************************************************************************/
/* ^NDISSTAT / ^NDISSTATQRY response parser */
@@ -29,6 +31,16 @@ gboolean mm_huawei_parse_ndisstatqry_response (const gchar *response,
GError **error);
/*****************************************************************************/
+/* ^DHCP response parser */
+gboolean mm_huawei_parse_dhcp_response (const char *reply,
+ guint *out_address,
+ guint *out_prefix,
+ guint *out_gateway,
+ guint *out_dns1,
+ guint *out_dns2,
+ GError **error);
+
+/*****************************************************************************/
/* ^SYSINFO response parser */
gboolean mm_huawei_parse_sysinfo_response (const char *reply,
guint *out_srv_status,
@@ -60,8 +72,9 @@ typedef struct {
MMModemMode preferred;
} MMHuaweiPrefmodeCombination;
-GArray *mm_huawei_parse_prefmode_test (const gchar *response,
- GError **error);
+GArray *mm_huawei_parse_prefmode_test (const gchar *response,
+ gpointer log_object,
+ GError **error);
/*****************************************************************************/
/* ^PREFMODE response parser */
@@ -84,8 +97,9 @@ typedef struct {
MMModemMode preferred;
} MMHuaweiSyscfgCombination;
-GArray *mm_huawei_parse_syscfg_test (const gchar *response,
- GError **error);
+GArray *mm_huawei_parse_syscfg_test (const gchar *response,
+ gpointer log_object,
+ GError **error);
/*****************************************************************************/
/* ^SYSCFG response parser */
@@ -129,4 +143,51 @@ gboolean mm_huawei_parse_time_response (const gchar *response,
MMNetworkTimezone **tzp,
GError **error);
+/*****************************************************************************/
+/* ^HCSQ response parser */
+
+gboolean mm_huawei_parse_hcsq_response (const gchar *response,
+ MMModemAccessTechnology *out_act,
+ guint *out_value1,
+ guint *out_value2,
+ guint *out_value3,
+ guint *out_value4,
+ guint *out_value5,
+ GError **error);
+
+/*****************************************************************************/
+/* ^CVOICE response parser */
+
+gboolean mm_huawei_parse_cvoice_response (const gchar *response,
+ guint *hz,
+ guint *bits,
+ GError **error);
+
+/*****************************************************************************/
+/* ^GETPORTMODE response parser */
+
+typedef enum { /*< underscore_name=mm_huawei_port_mode >*/
+ MM_HUAWEI_PORT_MODE_NONE,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_GPS,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_CDROM,
+ MM_HUAWEI_PORT_MODE_SD,
+ MM_HUAWEI_PORT_MODE_BT,
+ MM_HUAWEI_PORT_MODE_SHELL,
+} MMHuaweiPortMode;
+
+#define MM_HUAWEI_PORT_MODE_IS_SERIAL(mode) \
+ (mode == MM_HUAWEI_PORT_MODE_PCUI || \
+ mode == MM_HUAWEI_PORT_MODE_MODEM || \
+ mode == MM_HUAWEI_PORT_MODE_DIAG || \
+ mode == MM_HUAWEI_PORT_MODE_GPS || \
+ mode == MM_HUAWEI_PORT_MODE_SHELL)
+
+GArray *mm_huawei_parse_getportmode_response (const gchar *response,
+ gpointer log_object,
+ GError **error);
+
#endif /* MM_MODEM_HELPERS_HUAWEI_H */
diff --git a/plugins/huawei/mm-plugin-huawei.c b/plugins/huawei/mm-plugin-huawei.c
index 30b18474..31ee6274 100644
--- a/plugins/huawei/mm-plugin-huawei.c
+++ b/plugins/huawei/mm-plugin-huawei.c
@@ -23,10 +23,13 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include <ModemManager-tags.h>
#include "mm-port-enums-types.h"
#include "mm-log.h"
#include "mm-plugin-huawei.h"
#include "mm-broadband-modem-huawei.h"
+#include "mm-modem-helpers-huawei.h"
+#include "mm-huawei-enums-types.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
@@ -38,8 +41,8 @@
G_DEFINE_TYPE (MMPluginHuawei, mm_plugin_huawei, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
@@ -52,9 +55,10 @@ int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
#define MAX_WAIT_TIME 5
typedef struct {
- guint first_usbif;
- guint timeout_id;
- gboolean custom_init_run;
+ MMPortProbe *probe;
+ gint first_usbif;
+ guint timeout_id;
+ gboolean custom_init_run;
} FirstInterfaceContext;
static void
@@ -62,37 +66,25 @@ first_interface_context_free (FirstInterfaceContext *ctx)
{
if (ctx->timeout_id)
g_source_remove (ctx->timeout_id);
+ g_object_unref (ctx->probe);
g_slice_free (FirstInterfaceContext, ctx);
}
-#define TAG_HUAWEI_PCUI_PORT "huawei-pcui-port"
-#define TAG_HUAWEI_MODEM_PORT "huawei-modem-port"
-#define TAG_HUAWEI_NDIS_PORT "huawei-ndis-port"
-#define TAG_HUAWEI_DIAG_PORT "huawei-diag-port"
-#define TAG_GETPORTMODE_SUPPORTED "getportmode-supported"
-#define TAG_AT_PORT_FLAGS "at-port-flags"
+#define TAG_GETPORTMODE_RESULT "getportmode-result"
+#define TAG_AT_PORT_FLAGS "at-port-flags"
typedef struct {
- MMPortProbe *probe;
MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- gboolean curc_done;
- guint curc_retries;
- gboolean getportmode_done;
- guint getportmode_retries;
+ gboolean curc_done;
+ guint curc_retries;
+ gboolean getportmode_done;
+ guint getportmode_retries;
} HuaweiCustomInitContext;
static void
-huawei_custom_init_context_complete_and_free (HuaweiCustomInitContext *ctx)
+huawei_custom_init_context_free (HuaweiCustomInitContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
g_slice_free (HuaweiCustomInitContext, ctx);
}
@@ -101,88 +93,67 @@ huawei_custom_init_finish (MMPortProbe *probe,
GAsyncResult *result,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
-static void huawei_custom_init_step (HuaweiCustomInitContext *ctx);
-
-static void
-cache_port_mode (MMDevice *device,
- const gchar *reply,
- const gchar *type,
- const gchar *tag)
-{
- gchar *p;
- glong i;
-
- /* Get the USB interface number of the PCUI port */
- p = strstr (reply, type);
- if (p) {
- errno = 0;
- /* shift by 1 so NULL return from g_object_get_data() means no tag */
- i = 1 + strtol (p + strlen (type), NULL, 10);
- if (i > 0 && i < 256 && errno == 0)
- g_object_set_data (G_OBJECT (device), tag, GINT_TO_POINTER ((gint) i));
- }
-}
+static void huawei_custom_init_step (GTask *task);
static void
getportmode_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- HuaweiCustomInitContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- const gchar *response;
- GError *error = NULL;
+ MMDevice *device;
+ MMPortProbe *probe;
+ HuaweiCustomInitContext *ctx;
+ const gchar *response;
+ GArray *modes;
+ g_autoptr(GError) error = NULL;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ device = mm_port_probe_peek_device (probe);
response = mm_port_serial_at_command_finish (port, res, &error);
if (error) {
- mm_dbg ("(Huawei) couldn't get port mode: '%s'",
- error->message);
+ mm_obj_dbg (probe, "couldn't get port mode: '%s'", error->message);
/* If any error occurred that was not ERROR or COMMAND NOT SUPPORT then
* retry the command.
*/
- if (!g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
- goto out;
-
- /* Port mode not supported */
- } else {
- MMDevice *device;
-
- mm_dbg ("(Huawei) port mode layout retrieved");
-
- /* Results are cached in the parent device object */
- device = mm_port_probe_peek_device (ctx->probe);
- cache_port_mode (device, response, "PCUI:", TAG_HUAWEI_PCUI_PORT);
- cache_port_mode (device, response, "MDM:", TAG_HUAWEI_MODEM_PORT);
- cache_port_mode (device, response, "NDIS:", TAG_HUAWEI_NDIS_PORT);
- cache_port_mode (device, response, "DIAG:", TAG_HUAWEI_DIAG_PORT);
- g_object_set_data (G_OBJECT (device), TAG_GETPORTMODE_SUPPORTED, GUINT_TO_POINTER (TRUE));
-
- /* Mark port as being AT already */
- mm_port_probe_set_result_at (ctx->probe, TRUE);
+ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
+ ctx->getportmode_done = TRUE;
+ huawei_custom_init_step (task);
+ return;
}
- ctx->getportmode_done = TRUE;
+ /* Mark port as being AT already */
+ mm_port_probe_set_result_at (probe, TRUE);
-out:
- if (error)
- g_error_free (error);
+ /* Flag as GETPORTMODE already done */
+ ctx->getportmode_done = TRUE;
- huawei_custom_init_step (ctx);
+ modes = mm_huawei_parse_getportmode_response (response, probe, &error);
+ if (!modes)
+ mm_obj_warn (probe, "failed to parse ^GETPORTMODE response: %s", error->message);
+ else
+ g_object_set_data_full (G_OBJECT (device), TAG_GETPORTMODE_RESULT, modes, (GDestroyNotify) g_array_unref);
+ huawei_custom_init_step (task);
}
static void
curc_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- HuaweiCustomInitContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- const gchar *response;
- GError *error = NULL;
+ MMPortProbe *probe;
+ HuaweiCustomInitContext *ctx;
+ g_autoptr(GError) error = NULL;
- response = mm_port_serial_at_command_finish (port, res, &error);
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_port_serial_at_command_finish (port, res, &error);
if (error) {
/* Retry if we get a timeout error */
if (g_error_matches (error,
@@ -190,27 +161,24 @@ curc_ready (MMPortSerialAt *port,
MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
goto out;
- mm_dbg ("(Huawei) couldn't turn off unsolicited messages in secondary ports: '%s'",
- error->message);
+ mm_obj_dbg (probe, "couldn't turn off unsolicited messages in secondary ports: %s", error->message);
}
- mm_dbg ("(Huawei) unsolicited messages in secondary ports turned off");
+ mm_obj_dbg (probe, "unsolicited messages in secondary ports turned off");
ctx->curc_done = TRUE;
out:
- if (error)
- g_error_free (error);
-
- huawei_custom_init_step (ctx);
+ huawei_custom_init_step (task);
}
static void
-try_next_usbif (MMDevice *device)
+try_next_usbif (MMPortProbe *probe,
+ MMDevice *device)
{
FirstInterfaceContext *fi_ctx;
- GList *l;
- gint closest;
+ GList *l;
+ gint closest;
fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT);
g_assert (fi_ctx != NULL);
@@ -219,13 +187,13 @@ try_next_usbif (MMDevice *device)
* and enable that one as being first */
closest = G_MAXINT;
for (l = mm_device_peek_port_probe_list (device); l; l = g_list_next (l)) {
- MMPortProbe *probe = MM_PORT_PROBE (l->data);
+ MMPortProbe *iter = MM_PORT_PROBE (l->data);
/* Only expect ttys for next probing attempt */
- if (g_str_equal (mm_port_probe_get_port_subsys (probe), "tty")) {
+ if (g_str_equal (mm_port_probe_get_port_subsys (iter), "tty")) {
gint usbif;
- usbif = g_udev_device_get_property_as_int (mm_port_probe_peek_port (probe), "ID_USB_INTERFACE_NUM");
+ usbif = mm_kernel_device_get_interface_number (mm_port_probe_peek_port (iter));
if (usbif == fi_ctx->first_usbif) {
/* This is the one we just probed, which wasn't yet removed, so just skip it */
} else if (usbif > fi_ctx->first_usbif &&
@@ -238,37 +206,39 @@ try_next_usbif (MMDevice *device)
if (closest == G_MAXINT) {
/* No more ttys to try! Just return something */
closest = 0;
- mm_dbg ("(Huawei) No more ports to run initial probing");
- } else {
- mm_dbg ("(Huawei) Will try initial probing with interface '%d' instead", closest);
- }
+ mm_obj_dbg (probe, "no more ports to run initial probing");
+ } else
+ mm_obj_dbg (probe, "will try initial probing with interface '%d' instead", closest);
fi_ctx->first_usbif = closest;
}
static void
-huawei_custom_init_step (HuaweiCustomInitContext *ctx)
+huawei_custom_init_step (GTask *task)
{
- FirstInterfaceContext *fi_ctx;
- GUdevDevice *port;
+ MMPortProbe *probe;
+ HuaweiCustomInitContext *ctx;
+ FirstInterfaceContext *fi_ctx;
+ MMKernelDevice *port;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
/* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(Huawei) no need to keep on running custom init in (%s)",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- huawei_custom_init_context_complete_and_free (ctx);
+ if (g_task_return_error_if_cancelled (task)) {
+ mm_obj_dbg (probe, "no need to keep on running custom init");
+ g_object_unref (task);
return;
}
if (!ctx->curc_done) {
if (ctx->curc_retries == 0) {
/* All retries consumed, probably not an AT port */
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ mm_port_probe_set_result_at (probe, FALSE);
/* Try with next */
- try_next_usbif (mm_port_probe_peek_device (ctx->probe));
- huawei_custom_init_context_complete_and_free (ctx);
+ try_next_usbif (probe, mm_port_probe_peek_device (probe));
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -280,18 +250,18 @@ huawei_custom_init_step (HuaweiCustomInitContext *ctx)
3,
FALSE, /* raw */
FALSE, /* allow_cached */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)curc_ready,
- ctx);
+ task);
return;
}
/* Try to get a port map from the modem */
- port = mm_port_probe_peek_port (ctx->probe);
- if (!ctx->getportmode_done && !g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_DISABLE_GETPORTMODE")) {
+ port = mm_port_probe_peek_port (probe);
+ if (!ctx->getportmode_done && !mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_HUAWEI_DISABLE_GETPORTMODE")) {
if (ctx->getportmode_retries == 0) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- huawei_custom_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -302,29 +272,33 @@ huawei_custom_init_step (HuaweiCustomInitContext *ctx)
3,
FALSE, /* raw */
FALSE, /* allow_cached */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)getportmode_ready,
- ctx);
+ task);
return;
}
/* All done it seems */
- fi_ctx = g_object_get_data (G_OBJECT (mm_port_probe_peek_device (ctx->probe)), TAG_FIRST_INTERFACE_CONTEXT);
+ fi_ctx = g_object_get_data (G_OBJECT (mm_port_probe_peek_device (probe)), TAG_FIRST_INTERFACE_CONTEXT);
g_assert (fi_ctx != NULL);
fi_ctx->custom_init_run = TRUE;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- huawei_custom_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static gboolean
first_interface_missing_timeout_cb (MMDevice *device)
{
- try_next_usbif (device);
+ FirstInterfaceContext *fi_ctx;
+
+ fi_ctx = g_object_get_data (G_OBJECT (device), TAG_FIRST_INTERFACE_CONTEXT);
+ g_assert (fi_ctx != NULL);
+ try_next_usbif (fi_ctx->probe, device);
/* Reload the timeout, just in case we end up not having the next interface to probe...
* which is anyway very unlikely as we got it by looking at the real probe list, but anyway... */
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static void
@@ -337,6 +311,7 @@ huawei_custom_init (MMPortProbe *probe,
MMDevice *device;
FirstInterfaceContext *fi_ctx;
HuaweiCustomInitContext *ctx;
+ GTask *task;
device = mm_port_probe_peek_device (probe);
@@ -353,6 +328,7 @@ huawei_custom_init (MMPortProbe *probe,
if (!fi_ctx) {
/* This is the first time we ask for the context. Set it up. */
fi_ctx = g_slice_new0 (FirstInterfaceContext);
+ fi_ctx->probe = g_object_ref (probe);
g_object_set_data_full (G_OBJECT (device),
TAG_FIRST_INTERFACE_CONTEXT,
fi_ctx,
@@ -373,33 +349,28 @@ huawei_custom_init (MMPortProbe *probe,
}
ctx = g_slice_new (HuaweiCustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- huawei_custom_init);
- ctx->probe = g_object_ref (probe);
ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
ctx->curc_done = FALSE;
ctx->curc_retries = 3;
ctx->getportmode_done = FALSE;
ctx->getportmode_retries = 3;
- /* Custom init only to be run in the first interface */
- if (g_udev_device_get_property_as_int (mm_port_probe_peek_port (probe),
- "ID_USB_INTERFACE_NUM") != fi_ctx->first_usbif) {
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)huawei_custom_init_context_free);
+ /* Custom init only to be run in the first interface */
+ if (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (probe)) != fi_ctx->first_usbif) {
if (fi_ctx->custom_init_run)
/* If custom init was run already, we can consider this as successfully run */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
else
/* Otherwise, we'll need to defer the probing a bit more */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_RETRY,
- "Defer needed");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Defer needed");
- huawei_custom_init_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
@@ -409,87 +380,259 @@ huawei_custom_init (MMPortProbe *probe,
fi_ctx->timeout_id = 0;
}
- huawei_custom_init_step (ctx);
+ huawei_custom_init_step (task);
}
/*****************************************************************************/
-static void
-propagate_port_mode_results (GList *probes)
+static gint
+probe_cmp_by_usbif (MMPortProbe *a,
+ MMPortProbe *b)
+{
+ return (mm_kernel_device_get_interface_number (mm_port_probe_peek_port (a)) -
+ mm_kernel_device_get_interface_number (mm_port_probe_peek_port (b)));
+}
+
+static guint
+propagate_getportmode_hints (MMPlugin *self,
+ GList *probes,
+ gboolean *primary_flagged)
{
MMDevice *device;
- GList *l;
- gboolean primary_flagged = FALSE;
+ GArray *modes;
+ GList *l;
+ GList *tty_probes = NULL;
+ guint n_ports_with_hints = 0;
+ guint mode_i = 0;
g_assert (probes != NULL);
device = mm_port_probe_peek_device (MM_PORT_PROBE (probes->data));
+ modes = g_object_get_data (G_OBJECT (device), TAG_GETPORTMODE_RESULT);
+
+ /* Nothing to do if GETPORTMODE is flagged as not supported */
+ if (!modes)
+ return 0;
- /* Now we propagate the tags to the specific port probes */
+ /* Build a list of TTY port probes (AT and not-AT) sorted by interface number */
for (l = probes; l; l = g_list_next (l)) {
- MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
- gint usbif;
-
- usbif = g_udev_device_get_property_as_int (mm_port_probe_peek_port (MM_PORT_PROBE (l->data)), "ID_USB_INTERFACE_NUM");
-
- if (GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_GETPORTMODE_SUPPORTED))) {
- if (usbif + 1 == GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), TAG_HUAWEI_PCUI_PORT))) {
- at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- primary_flagged = TRUE;
- } else if (usbif + 1 == GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), TAG_HUAWEI_MODEM_PORT)))
- at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
- else if (!g_object_get_data (G_OBJECT (device), TAG_HUAWEI_MODEM_PORT) &&
- usbif + 1 == GPOINTER_TO_INT (g_object_get_data (G_OBJECT (device), TAG_HUAWEI_NDIS_PORT)))
- /* If NDIS reported only instead of MDM, use it */
- at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
- } else if (usbif == 0 &&
- mm_port_probe_is_at (MM_PORT_PROBE (l->data))) {
- /* If GETPORTMODE is not supported, we assume usbif 0 is the modem port */
+ MMPortProbe *probe;
+
+ probe = MM_PORT_PROBE (l->data);
+ if (g_str_equal (mm_port_probe_get_port_subsys (probe), "tty"))
+ tty_probes = g_list_insert_sorted (tty_probes, probe, (GCompareFunc) probe_cmp_by_usbif);
+ }
+
+ /* Propagate the getportmode tags to the specific port probes */
+ for (l = tty_probes, mode_i = 0; l; l = g_list_next (l)) {
+ MMPortProbe *probe;
+ MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ MMHuaweiPortMode port_mode;
+
+ probe = MM_PORT_PROBE (l->data);
+
+ /* Look for the next serial port mode applicable */
+ while (!MM_HUAWEI_PORT_MODE_IS_SERIAL (g_array_index (modes, MMHuaweiPortMode, mode_i)) && (mode_i < modes->len))
+ mode_i++;
+ if (mode_i == modes->len) {
+ mm_obj_dbg (probe, "missing port mode hint");
+ continue;
+ }
+
+ port_mode = g_array_index (modes, MMHuaweiPortMode, mode_i);
+ if (!mm_port_probe_is_at (probe)) {
+ mm_obj_dbg (probe, "port mode hint for non-AT port: %s", mm_huawei_port_mode_get_string (port_mode));
+ mode_i++;
+ continue;
+ }
+
+ mm_obj_dbg (probe, "port mode hint for AT port: %s", mm_huawei_port_mode_get_string (port_mode));
+ if (port_mode == MM_HUAWEI_PORT_MODE_PCUI)
+ at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ else if (port_mode == MM_HUAWEI_PORT_MODE_MODEM)
at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
- /* /\* TODO. */
- /* * For CDMA modems we assume usbif0 is both primary and PPP, since */
- /* * they don't have problems with talking on secondary ports. */
- /* *\/ */
- /* if (caps & CAP_CDMA) */
- /* pflags |= MM_PORT_SERIAL_AT_FLAG_PRIMARY; */
+ if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) {
+ n_ports_with_hints++;
+ g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
}
+ mode_i++;
+ }
+
+ g_list_free (tty_probes);
+
+ return n_ports_with_hints;
+}
- g_object_set_data (G_OBJECT (l->data), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
+static guint
+propagate_description_hints (MMPlugin *self,
+ GList *probes,
+ gboolean *primary_flagged)
+{
+ GList *l;
+ guint n_ports_with_hints = 0;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ MMPortProbe *probe;
+ MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ const gchar *description;
+ g_autofree gchar *lower_description = NULL;
+
+ probe = MM_PORT_PROBE (l->data);
+
+ if (!mm_port_probe_is_at (probe))
+ continue;
+
+ description = mm_kernel_device_get_interface_description (mm_port_probe_peek_port (probe));
+ if (!description)
+ continue;
+
+ mm_obj_dbg (probe, "%s interface description: %s", mm_port_probe_get_port_name (probe), description);
+
+ lower_description = g_ascii_strdown (description, -1);
+ if (strstr (lower_description, "modem"))
+ at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
+ else if (strstr (lower_description, "pcui")) {
+ at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ *primary_flagged = TRUE;
+ }
+
+ if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) {
+ n_ports_with_hints++;
+ g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
+ }
}
- if (primary_flagged)
- return;
+ return n_ports_with_hints;
+}
+
+static guint
+propagate_generic_hints (MMPlugin *self,
+ GList *probes,
+ gboolean *primary_flagged)
+{
+ GList *l;
+ guint n_ports_with_hints = 0;
- /* For devices exposing a cdc-wdm port, make sure it gets flagged as primary, if there is none
- * already */
for (l = probes; l; l = g_list_next (l)) {
- MMPortProbe *probe = MM_PORT_PROBE (l->data);
+ MMPortProbe *probe;
+ MMKernelDevice *kernel_device;
+ MMPortSerialAtFlag at_port_flags = MM_PORT_SERIAL_AT_FLAG_NONE;
+
+ probe = MM_PORT_PROBE (l->data);
+
+ if (!mm_port_probe_is_at (probe))
+ continue;
+
+ kernel_device = mm_port_probe_peek_port (probe);
+ if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PRIMARY)) {
+ at_port_flags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ *primary_flagged = TRUE;
+ }
+ else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_SECONDARY))
+ at_port_flags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PPP))
+ at_port_flags = MM_PORT_SERIAL_AT_FLAG_PPP;
- if (mm_port_probe_is_at (probe) &&
- g_str_has_prefix (mm_port_probe_get_port_subsys (probe), "usb") &&
- g_str_has_prefix (mm_port_probe_get_port_name (probe), "cdc-wdm")) {
- /* Flag as PRIMARY and do nothing else */
+ if (at_port_flags != MM_PORT_SERIAL_AT_FLAG_NONE) {
+ n_ports_with_hints++;
+ g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (at_port_flags));
+ }
+ }
+
+ return n_ports_with_hints;
+}
+
+static guint
+fallback_primary_cdcwdm (MMPlugin *self,
+ GList *probes)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ MMPortProbe *probe;
+
+ probe = MM_PORT_PROBE (l->data);
+
+ if (!mm_port_probe_is_at (probe))
+ continue;
+
+ if (g_str_equal (mm_port_probe_get_port_subsys (probe), "usbmisc")) {
+ mm_obj_dbg (self, "fallback port type hint applied to first cdc-wmd port found");
g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (MM_PORT_SERIAL_AT_FLAG_PRIMARY));
- break;
+ return 1;
}
}
+ return 0;
+}
+
+static guint
+fallback_usbif0 (MMPlugin *self,
+ GList *probes)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ MMPortProbe *probe;
+ guint usbif;
+
+ probe = MM_PORT_PROBE (l->data);
+
+ if (!mm_port_probe_is_at (probe))
+ continue;
+
+ usbif = mm_kernel_device_get_property_as_int_hex (mm_port_probe_peek_port (probe), "ID_USB_INTERFACE_NUM");
+ if (usbif == 0) {
+ mm_obj_dbg (self, "fallback port type hint applied to interface 0");
+ g_object_set_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS, GUINT_TO_POINTER (MM_PORT_SERIAL_AT_FLAG_PPP));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+propagate_port_type_hints (MMPlugin *self,
+ GList *probes)
+{
+ gboolean primary_flagged = FALSE;
+ guint n_ports_with_hints;
+
+ g_assert (probes != NULL);
+
+ if ((n_ports_with_hints = propagate_getportmode_hints (self, probes, &primary_flagged)) > 0)
+ mm_obj_dbg (self, "port type hints set by GETPORTMODE");
+ else if ((n_ports_with_hints = propagate_description_hints (self, probes, &primary_flagged)) > 0)
+ mm_obj_dbg (self, "port type hints set by interface descriptions");
+ else if ((n_ports_with_hints = propagate_generic_hints (self, probes, &primary_flagged)) > 0)
+ mm_obj_dbg (self, "port type hints set by generic udev tags");
+
+ /* Fallback hint for the first cdc-wdm port if no other port has been flagged as primary */
+ if (!primary_flagged)
+ n_ports_with_hints += fallback_primary_cdcwdm (self, probes);
+
+ /* If not a single port type hint available (not plugin-provided and not generic)
+ * then we'll assume usbif 0 is the modem port */
+ if (!n_ports_with_hints)
+ n_ports_with_hints = fallback_usbif0 (self, probes);
+
+ mm_obj_dbg (self, "%u port hints have been set", n_ports_with_hints);
}
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- propagate_port_mode_results (probes);
+ propagate_port_type_hints (self, probes);
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Huawei modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered Huawei modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -499,8 +642,8 @@ create_modem (MMPlugin *self,
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
- mm_dbg ("MBIM-powered Huawei modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path,
+ mm_obj_dbg (self, "MBIM-powered Huawei modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -508,7 +651,7 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_huawei_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_huawei_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -521,44 +664,31 @@ grab_port (MMPlugin *self,
MMPortProbe *probe,
GError **error)
{
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
- GUdevDevice *port;
+ MMPortSerialAtFlag pflags;
+ MMKernelDevice *port;
MMPortType port_type;
port_type = mm_port_probe_get_port_type (probe);
port = mm_port_probe_peek_port (probe);
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_AT_PORT")) {
- mm_dbg ("(%s/%s)' Port flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_MODEM_PORT")) {
- mm_dbg ("(%s/%s) Port flagged as PPP",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_HUAWEI_GPS_PORT")) {
- mm_dbg ("(%s/%s) Port flagged as GPS",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- port_type = MM_PORT_TYPE_GPS;
- } else {
+ pflags = (MMPortSerialAtFlag) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS));
+ if (pflags != MM_PORT_SERIAL_AT_FLAG_NONE) {
gchar *str;
- pflags = (MMPortSerialAtFlag) GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (probe), TAG_AT_PORT_FLAGS));
str = mm_port_serial_at_flag_build_string_from_mask (pflags);
- mm_dbg ("(%s/%s) Port will have AT flags '%s'",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- str);
+ mm_obj_dbg (self, "(%s/%s) port will have AT flags '%s'",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe),
+ str);
g_free (str);
+ } else {
+ /* The huawei plugin handles the generic udev tags itself, so explicitly request
+ * to avoid processing them by the generic modem. */
+ pflags = MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC;
}
return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ port,
port_type,
pflags,
error);
@@ -569,7 +699,7 @@ grab_port (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendor_ids[] = { 0x12d1, 0 };
static const MMAsyncMethod custom_init = {
.async = G_CALLBACK (huawei_custom_init),
@@ -578,7 +708,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_HUAWEI,
- MM_PLUGIN_NAME, "Huawei",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/huawei/mm-sim-huawei.c b/plugins/huawei/mm-sim-huawei.c
index 4bb8f082..f937c773 100644
--- a/plugins/huawei/mm-sim-huawei.c
+++ b/plugins/huawei/mm-sim-huawei.c
@@ -25,7 +25,6 @@
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
@@ -41,38 +40,30 @@ load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- gchar *iccid;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- iccid = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("loaded SIM identifier: %s", iccid);
- return g_strdup (iccid);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_sim_identifier_ready (MMSimHuawei *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
gchar *simid;
simid = MM_BASE_SIM_CLASS (mm_sim_huawei_parent_class)->load_sim_identifier_finish (MM_BASE_SIM (self), res, &error);
if (simid)
- g_simple_async_result_set_op_res_gpointer (simple, simid, g_free);
+ g_task_return_pointer (task, simid, g_free);
else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
iccid_read_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseSim *self;
const gchar *response;
@@ -89,19 +80,17 @@ iccid_read_ready (MMBaseModem *modem,
parsed = mm_3gpp_parse_iccid (p, NULL);
if (parsed) {
- g_simple_async_result_set_op_res_gpointer (simple, parsed, g_free);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, parsed, g_free);
+ g_object_unref (task);
return;
}
error:
/* Chain up to parent method; older devices don't support ^ICCID */
- self = MM_BASE_SIM (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+ self = g_task_get_source_object (task);
MM_BASE_SIM_CLASS (mm_sim_huawei_parent_class)->load_sim_identifier (self,
(GAsyncReadyCallback) parent_load_sim_identifier_ready,
- simple);
- g_object_unref (self);
+ task);
}
static void
@@ -115,17 +104,13 @@ load_sim_identifier (MMBaseSim *self,
MM_BASE_SIM_MODEM, &modem,
NULL);
- mm_dbg ("loading (Huawei) SIM identifier...");
mm_base_modem_at_command (
modem,
"^ICCID?",
5,
FALSE,
(GAsyncReadyCallback)iccid_read_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_sim_identifier));
+ g_task_new (self, NULL, callback, user_data));
g_object_unref (modem);
}
@@ -163,6 +148,7 @@ mm_sim_huawei_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
diff --git a/plugins/huawei/tests/test-modem-helpers-huawei.c b/plugins/huawei/tests/test-modem-helpers-huawei.c
index 9e92eee2..e6f2490b 100644
--- a/plugins/huawei/tests/test-modem-helpers-huawei.c
+++ b/plugins/huawei/tests/test-modem-helpers-huawei.c
@@ -16,12 +16,14 @@
#include <glib.h>
#include <glib-object.h>
#include <locale.h>
+#include <arpa/inet.h>
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-test.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-huawei.h"
@@ -101,6 +103,10 @@ static const NdisstatqryTest ndisstatqry_tests[] = {
{ "^NDISSTATQRY: 1,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, TRUE, TRUE, FALSE },
{ "^NDISSTATQRY: 0,,,\"IPV4\",1,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, TRUE },
{ "^NDISSTATQRY: 0,,,\"IPV4\",0,,,\"IPV6\"\r\n", TRUE, FALSE, TRUE, FALSE },
+ { "^NDISSTATQry:1", TRUE, TRUE, FALSE, FALSE },
+ { "^NDISSTATQry:1\r\n", TRUE, TRUE, FALSE, FALSE },
+ { "^NDISSTATQry:0", TRUE, FALSE, FALSE, FALSE },
+ { "^NDISSTATQry:0\r\n", TRUE, FALSE, FALSE, FALSE },
{ NULL, FALSE, FALSE, FALSE, FALSE }
};
@@ -135,6 +141,61 @@ test_ndisstatqry (void)
}
/*****************************************************************************/
+/* Test ^DHCP responses */
+
+typedef struct {
+ const gchar *str;
+ const gchar *expected_addr;
+ guint expected_prefix;
+ const gchar *expected_gateway;
+ const gchar *expected_dns1;
+ const gchar *expected_dns2;
+} DhcpTest;
+
+static const DhcpTest dhcp_tests[] = {
+ { "^DHCP:a3ec5c64,f8ffffff,a1ec5c64,a1ec5c64,2200b10a,74bba80a,236800,236800\r\n",
+ "100.92.236.163", 29, "100.92.236.161", "10.177.0.34", "10.168.187.116" },
+ { "^DHCP:0xa3ec5c64,0xf8ffffff,0xa1ec5c64,0xa1ec5c64,0x2200b10a,0x74bba80a,236800,236800\r\n",
+ "100.92.236.163", 29, "100.92.236.161", "10.177.0.34", "10.168.187.116" },
+ { "^DHCP: 1010A0A,FCFFFFFF,2010A0A,2010A0A,0,0,150000000,150000000\r\n",
+ "10.10.1.1", 30, "10.10.1.2", "0.0.0.0", "0.0.0.0" },
+ { "^DHCP: CCDB080A,F8FFFFFF,C9DB080A,C9DB080A,E67B59C0,E77B59C0,85600,85600\r\n",
+ "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" },
+ { "^DHCP: 0xCCDB080A,0xF8FFFFFF,0xC9DB080A,0xC9DB080A,0xE67B59C0,0xE77B59C0,85600,85600\r\n",
+ "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" },
+ { "^DHCP: 0XCCDB080A,0XF8FFFFFF,0XC9DB080A,0XC9DB080A,0XE67B59C0,0XE77B59C0,85600,85600\r\n",
+ "10.8.219.204", 29, "10.8.219.201", "192.89.123.230", "192.89.123.231" },
+ { NULL }
+};
+
+static void
+test_dhcp (void)
+{
+ guint i;
+
+ for (i = 0; dhcp_tests[i].str; i++) {
+ GError *error = NULL;
+ guint addr, prefix, gateway, dns1, dns2;
+
+ g_assert (mm_huawei_parse_dhcp_response (
+ dhcp_tests[i].str,
+ &addr,
+ &prefix,
+ &gateway,
+ &dns1,
+ &dns2,
+ &error) == TRUE);
+ g_assert_no_error (error);
+
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &addr)), ==, dhcp_tests[i].expected_addr);
+ g_assert_cmpint (prefix, ==, dhcp_tests[i].expected_prefix);
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &gateway)), ==, dhcp_tests[i].expected_gateway);
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &dns1)), ==, dhcp_tests[i].expected_dns1);
+ g_assert_cmpstr (inet_ntoa (*((struct in_addr *) &dns2)), ==, dhcp_tests[i].expected_dns2);
+ }
+}
+
+/*****************************************************************************/
/* Test ^SYSINFO responses */
typedef struct {
@@ -326,30 +387,22 @@ test_prefmode (void)
n_expected_combinations++;
}
- combinations = mm_huawei_parse_prefmode_test (prefmode_tests[i].str, &error);
+ combinations = mm_huawei_parse_prefmode_test (prefmode_tests[i].str, NULL, &error);
g_assert_no_error (error);
g_assert (combinations != NULL);
g_assert_cmpuint (combinations->len, ==, n_expected_combinations);
-#if defined ENABLE_TEST_MESSAGE_TRACES
for (j = 0; j < combinations->len; j++) {
MMHuaweiPrefmodeCombination *single;
- gchar *allowed_str;
- gchar *preferred_str;
+ g_autofree gchar *allowed_str = NULL;
+ g_autofree gchar *preferred_str = NULL;
single = &g_array_index (combinations, MMHuaweiPrefmodeCombination, j);
allowed_str = mm_modem_mode_build_string_from_mask (single->allowed);
preferred_str = mm_modem_mode_build_string_from_mask (single->preferred);
- mm_dbg ("Test[%u], Combination[%u]: %u, \"%s\", \"%s\"",
- i,
- j,
- single->prefmode,
- allowed_str,
- preferred_str);
- g_free (allowed_str);
- g_free (preferred_str);
+ mm_obj_dbg (NULL, "test[%u], combination[%u]: %u, \"%s\", \"%s\"",
+ i, j, single->prefmode, allowed_str, preferred_str);
}
-#endif
for (j = 0; j < combinations->len; j++) {
MMHuaweiPrefmodeCombination *single;
@@ -414,12 +467,13 @@ test_prefmode_response (void)
const MMHuaweiPrefmodeCombination *found;
GError *error = NULL;
- combinations = mm_huawei_parse_prefmode_test (prefmode_response_tests[i].format, NULL);
+ combinations = mm_huawei_parse_prefmode_test (prefmode_response_tests[i].format, NULL, NULL);
g_assert (combinations != NULL);
found = mm_huawei_parse_prefmode_response (prefmode_response_tests[i].str,
combinations,
&error);
+ g_assert_no_error (error);
g_assert (found != NULL);
g_assert_cmpuint (found->allowed, ==, prefmode_response_tests[i].allowed);
g_assert_cmpuint (found->preferred, ==, prefmode_response_tests[i].preferred);
@@ -563,31 +617,22 @@ test_syscfg (void)
n_expected_combinations++;
}
- combinations = mm_huawei_parse_syscfg_test (syscfg_tests[i].str, &error);
+ combinations = mm_huawei_parse_syscfg_test (syscfg_tests[i].str, NULL, &error);
g_assert_no_error (error);
g_assert (combinations != NULL);
g_assert_cmpuint (combinations->len, ==, n_expected_combinations);
-#if defined ENABLE_TEST_MESSAGE_TRACES
for (j = 0; j < combinations->len; j++) {
MMHuaweiSyscfgCombination *single;
- gchar *allowed_str;
- gchar *preferred_str;
+ g_autofree gchar *allowed_str = NULL;
+ g_autofree gchar *preferred_str = NULL;
single = &g_array_index (combinations, MMHuaweiSyscfgCombination, j);
allowed_str = mm_modem_mode_build_string_from_mask (single->allowed);
preferred_str = mm_modem_mode_build_string_from_mask (single->preferred);
- mm_dbg ("Test[%u], Combination[%u]: %u, %u, \"%s\", \"%s\"",
- i,
- j,
- single->mode,
- single->acqorder,
- allowed_str,
- preferred_str);
- g_free (allowed_str);
- g_free (preferred_str);
+ mm_obj_dbg (NULL, "test[%u], combination[%u]: %u, %u, \"%s\", \"%s\"",
+ i, j, single->mode, single->acqorder, allowed_str, preferred_str);
}
-#endif
for (j = 0; j < combinations->len; j++) {
MMHuaweiSyscfgCombination *single;
@@ -652,6 +697,20 @@ static const SyscfgResponseTest syscfg_response_tests[] = {
.format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n",
.allowed = MM_MODEM_MODE_3G,
.preferred = MM_MODEM_MODE_NONE
+ },
+ {
+ /* Non-sensical acquisition order (WCDMA-only but acquire WCDMA-then-GSM */
+ .str = "^SYSCFG: 14,2,400000,0,3\r\n",
+ .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n",
+ .allowed = MM_MODEM_MODE_3G,
+ .preferred = MM_MODEM_MODE_NONE
+ },
+ {
+ /* Non-sensical acquisition order (GSM-only but acquire GSM-then-WCDMA */
+ .str = "^SYSCFG: 13,1,400000,0,3\r\n",
+ .format = "^SYSCFG:(2,13,14,16),(0-3),((400000,\"WCDMA2100\")),(0-2),(0-4)\r\n",
+ .allowed = MM_MODEM_MODE_2G,
+ .preferred = MM_MODEM_MODE_NONE
}
};
@@ -665,13 +724,14 @@ test_syscfg_response (void)
const MMHuaweiSyscfgCombination *found;
GError *error = NULL;
- combinations = mm_huawei_parse_syscfg_test (syscfg_response_tests[i].format, NULL);
+ combinations = mm_huawei_parse_syscfg_test (syscfg_response_tests[i].format, NULL, NULL);
g_assert (combinations != NULL);
found = mm_huawei_parse_syscfg_response (syscfg_response_tests[i].str,
combinations,
&error);
+ g_assert_no_error (error);
g_assert (found != NULL);
g_assert_cmpuint (found->allowed, ==, syscfg_response_tests[i].allowed);
g_assert_cmpuint (found->preferred, ==, syscfg_response_tests[i].preferred);
@@ -700,22 +760,22 @@ static const SyscfgexTest syscfgex_tests[] = {
"\r\n",
{
{
- .mode_str = "00",
+ .mode_str = (gchar *) "00",
.allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G | MM_MODEM_MODE_2G),
.preferred = MM_MODEM_MODE_NONE
},
{
- .mode_str = "03",
+ .mode_str = (gchar *) "03",
.allowed = MM_MODEM_MODE_4G,
.preferred = MM_MODEM_MODE_NONE
},
{
- .mode_str = "02",
+ .mode_str = (gchar *) "02",
.allowed = MM_MODEM_MODE_3G,
.preferred = MM_MODEM_MODE_NONE
},
{
- .mode_str = "01",
+ .mode_str = (gchar *) "01",
.allowed = MM_MODEM_MODE_2G,
.preferred = MM_MODEM_MODE_NONE
},
@@ -731,17 +791,17 @@ static const SyscfgexTest syscfgex_tests[] = {
"\r\n",
{
{
- .mode_str = "030201",
+ .mode_str = (gchar *) "030201",
.allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G | MM_MODEM_MODE_2G),
.preferred = MM_MODEM_MODE_4G
},
{
- .mode_str = "0302",
+ .mode_str = (gchar *) "0302",
.allowed = (MM_MODEM_MODE_4G | MM_MODEM_MODE_3G),
.preferred = MM_MODEM_MODE_4G
},
{
- .mode_str = "03",
+ .mode_str = (gchar *) "03",
.allowed = MM_MODEM_MODE_4G,
.preferred = MM_MODEM_MODE_NONE
},
@@ -757,7 +817,7 @@ static const SyscfgexTest syscfgex_tests[] = {
"\r\n",
{
{
- .mode_str = "03",
+ .mode_str = (gchar *) "03",
.allowed = MM_MODEM_MODE_4G,
.preferred = MM_MODEM_MODE_NONE
},
@@ -773,27 +833,27 @@ static const SyscfgexTest syscfgex_tests[] = {
"\r\n",
{
{
- .mode_str = "00",
+ .mode_str = (gchar *) "00",
.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_2G),
.preferred = MM_MODEM_MODE_NONE
},
{
- .mode_str = "01",
+ .mode_str = (gchar *) "01",
.allowed = MM_MODEM_MODE_2G,
.preferred = MM_MODEM_MODE_NONE
},
{
- .mode_str = "02",
+ .mode_str = (gchar *) "02",
.allowed = MM_MODEM_MODE_3G,
.preferred = MM_MODEM_MODE_NONE
},
{
- .mode_str = "0102",
+ .mode_str = (gchar *) "0102",
.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G),
.preferred = MM_MODEM_MODE_2G
},
{
- .mode_str = "0201",
+ .mode_str = (gchar *) "0201",
.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G),
.preferred = MM_MODEM_MODE_3G
}
@@ -822,25 +882,17 @@ test_syscfgex (void)
g_assert (combinations != NULL);
g_assert_cmpuint (combinations->len, ==, n_expected_combinations);
-#if defined ENABLE_TEST_MESSAGE_TRACES
for (j = 0; j < combinations->len; j++) {
MMHuaweiSyscfgexCombination *single;
- gchar *allowed_str;
- gchar *preferred_str;
+ g_autofree gchar *allowed_str = NULL;
+ g_autofree gchar *preferred_str = NULL;
single = &g_array_index (combinations, MMHuaweiSyscfgexCombination, j);
allowed_str = mm_modem_mode_build_string_from_mask (single->allowed);
preferred_str = mm_modem_mode_build_string_from_mask (single->preferred);
- mm_dbg ("Test[%u], Combination[%u]: \"%s\", \"%s\", \"%s\"",
- i,
- j,
- single->mode_str,
- allowed_str,
- preferred_str);
- g_free (allowed_str);
- g_free (preferred_str);
+ mm_obj_dbg (NULL, "test[%u], combination[%u]: \"%s\", \"%s\", \"%s\"",
+ i, j, single->mode_str, allowed_str, preferred_str);
}
-#endif
for (j = 0; j < combinations->len; j++) {
MMHuaweiSyscfgexCombination *single;
@@ -993,6 +1045,7 @@ test_syscfgex_response (void)
combinations,
&error);
+ g_assert_no_error (error);
g_assert (found != NULL);
g_assert_cmpuint (found->allowed, ==, syscfgex_response_tests[i].allowed);
g_assert_cmpuint (found->preferred, ==, syscfgex_response_tests[i].preferred);
@@ -1009,7 +1062,7 @@ typedef struct {
gboolean ret;
gboolean test_iso8601;
gboolean test_tz;
- gchar *iso8601;
+ const gchar *iso8601;
gint32 offset;
gint32 dst_offset;
gint32 leap_seconds;
@@ -1019,32 +1072,32 @@ typedef struct {
static const NwtimeTest nwtime_tests[] = {
{ "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, TRUE, FALSE,
- "2014-08-05T04:00:21+10:00", 600, 0, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+10", 600, 0, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, FALSE, TRUE,
- "2014-08-05T04:00:21+10:00", 600, 0, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+10", 600, 0, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,00", TRUE, TRUE, TRUE,
- "2014-08-05T04:00:21+10:00", 600, 0, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+10", 600, 0, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, TRUE, FALSE,
- "2014-08-05T04:00:21+05:00", 300, 0, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+05", 300, 0, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, FALSE, TRUE,
- "2014-08-05T04:00:21+05:00", 300, 0, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+05", 300, 0, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+20,00", TRUE, TRUE, TRUE,
- "2014-08-05T04:00:21+05:00", 300, 0, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+05", 300, 0, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, TRUE, FALSE,
- "2014-08-05T04:00:21+11:00", 600, 60, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+11", 600, 60, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, FALSE, TRUE,
- "2014-08-05T04:00:21+11:00", 600, 60, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+11", 600, 60, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,01", TRUE, TRUE, TRUE,
- "2014-08-05T04:00:21+11:00", 600, 60, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+11", 600, 60, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, TRUE, FALSE,
- "2014-08-05T04:00:21+12:00", 600, 120, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+12", 600, 120, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, FALSE, TRUE,
- "2014-08-05T04:00:21+12:00", 600, 120, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+12", 600, 120, NWT_UNKNOWN },
{ "^NWTIME: 14/08/05,04:00:21+40,02", TRUE, TRUE, TRUE,
- "2014-08-05T04:00:21+12:00", 600, 120, NWT_UNKNOWN },
+ "2014-08-05T04:00:21+12", 600, 120, NWT_UNKNOWN },
{ "^TIME: XX/XX/XX,XX:XX:XX+XX,XX", FALSE, TRUE, FALSE,
NULL, NWT_UNKNOWN, NWT_UNKNOWN, NWT_UNKNOWN },
@@ -1085,8 +1138,10 @@ test_nwtime (void)
g_assert (nwtime_tests[i].leap_seconds == mm_network_timezone_get_leap_seconds (tz));
}
- if (iso8601)
- g_free (iso8601);
+ g_free (iso8601);
+
+ if (tz)
+ g_object_unref (tz);
}
}
@@ -1096,12 +1151,12 @@ test_nwtime (void)
typedef struct {
const gchar *str;
gboolean ret;
- gchar *iso8601;
+ const gchar *iso8601;
} TimeTest;
static const TimeTest time_tests[] = {
- { "^TIME: 14/08/05 04:00:21", TRUE, "2014-08-05T04:00:21" },
- { "^TIME: 2014/08/05 04:00:21", TRUE, "2014-08-05T04:00:21" },
+ { "^TIME: 14/08/05 04:00:21", TRUE, "2014-08-05T04:00:21Z" },
+ { "^TIME: 2014/08/05 04:00:21", TRUE, "2014-08-05T04:00:21Z" },
{ "^TIME: 14-08-05 04:00:21", FALSE, NULL },
{ "^TIME: 14-08-05,04:00:21", FALSE, NULL },
{ "^TIME: 14/08/05 04:00:21 AEST", FALSE, NULL },
@@ -1125,44 +1180,231 @@ test_time (void)
g_assert (ret == time_tests[i].ret);
g_assert (ret == (error ? FALSE : TRUE));
+ g_clear_error (&error);
g_assert_cmpstr (time_tests[i].iso8601, ==, iso8601);
+ g_free (iso8601);
+ }
+}
+
+/*****************************************************************************/
+/* Test ^HCSQ responses */
+
+typedef struct {
+ const gchar *str;
+ gboolean ret;
+ MMModemAccessTechnology act;
+ guint value1;
+ guint value2;
+ guint value3;
+ guint value4;
+ guint value5;
+} HcsqTest;
+
+static const HcsqTest hcsq_tests[] = {
+ { "^HCSQ:\"LTE\",30,19,66,0\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 30, 19, 66, 0, 0 },
+ { "^HCSQ: \"WCDMA\",30,30,58\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_UMTS, 30, 30, 58, 0, 0 },
+ { "^HCSQ: \"GSM\",36,255\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 36, 255, 0, 0, 0 },
+ { "^HCSQ: LTE,33,40,135,11\r\n", TRUE, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 33, 40, 135, 11, 0 },
+ { "^HCSQ: \"NOSERVICE\"\r\n", FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, 0, 0, 0, 0 },
+ { "^HCSQ: NOSERVICE\r\n", FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, 0, 0, 0, 0 },
+ { NULL, FALSE, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, 0, 0, 0, 0 }
+};
+
+static void
+test_hcsq (void)
+{
+ guint i;
+
+ for (i = 0; hcsq_tests[i].str; i++) {
+ GError *error = NULL;
+ MMModemAccessTechnology act;
+ guint value1 = 0;
+ guint value2 = 0;
+ guint value3 = 0;
+ guint value4 = 0;
+ guint value5 = 0;
+ gboolean ret;
- if (iso8601)
- g_free (iso8601);
+ ret = mm_huawei_parse_hcsq_response (hcsq_tests[i].str,
+ &act,
+ &value1,
+ &value2,
+ &value3,
+ &value4,
+ &value5,
+ &error);
+ g_assert (ret == hcsq_tests[i].ret);
+ if (ret) {
+ g_assert_no_error (error);
+ g_assert_cmpint (hcsq_tests[i].act, ==, act);
+ g_assert_cmpint (hcsq_tests[i].value1, ==, value1);
+ g_assert_cmpint (hcsq_tests[i].value2, ==, value2);
+ g_assert_cmpint (hcsq_tests[i].value3, ==, value3);
+ g_assert_cmpint (hcsq_tests[i].value4, ==, value4);
+ g_assert_cmpint (hcsq_tests[i].value5, ==, value5);
+ } else
+ g_assert (error);
+ g_clear_error (&error);
}
}
/*****************************************************************************/
+/* Test ^GETPORTMODE response */
+
+typedef struct {
+ const gchar *str;
+ guint n_modes;
+ MMHuaweiPortMode modes[8];
+} GetportmodeTest;
+
+static const GetportmodeTest getportmode_tests[] = {
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,PCUI:0,MDM:1",
+ 2, { MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_MODEM }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,MDM:0,PCUI:1,NDIS:2,CDROM:3,SD:4,",
+ 5, { MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_CDROM,
+ MM_HUAWEI_PORT_MODE_SD }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,MDM:0,PCUI:1,NDIS:2,GPS:3,BT:4,",
+ 5, { MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_GPS,
+ MM_HUAWEI_PORT_MODE_BT
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,PCUI:0,MDM:1,NDIS:2,CDROM:3,SD:4,GPS:5,BT:6",
+ 7, { MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_CDROM,
+ MM_HUAWEI_PORT_MODE_SD,
+ MM_HUAWEI_PORT_MODE_GPS,
+ MM_HUAWEI_PORT_MODE_BT
+ }
+ },
+ {
+ "^getportmode:type:WCDMA:Qualcomm,NDIS:0,DIAG:1,PCUI:2,MDM:3,SD:4",
+ 5, { MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: ,pcui:1,modem:2,ncm:3,mass:4,mass_two:5,",
+ 5, { MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_SD,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei ,, rndis: 0, pcui: 1, c_shell: 2, a_shell: 3,3g_diag: 4, gps: 5, 4g_diag: 6, mass_two: 7",
+ 8, { MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_SHELL,
+ MM_HUAWEI_PORT_MODE_SHELL,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_GPS,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,ecm:1,pcui:2,c_shell:3,a_shell:4,3g_diag:5,gps:6,4g_diag:7,mass:8,",
+ 8, { MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_SHELL,
+ MM_HUAWEI_PORT_MODE_SHELL,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_GPS,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,rndis:1,pcui:2,c_shell:3,a_shell:4,3g_diag:5,gps:6,4g_diag:7,mass:8,",
+ 8, { MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_SHELL,
+ MM_HUAWEI_PORT_MODE_SHELL,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_GPS,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: huawei,,pcui:0,3g_modem:1,ncm:2,mass:3,mass_two:4",
+ 5, { MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_SD,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE:TYPE:WCDMA:Qualcomm,MDM:0,NDIS:1,DIAG:2,PCUI:3,CDROM:4,SD:5",
+ 6, { MM_HUAWEI_PORT_MODE_MODEM,
+ MM_HUAWEI_PORT_MODE_NET,
+ MM_HUAWEI_PORT_MODE_DIAG,
+ MM_HUAWEI_PORT_MODE_PCUI,
+ MM_HUAWEI_PORT_MODE_CDROM,
+ MM_HUAWEI_PORT_MODE_SD
+ }
+ },
+ {
+ "^GETPORTMODE: TYPE: WCDMA: Huawei Technologies Co.,Ltd.,",
+ 0
+ },
+};
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+static void
+test_getportmode (void)
{
-#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
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (getportmode_tests); i++) {
+ g_autoptr(GArray) modes = NULL;
+ g_autoptr(GError) error = NULL;
+
+ mm_obj_dbg (NULL, "testing ^GETPORTMODE response: '%s'", getportmode_tests[i].str);
+
+ modes = mm_huawei_parse_getportmode_response (getportmode_tests[i].str, NULL, &error);
+ if (modes) {
+ guint j;
+
+ g_assert_no_error (error);
+ g_assert_cmpuint (modes->len, ==, getportmode_tests[i].n_modes);
+ for (j = 0; j < getportmode_tests[i].n_modes; j++)
+ g_assert_cmpuint (g_array_index (modes, MMHuaweiPortMode, j), ==, getportmode_tests[i].modes[j]);
+ } else
+ g_assert (error);
+ }
}
+/*****************************************************************************/
+
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/huawei/ndisstatqry", test_ndisstatqry);
+ g_test_add_func ("/MM/huawei/dhcp", test_dhcp);
g_test_add_func ("/MM/huawei/sysinfo", test_sysinfo);
g_test_add_func ("/MM/huawei/sysinfoex", test_sysinfoex);
g_test_add_func ("/MM/huawei/prefmode", test_prefmode);
@@ -1173,6 +1415,8 @@ int main (int argc, char **argv)
g_test_add_func ("/MM/huawei/syscfgex/response", test_syscfgex_response);
g_test_add_func ("/MM/huawei/nwtime", test_nwtime);
g_test_add_func ("/MM/huawei/time", test_time);
+ g_test_add_func ("/MM/huawei/hcsq", test_hcsq);
+ g_test_add_func ("/MM/huawei/getportmode", test_getportmode);
return g_test_run ();
}
diff --git a/plugins/icera/mm-broadband-bearer-icera.c b/plugins/icera/mm-broadband-bearer-icera.c
index c4a40c22..1d558aa8 100644
--- a/plugins/icera/mm-broadband-bearer-icera.c
+++ b/plugins/icera/mm-broadband-bearer-icera.c
@@ -29,7 +29,7 @@
#include "mm-broadband-bearer-icera.h"
#include "mm-base-modem-at.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
#include "mm-daemon-enums-types.h"
@@ -63,43 +63,16 @@ struct _MMBroadbandBearerIceraPrivate {
/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerIcera *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
- GSimpleAsyncResult *result;
} GetIpConfig3gppContext;
-static GetIpConfig3gppContext *
-get_ip_config_3gpp_context_new (MMBroadbandBearerIcera *self,
- MMBaseModem *modem,
- MMPortSerialAt *primary,
- guint cid,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GetIpConfig3gppContext *ctx;
-
- ctx = g_new0 (GetIpConfig3gppContext, 1);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
- ctx->primary = g_object_ref (primary);
- ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_ip_config_3gpp_context_new);
- return ctx;
-}
-
static void
-get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx)
+get_ip_config_context_free (GetIpConfig3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -113,12 +86,10 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
MMBearerConnectResult *configs;
MMBearerIpConfig *ipv4, *ipv6;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ configs = g_task_propagate_pointer (G_TASK (res), error);
+ if (!configs)
return FALSE;
- configs = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- g_assert (configs);
-
ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs);
ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs);
g_assert (ipv4 || ipv6);
@@ -127,23 +98,27 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
if (ipv6_config && ipv6)
*ipv6_config = g_object_ref (ipv6);
+ mm_bearer_connect_result_unref (configs);
return TRUE;
}
static void
ip_config_ready (MMBaseModem *modem,
GAsyncResult *res,
- GetIpConfig3gppContext *ctx)
+ GTask *task)
{
+ GetIpConfig3gppContext *ctx;
MMBearerIpConfig *ipv4_config = NULL;
MMBearerIpConfig *ipv6_config = NULL;
const gchar *response;
GError *error = NULL;
MMBearerConnectResult *connect_result;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
goto out;
}
@@ -152,34 +127,34 @@ ip_config_ready (MMBaseModem *modem,
&ipv4_config,
&ipv6_config,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
goto out;
}
if (!ipv4_config && !ipv6_config) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get IP config: couldn't parse response '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get IP config: couldn't parse response '%s'",
+ response);
goto out;
}
connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
ipv4_config,
ipv6_config);
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- connect_result,
- (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_task_return_pointer (task,
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
out:
+ g_object_unref (task);
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
- get_ip_config_context_complete_and_free (ctx);
}
static void
-get_ip_config_3gpp (MMBroadbandBearer *self,
+get_ip_config_3gpp (MMBroadbandBearer *_self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
@@ -189,16 +164,19 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self);
GetIpConfig3gppContext *ctx;
+ GTask *task;
- ctx = get_ip_config_3gpp_context_new (MM_BROADBAND_BEARER_ICERA (self),
- MM_BASE_MODEM (modem),
- primary,
- cid,
- callback,
- user_data);
+ ctx = g_new0 (GetIpConfig3gppContext, 1);
+ ctx->modem = g_object_ref (MM_BASE_MODEM (modem));
+ ctx->primary = g_object_ref (primary);
+ ctx->cid = cid;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free);
- if (ctx->self->priv->default_ip_method == MM_BEARER_IP_METHOD_STATIC) {
+ if (self->priv->default_ip_method == MM_BEARER_IP_METHOD_STATIC) {
gchar *command;
command = g_strdup_printf ("%%IPDPADDR=%u", cid);
@@ -210,13 +188,13 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)ip_config_ready,
- ctx);
+ task);
g_free (command);
return;
}
/* Otherwise, DHCP */
- if (ctx->self->priv->default_ip_method == MM_BEARER_IP_METHOD_DHCP) {
+ if (self->priv->default_ip_method == MM_BEARER_IP_METHOD_DHCP) {
MMBearerConnectResult *connect_result;
MMBearerIpConfig *ipv4_config = NULL, *ipv6_config = NULL;
@@ -236,10 +214,10 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- connect_result,
- (GDestroyNotify)mm_bearer_connect_result_unref);
- get_ip_config_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
return;
}
@@ -249,58 +227,44 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
/*****************************************************************************/
/* 3GPP disconnection */
-typedef struct {
- MMBroadbandBearerIcera *self;
- GSimpleAsyncResult *result;
-} Disconnect3gppContext;
-
-static void
-disconnect_3gpp_context_complete_and_free (Disconnect3gppContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
disconnect_3gpp_timed_out_cb (MMBroadbandBearerIcera *self)
{
- Disconnect3gppContext *ctx;
+ GTask *task;
- /* Recover context */
- ctx = self->priv->disconnect_pending;
+ /* Recover disconnection task */
+ task = self->priv->disconnect_pending;
self->priv->disconnect_pending = NULL;
self->priv->disconnect_pending_id = 0;
- g_simple_async_result_set_error (ctx->result,
- MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
- "Disconnection attempt timed out");
+ g_task_return_new_error (task,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
+ "Disconnection attempt timed out");
+ g_object_unref (task);
- disconnect_3gpp_context_complete_and_free (ctx);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
-report_disconnect_status (MMBroadbandBearerIcera *self,
- MMBearerConnectionStatus status)
+process_pending_disconnect_attempt (MMBroadbandBearerIcera *self,
+ MMBearerConnectionStatus status)
{
- Disconnect3gppContext *ctx;
+ GTask *task;
- /* Recover context */
- ctx = self->priv->disconnect_pending;
+ /* Recover disconnection task */
+ task = self->priv->disconnect_pending;
self->priv->disconnect_pending = NULL;
- g_assert (ctx != NULL);
+ g_assert (task != NULL);
/* Cleanup timeout, if any */
if (self->priv->disconnect_pending_id) {
@@ -310,19 +274,19 @@ report_disconnect_status (MMBroadbandBearerIcera *self,
/* Received 'CONNECTED' during a disconnection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Disconnection failed");
- disconnect_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Disconnection failed");
+ g_object_unref (task);
return;
}
/* Received 'DISCONNECTED' during a disconnection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_3gpp_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -335,37 +299,39 @@ disconnect_ipdpact_ready (MMBaseModem *modem,
GAsyncResult *res,
MMBroadbandBearerIcera *self)
{
- Disconnect3gppContext *ctx;
GError *error = NULL;
+ GTask *task;
- /* Try to recover the disconnection context. If none found, it means the
- * context was already completed and we have nothing else to do. */
- ctx = self->priv->disconnect_pending;
-
- /* Balance refcount with the extra ref we passed to command_full() */
- g_object_unref (self);
-
- if (!ctx) {
- mm_dbg ("Disconnection context was finished already by an unsolicited message");
+ /* Try to recover the disconnection task. If none found, it means the
+ * task was already completed and we have nothing else to do. */
+ task = g_steal_pointer (&self->priv->disconnect_pending);
+ if (!task) {
+ mm_obj_dbg (self, "disconnection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
- * the result */
+ * about the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
- return;
+ goto out;
}
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- self->priv->disconnect_pending = NULL;
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_3gpp_context_complete_and_free (ctx);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
+ /* Track again */
+ self->priv->disconnect_pending = task;
+
/* Set a 60-second disconnection-failure timeout */
self->priv->disconnect_pending_id = g_timeout_add_seconds (60,
(GSourceFunc)disconnect_3gpp_timed_out_cb,
self);
+
+out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static void
@@ -380,35 +346,30 @@ disconnect_3gpp (MMBroadbandBearer *bearer,
{
MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (bearer);
gchar *command;
- Disconnect3gppContext *ctx;
+ GTask *task;
- ctx = g_new0 (Disconnect3gppContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect_3gpp);
+ task = g_task_new (self, NULL, callback, user_data);
/* The unsolicited response to %IPDPACT may come before the OK does.
- * We will keep the disconnection context in the bearer private data so
+ * We will keep the disconnection task in the bearer private data so
* that it is accessible from the unsolicited message handler. Note
- * also that we do NOT pass the ctx to the GAsyncReadyCallback, as it
+ * also that we do NOT pass the task to the GAsyncReadyCallback, as it
* may not be valid any more when the callback is called (it may be
* already completed in the unsolicited handling) */
- g_assert (ctx->self->priv->disconnect_pending == NULL);
- ctx->self->priv->disconnect_pending = ctx;
+ g_assert (self->priv->disconnect_pending == NULL);
+ self->priv->disconnect_pending = task;
command = g_strdup_printf ("%%IPDPACT=%d,0", cid);
mm_base_modem_at_command_full (
MM_BASE_MODEM (modem),
primary,
command,
- 60,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_ipdpact_ready,
- g_object_ref (ctx->self)); /* we pass the bearer object! */
+ g_object_ref (self)); /* we pass the bearer object! */
g_free (command);
}
@@ -416,177 +377,128 @@ disconnect_3gpp (MMBroadbandBearer *bearer,
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerIcera *self;
- MMBaseModem *modem;
+ MMBaseModem *modem;
MMPortSerialAt *primary;
- guint cid;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- MMPort *data;
- guint authentication_retries;
- GError *saved_error;
+ guint cid;
+ MMPort *data;
+ guint authentication_retries;
+ GError *saved_error;
} Dial3gppContext;
static void
-dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+dial_3gpp_context_free (Dial3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->data)
- g_object_unref (ctx->data);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
+ g_assert (!ctx->saved_error);
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->primary);
+ g_clear_object (&ctx->modem);
g_slice_free (Dial3gppContext, ctx);
}
-static gboolean
-dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx,
- GError **error)
-{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Dial operation has been cancelled");
- return TRUE;
-}
-
-static gboolean
-dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx)
+static guint
+dial_3gpp_get_connecting_cid (GTask *task)
{
- GError *error = NULL;
-
- if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error))
- return FALSE;
+ Dial3gppContext *ctx;
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return TRUE;
+ ctx = g_task_get_task_data (task);
+ return ctx->cid;
}
static MMPort *
-dial_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ return MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
}
static void
-connect_reset_ready (MMBaseModem *modem,
+connect_reset_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_full_finish (modem, res, NULL);
- /* error should have already been set in the simple async result */
- dial_3gpp_context_complete_and_free (ctx);
+ /* When reset is requested, it was either cancelled or an error was stored */
+ if (!g_task_return_error_if_cancelled (task)) {
+ g_assert (ctx->saved_error);
+ g_task_return_error (task, ctx->saved_error);
+ ctx->saved_error = NULL;
+ }
+
+ g_object_unref (task);
}
static void
-connect_reset (Dial3gppContext *ctx)
+connect_reset (GTask *task)
{
- gchar *command;
+ Dial3gppContext *ctx;
+ gchar *command;
+
+ ctx = g_task_get_task_data (task);
/* Need to reset the connection attempt */
command = g_strdup_printf ("%%IPDPACT=%d,0", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
- 3,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)connect_reset_ready,
- ctx);
+ task);
g_free (command);
}
static gboolean
connect_timed_out_cb (MMBroadbandBearerIcera *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- /* Recover context and remove it from the private info */
- ctx = self->priv->connect_pending;
+ /* Cleanup timeout ID */
+ self->priv->connect_pending_id = 0;
+
+ /* Recover task and own it */
+ task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
+ g_assert (task);
- /* Remove cancellation, if found */
- if (self->priv->connect_cancellable_id) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
- self->priv->connect_cancellable_id = 0;
- }
+ ctx = g_task_get_task_data (task);
/* Remove closed port watch, if found */
- if (ctx && self->priv->connect_port_closed_id) {
+ if (self->priv->connect_port_closed_id) {
g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id);
self->priv->connect_port_closed_id = 0;
}
- /* Cleanup timeout ID */
- self->priv->connect_pending_id = 0;
-
- /* If we were cancelled, prefer that error */
- if (ctx->saved_error) {
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
- ctx->saved_error = NULL;
- } else
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Connection attempt timed out");
+ /* Setup error to return after the reset */
+ g_assert (!ctx->saved_error);
+ ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Connection attempt timed out");
/* It's probably pointless to try to reset this here, but anyway... */
- connect_reset (ctx);
+ connect_reset (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
-connect_cancelled_cb (GCancellable *cancellable,
- MMBroadbandBearerIcera *self)
-{
- Dial3gppContext *ctx;
-
- /* Recover context but DON'T remove it from the private info */
- ctx = self->priv->connect_pending;
-
- /* Remove the cancellable
- * NOTE: we shouldn't remove the timeout yet. We still need to wait
- * to get connected before running the explicit connection reset */
- self->priv->connect_cancellable_id = 0;
-
- /* Store cancelled error */
- g_assert (dial_3gpp_context_set_error_if_cancelled (ctx, &ctx->saved_error));
-
- /* We cannot reset right here, we need to wait for the connection
- * attempt to finish */
-}
-
-static void
-forced_close_cb (MMPortSerial *port,
- MMBroadbandBearerIcera *self)
-{
- /* Just treat the forced close event as any other unsolicited message */
- mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
- MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED);
-}
-
-static void
-ier_query_ready (MMBaseModem *modem,
+ier_query_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
- const gchar *response;
- GError *activation_error = NULL;
+ MMBroadbandBearerIcera *self;
+ const gchar *response;
+ GError *activation_error = NULL;
+
+ self = g_task_get_source_object (task);
response = mm_base_modem_at_command_full_finish (modem, res, NULL);
if (response) {
@@ -599,48 +511,39 @@ ier_query_ready (MMBaseModem *modem,
* 33 - Requested service option not subscribed
*/
if (nw_activation_err == 27 || nw_activation_err == 33)
- activation_error = mm_mobile_equipment_error_for_code (
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED);
+ activation_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED, self);
}
}
if (activation_error)
- g_simple_async_result_take_error (ctx->result, activation_error);
+ g_task_return_error (task, activation_error);
else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Call setup failed");
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Call setup failed");
+ g_object_unref (task);
}
static void
-report_connect_status (MMBroadbandBearerIcera *self,
- MMBearerConnectionStatus status)
+process_pending_connect_attempt (MMBroadbandBearerIcera *self,
+ MMBearerConnectionStatus status)
{
+ GTask *task;
Dial3gppContext *ctx;
- g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
- status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
- status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
-
- /* Recover context and remove it from the private info */
- ctx = self->priv->connect_pending;
+ /* Recover task and remove both cancellation and timeout (if any)*/
+ g_assert (self->priv->connect_pending);
+ task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
- g_assert (ctx != NULL);
- /* Cleanup cancellable, timeout and port closed watch, if any */
+ ctx = g_task_get_task_data (task);
+
if (self->priv->connect_pending_id) {
g_source_remove (self->priv->connect_pending_id);
self->priv->connect_pending_id = 0;
}
- if (self->priv->connect_cancellable_id) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
- self->priv->connect_cancellable_id = 0;
- }
-
if (self->priv->connect_port_closed_id) {
g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id);
self->priv->connect_port_closed_id = 0;
@@ -648,29 +551,21 @@ report_connect_status (MMBroadbandBearerIcera *self,
/* Received 'CONNECTED' during a connection attempt? */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
- /* If we wanted to get cancelled before, do it now */
- if (ctx->saved_error) {
- /* Keep error */
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
- ctx->saved_error = NULL;
- /* Cancel connection */
- connect_reset (ctx);
+ /* If we wanted to get cancelled before, do it now. */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ connect_reset (task);
return;
}
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- (GDestroyNotify)g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
return;
}
/* If we wanted to get cancelled before and now we couldn't connect,
* use the cancelled error and return */
- if (ctx->saved_error) {
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
- ctx->saved_error = NULL;
- dial_3gpp_context_complete_and_free (ctx);
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
@@ -685,86 +580,109 @@ report_connect_status (MMBroadbandBearerIcera *self,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
- (GAsyncReadyCallback)ier_query_ready,
- ctx);
+ (GAsyncReadyCallback) ier_query_ready,
+ task);
return;
}
/* Otherwise, received 'DISCONNECTED' during a connection attempt? */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Call setup failed");
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed");
+ g_object_unref (task);
+}
+
+static void
+forced_close_cb (MMBroadbandBearerIcera *self)
+{
+ /* Just treat the forced close event as any other unsolicited message */
+ mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
+ MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED);
}
static void
-activate_ready (MMBaseModem *modem,
- GAsyncResult *res,
+activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
MMBroadbandBearerIcera *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- GError *error = NULL;
+ GError *error = NULL;
+
+ task = g_steal_pointer (&self->priv->connect_pending);
/* Try to recover the connection context. If none found, it means the
* context was already completed and we have nothing else to do. */
- ctx = self->priv->connect_pending;
-
- /* Balance refcount with the extra ref we passed to command_full() */
- g_object_unref (self);
-
- if (!ctx) {
- mm_dbg ("Connection context was finished already by an unsolicited message");
-
+ if (!task) {
+ mm_obj_dbg (self, "connection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
* the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
- return;
+ goto out;
}
/* Errors on the dial command are fatal */
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- self->priv->connect_pending = NULL;
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
+ /* Track again */
+ self->priv->connect_pending = task;
+
/* We will now setup a timeout and keep the context in the bearer's private.
* Reports of modem being connected will arrive via unsolicited messages.
* This timeout should be long enough. Actually... ideally should never get
* reached. */
- self->priv->connect_pending_id = g_timeout_add_seconds (60,
+ self->priv->connect_pending_id = g_timeout_add_seconds (MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
(GSourceFunc)connect_timed_out_cb,
self);
- /* From now on, if we get cancelled, we'll still need to wait for the connection
- * attempt to finish before resetting it */
- self->priv->connect_cancellable_id = g_cancellable_connect (ctx->cancellable,
- G_CALLBACK (connect_cancelled_cb),
- self,
- NULL);
-
/* If we get the port closed, we treat as a connect error */
- self->priv->connect_port_closed_id = g_signal_connect (ctx->primary,
- "forced-close",
- G_CALLBACK (forced_close_cb),
- self);
+ ctx = g_task_get_task_data (task);
+ self->priv->connect_port_closed_id = g_signal_connect_swapped (ctx->primary,
+ "forced-close",
+ G_CALLBACK (forced_close_cb),
+ self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static void
-deactivate_ready (MMBaseModem *modem,
- GAsyncResult *res,
- Dial3gppContext *ctx)
-{
- gchar *command;
+dial_3gpp (MMBroadbandBearer *_self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self);
+ GTask *task;
+ Dial3gppContext *ctx;
+ g_autofree gchar *cmd = NULL;
- /*
- * Ignore any error here; %IPDPACT=ctx,0 will produce an error 767
- * if the context is not, in fact, connected. This is annoying but
- * harmless.
- */
- mm_base_modem_at_command_full_finish (modem, res, NULL);
+ g_assert (primary != NULL);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ ctx = g_slice_new0 (Dial3gppContext);
+ ctx->modem = g_object_ref (modem);
+ ctx->primary = g_object_ref (primary);
+ ctx->cid = cid;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
+
+ /* We need a net data port */
+ ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
+ if (!ctx->data) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
+ return;
+ }
/* The unsolicited response to %IPDPACT may come before the OK does.
* We will keep the connection context in the bearer private data so
@@ -772,207 +690,57 @@ deactivate_ready (MMBaseModem *modem,
* also that we do NOT pass the ctx to the GAsyncReadyCallback, as it
* may not be valid any more when the callback is called (it may be
* already completed in the unsolicited handling) */
- g_assert (ctx->self->priv->connect_pending == NULL);
- ctx->self->priv->connect_pending = ctx;
-
- command = g_strdup_printf ("%%IPDPACT=%d,1", ctx->cid);
- mm_base_modem_at_command_full (
- ctx->modem,
- ctx->primary,
- command,
- 60,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)activate_ready,
- g_object_ref (ctx->self)); /* we pass the bearer object! */
- g_free (command);
-}
-
-static void authenticate (Dial3gppContext *ctx);
-
-static gboolean
-retry_authentication_cb (Dial3gppContext *ctx)
-{
- authenticate (ctx);
- return FALSE;
-}
-
-static void
-authenticate_ready (MMBaseModem *modem,
- GAsyncResult *res,
- Dial3gppContext *ctx)
-{
- GError *error = NULL;
- gchar *command;
-
- /* If cancelled, complete */
- if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
- return;
-
- if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- /* Retry configuring the context. It sometimes fails with a 583
- * error ["a profile (CID) is currently active"] if a connect
- * is attempted too soon after a disconnect. */
- if (++ctx->authentication_retries < 3) {
- mm_dbg ("Authentication failed: '%s'; retrying...", error->message);
- g_error_free (error);
- g_timeout_add_seconds (1, (GSourceFunc)retry_authentication_cb, ctx);
- return;
- }
-
- /* Return an error */
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return;
- }
-
- /*
- * Deactivate the context we want to use before we try to activate
- * it. This handles the case where ModemManager crashed while
- * connected and is now trying to reconnect. (Should some part of
- * the core or modem driver have made sure of this already?)
- */
- command = g_strdup_printf ("%%IPDPACT=%d,0", ctx->cid);
- mm_base_modem_at_command_full (
- ctx->modem,
- ctx->primary,
- command,
- 60,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)deactivate_ready,
- ctx);
- g_free (command);
-}
-
-static void
-authenticate (Dial3gppContext *ctx)
-{
- gchar *command;
- const gchar *user;
- const gchar *password;
- MMBearerAllowedAuth allowed_auth;
-
- user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
-
- /* Both user and password are required; otherwise firmware returns an error */
- if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) {
- mm_dbg ("Not using authentication");
- command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->cid);
- } else {
- gchar *quoted_user;
- gchar *quoted_password;
- guint icera_auth;
-
- if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
- mm_dbg ("Using default (PAP) authentication method");
- icera_auth = 1;
- } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
- mm_dbg ("Using PAP authentication method");
- icera_auth = 1;
- } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) {
- mm_dbg ("Using CHAP authentication method");
- icera_auth = 2;
- } else {
- gchar *str;
-
- str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot use any of the specified authentication methods (%s)",
- str);
- g_free (str);
- dial_3gpp_context_complete_and_free (ctx);
- return;
- }
-
- quoted_user = mm_port_serial_at_quote_string (user);
- quoted_password = mm_port_serial_at_quote_string (password);
- command = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s",
- ctx->cid, icera_auth, quoted_user, quoted_password);
- g_free (quoted_user);
- g_free (quoted_password);
- }
+ g_assert (self->priv->connect_pending == NULL);
+ self->priv->connect_pending = task;
+ cmd = g_strdup_printf ("%%IPDPACT=%d,1", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
- command,
- 60,
+ cmd,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
- (GAsyncReadyCallback)authenticate_ready,
- ctx);
- g_free (command);
+ (GAsyncReadyCallback) activate_ready,
+ g_object_ref (self)); /* we pass the bearer object! */
}
-static void
-dial_3gpp (MMBroadbandBearer *self,
- MMBaseModem *modem,
- MMPortSerialAt *primary,
- guint cid,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- Dial3gppContext *ctx;
-
- g_assert (primary != NULL);
-
- ctx = g_slice_new0 (Dial3gppContext);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
- ctx->primary = g_object_ref (primary);
- ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- dial_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
-
- /* We need a net data port */
- ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
- if (!ctx->data) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
- dial_3gpp_context_complete_and_free (ctx);
- return;
- }
+/*****************************************************************************/
- authenticate (ctx);
+gint
+mm_broadband_bearer_icera_get_connecting_profile_id (MMBroadbandBearerIcera *self)
+{
+ return (self->priv->connect_pending ?
+ (gint)dial_3gpp_get_connecting_cid (self->priv->connect_pending) :
+ MM_3GPP_PROFILE_ID_UNKNOWN);
}
/*****************************************************************************/
static void
-report_connection_status (MMBaseBearer *bearer,
- MMBearerConnectionStatus status)
+report_connection_status (MMBaseBearer *_self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (bearer);
+ MMBroadbandBearerIcera *self = MM_BROADBAND_BEARER_ICERA (_self);
+
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
+ status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
/* Process pending connection attempt */
if (self->priv->connect_pending) {
- report_connect_status (self, status);
+ process_pending_connect_attempt (self, status);
return;
}
/* Process pending disconnection attempt */
if (self->priv->disconnect_pending) {
- report_disconnect_status (self, status);
+ process_pending_disconnect_attempt (self, status);
return;
}
- mm_dbg ("Received spontaneous %%IPDPACT (%s)",
- mm_bearer_connection_status_get_string (status));
+ mm_obj_dbg (self, "received spontaneous %%IPDPACT (%s)", mm_bearer_connection_status_get_string (status));
/* Received a random 'DISCONNECTED'...*/
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
@@ -980,8 +748,9 @@ report_connection_status (MMBaseBearer *bearer,
/* If no connection/disconnection attempt on-going, make sure we mark ourselves as
* disconnected. Make sure we only pass 'DISCONNECTED' to the parent */
MM_BASE_BEARER_CLASS (mm_broadband_bearer_icera_parent_class)->report_connection_status (
- bearer,
- MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ _self,
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
+ connection_error);
}
}
@@ -1086,7 +855,15 @@ mm_broadband_bearer_icera_class_init (MMBroadbandBearerIceraClass *klass)
object_class->get_property = get_property;
object_class->set_property = set_property;
+
base_bearer_class->report_connection_status = report_connection_status;
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = NULL;
+ base_bearer_class->reload_connection_status_finish = NULL;
+#endif
+
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
diff --git a/plugins/icera/mm-broadband-bearer-icera.h b/plugins/icera/mm-broadband-bearer-icera.h
index 8061e216..e169cb7c 100644
--- a/plugins/icera/mm-broadband-bearer-icera.h
+++ b/plugins/icera/mm-broadband-bearer-icera.h
@@ -61,4 +61,6 @@ void mm_broadband_bearer_icera_new (MMBroadbandModem *modem,
MMBaseBearer *mm_broadband_bearer_icera_new_finish (GAsyncResult *res,
GError **error);
+gint mm_broadband_bearer_icera_get_connecting_profile_id (MMBroadbandBearerIcera *self);
+
#endif /* MM_BROADBAND_BEARER_ICERA_H */
diff --git a/plugins/icera/mm-broadband-modem-icera.c b/plugins/icera/mm-broadband-modem-icera.c
index 730a79fb..e60d4bd5 100644
--- a/plugins/icera/mm-broadband-modem-icera.c
+++ b/plugins/icera/mm-broadband-modem-icera.c
@@ -25,27 +25,33 @@
#include "ModemManager.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-iface-modem-time.h"
+#include "mm-common-helpers.h"
#include "mm-base-modem-at.h"
#include "mm-bearer-list.h"
#include "mm-broadband-bearer-icera.h"
#include "mm-broadband-modem-icera.h"
+#include "mm-modem-helpers-icera.h"
-static void iface_modem_init (MMIfaceModem *iface);
-static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
-static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
-static MMIfaceModem *iface_modem_parent;
-static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModem3gppProfileManager *iface_modem_3gpp_profile_manager_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIcera, mm_broadband_modem_icera, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init))
enum {
@@ -71,38 +77,38 @@ struct _MMBroadbandModemIceraPrivate {
/* Load supported modes (Modem interface) */
static void
-add_supported_mode (GArray **combinations,
- guint mode)
+add_supported_mode (MMBroadbandModemIcera *self,
+ GArray **combinations,
+ guint mode)
{
MMModemModeCombination combination;
switch (mode) {
case 0:
- mm_dbg ("Modem supports 2G-only mode");
+ mm_obj_dbg (self, "2G-only mode supported");
combination.allowed = MM_MODEM_MODE_2G;
combination.preferred = MM_MODEM_MODE_NONE;
break;
case 1:
- mm_dbg ("Modem supports 3G-only mode");
+ mm_obj_dbg (self, "3G-only mode supported");
combination.allowed = MM_MODEM_MODE_3G;
combination.preferred = MM_MODEM_MODE_NONE;
break;
case 2:
- mm_dbg ("Modem supports 2G/3G mode with 2G preferred");
+ mm_obj_dbg (self, "2G/3G mode with 2G preferred supported");
combination.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
combination.preferred = MM_MODEM_MODE_2G;
break;
case 3:
- mm_dbg ("Modem supports 2G/3G mode with 3G preferred");
+ mm_obj_dbg (self, "2G/3G mode with 3G preferred supported");
combination.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
combination.preferred = MM_MODEM_MODE_3G;
break;
case 5:
- mm_dbg ("Modem supports 'any', but not explicitly listing it");
/* Any, no need to add it to the list */
return;
default:
- mm_warn ("Unsupported Icera mode found: %u", mode);
+ mm_obj_warn (self, "unsupported mode found in %%IPSYS=?: %u", mode);
return;
}
@@ -182,18 +188,18 @@ load_supported_modes_finish (MMIfaceModem *self,
guint j;
for (j = modefirst; j <= modelast; j++)
- add_supported_mode (&combinations, j);
+ add_supported_mode (MM_BROADBAND_MODEM_ICERA (self), &combinations, j);
} else
- mm_warn ("Couldn't parse mode interval (%s) in %%IPSYS=? response", split[i]);
+ mm_obj_warn (self, "couldn't parse mode interval in %%IPSYS=? response: %s", split[i]);
g_free (first);
} else {
guint mode;
/* Add single */
if (mm_get_uint_from_str (split[i], &mode))
- add_supported_mode (&combinations, mode);
+ add_supported_mode (MM_BROADBAND_MODEM_ICERA (self), &combinations, mode);
else
- mm_warn ("Couldn't parse mode (%s) in %%IPSYS=? response", split[i]);
+ mm_obj_warn (self, "couldn't parse mode in %%IPSYS=? response: %s", split[i]);
}
}
@@ -305,24 +311,24 @@ modem_set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -332,14 +338,11 @@ modem_set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint icera_mode = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
/*
* The core has checked the following:
@@ -366,18 +369,16 @@ modem_set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -388,7 +389,7 @@ modem_set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -404,12 +405,21 @@ static void
bearer_list_report_status_foreach (MMBaseBearer *bearer,
BearerListReportStatusForeachContext *ctx)
{
- if (mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (bearer)) != ctx->cid)
- return;
+ gint profile_id;
+ gint connecting_profile_id;
if (!MM_IS_BROADBAND_BEARER_ICERA (bearer))
return;
+ /* The profile ID in the base bearer is set only once the modem is connected */
+ profile_id = mm_base_bearer_get_profile_id (bearer);
+
+ /* The profile ID in the icera bearer is available during the connecting phase */
+ connecting_profile_id = mm_broadband_bearer_icera_get_connecting_profile_id (MM_BROADBAND_BEARER_ICERA (bearer));
+
+ if ((profile_id != (gint)ctx->cid) && (connecting_profile_id != (gint)ctx->cid))
+ return;
+
mm_base_bearer_report_connection_status (bearer, ctx->status);
}
@@ -446,7 +456,7 @@ ipdpact_received (MMPortSerialAt *port,
ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED;
break;
default:
- mm_warn ("Unknown Icera connect status %d", status);
+ mm_obj_warn (self, "unknown %%IPDPACT connect status %d", status);
break;
}
@@ -555,7 +565,7 @@ set_unsolicited_events_handlers (MMBroadbandModemIcera *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -597,12 +607,16 @@ modem_load_access_technologies_finish (MMIfaceModem *self,
guint *mask,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
- *access_technologies = ((MMModemAccessTechnology) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res))));
+ *access_technologies = (MMModemAccessTechnology) value;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
}
@@ -610,26 +624,22 @@ modem_load_access_technologies_finish (MMIfaceModem *self,
static void
nwstate_query_ready (MMBroadbandModemIcera *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (error) {
- mm_dbg ("Couldn't query access technology: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- } else {
+ if (error)
+ g_task_return_error (task, error);
+ else {
/*
* The unsolicited message handler will already have run and
* removed the NWSTATE response, so we use the result from there.
*/
- g_simple_async_result_set_op_res_gpointer (operation_result,
- GUINT_TO_POINTER (self->priv->last_act),
- NULL);
+ g_task_return_int (task, self->priv->last_act);
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -637,12 +647,9 @@ modem_load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_access_technologies);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (
MM_BASE_MODEM (self),
@@ -650,7 +657,7 @@ modem_load_access_technologies (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)nwstate_query_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -661,26 +668,24 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -692,25 +697,21 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events));
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -718,13 +719,6 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
-
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_ICERA (self), FALSE);
@@ -732,7 +726,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -743,35 +737,33 @@ modem_3gpp_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
own_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -782,7 +774,7 @@ parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
3,
FALSE,
(GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -794,38 +786,33 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->enable_unsolicited_events (
self,
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events));
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
own_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -833,7 +820,7 @@ own_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->disable_unsolicited_events (
MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -847,10 +834,7 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
3,
FALSE,
(GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -861,84 +845,72 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_BASE_BEARER (g_object_ref (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res))));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_icera_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_icera_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
-modem_create_bearer (MMIfaceModem *self,
- MMBearerProperties *properties,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_create_bearer (MMIfaceModem *self,
+ MMBearerProperties *props,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
/* If we get a NET port, create Icera bearer */
if (mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET)) {
mm_broadband_bearer_icera_new (
MM_BROADBAND_MODEM (self),
MM_BROADBAND_MODEM_ICERA (self)->priv->default_ip_method,
- properties,
+ props,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_icera_new_ready,
- result);
+ task);
return;
}
/* Otherwise, plain generic broadband bearer */
mm_broadband_bearer_new (
MM_BROADBAND_MODEM (self),
- properties,
+ props,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -949,13 +921,13 @@ modem_power_up_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cfun_enable_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
@@ -964,13 +936,14 @@ cfun_enable_ready (MMBaseModem *self,
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED))
- g_simple_async_result_take_error (simple, error);
- else
+ g_task_return_error (task, error);
+ else {
g_error_free (error);
+ g_task_return_boolean (task, TRUE);
+ }
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -978,19 +951,12 @@ modem_power_up (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_power_up);
-
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CFUN=1",
10,
FALSE,
(GAsyncReadyCallback)cfun_enable_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1056,16 +1022,13 @@ modem_load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_unlock_retries_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -1073,10 +1036,8 @@ load_unlock_retries_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1088,18 +1049,15 @@ load_unlock_retries_ready (MMBaseModem *self,
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- retries,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, retries, g_object_unref);
} else {
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid unlock retries response: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid unlock retries response: '%s'",
+ response);
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -1113,10 +1071,7 @@ modem_load_unlock_retries (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_unlock_retries_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_unlock_retries));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1137,20 +1092,20 @@ band_free (Band *b)
static const Band modem_bands[] = {
/* Sort 3G first since it's preferred */
- { MM_MODEM_BAND_U2100, "FDD_BAND_I", FALSE },
- { MM_MODEM_BAND_U1900, "FDD_BAND_II", FALSE },
- { MM_MODEM_BAND_U1800, "FDD_BAND_III", FALSE },
- { MM_MODEM_BAND_U17IV, "FDD_BAND_IV", FALSE },
- { MM_MODEM_BAND_U800, "FDD_BAND_VI", FALSE },
- { MM_MODEM_BAND_U850, "FDD_BAND_V", FALSE },
- { MM_MODEM_BAND_U900, "FDD_BAND_VIII", FALSE },
+ { MM_MODEM_BAND_UTRAN_1, (gchar *) "FDD_BAND_I", FALSE },
+ { MM_MODEM_BAND_UTRAN_2, (gchar *) "FDD_BAND_II", FALSE },
+ { MM_MODEM_BAND_UTRAN_3, (gchar *) "FDD_BAND_III", FALSE },
+ { MM_MODEM_BAND_UTRAN_4, (gchar *) "FDD_BAND_IV", FALSE },
+ { MM_MODEM_BAND_UTRAN_5, (gchar *) "FDD_BAND_V", FALSE },
+ { MM_MODEM_BAND_UTRAN_6, (gchar *) "FDD_BAND_VI", FALSE },
+ { MM_MODEM_BAND_UTRAN_8, (gchar *) "FDD_BAND_VIII", FALSE },
/* 2G second */
- { MM_MODEM_BAND_G850, "G850", FALSE },
- { MM_MODEM_BAND_DCS, "DCS", FALSE },
- { MM_MODEM_BAND_EGSM, "EGSM", FALSE },
- { MM_MODEM_BAND_PCS, "PCS", FALSE },
+ { MM_MODEM_BAND_G850, (gchar *) "G850", FALSE },
+ { MM_MODEM_BAND_DCS, (gchar *) "DCS", FALSE },
+ { MM_MODEM_BAND_EGSM, (gchar *) "EGSM", FALSE },
+ { MM_MODEM_BAND_PCS, (gchar *) "PCS", FALSE },
/* And ANY last since it's most inclusive */
- { MM_MODEM_BAND_ANY, "ANY", FALSE },
+ { MM_MODEM_BAND_ANY, (gchar *) "ANY", FALSE },
};
static const guint modem_band_any_bit = 1 << (G_N_ELEMENTS (modem_bands) - 1);
@@ -1158,7 +1113,7 @@ static const guint modem_band_any_bit = 1 << (G_N_ELEMENTS (modem_bands) - 1);
static MMModemBand
icera_band_to_mm (const char *icera)
{
- int i;
+ guint i;
for (i = 0 ; i < G_N_ELEMENTS (modem_bands); i++) {
if (g_strcmp0 (icera, modem_bands[i].name) == 0)
@@ -1219,7 +1174,7 @@ parse_bands (const gchar *response, guint32 *out_len)
/* Load supported bands (Modem interface) */
typedef struct {
- MMBaseModemAtCommand *cmds;
+ MMBaseModemAtCommandAlloc *cmds;
GSList *check_bands;
GSList *enabled_bands;
guint32 idx;
@@ -1231,7 +1186,7 @@ supported_bands_context_free (SupportedBandsContext *ctx)
guint i;
for (i = 0; ctx->cmds[i].command; i++)
- g_free (ctx->cmds[i].command);
+ mm_base_modem_at_command_alloc_clear (&ctx->cmds[i]);
g_free (ctx->cmds);
g_slist_free_full (ctx->check_bands, (GDestroyNotify) band_free);
g_slist_free_full (ctx->enabled_bands, (GDestroyNotify) band_free);
@@ -1243,17 +1198,13 @@ modem_load_supported_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_supported_bands_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
SupportedBandsContext *ctx = NULL;
@@ -1262,7 +1213,7 @@ load_supported_bands_ready (MMBaseModem *self,
mm_base_modem_at_sequence_finish (self, res, (gpointer) &ctx, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), ctx->idx);
@@ -1282,26 +1233,26 @@ load_supported_bands_ready (MMBaseModem *self,
g_array_prepend_val (bands, b->band);
}
- g_simple_async_result_set_op_res_gpointer (simple,
- bands,
- (GDestroyNotify) g_array_unref);
+ g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
-static gboolean
-load_supported_bands_response_processor (MMBaseModem *self,
- gpointer context,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
-{
- SupportedBandsContext *ctx = context;
- Band *b = g_slist_nth_data (ctx->check_bands, ctx->idx++);
+static MMBaseModemAtResponseProcessorResult
+load_supported_bands_response_processor (MMBaseModem *self,
+ gpointer context,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ SupportedBandsContext *ctx;
+ Band *b;
+
+ ctx = context;
+ b = g_slist_nth_data (ctx->check_bands, ctx->idx++);
/* If there was no error setting the band, that band is supported. We
* abuse the 'enabled' item to mean supported/unsupported.
@@ -1309,13 +1260,13 @@ load_supported_bands_response_processor (MMBaseModem *self,
b->enabled = !error;
/* Continue to next band */
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
static void
load_supported_bands_get_current_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
SupportedBandsContext *ctx;
const gchar *response;
@@ -1325,10 +1276,8 @@ load_supported_bands_get_current_bands_ready (MMIfaceModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query current bands: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1338,7 +1287,7 @@ load_supported_bands_get_current_bands_ready (MMIfaceModem *self,
* to its current enabled/disabled state.
*/
iter = ctx->check_bands = parse_bands (response, &len);
- ctx->cmds = g_new0 (MMBaseModemAtCommand, len + 1);
+ ctx->cmds = g_new0 (MMBaseModemAtCommandAlloc, len + 1);
while (iter) {
Band *b = iter->data;
@@ -1362,11 +1311,11 @@ load_supported_bands_get_current_bands_ready (MMIfaceModem *self,
}
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
- ctx->cmds,
+ (const MMBaseModemAtCommand *)ctx->cmds,
ctx,
(GDestroyNotify) supported_bands_context_free,
(GAsyncReadyCallback) load_supported_bands_ready,
- operation_result);
+ task);
}
static void
@@ -1385,10 +1334,7 @@ modem_load_supported_bands (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_supported_bands_get_current_bands_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_bands));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1399,17 +1345,13 @@ modem_load_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_current_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GArray *bands;
const gchar *response;
@@ -1419,10 +1361,7 @@ load_current_bands_ready (MMIfaceModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query current bands: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
} else {
/* Parse bands from Icera response into MM band numbers */
parsed = parse_bands (response, &len);
@@ -1435,12 +1374,9 @@ load_current_bands_ready (MMIfaceModem *self,
}
g_slist_free_full (parsed, (GDestroyNotify) band_free);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- bands,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
}
+ g_object_unref (task);
}
static void
@@ -1454,17 +1390,13 @@ modem_load_current_bands (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_current_bands_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_current_bands));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Set current bands (Modem interface) */
typedef struct {
- GSimpleAsyncResult *result;
guint bandbits;
guint enablebits;
guint disablebits;
@@ -1482,43 +1414,37 @@ modem_set_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void set_one_band (MMIfaceModem *self, SetCurrentBandsContext *ctx);
-
-static void
-set_current_bands_context_complete_and_free (SetCurrentBandsContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_slice_free (SetCurrentBandsContext, ctx);
-}
+static void set_one_band (MMIfaceModem *self, GTask *task);
static void
set_current_bands_next (MMIfaceModem *self,
GAsyncResult *res,
- SetCurrentBandsContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) {
- mm_dbg ("Couldn't set current bands: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- set_current_bands_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- set_one_band (self, ctx);
+ set_one_band (self, task);
}
static void
set_one_band (MMIfaceModem *self,
- SetCurrentBandsContext *ctx)
+ GTask *task)
{
+ SetCurrentBandsContext *ctx;
guint enable, band;
gchar *command;
+ ctx = g_task_get_task_data (task);
+
/* Find the next band to enable or disable, always doing enables first */
enable = 1;
band = ffs (ctx->enablebits);
@@ -1528,22 +1454,22 @@ set_one_band (MMIfaceModem *self,
}
if (band == 0) {
/* Both enabling and disabling are done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_bands_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Note that ffs() returning 2 corresponds to 1 << 1, not 1 << 2 */
band--;
- mm_dbg("1. enablebits %x disablebits %x band %d enable %d",
- ctx->enablebits, ctx->disablebits, band, enable);
+ mm_obj_dbg (self, "preparing %%IPBM command (1/2): enablebits %x, disablebits %x, band %d, enable %d",
+ ctx->enablebits, ctx->disablebits, band, enable);
if (enable)
ctx->enablebits &= ~(1 << band);
else
ctx->disablebits &= ~(1 << band);
- mm_dbg("2. enablebits %x disablebits %x",
- ctx->enablebits, ctx->disablebits);
+ mm_obj_dbg (self, "preparing %%IPBM command (2/2): enablebits %x, disablebits %x",
+ ctx->enablebits, ctx->disablebits);
command = g_strdup_printf ("%%IPBM=\"%s\",%d",
modem_bands[band].name,
@@ -1554,7 +1480,7 @@ set_one_band (MMIfaceModem *self,
10,
FALSE,
(GAsyncReadyCallback)set_current_bands_next,
- ctx);
+ task);
g_free (command);
}
@@ -1582,24 +1508,26 @@ band_array_to_bandbits (GArray *bands)
static void
set_current_bands_got_current_bands (MMIfaceModem *self,
GAsyncResult *res,
- SetCurrentBandsContext *ctx)
+ GTask *task)
{
+ SetCurrentBandsContext *ctx;
GArray *bands;
GError *error = NULL;
guint currentbits;
bands = modem_load_current_bands_finish (self, res, &error);
if (!bands) {
- g_simple_async_result_take_error (ctx->result, error);
- set_current_bands_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
currentbits = band_array_to_bandbits (bands);
ctx->enablebits = ctx->bandbits & ~currentbits;
ctx->disablebits = currentbits & ~ctx->bandbits;
- set_one_band (self, ctx);
+ set_one_band (self, task);
}
static void
@@ -1609,27 +1537,27 @@ modem_set_current_bands (MMIfaceModem *self,
gpointer user_data)
{
SetCurrentBandsContext *ctx;
+ GTask *task;
- ctx = g_slice_new0 (SetCurrentBandsContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_set_current_bands);
+ ctx = g_new0 (SetCurrentBandsContext, 1);
ctx->bandbits = band_array_to_bandbits (bands_array);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
/*
* If ANY is requested, simply enable ANY to activate all bands except for
* those forbidden. */
if (ctx->bandbits & modem_band_any_bit) {
ctx->enablebits = modem_band_any_bit;
ctx->disablebits = 0;
- set_one_band (self, ctx);
+ set_one_band (self, task);
return;
}
modem_load_current_bands (self,
(GAsyncReadyCallback)set_current_bands_got_current_bands,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -1763,6 +1691,424 @@ modem_time_load_network_timezone (MMIfaceModemTime *self,
}
/*****************************************************************************/
+/* List profiles (3GPP profile management interface) */
+
+typedef struct {
+ GList *profiles;
+} ListProfilesContext;
+
+static void
+list_profiles_context_free (ListProfilesContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->profiles);
+ g_slice_free (ListProfilesContext, ctx);
+}
+
+static gboolean
+modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **out_profiles,
+ GError **error)
+{
+ ListProfilesContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (out_profiles)
+ *out_profiles = g_steal_pointer (&ctx->profiles);
+ return TRUE;
+}
+
+static void
+profile_manager_ipdpcfg_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ const gchar *response;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response)
+ mm_obj_warn (self, "couldn't load PDP context auth settings: %s", error->message);
+ else if (!mm_icera_parse_ipdpcfg_query_response (response, ctx->profiles, self, &error))
+ mm_obj_warn (self, "couldn't update profile list with PDP context auth settings: %s", error->message);
+
+ /* complete successfully anyway */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+profile_manager_parent_list_profiles_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_slice_new0 (ListProfilesContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free);
+
+ if (!iface_modem_3gpp_profile_manager_parent->list_profiles_finish (self, res, &ctx->profiles, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!ctx->profiles) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "%IPDPCFG?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)profile_manager_ipdpcfg_query_ready,
+ task);
+}
+
+static void
+modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ iface_modem_3gpp_profile_manager_parent->list_profiles (
+ self,
+ (GAsyncReadyCallback)profile_manager_parent_list_profiles_ready,
+ task);
+}
+
+typedef struct {
+ gboolean new_id;
+ gint min_profile_id;
+ gint max_profile_id;
+ GEqualFunc apn_cmp;
+ MM3gppProfileCmpFlags profile_cmp_flags;
+} CheckFormatContext;
+
+static void
+check_format_context_free (CheckFormatContext *ctx)
+{
+ g_slice_free (CheckFormatContext, ctx);
+}
+
+static gboolean
+modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *new_id,
+ gint *min_profile_id,
+ gint *max_profile_id,
+ GEqualFunc *apn_cmp,
+ MM3gppProfileCmpFlags *profile_cmp_flags,
+ GError **error)
+{
+ CheckFormatContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (new_id)
+ *new_id = ctx->new_id;
+ if (min_profile_id)
+ *min_profile_id = (gint) ctx->min_profile_id;
+ if (max_profile_id)
+ *max_profile_id = (gint) ctx->max_profile_id;
+ if (apn_cmp)
+ *apn_cmp = ctx->apn_cmp;
+ if (profile_cmp_flags)
+ *profile_cmp_flags = ctx->profile_cmp_flags;
+ return TRUE;
+}
+
+static void
+profile_manager_parent_check_format_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ CheckFormatContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!iface_modem_3gpp_profile_manager_parent->check_format_finish (self,
+ res,
+ &ctx->new_id,
+ &ctx->min_profile_id,
+ &ctx->max_profile_id,
+ &ctx->apn_cmp,
+ &ctx->profile_cmp_flags,
+ &error)) {
+ g_task_return_error (task, error);
+ } else {
+ /* the icera implementation supports AUTH, so unset that cmp flag */
+ ctx->profile_cmp_flags &= ~MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH;
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self,
+ MMBearerIpFamily ip_type,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CheckFormatContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (CheckFormatContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)check_format_context_free);
+
+ iface_modem_3gpp_profile_manager_parent->check_format (
+ self,
+ ip_type,
+ (GAsyncReadyCallback)profile_manager_parent_check_format_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Deactivate profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+deactivate_profile_ipdpact_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ g_autofree gchar *cmd = NULL;
+ gint profile_id;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ mm_obj_dbg (self, "deactivating profile '%d'...", profile_id);
+
+ cmd = g_strdup_printf ("%%IPDPACT=%d,0", profile_id);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ cmd,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback)deactivate_profile_ipdpact_set_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set profile (3GPP profile management interface) */
+
+#define IPDPCFG_SET_MAX_ATTEMPTS 3
+#define IPDPCFG_SET_RETRY_TIMEOUT_SECS 1
+
+typedef struct {
+ MM3gppProfile *profile;
+ gchar *cmd;
+ gint profile_id;
+ guint n_attempts;
+} StoreProfileContext;
+
+static void
+store_profile_context_free (StoreProfileContext *ctx)
+{
+ g_free (ctx->cmd);
+ g_clear_object (&ctx->profile);
+ g_slice_free (StoreProfileContext, ctx);
+}
+
+static gint
+modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ StoreProfileContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return MM_3GPP_PROFILE_ID_UNKNOWN;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ return ctx->profile_id;
+}
+
+static void profile_manager_store_profile_auth_settings (GTask *task);
+
+static gboolean
+profile_manager_ipdpcfg_set_retry (GTask *task)
+{
+ profile_manager_store_profile_auth_settings (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+profile_manager_ipdpcfg_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ StoreProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ /* Retry configuring the context. It sometimes fails with a 583
+ * error ["a profile (CID) is currently active"] if a connect
+ * is attempted too soon after a disconnect. */
+ if (ctx->n_attempts < IPDPCFG_SET_MAX_ATTEMPTS) {
+ mm_obj_dbg (self, "couldn't store auth settings in profile '%d': %s; retrying...",
+ ctx->profile_id, error->message);
+ g_timeout_add_seconds (IPDPCFG_SET_RETRY_TIMEOUT_SECS, (GSourceFunc)profile_manager_ipdpcfg_set_retry, task);
+ return;
+ }
+ g_task_return_error (task, g_steal_pointer (&error));
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+profile_manager_store_profile_auth_settings (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ StoreProfileContext *ctx;
+ g_autofree gchar *cmd = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!ctx->cmd) {
+ const gchar *user;
+ const gchar *password;
+ MMBearerAllowedAuth allowed_auth;
+
+ user = mm_3gpp_profile_get_user (ctx->profile);
+ password = mm_3gpp_profile_get_password (ctx->profile);
+ allowed_auth = mm_3gpp_profile_get_allowed_auth (ctx->profile);
+
+ /* Both user and password are required; otherwise firmware returns an error */
+ if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) {
+ mm_obj_dbg (self, "not using authentication");
+ ctx->cmd = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->profile_id);
+ } else {
+ g_autofree gchar *quoted_user = NULL;
+ g_autofree gchar *quoted_password = NULL;
+ guint icera_auth;
+
+ if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
+ mm_obj_dbg (self, "using default (CHAP) authentication method");
+ icera_auth = 2;
+ } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) {
+ mm_obj_dbg (self, "using CHAP authentication method");
+ icera_auth = 2;
+ } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
+ mm_obj_dbg (self, "using PAP authentication method");
+ icera_auth = 1;
+ } else {
+ g_autofree gchar *str = NULL;
+
+ str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot use any of the specified authentication methods (%s)",
+ str);
+ g_object_unref (task);
+ return;
+ }
+
+ quoted_user = mm_port_serial_at_quote_string (user);
+ quoted_password = mm_port_serial_at_quote_string (password);
+ ctx->cmd = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s",
+ ctx->profile_id,
+ icera_auth,
+ quoted_user,
+ quoted_password);
+ }
+ }
+
+ ctx->n_attempts++;
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ ctx->cmd,
+ 6,
+ FALSE,
+ (GAsyncReadyCallback)profile_manager_ipdpcfg_set_ready,
+ task);
+}
+
+static void
+profile_manager_parent_store_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (iface_modem_3gpp_profile_manager_parent->store_profile_finish (self, res, &error) == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ profile_manager_store_profile_auth_settings (task);
+}
+
+static void
+modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ StoreProfileContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (StoreProfileContext);
+ ctx->profile = g_object_ref (profile);
+ ctx->profile_id = mm_3gpp_profile_get_profile_id (ctx->profile);
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) store_profile_context_free);
+
+ iface_modem_3gpp_profile_manager_parent->store_profile (
+ self,
+ profile,
+ (GAsyncReadyCallback)profile_manager_parent_store_profile_ready,
+ task);
+}
+
+/*****************************************************************************/
/* Load network time (Time interface) */
static gchar *
@@ -1801,11 +2147,7 @@ modem_time_check_support_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
- /* We assume Icera devices always support *TLTS, since they appear
- * to return ERROR if the modem is not powered up, and thus we cannot
- * check for *TLTS support during modem initialization.
- */
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -1813,15 +2155,16 @@ modem_time_check_support (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_time_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ /* We assume Icera devices always support *TLTS, since they appear
+ * to return ERROR if the modem is not powered up, and thus we cannot
+ * check for *TLTS support during modem initialization.
+ */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -1852,6 +2195,9 @@ mm_broadband_modem_icera_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (AT) or Icera bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -1971,6 +2317,23 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
}
static void
+iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
+{
+ iface_modem_3gpp_profile_manager_parent = g_type_interface_peek_parent (iface);
+
+ iface->list_profiles = modem_3gpp_profile_manager_list_profiles;
+ iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish;
+ iface->check_format = modem_3gpp_profile_manager_check_format;
+ iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish;
+ /* note: the parent check_activated_profile() implementation using +CGACT? seems to
+ * be perfectly valid. */
+ iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile;
+ iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish;
+ iface->store_profile = modem_3gpp_profile_manager_store_profile;
+ iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish;
+}
+
+static void
iface_modem_time_init (MMIfaceModemTime *iface)
{
iface->check_support = modem_time_check_support;
diff --git a/plugins/icera/mm-broadband-modem-icera.h b/plugins/icera/mm-broadband-modem-icera.h
index 7826c661..cb88aaf5 100644
--- a/plugins/icera/mm-broadband-modem-icera.h
+++ b/plugins/icera/mm-broadband-modem-icera.h
@@ -42,8 +42,10 @@ struct _MMBroadbandModemIceraClass{
MMBroadbandModemClass parent;
};
+G_MODULE_EXPORT
GType mm_broadband_modem_icera_get_type (void);
+G_MODULE_EXPORT
MMBroadbandModemIcera *mm_broadband_modem_icera_new (const gchar *device,
const gchar **drivers,
const gchar *plugin,
diff --git a/plugins/icera/mm-modem-helpers-icera.c b/plugins/icera/mm-modem-helpers-icera.c
index f9aa6125..da1cd873 100644
--- a/plugins/icera/mm-modem-helpers-icera.c
+++ b/plugins/icera/mm-modem-helpers-icera.c
@@ -25,6 +25,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-icera.h"
@@ -145,43 +146,53 @@ parse_ipdpaddr_v6 (const gchar **items, guint num_items, GError **error)
if (num_items < 12)
return NULL;
+ /* No IPv6 IP and no IPv6 DNS, return NULL without error. */
+ if (g_strcmp0 (items[9], "::") == 0 && g_strcmp0 (items[11], "::") == 0)
+ return NULL;
+
+ config = mm_bearer_ip_config_new ();
+
/* It appears that for IPv6 %IPDPADDR returns only the expected
* link-local address and a DNS address, and that to retrieve the
* default router, extra DNS, and search domains, the host must listen
* for IPv6 Router Advertisements on the net port.
*/
-
- config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
-
- /* IP address and prefix */
- if (inet_pton (AF_INET6, items[9], &tmp6) != 1 ||
+ if (g_strcmp0 (items[9], "::") != 0) {
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+ /* IP address and prefix */
+ if (inet_pton (AF_INET6, items[9], &tmp6) != 1 ||
IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Couldn't parse IPv6 address '%s'", items[9]);
- goto error;
- }
- mm_bearer_ip_config_set_address (config, items[9]);
- mm_bearer_ip_config_set_prefix (config, 64);
-
- /* If the address is a link-local one, then SLAAC or DHCP must be used
- * to get the real prefix and address. Change the method to DHCP to
- * indicate this to clients.
- */
- if (IN6_IS_ADDR_LINKLOCAL (&tmp6))
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse IPv6 address '%s'", items[9]);
+ goto error;
+ }
+ mm_bearer_ip_config_set_address (config, items[9]);
+ mm_bearer_ip_config_set_prefix (config, 64);
+
+ /* If the address is a link-local one, then SLAAC or DHCP must be used
+ * to get the real prefix and address. Change the method to DHCP to
+ * indicate this to clients.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL (&tmp6))
+ mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
+ } else {
+ /* No IPv6 given, but DNS will be available, try with DHCP */
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
+ }
/* DNS server */
- memset (&tmp6, 0, sizeof (tmp6));
- if (inet_pton (AF_INET6, items[11], &tmp6) == 1 &&
+ if (g_strcmp0 (items[11], "::") != 0) {
+ memset (&tmp6, 0, sizeof (tmp6));
+ if (inet_pton (AF_INET6, items[11], &tmp6) == 1 &&
!IN6_IS_ADDR_UNSPECIFIED (&tmp6)) {
- dns[0] = items[11];
- dns[1] = NULL;
- mm_bearer_ip_config_set_dns (config, (const gchar **) dns);
- } else {
- g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Couldn't parse DNS address '%s'", items[11]);
- goto error;
+ dns[0] = items[11];
+ dns[1] = NULL;
+ mm_bearer_ip_config_set_dns (config, (const gchar **) dns);
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse DNS address '%s'", items[11]);
+ goto error;
+ }
}
return config;
@@ -205,8 +216,8 @@ mm_icera_parse_ipdpaddr_response (const gchar *response,
GError *local = NULL;
gboolean success = FALSE;
char **items;
- guint num_items, i, first_free;
- gint num;
+ guint num_items, i;
+ guint num;
g_return_val_if_fail (out_ip4_config, FALSE);
g_return_val_if_fail (out_ip6_config, FALSE);
@@ -223,14 +234,14 @@ mm_icera_parse_ipdpaddr_response (const gchar *response,
* Sierra USB305: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0
* K3805-Z: %IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 255.255.255.0, 21.93.217.10,
* Nokia 21M: %IPDPADDR: 2, 33.196.7.127, 33.196.7.128, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0, 255.0.0.0, 33.196.7.128, fe80::f:9135:5901, ::, fd00:976a::9, ::, ::, ::, ::, ::
- * Nokia 21M: %IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::
+ * Nokia 21M: %IPDPADDR: 3, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::2e:437b:7901, ::, fd00:976a::9, ::, ::, ::, ::, ::
*/
response = mm_strip_tag (response, IPDPADDR_TAG);
items = g_strsplit_set (response, ",", 0);
/* Strip any spaces on elements; inet_pton() doesn't like them */
num_items = g_strv_length (items);
- for (i = 0, first_free = 0; i < num_items; i++)
+ for (i = 0; i < num_items; i++)
items[i] = g_strstrip (items[i]);
if (num_items < 7) {
@@ -242,7 +253,7 @@ mm_icera_parse_ipdpaddr_response (const gchar *response,
}
/* Validate context ID */
- if (!mm_get_int_from_str (items[0], &num) ||
+ if (!mm_get_uint_from_str (items[0], &num) ||
num != expected_cid) {
g_set_error (error,
MM_CORE_ERROR,
@@ -275,3 +286,104 @@ out:
return success;
}
+/*****************************************************************************/
+/* %IPDPCFG? response parser.
+ * Modifies the input list of profiles in place
+ *
+ * AT%IPDPCFG?
+ * %IPDPCFG: 1,0,0,,,0
+ * %IPDPCFG: 2,0,0,,,0
+ * %IPDPCFG: 3,0,2,"user","pass",0
+ * %IPDPCFG: 4,0,0,,,0
+ * OK
+ */
+
+gboolean
+mm_icera_parse_ipdpcfg_query_response (const gchar *str,
+ GList *profiles,
+ gpointer log_object,
+ GError **error)
+{
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GError) inner_error = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ guint n_updates = 0;
+ guint n_profiles;
+
+ n_profiles = g_list_length (profiles);
+
+ r = g_regex_new ("%IPDPCFG:\\s*(\\d+),(\\d+),(\\d+),([^,]*),([^,]*),(\\d+)",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ /* Parse the results */
+ while (g_match_info_matches (match_info)) {
+ guint cid;
+ guint auth;
+ MMBearerAllowedAuth allowed_auth;
+ g_autofree gchar *user = NULL;
+ g_autofree gchar *password = NULL;
+ GList *l;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &cid)) {
+ mm_obj_warn (log_object, "couldn't parse cid from %%IPDPCFG line");
+ goto next;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 3, &auth)) {
+ mm_obj_warn (log_object, "couldn't parse auth from %%IPDPCFG line");
+ goto next;
+ }
+
+ switch (auth) {
+ case 0:
+ allowed_auth = MM_BEARER_ALLOWED_AUTH_NONE;
+ break;
+ case 1:
+ allowed_auth = MM_BEARER_ALLOWED_AUTH_PAP;
+ break;
+ case 2:
+ allowed_auth = MM_BEARER_ALLOWED_AUTH_CHAP;
+ break;
+ default:
+ mm_obj_warn (log_object, "unexpected icera auth setting: %u", auth);
+ goto next;
+ }
+
+ user = mm_get_string_unquoted_from_match_info (match_info, 4);
+ password = mm_get_string_unquoted_from_match_info (match_info, 5);
+
+ mm_obj_dbg (log_object, "found icera auth settings for profile with id '%u'", cid);
+
+ /* Find profile and update in place */
+ for (l = profiles; l; l = g_list_next (l)) {
+ MM3gppProfile *iter = l->data;
+
+ if (mm_3gpp_profile_get_profile_id (iter) == (gint) cid) {
+ n_updates++;
+ mm_3gpp_profile_set_allowed_auth (iter, allowed_auth);
+ mm_3gpp_profile_set_user (iter, user);
+ mm_3gpp_profile_set_password (iter, password);
+ break;
+ }
+ }
+ if (!l)
+ mm_obj_warn (log_object, "couldn't update auth settings in profile with id '%d': not found", cid);
+
+ next:
+ g_match_info_next (match_info, NULL);
+ }
+
+ if (n_updates != n_profiles)
+ mm_obj_warn (log_object, "couldn't update auth settings in all profiles: %u/%u updated",
+ n_updates, n_profiles);
+
+ return TRUE;
+}
diff --git a/plugins/icera/mm-modem-helpers-icera.h b/plugins/icera/mm-modem-helpers-icera.h
index 8a7a87a2..fa4f9016 100644
--- a/plugins/icera/mm-modem-helpers-icera.h
+++ b/plugins/icera/mm-modem-helpers-icera.h
@@ -25,4 +25,10 @@ gboolean mm_icera_parse_ipdpaddr_response (const gchar *response,
MMBearerIpConfig **out_ip6_config,
GError **error);
+/* %IPDPCFG? response parser */
+gboolean mm_icera_parse_ipdpcfg_query_response (const gchar *response,
+ GList *profiles,
+ gpointer log_object,
+ GError **error);
+
#endif /* MM_MODEM_HELPERS_HUAWEI_H */
diff --git a/plugins/icera/mm-shared.c b/plugins/icera/mm-shared.c
new file mode 100644
index 00000000..735d61bf
--- /dev/null
+++ b/plugins/icera/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Icera)
diff --git a/plugins/icera/tests/test-modem-helpers-icera.c b/plugins/icera/tests/test-modem-helpers-icera.c
index 8eaff4cb..dceb1e2b 100644
--- a/plugins/icera/tests/test-modem-helpers-icera.c
+++ b/plugins/icera/tests/test-modem-helpers-icera.c
@@ -24,7 +24,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-test.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-icera.h"
@@ -49,7 +49,7 @@ typedef struct {
static const IpdpaddrTest ipdpaddr_tests[] = {
/* Sierra USB305 */
- { "%IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0\r\n",
+ { "%IPDPADDR: 2, 21.93.217.11, 21.93.217.10, 10.177.0.34, 10.161.171.220, 0.0.0.0, 0.0.0.0\r\n",
2, "21.93.217.11", 32, "21.93.217.10", "10.177.0.34", "10.161.171.220",
NULL, NULL },
@@ -87,6 +87,18 @@ static const IpdpaddrTest ipdpaddr_tests[] = {
2, "188.150.116.13", 16, "188.150.116.14", "188.149.250.16", NULL,
"fe80::1:e414:eb01", "2a00:e18:0:3::6" },
+ { "%IPDPADDR: 1, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, fe80::1f:fad1:4c01, ::, 2001:4600:4:fff::54, 2001:4600:4:1fff::54, ::, ::, ::, ::\r\n",
+ 1, NULL, 0, NULL, NULL, NULL,
+ "fe80::1f:fad1:4c01", "2001:4600:4:fff::54" },
+
+ { "%IPDPADDR: 1, 46.157.76.179, 46.157.76.180, 193.213.112.4, 130.67.15.198, 0.0.0.0, 0.0.0.0, 255.0.0.0, 46.157.76.180, ::, ::, ::, ::, ::, ::, ::, ::\r\n",
+ 1, "46.157.76.179", 32, "46.157.76.180", "193.213.112.4", "130.67.15.198",
+ NULL, NULL },
+
+ { "%IPDPADDR: 1, 0.0.0.0, 0.0.0.0, 193.213.112.4, 130.67.15.198, 0.0.0.0, 0.0.0.0, 0.0.0.0, 0.0.0.0, ::, ::, 2001:4600:4:fff::52, 2001:4600:4:1fff::52, ::, ::, ::, ::",
+ 1, NULL, 0, NULL, NULL, NULL,
+ NULL, "2001:4600:4:fff::52" },
+
{ NULL }
};
@@ -129,63 +141,122 @@ test_ipdpaddr (void)
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv4_dns1);
g_assert_cmpstr (dns[1], ==, ipdpaddr_tests[i].ipv4_dns2);
+ g_object_unref (ipv4);
} else
g_assert (ipv4 == NULL);
/* IPv6 */
- if (ipdpaddr_tests[i].ipv6_addr) {
+ if (ipdpaddr_tests[i].ipv6_addr || ipdpaddr_tests[i].ipv6_dns1) {
struct in6_addr a6;
g_assert (ipv6);
- g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, ipdpaddr_tests[i].ipv6_addr);
- g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64);
+ if (ipdpaddr_tests[i].ipv6_addr) {
+ g_assert_cmpstr (mm_bearer_ip_config_get_address (ipv6), ==, ipdpaddr_tests[i].ipv6_addr);
+ g_assert_cmpint (mm_bearer_ip_config_get_prefix (ipv6), ==, 64);
- g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6));
- if (IN6_IS_ADDR_LINKLOCAL (&a6))
+ g_assert (inet_pton (AF_INET6, mm_bearer_ip_config_get_address (ipv6), &a6));
+ if (IN6_IS_ADDR_LINKLOCAL (&a6))
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP);
+ else
+ g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC);
+ } else
g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_DHCP);
- else
- g_assert_cmpint (mm_bearer_ip_config_get_method (ipv6), ==, MM_BEARER_IP_METHOD_STATIC);
dns = mm_bearer_ip_config_get_dns (ipv6);
g_assert (dns);
dnslen = g_strv_length ((gchar **) dns);
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, ipdpaddr_tests[i].ipv6_dns1);
+ g_object_unref (ipv6);
} else
g_assert (ipv6 == NULL);
}
}
/*****************************************************************************/
+/* Test %IPDPCFG responses */
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+static void
+test_ipdpcfg (void)
{
-#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
+ MM3gppProfile *profile;
+ GList *profiles = NULL;
+ GList *l;
+ GError *error = NULL;
+ gboolean result;
+ gboolean cid_1_validated = FALSE;
+ gboolean cid_2_validated = FALSE;
+ gboolean cid_5_validated = FALSE;
+ const gchar *response =
+ "%IPDPCFG: 1,0,0,,,0\r\n"
+ "%IPDPCFG: 2,0,1,\"aaaa\",\"bbbbb\",0\r\n"
+ "%IPDPCFG: 5,0,2,\"user\",\"pass\",0"; /* last line without CRLF */
+
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, 1);
+ mm_3gpp_profile_set_apn (profile, "internet");
+ mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4);
+ profiles = g_list_append (profiles, profile);
+
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, 2);
+ mm_3gpp_profile_set_apn (profile, "internet2");
+ mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV4V6);
+ profiles = g_list_append (profiles, profile);
+
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, 5);
+ mm_3gpp_profile_set_apn (profile, "internet3");
+ mm_3gpp_profile_set_ip_type (profile, MM_BEARER_IP_FAMILY_IPV6);
+ profiles = g_list_append (profiles, profile);
+
+ result = mm_icera_parse_ipdpcfg_query_response (response, profiles, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ for (l = profiles; l; l = g_list_next (l)) {
+ MM3gppProfile *iter = l->data;
+
+ switch (mm_3gpp_profile_get_profile_id (iter)) {
+ case 1:
+ cid_1_validated = TRUE;
+ g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_NONE);
+ g_assert (!mm_3gpp_profile_get_user (iter));
+ g_assert (!mm_3gpp_profile_get_password (iter));
+ break;
+ case 2:
+ cid_2_validated = TRUE;
+ g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_PAP);
+ g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "aaaa");
+ g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "bbbbb");
+ break;
+ case 5:
+ cid_5_validated = TRUE;
+ g_assert_cmpuint (mm_3gpp_profile_get_allowed_auth (iter), ==, MM_BEARER_ALLOWED_AUTH_CHAP);
+ g_assert_cmpstr (mm_3gpp_profile_get_user (iter), ==, "user");
+ g_assert_cmpstr (mm_3gpp_profile_get_password (iter), ==, "pass");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ g_assert (cid_1_validated);
+ g_assert (cid_2_validated);
+ g_assert (cid_5_validated);
+
+ g_list_free_full (profiles, (GDestroyNotify)g_object_unref);
}
+/*****************************************************************************/
+
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/icera/ipdpaddr", test_ipdpaddr);
+ g_test_add_func ("/MM/icera/ipdpcfg", test_ipdpcfg);
return g_test_run ();
}
diff --git a/plugins/iridium/mm-bearer-iridium.c b/plugins/iridium/mm-bearer-iridium.c
index fd54647d..b6df8e8d 100644
--- a/plugins/iridium/mm-bearer-iridium.c
+++ b/plugins/iridium/mm-bearer-iridium.c
@@ -26,35 +26,27 @@
#include "mm-bearer-iridium.h"
#include "mm-base-modem-at.h"
-#include "mm-log.h"
/* Allow up to 200s to get a proper IP connection */
#define BEARER_IRIDIUM_IP_TIMEOUT_DEFAULT 200
-G_DEFINE_TYPE (MMBearerIridium, mm_bearer_iridium, MM_TYPE_BASE_BEARER);
+G_DEFINE_TYPE (MMBearerIridium, mm_bearer_iridium, MM_TYPE_BASE_BEARER)
/*****************************************************************************/
/* Connect */
typedef struct {
- MMBearerIridium *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MMPortSerialAt *primary;
GError *saved_error;
} ConnectContext;
static void
-connect_context_complete_and_free (ConnectContext *ctx)
+connect_context_free (ConnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->saved_error)
g_error_free (ctx->saved_error);
if (ctx->primary)
g_object_unref (ctx->primary);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -63,58 +55,50 @@ connect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
connect_report_ready (MMBaseModem *modem,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ ConnectContext *ctx;
const gchar *result;
/* If cancelled, complete */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Connection setup operation has been cancelled");
- connect_context_complete_and_free (ctx);
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* If we got a proper extended reply, build the new error to be set */
result = mm_base_modem_at_command_full_finish (modem, res, NULL);
- if (result &&
- g_str_has_prefix (result, "+CEER: ") &&
- strlen (result) > 7) {
- g_simple_async_result_set_error (ctx->result,
- ctx->saved_error->domain,
- ctx->saved_error->code,
- "%s", &result[7]);
- g_error_free (ctx->saved_error);
+ if (result && g_str_has_prefix (result, "+CEER: ") && strlen (result) > 7) {
+ g_task_return_new_error (task,
+ ctx->saved_error->domain,
+ ctx->saved_error->code,
+ "%s", &result[7]);
+ } else {
+ /* Otherwise, take the original error as it was */
+ g_task_return_error (task, ctx->saved_error);
ctx->saved_error = NULL;
- connect_context_complete_and_free (ctx);
- return;
}
-
- /* Take the original error as it was */
- g_simple_async_result_take_error (ctx->result,
- ctx->saved_error);
- ctx->saved_error = NULL;
- connect_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
dial_ready (MMBaseModem *modem,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ ConnectContext *ctx;
MMBearerIpConfig *config;
+ ctx = g_task_get_task_data (task);
+
/* DO NOT check for cancellable here. If we got here without errors, the
* bearer is really connected and therefore we need to reflect that in
* the state machine. */
@@ -130,7 +114,7 @@ dial_ready (MMBaseModem *modem,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)connect_report_ready,
- ctx);
+ task);
return;
}
@@ -141,38 +125,36 @@ dial_ready (MMBaseModem *modem,
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP);
- /* Set operation result */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
+ /* Return operation result */
+ g_task_return_pointer (
+ task,
mm_bearer_connect_result_new (MM_PORT (ctx->primary), config, NULL),
(GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
g_object_unref (config);
-
- connect_context_complete_and_free (ctx);
}
static void
service_type_ready (MMBaseModem *modem,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ ConnectContext *ctx;
GError *error = NULL;
/* If cancelled, complete */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Connection setup operation has been cancelled");
- connect_context_complete_and_free (ctx);
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Errors setting the service type will be critical */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -183,12 +165,12 @@ service_type_ready (MMBaseModem *modem,
modem,
ctx->primary,
"ATDT008816000025",
- 60,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)dial_ready,
- ctx);
+ task);
}
static void
@@ -198,8 +180,18 @@ connect (MMBaseBearer *self,
gpointer user_data)
{
ConnectContext *ctx;
+ GTask *task;
MMBaseModem *modem = NULL;
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (mm_bearer_properties_get_multiplex (mm_base_bearer_peek_config (self)) == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Multiplex support not available");
+ g_object_unref (task);
+ return;
+ }
+
g_object_get (self,
MM_BASE_BEARER_MODEM, &modem,
NULL);
@@ -210,13 +202,8 @@ connect (MMBaseBearer *self,
/* In this context, we only keep the stuff we'll need later */
ctx = g_new0 (ConnectContext, 1);
- ctx->self = g_object_ref (self);
ctx->primary = mm_base_modem_get_port_primary (modem);
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- connect);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) connect_context_free);
/* Bearer service type set to 9600bps (V.110), which behaves better than the
* default 9600bps (V.32). */
@@ -229,7 +216,7 @@ connect (MMBaseBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)service_type_ready,
- ctx);
+ task);
g_object_unref (modem);
}
@@ -270,4 +257,10 @@ mm_bearer_iridium_class_init (MMBearerIridiumClass *klass)
/* Virtual methods */
base_bearer_class->connect = connect;
base_bearer_class->connect_finish = connect_finish;
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = NULL;
+ base_bearer_class->reload_connection_status_finish = NULL;
+#endif
}
diff --git a/plugins/iridium/mm-broadband-modem-iridium.c b/plugins/iridium/mm-broadband-modem-iridium.c
index c64e8d2d..681d9123 100644
--- a/plugins/iridium/mm-broadband-modem-iridium.c
+++ b/plugins/iridium/mm-broadband-modem-iridium.c
@@ -23,7 +23,7 @@
#include <ctype.h>
#include "ModemManager.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
@@ -41,43 +41,54 @@ static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemIridium, mm_broadband_modem_iridium, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init));
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init))
/*****************************************************************************/
-/* Operator Code and Name loading (3GPP interface) */
+/* Operator Code loading (3GPP interface) */
static gchar *
load_operator_code_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_operator_code (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
/* Only "90103" operator code is assumed */
- return g_strdup ("90103");
+ g_task_return_pointer (task, g_strdup ("90103"), g_free);
+ g_object_unref (task);
}
+/*****************************************************************************/
+/* Operator Name loading (3GPP interface) */
+
static gchar *
load_operator_name_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- /* Only "IRIDIUM" operator name is assumed */
- return g_strdup ("IRIDIUM");
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-load_operator_name_or_code (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_operator_name (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_operator_name_or_code);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ /* Only "IRIDIUM" operator name is assumed */
+ g_task_return_pointer (task, g_strdup ("IRIDIUM"), g_free);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -168,25 +179,24 @@ setup_flow_control_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_flow_control_ready (MMBroadbandModemIridium *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
/* Let the error be critical. We DO need RTS/CTS in order to have
* proper modem disabling. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -194,13 +204,6 @@ setup_flow_control (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- setup_flow_control);
-
/* Enable RTS/CTS flow control.
* Other available values:
* AT&K0: Disable flow control
@@ -208,12 +211,13 @@ setup_flow_control (MMIfaceModem *self,
* AT&K4: XOFF/XON
* AT&K6: Both RTS/CTS and XOFF/XON
*/
+ g_object_set (self, MM_BROADBAND_MODEM_FLOW_CONTROL, MM_FLOW_CONTROL_RTS_CTS, NULL);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"&K3",
3,
FALSE,
(GAsyncReadyCallback)setup_flow_control_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -224,7 +228,7 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -232,14 +236,9 @@ load_supported_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
GArray *combinations;
MMModemModeCombination mode;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes);
+ GTask *task;
/* Build list of combinations */
combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
@@ -249,9 +248,9 @@ load_supported_modes (MMIfaceModem *self,
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (combinations, mode);
- g_simple_async_result_set_op_res_gpointer (result, combinations, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -285,12 +284,7 @@ create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New Iridium bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -300,20 +294,14 @@ create_bearer (MMIfaceModem *self,
gpointer user_data)
{
MMBaseBearer *bearer;
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- create_bearer);
- mm_dbg ("Creating Iridium bearer...");
+ mm_obj_dbg (self, "creating Iridium bearer...");
bearer = mm_bearer_iridium_new (MM_BROADBAND_MODEM_IRIDIUM (self),
properties);
- g_simple_async_result_set_op_res_gpointer (result,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -337,7 +325,7 @@ setup_ports (MMBroadbandModem *self)
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_iridium_parent_class)->setup_ports (self);
/* Set 9600 baudrate by default in the AT port */
- mm_dbg ("Baudrate will be set to 9600 bps...");
+ mm_obj_dbg (self, "baudrate will be set to 9600 bps...");
primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
if (!primary)
return;
@@ -363,6 +351,9 @@ mm_broadband_modem_iridium_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Iridium bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
/* Allow only up to 3 consecutive timeouts in the serial port */
MM_BASE_MODEM_MAX_TIMEOUTS, 3,
/* Only CS network is supported by the Iridium modem */
@@ -409,9 +400,9 @@ static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
/* Fixed operator code and name to be reported */
- iface->load_operator_name = load_operator_name_or_code;
+ iface->load_operator_name = load_operator_name;
iface->load_operator_name_finish = load_operator_name_finish;
- iface->load_operator_code = load_operator_name_or_code;
+ iface->load_operator_code = load_operator_code;
iface->load_operator_code_finish = load_operator_code_finish;
/* Don't try to scan networks with AT+COPS=?.
diff --git a/plugins/iridium/mm-plugin-iridium.c b/plugins/iridium/mm-plugin-iridium.c
index 3f504a24..582d86a3 100644
--- a/plugins/iridium/mm-plugin-iridium.c
+++ b/plugins/iridium/mm-plugin-iridium.c
@@ -29,23 +29,22 @@
#include "mm-plugin-iridium.h"
#include "mm-broadband-modem-iridium.h"
#include "mm-private-boxed-types.h"
-#include "mm-log.h"
G_DEFINE_TYPE (MMPluginIridium, mm_plugin_iridium, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_iridium_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_iridium_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -61,12 +60,12 @@ mm_plugin_create (void)
static const guint16 vendor_ids[] = { 0x1edd, 0 };
static const gchar *vendor_strings[] = { "iridium", NULL };
/* Also support motorola-branded Iridium modems */
- static const mm_str_pair product_strings[] = {{"motorola", "satellite" },
+ static const mm_str_pair product_strings[] = {{(gchar *)"motorola", (gchar *)"satellite" },
{ NULL, NULL }};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_IRIDIUM,
- MM_PLUGIN_NAME, "Iridium",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, product_strings,
diff --git a/plugins/iridium/mm-sim-iridium.c b/plugins/iridium/mm-sim-iridium.c
index 1240e6af..3495039b 100644
--- a/plugins/iridium/mm-sim-iridium.c
+++ b/plugins/iridium/mm-sim-iridium.c
@@ -63,6 +63,7 @@ mm_sim_iridium_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
@@ -85,4 +86,10 @@ mm_sim_iridium_class_init (MMSimIridiumClass *klass)
base_sim_class->load_operator_identifier_finish = NULL;
base_sim_class->load_operator_name = NULL;
base_sim_class->load_operator_name_finish = NULL;
+
+ /* Skip managing preferred networks, not applicable to Iridium modems */
+ base_sim_class->load_preferred_networks = NULL;
+ base_sim_class->load_preferred_networks_finish = NULL;
+ base_sim_class->set_preferred_networks = NULL;
+ base_sim_class->set_preferred_networks_finish = NULL;
}
diff --git a/plugins/linktop/mm-broadband-modem-linktop.c b/plugins/linktop/mm-broadband-modem-linktop.c
index a23f5320..a83682c8 100644
--- a/plugins/linktop/mm-broadband-modem-linktop.c
+++ b/plugins/linktop/mm-broadband-modem-linktop.c
@@ -28,15 +28,11 @@
#include "ModemManager.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-iface-modem.h"
#include "mm-base-modem-at.h"
#include "mm-broadband-modem-linktop.h"
-
-#define LINKTOP_MODE_ANY 1
-#define LINKTOP_MODE_2G 5
-#define LINKTOP_MODE_3G 6
+#include "mm-modem-helpers-linktop.h"
static void iface_modem_init (MMIfaceModem *iface);
@@ -53,16 +49,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -72,9 +65,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -95,13 +87,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -113,10 +104,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -130,44 +118,13 @@ load_current_modes_finish (MMIfaceModem *self,
GError **error)
{
const gchar *response;
- const gchar *str;
- guint aux;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
- if (!response)
- return FALSE;
-
- str = mm_strip_tag (response, "CFUN:");
- if (!mm_get_uint_from_str (str, &aux)) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse CFUN? response: '%s'",
- response);
+ if (!response || !mm_linktop_parse_cfun_query_current_modes (response, allowed, error))
return FALSE;
- }
- switch (aux) {
- case LINKTOP_MODE_2G:
- *allowed = MM_MODEM_MODE_2G;
- *preferred = MM_MODEM_MODE_NONE;
- break;
-
- case LINKTOP_MODE_3G:
- *allowed = MM_MODEM_MODE_3G;
- *preferred = MM_MODEM_MODE_NONE;
- break;
-
- case LINKTOP_MODE_ANY:
- *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- *preferred = MM_MODEM_MODE_NONE;
- break;
-
- default:
- *allowed = MM_MODEM_MODE_ANY;
- *preferred = MM_MODEM_MODE_NONE;
- break;
- }
+ /* None preferred always */
+ *preferred = MM_MODEM_MODE_NONE;
return TRUE;
}
@@ -193,24 +150,24 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBroadbandModemLinktop *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -220,14 +177,11 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint linktop_mode = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (allowed == MM_MODEM_MODE_2G)
linktop_mode = LINKTOP_MODE_2G;
@@ -246,18 +200,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -268,7 +220,7 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -287,6 +239,9 @@ mm_broadband_modem_linktop_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports AT only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/linktop/mm-modem-helpers-linktop.c b/plugins/linktop/mm-modem-helpers-linktop.c
new file mode 100644
index 00000000..2ca46bb6
--- /dev/null
+++ b/plugins/linktop/mm-modem-helpers-linktop.c
@@ -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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-linktop.h"
+
+/*****************************************************************************/
+
+gboolean
+mm_linktop_parse_cfun_query_current_modes (const gchar *response,
+ MMModemMode *allowed,
+ GError **error)
+{
+ guint state;
+
+ g_assert (allowed);
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return FALSE;
+
+ switch (state) {
+ case LINKTOP_MODE_OFFLINE:
+ case LINKTOP_MODE_LOW_POWER:
+ *allowed = MM_MODEM_MODE_NONE;
+ return TRUE;
+ case LINKTOP_MODE_2G:
+ *allowed = MM_MODEM_MODE_2G;
+ return TRUE;
+ case LINKTOP_MODE_3G:
+ *allowed = MM_MODEM_MODE_3G;
+ return TRUE;
+ case LINKTOP_MODE_ANY:
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ return TRUE;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown linktop +CFUN current mode: %u", state);
+ return FALSE;
+ }
+}
diff --git a/plugins/linktop/mm-modem-helpers-linktop.h b/plugins/linktop/mm-modem-helpers-linktop.h
new file mode 100644
index 00000000..69fa7ee2
--- /dev/null
+++ b/plugins/linktop/mm-modem-helpers-linktop.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2016 Red Hat, Inc.
+ * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_MODEM_HELPERS_LINKTOP_H
+#define MM_MODEM_HELPERS_LINKTOP_H
+
+#include <glib.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+typedef enum {
+ LINKTOP_MODE_OFFLINE = 0,
+ LINKTOP_MODE_ANY = 1,
+ LINKTOP_MODE_LOW_POWER = 4,
+ LINKTOP_MODE_2G = 5,
+ LINKTOP_MODE_3G = 6,
+} MMLinktopMode;
+
+/* AT+CFUN? response parsers */
+gboolean mm_linktop_parse_cfun_query_current_modes (const gchar *response,
+ MMModemMode *allowed,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_LINKTOP_H */
diff --git a/plugins/linktop/mm-plugin-linktop.c b/plugins/linktop/mm-plugin-linktop.c
index 8919d4b5..d4b0bdc9 100644
--- a/plugins/linktop/mm-plugin-linktop.c
+++ b/plugins/linktop/mm-plugin-linktop.c
@@ -26,21 +26,21 @@
G_DEFINE_TYPE (MMPluginLinktop, mm_plugin_linktop, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_linktop_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_linktop_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -57,7 +57,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_LINKTOP,
- MM_PLUGIN_NAME, "Linktop",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/linktop/tests/test-modem-helpers-linktop.c b/plugins/linktop/tests/test-modem-helpers-linktop.c
new file mode 100644
index 00000000..07aa8378
--- /dev/null
+++ b/plugins/linktop/tests/test-modem-helpers-linktop.c
@@ -0,0 +1,71 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-linktop.h"
+
+/*****************************************************************************/
+
+typedef struct {
+ const gchar *str;
+ MMModemMode allowed;
+} CfunQueryCurrentModeTest;
+
+static const CfunQueryCurrentModeTest cfun_query_current_mode_tests[] = {
+ { "+CFUN: 0", MM_MODEM_MODE_NONE },
+ { "+CFUN: 1", MM_MODEM_MODE_2G | MM_MODEM_MODE_3G },
+ { "+CFUN: 4", MM_MODEM_MODE_NONE },
+ { "+CFUN: 5", MM_MODEM_MODE_2G },
+ { "+CFUN: 6", MM_MODEM_MODE_3G },
+};
+
+static void
+test_cfun_query_current_modes (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cfun_query_current_mode_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMModemMode allowed = MM_MODEM_MODE_NONE;
+
+ success = mm_linktop_parse_cfun_query_current_modes (cfun_query_current_mode_tests[i].str, &allowed, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cfun_query_current_mode_tests[i].allowed, ==, allowed);
+ }
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/linktop/cfun/query/current-modes", test_cfun_query_current_modes);
+
+ return g_test_run ();
+}
diff --git a/plugins/longcheer/77-mm-longcheer-port-types.rules b/plugins/longcheer/77-mm-longcheer-port-types.rules
index e91ba95f..e0fbe849 100644
--- a/plugins/longcheer/77-mm-longcheer-port-types.rules
+++ b/plugins/longcheer/77-mm-longcheer-port-types.rules
@@ -12,10 +12,7 @@
# cmser.inf lists the aux ports that may be either AT-capable or not but
# cannot be used for PPP.
-
-ACTION!="add|change|move", GOTO="mm_longcheer_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_longcheer_port_types_end"
-
+ACTION!="add|change|move|bind", GOTO="mm_longcheer_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1c9e", GOTO="mm_longcheer_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bbb", GOTO="mm_tamobile_vendorcheck"
GOTO="mm_longcheer_port_types_end"
@@ -23,156 +20,154 @@ GOTO="mm_longcheer_port_types_end"
LABEL="mm_longcheer_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="3197", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6060", ENV{ID_MM_LONGCHEER_TAGGED}="1"
# Alcatel One Touch X020
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="6061", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7001", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7002", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7101", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="7102", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8001", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="8002", ENV{ID_MM_LONGCHEER_TAGGED}="1"
# ChinaBird PL68
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9001", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9002", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9003", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9004", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9005", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9010", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9012", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9020", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9022", ENV{ID_MM_LONGCHEER_TAGGED}="1"
# Zoom products
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9602", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9603", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9604", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9605", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9606", ENV{ID_MM_LONGCHEER_TAGGED}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1c9e", ATTRS{idProduct}=="9607", ENV{ID_MM_LONGCHEER_TAGGED}="1"
GOTO="mm_longcheer_port_types_end"
-
LABEL="mm_tamobile_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Alcatel One Touch X060s
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_LONGCHEER_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_LONGCHEER_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{ID_MM_LONGCHEER_TAGGED}="1"
GOTO="mm_longcheer_port_types_end"
-
LABEL="mm_longcheer_port_types_end"
diff --git a/plugins/longcheer/mm-broadband-modem-longcheer.c b/plugins/longcheer/mm-broadband-modem-longcheer.c
index 27da1868..0926de2c 100644
--- a/plugins/longcheer/mm-broadband-modem-longcheer.c
+++ b/plugins/longcheer/mm-broadband-modem-longcheer.c
@@ -46,16 +46,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -65,9 +62,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -92,13 +88,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -110,10 +105,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -199,24 +191,23 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBroadbandModemLongcheer *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -226,14 +217,11 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint mododr = 0;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (allowed == MM_MODEM_MODE_2G)
mododr = 3;
@@ -254,18 +242,17 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
+
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -276,7 +263,7 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -307,7 +294,6 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading access technology (longcheer)...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+PSRAT",
3,
@@ -324,16 +310,13 @@ load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_unlock_retries_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -341,10 +324,8 @@ load_unlock_retries_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -361,18 +342,15 @@ load_unlock_retries_ready (MMBaseModem *self,
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- retries,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, retries, g_object_unref);
} else {
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid unlock retries response: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid unlock retries response: '%s'",
+ response);
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -386,10 +364,7 @@ load_unlock_retries (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_unlock_retries_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_unlock_retries));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -407,6 +382,9 @@ mm_broadband_modem_longcheer_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports AT only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/longcheer/mm-plugin-longcheer.c b/plugins/longcheer/mm-plugin-longcheer.c
index 37d22e7b..4331d21b 100644
--- a/plugins/longcheer/mm-plugin-longcheer.c
+++ b/plugins/longcheer/mm-plugin-longcheer.c
@@ -21,37 +21,28 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-plugin-longcheer.h"
#include "mm-broadband-modem-longcheer.h"
G_DEFINE_TYPE (MMPluginLongcheer, mm_plugin_longcheer, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
typedef struct {
- MMPortProbe *probe;
MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
guint retries;
} LongcheerCustomInitContext;
static void
-longcheer_custom_init_context_complete_and_free (LongcheerCustomInitContext *ctx)
+longcheer_custom_init_context_free (LongcheerCustomInitContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
g_slice_free (LongcheerCustomInitContext, ctx);
}
@@ -60,25 +51,27 @@ longcheer_custom_init_finish (MMPortProbe *probe,
GAsyncResult *result,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
-static void longcheer_custom_init_step (LongcheerCustomInitContext *ctx);
+static void longcheer_custom_init_step (GTask *task);
static void
gmr_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- LongcheerCustomInitContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
+ MMPortProbe *probe;
const gchar *p;
const gchar *response;
- GError *error = NULL;
- response = mm_port_serial_at_command_finish (port, res, &error);
- if (error) {
- /* Just retry... */
- longcheer_custom_init_step (ctx);
- goto out;
+ probe = g_task_get_source_object (task);
+
+ response = mm_port_serial_at_command_finish (port, res, NULL);
+ if (!response) {
+ mm_obj_dbg (probe, "retrying custom init step...");
+ longcheer_custom_init_step (task);
+ return;
}
/* Note the lack of a ':' on the GMR; the X200 doesn't send one */
@@ -90,42 +83,44 @@ gmr_ready (MMPortSerialAt *port,
* does not support since it uses a different chipset even though the
* X060s and the X200 have the exact same USB VID and PID.
*/
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "X200 cannot be supported with the Longcheer plugin");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "X200 cannot be supported with the Longcheer plugin");
} else {
- mm_dbg ("(Longcheer) device is not a X200");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ mm_obj_dbg (probe, "device is not a X200");
+ g_task_return_boolean (task, TRUE);
}
-
- longcheer_custom_init_context_complete_and_free (ctx);
-
-out:
- if (error)
- g_error_free (error);
+ g_object_unref (task);
}
static void
-longcheer_custom_init_step (LongcheerCustomInitContext *ctx)
+longcheer_custom_init_step (GTask *task)
{
+ MMPortProbe *probe;
+ LongcheerCustomInitContext *ctx;
+ GCancellable *cancellable;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ cancellable = g_task_get_cancellable (task);
+
/* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(Longcheer) no need to keep on running custom init in (%s)",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- longcheer_custom_init_context_complete_and_free (ctx);
+ if (g_cancellable_is_cancelled (cancellable)) {
+ mm_obj_dbg (probe, "no need to keep on running custom init");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
if (ctx->retries == 0) {
/* In this case, we need the AT command result to decide whether we can
* support this modem or not, so really fail if we didn't get it. */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get device revision information");
- longcheer_custom_init_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get device revision information");
+ g_object_unref (task);
return;
}
@@ -136,9 +131,9 @@ longcheer_custom_init_step (LongcheerCustomInitContext *ctx)
3,
FALSE, /* raw */
FALSE, /* allow_cached */
- ctx->cancellable,
+ cancellable,
(GAsyncReadyCallback)gmr_ready,
- ctx);
+ task);
}
static void
@@ -150,17 +145,19 @@ longcheer_custom_init (MMPortProbe *probe,
{
MMDevice *device;
LongcheerCustomInitContext *ctx;
+ GTask *task;
ctx = g_slice_new (LongcheerCustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- longcheer_custom_init);
- ctx->probe = g_object_ref (probe);
ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
ctx->retries = 3;
+ task = g_task_new (probe, cancellable, callback, user_data);
+ /* Clears the check-cancellable flag of the task as we expect the task to
+ * return TRUE upon cancellation.
+ */
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)longcheer_custom_init_context_free);
+
/* TCT/Alcatel in their infinite wisdom assigned the same USB VID/PID to
* the x060s (Longcheer firmware) and the x200 (something else) and thus
* we can't tell them apart via udev rules. Worse, they both report the
@@ -174,76 +171,32 @@ longcheer_custom_init (MMPortProbe *probe,
if (mm_device_get_vendor (device) != 0x1bbb ||
mm_device_get_product (device) != 0x0000) {
/* If not exactly this vendor/product, just skip */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- longcheer_custom_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- longcheer_custom_init_step (ctx);
+ longcheer_custom_init_step (task);
}
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_longcheer_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_longcheer_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- GUdevDevice *port;
- MMPortType ptype;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-
- port = mm_port_probe_peek_port (probe);
- ptype = mm_port_probe_get_port_type (probe);
-
- /* Look for port type hints; just probing can't distinguish which port should
- * be the data/primary port on these devices. We have to tag them based on
- * what the Windows .INF files say the port layout should be.
- */
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_MODEM")) {
- mm_dbg ("longcheer: AT port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_LONGCHEER_PORT_TYPE_AUX")) {
- mm_dbg ("longcheer: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else {
- /* If the port was tagged by the udev rules but isn't a primary or secondary,
- * then ignore it to guard against race conditions if a device just happens
- * to show up with more than two AT-capable ports.
- */
- ptype = MM_PORT_TYPE_IGNORED;
- }
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- ptype,
- pflags,
- error);
-}
-
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
@@ -266,7 +219,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_LONGCHEER,
- MM_PLUGIN_NAME, "Longcheer",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
@@ -286,5 +239,4 @@ mm_plugin_longcheer_class_init (MMPluginLongcheerClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/mbm/77-mm-ericsson-mbm.rules b/plugins/mbm/77-mm-ericsson-mbm.rules
index 64efd7c1..73e08692 100644
--- a/plugins/mbm/77-mm-ericsson-mbm.rules
+++ b/plugins/mbm/77-mm-ericsson-mbm.rules
@@ -1,18 +1,28 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_mbm_end"
-SUBSYSTEMS=="usb", GOTO="mm_mbm_check"
+ACTION!="add|change|move|bind", GOTO="mm_mbm_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bdb", GOTO="mm_mbm_ericsson_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0fce", GOTO="mm_mbm_sony_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="413c", GOTO="mm_mbm_dell_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="03f0", GOTO="mm_mbm_hp_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0930", GOTO="mm_mbm_toshiba_vendorcheck"
GOTO="mm_mbm_end"
-LABEL="mm_mbm_check"
+LABEL="mm_mbm_ericsson_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Ericsson F3507g
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1900", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1902", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F3607gw
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1904", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1905", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1906", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F3307
@@ -29,7 +39,9 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1049", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190b", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson F5521gw
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="190d", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1911", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson H5321gw
@@ -51,7 +63,9 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="191f", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1921", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson H5321gw
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1926", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1927", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1927", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C3304w
@@ -60,6 +74,13 @@ ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1928", ENV{ID_MM_ERICSSON_MBM}="1"
# Ericsson C5621 TFF
ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="1936", ENV{ID_MM_ERICSSON_MBM}="1"
+# Lenovo N5321gw
+ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="193e", ENV{ID_MM_ERICSSON_MBM}="1"
+
+GOTO="mm_mbm_end"
+
+LABEL="mm_mbm_sony_vendorcheck"
+
# Sony-Ericsson MD300
ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0cf", ENV{ID_MM_ERICSSON_MBM}="1"
@@ -69,23 +90,39 @@ ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d0e1", ENV{ID_MM_ERICSSON_MBM}="1"
# Sony-Ericsson MD400G
ATTRS{idVendor}=="0fce", ATTRS{idProduct}=="d103", ENV{ID_MM_ERICSSON_MBM}="1"
+GOTO="mm_mbm_end"
+
+LABEL="mm_mbm_dell_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
# Dell 5560
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818e", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818e", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
# Dell 5550
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818d", ENV{ID_MM_ERICSSON_MBM}="1"
# Dell 5530 HSDPA
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8147", ENV{ID_MM_ERICSSON_MBM}="1"
# Dell F3607gw
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8183", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="8184", ENV{ID_MM_ERICSSON_MBM}="1"
# Dell F3307
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818b", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="413c", ATTRS{idProduct}=="818c", ENV{ID_MM_ERICSSON_MBM}="1"
+GOTO="mm_mbm_end"
+
+LABEL="mm_mbm_hp_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
# HP hs2330 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="271d", ENV{ID_MM_ERICSSON_MBM}="1"
@@ -96,6 +133,7 @@ ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="261d", ENV{ID_MM_ERICSSON_MBM}="1"
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3a1d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP hs2350 Mobile Broadband Module
+ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3d1d", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="3d1d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP lc2000 Mobile Broadband Module
@@ -104,11 +142,19 @@ ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="301d", ENV{ID_MM_ERICSSON_MBM}="1"
# HP lc2010 Mobile Broadband Module
ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="2f1d", ENV{ID_MM_ERICSSON_MBM}="1"
+GOTO="mm_mbm_end"
+
+LABEL="mm_mbm_toshiba_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
# Toshiba
+ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130b", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba F3607gw
+ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="130c", ENV{ID_MM_ERICSSON_MBM}="1"
+ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{.MM_USBIFNUM}=="09", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1311", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba F3307
@@ -123,7 +169,6 @@ ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1314", ENV{ID_MM_ERICSSON_MBM}="1"
# Toshiba H5321gw
ATTRS{idVendor}=="0930", ATTRS{idProduct}=="1319", ENV{ID_MM_ERICSSON_MBM}="1"
-# Lenovo N5321gw
-ATTRS{idVendor}=="0bdb", ATTRS{idProduct}=="193e", ENV{ID_MM_ERICSSON_MBM}="1"
+GOTO="mm_mbm_end"
LABEL="mm_mbm_end"
diff --git a/plugins/mbm/mm-broadband-bearer-mbm.c b/plugins/mbm/mm-broadband-bearer-mbm.c
index eb4ebce1..263ab5b3 100644
--- a/plugins/mbm/mm-broadband-bearer-mbm.c
+++ b/plugins/mbm/mm-broadband-bearer-mbm.c
@@ -13,13 +13,14 @@
* Copyright (C) 2008 - 2010 Ericsson AB
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2017 Aleksander Morgado <aleksander@aleksander.es>
*
* Author: Per Hallsmark <per.hallsmark@ericsson.com>
* Bjorn Runaker <bjorn.runaker@ericsson.com>
* Torgny Johansson <torgny.johansson@ericsson.com>
* Jonas Sjöquist <jonas.sjoquist@ericsson.com>
* Dan Williams <dcbw@redhat.com>
- * Aleksander Morgado <aleksander@lanedo.com>
+ * Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -37,244 +38,211 @@
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-mbm.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-mbm.h"
#include "mm-daemon-enums-types.h"
-G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER);
+G_DEFINE_TYPE (MMBroadbandBearerMbm, mm_broadband_bearer_mbm, MM_TYPE_BROADBAND_BEARER)
struct _MMBroadbandBearerMbmPrivate {
- gpointer connect_pending;
- guint connect_pending_id;
- gulong connect_cancellable_id;
+ GTask *connect_pending;
+ GTask *disconnect_pending;
};
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerMbm *self;
- MMBaseModem *modem;
+ MMBaseModem *modem;
MMPortSerialAt *primary;
- guint cid;
- GCancellable *cancellable;
- MMPort *data;
- GSimpleAsyncResult *result;
- guint poll_count;
+ guint cid;
+ MMPort *data;
+ guint poll_count;
+ guint poll_id;
+ GError *saved_error;
} Dial3gppContext;
static void
-dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+dial_3gpp_context_free (Dial3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->data)
- g_object_unref (ctx->data);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
+ g_assert (!ctx->poll_id);
+ g_assert (!ctx->saved_error);
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->primary);
+ g_clear_object (&ctx->modem);
g_slice_free (Dial3gppContext, ctx);
}
-static gboolean
-dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx,
- GError **error)
+static MMPort *
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Dial operation has been cancelled");
- return TRUE;
+ return MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
}
-static gboolean
-dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx)
+static void
+connect_reset_ready (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ Dial3gppContext *ctx;
- if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error))
- return FALSE;
+ ctx = g_task_get_task_data (task);
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return TRUE;
+ MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self, res, NULL);
+
+ /* When reset is requested, it was either cancelled or an error was stored */
+ if (!g_task_return_error_if_cancelled (task)) {
+ g_assert (ctx->saved_error);
+ g_task_return_error (task, ctx->saved_error);
+ ctx->saved_error = NULL;
+ }
+
+ g_object_unref (task);
}
-static MMPort *
-dial_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
+static void
+connect_reset (GTask *task)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ MMBroadbandBearerMbm *self;
+ Dial3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp (
+ MM_BROADBAND_BEARER (self),
+ MM_BROADBAND_MODEM (ctx->modem),
+ ctx->primary,
+ NULL,
+ ctx->data,
+ ctx->cid,
+ (GAsyncReadyCallback) connect_reset_ready,
+ task);
}
static void
-report_connection_status (MMBaseBearer *bearer,
- MMBearerConnectionStatus status)
+process_pending_connect_attempt (MMBroadbandBearerMbm *self,
+ MMBearerConnectionStatus status)
{
- MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (bearer);
+ GTask *task;
Dial3gppContext *ctx;
- g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
- status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
-
- /* Recover context (if any) and remove both cancellation and timeout (if any)*/
- ctx = self->priv->connect_pending;
+ /* Recover connection task */
+ task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
+ g_assert (task != NULL);
- /* Connection status reported but no connection attempt? */
- if (!ctx) {
- g_assert (self->priv->connect_pending_id == 0);
+ ctx = g_task_get_task_data (task);
- mm_dbg ("Received spontaneous *E2NAP (%s)",
- mm_bearer_connection_status_get_string (status));
-
- if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
- /* If no connection attempt on-going, make sure we mark ourselves as
- * disconnected */
- MM_BASE_BEARER_CLASS (mm_broadband_bearer_mbm_parent_class)->report_connection_status (
- bearer,
- status);
- }
- return;
+ if (ctx->poll_id) {
+ g_source_remove (ctx->poll_id);
+ ctx->poll_id = 0;
}
- if (self->priv->connect_pending_id) {
- g_source_remove (self->priv->connect_pending_id);
- self->priv->connect_pending_id = 0;
- }
+ /* Received 'CONNECTED' during a connection attempt? */
+ if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
+ /* If we wanted to get cancelled before, do it now. */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ connect_reset (task);
+ return;
+ }
- if (self->priv->connect_cancellable_id) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
- self->priv->connect_cancellable_id = 0;
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
+ return;
}
- /* Reporting connected */
- if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- (GDestroyNotify)g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
+ /* If we wanted to get cancelled before and now we couldn't connect,
+ * use the cancelled error and return */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
- /* Reporting disconnected */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Call setup failed");
- dial_3gpp_context_complete_and_free (ctx);
+ /* Otherwise, received 'DISCONNECTED' during a connection attempt? */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed");
+ g_object_unref (task);
}
-static void
-connect_cancelled_cb (GCancellable *cancellable,
- MMBroadbandBearerMbm *self)
-{
- GError *error = NULL;
- Dial3gppContext *ctx;
-
- /* Recover context and remove timeout */
- ctx = self->priv->connect_pending;
-
- g_source_remove (self->priv->connect_pending_id);
-
- self->priv->connect_pending = NULL;
- self->priv->connect_pending_id = 0;
- self->priv->connect_cancellable_id = 0;
-
- g_assert (dial_3gpp_context_set_error_if_cancelled (ctx, &error));
-
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
-}
-
-static gboolean poll_timeout_cb (MMBroadbandBearerMbm *self);
+static gboolean connect_poll_cb (MMBroadbandBearerMbm *self);
static void
-poll_ready (MMBaseModem *modem,
- GAsyncResult *res,
- MMBroadbandBearerMbm *self)
+connect_poll_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- GError *error = NULL;
- const gchar *response;
- guint state;
+ GError *error = NULL;
+ const gchar *response;
+ guint state;
- /* Try to recover the connection context. If none found, it means the
- * context was already completed and we have nothing else to do. */
- ctx = self->priv->connect_pending;
-
- /* Balance refcount with the extra ref we passed to command_full() */
- g_object_unref (self);
-
- if (!ctx) {
- mm_dbg ("Connection context was finished already by an unsolicited message");
+ task = g_steal_pointer (&self->priv->connect_pending);
+ if (!task) {
+ mm_obj_dbg (self, "connection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
* the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
return;
}
- response = mm_base_modem_at_command_full_finish (modem, res, &error);
- if (response
- && sscanf (response, "*ENAP: %d", &state) == 1
- && state == 1) {
- /* Success! Connected... */
- self->priv->connect_pending = NULL;
+ ctx = g_task_get_task_data (task);
- if (ctx && self->priv->connect_cancellable_id) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
- self->priv->connect_cancellable_id = 0;
- }
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (!response) {
+ ctx->saved_error = error;
+ connect_reset (task);
+ return;
+ }
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- (GDestroyNotify)g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
+ if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 1) {
+ /* Success! Connected... */
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
return;
}
- self->priv->connect_pending_id = g_timeout_add_seconds (1,
- (GSourceFunc)poll_timeout_cb,
- self);
+ /* Restore pending task and check again in one second */
+ self->priv->connect_pending = task;
+ g_assert (ctx->poll_id == 0);
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) connect_poll_cb, self);
}
static gboolean
-poll_timeout_cb (MMBroadbandBearerMbm *self)
+connect_poll_cb (MMBroadbandBearerMbm *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- /* Recover context */
- ctx = self->priv->connect_pending;
+ task = g_steal_pointer (&self->priv->connect_pending);
+
+ g_assert (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->poll_id = 0;
+
+ /* Complete if we were cancelled */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ connect_reset (task);
+ return G_SOURCE_REMOVE;
+ }
/* Too many retries... */
- if (ctx->poll_count > 50) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
-
- self->priv->connect_pending = NULL;
- self->priv->connect_pending_id = 0;
- self->priv->connect_cancellable_id = 0;
-
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Connection attempt timed out");
- dial_3gpp_context_complete_and_free (ctx);
- return FALSE;
+ if (ctx->poll_count > MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT) {
+ g_assert (!ctx->saved_error);
+ ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Connection attempt timed out");
+ connect_reset (task);
+ return G_SOURCE_REMOVE;
}
+ /* Restore pending task and poll */
+ self->priv->connect_pending = task;
ctx->poll_count++;
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
@@ -282,231 +250,211 @@ poll_timeout_cb (MMBroadbandBearerMbm *self)
3,
FALSE,
FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)poll_ready,
- g_object_ref (ctx->self)); /* we pass the bearer object! */
- self->priv->connect_pending_id = 0;
- return FALSE;
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)connect_poll_ready,
+ self);
+ return G_SOURCE_REMOVE;
}
static void
-activate_ready (MMBaseModem *modem,
- GAsyncResult *res,
+activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
MMBroadbandBearerMbm *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- GError *error = NULL;
+ GError *error = NULL;
/* Try to recover the connection context. If none found, it means the
* context was already completed and we have nothing else to do. */
- ctx = self->priv->connect_pending;
-
- /* Balance refcount with the extra ref we passed to command_full() */
- g_object_unref (self);
-
- if (!ctx) {
- mm_dbg ("Connection context was finished already by an unsolicited message");
+ task = g_steal_pointer (&self->priv->connect_pending);
+ if (!task) {
+ mm_obj_dbg (self, "connection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
* the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
- return;
+ goto out;
}
/* From now on, if we get cancelled, we'll need to run the connection
* reset ourselves just in case */
-
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
- /* We will now setup a timeout to poll for the status */
- self->priv->connect_pending_id = g_timeout_add_seconds (1,
- (GSourceFunc)poll_timeout_cb,
- self);
+ ctx = g_task_get_task_data (task);
+
+ /* No unsolicited E2NAP status yet; wait for it and periodically poll
+ * to handle very old F3507g/MD300 firmware that may not send E2NAP. */
+ self->priv->connect_pending = task;
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)connect_poll_cb, self);
- self->priv->connect_cancellable_id = g_cancellable_connect (ctx->cancellable,
- G_CALLBACK (connect_cancelled_cb),
- self,
- NULL);
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static void
-activate (Dial3gppContext *ctx)
+activate (GTask *task)
{
- gchar *command;
+ MMBroadbandBearerMbm *self;
+ Dial3gppContext *ctx;
+ gchar *command;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
/* The unsolicited response to ENAP may come before the OK does.
* We will keep the connection context in the bearer private data so
- * that it is accessible from the unsolicited message handler. Note
- * also that we do NOT pass the ctx to the GAsyncReadyCallback, as it
- * may not be valid any more when the callback is called (it may be
- * already completed in the unsolicited handling) */
- g_assert (ctx->self->priv->connect_pending == NULL);
- ctx->self->priv->connect_pending = ctx;
-
- /* Success, activate the PDP context and start the data session */
- command = g_strdup_printf ("AT*ENAP=1,%d",
- ctx->cid);
+ * that it is accessible from the unsolicited message handler. */
+ g_assert (self->priv->connect_pending == NULL);
+ self->priv->connect_pending = task;
+
+ /* Activate the PDP context and start the data session */
+ command = g_strdup_printf ("AT*ENAP=1,%d", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
- 3,
+ 10,
FALSE,
FALSE, /* raw */
- NULL, /* cancellable */
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)activate_ready,
- g_object_ref (ctx->self)); /* we pass the bearer object! */
+ g_object_ref (self)); /* we pass the bearer object! */
g_free (command);
}
static void
-authenticate_ready (MMBaseModem *modem,
+authenticate_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
GError *error = NULL;
- /* If cancelled, complete */
- if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
- return;
-
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- activate (ctx);
+ activate (task);
}
static void
-authenticate (Dial3gppContext *ctx)
+authenticate (GTask *task)
{
- const gchar *user;
- const gchar *password;
+ MMBroadbandBearerMbm *self;
+ Dial3gppContext *ctx;
+ const gchar *user;
+ const gchar *password;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
/* Both user and password are required; otherwise firmware returns an error */
if (user || password) {
- gchar *command;
- gchar *encoded_user;
- gchar *encoded_password;
+ g_autofree gchar *command = NULL;
+ g_autofree gchar *user_enc = NULL;
+ g_autofree gchar *password_enc = NULL;
+ GError *error = NULL;
+
+ user_enc = mm_modem_charset_str_from_utf8 (user,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)),
+ FALSE,
+ &error);
+ if (!user_enc) {
+ g_prefix_error (&error, "Couldn't convert user to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- encoded_user = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (ctx->modem),
- g_strdup (user));
- encoded_password = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (ctx->modem),
- g_strdup (password));
+ password_enc = mm_modem_charset_str_from_utf8 (password,
+ mm_broadband_modem_get_current_charset (MM_BROADBAND_MODEM (ctx->modem)),
+ FALSE,
+ &error);
+ if (!password_enc) {
+ g_prefix_error (&error, "Couldn't convert password to current charset: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
command = g_strdup_printf ("AT*EIAAUW=%d,1,\"%s\",\"%s\"",
- ctx->cid,
- encoded_user ? encoded_user : "",
- encoded_password ? encoded_password : "");
- g_free (encoded_user);
- g_free (encoded_password);
-
+ ctx->cid, user_enc, password_enc);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)authenticate_ready,
- ctx);
- g_free (command);
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) authenticate_ready,
+ task);
return;
}
- mm_dbg ("Authentication not needed");
- activate (ctx);
+ mm_obj_dbg (self, "authentication not needed");
+ activate (task);
}
static void
-dial_3gpp (MMBroadbandBearer *self,
- MMBaseModem *modem,
- MMPortSerialAt *primary,
- guint cid,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dial_3gpp (MMBroadbandBearer *_self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- Dial3gppContext *ctx;
+ MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
+ GTask *task;
+ Dial3gppContext *ctx;
g_assert (primary != NULL);
+ task = g_task_new (self, cancellable, callback, user_data);
+
ctx = g_slice_new0 (Dial3gppContext);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
+ ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
- ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- dial_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
- ctx->poll_count = 0;
+ ctx->cid = cid;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
/* We need a net data port */
ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
if (!ctx->data) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
return;
}
- authenticate (ctx);
+ authenticate (task);
}
/*****************************************************************************/
/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerMbm *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMBearerIpFamily family;
- GSimpleAsyncResult *result;
} GetIpConfig3gppContext;
-static GetIpConfig3gppContext *
-get_ip_config_3gpp_context_new (MMBroadbandBearerMbm *self,
- MMBaseModem *modem,
- MMPortSerialAt *primary,
- MMBearerIpFamily family,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GetIpConfig3gppContext *ctx;
-
- ctx = g_new0 (GetIpConfig3gppContext, 1);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
- ctx->primary = g_object_ref (primary);
- ctx->family = family;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_ip_config_3gpp_context_new);
- return ctx;
-}
-
static void
-get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx)
+get_ip_config_context_free (GetIpConfig3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -520,12 +468,10 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
MMBearerConnectResult *configs;
MMBearerIpConfig *ipv4, *ipv6;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ configs = g_task_propagate_pointer (G_TASK (res), error);
+ if (!configs)
return FALSE;
- configs = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- g_assert (configs);
-
ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs);
ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs);
g_assert (ipv4 || ipv6);
@@ -534,20 +480,24 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
if (ipv6_config && ipv6)
*ipv6_config = g_object_ref (ipv6);
+ mm_bearer_connect_result_unref (configs);
return TRUE;
}
static void
ip_config_ready (MMBaseModem *modem,
GAsyncResult *res,
- GetIpConfig3gppContext *ctx)
+ GTask *task)
{
+ GetIpConfig3gppContext *ctx;
MMBearerIpConfig *ipv4_config = NULL;
MMBearerIpConfig *ipv6_config = NULL;
const gchar *response;
GError *error = NULL;
MMBearerConnectResult *connect_result;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
g_error_free (error);
@@ -566,16 +516,16 @@ ip_config_ready (MMBaseModem *modem,
&ipv4_config,
&ipv6_config,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
goto out;
}
if (!ipv4_config && !ipv6_config) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get IP config: couldn't parse response '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get IP config: couldn't parse response '%s'",
+ response);
goto out;
}
}
@@ -583,14 +533,14 @@ ip_config_ready (MMBaseModem *modem,
connect_result = mm_bearer_connect_result_new (MM_PORT (ctx->primary),
ipv4_config,
ipv6_config);
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- connect_result,
- (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_task_return_pointer (task,
+ connect_result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
out:
+ g_object_unref (task);
g_clear_object (&ipv4_config);
g_clear_object (&ipv6_config);
- get_ip_config_context_complete_and_free (ctx);
}
static void
@@ -605,13 +555,15 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
gpointer user_data)
{
GetIpConfig3gppContext *ctx;
+ GTask *task;
- ctx = get_ip_config_3gpp_context_new (MM_BROADBAND_BEARER_MBM (self),
- MM_BASE_MODEM (modem),
- primary,
- ip_family,
- callback,
- user_data);
+ ctx = g_new0 (GetIpConfig3gppContext, 1);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+ ctx->primary = g_object_ref (primary);
+ ctx->family = ip_family;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free);
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
@@ -621,78 +573,216 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)ip_config_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* 3GPP disconnect */
typedef struct {
- MMBroadbandBearerMbm *self;
- MMBaseModem *modem;
+ MMBaseModem *modem;
MMPortSerialAt *primary;
- GSimpleAsyncResult *result;
+ guint poll_count;
+ guint poll_id;
} DisconnectContext;
static void
-disconnect_context_complete_and_free (DisconnectContext *ctx)
+disconnect_context_free (DisconnectContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->self);
- g_object_unref (ctx->modem);
+ g_assert (!ctx->poll_id);
+ g_clear_object (&ctx->primary);
+ g_clear_object (&ctx->modem);
g_free (ctx);
}
static gboolean
-disconnect_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
+disconnect_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-disconnect_enap_ready (MMBaseModem *modem,
- GAsyncResult *res,
- DisconnectContext *ctx)
+process_pending_disconnect_attempt (MMBroadbandBearerMbm *self,
+ MMBearerConnectionStatus status)
{
- GError *error = NULL;
+ GTask *task;
+ DisconnectContext *ctx;
+
+ /* Recover disconnection task */
+ task = g_steal_pointer (&self->priv->disconnect_pending);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->poll_id) {
+ g_source_remove (ctx->poll_id);
+ ctx->poll_id = 0;
+ }
+
+ /* Received 'DISCONNECTED' during a disconnection attempt? */
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ mm_obj_dbg (self, "connection disconnect indicated by an unsolicited message");
+ g_task_return_boolean (task, TRUE);
+ } else {
+ /* Otherwise, report error */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Disconnection failed");
+ }
+ g_object_unref (task);
+}
+
+static gboolean disconnect_poll_cb (MMBroadbandBearerMbm *self);
+
+static void
+disconnect_poll_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
+
+{
+ GTask *task;
+ DisconnectContext *ctx;
+ GError *error = NULL;
+ const gchar *response;
+ guint state;
+
+ task = g_steal_pointer (&self->priv->disconnect_pending);
+
+ if (!task) {
+ mm_obj_dbg (self, "disconnection context was finished already by an unsolicited message");
+ /* Run _finish() to finalize the async call, even if we don't care
+ * the result */
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+ goto out;
+ }
+
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ if (sscanf (response, "*ENAP: %d", &state) == 1 && state == 0) {
+ /* Disconnected */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* Restore pending task and check in 1s */
+ self->priv->disconnect_pending = task;
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->poll_id == 0);
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc) disconnect_poll_cb, self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
+}
+
+static gboolean
+disconnect_poll_cb (MMBroadbandBearerMbm *self)
+{
+ GTask *task;
+ DisconnectContext *ctx;
+
+ task = self->priv->disconnect_pending;
+ self->priv->disconnect_pending = NULL;
+
+ g_assert (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->poll_id = 0;
+
+ /* Too many retries... */
+ if (ctx->poll_count > MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT) {
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Disconnection attempt timed out");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+ }
+
+ /* Restore pending task and poll */
+ self->priv->disconnect_pending = task;
+ ctx->poll_count++;
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ "AT*ENAP?",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) disconnect_poll_ready,
+ g_object_ref (self)); /* we pass the bearer object! */
+ return G_SOURCE_REMOVE;
+}
+
+static void
+disconnect_enap_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerMbm *self)
+{
+ DisconnectContext *ctx;
+ GTask *task;
+ GError *error = NULL;
+
+ task = g_steal_pointer (&self->priv->disconnect_pending);
+
+ /* Try to recover the disconnection context. If none found, it means the
+ * context was already completed and we have nothing else to do. */
+ if (!task) {
+ mm_base_modem_at_command_full_finish (modem, res, NULL);
+ goto out;
+ }
+
+ ctx = g_task_get_task_data (task);
/* Ignore errors for now */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- mm_dbg ("Disconnection failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_context_complete_and_free (ctx);
+ /* No unsolicited E2NAP status yet; wait for it and periodically poll
+ * to handle very old F3507g/MD300 firmware that may not send E2NAP. */
+ self->priv->disconnect_pending = task;
+ ctx->poll_id = g_timeout_add_seconds (1, (GSourceFunc)disconnect_poll_cb, self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
static void
-disconnect_3gpp (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
- MMPortSerialAt *primary,
- MMPortSerialAt *secondary,
- MMPort *data,
- guint cid,
- GAsyncReadyCallback callback,
- gpointer user_data)
+disconnect_3gpp (MMBroadbandBearer *_self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- DisconnectContext *ctx;
+ MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
+ GTask *task;
+ DisconnectContext *ctx;
g_assert (primary != NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+
ctx = g_new0 (DisconnectContext, 1);
- ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect_3gpp);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_context_free);
+
+ /* The unsolicited response to ENAP may come before the OK does.
+ * We will keep the disconnection context in the bearer private data so
+ * that it is accessible from the unsolicited message handler. */
+ g_assert (self->priv->disconnect_pending == NULL);
+ self->priv->disconnect_pending = task;
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
@@ -702,7 +792,47 @@ disconnect_3gpp (MMBroadbandBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_enap_ready,
- ctx);
+ g_object_ref (self)); /* we pass the bearer object! */
+}
+
+/*****************************************************************************/
+
+static void
+report_connection_status (MMBaseBearer *_self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
+{
+ MMBroadbandBearerMbm *self = MM_BROADBAND_BEARER_MBM (_self);
+
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
+ status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+
+ /* Process pending connection attempt */
+ if (self->priv->connect_pending) {
+ process_pending_connect_attempt (self, status);
+ return;
+ }
+
+ /* Process pending disconnection attempt */
+ if (self->priv->disconnect_pending) {
+ process_pending_disconnect_attempt (self, status);
+ return;
+ }
+
+ mm_obj_dbg (self, "received spontaneous E2NAP (%s)",
+ mm_bearer_connection_status_get_string (status));
+
+ /* Received a random 'DISCONNECTED'...*/
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
+ /* If no connection/disconnection attempt on-going, make sure we mark ourselves as
+ * disconnected. Make sure we only pass 'DISCONNECTED' to the parent */
+ MM_BASE_BEARER_CLASS (mm_broadband_bearer_mbm_parent_class)->report_connection_status (
+ _self,
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
+ NULL);
+ }
}
/*****************************************************************************/
@@ -765,6 +895,13 @@ mm_broadband_bearer_mbm_class_init (MMBroadbandBearerMbmClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerMbmPrivate));
base_bearer_class->report_connection_status = report_connection_status;
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = NULL;
+ base_bearer_class->reload_connection_status_finish = NULL;
+#endif
+
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
diff --git a/plugins/mbm/mm-broadband-modem-mbm.c b/plugins/mbm/mm-broadband-modem-mbm.c
index 0eaa65a7..fbc9830c 100644
--- a/plugins/mbm/mm-broadband-modem-mbm.c
+++ b/plugins/mbm/mm-broadband-modem-mbm.c
@@ -31,32 +31,34 @@
#include <ctype.h>
#include "ModemManager.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-bearer-list.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-mbm.h"
#include "mm-broadband-modem-mbm.h"
#include "mm-broadband-bearer-mbm.h"
#include "mm-sim-mbm.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-location.h"
+
+/* sets the interval in seconds on how often the card emits the NMEA sentences */
+#define MBM_GPS_NMEA_INTERVAL "5"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
static MMIfaceModem *iface_modem_parent;
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbm, mm_broadband_modem_mbm, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init))
-
-#define MBM_NETWORK_MODE_OFFLINE 0
-#define MBM_NETWORK_MODE_ANY 1
-#define MBM_NETWORK_MODE_LOW_POWER 4
-#define MBM_NETWORK_MODE_2G 5
-#define MBM_NETWORK_MODE_3G 6
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init))
#define MBM_E2NAP_DISCONNECTED 0
#define MBM_E2NAP_CONNECTED 1
@@ -74,6 +76,8 @@ struct _MMBroadbandModemMbmPrivate {
GRegex *emwi_regex;
GRegex *erinfo_regex;
+ MMModemLocationSource enabled_sources;
+
guint mbm_mode;
};
@@ -85,31 +89,24 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New MBM bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_mbm_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_mbm_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
@@ -118,19 +115,12 @@ modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
-
- mm_dbg ("Creating MBM bearer...");
+ mm_obj_dbg (self, "creating MBM bearer...");
mm_broadband_bearer_mbm_new (MM_BROADBAND_MODEM_MBM (self),
properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_mbm_new_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -164,15 +154,15 @@ modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -180,74 +170,67 @@ modem_after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
+ task = g_task_new (self, NULL, callback, user_data);
/* wait so sim pin is done */
- g_timeout_add (500, (GSourceFunc)after_sim_unlock_wait_cb, result);
+ g_timeout_add (500, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
/* Load supported modes (Modem interface) */
static GArray *
-load_supported_modes_finish (MMIfaceModem *self,
+load_supported_modes_finish (MMIfaceModem *_self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
-}
-
-static void
-parent_load_supported_modes_ready (MMIfaceModem *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- GError *error = NULL;
- GArray *all;
+ MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
+ const gchar *response;
+ guint32 mask = 0;
GArray *combinations;
- GArray *filtered;
MMModemModeCombination mode;
- all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
- if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return FALSE;
+
+ if (!mm_mbm_parse_cfun_test (response, self, &mask, error))
+ return FALSE;
/* Build list of combinations */
combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 3);
/* 2G only */
- mode.allowed = MM_MODEM_MODE_2G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
+ if (mask & (1 << MBM_NETWORK_MODE_2G)) {
+ mode.allowed = MM_MODEM_MODE_2G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
/* 3G only */
- mode.allowed = MM_MODEM_MODE_3G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
+ if (mask & (1 << MBM_NETWORK_MODE_3G)) {
+ mode.allowed = MM_MODEM_MODE_3G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
/* 2G and 3G */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
+ if (mask & (1 << MBM_NETWORK_MODE_ANY)) {
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
- /* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
- g_array_unref (all);
- g_array_unref (combinations);
+ if (combinations->len == 0) {
+ g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't load any supported mode");
+ g_array_unref (combinations);
+ return NULL;
+ }
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ return combinations;
}
static void
@@ -255,14 +238,12 @@ load_supported_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- /* Run parent's loading */
- iface_modem_parent->load_supported_modes (
- MM_IFACE_MODEM (self),
- (GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN=?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
}
/*****************************************************************************/
@@ -277,45 +258,22 @@ load_current_modes_finish (MMIfaceModem *_self,
{
MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
const gchar *response;
- guint a;
+ gint mbm_mode = -1;
+
+ g_assert (allowed);
+ g_assert (preferred);
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
- if (!response)
+ if (!response || !mm_mbm_parse_cfun_query_current_modes (response, allowed, &mbm_mode, error))
return FALSE;
- if (mm_get_uint_from_str (mm_strip_tag (response, "+CFUN:"), &a)) {
- /* No settings to set preferred */
- *preferred = MM_MODEM_MODE_NONE;
-
- switch (a) {
- case MBM_NETWORK_MODE_OFFLINE:
- case MBM_NETWORK_MODE_LOW_POWER:
- /* Do not update internal mbm_mode */
- *allowed = MM_MODEM_MODE_NONE;
- break;
- case MBM_NETWORK_MODE_2G:
- self->priv->mbm_mode = MBM_NETWORK_MODE_2G;
- *allowed = MM_MODEM_MODE_2G;
- break;
- case MBM_NETWORK_MODE_3G:
- self->priv->mbm_mode = MBM_NETWORK_MODE_3G;
- *allowed = MM_MODEM_MODE_3G;
- break;
- default:
- /* Do not update internal mbm_mode */
- *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- break;
- }
+ /* No settings to set preferred */
+ *preferred = MM_MODEM_MODE_NONE;
- return TRUE;
- }
+ if (mbm_mode != -1)
+ self->priv->mbm_mode = mbm_mode;
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse +CFUN response: '%s'",
- response);
- return FALSE;
+ return TRUE;
}
static void
@@ -335,45 +293,37 @@ load_current_modes (MMIfaceModem *self,
/* Set allowed modes (Modem interface) */
typedef struct {
- MMBroadbandModemMbm *self;
- GSimpleAsyncResult *result;
gint mbm_mode;
} SetCurrentModesContext;
-static void
-set_current_modes_context_complete_and_free (SetCurrentModesContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (SetCurrentModesContext, ctx);
-}
-
static gboolean
set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBaseModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
+ SetCurrentModesContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_finish (self, res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else {
/* Cache current allowed mode */
- ctx->self->priv->mbm_mode = ctx->mbm_mode;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ MM_BROADBAND_MODEM_MBM (self)->priv->mbm_mode = ctx->mbm_mode;
+ g_task_return_boolean (task, TRUE);
}
- set_current_modes_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
@@ -384,16 +334,15 @@ set_current_modes (MMIfaceModem *self,
gpointer user_data)
{
SetCurrentModesContext *ctx;
+ GTask *task;
gchar *command;
- ctx = g_slice_new (SetCurrentModesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ ctx = g_new (SetCurrentModesContext, 1);
ctx->mbm_mode = -1;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
if (allowed == MM_MODEM_MODE_2G)
ctx->mbm_mode = MBM_NETWORK_MODE_2G;
else if (allowed == MM_MODEM_MODE_3G)
@@ -409,17 +358,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- set_current_modes_context_complete_and_free (ctx);
return;
}
@@ -430,44 +378,30 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- ctx);
+ task);
g_free (command);
}
/*****************************************************************************/
/* Initializing the modem (during first enabling) */
-typedef struct {
- GSimpleAsyncResult *result;
- MMBroadbandModemMbm *self;
-} EnablingModemInitContext;
-
-static void
-enabling_modem_init_context_complete_and_free (EnablingModemInitContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (EnablingModemInitContext, ctx);
-}
-
static gboolean
enabling_modem_init_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enabling_init_sequence_ready (MMBaseModem *self,
GAsyncResult *res,
- EnablingModemInitContext *ctx)
+ GTask *task)
{
/* Ignore errors */
mm_base_modem_at_sequence_full_finish (self, res, NULL, NULL);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_modem_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static const MMBaseModemAtCommand enabling_modem_init_sequence[] = {
@@ -479,22 +413,25 @@ static const MMBaseModemAtCommand enabling_modem_init_sequence[] = {
};
static void
-run_enabling_init_sequence (EnablingModemInitContext *ctx)
+run_enabling_init_sequence (GTask *task)
{
- mm_base_modem_at_sequence_full (MM_BASE_MODEM (ctx->self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
+ MMBaseModem *self;
+
+ self = g_task_get_source_object (task);
+ mm_base_modem_at_sequence_full (self,
+ mm_base_modem_peek_port_primary (self),
enabling_modem_init_sequence,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)enabling_init_sequence_ready,
- ctx);
+ task);
}
static void
emrdy_ready (MMBaseModem *self,
GAsyncResult *res,
- EnablingModemInitContext *ctx)
+ GTask *task)
{
GError *error = NULL;
@@ -511,32 +448,28 @@ emrdy_ready (MMBaseModem *self,
if (g_error_matches (error,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
- mm_warn ("timed out waiting for EMRDY response.");
+ mm_obj_warn (self, "timed out waiting for EMRDY response");
else
- ctx->self->priv->have_emrdy = TRUE;
+ MM_BROADBAND_MODEM_MBM (self)->priv->have_emrdy = TRUE;
g_error_free (error);
}
- run_enabling_init_sequence (ctx);
+ run_enabling_init_sequence (task);
}
static void
-enabling_modem_init (MMBroadbandModem *self,
+enabling_modem_init (MMBroadbandModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- EnablingModemInitContext *ctx;
+ MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
+ GTask *task;
- ctx = g_slice_new0 (EnablingModemInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enabling_modem_init);
- ctx->self = g_object_ref (self);
+ task = g_task_new (self, NULL, callback, user_data);
/* Modem is ready?, no need to check EMRDY */
- if (ctx->self->priv->have_emrdy) {
- run_enabling_init_sequence (ctx);
+ if (self->priv->have_emrdy) {
+ run_enabling_init_sequence (task);
return;
}
@@ -545,7 +478,7 @@ enabling_modem_init (MMBroadbandModem *self,
3,
FALSE,
(GAsyncReadyCallback)emrdy_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -617,36 +550,14 @@ load_power_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- const gchar *response;
- guint a;
+ const gchar *response;
+ MMModemPowerState state;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
- if (!response)
- return FALSE;
-
- if (mm_get_uint_from_str (mm_strip_tag (response, "+CFUN:"), &a)) {
- switch (a) {
- case MBM_NETWORK_MODE_OFFLINE:
- return MM_MODEM_POWER_STATE_OFF;
-
- case MBM_NETWORK_MODE_LOW_POWER:
- return MM_MODEM_POWER_STATE_LOW;
-
- case MBM_NETWORK_MODE_ANY:
- case MBM_NETWORK_MODE_2G:
- case MBM_NETWORK_MODE_3G:
- return MM_MODEM_POWER_STATE_ON;
- default:
- break;
- }
- }
+ if (!response || !mm_mbm_parse_cfun_query_power_state (response, &state, error))
+ return MM_MODEM_POWER_STATE_UNKNOWN;
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse +CFUN response: '%s'",
- response);
- return MM_MODEM_POWER_STATE_UNKNOWN;
+ return state;
}
static void
@@ -724,7 +635,7 @@ factory_reset (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("Ignoring factory reset code: '%s'", code);
+ mm_obj_dbg (self, "ignoring user-provided factory reset code: '%s'", code);
mm_base_modem_at_sequence (MM_BASE_MODEM (self),
factory_reset_sequence,
@@ -784,7 +695,6 @@ load_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading unlock retries (mbm)...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"*EPIN?",
10,
@@ -823,19 +733,19 @@ e2nap_received (MMPortSerialAt *port,
switch (state) {
case MBM_E2NAP_DISCONNECTED:
- mm_dbg ("disconnected");
+ mm_obj_dbg (self, "disconnected");
ctx.status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED;
break;
case MBM_E2NAP_CONNECTED:
- mm_dbg ("connected");
+ mm_obj_dbg (self, "connected");
ctx.status = MM_BEARER_CONNECTION_STATUS_CONNECTED;
break;
case MBM_E2NAP_CONNECTING:
- mm_dbg ("connecting");
+ mm_obj_dbg (self, "connecting");
break;
default:
/* Should not happen */
- mm_dbg ("unhandled E2NAP state %d", state);
+ mm_obj_dbg (self, "unhandled E2NAP state %d", state);
}
/* If unknown status, don't try to report anything */
@@ -909,7 +819,7 @@ set_unsolicited_events_handlers (MMBroadbandModemMbm *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -942,26 +852,24 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -969,33 +877,26 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
-
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -1003,13 +904,6 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
-
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), FALSE);
@@ -1017,7 +911,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1028,23 +922,23 @@ modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
own_enable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
@@ -1056,14 +950,14 @@ static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
/* Our own enable now */
@@ -1075,7 +969,7 @@ parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -1083,18 +977,11 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
-
/* Chain up parent's enable */
iface_modem_3gpp_parent->enable_unsolicited_events (
self,
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1105,36 +992,35 @@ modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
own_disable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1142,7 +1028,7 @@ own_disable_unsolicited_events_ready (MMBaseModem *self,
iface_modem_3gpp_parent->disable_unsolicited_events (
MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ task);
}
static const MMBaseModemAtCommand unsolicited_disable_sequence[] = {
@@ -1156,13 +1042,6 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
-
/* Our own disable first */
mm_base_modem_at_sequence_full (
MM_BASE_MODEM (self),
@@ -1172,7 +1051,300 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Location capabilities loading (Location interface) */
+
+static MMModemLocationSource
+location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+
+ sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* not sure how to check if GPS is supported, just allow it */
+ if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)))
+ sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED);
+
+ /* So we're done, complete */
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Chain up parent's setup */
+ iface_modem_location_parent->load_capabilities (
+ self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Enable/Disable location gathering (Location interface) */
+
+typedef struct {
+ MMModemLocationSource source;
+} LocationGatheringContext;
+
+/******************************/
+/* Disable location gathering */
+
+static gboolean
+disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+gps_disabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LocationGatheringContext *ctx;
+ MMPortSerialGps *gps_port;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ mm_base_modem_at_command_full_finish (self, res, &error);
+
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ /* Even if we get an error here, we try to close the GPS port */
+ gps_port = mm_base_modem_peek_port_gps (self);
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+disable_location_gathering (MMIfaceModemLocation *_self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
+ gboolean stop_gps = FALSE;
+ LocationGatheringContext *ctx;
+ GTask *task;
+
+ ctx = g_new (LocationGatheringContext, 1);
+ ctx->source = source;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ /* Only stop GPS engine if no GPS-related sources enabled */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ self->priv->enabled_sources &= ~source;
+
+ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ stop_gps = TRUE;
+ }
+
+ if (stop_gps) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (_self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (_self)),
+ "AT*E2GPSCTL=0",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)gps_disabled_ready,
+ task);
+ return;
+ }
+
+ /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+static gboolean
+enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+gps_enabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LocationGatheringContext *ctx;
+ GError *error = NULL;
+ MMPortSerialGps *gps_port;
+
+ if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ gps_port = mm_base_modem_peek_port_gps (self);
+ if (!gps_port ||
+ !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ } else {
+ GByteArray *buf;
+ const gchar *command = "ATE0*E2GPSNPD\r\n";
+
+ /* We need to send an AT command to the GPS data port to
+ * toggle it into this data mode. This is a particularity of
+ * mbm cards where the GPS data port is not hard wired. So
+ * we need to use the MMPortSerial API here.
+ */
+ buf = g_byte_array_new ();
+ g_byte_array_append (buf, (const guint8 *) command, strlen (command));
+ mm_port_serial_command (MM_PORT_SERIAL (gps_port),
+ buf,
+ 3,
+ FALSE, /* never cached */
+ FALSE, /* always queued last */
+ NULL,
+ NULL,
+ NULL);
+ g_byte_array_unref (buf);
+ g_task_return_boolean (task, TRUE);
+ }
+
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
+ LocationGatheringContext *ctx;
+ gboolean start_gps = FALSE;
+ GError *error = NULL;
+
+ if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Now our own enabling */
+
+ /* NMEA and RAW are both enabled in the same way */
+ ctx = g_task_get_task_data (task);
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ /* Only start GPS engine if not done already */
+ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ start_gps = TRUE;
+ self->priv->enabled_sources |= ctx->source;
+ }
+
+ if (start_gps) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+ "AT*E2GPSCTL=1," MBM_GPS_NMEA_INTERVAL ",0",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)gps_enabled_ready,
+ task);
+ return;
+ }
+
+ /* For any other location (e.g. 3GPP), or if GPS already running just return */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LocationGatheringContext *ctx;
+ GTask *task;
+
+ ctx = g_new (LocationGatheringContext, 1);
+ ctx->source = source;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ /* Chain up parent's gathering enable */
+ iface_modem_location_parent->enable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
}
/*****************************************************************************/
@@ -1187,10 +1359,19 @@ emrdy_received (MMPortSerialAt *port,
}
static void
+gps_trace_received (MMPortSerialGps *port,
+ const gchar *trace,
+ MMIfaceModemLocation *self)
+{
+ mm_iface_modem_location_gps_update (self, trace);
+}
+
+static void
setup_ports (MMBroadbandModem *_self)
{
MMBroadbandModemMbm *self = MM_BROADBAND_MODEM_MBM (_self);
MMPortSerialAt *ports[2];
+ MMPortSerialGps *gps_data_port;
guint i;
/* Call parent's setup ports first always */
@@ -1200,7 +1381,7 @@ setup_ports (MMBroadbandModem *_self)
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Setup unsolicited handlers which should be always on */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -1244,6 +1425,20 @@ setup_ports (MMBroadbandModem *_self)
/* Now reset the unsolicited messages we'll handle when enabled */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MBM (self), FALSE);
+
+ /* NMEA GPS monitoring */
+ gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_data_port) {
+ /* make sure GPS is stopped incase it was left enabled */
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+ "AT*E2GPSCTL=0",
+ 3, FALSE, FALSE, NULL, NULL, NULL);
+ /* Add handler for the NMEA traces */
+ mm_port_serial_gps_add_trace_handler (gps_data_port,
+ (MMPortSerialGpsTraceFn)gps_trace_received,
+ self, NULL);
+ }
}
/*****************************************************************************/
@@ -1261,6 +1456,9 @@ mm_broadband_modem_mbm_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* MBM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
NULL);
}
@@ -1358,6 +1556,19 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
}
static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = location_load_capabilities;
+ iface->load_capabilities_finish = location_load_capabilities_finish;
+ iface->enable_location_gathering = enable_location_gathering;
+ iface->enable_location_gathering_finish = enable_location_gathering_finish;
+ iface->disable_location_gathering = disable_location_gathering;
+ iface->disable_location_gathering_finish = disable_location_gathering_finish;
+}
+
+static void
mm_broadband_modem_mbm_class_init (MMBroadbandModemMbmClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
diff --git a/plugins/mbm/mm-modem-helpers-mbm.c b/plugins/mbm/mm-modem-helpers-mbm.c
index 42653d88..31fbb376 100644
--- a/plugins/mbm/mm-modem-helpers-mbm.c
+++ b/plugins/mbm/mm-modem-helpers-mbm.c
@@ -25,6 +25,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-mbm.h"
@@ -89,7 +90,7 @@ mm_mbm_parse_e2ipcfg_response (const gchar *response,
}
/* *E2IPCFG: (1,<IP>)(2,<gateway>)(3,<DNS>)(3,<DNS>)
- *
+ *
* *E2IPCFG: (1,"46.157.32.246")(2,"46.157.32.243")(3,"193.213.112.4")(3,"130.67.15.198")
* *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0000:e537:1801")(3,"2001:4600:0004:0fff:0000:0000:0000:0054")(3,"2001:4600:0004:1fff:0000:0000:0000:0054")
* *E2IPCFG: (1,"fe80:0000:0000:0000:0000:0027:b7fe:9401")(3,"fd00:976a:0000:0000:0000:0000:0000:0009")
@@ -158,9 +159,178 @@ mm_mbm_parse_e2ipcfg_response (const gchar *response,
}
done:
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return !!*ip_config;
}
+/*****************************************************************************/
+
+#define CFUN_TAG "+CFUN:"
+
+static void
+add_supported_mode (guint mode,
+ gpointer log_object,
+ guint32 *mask)
+{
+ g_assert (mask);
+ if (mode >= 32)
+ mm_obj_warn (log_object, "ignored unexpected mode in +CFUN match: %d", mode);
+ else
+ *mask |= (1 << mode);
+}
+
+gboolean
+mm_mbm_parse_cfun_test (const gchar *response,
+ gpointer log_object,
+ guint32 *supported_mask,
+ GError **error)
+{
+ gchar **groups;
+ guint32 mask = 0;
+
+ g_assert (supported_mask);
+
+ if (!response || !g_str_has_prefix (response, CFUN_TAG)) {
+ g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Missing " CFUN_TAG " prefix");
+ return FALSE;
+ }
+
+ /*
+ * AT+CFUN=?
+ * +CFUN: (0,1,4-6),(0,1)
+ * OK
+ */
+
+ /* Strip tag from response */
+ response = mm_strip_tag (response, CFUN_TAG);
+
+ /* Split response in (groups) */
+ groups = mm_split_string_groups (response);
+
+ /* First group is the one listing supported modes */
+ if (groups && groups[0]) {
+ gchar **supported_modes;
+
+ supported_modes = g_strsplit_set (groups[0], ", ", -1);
+ if (supported_modes) {
+ guint i;
+
+ for (i = 0; supported_modes[i]; i++) {
+ gchar *separator;
+ guint mode;
+
+ if (!supported_modes[i][0])
+ continue;
+
+ /* Check if this is a range that's being given to us */
+ separator = strchr (supported_modes[i], '-');
+ if (separator) {
+ gchar *first_str;
+ gchar *last_str;
+ guint first;
+ guint last;
+
+ *separator = '\0';
+ first_str = supported_modes[i];
+ last_str = separator + 1;
+
+ if (!mm_get_uint_from_str (first_str, &first))
+ mm_obj_warn (log_object, "couldn't match range start: '%s'", first_str);
+ else if (!mm_get_uint_from_str (last_str, &last))
+ mm_obj_warn (log_object, "couldn't match range stop: '%s'", last_str);
+ else if (first >= last)
+ mm_obj_warn (log_object, "couldn't match range: wrong first '%s' and last '%s' items", first_str, last_str);
+ else {
+ for (mode = first; mode <= last; mode++)
+ add_supported_mode (mode, log_object, &mask);
+ }
+ } else {
+ if (!mm_get_uint_from_str (supported_modes[i], &mode))
+ mm_obj_warn (log_object, "couldn't match mode: '%s'", supported_modes[i]);
+ else
+ add_supported_mode (mode, log_object, &mask);
+ }
+ }
+
+ g_strfreev (supported_modes);
+ }
+ }
+ g_strfreev (groups);
+
+ if (mask)
+ *supported_mask = mask;
+ return !!mask;
+}
+
+/*****************************************************************************/
+/* AT+CFUN? response parsers */
+
+gboolean
+mm_mbm_parse_cfun_query_power_state (const gchar *response,
+ MMModemPowerState *out_state,
+ GError **error)
+{
+ guint state;
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return FALSE;
+
+ switch (state) {
+ case MBM_NETWORK_MODE_OFFLINE:
+ *out_state = MM_MODEM_POWER_STATE_OFF;
+ return TRUE;
+ case MBM_NETWORK_MODE_LOW_POWER:
+ *out_state = MM_MODEM_POWER_STATE_LOW;
+ return TRUE;
+ case MBM_NETWORK_MODE_ANY:
+ case MBM_NETWORK_MODE_2G:
+ case MBM_NETWORK_MODE_3G:
+ *out_state = MM_MODEM_POWER_STATE_ON;
+ return TRUE;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown +CFUN pòwer state: '%u'", state);
+ return FALSE;
+ }
+}
+
+gboolean
+mm_mbm_parse_cfun_query_current_modes (const gchar *response,
+ MMModemMode *allowed,
+ gint *mbm_mode,
+ GError **error)
+{
+ guint state;
+
+ g_assert (mbm_mode);
+ g_assert (allowed);
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return FALSE;
+
+ switch (state) {
+ case MBM_NETWORK_MODE_OFFLINE:
+ case MBM_NETWORK_MODE_LOW_POWER:
+ /* Do not update mbm_mode */
+ *allowed = MM_MODEM_MODE_NONE;
+ return TRUE;
+ case MBM_NETWORK_MODE_2G:
+ *mbm_mode = MBM_NETWORK_MODE_2G;
+ *allowed = MM_MODEM_MODE_2G;
+ return TRUE;
+ case MBM_NETWORK_MODE_3G:
+ *mbm_mode = MBM_NETWORK_MODE_3G;
+ *allowed = MM_MODEM_MODE_3G;
+ return TRUE;
+ case MBM_NETWORK_MODE_ANY:
+ /* Do not update mbm_mode */
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ return TRUE;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown +CFUN current mode: '%u'", state);
+ return FALSE;
+ }
+}
diff --git a/plugins/mbm/mm-modem-helpers-mbm.h b/plugins/mbm/mm-modem-helpers-mbm.h
index ef15845d..3e3bf57a 100644
--- a/plugins/mbm/mm-modem-helpers-mbm.h
+++ b/plugins/mbm/mm-modem-helpers-mbm.h
@@ -24,4 +24,28 @@ gboolean mm_mbm_parse_e2ipcfg_response (const gchar *response,
MMBearerIpConfig **out_ip6_config,
GError **error);
+typedef enum {
+ MBM_NETWORK_MODE_OFFLINE = 0,
+ MBM_NETWORK_MODE_ANY = 1,
+ MBM_NETWORK_MODE_LOW_POWER = 4,
+ MBM_NETWORK_MODE_2G = 5,
+ MBM_NETWORK_MODE_3G = 6,
+} MbmNetworkMode;
+
+/* AT+CFUN=? test parser
+ * Returns a bitmask, bit index set for the supported modes reported */
+gboolean mm_mbm_parse_cfun_test (const gchar *response,
+ gpointer log_object,
+ guint32 *supported_mask,
+ GError **error);
+
+/* AT+CFUN? response parsers */
+gboolean mm_mbm_parse_cfun_query_power_state (const gchar *response,
+ MMModemPowerState *out_state,
+ GError **error);
+gboolean mm_mbm_parse_cfun_query_current_modes (const gchar *response,
+ MMModemMode *allowed,
+ gint *mbm_mode,
+ GError **error);
+
#endif /* MM_MODEM_HELPERS_MBM_H */
diff --git a/plugins/mbm/mm-plugin-mbm.c b/plugins/mbm/mm-plugin-mbm.c
index bef3ab29..ffd633e3 100644
--- a/plugins/mbm/mm-plugin-mbm.c
+++ b/plugins/mbm/mm-plugin-mbm.c
@@ -23,7 +23,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-plugin-mbm.h"
#include "mm-broadband-modem-mbm.h"
@@ -33,14 +33,14 @@
G_DEFINE_TYPE (MMPluginMbm, mm_plugin_mbm, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -49,8 +49,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
- mm_dbg ("MBIM-powered Ericsson modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path,
+ mm_obj_dbg (self, "MBIM-powered Ericsson modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -58,7 +58,7 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_mbm_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_mbm_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -70,7 +70,7 @@ create_modem (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const gchar *udev_tags[] = {
"ID_MM_ERICSSON_MBM",
NULL
@@ -78,7 +78,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_MBM,
- MM_PLUGIN_NAME, "Ericsson MBM",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/mbm/mm-sim-mbm.c b/plugins/mbm/mm-sim-mbm.c
index a1e1a76e..d3f73954 100644
--- a/plugins/mbm/mm-sim-mbm.c
+++ b/plugins/mbm/mm-sim-mbm.c
@@ -24,7 +24,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-base-modem-at.h"
#include "mm-sim-mbm.h"
@@ -34,20 +34,14 @@ G_DEFINE_TYPE (MMSimMbm, mm_sim_mbm, MM_TYPE_BASE_SIM)
/* SEND PIN/PUK (Generic implementation) */
typedef struct {
- MMSimMbm *self;
MMBaseModem *modem;
- GSimpleAsyncResult *result;
- MMModemLock expected;
guint retries;
} SendPinPukContext;
static void
-send_pin_puk_context_complete_and_free (SendPinPukContext *ctx)
+send_pin_puk_context_free (SendPinPukContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (SendPinPukContext, ctx);
}
@@ -56,15 +50,15 @@ common_send_pin_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void wait_for_unlocked_status (SendPinPukContext *ctx);
+static void wait_for_unlocked_status (GTask *task);
static void
cpin_query_ready (MMBaseModem *modem,
GAsyncResult *res,
- SendPinPukContext *ctx)
+ GTask *task)
{
const gchar *result;
@@ -72,64 +66,75 @@ cpin_query_ready (MMBaseModem *modem,
result = mm_base_modem_at_command_finish (modem, res, NULL);
if (result && strstr (result, "READY")) {
/* All done! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- send_pin_puk_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Need to recheck */
- wait_for_unlocked_status (ctx);
+ wait_for_unlocked_status (task);
}
static gboolean
-cpin_query_cb (SendPinPukContext *ctx)
+cpin_query_cb (GTask *task)
{
+ SendPinPukContext *ctx;
+
+ ctx = g_task_get_task_data (task);
mm_base_modem_at_command (ctx->modem,
"+CPIN?",
20,
FALSE,
(GAsyncReadyCallback)cpin_query_ready,
- ctx);
- return FALSE;
+ task);
+ return G_SOURCE_REMOVE;
}
static void
-wait_for_unlocked_status (SendPinPukContext *ctx)
+wait_for_unlocked_status (GTask *task)
{
+ MMSimMbm *self;
+ SendPinPukContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Oops... :/ */
if (ctx->retries == 0) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "PIN was sent but modem didn't report unlocked");
- send_pin_puk_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "PIN was sent but modem didn't report unlocked");
+ g_object_unref (task);
return;
}
/* Check status */
ctx->retries--;
- mm_dbg ("Scheduling lock state check...");
- g_timeout_add_seconds (1, (GSourceFunc)cpin_query_cb, ctx);
+ mm_obj_dbg (self, "scheduling lock state check...");
+ g_timeout_add_seconds (1, (GSourceFunc)cpin_query_cb, task);
}
static void
send_pin_puk_ready (MMBaseModem *modem,
GAsyncResult *res,
- SendPinPukContext *ctx)
+ GTask *task)
{
+ SendPinPukContext *ctx;
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- send_pin_puk_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* No explicit error sending the PIN/PUK, now check status until we have the
* expected lock status */
+ ctx = g_task_get_task_data (task);
ctx->retries = 3;
- wait_for_unlocked_status (ctx);
+ wait_for_unlocked_status (task);
}
static void
@@ -140,18 +145,17 @@ common_send_pin_puk (MMBaseSim *self,
gpointer user_data)
{
SendPinPukContext *ctx;
+ GTask *task;
gchar *command;
ctx = g_slice_new (SendPinPukContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_send_pin_puk);
- g_object_get (ctx->self,
+ g_object_get (self,
MM_BASE_SIM_MODEM, &ctx->modem,
NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)send_pin_puk_context_free);
+
command = (puk ?
g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) :
g_strdup_printf ("+CPIN=\"%s\"", pin));
@@ -160,7 +164,7 @@ common_send_pin_puk (MMBaseSim *self,
3,
FALSE,
(GAsyncReadyCallback)send_pin_puk_ready,
- ctx);
+ task);
g_free (command);
}
@@ -217,6 +221,7 @@ mm_sim_mbm_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
diff --git a/plugins/mbm/tests/test-modem-helpers-mbm.c b/plugins/mbm/tests/test-modem-helpers-mbm.c
index 2e6dd1a3..4169140a 100644
--- a/plugins/mbm/tests/test-modem-helpers-mbm.c
+++ b/plugins/mbm/tests/test-modem-helpers-mbm.c
@@ -24,7 +24,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-test.h"
#include "mm-modem-helpers.h"
#include "mm-modem-helpers-mbm.h"
@@ -47,7 +47,7 @@ typedef struct {
} E2ipcfgTest;
static const E2ipcfgTest tests[] = {
- { "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n",
+ { "*E2IPCFG: (1,\"46.157.32.246\")(2,\"46.157.32.243\")(3,\"193.213.112.4\")(3,\"130.67.15.198\")\r\n",
"46.157.32.246", "46.157.32.243", "193.213.112.4", "130.67.15.198",
NULL, NULL },
@@ -96,6 +96,7 @@ test_e2ipcfg (void)
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, tests[i].ipv4_dns1);
g_assert_cmpstr (dns[1], ==, tests[i].ipv4_dns2);
+ g_object_unref (ipv4);
} else
g_assert (ipv4 == NULL);
@@ -122,41 +123,146 @@ test_e2ipcfg (void)
g_assert_cmpint (dnslen, ==, 1);
g_assert_cmpstr (dns[0], ==, tests[i].ipv6_dns1);
g_assert_cmpstr (dns[1], ==, tests[i].ipv6_dns2);
+ g_object_unref (ipv6);
} else
g_assert (ipv6 == NULL);
}
}
/*****************************************************************************/
+/* Test +CFUN test responses */
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+#define MAX_MODES 32
+
+typedef struct {
+ const gchar *str;
+ guint32 expected_mask;
+} CfunTest;
+
+static const CfunTest cfun_tests[] = {
+ {
+ "+CFUN: (0,1,4-6),(1-0)\r\n",
+ ((1 << MBM_NETWORK_MODE_OFFLINE) |
+ (1 << MBM_NETWORK_MODE_ANY) |
+ (1 << MBM_NETWORK_MODE_LOW_POWER) |
+ (1 << MBM_NETWORK_MODE_2G) |
+ (1 << MBM_NETWORK_MODE_3G))
+ },
+ {
+ "+CFUN: (0,1,4-6)\r\n",
+ ((1 << MBM_NETWORK_MODE_OFFLINE) |
+ (1 << MBM_NETWORK_MODE_ANY) |
+ (1 << MBM_NETWORK_MODE_LOW_POWER) |
+ (1 << MBM_NETWORK_MODE_2G) |
+ (1 << MBM_NETWORK_MODE_3G))
+ },
+ {
+ "+CFUN: (0,1,4)\r\n",
+ ((1 << MBM_NETWORK_MODE_OFFLINE) |
+ (1 << MBM_NETWORK_MODE_ANY) |
+ (1 << MBM_NETWORK_MODE_LOW_POWER))
+ },
+ {
+ "+CFUN: (0,1)\r\n",
+ ((1 << MBM_NETWORK_MODE_OFFLINE) |
+ (1 << MBM_NETWORK_MODE_ANY))
+ },
+};
+
+static void
+test_cfun_test (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cfun_tests); i++) {
+ guint32 mask;
+ gboolean success;
+ GError *error = NULL;
+
+ success = mm_mbm_parse_cfun_test (cfun_tests[i].str, NULL, &mask, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (mask, ==, cfun_tests[i].expected_mask);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ const gchar *str;
+ MMModemPowerState state;
+} CfunQueryPowerStateTest;
+
+static const CfunQueryPowerStateTest cfun_query_power_state_tests[] = {
+ { "+CFUN: 0", MM_MODEM_POWER_STATE_OFF },
+ { "+CFUN: 1", MM_MODEM_POWER_STATE_ON },
+ { "+CFUN: 4", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 5", MM_MODEM_POWER_STATE_ON },
+ { "+CFUN: 6", MM_MODEM_POWER_STATE_ON },
+};
+
+static void
+test_cfun_query_power_state (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cfun_query_power_state_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMModemPowerState state;
+
+ success = mm_mbm_parse_cfun_query_power_state (cfun_query_power_state_tests[i].str, &state, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cfun_query_power_state_tests[i].state, ==, state);
+ }
+}
+
+typedef struct {
+ const gchar *str;
+ MMModemMode allowed;
+ gint mbm_mode;
+} CfunQueryCurrentModeTest;
+
+static const CfunQueryCurrentModeTest cfun_query_current_mode_tests[] = {
+ { "+CFUN: 0", MM_MODEM_MODE_NONE, -1 },
+ { "+CFUN: 1", MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, -1 },
+ { "+CFUN: 4", MM_MODEM_MODE_NONE, -1 },
+ { "+CFUN: 5", MM_MODEM_MODE_2G, MBM_NETWORK_MODE_2G },
+ { "+CFUN: 6", MM_MODEM_MODE_3G, MBM_NETWORK_MODE_3G },
+};
+
+static void
+test_cfun_query_current_modes (void)
{
-#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
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cfun_query_current_mode_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMModemMode allowed = MM_MODEM_MODE_NONE;
+ gint mbm_mode = -1;
+
+ success = mm_mbm_parse_cfun_query_current_modes (cfun_query_current_mode_tests[i].str, &allowed, &mbm_mode, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cfun_query_current_mode_tests[i].allowed, ==, allowed);
+ g_assert_cmpint (cfun_query_current_mode_tests[i].mbm_mode, ==, mbm_mode);
+ }
}
+/*****************************************************************************/
+
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
- g_type_init ();
g_test_init (&argc, &argv, NULL);
- g_test_add_func ("/MM/mbm/e2ipcfg", test_e2ipcfg);
+ g_test_add_func ("/MM/mbm/e2ipcfg", test_e2ipcfg);
+ g_test_add_func ("/MM/mbm/cfun/test", test_cfun_test);
+ g_test_add_func ("/MM/mbm/cfun/query/power-state", test_cfun_query_power_state);
+ g_test_add_func ("/MM/mbm/cfun/query/current-modes", test_cfun_query_current_modes);
return g_test_run ();
}
diff --git a/plugins/meson.build b/plugins/meson.build
new file mode 100644
index 00000000..601ef715
--- /dev/null
+++ b/plugins/meson.build
@@ -0,0 +1,990 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+symbol_map = plugins_dir / 'symbol.map'
+ldflags = cc.get_supported_link_arguments('-Wl,--version-script,@0@'.format(symbol_map))
+
+# common service test support
+sources = files(
+ 'tests/test-fixture.c',
+ 'tests/test-helpers.c',
+ 'tests/test-port-context.c',
+)
+
+deps = [
+ libhelpers_dep,
+ libmm_test_generated_dep,
+]
+
+libmm_test_common = shared_library(
+ 'mm-test-common',
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps + [gio_unix_dep],
+ c_args: '-DTEST_SERVICES="@0@"'.format(source_root / 'data/tests'),
+)
+
+libmm_test_common_dep = declare_dependency(
+ include_directories: 'tests',
+ dependencies: deps,
+ link_with: libmm_test_common,
+)
+
+# plugins
+plugins = {}
+plugins_data = []
+plugins_udev_rules = []
+
+# never include static libs as deps when building
+# plugins or shared utils modules
+plugins_incs = [
+ top_inc,
+ src_inc,
+ kerneldevice_inc,
+]
+
+plugins_deps = [
+ libmm_glib_dep,
+ mbim_glib_dep,
+ qmi_glib_dep,
+]
+
+# Common Foxconn modem support library (MBIM only)
+if plugins_shared['foxconn']
+ foxconn_inc = include_directories('foxconn')
+
+ sources = files(
+ 'foxconn/mm-broadband-modem-mbim-foxconn.c',
+ 'foxconn/mm-shared.c',
+ )
+
+ c_args = [
+ '-DMM_MODULE_NAME="shared-foxconn"',
+ '-DPKGDATADIR="@0@"'.format(mm_pkgdatadir),
+ ]
+
+ plugins += {'shared-foxconn': {
+ 'plugin': false,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+endif
+
+# common icera support
+if plugins_shared['icera']
+ icera_inc = include_directories('icera')
+
+ common_c_args = '-DMM_MODULE_NAME="shared-icera"'
+
+ sources = files(
+ 'icera/mm-broadband-bearer-icera.c',
+ 'icera/mm-broadband-modem-icera.c',
+ 'icera/mm-shared.c',
+ )
+
+ plugins += {'shared-icera': {
+ 'plugin': false,
+ 'helper': {'sources': files('icera/mm-modem-helpers-icera.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('icera/tests/test-modem-helpers-icera.c'), 'include_directories': plugins_incs + [icera_inc], 'dependencies': libhelpers_dep},
+ }}
+endif
+
+# common novatel support
+if plugins_shared['novatel']
+ novatel_inc = include_directories('novatel')
+
+ sources = files(
+ 'novatel/mm-broadband-modem-novatel.c',
+ 'novatel/mm-common-novatel.c',
+ 'novatel/mm-shared.c',
+ )
+
+ plugins += {'shared-novatel': {
+ 'plugin': false,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="shared-novatel"'},
+ }}
+endif
+
+# common option support
+if plugins_shared['option']
+ sources = files(
+ 'option/mm-broadband-modem-option.c',
+ 'option/mm-shared.c',
+ 'option/mm-shared-option.c',
+ 'option/mm-sim-option.c',
+ )
+
+ plugins += {'shared-option': {
+ 'plugin': false,
+ 'module': {'sources': sources, 'include_directories': plugins_incs},
+ }}
+endif
+
+# common sierra support
+if plugins_shared['sierra']
+ sierra_inc = include_directories('sierra')
+
+ common_c_args = '-DMM_MODULE_NAME="shared-sierra"'
+
+ sources = files(
+ 'sierra/mm-broadband-bearer-sierra.c',
+ 'sierra/mm-broadband-modem-sierra.c',
+ 'sierra/mm-common-sierra.c',
+ 'sierra/mm-shared.c',
+ 'sierra/mm-sim-sierra.c',
+ )
+
+ plugins += {'shared-sierra': {
+ 'plugin': false,
+ 'helper': {'sources': files('sierra/mm-modem-helpers-sierra.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('sierra/tests/test-modem-helpers-sierra.c'), 'include_directories': sierra_inc, 'dependencies': libhelpers_dep},
+ }}
+endif
+
+# common telit support
+if plugins_shared['telit']
+ telit_inc = include_directories('telit')
+
+ common_c_args = '-DMM_MODULE_NAME="shared-telit"'
+
+ headers = files('telit/mm-modem-helpers-telit.h')
+
+ sources = files(
+ 'telit/mm-broadband-modem-telit.c',
+ 'telit/mm-common-telit.c',
+ 'telit/mm-shared.c',
+ 'telit/mm-shared-telit.c',
+ )
+
+ enums_types = 'mm-telit-enums-types'
+
+ sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: headers,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-telit-enums-types.h"',
+ )
+
+ sources += gnome.mkenums(
+ enums_types + '.h',
+ sources: headers,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include "mm-modem-helpers-telit.h"\n#ifndef __MM_TELIT_ENUMS_TYPES_H__\n#define __MM_TELIT_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_TELIT_ENUMS_TYPES_H__ */\n',
+ )
+
+ if enable_mbim
+ sources += files('telit/mm-broadband-modem-mbim-telit.c')
+ endif
+
+ plugins += {'shared-telit': {
+ 'plugin': false,
+ 'helper': {'sources': files('telit/mm-modem-helpers-telit.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs + [telit_inc], 'c_args': common_c_args},
+ 'test': {'sources': files('telit/tests/test-mm-modem-helpers-telit.c'), 'include_directories': telit_inc, 'dependencies': libmm_test_common_dep},
+ }}
+endif
+
+# common xmm support
+if plugins_shared['xmm']
+ xmm_inc = include_directories('xmm')
+
+ common_c_args = '-DMM_MODULE_NAME="shared-xmm"'
+
+ sources = files(
+ 'xmm/mm-broadband-modem-xmm.c',
+ 'xmm/mm-shared.c',
+ 'xmm/mm-shared-xmm.c',
+ )
+
+ if enable_mbim
+ sources += files('xmm/mm-broadband-modem-mbim-xmm.c')
+ endif
+
+ plugins += {'shared-xmm': {
+ 'plugin': false,
+ 'helper': {'sources': files('xmm/mm-modem-helpers-xmm.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('xmm/tests/test-modem-helpers-xmm.c'), 'include_directories': xmm_inc, 'dependencies': libhelpers_dep},
+ }}
+endif
+
+# plugin: altair lte
+if plugins_options['altair-lte']
+ common_c_args = '-DMM_MODULE_NAME="altair-lte"'
+
+ sources = files(
+ 'altair/mm-broadband-bearer-altair-lte.c',
+ 'altair/mm-broadband-modem-altair-lte.c',
+ 'altair/mm-plugin-altair-lte.c',
+ )
+
+ plugins += {'plugin-altair-lte': {
+ 'plugin': true,
+ 'helper': {'sources': files('altair/mm-modem-helpers-altair-lte.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('altair/tests/test-modem-helpers-altair-lte.c'), 'include_directories': include_directories('altair'), 'dependencies': libhelpers_dep},
+ }}
+endif
+
+# plugin: anydata
+if plugins_options['anydata']
+ sources = files(
+ 'anydata/mm-broadband-modem-anydata.c',
+ 'anydata/mm-plugin-anydata.c',
+ )
+
+ plugins += {'plugin-anydata': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="anydata"'},
+ }}
+endif
+
+# plugin: broadmobi
+if plugins_options['broadmobi']
+ c_args = [
+ '-DMM_MODULE_NAME="broadmobi"',
+ '-DTESTUDEVRULESDIR_BROADMOBI="@0@"'.format(plugins_dir / 'broadmobi'),
+ ]
+
+ plugins += {'plugin-broadmobi': {
+ 'plugin': true,
+ 'module': {'sources': files('broadmobi/mm-plugin-broadmobi.c'), 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('broadmobi/77-mm-broadmobi-port-types.rules')
+endif
+
+# plugin: cinterion (previously siemens)
+if plugins_options['cinterion']
+ common_c_args = [
+ '-DMM_MODULE_NAME="cinterion"',
+ '-DTESTUDEVRULESDIR_CINTERION="@0@"'.format(plugins_dir / 'cinterion'),
+ ]
+
+ sources = files(
+ 'cinterion/mm-broadband-bearer-cinterion.c',
+ 'cinterion/mm-broadband-modem-cinterion.c',
+ 'cinterion/mm-plugin-cinterion.c',
+ 'cinterion/mm-shared-cinterion.c',
+ )
+
+ if enable_qmi
+ sources += files('cinterion/mm-broadband-modem-qmi-cinterion.c')
+ endif
+
+ if enable_mbim
+ sources += files('cinterion/mm-broadband-modem-mbim-cinterion.c')
+ endif
+
+ plugins += {'plugin-cinterion': {
+ 'plugin': true,
+ 'helper': {'sources': files('cinterion/mm-modem-helpers-cinterion.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('cinterion/tests/test-modem-helpers-cinterion.c'), 'include_directories': plugins_incs + [include_directories('cinterion')], 'dependencies': libport_dep},
+ }}
+
+ plugins_udev_rules += files('cinterion/77-mm-cinterion-port-types.rules')
+endif
+
+# plugin: dell
+if plugins_options['dell']
+ incs = plugins_incs + [
+ foxconn_inc,
+ novatel_inc,
+ sierra_inc,
+ telit_inc,
+ xmm_inc,
+ ]
+
+ c_args = [
+ '-DMM_MODULE_NAME="dell"',
+ '-DTESTUDEVRULESDIR_DELL="@0@"'.format(plugins_dir / 'dell'),
+ ]
+
+ plugins += {'plugin-dell': {
+ 'plugin': true,
+ 'module': {'sources': files('dell/mm-plugin-dell.c'), 'include_directories': incs, 'c_args': c_args}
+ }}
+
+ plugins_udev_rules += files('dell/77-mm-dell-port-types.rules')
+endif
+
+# plugin: dlink
+if plugins_options['dlink']
+ c_args = [
+ '-DMM_MODULE_NAME="d-link"',
+ '-DTESTUDEVRULESDIR_DLINK="@0@"'.format(plugins_dir / 'dlink'),
+ ]
+
+ plugins += {'plugin-dlink': {
+ 'plugin': true,
+ 'module': {'sources': files('dlink/mm-plugin-dlink.c'), 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('dlink/77-mm-dlink-port-types.rules')
+endif
+
+# plugin: fibocom
+if plugins_options['fibocom']
+ c_args = [
+ '-DMM_MODULE_NAME="fibocom"',
+ '-DTESTUDEVRULESDIR_FIBOCOM="@0@"'.format(plugins_dir / 'fibocom'),
+ ]
+
+ plugins += {'plugin-fibocom': {
+ 'plugin': true,
+ 'module': {'sources': files('fibocom/mm-plugin-fibocom.c'), 'include_directories': plugins_incs + [xmm_inc], 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('fibocom/77-mm-fibocom-port-types.rules')
+endif
+
+# plugin: foxconn
+if plugins_options['foxconn']
+ foxconn_dir = plugins_dir / 'foxconn'
+
+ c_args = [
+ '-DMM_MODULE_NAME="foxconn"',
+ '-DTESTUDEVRULESDIR_FOXCONN="@0@"'.format(foxconn_dir),
+ '-DTESTKEYFILE_FOXCONN_T77W968="@0@"'.format(foxconn_dir / 'mm-foxconn-t77w968-carrier-mapping.conf'),
+ ]
+
+ plugins += {'plugin-foxconn': {
+ 'plugin': true,
+ 'module': {'sources': files('foxconn/mm-plugin-foxconn.c'), 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_data += files(
+ 'foxconn/mm-foxconn-t77w968-carrier-mapping.conf',
+ )
+ plugins_udev_rules += files('foxconn/77-mm-foxconn-port-types.rules')
+endif
+
+# plugin: generic
+if plugins_options['generic']
+ # FIXME
+ '''
+ 15/16 test-service-generic FAIL 0.02s killed by signal 5 SIGTRAP
+ >>> MALLOC_PERTURB_=124 /ModemManager/_build/plugins/test-service-generic
+ ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― ✀ ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
+ stdout:
+ # random seed: R02S5d0d577043f61f2806f319a6510e83a4
+ 1..1
+ # Start of MM tests
+ # Start of Service tests
+ # Start of Generic tests
+ Bail out! FATAL-ERROR: Error starting ModemManager in test bus: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.ModemManager1 was not provided by any .service files
+ stderr:
+
+ ** (/ModemManager/_build/plugins/test-service-generic:36444): ERROR **: 21:06:16.248: Error starting ModemManager in test bus: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.ModemManager1 was not provided by any .service files
+ cleaning up pid 36446
+ '''
+
+ plugins += {'plugin-generic': {
+ 'plugin': true,
+ 'module': {'sources': files('generic/mm-plugin-generic.c'), 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="generic"'},
+ 'test': {'sources': files('generic/tests/test-service-generic.c'), 'include_directories': include_directories('generic'), 'dependencies': libmm_test_common_dep, 'c_args': '-DCOMMON_GSM_PORT_CONF="@0@"'.format(plugins_dir / 'tests/gsm-port.conf')},
+ }}
+endif
+
+# plugin: gosuncn
+if plugins_options['gosuncn']
+ c_args = [
+ '-DMM_MODULE_NAME="gosuncn"',
+ '-DTESTUDEVRULESDIR_GOSUNCN="@0@"'.format(plugins_dir / 'gosuncn'),
+ ]
+
+ plugins += {'plugin-gosuncn': {
+ 'plugin': true,
+ 'module': {'sources': files('gosuncn/mm-plugin-gosuncn.c'), 'include_directories': plugins_incs, 'c_args': c_args}
+ }}
+
+ plugins_udev_rules += files('gosuncn/77-mm-gosuncn-port-types.rules')
+endif
+
+# plugin: haier
+if plugins_options['haier']
+ c_args = [
+ '-DMM_MODULE_NAME="haier"',
+ '-DTESTUDEVRULESDIR_HAIER="@0@"'.format(plugins_dir / 'haier'),
+ ]
+
+ plugins += {'plugin-haier': {
+ 'plugin': true,
+ 'module': {'sources': files('haier/mm-plugin-haier.c'), 'include_directories': plugins_incs, 'c_args': c_args}
+ }}
+
+ plugins_udev_rules += files('haier/77-mm-haier-port-types.rules')
+endif
+
+# plugin: huawei
+if plugins_options['huawei']
+ huawei_inc = include_directories('huawei')
+
+ common_c_args = ['-DTESTUDEVRULESDIR_HUAWEI="@0@"'.format(plugins_dir / 'huawei')]
+
+ headers = files('huawei/mm-modem-helpers-huawei.h')
+
+ sources = files(
+ 'huawei/mm-broadband-bearer-huawei.c',
+ 'huawei/mm-broadband-modem-huawei.c',
+ 'huawei/mm-plugin-huawei.c',
+ 'huawei/mm-sim-huawei.c',
+ )
+
+ enums_types = 'mm-huawei-enums-types'
+
+ enums_sources = []
+ enums_sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: headers,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-huawei-enums-types.h"',
+ )
+
+ enums_sources += gnome.mkenums(
+ enums_types + '.h',
+ sources: headers,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include "mm-modem-helpers-huawei.h"\n#ifndef __MM_HUAWEI_ENUMS_TYPES_H__\n#define __MM_HUAWEI_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_HUAWEI_ENUMS_TYPES_H__ */\n',
+ )
+
+ plugins += {'plugin-huawei': {
+ 'plugin': true,
+ 'helper': {'sources': files('huawei/mm-modem-helpers-huawei.c'), 'include_directories': plugins_incs + [huawei_inc], 'c_args': common_c_args + ['-DMM_MODULE_NAME="huawei"']},
+ 'module': {'sources': sources + enums_sources, 'include_directories': plugins_incs + [huawei_inc], 'c_args': common_c_args + ['-DMM_MODULE_NAME="huawei"']},
+ 'test': {'sources': files('huawei/tests/test-modem-helpers-huawei.c') + enums_sources, 'include_directories': huawei_inc, 'dependencies': libhelpers_dep},
+ }}
+
+ plugins_udev_rules += files('huawei/77-mm-huawei-net-port-types.rules')
+endif
+
+# plugin: iridium
+if plugins_options['iridium']
+ sources = files(
+ 'iridium/mm-bearer-iridium.c',
+ 'iridium/mm-broadband-modem-iridium.c',
+ 'iridium/mm-plugin-iridium.c',
+ 'iridium/mm-sim-iridium.c',
+ )
+
+ plugins += {'plugin-iridium': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="iridium"'},
+ }}
+endif
+
+# plugin: linktop
+if plugins_options['linktop']
+ common_c_args = '-DMM_MODULE_NAME="linktop"'
+
+ sources = files(
+ 'linktop/mm-plugin-linktop.c',
+ 'linktop/mm-broadband-modem-linktop.c',
+ )
+
+ plugins += {'plugin-linktop': {
+ 'plugin': true,
+ 'helper': {'sources': files('linktop/mm-modem-helpers-linktop.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('linktop/tests/test-modem-helpers-linktop.c'), 'include_directories': include_directories('linktop'), 'dependencies': libhelpers_dep},
+ }}
+endif
+
+# plugin: longcheer (and rebranded dongles)
+if plugins_options['longcheer']
+ sources = files(
+ 'longcheer/mm-broadband-modem-longcheer.c',
+ 'longcheer/mm-plugin-longcheer.c',
+ )
+
+ c_args = [
+ '-DMM_MODULE_NAME="longcheer"',
+ '-DTESTUDEVRULESDIR_LONGCHEER="@0@"'.format(plugins_dir / 'longcheer'),
+ ]
+
+ plugins += {'plugin-longcheer': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('longcheer/77-mm-longcheer-port-types.rules')
+endif
+
+# plugin: ericsson mbm
+if plugins_options['mbm']
+ common_c_args = ['-DTESTUDEVRULESDIR_MBM="@0@"'.format(plugins_dir / 'mbm')]
+
+ sources = files(
+ 'mbm/mm-broadband-bearer-mbm.c',
+ 'mbm/mm-broadband-modem-mbm.c',
+ 'mbm/mm-plugin-mbm.c',
+ 'mbm/mm-sim-mbm.c',
+ )
+
+ plugins += {'plugin-ericsson-mbm': {
+ 'plugin': true,
+ 'helper': {'sources': files('mbm/mm-modem-helpers-mbm.c'), 'include_directories': plugins_incs, 'c_args': common_c_args + ['-DMM_MODULE_NAME="ericsson-mbm"']},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args + ['-DMM_MODULE_NAME="ericsson-mbm"']},
+ 'test': {'sources': files('mbm/tests/test-modem-helpers-mbm.c'), 'include_directories': plugins_incs + [include_directories('mbm')], 'dependencies': libhelpers_dep},
+ }}
+
+ plugins_udev_rules += files('mbm/77-mm-ericsson-mbm.rules')
+endif
+
+# plugin: motorola
+if plugins_options['motorola']
+ sources = files(
+ 'motorola/mm-broadband-modem-motorola.c',
+ 'motorola/mm-plugin-motorola.c',
+ )
+
+ plugins += {'plugin-motorola': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="motorola"'},
+ }}
+endif
+
+# plugin: mtk
+if plugins_options['mtk']
+ sources = files(
+ 'mtk/mm-broadband-modem-mtk.c',
+ 'mtk/mm-plugin-mtk.c',
+ )
+
+ c_args = [
+ '-DMM_MODULE_NAME="motorola"',
+ '-DTESTUDEVRULESDIR_MTK="@0@"'.format(plugins_dir / 'mtk'),
+ ]
+
+ plugins += {'plugin-mtk': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('mtk/77-mm-mtk-port-types.rules')
+endif
+
+# plugin: nokia
+if plugins_options['nokia']
+ sources = files(
+ 'nokia/mm-broadband-modem-nokia.c',
+ 'nokia/mm-plugin-nokia.c',
+ 'nokia/mm-sim-nokia.c',
+ )
+
+ plugins += {'plugin-nokia': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="nokia"'},
+ }}
+endif
+
+# plugin: nokia (icera)
+if plugins_options['nokia-icera']
+ c_args = [
+ '-DMM_MODULE_NAME="nokia-icera"',
+ '-DTESTUDEVRULESDIR_NOKIA_ICERA="@0@"'.format(plugins_dir / 'nokia'),
+ ]
+
+ plugins += {'plugin-nokia-icera': {
+ 'plugin': true,
+ 'module': {'sources': files('nokia/mm-plugin-nokia-icera.c'), 'include_directories': plugins_incs + [icera_inc], 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('nokia/77-mm-nokia-port-types.rules')
+endif
+
+# plugin: novatel non-lte
+if plugins_options['novatel']
+ plugins += {'plugin-novatel': {
+ 'plugin': true,
+ 'module': {'sources': files('novatel/mm-plugin-novatel.c'), 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="novatel"'},
+ }}
+endif
+
+# plugin: novatel lte
+if plugins_options['novatel-lte']
+ sources = files(
+ 'novatel/mm-plugin-novatel-lte.c',
+ 'novatel/mm-broadband-modem-novatel-lte.c',
+ 'novatel/mm-broadband-bearer-novatel-lte.c',
+ 'novatel/mm-sim-novatel-lte.c',
+ )
+
+ plugins += {'plugin-novatel-lte': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="novatel-lte"'},
+ }}
+endif
+
+# plugin: option
+if plugins_options['option']
+ plugins += {'plugin-option': {
+ 'plugin': true,
+ 'module': {'sources': files('option/mm-plugin-option.c'), 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="option"'},
+ }}
+endif
+
+# plugin: option hso
+if plugins_options['option-hso']
+ sources = files(
+ 'option/mm-plugin-hso.c',
+ 'option/mm-broadband-bearer-hso.c',
+ 'option/mm-broadband-modem-hso.c',
+ )
+
+ plugins += {'plugin-option-hso': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="option-hso"'},
+ }}
+endif
+
+# plugin: pantech
+if plugins_options['pantech']
+ sources = files(
+ 'pantech/mm-broadband-modem-pantech.c',
+ 'pantech/mm-plugin-pantech.c',
+ 'pantech/mm-sim-pantech.c',
+ )
+
+ plugins += {'plugin-pantech': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="pantech"'},
+ }}
+endif
+
+# plugin: qcom-soc
+if plugins_options['qcom-soc']
+ sources = files(
+ 'qcom-soc/mm-broadband-modem-qmi-qcom-soc.c',
+ 'qcom-soc/mm-plugin-qcom-soc.c',
+ )
+
+ c_args = [
+ '-DMM_MODULE_NAME="qcom-soc"',
+ '-DTESTUDEVRULESDIR_QCOM_SOC="@0@"'.format(plugins_dir / 'qcom-soc'),
+ ]
+
+ plugins += {'plugin-qcom-soc': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('qcom-soc/77-mm-qcom-soc.rules')
+endif
+
+# plugin: quectel
+if plugins_options['quectel']
+ common_c_args = ['-DTESTUDEVRULESDIR_QUECTEL="@0@"'.format(plugins_dir / 'quectel')]
+
+ sources = files(
+ 'quectel/mm-broadband-modem-quectel.c',
+ 'quectel/mm-plugin-quectel.c',
+ 'quectel/mm-shared-quectel.c',
+ )
+
+ if enable_qmi
+ sources += files('quectel/mm-broadband-modem-qmi-quectel.c')
+ endif
+
+ if enable_mbim
+ sources += files('quectel/mm-broadband-modem-mbim-quectel.c')
+ endif
+
+ plugins += {'plugin-quectel': {
+ 'plugin': true,
+ 'helper': {'sources': files('quectel/mm-modem-helpers-quectel.c'), 'include_directories': plugins_incs, 'c_args': common_c_args + ['-DMM_MODULE_NAME="quectel"']},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args + ['-DMM_MODULE_NAME="quectel"']},
+ 'test': {'sources': files('quectel/tests/test-modem-helpers-quectel.c'), 'include_directories': include_directories('quectel'), 'dependencies': libhelpers_dep},
+ }}
+
+ plugins_udev_rules += files('quectel/77-mm-quectel-port-types.rules')
+endif
+
+# plugin: samsung
+if plugins_options['samsung']
+ sources = files(
+ 'samsung/mm-broadband-modem-samsung.c',
+ 'samsung/mm-plugin-samsung.c',
+ )
+
+ plugins += {'plugin-samsung': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs + [icera_inc], 'c_args': '-DMM_MODULE_NAME="samsung"'},
+ }}
+endif
+
+# plugin: sierra (legacy)
+if plugins_options['sierra-legacy']
+ sources = files(
+ 'sierra/mm-broadband-modem-sierra-icera.c',
+ 'sierra/mm-plugin-sierra-legacy.c',
+ )
+
+ plugins += {'plugin-sierra-legacy': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs + [icera_inc], 'c_args': '-DMM_MODULE_NAME="sierra-legacy"'},
+ }}
+endif
+
+# plugin: sierra (new QMI or MBIM modems)
+if plugins_options['sierra']
+ plugins += {'plugin-sierra': {
+ 'plugin': true,
+ 'module': {'sources': files('sierra/mm-plugin-sierra.c'), 'include_directories': plugins_incs + [xmm_inc], 'c_args': '-DMM_MODULE_NAME="sierra"'},
+ }}
+
+ plugins_udev_rules += files('sierra/77-mm-sierra.rules')
+endif
+
+# plugin: simtech
+if plugins_options['simtech']
+ common_c_args = ['-DTESTUDEVRULESDIR_SIMTECH="@0@"'.format(plugins_dir / 'simtech')]
+
+ sources = files(
+ 'simtech/mm-broadband-modem-simtech.c',
+ 'simtech/mm-plugin-simtech.c',
+ 'simtech/mm-shared-simtech.c',
+ )
+
+ if enable_qmi
+ sources += files('simtech/mm-broadband-modem-qmi-simtech.c')
+ endif
+
+ plugins += {'plugin-simtech': {
+ 'plugin': true,
+ 'helper': {'sources': files('simtech/mm-modem-helpers-simtech.c'), 'include_directories': plugins_incs, 'c_args': common_c_args + ['-DMM_MODULE_NAME="simtech"']},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args + ['-DMM_MODULE_NAME="quectel"']},
+ 'test': {'sources': files('simtech/tests/test-modem-helpers-simtech.c'), 'include_directories': plugins_incs + [include_directories('simtech')], 'dependencies': libport_dep},
+ }}
+
+ plugins_udev_rules += files('simtech/77-mm-simtech-port-types.rules')
+endif
+
+# plugin: telit
+if plugins_options['telit']
+ c_args = [
+ '-DMM_MODULE_NAME="telit"',
+ '-DTESTUDEVRULESDIR_TELIT="@0@"'.format(plugins_dir / 'telit'),
+ ]
+
+ plugins += {'plugin-telit': {
+ 'plugin': true,
+ 'module': {'sources': files('telit/mm-plugin-telit.c'), 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('telit/77-mm-telit-port-types.rules')
+endif
+
+# plugin: thuraya xt
+if plugins_options['thuraya']
+ common_c_args = ['-DMM_MODULE_NAME="thuraya"']
+
+ sources = files(
+ 'thuraya/mm-broadband-modem-thuraya.c',
+ 'thuraya/mm-plugin-thuraya.c',
+ )
+
+ plugins += {'plugin-thuraya': {
+ 'plugin': true,
+ 'helper': {'sources': files('thuraya/mm-modem-helpers-thuraya.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'test': {'sources': files('thuraya/tests/test-mm-modem-helpers-thuraya.c'), 'include_directories': include_directories('thuraya'), 'dependencies': libhelpers_dep},
+ }}
+endif
+
+# plugin: tplink
+if plugins_options['tplink']
+ c_args = [
+ '-DMM_MODULE_NAME="tp-link"',
+ '-DTESTUDEVRULESDIR_TPLINK="@0@"'.format(plugins_dir / 'tplink'),
+ ]
+
+ plugins += {'plugin-tplink': {
+ 'plugin': true,
+ 'module': {'sources': files('tplink/mm-plugin-tplink.c'), 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('tplink/77-mm-tplink-port-types.rules')
+endif
+
+# plugin: u-blox
+if plugins_options['ublox']
+ ublox_inc = include_directories('ublox')
+
+ common_c_args = '-DMM_MODULE_NAME="u-blox"'
+
+ headers = files('ublox/mm-modem-helpers-ublox.h')
+
+ sources = files(
+ 'ublox/mm-broadband-bearer-ublox.c',
+ 'ublox/mm-broadband-modem-ublox.c',
+ 'ublox/mm-plugin-ublox.c',
+ 'ublox/mm-sim-ublox.c',
+ )
+
+ enums_types = 'mm-ublox-enums-types'
+
+ sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: headers,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-ublox-enums-types.h"',
+ )
+
+ sources += gnome.mkenums(
+ enums_types + '.h',
+ sources: headers,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include "mm-modem-helpers-ublox.h"\n#ifndef __MM_UBLOX_ENUMS_TYPES_H__\n#define __MM_UBLOX_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_UBLOX_ENUMS_TYPES_H__ */\n',
+ )
+
+ plugins += {'plugin-ublox': {
+ 'plugin': true,
+ 'helper': {'sources': files('ublox/mm-modem-helpers-ublox.c'), 'include_directories': plugins_incs, 'c_args': common_c_args},
+ 'module': {'sources': sources, 'include_directories': plugins_incs + [ublox_inc], 'c_args': common_c_args},
+ 'test': {'sources': files('ublox/tests/test-modem-helpers-ublox.c'), 'include_directories': ublox_inc, 'dependencies': libmm_test_common_dep},
+ }}
+
+ plugins_udev_rules += files('ublox/77-mm-ublox-port-types.rules')
+endif
+
+# plugin: via
+if plugins_options['via']
+ sources = files(
+ 'via/mm-broadband-modem-via.c',
+ 'via/mm-plugin-via.c',
+ )
+
+ plugins += {'plugin-via': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="via"'},
+ }}
+endif
+
+# plugin: wavecom (now sierra airlink)
+if plugins_options['wavecom']
+ sources = files(
+ 'wavecom/mm-broadband-modem-wavecom.c',
+ 'wavecom/mm-plugin-wavecom.c',
+ )
+
+ plugins += {'plugin-wavecom': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': '-DMM_MODULE_NAME="wavecom"'},
+ }}
+endif
+
+# plugin: alcatel/TCT/JRD x220D and possibly others
+if plugins_options['x22x']
+ sources = files(
+ 'x22x/mm-broadband-modem-x22x.c',
+ 'x22x/mm-plugin-x22x.c',
+ )
+
+ c_args = [
+ '-DMM_MODULE_NAME="x22x"',
+ '-DTESTUDEVRULESDIR_X22X="@0@"'.format(plugins_dir / 'x22x'),
+ ]
+
+ plugins += {'plugin-x22x': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs, 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('x22x/77-mm-x22x-port-types.rules')
+endif
+
+# plugin: zte
+if plugins_options['zte']
+ sources = files(
+ 'zte/mm-broadband-modem-zte.c',
+ 'zte/mm-broadband-modem-zte-icera.c',
+ 'zte/mm-common-zte.c',
+ 'zte/mm-plugin-zte.c',
+ )
+
+ c_args = [
+ '-DMM_MODULE_NAME="zte"',
+ '-DTESTUDEVRULESDIR_ZTE="@0@"'.format(plugins_dir / 'zte'),
+ ]
+
+ plugins += {'plugin-zte': {
+ 'plugin': true,
+ 'module': {'sources': sources, 'include_directories': plugins_incs + [icera_inc], 'c_args': c_args},
+ }}
+
+ plugins_udev_rules += files('zte/77-mm-zte-port-types.rules')
+endif
+
+foreach plugin_name, plugin_data: plugins
+ libpluginhelpers = []
+ if plugin_data.has_key('helper')
+ libpluginhelpers = static_library(
+ 'helpers-' + plugin_name,
+ dependencies: plugins_deps,
+ kwargs: plugin_data['helper'],
+ )
+ endif
+
+ module_args = plugin_data['module']
+ if plugin_data['plugin']
+ module_args += {
+ 'link_args': ldflags,
+ 'link_depends': symbol_map,
+ }
+ endif
+
+ shared_module(
+ 'mm-' + plugin_name,
+ dependencies: plugins_deps,
+ link_with: libpluginhelpers,
+ kwargs: module_args,
+ install: true,
+ install_dir: mm_pkglibdir,
+ )
+
+ if plugin_data.has_key('test')
+ test_unit = 'test-' + plugin_name
+
+ exe = executable(
+ test_unit,
+ link_with: libpluginhelpers,
+ kwargs: plugin_data['test'],
+ )
+
+ test(test_unit, exe)
+ endif
+endforeach
+
+install_data(
+ plugins_data,
+ install_dir: mm_pkgdatadir,
+)
+
+install_data(
+ plugins_udev_rules,
+ install_dir: udev_rulesdir,
+)
+
+# udev-rules and keyfiles tests
+test_units = {
+ 'udev-rules': {'include_directories': top_inc, 'dependencies': libkerneldevice_dep},
+ 'keyfiles': {'include_directories': [top_inc, src_inc], 'dependencies': libmm_glib_dep},
+}
+
+foreach name, data: test_units
+ test_name = 'test-' + name
+
+ exe = executable(
+ test_name,
+ sources: 'tests/@0@.c'.format(test_name),
+ kwargs: data,
+ )
+
+ test(test_name, exe)
+endforeach
diff --git a/plugins/motorola/mm-broadband-modem-motorola.c b/plugins/motorola/mm-broadband-modem-motorola.c
index 10d04d81..8ebd2114 100644
--- a/plugins/motorola/mm-broadband-modem-motorola.c
+++ b/plugins/motorola/mm-broadband-modem-motorola.c
@@ -50,6 +50,9 @@ mm_broadband_modem_motorola_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/motorola/mm-plugin-motorola.c b/plugins/motorola/mm-plugin-motorola.c
index f25d9a29..368e898b 100644
--- a/plugins/motorola/mm-plugin-motorola.c
+++ b/plugins/motorola/mm-plugin-motorola.c
@@ -27,21 +27,21 @@
G_DEFINE_TYPE (MMPluginMotorola, mm_plugin_motorola, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_motorola_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_motorola_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -62,7 +62,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_MOTOROLA,
- MM_PLUGIN_NAME, "Motorola",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_PRODUCT_IDS, product_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/mtk/77-mm-mtk-port-types.rules b/plugins/mtk/77-mm-mtk-port-types.rules
index 26f01f2d..96939fd4 100644
--- a/plugins/mtk/77-mm-mtk-port-types.rules
+++ b/plugins/mtk/77-mm-mtk-port-types.rules
@@ -1,34 +1,34 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_mtk_port_types_end"
-
+ACTION!="add|change|move|bind", GOTO="mm_mtk_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0e8d", GOTO="mm_mtk_port_types_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="2001", GOTO="mm_dlink_port_types_vendorcheck"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="07d1", GOTO="mm_dlink_port_types_vendorcheck"
GOTO="mm_mtk_port_types_end"
# MediaTek devices ---------------------------
LABEL="mm_mtk_port_types_vendorcheck"
-ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_MTK_MODEM_PORT}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_MTK_AT_PORT}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a1", ENV{ID_MM_MTK_TAGGED}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_MTK_MODEM_PORT}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_MTK_AT_PORT}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a2", ENV{ID_MM_MTK_TAGGED}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_MTK_MODEM_PORT}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_MTK_AT_PORT}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a4", ENV{ID_MM_MTK_TAGGED}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_MTK_MODEM_PORT}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_MTK_AT_PORT}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a5", ENV{ID_MM_MTK_TAGGED}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_MTK_MODEM_PORT}="1"
-ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_MTK_AT_PORT}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0e8d", ATTRS{idProduct}=="00a7", ENV{ID_MM_MTK_TAGGED}="1"
GOTO="mm_mtk_port_types_end"
@@ -36,11 +36,16 @@ GOTO="mm_mtk_port_types_end"
# D-Link devices ---------------------------
LABEL="mm_dlink_port_types_vendorcheck"
-ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# D-Link DWM-156 A3
+ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="7e11", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="7e11", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="07d1", ATTRS{idProduct}=="7e11", ENV{ID_MM_MTK_TAGGED}="1"
# D-Link DWM-156 A5 (and later?)
-ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_MTK_MODEM_PORT}="1"
-ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_MTK_AT_PORT}="1"
+ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="2001", ATTRS{idProduct}=="7d00", ENV{ID_MM_MTK_TAGGED}="1"
GOTO="mm_mtk_port_types_end"
diff --git a/plugins/mtk/mm-broadband-modem-mtk.c b/plugins/mtk/mm-broadband-modem-mtk.c
index df900463..131f8daa 100644
--- a/plugins/mtk/mm-broadband-modem-mtk.c
+++ b/plugins/mtk/mm-broadband-modem-mtk.c
@@ -24,7 +24,7 @@
#include <ctype.h>
#include "ModemManager.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
@@ -59,15 +59,13 @@ load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_unlock_retries_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -79,10 +77,8 @@ load_unlock_retries_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -103,18 +99,16 @@ load_unlock_retries_ready (MMBaseModem *self,
MM_CORE_ERROR_FAILED,
"Failed to match EPINC response: %s", response);
}
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
} else if (!mm_get_int_from_match_info (match_info, 1, &pin1) ||
!mm_get_int_from_match_info (match_info, 2, &pin2) ||
!mm_get_int_from_match_info (match_info, 3, &puk1) ||
!mm_get_int_from_match_info (match_info, 4, &puk2)) {
- g_set_error (&error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse the EPINC response: '%s'",
- response);
-
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse the EPINC response: '%s'",
+ response);
} else {
retries = mm_unlock_retries_new ();
@@ -123,18 +117,12 @@ load_unlock_retries_ready (MMBaseModem *self,
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- retries,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, retries, g_object_unref);
}
+ g_object_unref (task);
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
- return;
}
static void
@@ -148,10 +136,7 @@ load_unlock_retries (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_unlock_retries_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_unlock_retries));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -160,15 +145,15 @@ modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -176,15 +161,12 @@ modem_after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
+ task = g_task_new (self, NULL, callback, user_data);
/* For device, 3 second is OK for SIM get ready */
- g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, result);
+ g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
@@ -193,7 +175,7 @@ modem_after_sim_unlock (MMIfaceModem *self,
static void
get_supported_modes_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
@@ -207,10 +189,8 @@ get_supported_modes_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Fail to get response %s", response);
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -241,8 +221,7 @@ get_supported_modes_ready (MMBaseModem *self,
response);
g_regex_unref (r);
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
return;
}
@@ -293,17 +272,11 @@ get_supported_modes_ready (MMBaseModem *self,
* +GCAP, +WS64 not support completely, generic filter will filter
* out 4G modes.
*/
- g_simple_async_result_set_op_res_gpointer (simple,
- combinations,
- (GDestroyNotify)g_array_unref);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
g_regex_unref (r);
- if (match_info)
- g_match_info_free (match_info);
- return;
+ g_match_info_free (match_info);
}
static void
@@ -311,17 +284,12 @@ load_supported_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- "+EGMR=0,0",
- 3,
- FALSE,
- (GAsyncReadyCallback)get_supported_modes_ready,
- g_simple_async_result_new (
- G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+EGMR=0,0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)get_supported_modes_ready,
+ g_task_new (self, NULL, callback, user_data));
}
static GArray *
@@ -329,10 +297,7 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
/*****************************************************************************/
@@ -415,7 +380,7 @@ load_current_modes_finish (MMIfaceModem *self,
break;
default:
result = FALSE;
- mm_dbg ("Not supported allowed mode %d", erat_mode);
+ mm_obj_dbg (self, "unsupported allowed mode reported in +ERAT: %d", erat_mode);
goto done;
}
@@ -434,15 +399,14 @@ load_current_modes_finish (MMIfaceModem *self,
break;
default:
result = FALSE;
- mm_dbg ("Not supported preferred mode %d", erat_pref);
+ mm_obj_dbg (self, "unsupported preferred mode %d", erat_pref);
goto done;
}
done:
if (r)
g_regex_unref (r);
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
return result;
}
@@ -468,13 +432,13 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBroadbandModemMtk *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
@@ -482,11 +446,11 @@ allowed_mode_update_ready (MMBroadbandModemMtk *self,
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -496,15 +460,12 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint erat_mode = -1;
gint erat_pref = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (allowed == MM_MODEM_MODE_2G) {
erat_mode = 0;
@@ -542,18 +503,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (
+ task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -564,7 +523,7 @@ set_current_modes (MMIfaceModem *self,
30,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -584,10 +543,10 @@ mtk_80_signal_changed (MMPortSerialAt *port,
if (quality == 99)
quality = 0;
else
- quality = CLAMP(quality, 0, 31) * 100 / 31;
+ quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31;
- mm_dbg ("6280 signal quality URC received: quality = %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ mm_obj_dbg (self, "6280 signal quality URC received: %u", quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -603,10 +562,10 @@ mtk_90_2g_signal_changed (MMPortSerialAt *port,
if (quality == 99)
quality = 0;
else
- quality = CLAMP (quality, 0, 63) * 100 / 63;
+ quality = MM_CLAMP_HIGH (quality, 63) * 100 / 63;
- mm_dbg ("2G signal quality URC received: quality = %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ mm_obj_dbg (self, "2G signal quality URC received: %u", quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -619,10 +578,10 @@ mtk_90_3g_signal_changed (MMPortSerialAt *port,
if (!mm_get_uint_from_match_info (match_info, 1, &quality))
return;
- quality = CLAMP (quality, 0, 96) * 100 / 96;
+ quality = MM_CLAMP_HIGH (quality, 96) * 100 / 96;
- mm_dbg ("3G signal quality URC received: quality = %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ mm_obj_dbg (self, "3G signal quality URC received: %u", quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -635,10 +594,10 @@ mtk_90_4g_signal_changed (MMPortSerialAt *port,
if (!mm_get_uint_from_match_info (match_info, 1, &quality))
return;
- quality = CLAMP (quality, 0, 97) * 100 / 97;
+ quality = MM_CLAMP_HIGH (quality, 97) * 100 / 97;
- mm_dbg ("4G signal quality URC received: quality = %u", quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ mm_obj_dbg (self, "4G signal quality URC received: %u", quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -652,7 +611,7 @@ set_unsolicited_events_handlers (MMBroadbandModemMtk *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable/disable unsolicited events in given port */
- for (i = 0; i < 2; i++){
+ for (i = 0; i < G_N_ELEMENTS (ports); i++){
if(!ports[i])
continue;
@@ -698,27 +657,25 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MTK (self),
TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -726,33 +683,26 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
-
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -760,13 +710,6 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
-
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_MTK (self), FALSE);
@@ -774,59 +717,57 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
/* enable signal URC */
- {"+ECSQ=2", 5, FALSE, NULL},
- {NULL}
+ { "+ECSQ=2", 5, FALSE, NULL },
+ { NULL }
};
static const MMBaseModemAtCommand unsolicited_disable_sequence[] = {
/* disable signal URC */
- {"+ECSQ=0", 5, FALSE, NULL},
- {NULL}
+ { "+ECSQ=0" , 5, FALSE, NULL },
+ { NULL }
};
static void
own_enable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
/* Our own enable now */
mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- unsolicited_enable_sequence,
- NULL,NULL,NULL,
- (GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+ unsolicited_enable_sequence,
+ NULL,NULL,NULL,
+ (GAsyncReadyCallback)own_enable_unsolicited_events_ready,
+ task);
}
static void
@@ -834,18 +775,11 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
-
/* Chain up parent's enable */
iface_modem_3gpp_parent->enable_unsolicited_events (
- self,
- (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- result);
+ self,
+ (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
+ g_task_new (self, NULL, callback, user_data));
}
static gboolean
@@ -853,44 +787,43 @@ modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
own_disable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Next, chain up parent's disable */
iface_modem_3gpp_parent->disable_unsolicited_events (
- MM_IFACE_MODEM_3GPP (self),
- (GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
+ task);
}
static void
@@ -898,21 +831,14 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
-
/* Our own disable first */
mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- unsolicited_disable_sequence,
- NULL, NULL, NULL,
- (GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- result);
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
+ unsolicited_disable_sequence,
+ NULL, NULL, NULL,
+ (GAsyncReadyCallback)own_disable_unsolicited_events_ready,
+ g_task_new (self, NULL, callback, user_data));
}
static gboolean
@@ -920,7 +846,7 @@ modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
/*****************************************************************************/
@@ -950,6 +876,9 @@ mm_broadband_modem_mtk_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/mtk/mm-plugin-mtk.c b/plugins/mtk/mm-plugin-mtk.c
index d734c8a3..5a4ea1c1 100644
--- a/plugins/mtk/mm-plugin-mtk.c
+++ b/plugins/mtk/mm-plugin-mtk.c
@@ -21,70 +21,33 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
#include "mm-plugin-mtk.h"
#include "mm-broadband-modem-mtk.h"
G_DEFINE_TYPE (MMPluginMtk, mm_plugin_mtk, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* MTK done */
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_mtk_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_mtk_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
-/* MTK done */
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- GUdevDevice *port;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-
- port = mm_port_probe_peek_port (probe);
-
- if (mm_port_probe_is_at (probe)) {
- /* Get port type from udev */
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_MTK_AT_PORT")) {
- mm_dbg ("MTK: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_MTK_MODEM_PORT")) {
- mm_dbg ("MTK: Modem port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- }
- }
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- mm_port_probe_get_port_type (probe),
- pflags,
- error);
-}
-
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
@@ -97,7 +60,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_MTK,
- MM_PLUGIN_NAME, "MTK",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags,
MM_PLUGIN_ALLOWED_AT, TRUE,
@@ -115,5 +78,4 @@ mm_plugin_mtk_class_init (MMPluginMtkClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/nokia/77-mm-nokia-port-types.rules b/plugins/nokia/77-mm-nokia-port-types.rules
index f3c44795..daa8bd4a 100644
--- a/plugins/nokia/77-mm-nokia-port-types.rules
+++ b/plugins/nokia/77-mm-nokia-port-types.rules
@@ -1,40 +1,38 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_nokia_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_nokia_port_types_end"
-
-SUBSYSTEMS=="usb", ATTRS{idVendor}=="0421", GOTO="mm_nokia_port_types_vendorcheck"
+ACTION!="add|change|move|bind", GOTO="mm_nokia_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="0421", GOTO="mm_nokia_port_types"
GOTO="mm_nokia_port_types_end"
-LABEL="mm_nokia_port_types_vendorcheck"
+LABEL="mm_nokia_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# For Nokia Internet Sticks (CS-xx) the modem/PPP port appears to always be USB interface 1
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="060D", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="060D", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0611", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0611", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061A", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061A", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061B", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061B", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061F", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="061F", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0619", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0619", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0620", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0620", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0623", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0623", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0624", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0624", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0625", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="0625", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062A", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062A", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062E", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062E", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
-ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062F", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_NOKIA_PORT_TYPE_MODEM}="1"
+ATTRS{idVendor}=="0421", ATTRS{idProduct}=="062F", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
LABEL="mm_nokia_port_types_end"
diff --git a/plugins/nokia/mm-broadband-modem-nokia.c b/plugins/nokia/mm-broadband-modem-nokia.c
index 21e76dd8..fd608868 100644
--- a/plugins/nokia/mm-broadband-modem-nokia.c
+++ b/plugins/nokia/mm-broadband-modem-nokia.c
@@ -25,7 +25,6 @@
#include "ModemManager.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
@@ -75,20 +74,6 @@ typedef struct {
guint mask;
} AccessTechInfo;
-static void
-access_tech_set_result (GSimpleAsyncResult *simple,
- MMModemAccessTechnology act,
- guint mask)
-{
- AccessTechInfo *info;
-
- info = g_new (AccessTechInfo, 1);
- info->act = act;
- info->mask = mask;
-
- g_simple_async_result_set_op_res_gpointer (simple, info, g_free);
-}
-
static gboolean
load_access_technologies_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -96,40 +81,41 @@ load_access_technologies_finish (MMIfaceModem *self,
guint *mask,
GError **error)
{
- AccessTechInfo *info;
+ GError *inner_error = NULL;
+ gssize value;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
- info = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- g_assert (info);
- *access_technologies = info->act;
- *mask = info->mask;
+ *access_technologies = (MMModemAccessTechnology)value;
+ *mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
return TRUE;
}
static void
parent_load_access_technologies_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
guint mask = 0;
GError *error = NULL;
if (!iface_modem_parent->load_access_technologies_finish (self, res, &act, &mask, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- access_tech_set_result (simple, act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
+ g_task_return_int (task, act);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
access_tech_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
const gchar *response, *p;
@@ -140,7 +126,7 @@ access_tech_ready (MMBaseModem *self,
iface_modem_parent->load_access_technologies (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_access_technologies_ready,
- simple);
+ task);
return;
}
@@ -150,17 +136,16 @@ access_tech_ready (MMBaseModem *self,
act = mm_string_to_access_tech (p + 1);
if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN)
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't parse access technologies result: '%s'",
response);
else
- access_tech_set_result (simple, act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
+ g_task_return_int (task, act);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -168,91 +153,117 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_access_technologies);
-
mm_base_modem_at_command (MM_BASE_MODEM (self),
"*CNTI=0",
3,
FALSE,
(GAsyncReadyCallback)access_tech_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
-/* Initializing the modem (during first enabling) */
+/* Loading supported charsets (Modem interface) */
-typedef struct {
- GSimpleAsyncResult *result;
- MMBroadbandModemNokia *self;
- guint retries;
-} EnablingModemInitContext;
+static MMModemCharset
+load_supported_charsets_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return MM_MODEM_CHARSET_UNKNOWN;
+
+ if (!mm_3gpp_parse_cscs_test_response (response, &charsets)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to parse the supported character sets response");
+ return MM_MODEM_CHARSET_UNKNOWN;
+ }
+
+ return charsets;
+}
static void
-enabling_modem_init_context_complete_and_free (EnablingModemInitContext *ctx)
+load_supported_charsets (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (EnablingModemInitContext, ctx);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CSCS=?",
+ 20,
+ TRUE,
+ callback,
+ user_data);
}
+/*****************************************************************************/
+/* Initializing the modem (during first enabling) */
+
+typedef struct {
+ guint retries;
+} EnablingModemInitContext;
+
static gboolean
enabling_modem_init_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void retry_atz (EnablingModemInitContext *ctx);
+static void retry_atz (GTask *task);
static void
atz_ready (MMBaseModem *self,
GAsyncResult *res,
- EnablingModemInitContext *ctx)
+ GTask *task)
{
+ EnablingModemInitContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
/* One retry less */
ctx->retries--;
if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
/* Consumed all retries... */
if (ctx->retries == 0) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_modem_init_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Retry... */
g_error_free (error);
- retry_atz (ctx);
+ retry_atz (task);
return;
}
/* Good! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_modem_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-retry_atz (EnablingModemInitContext *ctx)
+retry_atz (GTask *task)
{
- mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
+ MMBaseModem *self;
+
+ self = g_task_get_source_object (task);
+
+ mm_base_modem_at_command_full (self,
+ mm_base_modem_peek_port_primary (self),
"Z",
6,
FALSE,
FALSE,
NULL, /* cancellable */
(GAsyncReadyCallback)atz_ready,
- ctx);
+ task);
}
static void
@@ -261,20 +272,20 @@ enabling_modem_init (MMBroadbandModem *self,
gpointer user_data)
{
EnablingModemInitContext *ctx;
+ GTask *task;
- ctx = g_slice_new0 (EnablingModemInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enabling_modem_init);
- ctx->self = g_object_ref (self);
+ ctx = g_new (EnablingModemInitContext, 1);
/* Send the init command twice; some devices (Nokia N900) appear to take a
* few commands before responding correctly. Instead of penalizing them for
* being stupid the first time by failing to enable the device, just
* try again. */
ctx->retries = 2;
- retry_atz (ctx);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ retry_atz (task);
}
/*****************************************************************************/
@@ -327,6 +338,9 @@ mm_broadband_modem_nokia_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -367,6 +381,8 @@ iface_modem_init (MMIfaceModem *iface)
iface->modem_power_up_finish = NULL;
iface->modem_power_down = NULL;
iface->modem_power_down_finish = NULL;
+ iface->load_supported_charsets = load_supported_charsets;
+ iface->load_supported_charsets_finish = load_supported_charsets_finish;
iface->load_access_technologies = load_access_technologies;
iface->load_access_technologies_finish = load_access_technologies_finish;
diff --git a/plugins/nokia/mm-plugin-nokia-icera.c b/plugins/nokia/mm-plugin-nokia-icera.c
index 3ee1e473..f8cbc432 100644
--- a/plugins/nokia/mm-plugin-nokia-icera.c
+++ b/plugins/nokia/mm-plugin-nokia-icera.c
@@ -20,14 +20,13 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
#include "mm-plugin-nokia-icera.h"
#include "mm-broadband-modem-icera.h"
G_DEFINE_TYPE (MMPluginNokiaIcera, mm_plugin_nokia_icera, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom commands for AT probing */
@@ -43,55 +42,20 @@ static const MMPortProbeAtCommand custom_at_probe[] = {
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_icera_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_icera_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- GUdevDevice *port;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-
- port = mm_port_probe_peek_port (probe);
-
- /* Look for port type hints */
- if (mm_port_probe_is_at (probe)) {
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_NOKIA_PORT_TYPE_MODEM")) {
- mm_dbg ("Nokia: AT port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_NOKIA_PORT_TYPE_AUX")) {
- mm_dbg ("Nokia: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- }
- }
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- mm_port_probe_get_port_type (probe),
- pflags,
- error);
-}
-
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
@@ -102,7 +66,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_NOKIA_ICERA,
- MM_PLUGIN_NAME, "Nokia (Icera)",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe,
@@ -122,5 +86,4 @@ mm_plugin_nokia_icera_class_init (MMPluginNokiaIceraClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/nokia/mm-plugin-nokia.c b/plugins/nokia/mm-plugin-nokia.c
index da8187d9..29b8c8ff 100644
--- a/plugins/nokia/mm-plugin-nokia.c
+++ b/plugins/nokia/mm-plugin-nokia.c
@@ -21,14 +21,13 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
#include "mm-plugin-nokia.h"
#include "mm-broadband-modem-nokia.h"
G_DEFINE_TYPE (MMPluginNokia, mm_plugin_nokia, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom commands for AT probing */
@@ -44,14 +43,14 @@ static const MMPortProbeAtCommand custom_at_probe[] = {
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_nokia_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_nokia_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -69,7 +68,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_NOKIA,
- MM_PLUGIN_NAME, "Nokia",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
diff --git a/plugins/nokia/mm-sim-nokia.c b/plugins/nokia/mm-sim-nokia.c
index 043d6349..a0d7c81a 100644
--- a/plugins/nokia/mm-sim-nokia.c
+++ b/plugins/nokia/mm-sim-nokia.c
@@ -62,6 +62,7 @@ mm_sim_nokia_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
diff --git a/plugins/novatel/mm-broadband-bearer-novatel-lte.c b/plugins/novatel/mm-broadband-bearer-novatel-lte.c
index bd66ee1f..4419eb23 100644
--- a/plugins/novatel/mm-broadband-bearer-novatel-lte.c
+++ b/plugins/novatel/mm-broadband-bearer-novatel-lte.c
@@ -13,6 +13,7 @@
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -29,18 +30,14 @@
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-novatel-lte.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
-#define CONNECTION_CHECK_TIMEOUT_SEC 5
#define QMISTATUS_TAG "$NWQMISTATUS:"
-G_DEFINE_TYPE (MMBroadbandBearerNovatelLte, mm_broadband_bearer_novatel_lte, MM_TYPE_BROADBAND_BEARER);
+G_DEFINE_TYPE (MMBroadbandBearerNovatelLte, mm_broadband_bearer_novatel_lte, MM_TYPE_BROADBAND_BEARER)
-struct _MMBroadbandBearerNovatelLtePrivate {
- /* timeout id for checking whether we're still connected */
- guint connection_poller;
-};
+/*****************************************************************************/
static gchar *
normalize_qmistatus (const gchar *status)
@@ -58,46 +55,6 @@ normalize_qmistatus (const gchar *status)
return normalized_status;
}
-/*****************************************************************************/
-/* 3GPP Connection sequence */
-
-typedef struct {
- MMBroadbandBearerNovatelLte *self;
- MMBaseModem *modem;
- MMPortSerialAt *primary;
- MMPort *data;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- gint retries;
-} DetailedConnectContext;
-
-static void
-detailed_connect_context_complete_and_free (DetailedConnectContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->cancellable);
- if (ctx->data)
- g_object_unref (ctx->data);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
- g_slice_free (DetailedConnectContext, ctx);
-}
-
-static MMBearerConnectResult *
-connect_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
-}
-
-static gboolean connect_3gpp_qmistatus (DetailedConnectContext *ctx);
-
static gboolean
is_qmistatus_connected (const gchar *str)
{
@@ -122,121 +79,176 @@ is_qmistatus_call_failed (const gchar *str)
return (g_strrstr (str, "QMI_RESULT_FAILURE:QMI_ERR_CALL_FAILED") != NULL);
}
+/*****************************************************************************/
+/* Connection status monitoring */
+
+static MMBearerConnectionStatus
+load_connection_status_finish (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ }
+ return (MMBearerConnectionStatus)value;
+}
+
static void
-poll_connection_ready (MMBaseModem *modem,
+poll_connection_ready (MMBaseModem *modem,
GAsyncResult *res,
- MMBroadbandBearerNovatelLte *bearer)
+ GTask *task)
{
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_finish (modem, res, &error);
- if (!result) {
- mm_warn ("QMI connection status failed: %s", error->message);
- g_error_free (error);
- return;
- }
-
- if (is_qmistatus_disconnected (result)) {
- mm_base_bearer_report_connection_status (MM_BASE_BEARER (bearer), MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
- g_source_remove (bearer->priv->connection_poller);
- bearer->priv->connection_poller = 0;
- }
+ if (!result)
+ g_task_return_error (task, error);
+ else if (is_qmistatus_disconnected (result))
+ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ else
+ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED);
+ g_object_unref (task);
}
-static gboolean
-poll_connection (MMBroadbandBearerNovatelLte *bearer)
+static void
+load_connection_status (MMBaseBearer *bearer,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
MMBaseModem *modem = NULL;
+ task = g_task_new (bearer, NULL, callback, user_data);
+
g_object_get (MM_BASE_BEARER (bearer),
MM_BASE_BEARER_MODEM, &modem,
NULL);
+
mm_base_modem_at_command (
modem,
"$NWQMISTATUS",
3,
FALSE,
- (GAsyncReadyCallback)poll_connection_ready,
- bearer);
+ (GAsyncReadyCallback) poll_connection_ready,
+ task);
+
g_object_unref (modem);
+}
+
+/*****************************************************************************/
+/* 3GPP Connection sequence */
+
+typedef struct {
+ MMBaseModem *modem;
+ MMPortSerialAt *primary;
+ MMPort *data;
+ gint retries;
+} DetailedConnectContext;
+
+static void
+detailed_connect_context_free (DetailedConnectContext *ctx)
+{
+ if (ctx->data)
+ g_object_unref (ctx->data);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->modem);
+ g_slice_free (DetailedConnectContext, ctx);
+}
- return TRUE;
+static MMBearerConnectResult *
+connect_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
}
+static gboolean connect_3gpp_qmistatus (GTask *task);
+
static void
connect_3gpp_qmistatus_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
+ MMBroadbandBearerNovatelLte *self;
+ DetailedConnectContext *ctx;
const gchar *result;
gchar *normalized_result;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
- mm_warn ("QMI connection status failed: %s", error->message);
if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ mm_obj_dbg (self, "connection status failed: %s; will retry", error->message);
g_error_free (error);
- result = "Unknown error";
- } else if (is_qmistatus_connected (result)) {
+ goto retry;
+ }
+
+ if (is_qmistatus_connected (result)) {
MMBearerIpConfig *config;
- mm_dbg("Connected");
- ctx->self->priv->connection_poller = g_timeout_add_seconds (CONNECTION_CHECK_TIMEOUT_SEC,
- (GSourceFunc)poll_connection,
- ctx->self);
+ mm_obj_dbg (self, "connected");
config = mm_bearer_ip_config_new ();
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
+ g_task_return_pointer (
+ task,
mm_bearer_connect_result_new (ctx->data, config, NULL),
(GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
g_object_unref (config);
- detailed_connect_context_complete_and_free (ctx);
return;
- } else if (is_qmistatus_call_failed (result)) {
- /* Don't retry if the call failed */
- ctx->retries = 0;
}
- mm_dbg ("Error: '%s'", result);
-
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Connection setup operation has been cancelled");
- detailed_connect_context_complete_and_free (ctx);
- return;
+ /* Don't retry if the call failed */
+ if (is_qmistatus_call_failed (result)) {
+ mm_obj_dbg (self, "not retrying: call failed");
+ ctx->retries = 0;
}
+retry:
if (ctx->retries > 0) {
ctx->retries--;
- mm_dbg ("Retrying status check in a second. %d retries left.",
- ctx->retries);
- g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, ctx);
+ mm_obj_dbg (self, "retrying status check in a second: %d retries left", ctx->retries);
+ g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, task);
return;
}
/* Already exhausted all retries */
normalized_result = normalize_qmistatus (result);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "QMI connect failed: %s",
- normalized_result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "QMI connect failed: %s",
+ normalized_result);
+ g_object_unref (task);
g_free (normalized_result);
- detailed_connect_context_complete_and_free (ctx);
}
static gboolean
-connect_3gpp_qmistatus (DetailedConnectContext *ctx)
+connect_3gpp_qmistatus (GTask *task)
{
+ DetailedConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_full (
ctx->modem,
ctx->primary,
@@ -244,26 +256,25 @@ connect_3gpp_qmistatus (DetailedConnectContext *ctx)
3, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_3gpp_qmistatus_ready, /* callback */
- ctx); /* user_data */
+ task); /* user_data */
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
connect_3gpp_qmiconnect_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
const gchar *result;
GError *error = NULL;
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (!result) {
- mm_warn ("QMI connection failed: %s", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -273,16 +284,21 @@ connect_3gpp_qmiconnect_ready (MMBaseModem *modem,
* happened. Instead, we need to poll the modem to see if it's
* ready.
*/
- g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, ctx);
+ g_timeout_add_seconds (1, (GSourceFunc)connect_3gpp_qmistatus, task);
}
static void
-connect_3gpp_authenticate (DetailedConnectContext *ctx)
+connect_3gpp_authenticate (GTask *task)
{
+ MMBroadbandBearerNovatelLte *self;
+ DetailedConnectContext *ctx;
MMBearerProperties *config;
gchar *command, *apn, *user, *password;
- config = mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self));
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ config = mm_base_bearer_peek_config (MM_BASE_BEARER (self));
apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (config));
user = mm_port_serial_at_quote_string (mm_bearer_properties_get_user (config));
password = mm_port_serial_at_quote_string (mm_bearer_properties_get_password (config));
@@ -298,9 +314,9 @@ connect_3gpp_authenticate (DetailedConnectContext *ctx)
10, /* timeout */
FALSE, /* allow_cached */
FALSE, /* is_raw */
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)connect_3gpp_qmiconnect_ready,
- ctx); /* user_data */
+ task); /* user_data */
g_free (command);
}
@@ -314,77 +330,47 @@ connect_3gpp (MMBroadbandBearer *self,
gpointer user_data)
{
DetailedConnectContext *ctx;
+ GTask *task;
ctx = g_slice_new0 (DetailedConnectContext);
- ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- connect_3gpp);
- ctx->retries = 60;
+ ctx->retries = MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free);
/* Get a 'net' data port */
ctx->data = mm_base_modem_get_best_data_port (ctx->modem, MM_PORT_TYPE_NET);
if (!ctx->data) {
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_CONNECTED,
"Couldn't connect: no available net port available");
- detailed_connect_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
- connect_3gpp_authenticate (ctx);
+ connect_3gpp_authenticate (task);
}
/*****************************************************************************/
/* 3GPP Disonnection sequence */
typedef struct {
- MMBroadbandBearer *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPort *data;
- GSimpleAsyncResult *result;
gint retries;
} DetailedDisconnectContext;
-static DetailedDisconnectContext *
-detailed_disconnect_context_new (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
- MMPortSerialAt *primary,
- MMPort *data,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- DetailedDisconnectContext *ctx;
-
- ctx = g_new0 (DetailedDisconnectContext, 1);
- ctx->self = g_object_ref (self);
- ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
- ctx->primary = g_object_ref (primary);
- ctx->data = g_object_ref (data);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- detailed_disconnect_context_new);
- ctx->retries = 60;
- return ctx;
-}
-
static void
-detailed_disconnect_context_complete_and_free (DetailedDisconnectContext *ctx)
+detailed_disconnect_context_free (DetailedDisconnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -393,41 +379,46 @@ disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static gboolean disconnect_3gpp_qmistatus (DetailedDisconnectContext *ctx);
+static gboolean disconnect_3gpp_qmistatus (GTask *task);
static void
-disconnect_3gpp_status_ready (MMBaseModem *modem,
+disconnect_3gpp_status_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- const gchar *result;
- GError *error = NULL;
- gboolean is_connected = FALSE;
+ MMBroadbandBearerNovatelLte *self;
+ DetailedDisconnectContext *ctx;
+ const gchar *result;
+ GError *error = NULL;
+ gboolean is_connected = FALSE;
+
+ self = g_task_get_source_object (task);
result = mm_base_modem_at_command_full_finish (modem, res, &error);
if (result) {
- mm_dbg ("QMI connection status: %s", result);
+ mm_obj_dbg (self, "QMI connection status: %s", result);
if (is_qmistatus_disconnected (result)) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
- } else if (is_qmistatus_connected (result)) {
- is_connected = TRUE;
}
+ if (is_qmistatus_connected (result))
+ is_connected = TRUE;
} else {
- mm_dbg ("QMI connection status failed: %s", error->message);
+ mm_obj_dbg (self, "QMI connection status failed: %s", error->message);
g_error_free (error);
result = "Unknown error";
}
+ ctx = g_task_get_task_data (task);
+
if (ctx->retries > 0) {
ctx->retries--;
- mm_dbg ("Retrying status check in a second. %d retries left.",
- ctx->retries);
- g_timeout_add_seconds (1, (GSourceFunc)disconnect_3gpp_qmistatus, ctx);
+ mm_obj_dbg (self, "retrying status check in a second: %d retries left", ctx->retries);
+ g_timeout_add_seconds (1, (GSourceFunc)disconnect_3gpp_qmistatus, task);
return;
}
@@ -438,21 +429,25 @@ disconnect_3gpp_status_ready (MMBaseModem *modem,
gchar *normalized_result;
normalized_result = normalize_qmistatus (result);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "QMI disconnect failed: %s",
- normalized_result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "QMI disconnect failed: %s",
+ normalized_result);
g_free (normalized_result);
} else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static gboolean
-disconnect_3gpp_qmistatus (DetailedDisconnectContext *ctx)
+disconnect_3gpp_qmistatus (GTask *task)
{
+ DetailedDisconnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_full (
ctx->modem,
ctx->primary,
@@ -462,25 +457,28 @@ disconnect_3gpp_qmistatus (DetailedDisconnectContext *ctx)
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_3gpp_status_ready,
- ctx); /* user_data */
- return FALSE;
+ task); /* user_data */
+ return G_SOURCE_REMOVE;
}
static void
-disconnect_3gpp_check_status (MMBaseModem *modem,
+disconnect_3gpp_check_status (MMBaseModem *modem,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearerNovatelLte *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- mm_dbg("Disconnection error: %s", error->message);
+ mm_obj_dbg (self, "disconnection error: %s", error->message);
g_error_free (error);
}
- disconnect_3gpp_qmistatus (ctx);
+ disconnect_3gpp_qmistatus (task);
}
static void
@@ -494,14 +492,16 @@ disconnect_3gpp (MMBroadbandBearer *self,
gpointer user_data)
{
DetailedDisconnectContext *ctx;
- MMBroadbandBearerNovatelLte *bearer = MM_BROADBAND_BEARER_NOVATEL_LTE (self);
+ GTask *task;
- if (bearer->priv->connection_poller) {
- g_source_remove (bearer->priv->connection_poller);
- bearer->priv->connection_poller = 0;
- }
+ ctx = g_new0 (DetailedDisconnectContext, 1);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
+ ctx->primary = g_object_ref (primary);
+ ctx->data = g_object_ref (data);
+ ctx->retries = MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT;
- ctx = detailed_disconnect_context_new (self, modem, primary, data, callback, user_data);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free);
mm_base_modem_at_command_full (
ctx->modem,
@@ -512,7 +512,7 @@ disconnect_3gpp (MMBroadbandBearer *self,
FALSE, /* is_raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_3gpp_check_status,
- ctx); /* user_data */
+ task); /* user_data */
}
/*****************************************************************************/
@@ -558,33 +558,20 @@ mm_broadband_bearer_novatel_lte_new (MMBroadbandModemNovatelLte *modem,
static void
mm_broadband_bearer_novatel_lte_init (MMBroadbandBearerNovatelLte *self)
{
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_BROADBAND_BEARER_NOVATEL_LTE,
- MMBroadbandBearerNovatelLtePrivate);
-
- self->priv->connection_poller = 0;
-}
-
-static void
-finalize (GObject *object)
-{
- MMBroadbandBearerNovatelLte *self = MM_BROADBAND_BEARER_NOVATEL_LTE (object);
-
- if (self->priv->connection_poller)
- g_source_remove (self->priv->connection_poller);
-
- G_OBJECT_CLASS (mm_broadband_bearer_novatel_lte_parent_class)->finalize (object);
}
static void
mm_broadband_bearer_novatel_lte_class_init (MMBroadbandBearerNovatelLteClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
- g_type_class_add_private (object_class, sizeof (MMBroadbandBearerNovatelLtePrivate));
-
- object_class->finalize = finalize;
+ base_bearer_class->load_connection_status = load_connection_status;
+ base_bearer_class->load_connection_status_finish = load_connection_status_finish;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = load_connection_status;
+ base_bearer_class->reload_connection_status_finish = load_connection_status_finish;
+#endif
broadband_bearer_class->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;
diff --git a/plugins/novatel/mm-broadband-bearer-novatel-lte.h b/plugins/novatel/mm-broadband-bearer-novatel-lte.h
index d5799911..4f503865 100644
--- a/plugins/novatel/mm-broadband-bearer-novatel-lte.h
+++ b/plugins/novatel/mm-broadband-bearer-novatel-lte.h
@@ -36,11 +36,9 @@
typedef struct _MMBroadbandBearerNovatelLte MMBroadbandBearerNovatelLte;
typedef struct _MMBroadbandBearerNovatelLteClass MMBroadbandBearerNovatelLteClass;
-typedef struct _MMBroadbandBearerNovatelLtePrivate MMBroadbandBearerNovatelLtePrivate;
struct _MMBroadbandBearerNovatelLte {
MMBroadbandBearer parent;
- MMBroadbandBearerNovatelLtePrivate *priv;
};
struct _MMBroadbandBearerNovatelLteClass {
diff --git a/plugins/novatel/mm-broadband-modem-novatel-lte.c b/plugins/novatel/mm-broadband-modem-novatel-lte.c
index 499aafe7..19d1c594 100644
--- a/plugins/novatel/mm-broadband-modem-novatel-lte.c
+++ b/plugins/novatel/mm-broadband-modem-novatel-lte.c
@@ -31,13 +31,15 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-messaging.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-serial-parsers.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemNovatelLte, mm_broadband_modem_novatel_lte, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init));
@@ -74,33 +76,23 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_novatel_lte_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
static void
@@ -109,20 +101,12 @@ modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- /* Set a new ref to the bearer object as result */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
-
/* We just create a MMBroadbandBearer */
mm_broadband_bearer_novatel_lte_new (MM_BROADBAND_MODEM_NOVATEL_LTE (self),
properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -156,15 +140,15 @@ modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -172,16 +156,13 @@ modem_after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
+ task = g_task_new (self, NULL, callback, user_data);
/* A 3-second wait is necessary for SIM to become ready.
* Otherwise, a subsequent AT+CRSM command will likely fail. */
- g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, result);
+ g_timeout_add_seconds (3, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
@@ -203,65 +184,76 @@ load_own_numbers_finish (MMIfaceModem *self,
return own_numbers;
}
-static gboolean
-response_processor_cnum_ignore_at_errors (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+response_processor_cnum_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
GStrv own_numbers;
+ *result = NULL;
+ *result_error = NULL;
+
if (error) {
/* Ignore AT errors (ie, ERROR or CMx ERROR) */
- if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command)
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
*result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
- own_numbers = mm_3gpp_parse_cnum_exec_response (response, result_error);
+ own_numbers = mm_3gpp_parse_cnum_exec_response (response);
if (!own_numbers)
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
*result = g_variant_new_strv ((const gchar *const *) own_numbers, -1);
g_strfreev (own_numbers);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
-static gboolean
-response_processor_nwmdn_ignore_at_errors (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
-{
- GArray *array;
- GStrv own_numbers;
- gchar *mdn;
+static MMBaseModemAtResponseProcessorResult
+response_processor_nwmdn_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ g_auto(GStrv) own_numbers = NULL;
+ GPtrArray *array;
+ gchar *mdn;
+
+ *result = NULL;
+ *result_error = NULL;
if (error) {
/* Ignore AT errors (ie, ERROR or CMx ERROR) */
- if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command)
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
*result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
mdn = g_strdup (mm_strip_tag (response, "$NWMDN:"));
- array = g_array_new (TRUE, TRUE, sizeof (gchar *));
- g_array_append_val (array, mdn);
- own_numbers = (GStrv) g_array_free (array, FALSE);
+
+ array = g_ptr_array_new ();
+ g_ptr_array_add (array, mdn);
+ g_ptr_array_add (array, NULL);
+ own_numbers = (GStrv) g_ptr_array_free (array, FALSE);
*result = g_variant_new_strv ((const gchar *const *) own_numbers, -1);
- g_strfreev (own_numbers);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
static const MMBaseModemAtCommand own_numbers_commands[] = {
@@ -275,7 +267,6 @@ load_own_numbers (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading (Novatel LTE) own numbers...");
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
own_numbers_commands,
@@ -293,38 +284,38 @@ load_own_numbers (MMIfaceModem *self,
* The bit positions and band names on the right come from the response to $NWBAND=?
*/
static MMModemBand bandbits[] = {
- MM_MODEM_BAND_CDMA_BC0_CELLULAR_800, /* "00 CDMA2000 Band Class 0, A-System" */
- MM_MODEM_BAND_CDMA_BC0_CELLULAR_800, /* "01 CDMA2000 Band Class 0, B-System" */
- MM_MODEM_BAND_CDMA_BC1_PCS_1900, /* "02 CDMA2000 Band Class 1, all blocks" */
- MM_MODEM_BAND_CDMA_BC2_TACS, /* "03 CDMA2000 Band Class 2, place holder" */
- MM_MODEM_BAND_CDMA_BC3_JTACS, /* "04 CDMA2000 Band Class 3, A-System" */
- MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS, /* "05 CDMA2000 Band Class 4, all blocks" */
- MM_MODEM_BAND_CDMA_BC5_NMT450, /* "06 CDMA2000 Band Class 5, all blocks" */
- MM_MODEM_BAND_DCS, /* "07 GSM DCS band" */
- MM_MODEM_BAND_EGSM, /* "08 GSM Extended GSM (E-GSM) band" */
- MM_MODEM_BAND_UNKNOWN, /* "09 GSM Primary GSM (P-GSM) band" */
- MM_MODEM_BAND_CDMA_BC6_IMT2000, /* "10 CDMA2000 Band Class 6" */
- MM_MODEM_BAND_CDMA_BC7_CELLULAR_700, /* "11 CDMA2000 Band Class 7" */
- MM_MODEM_BAND_CDMA_BC8_1800, /* "12 CDMA2000 Band Class 8" */
- MM_MODEM_BAND_CDMA_BC9_900, /* "13 CDMA2000 Band Class 9" */
- MM_MODEM_BAND_CDMA_BC10_SECONDARY_800, /* "14 CDMA2000 Band Class 10 */
- MM_MODEM_BAND_CDMA_BC11_PAMR_400, /* "15 CDMA2000 Band Class 11 */
- MM_MODEM_BAND_UNKNOWN, /* "16 GSM 450 band" */
- MM_MODEM_BAND_UNKNOWN, /* "17 GSM 480 band" */
- MM_MODEM_BAND_UNKNOWN, /* "18 GSM 750 band" */
- MM_MODEM_BAND_G850, /* "19 GSM 850 band" */
- MM_MODEM_BAND_UNKNOWN, /* "20 GSM band" */
- MM_MODEM_BAND_PCS, /* "21 GSM PCS band" */
- MM_MODEM_BAND_U2100, /* "22 WCDMA I IMT 2000 band" */
- MM_MODEM_BAND_U1900, /* "23 WCDMA II PCS band" */
- MM_MODEM_BAND_U1800, /* "24 WCDMA III 1700 band" */
- MM_MODEM_BAND_U17IV, /* "25 WCDMA IV 1700 band" */
- MM_MODEM_BAND_U850, /* "26 WCDMA V US850 band" */
- MM_MODEM_BAND_U800, /* "27 WCDMA VI JAPAN 800 band" */
- MM_MODEM_BAND_UNKNOWN, /* "28 Reserved for BC12/BC14 */
- MM_MODEM_BAND_UNKNOWN, /* "29 Reserved for BC12/BC14 */
- MM_MODEM_BAND_UNKNOWN, /* "30 Reserved" */
- MM_MODEM_BAND_UNKNOWN, /* "31 Reserved" */
+ MM_MODEM_BAND_CDMA_BC0, /* "00 CDMA2000 Band Class 0, A-System" */
+ MM_MODEM_BAND_CDMA_BC0, /* "01 CDMA2000 Band Class 0, B-System" */
+ MM_MODEM_BAND_CDMA_BC1, /* "02 CDMA2000 Band Class 1, all blocks" */
+ MM_MODEM_BAND_CDMA_BC2, /* "03 CDMA2000 Band Class 2, place holder" */
+ MM_MODEM_BAND_CDMA_BC3, /* "04 CDMA2000 Band Class 3, A-System" */
+ MM_MODEM_BAND_CDMA_BC4, /* "05 CDMA2000 Band Class 4, all blocks" */
+ MM_MODEM_BAND_CDMA_BC5, /* "06 CDMA2000 Band Class 5, all blocks" */
+ MM_MODEM_BAND_DCS, /* "07 GSM DCS band" */
+ MM_MODEM_BAND_EGSM, /* "08 GSM Extended GSM (E-GSM) band" */
+ MM_MODEM_BAND_UNKNOWN, /* "09 GSM Primary GSM (P-GSM) band" */
+ MM_MODEM_BAND_CDMA_BC6, /* "10 CDMA2000 Band Class 6" */
+ MM_MODEM_BAND_CDMA_BC7, /* "11 CDMA2000 Band Class 7" */
+ MM_MODEM_BAND_CDMA_BC8, /* "12 CDMA2000 Band Class 8" */
+ MM_MODEM_BAND_CDMA_BC9, /* "13 CDMA2000 Band Class 9" */
+ MM_MODEM_BAND_CDMA_BC10, /* "14 CDMA2000 Band Class 10 */
+ MM_MODEM_BAND_CDMA_BC11, /* "15 CDMA2000 Band Class 11 */
+ MM_MODEM_BAND_G450, /* "16 GSM 450 band" */
+ MM_MODEM_BAND_G480, /* "17 GSM 480 band" */
+ MM_MODEM_BAND_G750, /* "18 GSM 750 band" */
+ MM_MODEM_BAND_G850, /* "19 GSM 850 band" */
+ MM_MODEM_BAND_UNKNOWN, /* "20 GSM 900 Railways band" */
+ MM_MODEM_BAND_PCS, /* "21 GSM PCS band" */
+ MM_MODEM_BAND_UTRAN_1, /* "22 WCDMA I IMT 2000 band" */
+ MM_MODEM_BAND_UTRAN_2, /* "23 WCDMA II PCS band" */
+ MM_MODEM_BAND_UTRAN_3, /* "24 WCDMA III 1700 band" */
+ MM_MODEM_BAND_UTRAN_4, /* "25 WCDMA IV 1700 band" */
+ MM_MODEM_BAND_UTRAN_5, /* "26 WCDMA V US850 band" */
+ MM_MODEM_BAND_UTRAN_6, /* "27 WCDMA VI JAPAN 800 band" */
+ MM_MODEM_BAND_UNKNOWN, /* "28 Reserved for BC12/BC14 */
+ MM_MODEM_BAND_UNKNOWN, /* "29 Reserved for BC12/BC14 */
+ MM_MODEM_BAND_UNKNOWN, /* "30 Reserved" */
+ MM_MODEM_BAND_UNKNOWN, /* "31 Reserved" */
};
static GArray *
@@ -332,9 +323,7 @@ load_supported_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- /* Never fails */
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -342,14 +331,11 @@ load_supported_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
GArray *bands;
guint i;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_bands);
+ task = g_task_new (self, NULL, callback, user_data);
/*
* The modem doesn't support telling us what bands are supported;
@@ -361,11 +347,8 @@ load_supported_bands (MMIfaceModem *self,
g_array_append_val(bands, bandbits[i]);
}
- g_simple_async_result_set_op_res_gpointer (result,
- bands,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -376,17 +359,13 @@ load_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_current_bands_done (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GArray *bands;
const gchar *response;
@@ -396,10 +375,8 @@ load_current_bands_done (MMIfaceModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query supported bands: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -415,11 +392,8 @@ load_current_bands_done (MMIfaceModem *self,
g_array_append_val(bands, bandbits[i]);
}
- g_simple_async_result_set_op_res_gpointer (operation_result,
- bands,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
}
static void
@@ -427,20 +401,75 @@ load_current_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_bands);
-
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"$NWBAND?",
3,
FALSE,
(GAsyncReadyCallback)load_current_bands_done,
- result);
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Load unlock retries (Modem interface) */
+
+static MMUnlockRetries *
+load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_unlock_retries_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ gint pin_num, pin_value;
+ int scan_count;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ response = mm_strip_tag (response, "$NWPINR:");
+
+ scan_count = sscanf (response, "PIN%d, %d", &pin_num, &pin_value);
+ if (scan_count != 2 || (pin_num != 1 && pin_num != 2)) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid unlock retries response: '%s'",
+ response);
+ } else {
+ MMUnlockRetries *retries;
+
+ retries = mm_unlock_retries_new ();
+ mm_unlock_retries_set (retries,
+ pin_num == 1 ? MM_MODEM_LOCK_SIM_PIN : MM_MODEM_LOCK_SIM_PIN2,
+ pin_value);
+ g_task_return_pointer (task, retries, g_object_unref);
+ }
+ g_object_unref (task);
+}
+
+static void
+load_unlock_retries (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$NWPINR?",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_unlock_retries_ready,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -453,12 +482,16 @@ load_access_technologies_finish (MMIfaceModem *self,
guint *mask,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
- *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ *access_technologies = (MMModemAccessTechnology) value;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
}
@@ -466,7 +499,7 @@ load_access_technologies_finish (MMIfaceModem *self,
static void
load_access_technologies_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
MMModemAccessTechnology act;
@@ -474,10 +507,8 @@ load_access_technologies_ready (MMIfaceModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query access technology: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -495,11 +526,8 @@ load_access_technologies_ready (MMIfaceModem *self,
if (strstr (response, "GSM"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
- g_simple_async_result_set_op_res_gpointer (operation_result,
- GUINT_TO_POINTER (act),
- NULL);
- g_simple_async_result_complete_in_idle (operation_result);
- g_object_unref (operation_result);
+ g_task_return_int (task, act);
+ g_object_unref (task);
}
static void
@@ -507,20 +535,13 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_access_technologies);
-
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"$NWSYSMODE",
3,
FALSE,
(GAsyncReadyCallback)load_access_technologies_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -555,42 +576,25 @@ scan_networks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-cops_query_ready (MMBroadbandModemNovatelLte *self,
- GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+parent_scan_networks_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
- const gchar *response;
GError *error = NULL;
- GList *scan_result;
-
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (error) {
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
- return;
- }
+ GList *scan_result;
- scan_result = mm_3gpp_parse_cops_test_response (response, &error);
- if (error) {
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
- return;
- }
-
- g_simple_async_result_set_op_res_gpointer (operation_result,
- scan_result,
- NULL);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ scan_result = iface_modem_3gpp_parent->scan_networks_finish (self, res, &error);
+ if (!scan_result)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task,
+ scan_result,
+ (GDestroyNotify)mm_3gpp_network_info_list_free);
+ g_object_unref (task);
}
static void
@@ -598,43 +602,36 @@ scan_networks (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
MMModemAccessTechnology access_tech;
- mm_dbg ("scanning for networks (Novatel LTE)...");
+ mm_obj_dbg (self, "scanning for networks (Novatel LTE)...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- scan_networks);
+ task = g_task_new (self, NULL, callback, user_data);
- access_tech = mm_iface_modem_get_access_technologies (MM_IFACE_MODEM (self));
/* The Novatel LTE modem does not properly support AT+COPS=? in LTE mode.
* Thus, do not try to scan networks when the current access technologies
* include LTE.
*/
+ access_tech = mm_iface_modem_get_access_technologies (MM_IFACE_MODEM (self));
if (access_tech & MM_MODEM_ACCESS_TECHNOLOGY_LTE) {
- gchar *access_tech_string;
+ g_autofree gchar *access_tech_string = NULL;
access_tech_string = mm_modem_access_technology_build_string_from_mask (access_tech);
- mm_warn ("Couldn't scan for networks with access technologies: %s", access_tech_string);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Couldn't scan for networks with access technologies: %s",
- access_tech_string);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- g_free (access_tech_string);
+ mm_obj_warn (self, "couldn't scan for networks with access technologies: %s", access_tech_string);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't scan for networks with access technologies: %s",
+ access_tech_string);
+ g_object_unref (task);
return;
}
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+COPS=?",
- 120,
- FALSE,
- (GAsyncReadyCallback)cops_query_ready,
- result);
+ /* Otherwise, just fallback to the generic scan method */
+ iface_modem_3gpp_parent->scan_networks (self,
+ (GAsyncReadyCallback)parent_scan_networks_ready,
+ task);
}
/*****************************************************************************/
@@ -652,6 +649,9 @@ mm_broadband_modem_novatel_lte_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Novatel LTE bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
NULL);
}
@@ -677,6 +677,8 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_supported_bands_finish = load_supported_bands_finish;
iface->load_current_bands = load_current_bands;
iface->load_current_bands_finish = load_current_bands_finish;
+ iface->load_unlock_retries = load_unlock_retries;
+ iface->load_unlock_retries_finish = load_unlock_retries_finish;
/* No support for setting bands, as it destabilizes the modem. */
iface->load_access_technologies = load_access_technologies;
iface->load_access_technologies_finish = load_access_technologies_finish;
@@ -687,6 +689,8 @@ iface_modem_init (MMIfaceModem *iface)
static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
+ iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+
iface->scan_networks = scan_networks;
iface->scan_networks_finish = scan_networks_finish;
}
diff --git a/plugins/novatel/mm-broadband-modem-novatel.c b/plugins/novatel/mm-broadband-modem-novatel.c
index 765595e3..4eba0e16 100644
--- a/plugins/novatel/mm-broadband-modem-novatel.c
+++ b/plugins/novatel/mm-broadband-modem-novatel.c
@@ -33,9 +33,10 @@
#include "mm-broadband-modem-novatel.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
+#include "mm-common-helpers.h"
#include "libqcdm/src/commands.h"
#include "libqcdm/src/result.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
@@ -58,16 +59,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -77,9 +75,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -108,13 +105,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -126,10 +122,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -149,22 +142,23 @@ load_current_modes_finish (MMIfaceModem *self,
{
LoadCurrentModesResult *result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- /* When a valid result is given, we never complete in idle */
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*allowed = result->allowed;
*preferred = result->preferred;
+ g_free (result);
+
return TRUE;
}
static void
nwrat_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- LoadCurrentModesResult result;
+ LoadCurrentModesResult *result;
GError *error = NULL;
const gchar *response;
GRegex *r;
@@ -174,9 +168,8 @@ nwrat_query_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -186,15 +179,14 @@ nwrat_query_ready (MMBaseModem *self,
if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &error)) {
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't match NWRAT reply: %s",
- response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match NWRAT reply: %s",
+ response);
+ g_object_unref (task);
g_match_info_free (match_info);
g_regex_unref (r);
return;
@@ -204,40 +196,41 @@ nwrat_query_ready (MMBaseModem *self,
!mm_get_int_from_match_info (match_info, 2, &b) ||
a < 0 || a > 2 ||
b < 1 || b > 2) {
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse mode/tech response '%s': invalid modes reported",
response);
+ g_object_unref (task);
g_match_info_free (match_info);
g_regex_unref (r);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
return;
}
+ result = g_new0 (LoadCurrentModesResult, 1);
+
switch (a) {
case 0:
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 1:
if (b == 1) {
- result.allowed = MM_MODEM_MODE_2G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_2G;
+ result->preferred = MM_MODEM_MODE_NONE;
} else /* b == 2 */ {
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_2G;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_2G;
}
break;
case 2:
if (b == 1) {
- result.allowed = MM_MODEM_MODE_3G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_3G;
+ result->preferred = MM_MODEM_MODE_NONE;
} else /* b == 2 */ {
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_3G;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_3G;
}
break;
default:
@@ -249,10 +242,8 @@ nwrat_query_ready (MMBaseModem *self,
g_match_info_free (match_info);
g_regex_unref (r);
- /* When a valid result is given, we never complete in idle */
- g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, result, g_free);
+ g_object_unref (task);
}
static void
@@ -260,22 +251,18 @@ load_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
/* Load allowed modes only in 3GPP modems */
if (!mm_iface_modem_is_3gpp (self)) {
- g_simple_async_result_set_error (
- result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Loading allowed modes not supported in CDMA-only modems");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
return;
}
@@ -284,7 +271,7 @@ load_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)nwrat_query_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -295,24 +282,23 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBroadbandModemNovatel *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -322,25 +308,21 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint a = -1;
gint b = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
/* Setting allowed modes only in 3GPP modems */
if (!mm_iface_modem_is_3gpp (self)) {
- g_simple_async_result_set_error (
- result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Setting allowed modes not supported in CDMA-only modems");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
return;
}
@@ -370,18 +352,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -392,112 +372,91 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
/*****************************************************************************/
-/* Load access technologies (Modem interface) */
-
-typedef struct {
- guint hdr_revision; /* QCDM_HDR_REV_x */
- MMModemAccessTechnology generic_act;
- guint mask;
-} SnapshotResult;
-
-typedef struct {
- MMBaseModem *self;
- MMPortSerialQcdm *port;
- GSimpleAsyncResult *simple;
- MMModemAccessTechnology generic_act;
- guint mask;
-} SnapshotContext;
static void
-snapshot_result_complete (GSimpleAsyncResult *simple,
- guint hdr_revision,
- MMModemAccessTechnology generic_act,
- guint mask)
+close_and_unref_port (MMPortSerialQcdm *port)
{
- SnapshotResult *r;
-
- r = g_new0 (SnapshotResult, 1);
- r->hdr_revision = hdr_revision;
- r->generic_act = generic_act;
- r->mask = mask;
-
- g_simple_async_result_set_op_res_gpointer (simple, r, g_free);
- g_simple_async_result_complete (simple);
+ mm_port_serial_close (MM_PORT_SERIAL (port));
+ g_object_unref (port);
}
-static void
-snapshot_result_complete_simple (GSimpleAsyncResult *simple,
- MMModemAccessTechnology generic_act,
- guint mask)
+static gboolean
+get_evdo_version_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ guint *hdr_revision, /* QCDM_HDR_REV_* */
+ GError **error)
{
- snapshot_result_complete (simple, QCDM_HDR_REV_UNKNOWN, generic_act, mask);
-}
+ gssize result;
-static void
-snapshot_context_complete_and_free (SnapshotContext *ctx, guint hdr_revision)
-{
- snapshot_result_complete (ctx->simple,
- hdr_revision,
- ctx->generic_act,
- ctx->mask);
- g_object_unref (ctx->simple);
- g_object_unref (ctx->self);
- g_object_unref (ctx->port);
- g_free (ctx);
+ result = g_task_propagate_int (G_TASK (res), error);
+ if (result < 0)
+ return FALSE;
+
+ *hdr_revision = (guint8) result;
+ return TRUE;
}
static void
-nw_snapshot_old_cb (MMPortSerialQcdm *port,
- GAsyncResult *res,
- SnapshotContext *ctx)
+nw_snapshot_old_ready (MMPortSerialQcdm *port,
+ GAsyncResult *res,
+ GTask *task)
{
QcdmResult *result;
- guint8 hdr_revision = QCDM_HDR_REV_UNKNOWN;
GError *error = NULL;
GByteArray *response;
+ guint8 hdr_revision = QCDM_HDR_REV_UNKNOWN;
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
/* Just ignore the error and complete with the input info */
- mm_dbg ("Couldn't run QCDM Novatel Modem MSM6500 snapshot: '%s'", error->message);
- g_error_free (error);
- snapshot_context_complete_and_free (ctx, QCDM_HDR_REV_UNKNOWN);
+ g_prefix_error (&error, "Couldn't run QCDM Novatel Modem MSM6500 snapshot: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Parse the response */
result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result ((const gchar *) response->data, response->len, NULL);
g_byte_array_unref (response);
- if (result) {
- qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &hdr_revision);
- qcdm_result_unref (result);
- } else
- mm_dbg ("Failed to get QCDM Novatel Modem MSM6500 snapshot.");
+ if (!result) {
+ g_prefix_error (&error, "Failed to get QCDM Novatel Modem MSM6500 snapshot: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- snapshot_context_complete_and_free (ctx, hdr_revision);
+ /* Success */
+ qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &hdr_revision);
+ qcdm_result_unref (result);
+
+ g_task_return_int (task, (gint) hdr_revision);
+ g_object_unref (task);
}
static void
-nw_snapshot_new_cb (MMPortSerialQcdm *port,
- GAsyncResult *res,
- SnapshotContext *ctx)
+nw_snapshot_new_ready (MMPortSerialQcdm *port,
+ GAsyncResult *res,
+ GTask *task)
{
- QcdmResult *result;
- GByteArray *nwsnap;
- guint8 hdr_revision = QCDM_HDR_REV_UNKNOWN;
- GError *error = NULL;
- GByteArray *response;
+ MMBroadbandModemNovatel *self;
+ QcdmResult *result;
+ GByteArray *nwsnap;
+ GError *error = NULL;
+ GByteArray *response;
+ guint8 hdr_revision = QCDM_HDR_REV_UNKNOWN;
+
+ self = g_task_get_source_object (task);
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- mm_dbg ("Couldn't run QCDM Novatel Modem MSM6800 snapshot: '%s'", error->message);
- g_error_free (error);
- snapshot_context_complete_and_free (ctx, QCDM_HDR_REV_UNKNOWN);
+ g_prefix_error (&error, "couldn't run QCDM Novatel Modem MSM6800 snapshot: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -505,13 +464,16 @@ nw_snapshot_new_cb (MMPortSerialQcdm *port,
result = qcdm_cmd_nw_subsys_modem_snapshot_cdma_result ((const gchar *) response->data, response->len, NULL);
g_byte_array_unref (response);
if (result) {
+ /* Success */
qcdm_result_get_u8 (result, QCDM_CMD_NW_SUBSYS_MODEM_SNAPSHOT_CDMA_ITEM_HDR_REV, &hdr_revision);
qcdm_result_unref (result);
- snapshot_context_complete_and_free (ctx, hdr_revision);
+
+ g_task_return_int (task, (gint) hdr_revision);
+ g_object_unref (task);
return;
}
- mm_dbg ("Failed to get QCDM Novatel Modem MSM6800 snapshot.");
+ mm_obj_dbg (self, "failed to get QCDM Novatel Modem MSM6800 snapshot");
/* Try for MSM6500 */
nwsnap = g_byte_array_sized_new (25);
@@ -521,32 +483,38 @@ nw_snapshot_new_cb (MMPortSerialQcdm *port,
nwsnap,
3,
NULL,
- (GAsyncReadyCallback)nw_snapshot_old_cb,
- ctx);
+ (GAsyncReadyCallback)nw_snapshot_old_ready,
+ task);
g_byte_array_unref (nwsnap);
}
-static gboolean
-get_nw_modem_snapshot (MMBaseModem *self,
- GSimpleAsyncResult *simple,
- MMModemAccessTechnology generic_act,
- guint mask)
+static void
+get_evdo_version (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- SnapshotContext *ctx;
+ GError *error = NULL;
GByteArray *nwsnap;
+ GTask *task;
MMPortSerialQcdm *port;
- port = mm_base_modem_peek_port_qcdm (self);
- if (!port)
- return FALSE;
+ task = g_task_new (self, NULL, callback, user_data);
- /* Setup context */
- ctx = g_new0 (SnapshotContext, 1);
- ctx->self = g_object_ref (self);
- ctx->port = g_object_ref (port);
- ctx->simple = simple;
- ctx->generic_act = generic_act;
- ctx->mask = mask;
+ port = mm_base_modem_get_port_qcdm (self);
+ if (!port) {
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No available QCDM port");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ g_task_set_task_data (task, port, (GDestroyNotify) close_and_unref_port);
+
+ if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) {
+ g_prefix_error (&error, "couldn't open QCDM port: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
/* Try MSM6800 first since newer cards use that */
nwsnap = g_byte_array_sized_new (25);
@@ -556,12 +524,20 @@ get_nw_modem_snapshot (MMBaseModem *self,
nwsnap,
3,
NULL,
- (GAsyncReadyCallback)nw_snapshot_new_cb,
- ctx);
+ (GAsyncReadyCallback)nw_snapshot_new_ready,
+ task);
g_byte_array_unref (nwsnap);
- return TRUE;
}
+/*****************************************************************************/
+/* Load access technologies (Modem interface) */
+
+typedef struct {
+ MMModemAccessTechnology act;
+ guint mask;
+ guint hdr_revision; /* QCDM_HDR_REV_* */
+} AccessTechContext;
+
static gboolean
modem_load_access_technologies_finish (MMIfaceModem *self,
GAsyncResult *res,
@@ -569,48 +545,45 @@ modem_load_access_technologies_finish (MMIfaceModem *self,
guint *mask,
GError **error)
{
- SnapshotResult *r;
- MMModemAccessTechnology act;
+ AccessTechContext *ctx;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ if (!g_task_propagate_boolean (G_TASK (res), error))
return FALSE;
- r = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
- act = r->generic_act;
- if (act & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK) {
- /* Update access technology with specific EVDO revision from QCDM */
- if (r->hdr_revision == QCDM_HDR_REV_0) {
- mm_dbg ("Novatel Modem Snapshot EVDO revision: 0");
- act &= ~MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK;
- act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
- } else if (r->hdr_revision == QCDM_HDR_REV_A) {
- mm_dbg ("Novatel Modem Snapshot EVDO revision: A");
- act &= ~MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK;
- act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA;
+ /* Update access technology with specific EVDO revision from QCDM if we have them */
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (ctx->act & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK) {
+ if (ctx->hdr_revision == QCDM_HDR_REV_0) {
+ mm_obj_dbg (self, "modem snapshot EVDO revision: 0");
+ ctx->act &= ~MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK;
+ ctx->act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
+ } else if (ctx->hdr_revision == QCDM_HDR_REV_A) {
+ mm_obj_dbg (self, "modem snapshot EVDO revision: A");
+ ctx->act &= ~MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK;
+ ctx->act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDOA;
} else
- mm_dbg ("Novatel Modem Snapshot EVDO revision: %d (unknown)", r->hdr_revision);
+ mm_obj_dbg (self, "modem snapshot EVDO revision: %d (unknown)", ctx->hdr_revision);
}
- *access_technologies = act;
- *mask = r->mask;
+ *access_technologies = ctx->act;
+ *mask = ctx->mask;
return TRUE;
}
static void
cnti_set_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
+ AccessTechContext *ctx = g_task_get_task_data (task);
GError *error = NULL;
const gchar *response;
const gchar *p;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -621,55 +594,64 @@ cnti_set_ready (MMBaseModem *self,
MM_CORE_ERROR_FAILED,
"Couldn't parse $CNTI result '%s'",
response);
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->act = mm_string_to_access_tech (p);
+ ctx->mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+evdo_version_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ AccessTechContext *ctx = g_task_get_task_data (task);
+
+ if (!get_evdo_version_finish (self, res, &ctx->hdr_revision, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- snapshot_result_complete_simple (simple,
- mm_string_to_access_tech (p),
- MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
parent_load_access_technologies_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- guint mask = 0;
GError *error = NULL;
+ AccessTechContext *ctx = g_task_get_task_data (task);
if (!iface_modem_parent->load_access_technologies_finish (self,
res,
- &act,
- &mask,
+ &ctx->act,
+ &ctx->mask,
&error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* No point in checking EVDO revision if EVDO isn't being used or if for
- * some reason we don't have a QCDM port.
- */
- if (!(act & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK)) {
- snapshot_result_complete (simple, QCDM_HDR_REV_UNKNOWN, act, mask);
- g_object_unref (simple);
+ /* No point in checking EVDO revision if EVDO isn't being used */
+ if (!(ctx->act & MM_IFACE_MODEM_CDMA_ALL_EVDO_ACCESS_TECHNOLOGIES_MASK)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- /* Pass along the access tech & mask that the parent determined so we
- * can specialize it based on the EVDO revision from QCDM.
- */
- if (!get_nw_modem_snapshot (MM_BASE_MODEM (self), simple, act, mask)) {
- /* If there's any error, use the access tech that the parent interface determined */
- snapshot_result_complete (simple, QCDM_HDR_REV_UNKNOWN, act, mask);
- g_object_unref (simple);
- }
+ /* Get the EVDO revision from QCDM */
+ get_evdo_version (MM_BASE_MODEM (self),
+ (GAsyncReadyCallback) evdo_version_ready,
+ task);
}
static void
@@ -677,12 +659,14 @@ modem_load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ AccessTechContext *ctx;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_access_technologies);
+ /* Setup context */
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_new0 (AccessTechContext, 1);
+ g_task_set_task_data (task, ctx, g_free);
/* CDMA-only modems defer to parent for generic access technology
* checking, but can determine EVDOr0 vs. EVDOrA through proprietary
@@ -692,7 +676,7 @@ modem_load_access_technologies (MMIfaceModem *self,
iface_modem_parent->load_access_technologies (
self,
(GAsyncReadyCallback)parent_load_access_technologies_ready,
- result);
+ task);
return;
}
@@ -702,7 +686,7 @@ modem_load_access_technologies (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)cnti_set_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -713,30 +697,31 @@ modem_load_signal_quality_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return 0;
+ GError *inner_error = NULL;
+ gssize value;
- return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return 0;
+ }
+ return (guint)value;
}
static void
parent_load_signal_quality_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
guint signal_quality;
signal_quality = iface_modem_parent->load_signal_quality_finish (self, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (signal_quality),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, signal_quality);
+ g_object_unref (task);
}
static gint
@@ -776,17 +761,15 @@ get_one_quality (const gchar *reply,
/* Some cards appear to use RX0/RX1 and output RSSI in negative dBm */
if (dbm < 0)
success = TRUE;
- } else if (isdigit (*temp) && (dbm > 0) && (dbm < 115)) {
+ } else if (isdigit (*temp) && (dbm > 0) && (dbm <= 125)) {
/* S720 appears to use "1x RSSI" and print RSSI in dBm without '-' */
dbm *= -1;
success = TRUE;
}
}
- if (success) {
- dbm = CLAMP (dbm, -113, -51);
- quality = 100 - ((dbm + 51) * 100 / (-113 + 51));
- }
+ if (success)
+ quality = MM_RSSI_TO_QUALITY (dbm);
g_free (temp);
return quality;
@@ -795,7 +778,7 @@ get_one_quality (const gchar *reply,
static void
nwrssi_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
gint quality;
@@ -806,7 +789,7 @@ nwrssi_ready (MMBaseModem *self,
iface_modem_parent->load_signal_quality (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_signal_quality_ready,
- simple);
+ task);
return;
}
@@ -820,17 +803,14 @@ nwrssi_ready (MMBaseModem *self,
quality = get_one_quality (response, "HDR RSSI=");
if (quality >= 0)
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER ((guint)quality),
- NULL);
+ g_task_return_int (task, quality);
else
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse $NWRSSI response: '%s'",
- response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse $NWRSSI response: '%s'",
+ response);
+ g_object_unref (task);
}
static void
@@ -838,20 +818,16 @@ modem_load_signal_quality (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- mm_dbg ("loading signal quality...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_signal_quality);
+ task = g_task_new (self, NULL, callback, user_data);
/* 3GPP modems can just run parent's signal quality loading */
if (mm_iface_modem_is_3gpp (self)) {
iface_modem_parent->load_signal_quality (
self,
(GAsyncReadyCallback)parent_load_signal_quality_ready,
- result);
+ task);
return;
}
@@ -862,7 +838,293 @@ modem_load_signal_quality (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)nwrssi_ready,
- result);
+ task);
+}
+
+/*****************************************************************************/
+/* Automatic activation (CDMA interface) */
+
+static gboolean
+modem_cdma_activate_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+qcmipgetp_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else {
+ mm_obj_dbg (self, "current profile information retrieved: %s", response);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+activate_cdv_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Let's query the MIP profile */
+ mm_base_modem_at_command (self,
+ "$QCMIPGETP",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)qcmipgetp_ready,
+ task);
+}
+
+static void
+modem_cdma_activate (MMIfaceModemCdma *self,
+ const gchar *carrier_code,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *cmd;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ cmd = g_strdup_printf ("+CDV=%s", carrier_code);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)activate_cdv_ready,
+ task);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+/* Manual activation (CDMA interface) */
+
+/* Wait up to 2 minutes */
+#define MAX_IOTA_QUERY_RETRIES 24
+#define MAX_IOTA_QUERY_RETRY_TIME 5
+
+typedef enum {
+ CDMA_ACTIVATION_STEP_FIRST,
+ CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION,
+ CDMA_ACTIVATION_STEP_OTA_UPDATE,
+ CDMA_ACTIVATION_STEP_PRL_UPDATE,
+ CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED,
+ CDMA_ACTIVATION_STEP_LAST
+} CdmaActivationStep;
+
+typedef struct {
+ CdmaActivationStep step;
+ MMCdmaManualActivationProperties *properties;
+ guint wait_timeout_id;
+ guint wait_retries;
+} CdmaActivationContext;
+
+static void
+cdma_activation_context_free (CdmaActivationContext *ctx)
+{
+ g_assert (ctx->wait_timeout_id == 0);
+ g_object_unref (ctx->properties);
+ g_slice_free (CdmaActivationContext, ctx);
+}
+
+static gboolean
+modem_cdma_activate_manual_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void cdma_activation_step (GTask *task);
+
+static gboolean
+cdma_activation_step_retry (GTask *task)
+{
+ CdmaActivationContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->wait_timeout_id = 0;
+ cdma_activation_step (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+iota_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ CdmaActivationContext *ctx;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ /* Finished? */
+ if (strstr (response, "IOTA Enabled")) {
+ ctx->step++;
+ cdma_activation_step (task);
+ return;
+ }
+
+ /* Too many retries? */
+ if (ctx->wait_retries == MAX_IOTA_QUERY_RETRIES) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Too much time waiting to finish the IOTA activation");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Otherwise, schedule retry in some secs */
+ g_assert (ctx->wait_timeout_id == 0);
+ ctx->wait_retries++;
+ ctx->wait_timeout_id = g_timeout_add_seconds (MAX_IOTA_QUERY_RETRY_TIME,
+ (GSourceFunc)cdma_activation_step_retry,
+ task);
+}
+
+static void
+activation_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ CdmaActivationContext *ctx;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ cdma_activation_step (task);
+}
+
+static void
+cdma_activation_step (GTask *task)
+{
+ MMBroadbandModemNovatel *self;
+ CdmaActivationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case CDMA_ACTIVATION_STEP_FIRST:
+ mm_obj_dbg (self, "launching manual activation...");
+ ctx->step++;
+ /* fall-through */
+
+ case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION: {
+ gchar *command;
+
+ mm_obj_info (self, "activation step [1/5]: setting up activation details");
+ command = g_strdup_printf ("$NWACTIVATION=%s,%s,%s",
+ mm_cdma_manual_activation_properties_get_spc (ctx->properties),
+ mm_cdma_manual_activation_properties_get_mdn (ctx->properties),
+ mm_cdma_manual_activation_properties_get_min (ctx->properties));
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)activation_command_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ case CDMA_ACTIVATION_STEP_OTA_UPDATE:
+ mm_obj_info (self, "activation step [2/5]: starting OTA activation");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+IOTA=1",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)activation_command_ready,
+ task);
+ return;
+
+ case CDMA_ACTIVATION_STEP_PRL_UPDATE:
+ mm_obj_info (self, "activation step [3/5]: starting PRL update");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+IOTA=2",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)activation_command_ready,
+ task);
+ return;
+
+ case CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED:
+ mm_obj_info (self, "activation step [4/5]: checking activation process status");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+IOTA?",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)iota_query_ready,
+ task);
+ return;
+
+ case CDMA_ACTIVATION_STEP_LAST:
+ mm_obj_info (self, "activation step [5/5]: activation process finished");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_cdma_activate_manual (MMIfaceModemCdma *self,
+ MMCdmaManualActivationProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CdmaActivationContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Setup context */
+ ctx = g_slice_new0 (CdmaActivationContext);
+ ctx->properties = g_object_ref (properties);
+ ctx->step = CDMA_ACTIVATION_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) cdma_activation_context_free);
+
+ /* And start it */
+ cdma_activation_step (task);
}
/*****************************************************************************/
@@ -894,23 +1156,25 @@ messaging_enable_unsolicited_events (MMIfaceModemMessaging *self,
/* Detailed registration state (CDMA interface) */
typedef struct {
- MMModemCdmaRegistrationState detailed_cdma1x_state;
- MMModemCdmaRegistrationState detailed_evdo_state;
-} DetailedRegistrationStateResults;
+ MMModemCdmaRegistrationState cdma1x_state;
+ MMModemCdmaRegistrationState evdo_state;
+} DetailedRegistrationStateResult;
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- DetailedRegistrationStateResults state;
+ MMPortSerialQcdm *port;
+ gboolean close_port;
+ MMModemCdmaRegistrationState cdma1x_state;
+ MMModemCdmaRegistrationState evdo_state;
} DetailedRegistrationStateContext;
static void
-detailed_registration_state_context_complete_and_free (DetailedRegistrationStateContext *ctx)
+detailed_registration_state_context_free (DetailedRegistrationStateContext *ctx)
{
- /* Always not in idle! we're passing a struct in stack as result */
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
+ if (ctx->port) {
+ if (ctx->close_port)
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
+ g_object_unref (ctx->port);
+ }
g_free (ctx);
}
@@ -921,14 +1185,14 @@ modem_cdma_get_detailed_registration_state_finish (MMIfaceModemCdma *self,
MMModemCdmaRegistrationState *detailed_evdo_state,
GError **error)
{
- DetailedRegistrationStateResults *results;
+ GTask *task = G_TASK (res);
+ DetailedRegistrationStateContext *ctx = g_task_get_task_data (task);;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ if (!g_task_propagate_boolean (task, error))
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *detailed_cdma1x_state = results->detailed_cdma1x_state;
- *detailed_evdo_state = results->detailed_evdo_state;
+ *detailed_cdma1x_state = ctx->cdma1x_state;
+ *detailed_evdo_state = ctx->evdo_state;
return TRUE;
}
@@ -980,84 +1244,94 @@ parse_modem_eri (DetailedRegistrationStateContext *ctx, QcdmResult *result)
else
new_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
- if (ctx->state.detailed_cdma1x_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
- ctx->state.detailed_cdma1x_state = new_state;
- if (ctx->state.detailed_evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
- ctx->state.detailed_evdo_state = new_state;
+ if (ctx->cdma1x_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
+ ctx->cdma1x_state = new_state;
+ if (ctx->evdo_state != MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
+ ctx->evdo_state = new_state;
}
static void
reg_eri_6500_cb (MMPortSerialQcdm *port,
- GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
- GByteArray *response;
+ MMBroadbandModemNovatel *self;
+ DetailedRegistrationStateContext *ctx;
+ GError *error = NULL;
+ GByteArray *response;
+ QcdmResult *result;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
/* Just ignore the error and complete with the input info */
- mm_dbg ("Couldn't run QCDM MSM6500 ERI: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't run QCDM MSM6500 ERI: %s", error->message);
g_error_free (error);
- } else {
- QcdmResult *result;
+ goto done;
+ }
- result = qcdm_cmd_nw_subsys_eri_result ((const gchar *) response->data, response->len, NULL);
- g_byte_array_unref (response);
- if (result) {
- parse_modem_eri (ctx, result);
- qcdm_result_unref (result);
- }
+ result = qcdm_cmd_nw_subsys_eri_result ((const gchar *) response->data, response->len, NULL);
+ g_byte_array_unref (response);
+ if (result) {
+ parse_modem_eri (ctx, result);
+ qcdm_result_unref (result);
}
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
+done:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
reg_eri_6800_cb (MMPortSerialQcdm *port,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- GByteArray *response;
+ MMBroadbandModemNovatel *self;
+ DetailedRegistrationStateContext *ctx;
+ GError *error = NULL;
+ GByteArray *response;
+ GByteArray *nweri;
+ QcdmResult *result;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
/* Just ignore the error and complete with the input info */
- mm_dbg ("Couldn't run QCDM MSM6800 ERI: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't run QCDM MSM6800 ERI: %s", error->message);
g_error_free (error);
- } else {
- QcdmResult *result;
-
- /* Parse the response */
- result = qcdm_cmd_nw_subsys_eri_result ((const gchar *) response->data, response->len, NULL);
- g_byte_array_unref (response);
- if (result) {
- parse_modem_eri (ctx, result);
- qcdm_result_unref (result);
- } else {
- GByteArray *nweri;
-
- /* Try for MSM6500 */
- nweri = g_byte_array_sized_new (25);
- nweri->len = qcdm_cmd_nw_subsys_eri_new ((char *) nweri->data, 25, QCDM_NW_CHIPSET_6500);
- g_assert (nweri->len);
- mm_port_serial_qcdm_command (port,
- nweri,
- 3,
- NULL,
- (GAsyncReadyCallback)reg_eri_6500_cb,
- ctx);
- g_byte_array_unref (nweri);
- return;
- }
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
}
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
+ /* Parse the response */
+ result = qcdm_cmd_nw_subsys_eri_result ((const gchar *) response->data, response->len, NULL);
+ g_byte_array_unref (response);
+ if (result) {
+ /* Success */
+ parse_modem_eri (ctx, result);
+ qcdm_result_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Try for MSM6500 */
+ nweri = g_byte_array_sized_new (25);
+ nweri->len = qcdm_cmd_nw_subsys_eri_new ((char *) nweri->data, 25, QCDM_NW_CHIPSET_6500);
+ g_assert (nweri->len);
+ mm_port_serial_qcdm_command (port,
+ nweri,
+ 3,
+ NULL,
+ (GAsyncReadyCallback)reg_eri_6500_cb,
+ task);
+ g_byte_array_unref (nweri);
}
static void
@@ -1068,31 +1342,48 @@ modem_cdma_get_detailed_registration_state (MMIfaceModemCdma *self,
gpointer user_data)
{
DetailedRegistrationStateContext *ctx;
+ GTask *task;
GByteArray *nweri;
- MMPortSerialQcdm *port;
+ GError *error = NULL;
/* Setup context */
+ task = g_task_new (self, NULL, callback, user_data);
ctx = g_new0 (DetailedRegistrationStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_get_detailed_registration_state);
- ctx->state.detailed_cdma1x_state = cdma1x_state;
- ctx->state.detailed_evdo_state = evdo_state;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) detailed_registration_state_context_free);
- port = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
+ ctx->cdma1x_state = cdma1x_state;
+ ctx->evdo_state = evdo_state;
+
+ ctx->port = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
+ if (!ctx->port) {
+ /* Ignore errors and use non-detailed registration state */
+ mm_obj_dbg (self, "no available QCDM port");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) {
+ /* Ignore errors and use non-detailed registration state */
+ mm_obj_dbg (self, "couldn't open QCDM port: %s", error->message);
+ g_error_free (error);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->close_port = TRUE;
/* Try MSM6800 first since newer cards use that */
nweri = g_byte_array_sized_new (25);
nweri->len = qcdm_cmd_nw_subsys_eri_new ((char *) nweri->data, 25, QCDM_NW_CHIPSET_6800);
g_assert (nweri->len);
- mm_port_serial_qcdm_command (port,
+ mm_port_serial_qcdm_command (ctx->port,
nweri,
3,
NULL,
(GAsyncReadyCallback)reg_eri_6800_cb,
- ctx);
+ task);
g_byte_array_unref (nweri);
}
@@ -1160,8 +1451,7 @@ parse_nwltime_reply (const char *response,
else
g_free (result);
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return success;
}
@@ -1204,7 +1494,7 @@ modem_time_load_network_timezone_finish (MMIfaceModemTime *self,
const gchar *response;
MMNetworkTimezone *tz = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (response)
parse_nwltime_reply (response, NULL, &tz, error);
return tz;
@@ -1231,7 +1521,7 @@ modem_time_check_support_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
}
static void
@@ -1263,6 +1553,9 @@ mm_broadband_modem_novatel_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -1300,6 +1593,10 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
{
iface->get_detailed_registration_state = modem_cdma_get_detailed_registration_state;
iface->get_detailed_registration_state_finish = modem_cdma_get_detailed_registration_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
diff --git a/plugins/novatel/mm-common-novatel.c b/plugins/novatel/mm-common-novatel.c
new file mode 100644
index 00000000..b6b0e272
--- /dev/null
+++ b/plugins/novatel/mm-common-novatel.c
@@ -0,0 +1,145 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-common-novatel.h"
+#include "mm-log-object.h"
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortSerialAt *port;
+ guint nwdmat_retries;
+ guint wait_time;
+} CustomInitContext;
+
+static void
+custom_init_context_free (CustomInitContext *ctx)
+{
+ g_object_unref (ctx->port);
+ g_slice_free (CustomInitContext, ctx);
+}
+
+gboolean
+mm_common_novatel_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void custom_init_step (GTask *task);
+
+static void
+nwdmat_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ MMPortProbe *probe;
+
+ probe = g_task_get_source_object (task);
+
+ mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ custom_init_step (task);
+ return;
+ }
+
+ mm_obj_dbg (probe, "error flipping secondary ports to AT mode: %s", error->message);
+ }
+
+ /* Finish custom_init */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static gboolean
+custom_init_wait_cb (GTask *task)
+{
+ custom_init_step (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+custom_init_step (GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* If cancelled, end */
+ if (g_task_return_error_if_cancelled (task)) {
+ mm_obj_dbg (probe, "no need to keep on running custom init");
+ g_object_unref (task);
+ return;
+ }
+
+ /* If device has a QMI port, don't run $NWDMAT */
+ if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (probe)))) {
+ mm_obj_dbg (probe, "no need to run custom init: device has QMI port");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (ctx->wait_time > 0) {
+ ctx->wait_time--;
+ g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, task);
+ return;
+ }
+
+ if (ctx->nwdmat_retries > 0) {
+ ctx->nwdmat_retries--;
+ mm_port_serial_at_command (ctx->port,
+ "$NWDMAT=1",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)nwdmat_ready,
+ task);
+ return;
+ }
+
+ /* Finish custom_init */
+ mm_obj_dbg (probe, "couldn't flip secondary port to AT: all retries consumed");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_common_novatel_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CustomInitContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new (CustomInitContext);
+ ctx->port = g_object_ref (port);
+ ctx->nwdmat_retries = 3;
+ ctx->wait_time = 2;
+
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)custom_init_context_free);
+
+ custom_init_step (task);
+}
diff --git a/plugins/novatel/mm-common-novatel.h b/plugins/novatel/mm-common-novatel.h
new file mode 100644
index 00000000..70572fd3
--- /dev/null
+++ b/plugins/novatel/mm-common-novatel.h
@@ -0,0 +1,31 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_COMMON_NOVATEL_H
+#define MM_COMMON_NOVATEL_H
+
+#include "glib.h"
+#include "mm-plugin.h"
+
+void mm_common_novatel_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_common_novatel_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error);
+
+#endif /* MM_COMMON_NOVATEL_H */
diff --git a/plugins/novatel/mm-plugin-novatel-lte.c b/plugins/novatel/mm-plugin-novatel-lte.c
index e5fd91d9..18aac32c 100644
--- a/plugins/novatel/mm-plugin-novatel-lte.c
+++ b/plugins/novatel/mm-plugin-novatel-lte.c
@@ -26,23 +26,22 @@
#include "mm-plugin-novatel-lte.h"
#include "mm-private-boxed-types.h"
#include "mm-broadband-modem-novatel-lte.h"
-#include "mm-log.h"
G_DEFINE_TYPE (MMPluginNovatelLte, mm_plugin_novatel_lte, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_novatel_lte_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_novatel_lte_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -60,7 +59,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_NOVATEL_LTE,
- MM_PLUGIN_NAME, "Novatel LTE",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_PRODUCT_IDS, products,
MM_PLUGIN_ALLOWED_SINGLE_AT, TRUE,
diff --git a/plugins/novatel/mm-plugin-novatel.c b/plugins/novatel/mm-plugin-novatel.c
index 9eaf2859..b26dcf9a 100644
--- a/plugins/novatel/mm-plugin-novatel.c
+++ b/plugins/novatel/mm-plugin-novatel.c
@@ -28,9 +28,10 @@
#include <libmm-glib.h>
#include "mm-plugin-novatel.h"
+#include "mm-common-novatel.h"
#include "mm-private-boxed-types.h"
#include "mm-broadband-modem-novatel.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
@@ -38,168 +39,14 @@
G_DEFINE_TYPE (MMPluginNovatel, mm_plugin_novatel, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
-
-/*****************************************************************************/
-/* Custom commands for AT probing */
-
-/* We need to explicitly flip secondary ports to AT mode.
- * We also use this command also for checking AT support in the current port.
- */
-static const MMPortProbeAtCommand custom_at_probe[] = {
- { "$NWDMAT=1", 3, mm_port_probe_response_processor_is_at },
- { "$NWDMAT=1", 3, mm_port_probe_response_processor_is_at },
- { "$NWDMAT=1", 3, mm_port_probe_response_processor_is_at },
- { NULL }
-};
-
-/*****************************************************************************/
-/* Custom init */
-
-typedef struct {
- MMPortProbe *probe;
- MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- guint nwdmat_retries;
- guint wait_time;
-} CustomInitContext;
-
-static void
-custom_init_context_complete_and_free (CustomInitContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
- g_slice_free (CustomInitContext, ctx);
-}
-
-static gboolean
-novatel_custom_init_finish (MMPortProbe *probe,
- GAsyncResult *result,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
-}
-
-static void custom_init_step (CustomInitContext *ctx);
-
-static void
-nwdmat_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- CustomInitContext *ctx)
-{
- const gchar *response;
- GError *error = NULL;
-
- response = mm_port_serial_at_command_finish (port, res, &error);
- if (error) {
- if (g_error_matches (error,
- MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
- custom_init_step (ctx);
- goto out;
- }
-
- mm_dbg ("(Novatel) Error flipping secondary ports to AT mode: %s", error->message);
- }
-
- /* Finish custom_init */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
-
-out:
- if (error)
- g_error_free (error);
-}
-
-static gboolean
-custom_init_wait_cb (CustomInitContext *ctx)
-{
- custom_init_step (ctx);
- return FALSE;
-}
-
-static void
-custom_init_step (CustomInitContext *ctx)
-{
- /* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(Novatel) no need to keep on running custom init in (%s)",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
- return;
- }
-
- /* If device has a QMI port, don't run $NWDMAT */
- if (mm_port_probe_list_has_qmi_port (mm_device_peek_port_probe_list (mm_port_probe_peek_device (ctx->probe)))) {
- mm_dbg ("(Novatel) no need to run custom init in (%s): device has QMI port",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
- return;
- }
-
- if (ctx->wait_time > 0) {
- ctx->wait_time--;
- g_timeout_add_seconds (1, (GSourceFunc)custom_init_wait_cb, ctx);
- return;
- }
-
- if (ctx->nwdmat_retries > 0) {
- ctx->nwdmat_retries--;
- mm_port_serial_at_command (ctx->port,
- "$NWDMAT=1",
- 3,
- FALSE, /* raw */
- FALSE, /* allow_cached */
- ctx->cancellable,
- (GAsyncReadyCallback)nwdmat_ready,
- ctx);
- return;
- }
-
- /* Finish custom_init */
- mm_dbg ("(Novatel) couldn't flip secondary port to AT in (%s): all retries consumed",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- custom_init_context_complete_and_free (ctx);
-}
-
-static void
-novatel_custom_init (MMPortProbe *probe,
- MMPortSerialAt *port,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- CustomInitContext *ctx;
-
- ctx = g_slice_new (CustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- novatel_custom_init);
- ctx->probe = g_object_ref (probe);
- ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- ctx->nwdmat_retries = 3;
- ctx->wait_time = 2;
-
- custom_init_step (ctx);
-}
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -208,8 +55,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Novatel modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered Novatel modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -217,7 +64,7 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_novatel_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_novatel_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -229,20 +76,18 @@ create_modem (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
- static const guint16 vendors[] = { 0x1410, /* Novatel */
- 0x413c, /* Dell */
- 0 };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendors[] = { 0x1410, 0 };
static const mm_uint16_pair forbidden_products[] = { { 0x1410, 0x9010 }, /* Novatel E362 */
{ 0, 0 } };
static const MMAsyncMethod custom_init = {
- .async = G_CALLBACK (novatel_custom_init),
- .finish = G_CALLBACK (novatel_custom_init_finish),
+ .async = G_CALLBACK (mm_common_novatel_custom_init),
+ .finish = G_CALLBACK (mm_common_novatel_custom_init_finish),
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_NOVATEL,
- MM_PLUGIN_NAME, "Novatel",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendors,
MM_PLUGIN_FORBIDDEN_PRODUCT_IDS, forbidden_products,
diff --git a/plugins/novatel/mm-shared.c b/plugins/novatel/mm-shared.c
new file mode 100644
index 00000000..6bd11e4b
--- /dev/null
+++ b/plugins/novatel/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Novatel)
diff --git a/plugins/novatel/mm-sim-novatel-lte.c b/plugins/novatel/mm-sim-novatel-lte.c
index 41d30411..4d71bd80 100644
--- a/plugins/novatel/mm-sim-novatel-lte.c
+++ b/plugins/novatel/mm-sim-novatel-lte.c
@@ -23,7 +23,6 @@
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
@@ -39,20 +38,13 @@ load_imsi_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- gchar *imsi;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- imsi = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("loaded IMSI: %s", imsi);
- return g_strdup (imsi);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
imsi_read_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *response, *str;
@@ -64,9 +56,8 @@ imsi_read_ready (MMBaseModem *modem,
response = mm_base_modem_at_command_finish (modem, res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -76,13 +67,12 @@ imsi_read_ready (MMBaseModem *modem,
/* With or without quotes... */
if (sscanf (str, "%d,%d,\"%18c\"", &sw1, &sw2, (char *) &buf) != 3 &&
sscanf (str, "%d,%d,%18c", &sw1, &sw2, (char *) &buf) != 3) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse the CRSM response: '%s'",
- response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse the CRSM response: '%s'",
+ response);
+ g_object_unref (task);
return;
}
@@ -90,13 +80,12 @@ imsi_read_ready (MMBaseModem *modem,
(sw1 != 0x91) &&
(sw1 != 0x92) &&
(sw1 != 0x9f)) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
- sw1, sw2);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ g_object_unref (task);
return;
}
@@ -114,25 +103,23 @@ imsi_read_ready (MMBaseModem *modem,
}
/* Invalid character */
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "CRSM IMSI response contained invalid character '%c'",
- buf[len]);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "CRSM IMSI response contained invalid character '%c'",
+ buf[len]);
+ g_object_unref (task);
return;
}
/* BCD encoded IMSIs plus the length byte and parity are 18 digits long */
if (len != 18) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid +CRSM IMSI response size (was %zd, expected 18)",
- len);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid +CRSM IMSI response size (was %zd, expected 18)",
+ len);
+ g_object_unref (task);
return;
}
@@ -160,19 +147,17 @@ imsi_read_ready (MMBaseModem *modem,
/* Ensure all 'F's, if any, are at the end */
for (; i < 15; i++) {
if (imsi[i] != 'F') {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid +CRSM IMSI length (unexpected F)");
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid +CRSM IMSI length (unexpected F)");
+ g_object_unref (task);
return;
}
}
- g_simple_async_result_set_op_res_gpointer (simple, imsi, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, g_strdup (imsi), g_free);
+ g_object_unref (task);
}
static void
@@ -186,17 +171,13 @@ load_imsi (MMBaseSim *self,
MM_BASE_SIM_MODEM, &modem,
NULL);
- mm_dbg ("loading (Novatel LTE) IMSI...");
mm_base_modem_at_command (
modem,
"+CRSM=176,28423,0,0,9",
3,
FALSE,
(GAsyncReadyCallback)imsi_read_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_imsi));
+ g_task_new (self, NULL, callback, user_data));
g_object_unref (modem);
}
@@ -234,6 +215,7 @@ mm_sim_novatel_lte_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
diff --git a/plugins/option/mm-broadband-bearer-hso.c b/plugins/option/mm-broadband-bearer-hso.c
index f1c0445e..592b2804 100644
--- a/plugins/option/mm-broadband-bearer-hso.c
+++ b/plugins/option/mm-broadband-bearer-hso.c
@@ -30,17 +30,17 @@
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-hso.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-daemon-enums-types.h"
G_DEFINE_TYPE (MMBroadbandBearerHso, mm_broadband_bearer_hso, MM_TYPE_BROADBAND_BEARER);
struct _MMBroadbandBearerHsoPrivate {
- guint auth_idx;
- gpointer connect_pending;
- guint connect_pending_id;
- gulong connect_cancellable_id;
+ guint auth_idx;
+
+ GTask *connect_pending;
+ guint connect_pending_id;
gulong connect_port_closed_id;
};
@@ -48,21 +48,16 @@ struct _MMBroadbandBearerHsoPrivate {
/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerHso *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
- GSimpleAsyncResult *result;
} GetIpConfig3gppContext;
static void
-get_ip_config_context_complete_and_free (GetIpConfig3gppContext *ctx)
+get_ip_config_context_free (GetIpConfig3gppContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (GetIpConfig3gppContext, ctx);
}
@@ -75,12 +70,12 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
{
MMBearerIpConfig *ip_config;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ ip_config = g_task_propagate_pointer (G_TASK (res), error);
+ if (!ip_config)
return FALSE;
/* No IPv6 for now */
- ip_config = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *ipv4_config = g_object_ref (ip_config);
+ *ipv4_config = ip_config; /* Transfer ownership */
*ipv6_config = NULL;
return TRUE;
}
@@ -90,8 +85,9 @@ get_ip_config_3gpp_finish (MMBroadbandBearer *self,
static void
ip_config_ready (MMBaseModem *modem,
GAsyncResult *res,
- GetIpConfig3gppContext *ctx)
+ GTask *task)
{
+ GetIpConfig3gppContext *ctx;
MMBearerIpConfig *ip_config = NULL;
const gchar *response;
GError *error = NULL;
@@ -102,8 +98,8 @@ ip_config_ready (MMBaseModem *modem,
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- get_ip_config_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -111,23 +107,24 @@ ip_config_ready (MMBaseModem *modem,
/* Check result */
if (!g_str_has_prefix (response, OWANDATA_TAG)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get IP config: invalid response '%s'",
- response);
- get_ip_config_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get IP config: invalid response '%s'",
+ response);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
response = mm_strip_tag (response, OWANDATA_TAG);
items = g_strsplit (response, ", ", 0);
for (i = 0, dns_i = 0; items[i]; i++) {
if (i == 0) { /* CID */
- gint num;
+ guint num;
- if (!mm_get_int_from_str (items[i], &num) ||
+ if (!mm_get_uint_from_str (items[i], &num) ||
num != ctx->cid) {
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -159,24 +156,22 @@ ip_config_ready (MMBaseModem *modem,
if (!ip_config) {
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get IP config: couldn't parse response '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get IP config: couldn't parse response '%s'",
+ response);
} else {
/* If we got DNS entries, set them in the IP config */
if (dns[0])
mm_bearer_ip_config_set_dns (ip_config, (const gchar **)dns);
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- ip_config,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, ip_config, g_object_unref);
}
- get_ip_config_context_complete_and_free (ctx);
+ g_object_unref (task);
g_strfreev (items);
}
@@ -192,17 +187,16 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
gpointer user_data)
{
GetIpConfig3gppContext *ctx;
+ GTask *task;
gchar *command;
ctx = g_slice_new0 (GetIpConfig3gppContext);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
+ ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_ip_config_3gpp);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)get_ip_config_context_free);
command = g_strdup_printf ("AT_OWANDATA=%d", cid);
mm_base_modem_at_command_full (
@@ -214,7 +208,7 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)ip_config_ready,
- ctx);
+ task);
g_free (command);
}
@@ -222,88 +216,72 @@ get_ip_config_3gpp (MMBroadbandBearer *self,
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearerHso *self;
- MMBaseModem *modem;
+ MMBaseModem *modem;
MMPortSerialAt *primary;
- guint cid;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- MMPort *data;
- guint auth_idx;
- GError *saved_error;
+ guint cid;
+ MMPort *data;
+ guint auth_idx;
+ GError *saved_error;
} Dial3gppContext;
static void
-dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+dial_3gpp_context_free (Dial3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->data)
- g_object_unref (ctx->data);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->primary);
- g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
+ g_assert (!ctx->saved_error);
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->primary);
+ g_clear_object (&ctx->modem);
g_slice_free (Dial3gppContext, ctx);
}
-static gboolean
-dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx,
- GError **error)
+static guint
+dial_3gpp_get_connecting_cid (GTask *task)
{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Dial operation has been cancelled");
- return TRUE;
-}
-
-static gboolean
-dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx)
-{
- GError *error = NULL;
-
- if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error))
- return FALSE;
+ Dial3gppContext *ctx;
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return TRUE;
+ ctx = g_task_get_task_data (task);
+ return ctx->cid;
}
static MMPort *
-dial_3gpp_finish (MMBroadbandBearer *self,
- GAsyncResult *res,
- GError **error)
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ return MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
}
static void
-connect_reset_ready (MMBaseModem *modem,
+connect_reset_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_full_finish (modem, res, NULL);
- /* error should have already been set in the simple async result */
- dial_3gpp_context_complete_and_free (ctx);
+ /* When reset is requested, it was either cancelled or an error was stored */
+ if (!g_task_return_error_if_cancelled (task)) {
+ g_assert (ctx->saved_error);
+ g_task_return_error (task, ctx->saved_error);
+ ctx->saved_error = NULL;
+ }
+
+ g_object_unref (task);
}
static void
-connect_reset (Dial3gppContext *ctx)
+connect_reset (GTask *task)
{
- gchar *command;
+ Dial3gppContext *ctx;
+ gchar *command;
+
+ ctx = g_task_get_task_data (task);
/* Need to reset the connection attempt */
- command = g_strdup_printf ("AT_OWANCALL=%d,0,1",
- ctx->cid);
+ command = g_strdup_printf ("AT_OWANCALL=%d,0,1", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
@@ -312,53 +290,29 @@ connect_reset (Dial3gppContext *ctx)
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)connect_reset_ready,
- ctx);
+ task);
g_free (command);
}
static void
-report_connection_status (MMBaseBearer *bearer,
- MMBearerConnectionStatus status)
+process_pending_connect_attempt (MMBroadbandBearerHso *self,
+ MMBearerConnectionStatus status)
{
- MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (bearer);
+ GTask *task;
Dial3gppContext *ctx;
- g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
- status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
- status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
-
- /* Recover context (if any) and remove both cancellation and timeout (if any)*/
- ctx = self->priv->connect_pending;
+ /* Recover task and remove both cancellation and timeout (if any)*/
+ g_assert (self->priv->connect_pending);
+ task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
- /* Connection status reported but no connection attempt? */
- if (!ctx) {
- g_assert (self->priv->connect_pending_id == 0);
-
- mm_dbg ("Received spontaneous _OWANCALL (%s)",
- mm_bearer_connection_status_get_string (status));
-
- if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
- /* If no connection attempt on-going, make sure we mark ourselves as
- * disconnected */
- MM_BASE_BEARER_CLASS (mm_broadband_bearer_hso_parent_class)->report_connection_status (
- bearer,
- status);
- }
- return;
- }
+ ctx = g_task_get_task_data (task);
if (self->priv->connect_pending_id) {
g_source_remove (self->priv->connect_pending_id);
self->priv->connect_pending_id = 0;
}
- if (self->priv->connect_cancellable_id) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
- self->priv->connect_cancellable_id = 0;
- }
-
if (self->priv->connect_port_closed_id) {
g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id);
self->priv->connect_port_closed_id = 0;
@@ -366,105 +320,60 @@ report_connection_status (MMBaseBearer *bearer,
/* Reporting connected */
if (status == MM_BEARER_CONNECTION_STATUS_CONNECTED) {
- /* If we wanted to get cancelled before, do it now */
- if (ctx->saved_error) {
- /* Keep error */
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
- ctx->saved_error = NULL;
- /* Cancel connection */
- connect_reset (ctx);
+ /* If we wanted to get cancelled before, do it now. */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ connect_reset (task);
return;
}
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- (GDestroyNotify)g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
- return;
- }
-
- /* If we wanted to get cancelled before and now we couldn't connect,
- * use the cancelled error and return */
- if (ctx->saved_error) {
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
- ctx->saved_error = NULL;
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
return;
}
- /* Received CONNECTION_FAILED or DISCONNECTED during a connection attempt? */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Call setup failed");
- dial_3gpp_context_complete_and_free (ctx);
+ /* Received CONNECTION_FAILED or DISCONNECTED during a connection attempt,
+ * so return a failed error. Note that if the cancellable has been cancelled
+ * already, a cancelled error would be returned instead. */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Call setup failed");
+ g_object_unref (task);
}
static gboolean
connect_timed_out_cb (MMBroadbandBearerHso *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- /* Recover context and remove it from the private info */
- ctx = self->priv->connect_pending;
+ /* Cleanup timeout ID */
+ self->priv->connect_pending_id = 0;
+
+ /* Recover task and own it */
+ task = self->priv->connect_pending;
self->priv->connect_pending = NULL;
+ g_assert (task);
- /* Remove cancellation, if found */
- if (self->priv->connect_cancellable_id) {
- g_cancellable_disconnect (ctx->cancellable,
- self->priv->connect_cancellable_id);
- self->priv->connect_cancellable_id = 0;
- }
+ ctx = g_task_get_task_data (task);
/* Remove closed port watch, if found */
- if (ctx && self->priv->connect_port_closed_id) {
+ if (self->priv->connect_port_closed_id) {
g_signal_handler_disconnect (ctx->primary, self->priv->connect_port_closed_id);
self->priv->connect_port_closed_id = 0;
}
- /* Cleanup timeout ID */
- self->priv->connect_pending_id = 0;
-
- /* If we were cancelled, prefer that error */
- if (ctx->saved_error) {
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
- ctx->saved_error = NULL;
- } else
- g_simple_async_result_set_error (ctx->result,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
- "Connection attempt timed out");
+ /* Setup error to return after the reset */
+ g_assert (!ctx->saved_error);
+ ctx->saved_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
+ "Connection attempt timed out");
/* It's probably pointless to try to reset this here, but anyway... */
- connect_reset (ctx);
+ connect_reset (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
-connect_cancelled_cb (GCancellable *cancellable,
- MMBroadbandBearerHso *self)
-{
- Dial3gppContext *ctx;
-
- /* Recover context but DON'T remove it from the private info */
- ctx = self->priv->connect_pending;
-
- /* Remove the cancellable
- * NOTE: we shouldn't remove the timeout yet. We still need to wait
- * to get connected before running the explicit connection reset */
- self->priv->connect_cancellable_id = 0;
-
- /* Store cancelled error */
- g_assert (dial_3gpp_context_set_error_if_cancelled (ctx, &ctx->saved_error));
-
- /* We cannot reset right here, we need to wait for the connection
- * attempt to finish */
-}
-
-static void
-forced_close_cb (MMPortSerial *port,
- MMBroadbandBearerHso *self)
+forced_close_cb (MMBroadbandBearerHso *self)
{
/* Just treat the forced close event as any other unsolicited message */
mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
@@ -472,77 +381,89 @@ forced_close_cb (MMPortSerial *port,
}
static void
-activate_ready (MMBaseModem *modem,
- GAsyncResult *res,
+activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
MMBroadbandBearerHso *self)
{
+ GTask *task;
Dial3gppContext *ctx;
- GError *error = NULL;
+ GError *error = NULL;
- /* Try to recover the connection context. If none found, it means the
- * context was already completed and we have nothing else to do. */
- ctx = self->priv->connect_pending;
-
- /* Balance refcount with the extra ref we passed to command_full() */
- g_object_unref (self);
-
- if (!ctx) {
- mm_dbg ("Connection context was finished already by an unsolicited message");
+ task = g_steal_pointer (&self->priv->connect_pending);
+ /* Try to recover the connection task. If none found, it means the
+ * task was already completed and we have nothing else to do.
+ * But note that we won't take owneship of the task yet! */
+ if (!task) {
+ mm_obj_dbg (self, "connection context was finished already by an unsolicited message");
/* Run _finish() to finalize the async call, even if we don't care
- * the result */
+ * about the result */
mm_base_modem_at_command_full_finish (modem, res, NULL);
- return;
+ goto out;
}
/* From now on, if we get cancelled, we'll need to run the connection
* reset ourselves just in case */
+ /* Errors on the dial command are fatal */
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
- /* We will now setup a timeout so that we don't wait forever to get the
- * connection on */
- self->priv->connect_pending_id = g_timeout_add_seconds (30,
+ /* Track the task again */
+ self->priv->connect_pending = task;
+
+ /* We will now setup a timeout and keep the context in the bearer's private.
+ * Reports of modem being connected will arrive via unsolicited messages.
+ * This timeout should be long enough. Actually... ideally should never get
+ * reached. */
+ self->priv->connect_pending_id = g_timeout_add_seconds (MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
(GSourceFunc)connect_timed_out_cb,
self);
- self->priv->connect_cancellable_id = g_cancellable_connect (ctx->cancellable,
- G_CALLBACK (connect_cancelled_cb),
- self,
- NULL);
/* If we get the port closed, we treat as a connect error */
- self->priv->connect_port_closed_id = g_signal_connect (ctx->primary,
- "forced-close",
- G_CALLBACK (forced_close_cb),
- self);
+ ctx = g_task_get_task_data (task);
+ self->priv->connect_port_closed_id = g_signal_connect_swapped (ctx->primary,
+ "forced-close",
+ G_CALLBACK (forced_close_cb),
+ self);
+
+ out:
+ /* Balance refcount with the extra ref we passed to command_full() */
+ g_object_unref (self);
}
-static void authenticate (Dial3gppContext *ctx);
+static void authenticate (GTask *task);
static void
-authenticate_ready (MMBaseModem *modem,
+authenticate_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
- gchar *command;
+ MMBroadbandBearerHso *self;
+ Dial3gppContext *ctx;
+ gchar *command;
/* If cancelled, complete */
- if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
if (!mm_base_modem_at_command_full_finish (modem, res, NULL)) {
/* Try the next auth command */
ctx->auth_idx++;
- authenticate (ctx);
+ authenticate (task);
return;
}
/* Store which auth command worked, for next attempts */
- ctx->self->priv->auth_idx = ctx->auth_idx;
+ self->priv->auth_idx = ctx->auth_idx;
/* The unsolicited response to AT_OWANCALL may come before the OK does.
* We will keep the connection context in the bearer private data so
@@ -550,12 +471,11 @@ authenticate_ready (MMBaseModem *modem,
* also that we do NOT pass the ctx to the GAsyncReadyCallback, as it
* may not be valid any more when the callback is called (it may be
* already completed in the unsolicited handling) */
- g_assert (ctx->self->priv->connect_pending == NULL);
- ctx->self->priv->connect_pending = ctx;
+ g_assert (self->priv->connect_pending == NULL);
+ self->priv->connect_pending = task;
/* Success, activate the PDP context and start the data session */
- command = g_strdup_printf ("AT_OWANCALL=%d,1,1",
- ctx->cid);
+ command = g_strdup_printf ("AT_OWANCALL=%d,1,1", ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
@@ -563,8 +483,8 @@ authenticate_ready (MMBaseModem *modem,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
- (GAsyncReadyCallback)activate_ready,
- g_object_ref (ctx->self)); /* we pass the bearer object! */
+ (GAsyncReadyCallback) activate_ready,
+ g_object_ref (self)); /* we pass the bearer object! */
g_free (command);
}
@@ -578,62 +498,66 @@ const gchar *auth_commands[] = {
};
static void
-authenticate (Dial3gppContext *ctx)
+authenticate (GTask *task)
{
- gchar *command;
- const gchar *user;
- const gchar *password;
- MMBearerAllowedAuth allowed_auth;
+ MMBroadbandBearerHso *self;
+ Dial3gppContext *ctx;
+ gchar *command;
+ const gchar *user;
+ const gchar *password;
+ MMBearerAllowedAuth allowed_auth;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
if (!auth_commands[ctx->auth_idx]) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't run HSO authentication");
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't run HSO authentication");
+ g_object_unref (task);
return;
}
- user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
/* Both user and password are required; otherwise firmware returns an error */
if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) {
- mm_dbg ("Not using authentication");
+ mm_obj_dbg (self, "not using authentication");
command = g_strdup_printf ("%s=%d,0",
auth_commands[ctx->auth_idx],
ctx->cid);
} else {
gchar *quoted_user;
gchar *quoted_password;
- guint hso_auth;
+ guint hso_auth;
if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
- mm_dbg ("Using default (PAP) authentication method");
- hso_auth = 1;
- } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
- mm_dbg ("Using PAP authentication method");
- hso_auth = 1;
+ mm_obj_dbg (self, "using default (CHAP) authentication method");
+ hso_auth = 2;
} else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) {
- mm_dbg ("Using CHAP authentication method");
+ mm_obj_dbg (self, "using CHAP authentication method");
hso_auth = 2;
+ } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
+ mm_obj_dbg (self, "using PAP authentication method");
+ hso_auth = 1;
} else {
gchar *str;
str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot use any of the specified authentication methods (%s)",
- str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot use any of the specified authentication methods (%s)",
+ str);
+ g_object_unref (task);
g_free (str);
- dial_3gpp_context_complete_and_free (ctx);
return;
}
- quoted_user = mm_port_serial_at_quote_string (user);
+ quoted_user = mm_port_serial_at_quote_string (user);
quoted_password = mm_port_serial_at_quote_string (password);
command = g_strdup_printf ("%s=%d,%u,%s,%s",
auth_commands[ctx->auth_idx],
@@ -653,70 +577,63 @@ authenticate (Dial3gppContext *ctx)
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)authenticate_ready,
- ctx);
+ task);
g_free (command);
}
static void
-dial_3gpp (MMBroadbandBearer *self,
- MMBaseModem *modem,
- MMPortSerialAt *primary,
- guint cid,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dial_3gpp (MMBroadbandBearer *_self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- Dial3gppContext *ctx;
+ MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (_self);
+ GTask *task;
+ Dial3gppContext *ctx;
g_assert (primary != NULL);
+ task = g_task_new (self, cancellable, callback, user_data);
+
ctx = g_slice_new0 (Dial3gppContext);
- ctx->self = g_object_ref (self);
- ctx->modem = g_object_ref (modem);
+ ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
- ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- dial_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
+ ctx->cid = cid;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
/* Always start with the index that worked last time
* (will be 0 the first time)*/
- ctx->auth_idx = ctx->self->priv->auth_idx;
+ ctx->auth_idx = self->priv->auth_idx;
/* We need a net data port */
ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
if (!ctx->data) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
return;
}
- authenticate (ctx);
+ authenticate (task);
}
/*****************************************************************************/
/* 3GPP disconnect */
typedef struct {
- MMBroadbandBearerHso *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
- GSimpleAsyncResult *result;
} DisconnectContext;
static void
-disconnect_context_complete_and_free (DisconnectContext *ctx)
+disconnect_context_free (DisconnectContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->primary);
- g_object_unref (ctx->self);
g_object_unref (ctx->modem);
g_free (ctx);
}
@@ -726,25 +643,28 @@ disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-disconnect_owancall_ready (MMBaseModem *modem,
+disconnect_owancall_ready (MMBaseModem *modem,
GAsyncResult *res,
- DisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearerHso *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
/* Ignore errors for now */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- mm_dbg ("Disconnection failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -759,34 +679,72 @@ disconnect_3gpp (MMBroadbandBearer *self,
{
gchar *command;
DisconnectContext *ctx;
+ GTask *task;
g_assert (primary != NULL);
ctx = g_new0 (DisconnectContext, 1);
- ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect_3gpp);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free);
/* Use specific CID */
command = g_strdup_printf ("AT_OWANCALL=%d,0,0", cid);
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
command,
- 3,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_owancall_ready,
- ctx);
+ task);
g_free (command);
}
/*****************************************************************************/
+gint
+mm_broadband_bearer_hso_get_connecting_profile_id (MMBroadbandBearerHso *self)
+{
+ return (self->priv->connect_pending ?
+ (gint)dial_3gpp_get_connecting_cid (self->priv->connect_pending) :
+ MM_3GPP_PROFILE_ID_UNKNOWN);
+}
+
+/*****************************************************************************/
+
+static void
+report_connection_status (MMBaseBearer *_self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
+{
+ MMBroadbandBearerHso *self = MM_BROADBAND_BEARER_HSO (_self);
+
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
+ status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED ||
+ status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+
+ /* Process pending connection attempt */
+ if (self->priv->connect_pending) {
+ process_pending_connect_attempt (self, status);
+ return;
+ }
+
+ mm_obj_dbg (self, "received spontaneous _OWANCALL (%s)",
+ mm_bearer_connection_status_get_string (status));
+
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ /* If no connection attempt on-going, make sure we mark ourselves as
+ * disconnected */
+ MM_BASE_BEARER_CLASS (mm_broadband_bearer_hso_parent_class)->report_connection_status (_self, status,connection_error);
+ }
+}
+
+/*****************************************************************************/
+
MMBaseBearer *
mm_broadband_bearer_hso_new_finish (GAsyncResult *res,
GError **error)
@@ -844,6 +802,13 @@ mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHsoPrivate));
base_bearer_class->report_connection_status = report_connection_status;
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = NULL;
+ base_bearer_class->reload_connection_status_finish = NULL;
+#endif
+
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
diff --git a/plugins/option/mm-broadband-bearer-hso.h b/plugins/option/mm-broadband-bearer-hso.h
index 7812d74d..def46ac3 100644
--- a/plugins/option/mm-broadband-bearer-hso.h
+++ b/plugins/option/mm-broadband-bearer-hso.h
@@ -56,4 +56,6 @@ void mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem,
MMBaseBearer *mm_broadband_bearer_hso_new_finish (GAsyncResult *res,
GError **error);
+gint mm_broadband_bearer_hso_get_connecting_profile_id (MMBroadbandBearerHso *self);
+
#endif /* MM_BROADBAND_BEARER_HSO_H */
diff --git a/plugins/option/mm-broadband-modem-hso.c b/plugins/option/mm-broadband-modem-hso.c
index d2d46891..a2cc1770 100644
--- a/plugins/option/mm-broadband-modem-hso.c
+++ b/plugins/option/mm-broadband-modem-hso.c
@@ -25,7 +25,7 @@
#include "ModemManager.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
@@ -34,7 +34,9 @@
#include "mm-broadband-modem-hso.h"
#include "mm-broadband-bearer-hso.h"
#include "mm-bearer-list.h"
+#include "mm-shared-option.h"
+static void shared_option_init (MMSharedOption *iface);
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
static void iface_modem_location_init (MMIfaceModemLocation *iface);
@@ -43,6 +45,7 @@ static MMIfaceModem3gpp *iface_modem_3gpp_parent;
static MMIfaceModemLocation *iface_modem_location_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemHso, mm_broadband_modem_hso, MM_TYPE_BROADBAND_MODEM_OPTION, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_OPTION, shared_option_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init));
@@ -62,52 +65,41 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New %s bearer created at DBus path '%s'",
- MM_IS_BROADBAND_BEARER_HSO (bearer) ? "HSO" : "Generic",
- mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
broadband_bearer_hso_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_hso_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
@@ -116,30 +108,27 @@ modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_bearer_properties_get_ip_type (properties) &
(MM_BEARER_IP_FAMILY_IPV6 | MM_BEARER_IP_FAMILY_IPV4V6)) {
- mm_dbg ("Creating generic bearer (IPv6 requested)...");
+ mm_obj_dbg (self, "creating generic bearer (IPv6 requested)...");
mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- result);
+ task);
return;
}
- mm_dbg ("Creating HSO bearer...");
+ mm_obj_dbg (self, "creating HSO bearer...");
mm_broadband_bearer_hso_new (MM_BROADBAND_MODEM_HSO (self),
properties,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_hso_new_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -150,16 +139,13 @@ load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_unlock_retries_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -167,10 +153,8 @@ load_unlock_retries_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -180,18 +164,15 @@ load_unlock_retries_ready (MMBaseModem *self,
retries = mm_unlock_retries_new ();
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- retries,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, retries, g_object_unref);
} else {
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid unlock retries response: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid unlock retries response: '%s'",
+ response);
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -205,10 +186,7 @@ load_unlock_retries (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_unlock_retries_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_unlock_retries));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -223,7 +201,19 @@ static void
bearer_list_report_status_foreach (MMBaseBearer *bearer,
BearerListReportStatusForeachContext *ctx)
{
- if (mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (bearer)) != ctx->cid)
+ gint profile_id;
+ gint connecting_profile_id;
+
+ if (!MM_IS_BROADBAND_BEARER_HSO (bearer))
+ return;
+
+ /* The profile ID in the base bearer is set only once the modem is connected */
+ profile_id = mm_base_bearer_get_profile_id (bearer);
+
+ /* The profile ID in the hso bearer is available during the connecting phase */
+ connecting_profile_id = mm_broadband_bearer_hso_get_connecting_profile_id (MM_BROADBAND_BEARER_HSO (bearer));
+
+ if ((profile_id != (gint)ctx->cid) && (connecting_profile_id != (gint)ctx->cid))
return;
mm_base_bearer_report_connection_status (MM_BASE_BEARER (bearer), ctx->status);
@@ -234,7 +224,7 @@ hso_connection_status_changed (MMPortSerialAt *port,
GMatchInfo *match_info,
MMBroadbandModemHso *self)
{
- MMBearerList *list = NULL;
+ g_autoptr(MMBearerList) list = NULL;
BearerListReportStatusForeachContext ctx;
guint cid;
guint status;
@@ -270,14 +260,10 @@ hso_connection_status_changed (MMPortSerialAt *port,
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
- if (!list)
- return;
/* Will report status only in the bearer with the specific CID */
- mm_bearer_list_foreach (list,
- (MMBearerListForeachFunc)bearer_list_report_status_foreach,
- &ctx);
- g_object_unref (list);
+ if (list)
+ mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_list_report_status_foreach, &ctx);
}
static gboolean
@@ -285,18 +271,18 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
mm_port_serial_at_add_unsolicited_msg_handler (
@@ -306,11 +292,9 @@ parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
self,
NULL);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -318,33 +302,26 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
-
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -352,13 +329,6 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
-
/* Our own cleanup first */
mm_port_serial_at_add_unsolicited_msg_handler (
mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
@@ -369,7 +339,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -380,26 +350,29 @@ location_load_capabilities_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_LOCATION_SOURCE_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
}
static void
parent_load_capabilities_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMModemLocationSource sources;
GError *error = NULL;
sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -423,11 +396,8 @@ parent_load_capabilities_ready (MMIfaceModemLocation *self,
MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED);
/* So we're done, complete */
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (sources),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, sources);
+ g_object_unref (task);
}
static void
@@ -435,38 +405,20 @@ location_load_capabilities (MMIfaceModemLocation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- location_load_capabilities);
-
/* Chain up parent's setup */
iface_modem_location_parent->load_capabilities (
self,
(GAsyncReadyCallback)parent_load_capabilities_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Enable/Disable location gathering (Location interface) */
typedef struct {
- MMBroadbandModemHso *self;
- GSimpleAsyncResult *result;
MMModemLocationSource source;
} LocationGatheringContext;
-static void
-location_gathering_context_complete_and_free (LocationGatheringContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LocationGatheringContext, ctx);
-}
-
/******************************/
/* Disable location gathering */
@@ -475,21 +427,21 @@ disable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
gps_disabled_ready (MMBaseModem *self,
GAsyncResult *res,
- LocationGatheringContext *ctx)
+ GTask *task)
{
+ LocationGatheringContext *ctx;
MMPortSerialGps *gps_port;
GError *error = NULL;
- if (!mm_base_modem_at_command_full_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ mm_base_modem_at_command_full_finish (self, res, &error);
+
+ ctx = g_task_get_task_data (task);
/* Only use the GPS port in NMEA/RAW setups */
if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
@@ -500,7 +452,12 @@ gps_disabled_ready (MMBaseModem *self,
mm_port_serial_close (MM_PORT_SERIAL (gps_port));
}
- location_gathering_context_complete_and_free (ctx);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -512,15 +469,14 @@ disable_location_gathering (MMIfaceModemLocation *self,
MMBroadbandModemHso *hso = MM_BROADBAND_MODEM_HSO (self);
gboolean stop_gps = FALSE;
LocationGatheringContext *ctx;
+ GTask *task;
- ctx = g_slice_new (LocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disable_location_gathering);
+ ctx = g_new (LocationGatheringContext, 1);
ctx->source = source;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
/* Only stop GPS engine if no GPS-related sources enabled */
if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
MM_MODEM_LOCATION_SOURCE_GPS_RAW |
@@ -543,13 +499,13 @@ disable_location_gathering (MMIfaceModemLocation *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)gps_disabled_ready,
- ctx);
+ task);
return;
}
/* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- location_gathering_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************/
@@ -560,22 +516,25 @@ enable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
gps_enabled_ready (MMBaseModem *self,
GAsyncResult *res,
- LocationGatheringContext *ctx)
+ GTask *task)
{
+ LocationGatheringContext *ctx;
GError *error = NULL;
if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Only use the GPS port in NMEA/RAW setups */
if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
@@ -585,37 +544,41 @@ gps_enabled_ready (MMBaseModem *self,
if (!gps_port ||
!mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't open raw GPS serial port");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
} else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
}
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
- location_gathering_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
-parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+parent_enable_location_gathering_ready (MMIfaceModemLocation *_self,
GAsyncResult *res,
- LocationGatheringContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemHso *self = MM_BROADBAND_MODEM_HSO (_self);
+ LocationGatheringContext *ctx;
gboolean start_gps = FALSE;
GError *error = NULL;
- if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- location_gathering_context_complete_and_free (ctx);
+ if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Now our own enabling */
+ ctx = g_task_get_task_data (task);
+
/* NMEA, RAW and UNMANAGED are all enabled in the same way */
if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
MM_MODEM_LOCATION_SOURCE_GPS_RAW |
@@ -623,11 +586,11 @@ parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
/* Only start GPS engine if not done already.
* NOTE: interface already takes care of making sure that raw/nmea and
* unmanaged are not enabled at the same time */
- if (!(ctx->self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
start_gps = TRUE;
- ctx->self->priv->enabled_sources |= ctx->source;
+ self->priv->enabled_sources |= ctx->source;
}
if (start_gps) {
@@ -640,13 +603,13 @@ parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)gps_enabled_ready,
- ctx);
+ task);
return;
}
/* For any other location (e.g. 3GPP), or if GPS already running just return */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- location_gathering_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -656,52 +619,30 @@ enable_location_gathering (MMIfaceModemLocation *self,
gpointer user_data)
{
LocationGatheringContext *ctx;
+ GTask *task;
- ctx = g_slice_new (LocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enable_location_gathering);
+ ctx = g_new (LocationGatheringContext, 1);
ctx->source = source;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
/* Chain up parent's gathering enable */
iface_modem_location_parent->enable_location_gathering (
self,
source,
(GAsyncReadyCallback)parent_enable_location_gathering_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* Setup ports (Broadband modem class) */
static void
-trace_received (MMPortSerialGps *port,
- const gchar *trace,
+trace_received (MMPortSerialGps *port,
+ const gchar *trace,
MMIfaceModemLocation *self)
{
- /* Helper to debug GPS location related issues. Don't depend on a real GPS
- * fix for debugging, just use some random values to update */
-#if 0
- if (g_str_has_prefix (trace, "$GPGGA")) {
- GString *str;
- GDateTime *now;
-
- now = g_date_time_new_now_utc ();
- str = g_string_new ("");
- g_string_append_printf (str,
- "$GPGGA,%02u%02u%02u,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47",
- g_date_time_get_hour (now),
- g_date_time_get_minute (now),
- g_date_time_get_second (now));
- mm_iface_modem_location_gps_update (self, str->str);
- g_string_free (str, TRUE);
- g_date_time_unref (now);
- return;
- }
-#endif
-
mm_iface_modem_location_gps_update (self, trace);
}
@@ -761,6 +702,9 @@ mm_broadband_modem_hso_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (AT) and HSO bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -788,8 +732,15 @@ mm_broadband_modem_hso_init (MMBroadbandModemHso *self)
}
static void
+shared_option_init (MMSharedOption *iface)
+{
+}
+
+static void
iface_modem_init (MMIfaceModem *iface)
{
+ iface->create_sim = mm_shared_option_create_sim;
+ iface->create_sim_finish = mm_shared_option_create_sim_finish;
iface->create_bearer = modem_create_bearer;
iface->create_bearer_finish = modem_create_bearer_finish;
iface->load_unlock_retries = load_unlock_retries;
diff --git a/plugins/option/mm-broadband-modem-option.c b/plugins/option/mm-broadband-modem-option.c
index cf02ba30..5919364d 100644
--- a/plugins/option/mm-broadband-modem-option.c
+++ b/plugins/option/mm-broadband-modem-option.c
@@ -25,13 +25,15 @@
#include "ModemManager.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
#include "mm-base-modem-at.h"
#include "mm-broadband-modem-option.h"
+#include "mm-shared-option.h"
+static void shared_option_init (MMSharedOption *iface);
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -39,6 +41,7 @@ static MMIfaceModem *iface_modem_parent;
static MMIfaceModem3gpp *iface_modem_3gpp_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemOption, mm_broadband_modem_option, MM_TYPE_BROADBAND_MODEM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_OPTION, shared_option_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init))
@@ -65,16 +68,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -84,9 +84,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -115,13 +114,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -133,10 +131,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -222,24 +217,24 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-allowed_mode_update_ready (MMBroadbandModemOption *self,
+allowed_mode_update_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ mm_base_modem_at_command_finish (self, res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -249,14 +244,11 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint option_mode = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (allowed == MM_MODEM_MODE_2G)
option_mode = 0;
@@ -278,18 +270,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -300,7 +290,7 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -316,24 +306,13 @@ typedef enum {
} AccessTechnologiesStep;
typedef struct {
- MMBroadbandModemOption *self;
- GSimpleAsyncResult *result;
MMModemAccessTechnology access_technology;
gboolean check_2g;
gboolean check_3g;
AccessTechnologiesStep step;
} AccessTechnologiesContext;
-static void load_access_technologies_step (AccessTechnologiesContext *ctx);
-
-static void
-access_technologies_context_complete_and_free (AccessTechnologiesContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
+static void load_access_technologies_step (GTask *task);
static gboolean
load_access_technologies_finish (MMIfaceModem *self,
@@ -342,12 +321,17 @@ load_access_technologies_finish (MMIfaceModem *self,
guint *mask,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
/* We are reporting ALL 3GPP access technologies here */
- *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ *access_technologies = (MMModemAccessTechnology) value;
*mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
return TRUE;
}
@@ -407,10 +391,13 @@ parse_ossys_response (const gchar *response,
static void
ossys_query_ready (MMBaseModem *self,
GAsyncResult *res,
- AccessTechnologiesContext *ctx)
+ GTask *task)
{
+ AccessTechnologiesContext *ctx;
const gchar *response;
+ ctx = g_task_get_task_data (task);
+
/* If for some reason the OSSYS request failed, still try to check
* explicit 2G/3G mode with OCTI and OWCTI; maybe we'll get something.
*/
@@ -429,7 +416,7 @@ ossys_query_ready (MMBaseModem *self,
/* Go on to next step */
ctx->step++;
- load_access_technologies_step (ctx);
+ load_access_technologies_step (task);
}
static gboolean
@@ -487,11 +474,14 @@ parse_octi_response (const gchar *response,
static void
octi_query_ready (MMBaseModem *self,
GAsyncResult *res,
- AccessTechnologiesContext *ctx)
+ GTask *task)
{
+ AccessTechnologiesContext *ctx;
MMModemAccessTechnology octi = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
const gchar *response;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (self, res, NULL);
if (response &&
parse_octi_response (response, &octi)) {
@@ -504,7 +494,7 @@ octi_query_ready (MMBaseModem *self,
/* Go on to next step */
ctx->step++;
- load_access_technologies_step (ctx);
+ load_access_technologies_step (task);
}
static gboolean
@@ -544,11 +534,14 @@ parse_owcti_response (const gchar *response,
static void
owcti_query_ready (MMBaseModem *self,
GAsyncResult *res,
- AccessTechnologiesContext *ctx)
+ GTask *task)
{
+ AccessTechnologiesContext *ctx;
MMModemAccessTechnology owcti = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
const gchar *response;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (self, res, NULL);
if (response &&
parse_owcti_response (response, &owcti)) {
@@ -557,59 +550,66 @@ owcti_query_ready (MMBaseModem *self,
/* Go on to next step */
ctx->step++;
- load_access_technologies_step (ctx);
+ load_access_technologies_step (task);
}
static void
-load_access_technologies_step (AccessTechnologiesContext *ctx)
+load_access_technologies_step (GTask *task)
{
+ MMBroadbandModemOption *self;
+ AccessTechnologiesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case ACCESS_TECHNOLOGIES_STEP_FIRST:
- /* Go on to next step */
ctx->step++;
+ /* fall through */
case ACCESS_TECHNOLOGIES_STEP_OSSYS:
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"_OSSYS?",
3,
FALSE,
(GAsyncReadyCallback)ossys_query_ready,
- ctx);
+ task);
break;
case ACCESS_TECHNOLOGIES_STEP_OCTI:
if (ctx->check_2g) {
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"_OCTI?",
3,
FALSE,
(GAsyncReadyCallback)octi_query_ready,
- ctx);
+ task);
return;
}
- /* Go on to next step */
ctx->step++;
+ /* fall through */
case ACCESS_TECHNOLOGIES_STEP_OWCTI:
if (ctx->check_3g) {
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"_OWCTI?",
3,
FALSE,
(GAsyncReadyCallback)owcti_query_ready,
- ctx);
+ task);
return;
}
- /* Go on to next step */
ctx->step++;
+ /* fall through */
case ACCESS_TECHNOLOGIES_STEP_LAST:
/* All done, set result and complete */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (ctx->access_technology),
- NULL);
- access_technologies_context_complete_and_free (ctx);
+ g_task_return_int (task, ctx->access_technology);
+ g_object_unref (task);
break;
+
+ default:
+ g_assert_not_reached ();
}
}
@@ -622,19 +622,18 @@ run_access_technology_loading_sequence (MMIfaceModem *self,
gpointer user_data)
{
AccessTechnologiesContext *ctx;
+ GTask *task;
ctx = g_new (AccessTechnologiesContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- run_access_technology_loading_sequence);
ctx->step = first;
ctx->check_2g = check_2g;
ctx->check_3g = check_3g;
ctx->access_technology = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- load_access_technologies_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ load_access_technologies_step (task);
}
static void
@@ -658,46 +657,38 @@ modem_after_power_up_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_power_up_wait_cb (GSimpleAsyncResult *result)
+after_power_up_wait_cb (GTask *task)
{
- MMBroadbandModemOption *option;
-
- option = MM_BROADBAND_MODEM_OPTION (g_async_result_get_source_object (G_ASYNC_RESULT (result)));
+ MMBroadbandModemOption *self;
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete (result);
- g_object_unref (result);
+ self = g_task_get_source_object (task);
+ self->priv->after_power_up_wait_id = 0;
- option->priv->after_power_up_wait_id = 0;
- g_object_unref (option);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
-modem_after_power_up (MMIfaceModem *self,
+modem_after_power_up (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MMBroadbandModemOption *option = MM_BROADBAND_MODEM_OPTION (self);
- GSimpleAsyncResult *result;
+ MMBroadbandModemOption *self = MM_BROADBAND_MODEM_OPTION (_self);
/* Some Option devices return OK on +CFUN=1 right away but need some time
* to finish initialization.
*/
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_power_up);
- g_warn_if_fail (option->priv->after_power_up_wait_id == 0);
- option->priv->after_power_up_wait_id =
+ g_warn_if_fail (self->priv->after_power_up_wait_id == 0);
+ self->priv->after_power_up_wait_id =
g_timeout_add_seconds (10,
(GSourceFunc)after_power_up_wait_cb,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -724,7 +715,6 @@ modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self,
if (comma)
*comma = '\0';
- mm_dbg ("loaded IMEI: %s", imei);
return imei;
}
@@ -733,7 +723,6 @@ modem_3gpp_load_imei (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading (Option) IMEI...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CGSN",
3,
@@ -820,7 +809,7 @@ option_signal_changed (MMPortSerialAt *port,
MMBroadbandModemOption *self)
{
gchar *str;
- gint quality = 0;
+ guint quality = 0;
str = g_match_info_fetch (match_info, 1);
if (str) {
@@ -833,10 +822,10 @@ option_signal_changed (MMPortSerialAt *port,
quality = 0;
} else {
/* Normalize the quality */
- quality = CLAMP (quality, 0, 31) * 100 / 31;
+ quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31;
}
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), (guint)quality);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -850,7 +839,7 @@ set_unsolicited_events_handlers (MMBroadbandModemOption *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -896,26 +885,25 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_OPTION (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -923,33 +911,26 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
-
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -957,13 +938,6 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
-
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_OPTION (self), FALSE);
@@ -971,7 +945,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -982,23 +956,23 @@ modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
own_enable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
@@ -1012,14 +986,13 @@ static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
/* Our own enable now */
@@ -1031,7 +1004,7 @@ parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -1039,18 +1012,11 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
-
/* Chain up parent's enable */
iface_modem_3gpp_parent->enable_unsolicited_events (
self,
(GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1061,7 +1027,7 @@ modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static const MMBaseModemAtCommand unsolicited_disable_sequence[] = {
@@ -1075,30 +1041,29 @@ static const MMBaseModemAtCommand unsolicited_disable_sequence[] = {
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
own_disable_unsolicited_events_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1106,7 +1071,7 @@ own_disable_unsolicited_events_ready (MMBaseModem *self,
iface_modem_3gpp_parent->disable_unsolicited_events (
MM_IFACE_MODEM_3GPP (self),
(GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ task);
}
static void
@@ -1114,13 +1079,6 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
-
/* Our own disable first */
mm_base_modem_at_sequence_full (
MM_BASE_MODEM (self),
@@ -1130,7 +1088,7 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1181,6 +1139,9 @@ mm_broadband_modem_option_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS, ignored,
NULL);
}
@@ -1222,10 +1183,17 @@ mm_broadband_modem_option_init (MMBroadbandModemOption *self)
}
static void
+shared_option_init (MMSharedOption *iface)
+{
+}
+
+static void
iface_modem_init (MMIfaceModem *iface)
{
iface_modem_parent = g_type_interface_peek_parent (iface);
+ iface->create_sim = mm_shared_option_create_sim;
+ iface->create_sim_finish = mm_shared_option_create_sim_finish;
iface->modem_after_power_up = modem_after_power_up;
iface->modem_after_power_up_finish = modem_after_power_up_finish;
iface->load_access_technologies = load_access_technologies;
diff --git a/plugins/option/mm-plugin-hso.c b/plugins/option/mm-plugin-hso.c
index 94400c15..dbc41c11 100644
--- a/plugins/option/mm-plugin-hso.c
+++ b/plugins/option/mm-plugin-hso.c
@@ -24,12 +24,12 @@
#include "mm-private-boxed-types.h"
#include "mm-plugin-hso.h"
#include "mm-broadband-modem-hso.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
G_DEFINE_TYPE (MMPluginHso, mm_plugin_hso, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
@@ -46,7 +46,7 @@ hso_custom_init_finish (MMPortProbe *probe,
GAsyncResult *result,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
@@ -56,13 +56,13 @@ hso_custom_init (MMPortProbe *probe,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GUdevDevice *udev_port;
- GSimpleAsyncResult *result;
+ MMKernelDevice *kernel_port;
+ GTask *task;
const gchar *subsys, *sysfs_path;
subsys = mm_port_probe_get_port_subsys (probe);
- udev_port = mm_port_probe_peek_port (probe);
- sysfs_path = g_udev_device_get_sysfs_path (udev_port);
+ kernel_port = mm_port_probe_peek_port (probe);
+ sysfs_path = mm_kernel_device_get_sysfs_path (kernel_port);
if (g_str_equal (subsys, "tty")) {
gchar *hsotype_path;
@@ -70,7 +70,7 @@ hso_custom_init (MMPortProbe *probe,
hsotype_path = g_build_filename (sysfs_path, "hsotype", NULL);
if (g_file_get_contents (hsotype_path, &contents, NULL, NULL)) {
- mm_dbg ("HSO port type %s: %s", hsotype_path, contents);
+ mm_obj_dbg (probe, "HSO port type %s: %s", hsotype_path, contents);
if (g_str_has_prefix (contents, "Control")) {
g_object_set_data (G_OBJECT (probe), TAG_HSO_AT_CONTROL, GUINT_TO_POINTER (TRUE));
mm_port_probe_set_result_at (probe, TRUE);
@@ -102,27 +102,23 @@ hso_custom_init (MMPortProbe *probe,
g_free (hsotype_path);
}
- result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- hso_custom_init);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (probe, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_hso_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_hso_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -160,9 +156,7 @@ grab_port (MMPlugin *self,
}
return mm_base_modem_grab_port (modem,
- subsys,
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ mm_port_probe_peek_port (probe),
port_type,
pflags,
error);
@@ -182,12 +176,13 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_HSO,
- MM_PLUGIN_NAME, "Option High-Speed",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_DRIVERS, drivers,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ MM_PLUGIN_SEND_DELAY, (guint64) 0,
NULL));
}
diff --git a/plugins/option/mm-plugin-option.c b/plugins/option/mm-plugin-option.c
index 010e597b..1f72325c 100644
--- a/plugins/option/mm-plugin-option.c
+++ b/plugins/option/mm-plugin-option.c
@@ -27,21 +27,21 @@
G_DEFINE_TYPE (MMPluginOption, mm_plugin_option, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_option_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_option_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -55,7 +55,7 @@ grab_port (MMPlugin *self,
GError **error)
{
MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
- GUdevDevice *port;
+ MMKernelDevice *port;
gint usbif;
/* The Option plugin cannot do anything with non-AT ports */
@@ -73,14 +73,12 @@ grab_port (MMPlugin *self,
* the modem/data port, per mail with Option engineers. Only this port
* will emit responses to dialing commands.
*/
- usbif = g_udev_device_get_property_as_int (port, "ID_USB_INTERFACE_NUM");
+ usbif = mm_kernel_device_get_interface_number (port);
if (usbif == 0)
pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY | MM_PORT_SERIAL_AT_FLAG_PPP;
return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ port,
MM_PORT_TYPE_AT, /* we only allow AT ports here */
pflags,
error);
@@ -99,7 +97,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_OPTION,
- MM_PLUGIN_NAME, "Option",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_DRIVERS, drivers,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
diff --git a/plugins/option/mm-shared-option.c b/plugins/option/mm-shared-option.c
new file mode 100644
index 00000000..a06888a1
--- /dev/null
+++ b/plugins/option/mm-shared-option.c
@@ -0,0 +1,77 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-sim-option.h"
+#include "mm-shared-option.h"
+
+/*****************************************************************************/
+/* Create SIM (Modem inteface) */
+
+MMBaseSim *
+mm_shared_option_create_sim_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return mm_sim_option_new_finish (res, error);
+}
+
+void
+mm_shared_option_create_sim (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_sim_option_new (MM_BASE_MODEM (self),
+ NULL, /* cancellable */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+
+static void
+shared_option_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_option_get_type (void)
+{
+ static GType shared_option_type = 0;
+
+ if (!G_UNLIKELY (shared_option_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedOption), /* class_size */
+ shared_option_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_option_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedOption", &info, 0);
+ g_type_interface_add_prerequisite (shared_option_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return shared_option_type;
+}
diff --git a/plugins/option/mm-shared-option.h b/plugins/option/mm-shared-option.h
new file mode 100644
index 00000000..0d4baf60
--- /dev/null
+++ b/plugins/option/mm-shared-option.h
@@ -0,0 +1,49 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SHARED_OPTION_H
+#define MM_SHARED_OPTION_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+
+#define MM_TYPE_SHARED_OPTION (mm_shared_option_get_type ())
+#define MM_SHARED_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_OPTION, MMSharedOption))
+#define MM_IS_SHARED_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_OPTION))
+#define MM_SHARED_OPTION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_OPTION, MMSharedOption))
+
+typedef struct _MMSharedOption MMSharedOption;
+
+struct _MMSharedOption {
+ GTypeInterface g_iface;
+};
+
+GType mm_shared_option_get_type (void);
+
+void mm_shared_option_create_sim (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseSim *mm_shared_option_create_sim_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_OPTION_H */
diff --git a/plugins/option/mm-shared.c b/plugins/option/mm-shared.c
new file mode 100644
index 00000000..3f89d86a
--- /dev/null
+++ b/plugins/option/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Option)
diff --git a/plugins/option/mm-sim-option.c b/plugins/option/mm-sim-option.c
new file mode 100644
index 00000000..0871c4f2
--- /dev/null
+++ b/plugins/option/mm-sim-option.c
@@ -0,0 +1,84 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-sim-option.h"
+
+G_DEFINE_TYPE (MMSimOption, mm_sim_option, MM_TYPE_BASE_SIM)
+
+/*****************************************************************************/
+
+MMBaseSim *
+mm_sim_option_new_finish (GAsyncResult *res,
+ GError **error)
+{
+ GObject *source;
+ GObject *sim;
+
+ source = g_async_result_get_source_object (res);
+ sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+ g_object_unref (source);
+
+ if (!sim)
+ return NULL;
+
+ /* Only export valid SIMs */
+ mm_base_sim_export (MM_BASE_SIM (sim));
+
+ return MM_BASE_SIM (sim);
+}
+
+void
+mm_sim_option_new (MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (MM_TYPE_SIM_OPTION,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
+ NULL);
+}
+
+static void
+mm_sim_option_init (MMSimOption *self)
+{
+}
+
+static void
+mm_sim_option_class_init (MMSimOptionClass *klass)
+{
+ MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
+
+ /* Skip managing preferred networks, not supported by Option modems */
+ base_sim_class->load_preferred_networks = NULL;
+ base_sim_class->load_preferred_networks_finish = NULL;
+ base_sim_class->set_preferred_networks = NULL;
+ base_sim_class->set_preferred_networks_finish = NULL;
+}
diff --git a/plugins/option/mm-sim-option.h b/plugins/option/mm-sim-option.h
new file mode 100644
index 00000000..c502a397
--- /dev/null
+++ b/plugins/option/mm-sim-option.h
@@ -0,0 +1,51 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SIM_OPTION_H
+#define MM_SIM_OPTION_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-base-sim.h"
+
+#define MM_TYPE_SIM_OPTION (mm_sim_option_get_type ())
+#define MM_SIM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_OPTION, MMSimOption))
+#define MM_SIM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_OPTION, MMSimOptionClass))
+#define MM_IS_SIM_OPTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_OPTION))
+#define MM_IS_SIM_OPTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_OPTION))
+#define MM_SIM_OPTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_OPTION, MMSimOptionClass))
+
+typedef struct _MMSimOption MMSimOption;
+typedef struct _MMSimOptionClass MMSimOptionClass;
+
+struct _MMSimOption {
+ MMBaseSim parent;
+};
+
+struct _MMSimOptionClass {
+ MMBaseSimClass parent;
+};
+
+GType mm_sim_option_get_type (void);
+
+void mm_sim_option_new (MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseSim *mm_sim_option_new_finish (GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SIM_OPTION_H */
diff --git a/plugins/pantech/mm-broadband-modem-pantech.c b/plugins/pantech/mm-broadband-modem-pantech.c
index 65464569..4e8b58e0 100644
--- a/plugins/pantech/mm-broadband-modem-pantech.c
+++ b/plugins/pantech/mm-broadband-modem-pantech.c
@@ -24,7 +24,6 @@
#include "ModemManager.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
-#include "mm-log.h"
#include "mm-errors-types.h"
#include "mm-broadband-modem-pantech.h"
#include "mm-sim-pantech.h"
@@ -114,15 +113,15 @@ modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -130,15 +129,10 @@ modem_after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
-
/* wait so sim pin is done */
- g_timeout_add_seconds (5, (GSourceFunc)after_sim_unlock_wait_cb, result);
+ g_timeout_add_seconds (5,
+ (GSourceFunc)after_sim_unlock_wait_cb,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -156,6 +150,9 @@ mm_broadband_modem_pantech_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/pantech/mm-plugin-pantech.c b/plugins/pantech/mm-plugin-pantech.c
index 788a45d0..6820e383 100644
--- a/plugins/pantech/mm-plugin-pantech.c
+++ b/plugins/pantech/mm-plugin-pantech.c
@@ -19,7 +19,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-plugin-pantech.h"
#include "mm-broadband-modem-pantech.h"
@@ -29,8 +29,8 @@
G_DEFINE_TYPE (MMPluginPantech, mm_plugin_pantech, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom commands for AT probing
@@ -76,7 +76,7 @@ static const MMPortProbeAtCommand custom_at_probe[] = {
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -85,8 +85,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Pantech modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered Pantech modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -94,7 +94,7 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_pantech_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_pantech_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -119,9 +119,7 @@ grab_port (MMPlugin *self,
}
return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ mm_port_probe_peek_port (probe),
ptype,
pflags,
error);
@@ -132,12 +130,12 @@ grab_port (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendor_ids[] = { 0x106c, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_PANTECH,
- MM_PLUGIN_NAME, "Pantech",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/pantech/mm-sim-pantech.c b/plugins/pantech/mm-sim-pantech.c
index 0cf61e97..33414572 100644
--- a/plugins/pantech/mm-sim-pantech.c
+++ b/plugins/pantech/mm-sim-pantech.c
@@ -62,6 +62,7 @@ mm_sim_pantech_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
diff --git a/plugins/qcom-soc/77-mm-qcom-soc.rules b/plugins/qcom-soc/77-mm-qcom-soc.rules
new file mode 100644
index 00000000..6511bf20
--- /dev/null
+++ b/plugins/qcom-soc/77-mm-qcom-soc.rules
@@ -0,0 +1,40 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_qcom_soc_end"
+
+# Process only known wwan, net and rpmsg ports
+SUBSYSTEM=="net", DRIVERS=="bam-dmux", GOTO="mm_qcom_soc_process"
+SUBSYSTEM=="net", DRIVERS=="ipa", GOTO="mm_qcom_soc_process"
+SUBSYSTEM=="wwan", DRIVERS=="qcom-q6v5-mss", GOTO="mm_qcom_soc_process"
+SUBSYSTEM=="rpmsg", DRIVERS=="qcom-q6v5-mss", GOTO="mm_qcom_soc_process"
+GOTO="mm_qcom_soc_end"
+
+LABEL="mm_qcom_soc_process"
+
+# Flag the port as being part of the SoC
+ENV{ID_MM_QCOM_SOC}="1"
+
+#
+# Add a common physdev UID to all ports in the Qualcomm SoC, so that they
+# are all bound together to the same modem object.
+#
+# The MSM8916, MSM8974, .... Qualcomm SoCs use the combination of RPMSG/WWAN
+# based control ports plus BAM-DMUX based network ports.
+#
+ENV{ID_MM_PHYSDEV_UID}="qcom-soc"
+
+# port type hints for the rpmsgexport-ed ports
+SUBSYSTEM=="rpmsg", ATTR{name}=="DATA[0-9]*_CNTL", ENV{ID_MM_PORT_TYPE_QMI}="1"
+SUBSYSTEM=="rpmsg", ATTR{name}=="DATA[0-9]", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# ignore every other port without explicit hints
+SUBSYSTEM=="rpmsg", ENV{ID_MM_PORT_TYPE_QMI}!="1", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}!="1", ENV{ID_MM_PORT_IGNORE}="1"
+
+# explicitly ignore ports intended for USB tethering (DATA40, DATA40_CNTL)
+SUBSYSTEM=="rpmsg", ATTR{name}=="DATA40*", ENV{ID_MM_PORT_IGNORE}="1"
+KERNEL=="rmnet_usb[0-9]*", ENV{ID_MM_PORT_IGNORE}="1"
+
+# flag all rpmsg ports under this plugin as candidate
+KERNEL=="rpmsg[0-9]*", SUBSYSTEM=="rpmsg", ENV{ID_MM_CANDIDATE}="1"
+
+LABEL="mm_qcom_soc_end"
diff --git a/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c b/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c
new file mode 100644
index 00000000..7a9a71c1
--- /dev/null
+++ b/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.c
@@ -0,0 +1,181 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log.h"
+#include "mm-iface-modem.h"
+#include "mm-broadband-modem-qmi-qcom-soc.h"
+
+G_DEFINE_TYPE (MMBroadbandModemQmiQcomSoc, mm_broadband_modem_qmi_qcom_soc, MM_TYPE_BROADBAND_MODEM_QMI)
+
+/*****************************************************************************/
+
+static const QmiSioPort sio_port_per_port_number[] = {
+ QMI_SIO_PORT_A2_MUX_RMNET0,
+ QMI_SIO_PORT_A2_MUX_RMNET1,
+ QMI_SIO_PORT_A2_MUX_RMNET2,
+ QMI_SIO_PORT_A2_MUX_RMNET3,
+ QMI_SIO_PORT_A2_MUX_RMNET4,
+ QMI_SIO_PORT_A2_MUX_RMNET5,
+ QMI_SIO_PORT_A2_MUX_RMNET6,
+ QMI_SIO_PORT_A2_MUX_RMNET7
+};
+
+static MMPortQmi *
+peek_port_qmi_for_data_bam_dmux (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error)
+{
+ GList *rpmsg_qmi_ports;
+ MMPortQmi *found = NULL;
+ MMKernelDevice *net_port;
+ gint net_port_number;
+
+ net_port = mm_port_peek_kernel_device (data);
+
+ /* The dev_port notified by the bam-dmux driver indicates which SIO port we should be using */
+ net_port_number = mm_kernel_device_get_attribute_as_int (net_port, "dev_port");
+ if (net_port_number < 0 || net_port_number >= (gint) G_N_ELEMENTS (sio_port_per_port_number)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Couldn't find SIO port number for 'net/%s'",
+ mm_port_get_device (data));
+ return NULL;
+ }
+
+ /* Find one QMI port, we don't care which one */
+ rpmsg_qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_UNKNOWN,
+ MM_PORT_TYPE_QMI);
+ if (!rpmsg_qmi_ports) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Couldn't find any QMI port for 'net/%s'",
+ mm_port_get_device (data));
+ return NULL;
+ }
+
+ /* Set outputs */
+ if (out_sio_port)
+ *out_sio_port = sio_port_per_port_number[net_port_number];
+ found = MM_PORT_QMI (rpmsg_qmi_ports->data);
+
+ g_list_free_full (rpmsg_qmi_ports, g_object_unref);
+
+ return found;
+}
+
+static MMPortQmi *
+peek_port_qmi_for_data_ipa (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error)
+{
+ MMPortQmi *found = NULL;
+
+ /* when using IPA, we have a master network interface that will be multiplexed
+ * to create link interfaces. We can assume any of the available QMI ports is
+ * able to manage that. */
+
+ found = mm_broadband_modem_qmi_peek_port_qmi (self);
+
+ if (!found)
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Couldn't find any QMI port for 'net/%s'",
+ mm_port_get_device (data));
+ else if (out_sio_port)
+ *out_sio_port = QMI_SIO_PORT_NONE;
+
+ return found;
+}
+
+static MMPortQmi *
+peek_port_qmi_for_data (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error)
+{
+ MMKernelDevice *net_port;
+ const gchar *net_port_driver;
+
+ g_assert (MM_IS_BROADBAND_MODEM_QMI (self));
+ g_assert (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET);
+
+ net_port = mm_port_peek_kernel_device (data);
+ net_port_driver = mm_kernel_device_get_driver (net_port);
+
+ if (g_strcmp0 (net_port_driver, "ipa") == 0)
+ return peek_port_qmi_for_data_ipa (self, data, out_sio_port, error);
+
+ if (g_strcmp0 (net_port_driver, "bam-dmux") == 0)
+ return peek_port_qmi_for_data_bam_dmux (self, data, out_sio_port, error);
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unsupported QMI kernel driver for 'net/%s': %s",
+ mm_port_get_device (data),
+ net_port_driver);
+ return NULL;
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemQmiQcomSoc *
+mm_broadband_modem_qmi_qcom_soc_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* QMI bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_qmi_qcom_soc_init (MMBroadbandModemQmiQcomSoc *self)
+{
+}
+
+static void
+mm_broadband_modem_qmi_qcom_soc_class_init (MMBroadbandModemQmiQcomSocClass *klass)
+{
+ MMBroadbandModemQmiClass *broadband_modem_qmi_class = MM_BROADBAND_MODEM_QMI_CLASS (klass);
+
+ broadband_modem_qmi_class->peek_port_qmi_for_data = peek_port_qmi_for_data;
+}
diff --git a/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.h b/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.h
new file mode 100644
index 00000000..92c37beb
--- /dev/null
+++ b/plugins/qcom-soc/mm-broadband-modem-qmi-qcom-soc.h
@@ -0,0 +1,49 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_QMI_QCOM_SOC_H
+#define MM_BROADBAND_MODEM_QMI_QCOM_SOC_H
+
+#include "mm-broadband-modem-qmi.h"
+
+#define MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC (mm_broadband_modem_qmi_qcom_soc_get_type ())
+#define MM_BROADBAND_MODEM_QMI_QCOM_SOC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MMBroadbandModemQmiQcomSoc))
+#define MM_BROADBAND_MODEM_QMI_QCOM_SOC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MMBroadbandModemQmiQcomSocClass))
+#define MM_IS_BROADBAND_MODEM_QMI_QCOM_SOC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC))
+#define MM_IS_BROADBAND_MODEM_QMI_QCOM_SOC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC))
+#define MM_BROADBAND_MODEM_QMI_QCOM_SOC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QCOM_SOC, MMBroadbandModemQmiQcomSocClass))
+
+typedef struct _MMBroadbandModemQmiQcomSoc MMBroadbandModemQmiQcomSoc;
+typedef struct _MMBroadbandModemQmiQcomSocClass MMBroadbandModemQmiQcomSocClass;
+typedef struct _MMBroadbandModemQmiQcomSocPrivate MMBroadbandModemQmiQcomSocPrivate;
+
+struct _MMBroadbandModemQmiQcomSoc {
+ MMBroadbandModemQmi parent;
+ MMBroadbandModemQmiQcomSocPrivate *priv;
+};
+
+struct _MMBroadbandModemQmiQcomSocClass{
+ MMBroadbandModemQmiClass parent;
+};
+
+GType mm_broadband_modem_qmi_qcom_soc_get_type (void);
+
+MMBroadbandModemQmiQcomSoc *mm_broadband_modem_qmi_qcom_soc_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_QMI_QCOM_SOC_H */
diff --git a/plugins/qcom-soc/mm-plugin-qcom-soc.c b/plugins/qcom-soc/mm-plugin-qcom-soc.c
new file mode 100644
index 00000000..58743a8d
--- /dev/null
+++ b/plugins/qcom-soc/mm-plugin-qcom-soc.c
@@ -0,0 +1,97 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <time.h>
+
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-qcom-soc.h"
+#include "mm-broadband-modem-qmi-qcom-soc.h"
+#include "mm-log-object.h"
+
+G_DEFINE_TYPE (MMPluginQcomSoc, mm_plugin_qcom_soc, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ if (!mm_port_probe_list_has_qmi_port (probes)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported device: at least a QMI port is required");
+ return NULL;
+ }
+
+ mm_obj_dbg (self, "Qualcomm SoC modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_qcom_soc_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "wwan", "rpmsg", "net", "qrtr", NULL };
+ static const gchar *udev_tags[] = {
+ "ID_MM_QCOM_SOC",
+ NULL
+ };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_QCOM_SOC,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags,
+ NULL));
+}
+
+static void
+mm_plugin_qcom_soc_init (MMPluginQcomSoc *self)
+{
+}
+
+static void
+mm_plugin_qcom_soc_class_init (MMPluginQcomSocClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/qcom-soc/mm-plugin-qcom-soc.h b/plugins/qcom-soc/mm-plugin-qcom-soc.h
new file mode 100644
index 00000000..54da154f
--- /dev/null
+++ b/plugins/qcom-soc/mm-plugin-qcom-soc.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_QCOM_SOC_H
+#define MM_PLUGIN_QCOM_SOC_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_QCOM_SOC (mm_plugin_qcom_soc_get_type ())
+#define MM_PLUGIN_QCOM_SOC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_QCOM_SOC, MMPluginQcomSoc))
+#define MM_PLUGIN_QCOM_SOC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_QCOM_SOC, MMPluginQcomSocClass))
+#define MM_IS_PLUGIN_QCOM_SOC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_QCOM_SOC))
+#define MM_IS_PLUGIN_QCOM_SOC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_QCOM_SOC))
+#define MM_PLUGIN_QCOM_SOC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_QCOM_SOC, MMPluginQcomSocClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginQcomSoc;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginQcomSocClass;
+
+GType mm_plugin_qcom_soc_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_QCOM_SOC_H */
diff --git a/plugins/quectel/77-mm-quectel-port-types.rules b/plugins/quectel/77-mm-quectel-port-types.rules
new file mode 100644
index 00000000..ce494169
--- /dev/null
+++ b/plugins/quectel/77-mm-quectel-port-types.rules
@@ -0,0 +1,70 @@
+# do not edit this file, it will be overwritten on update
+ACTION!="add|change|move|bind", GOTO="mm_quectel_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2c7c", GOTO="mm_quectel_port_types"
+GOTO="mm_quectel_port_types_end"
+
+LABEL="mm_quectel_port_types"
+
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Quectel EG06
+# ttyUSB0 (if #0): QCDM/DIAG port
+# ttyUSB1 (if #1): GPS data port
+# ttyUSB2 (if #2): AT primary port
+# ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0306", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# Quectel EG91
+# ttyUSB0 (if #0): QCDM/DIAG port
+# ttyUSB1 (if #1): GPS data port
+# ttyUSB2 (if #2): AT primary port
+# ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0191", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# Quectel EG95
+# ttyUSB0 (if #0): QCDM/DIAG port
+# ttyUSB1 (if #1): GPS data port
+# ttyUSB2 (if #2): AT primary port
+# ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0195", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# Quectel BG96
+# ttyUSB0 (if #0): QCDM/DIAG port
+# ttyUSB1 (if #1): GPS data port
+# ttyUSB2 (if #2): AT primary port
+# ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0296", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# Quectel EC25/EG25
+# ttyUSB0 (if #0): QCDM/DIAG port
+# ttyUSB1 (if #1): GPS data port
+# ttyUSB2 (if #2): AT primary port
+# ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# Quectel RM500
+# ttyUSB0 (if #0): QCDM/DIAG port
+# ttyUSB1 (if #1): GPS data port
+# ttyUSB2 (if #2): AT primary port
+# ttyUSB3 (if #3): AT secondary port
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="2c7c", ATTRS{idProduct}=="0800", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+LABEL="mm_quectel_port_types_end"
diff --git a/plugins/quectel/mm-broadband-modem-mbim-quectel.c b/plugins/quectel/mm-broadband-modem-mbim-quectel.c
new file mode 100644
index 00000000..2874e54e
--- /dev/null
+++ b/plugins/quectel/mm-broadband-modem-mbim-quectel.c
@@ -0,0 +1,132 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Ivan Mikhanchuk <ivan.mikhanchuk@quectel.com>
+ */
+
+#include <config.h>
+
+#include "mm-base-modem-at.h"
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-time.h"
+#include "mm-shared-quectel.h"
+#include "mm-modem-helpers-quectel.h"
+#include "mm-broadband-modem-mbim-quectel.h"
+
+static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void shared_quectel_init (MMSharedQuectel *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimQuectel, mm_broadband_modem_mbim_quectel, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init))
+
+/*****************************************************************************/
+/* Firmware update settings */
+
+static MMFirmwareUpdateSettings *
+firmware_load_update_settings_finish (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+quectel_get_firmware_version_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMFirmwareUpdateSettings *update_settings;
+ const gchar *version;
+
+ update_settings = mm_firmware_update_settings_new (MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE);
+
+ version = mm_base_modem_at_command_finish (modem, res, NULL);
+ if (version)
+ mm_firmware_update_settings_set_version (update_settings, version);
+ g_task_return_pointer (task, update_settings, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+firmware_load_update_settings (MMIfaceModemFirmware *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+QGMR?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) quectel_get_firmware_version_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemMbimQuectel *
+mm_broadband_modem_mbim_quectel_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* include carrier information */
+ MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, FALSE,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_mbim_quectel_init (MMBroadbandModemMbimQuectel *self)
+{
+}
+
+static void
+iface_modem_firmware_init (MMIfaceModemFirmware *iface)
+{
+ iface->load_update_settings = firmware_load_update_settings;
+ iface->load_update_settings_finish = firmware_load_update_settings_finish;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+ iface->check_support = mm_shared_quectel_time_check_support;
+ iface->check_support_finish = mm_shared_quectel_time_check_support_finish;
+}
+
+static void
+shared_quectel_init (MMSharedQuectel *iface)
+{
+}
+
+static void
+mm_broadband_modem_mbim_quectel_class_init (MMBroadbandModemMbimQuectelClass *klass)
+{
+}
diff --git a/plugins/quectel/mm-broadband-modem-mbim-quectel.h b/plugins/quectel/mm-broadband-modem-mbim-quectel.h
new file mode 100644
index 00000000..0d0c2b95
--- /dev/null
+++ b/plugins/quectel/mm-broadband-modem-mbim-quectel.h
@@ -0,0 +1,48 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Ivan Mikhanchuk <ivan.mikhanchuk@quectel.com>
+ */
+
+#ifndef MM_BROADBAND_MODEM_MBIM_QUECTEL_H
+#define MM_BROADBAND_MODEM_MBIM_QUECTEL_H
+
+#include "mm-broadband-modem-mbim.h"
+
+#define MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL (mm_broadband_modem_mbim_quectel_get_type ())
+#define MM_BROADBAND_MODEM_MBIM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MMBroadbandModemMbimQuectel))
+#define MM_BROADBAND_MODEM_MBIM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MMBroadbandModemMbimQuectelClass))
+#define MM_IS_BROADBAND_MODEM_MBIM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL))
+#define MM_IS_BROADBAND_MODEM_MBIM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL))
+#define MM_BROADBAND_MODEM_MBIM_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_QUECTEL, MMBroadbandModemMbimQuectelClass))
+
+typedef struct _MMBroadbandModemMbimQuectel MMBroadbandModemMbimQuectel;
+typedef struct _MMBroadbandModemMbimQuectelClass MMBroadbandModemMbimQuectelClass;
+
+struct _MMBroadbandModemMbimQuectel {
+ MMBroadbandModemMbim parent;
+};
+
+struct _MMBroadbandModemMbimQuectelClass{
+ MMBroadbandModemMbimClass parent;
+};
+
+GType mm_broadband_modem_mbim_quectel_get_type (void);
+
+MMBroadbandModemMbimQuectel *mm_broadband_modem_mbim_quectel_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_MBIM_QUECTEL_H */
diff --git a/plugins/quectel/mm-broadband-modem-qmi-quectel.c b/plugins/quectel/mm-broadband-modem-qmi-quectel.c
new file mode 100644
index 00000000..8cb290c5
--- /dev/null
+++ b/plugins/quectel/mm-broadband-modem-qmi-quectel.c
@@ -0,0 +1,136 @@
+/* -*- 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) 2018-2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include "mm-broadband-modem-qmi-quectel.h"
+#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-time.h"
+#include "mm-shared-quectel.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void shared_quectel_init (MMSharedQuectel *iface);
+
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiQuectel, mm_broadband_modem_qmi_quectel, MM_TYPE_BROADBAND_MODEM_QMI, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init))
+
+/*****************************************************************************/
+
+MMBroadbandModemQmiQuectel *
+mm_broadband_modem_qmi_quectel_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* QMI bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_qmi_quectel_init (MMBroadbandModemQmiQuectel *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->setup_sim_hot_swap = mm_shared_quectel_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = mm_shared_quectel_setup_sim_hot_swap_finish;
+}
+
+static MMIfaceModem *
+peek_parent_modem_interface (MMSharedQuectel *self)
+{
+ return iface_modem_parent;
+}
+
+static MMBroadbandModemClass *
+peek_parent_broadband_modem_class (MMSharedQuectel *self)
+{
+ return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_quectel_parent_class);
+}
+
+static void
+iface_modem_firmware_init (MMIfaceModemFirmware *iface)
+{
+ iface->load_update_settings = mm_shared_quectel_firmware_load_update_settings;
+ iface->load_update_settings_finish = mm_shared_quectel_firmware_load_update_settings_finish;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_quectel_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_quectel_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_quectel_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_quectel_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_quectel_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_quectel_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_modem_location_interface (MMSharedQuectel *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+ iface->check_support = mm_shared_quectel_time_check_support;
+ iface->check_support_finish = mm_shared_quectel_time_check_support_finish;
+}
+
+static void
+shared_quectel_init (MMSharedQuectel *iface)
+{
+ iface->peek_parent_modem_interface = peek_parent_modem_interface;
+ iface->peek_parent_modem_location_interface = peek_parent_modem_location_interface;
+ iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
+}
+
+static void
+mm_broadband_modem_qmi_quectel_class_init (MMBroadbandModemQmiQuectelClass *klass)
+{
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ broadband_modem_class->setup_ports = mm_shared_quectel_setup_ports;
+}
diff --git a/plugins/quectel/mm-broadband-modem-qmi-quectel.h b/plugins/quectel/mm-broadband-modem-qmi-quectel.h
new file mode 100644
index 00000000..f1580f0e
--- /dev/null
+++ b/plugins/quectel/mm-broadband-modem-qmi-quectel.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_QMI_QUECTEL_H
+#define MM_BROADBAND_MODEM_QMI_QUECTEL_H
+
+#include "mm-broadband-modem-qmi.h"
+
+#define MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL (mm_broadband_modem_qmi_quectel_get_type ())
+#define MM_BROADBAND_MODEM_QMI_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MMBroadbandModemQmiQuectel))
+#define MM_BROADBAND_MODEM_QMI_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MMBroadbandModemQmiQuectelClass))
+#define MM_IS_BROADBAND_MODEM_QMI_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL))
+#define MM_IS_BROADBAND_MODEM_QMI_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL))
+#define MM_BROADBAND_MODEM_QMI_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_QUECTEL, MMBroadbandModemQmiQuectelClass))
+
+typedef struct _MMBroadbandModemQmiQuectel MMBroadbandModemQmiQuectel;
+typedef struct _MMBroadbandModemQmiQuectelClass MMBroadbandModemQmiQuectelClass;
+
+struct _MMBroadbandModemQmiQuectel {
+ MMBroadbandModemQmi parent;
+};
+
+struct _MMBroadbandModemQmiQuectelClass{
+ MMBroadbandModemQmiClass parent;
+};
+
+GType mm_broadband_modem_qmi_quectel_get_type (void);
+
+MMBroadbandModemQmiQuectel *mm_broadband_modem_qmi_quectel_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_QMI_QUECTEL_H */
diff --git a/plugins/quectel/mm-broadband-modem-quectel.c b/plugins/quectel/mm-broadband-modem-quectel.c
new file mode 100644
index 00000000..b75a12ff
--- /dev/null
+++ b/plugins/quectel/mm-broadband-modem-quectel.c
@@ -0,0 +1,136 @@
+/* -*- 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) 2018-2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include "mm-broadband-modem-quectel.h"
+#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-time.h"
+#include "mm-shared-quectel.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_firmware_init (MMIfaceModemFirmware *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_time_init (MMIfaceModemTime *iface);
+static void shared_quectel_init (MMSharedQuectel *iface);
+
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQuectel, mm_broadband_modem_quectel, MM_TYPE_BROADBAND_MODEM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init))
+
+/*****************************************************************************/
+
+MMBroadbandModemQuectel *
+mm_broadband_modem_quectel_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_QUECTEL,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_quectel_init (MMBroadbandModemQuectel *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->setup_sim_hot_swap = mm_shared_quectel_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = mm_shared_quectel_setup_sim_hot_swap_finish;
+}
+
+static MMIfaceModem *
+peek_parent_modem_interface (MMSharedQuectel *self)
+{
+ return iface_modem_parent;
+}
+
+static MMBroadbandModemClass *
+peek_parent_broadband_modem_class (MMSharedQuectel *self)
+{
+ return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_quectel_parent_class);
+}
+
+static void
+iface_modem_firmware_init (MMIfaceModemFirmware *iface)
+{
+ iface->load_update_settings = mm_shared_quectel_firmware_load_update_settings;
+ iface->load_update_settings_finish = mm_shared_quectel_firmware_load_update_settings_finish;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_quectel_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_quectel_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_quectel_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_quectel_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_quectel_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_quectel_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_modem_location_interface (MMSharedQuectel *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_time_init (MMIfaceModemTime *iface)
+{
+ iface->check_support = mm_shared_quectel_time_check_support;
+ iface->check_support_finish = mm_shared_quectel_time_check_support_finish;
+}
+
+static void
+shared_quectel_init (MMSharedQuectel *iface)
+{
+ iface->peek_parent_modem_interface = peek_parent_modem_interface;
+ iface->peek_parent_modem_location_interface = peek_parent_modem_location_interface;
+ iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
+}
+
+static void
+mm_broadband_modem_quectel_class_init (MMBroadbandModemQuectelClass *klass)
+{
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ broadband_modem_class->setup_ports = mm_shared_quectel_setup_ports;
+}
diff --git a/plugins/quectel/mm-broadband-modem-quectel.h b/plugins/quectel/mm-broadband-modem-quectel.h
new file mode 100644
index 00000000..bf4ef7a7
--- /dev/null
+++ b/plugins/quectel/mm-broadband-modem-quectel.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_QUECTEL_H
+#define MM_BROADBAND_MODEM_QUECTEL_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_QUECTEL (mm_broadband_modem_quectel_get_type ())
+#define MM_BROADBAND_MODEM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QUECTEL, MMBroadbandModemQuectel))
+#define MM_BROADBAND_MODEM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QUECTEL, MMBroadbandModemQuectelClass))
+#define MM_IS_BROADBAND_MODEM_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QUECTEL))
+#define MM_IS_BROADBAND_MODEM_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QUECTEL))
+#define MM_BROADBAND_MODEM_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QUECTEL, MMBroadbandModemQuectelClass))
+
+typedef struct _MMBroadbandModemQuectel MMBroadbandModemQuectel;
+typedef struct _MMBroadbandModemQuectelClass MMBroadbandModemQuectelClass;
+
+struct _MMBroadbandModemQuectel {
+ MMBroadbandModem parent;
+};
+
+struct _MMBroadbandModemQuectelClass{
+ MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_quectel_get_type (void);
+
+MMBroadbandModemQuectel *mm_broadband_modem_quectel_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_QUECTEL_H */
diff --git a/plugins/quectel/mm-modem-helpers-quectel.c b/plugins/quectel/mm-modem-helpers-quectel.c
new file mode 100644
index 00000000..262d9794
--- /dev/null
+++ b/plugins/quectel/mm-modem-helpers-quectel.c
@@ -0,0 +1,91 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-quectel.h"
+
+gboolean
+mm_quectel_parse_ctzu_test_response (const gchar *response,
+ gpointer log_object,
+ gboolean *supports_disable,
+ gboolean *supports_enable,
+ gboolean *supports_enable_update_rtc,
+ GError **error)
+{
+ g_auto(GStrv) split = NULL;
+ g_autoptr(GArray) modes = NULL;
+ guint i;
+
+ /*
+ * Response may be:
+ * - +CTZU: (0,1)
+ * - +CTZU: (0,1,3)
+ */
+
+#define N_EXPECTED_GROUPS 1
+
+ split = mm_split_string_groups (mm_strip_tag (response, "+CTZU:"));
+ if (!split) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't split the +CTZU test response in groups");
+ return FALSE;
+ }
+
+ if (g_strv_length (split) != N_EXPECTED_GROUPS) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot parse +CTZU test response: invalid number of groups (%u != %u)",
+ g_strv_length (split), N_EXPECTED_GROUPS);
+ return FALSE;
+ }
+
+ modes = mm_parse_uint_list (split[0], error);
+ if (!modes) {
+ g_prefix_error (error, "Failed to parse integer list in +CTZU test response: ");
+ return FALSE;
+ }
+
+ *supports_disable = FALSE;
+ *supports_enable = FALSE;
+ *supports_enable_update_rtc = FALSE;
+
+ for (i = 0; i < modes->len; i++) {
+ guint mode;
+
+ mode = g_array_index (modes, guint, i);
+ switch (mode) {
+ case 0:
+ *supports_disable = TRUE;
+ break;
+ case 1:
+ *supports_enable = TRUE;
+ break;
+ case 3:
+ *supports_enable_update_rtc = TRUE;
+ break;
+ default:
+ mm_obj_dbg (log_object, "unknown +CTZU mode: %u", mode);
+ break;
+ }
+ }
+
+ return TRUE;
+}
diff --git a/plugins/quectel/mm-modem-helpers-quectel.h b/plugins/quectel/mm-modem-helpers-quectel.h
new file mode 100644
index 00000000..d4ec0eae
--- /dev/null
+++ b/plugins/quectel/mm-modem-helpers-quectel.h
@@ -0,0 +1,32 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_MODEM_HELPERS_QUECTEL_H
+#define MM_MODEM_HELPERS_QUECTEL_H
+
+#include <glib.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+gboolean mm_quectel_parse_ctzu_test_response (const gchar *response,
+ gpointer log_object,
+ gboolean *supports_disable,
+ gboolean *supports_enable,
+ gboolean *supports_enable_update_rtc,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_QUECTEL_H */
diff --git a/plugins/quectel/mm-plugin-quectel.c b/plugins/quectel/mm-plugin-quectel.c
new file mode 100644
index 00000000..45506151
--- /dev/null
+++ b/plugins/quectel/mm-plugin-quectel.c
@@ -0,0 +1,125 @@
+/* -*- 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) 2017-2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-plugin-quectel.h"
+#include "mm-broadband-modem-quectel.h"
+
+#if defined WITH_QMI
+#include "mm-broadband-modem-qmi-quectel.h"
+#endif
+
+#if defined WITH_MBIM
+#include "mm-broadband-modem-mbim.h"
+#include "mm-broadband-modem-mbim-quectel.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginQuectel, mm_plugin_quectel, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Quectel modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_quectel_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ if (vendor == 0x1eac) {
+ mm_obj_dbg (self, "MBIM-powered PCI Quectel modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_quectel_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ } else {
+ mm_obj_dbg (self, "MBIM-powered Quectel modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_quectel_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", "wwan", NULL };
+ static const gchar *vendor_strings[] = { "quectel", NULL };
+ static const guint16 vendor_ids[] = {
+ 0x2c7c, /* usb vid */
+ 0x1eac, /* pci vid */
+ 0 };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_QUECTEL,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_quectel_init (MMPluginQuectel *self)
+{
+}
+
+static void
+mm_plugin_quectel_class_init (MMPluginQuectelClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/quectel/mm-plugin-quectel.h b/plugins/quectel/mm-plugin-quectel.h
new file mode 100644
index 00000000..ec888821
--- /dev/null
+++ b/plugins/quectel/mm-plugin-quectel.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_QUECTEL_H
+#define MM_PLUGIN_QUECTEL_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_QUECTEL (mm_plugin_quectel_get_type ())
+#define MM_PLUGIN_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_QUECTEL, MMPluginQuectel))
+#define MM_PLUGIN_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_QUECTEL, MMPluginQuectelClass))
+#define MM_IS_PLUGIN_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_QUECTEL))
+#define MM_IS_PLUGIN_QUECTEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_QUECTEL))
+#define MM_PLUGIN_QUECTEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_QUECTEL, MMPluginQuectelClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginQuectel;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginQuectelClass;
+
+GType mm_plugin_quectel_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_QUECTEL_H */
diff --git a/plugins/quectel/mm-shared-quectel.c b/plugins/quectel/mm-shared-quectel.c
new file mode 100644
index 00000000..f8ccbbe3
--- /dev/null
+++ b/plugins/quectel/mm-shared-quectel.c
@@ -0,0 +1,810 @@
+/* -*- 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) 2018-2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-shared-quectel.h"
+#include "mm-modem-helpers-quectel.h"
+
+/*****************************************************************************/
+/* Private context */
+
+#define PRIVATE_TAG "shared-quectel-private-tag"
+static GQuark private_quark;
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED,
+} FeatureSupport;
+
+typedef struct {
+ MMBroadbandModemClass *broadband_modem_class_parent;
+ MMIfaceModem *iface_modem_parent;
+ MMIfaceModemLocation *iface_modem_location_parent;
+ MMModemLocationSource provided_sources;
+ MMModemLocationSource enabled_sources;
+ FeatureSupport qgps_supported;
+ GRegex *qgpsurc_regex;
+ GRegex *qlwurc_regex;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_regex_unref (priv->qgpsurc_regex);
+ g_regex_unref (priv->qlwurc_regex);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMSharedQuectel *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+
+ priv->provided_sources = MM_MODEM_LOCATION_SOURCE_NONE;
+ priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE;
+ priv->qgps_supported = FEATURE_SUPPORT_UNKNOWN;
+ priv->qgpsurc_regex = g_regex_new ("\\r\\n\\+QGPSURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ priv->qlwurc_regex = g_regex_new ("\\r\\n\\+QLWURC:.*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class);
+ priv->broadband_modem_class_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self);
+
+ g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_location_interface);
+ priv->iface_modem_location_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_location_interface (self);
+
+ g_assert (MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_interface);
+ priv->iface_modem_parent = MM_SHARED_QUECTEL_GET_INTERFACE (self)->peek_parent_modem_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+ return priv;
+}
+
+/*****************************************************************************/
+/* Setup ports (Broadband modem class) */
+
+void
+mm_shared_quectel_setup_ports (MMBroadbandModem *self)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+ g_assert (priv->broadband_modem_class_parent);
+ g_assert (priv->broadband_modem_class_parent->setup_ports);
+
+ /* Parent setup always first */
+ priv->broadband_modem_class_parent->setup_ports (self);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Enable/disable unsolicited events in given port */
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ /* Ignore +QGPSURC */
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ priv->qgpsurc_regex,
+ NULL, NULL, NULL);
+
+ /* Ignore +QLWURC */
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ priv->qlwurc_regex,
+ NULL, NULL, NULL);
+ }
+}
+
+/*****************************************************************************/
+/* Firmware update settings loading (Firmware interface) */
+
+MMFirmwareUpdateSettings *
+mm_shared_quectel_firmware_load_update_settings_finish (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+qfastboot_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMFirmwareUpdateSettings *update_settings;
+
+ if (!mm_base_modem_at_command_finish (self, res, NULL))
+ update_settings = mm_firmware_update_settings_new (MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE);
+ else {
+ update_settings = mm_firmware_update_settings_new (MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT);
+ mm_firmware_update_settings_set_fastboot_at (update_settings, "AT+QFASTBOOT");
+ }
+
+ g_task_return_pointer (task, update_settings, g_object_unref);
+ g_object_unref (task);
+}
+
+void
+mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT+QFASTBOOT=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)qfastboot_test_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* "+QUSIM: 1" URC is emitted by Quectel modems after the USIM has been
+ * (re)initialized. We register a handler for this URC and perform a check
+ * for SIM swap when it is encountered. The motivation for this is to detect
+ * M2M eUICC profile switches. According to SGP.02 chapter 3.2.1, the eUICC
+ * shall trigger a REFRESH operation with eUICC reset when a new profile is
+ * enabled. The +QUSIM URC appears after the eUICC has restarted and can act
+ * as a trigger for profile switch check. This should basically be handled
+ * the same as a physical SIM swap, so the existing SIM hot swap mechanism
+ * is used.
+ */
+
+static void
+quectel_qusim_check_for_sim_swap_ready (MMIfaceModem *self,
+ GAsyncResult *res)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't check SIM swap: %s", error->message);
+ else
+ mm_obj_dbg (self, "check SIM swap completed");
+}
+
+static void
+quectel_qusim_unsolicited_handler (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMIfaceModem *self)
+{
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap ||
+ !MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish)
+ return;
+
+ mm_obj_dbg (self, "checking SIM swap");
+ MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap (
+ self,
+ NULL,
+ (GAsyncReadyCallback)quectel_qusim_check_for_sim_swap_ready,
+ NULL);
+}
+
+/*****************************************************************************/
+/* Setup SIM hot swap context (Modem interface) */
+
+gboolean
+mm_shared_quectel_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_setup_sim_hot_swap_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ g_autoptr(GError) error = NULL;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+
+ if (!priv->iface_modem_parent->setup_sim_hot_swap_finish (self, res, &error))
+ mm_obj_dbg (self, "additional SIM hot swap detection setup failed: %s", error->message);
+
+ /* The +QUSIM based setup never fails, so we can safely return success here */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_quectel_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ GTask *task;
+ GRegex *pattern;
+ guint i;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ pattern = g_regex_new ("\\+QUSIM:\\s*1\\r\\n", G_REGEX_RAW, 0, NULL);
+ g_assert (pattern);
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (ports[i])
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ pattern,
+ (MMPortSerialAtUnsolicitedMsgFn)quectel_qusim_unsolicited_handler,
+ self,
+ NULL);
+ }
+
+ g_regex_unref (pattern);
+ mm_obj_dbg (self, "+QUSIM detection set up");
+
+ /* Now, if available, setup parent logic */
+ if (priv->iface_modem_parent->setup_sim_hot_swap &&
+ priv->iface_modem_parent->setup_sim_hot_swap_finish) {
+ priv->iface_modem_parent->setup_sim_hot_swap (self,
+ (GAsyncReadyCallback) parent_setup_sim_hot_swap_ready,
+ task);
+ return;
+ }
+
+ /* Otherwise, we're done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* GPS trace received */
+
+static void
+trace_received (MMPortSerialGps *port,
+ const gchar *trace,
+ MMIfaceModemLocation *self)
+{
+ mm_iface_modem_location_gps_update (self, trace);
+}
+
+/*****************************************************************************/
+/* Location capabilities loading (Location interface) */
+
+MMModemLocationSource
+mm_shared_quectel_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+probe_qgps_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQuectel *self;
+ Private *priv;
+ MMModemLocationSource sources;
+
+ self = MM_SHARED_QUECTEL (g_task_get_source_object (task));
+ priv = get_private (self);
+
+ priv->qgps_supported = (!!mm_base_modem_at_command_finish (_self, res, NULL) ?
+ FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED);
+
+ mm_obj_dbg (self, "GPS management with +QGPS is %ssupported",
+ priv->qgps_supported ? "" : "not ");
+
+ /* Recover parent sources */
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* Only flag as provided those sources not already provided by the parent */
+ if (priv->qgps_supported == FEATURE_SUPPORTED) {
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA))
+ priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW))
+ priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW;
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))
+ priv->provided_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
+
+ sources |= priv->provided_sources;
+
+ /* Add handler for the NMEA traces in the GPS data port */
+ mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)),
+ (MMPortSerialGpsTraceFn)trace_received,
+ self,
+ NULL);
+ }
+
+ /* So we're done, complete */
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ MMModemLocationSource sources;
+ GError *error = NULL;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+ sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Now our own check. If we don't have any GPS port, we're done */
+ if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) {
+ mm_obj_dbg (self, "no GPS data port found: no GPS capabilities");
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Store parent supported sources in task data */
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+
+ /* Probe QGPS support */
+ g_assert (priv->qgps_supported == FEATURE_SUPPORT_UNKNOWN);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+QGPS=?",
+ 3,
+ TRUE, /* cached */
+ (GAsyncReadyCallback)probe_qgps_ready,
+ task);
+}
+
+void
+mm_shared_quectel_location_load_capabilities (MMIfaceModemLocation *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (_self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_QUECTEL (_self));
+
+ /* Chain up parent's setup */
+ priv->iface_modem_location_parent->load_capabilities (_self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+/* NOTES:
+ * 1) "+QGPSCFG=\"nmeasrc\",1" will be necessary for getting location data
+ * without the nmea port.
+ * 2) may be necessary to set "+QGPSCFG=\"gpsnmeatype\".
+ * 3) QGPSXTRA=1 is necessary to support XTRA assistance data for
+ * faster GNSS location locks.
+ */
+static const MMBaseModemAtCommand gps_startup[] = {
+ { "+QGPSCFG=\"outport\",\"usbnmea\"", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { "+QGPS=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { "+QGPSXTRA=1", 3, FALSE, mm_base_modem_response_processor_no_result_continue },
+ { NULL }
+};
+
+gboolean
+mm_shared_quectel_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+gps_startup_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+
+ mm_base_modem_at_sequence_finish (self, res, NULL, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* Check if the nmea/raw gps port exists and is available */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ } else {
+ /* GPS port was successfully opened */
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ }
+ } else {
+ /* No need to open GPS port */
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+ if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_quectel_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+ gboolean start_gps = FALSE;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ /* Check if the source is provided by the parent */
+ if (!(priv->provided_sources & source)) {
+ priv->iface_modem_location_parent->enable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ /* Only start GPS engine if not done already */
+ start_gps = ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) &&
+ !(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)));
+
+ if (start_gps) {
+ mm_base_modem_at_sequence (
+ MM_BASE_MODEM (self),
+ gps_startup,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ (GAsyncReadyCallback)gps_startup_ready,
+ task);
+ return;
+ }
+
+ /* If the GPS is already running just return */
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Disable location gathering (Location interface) */
+
+gboolean
+mm_shared_quectel_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+qgps_end_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+disable_location_gathering_parent_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+ if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_quectel_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_QUECTEL (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv->enabled_sources &= ~source;
+
+ /* Pass handling to parent if we don't handle it */
+ if (!(source & priv->provided_sources)) {
+ /* The step to disable location gathering may not exist */
+ if (priv->iface_modem_location_parent->disable_location_gathering &&
+ priv->iface_modem_location_parent->disable_location_gathering_finish) {
+ priv->iface_modem_location_parent->disable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)disable_location_gathering_parent_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Turn off gps on the modem if the source uses gps,
+ * and there are no other gps sources enabled */
+ if ((source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) &&
+ !(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) {
+ /* Close the data port if we don't need it anymore */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) {
+ MMPortSerialGps *gps_port;
+
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+QGPSEND",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)qgps_end_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Check support (Time interface) */
+
+gboolean
+mm_shared_quectel_time_check_support_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+support_cclk_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ /* error never returned */
+ g_task_return_boolean (task, !!mm_base_modem_at_command_finish (self, res, NULL));
+ g_object_unref (task);
+}
+
+static void
+support_cclk_query (GTask *task)
+{
+ MMBaseModem *self;
+
+ self = g_task_get_source_object (task);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CCLK?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)support_cclk_query_ready,
+ task);
+}
+
+static void
+ctzu_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't enable automatic time zone update: %s", error->message);
+
+ support_cclk_query (task);
+}
+
+static void
+ctzu_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ const gchar *response;
+ gboolean supports_disable;
+ gboolean supports_enable;
+ gboolean supports_enable_update_rtc;
+ const gchar *cmd = NULL;
+
+ /* If CTZU isn't supported, run CCLK right away */
+ response = mm_base_modem_at_command_finish (self, res, NULL);
+ if (!response) {
+ support_cclk_query (task);
+ return;
+ }
+
+ if (!mm_quectel_parse_ctzu_test_response (response,
+ self,
+ &supports_disable,
+ &supports_enable,
+ &supports_enable_update_rtc,
+ &error)) {
+ mm_obj_warn (self, "couldn't parse +CTZU test response: %s", error->message);
+ support_cclk_query (task);
+ return;
+ }
+
+ /* Custom time support check because some Quectel modems (e.g. EC25) require
+ * +CTZU=3 in order to have the CCLK? time reported in localtime, instead of
+ * UTC time. */
+ if (supports_enable_update_rtc)
+ cmd = "+CTZU=3";
+ else if (supports_enable)
+ cmd = "+CTZU=1";
+
+ if (!cmd) {
+ mm_obj_warn (self, "unknown +CTZU support");
+ support_cclk_query (task);
+ return;
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)ctzu_set_ready,
+ task);
+}
+
+void
+mm_shared_quectel_time_check_support (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CTZU=?",
+ 3,
+ TRUE, /* cached! */
+ (GAsyncReadyCallback)ctzu_test_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static void
+shared_quectel_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_quectel_get_type (void)
+{
+ static GType shared_quectel_type = 0;
+
+ if (!G_UNLIKELY (shared_quectel_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedQuectel), /* class_size */
+ shared_quectel_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_quectel_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedQuectel", &info, 0);
+ g_type_interface_add_prerequisite (shared_quectel_type, MM_TYPE_IFACE_MODEM_FIRMWARE);
+ }
+
+ return shared_quectel_type;
+}
diff --git a/plugins/quectel/mm-shared-quectel.h b/plugins/quectel/mm-shared-quectel.h
new file mode 100644
index 00000000..f9266c06
--- /dev/null
+++ b/plugins/quectel/mm-shared-quectel.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2018-2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SHARED_QUECTEL_H
+#define MM_SHARED_QUECTEL_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-time.h"
+
+#define MM_TYPE_SHARED_QUECTEL (mm_shared_quectel_get_type ())
+#define MM_SHARED_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_QUECTEL, MMSharedQuectel))
+#define MM_IS_SHARED_QUECTEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_QUECTEL))
+#define MM_SHARED_QUECTEL_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_QUECTEL, MMSharedQuectel))
+
+typedef struct _MMSharedQuectel MMSharedQuectel;
+
+struct _MMSharedQuectel {
+ GTypeInterface g_iface;
+ MMBroadbandModemClass * (* peek_parent_broadband_modem_class) (MMSharedQuectel *self);
+ MMIfaceModem * (* peek_parent_modem_interface) (MMSharedQuectel *self);
+ MMIfaceModemLocation * (* peek_parent_modem_location_interface) (MMSharedQuectel *self);
+};
+
+GType mm_shared_quectel_get_type (void);
+
+void mm_shared_quectel_setup_ports (MMBroadbandModem *self);
+
+void mm_shared_quectel_firmware_load_update_settings (MMIfaceModemFirmware *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+MMFirmwareUpdateSettings *mm_shared_quectel_firmware_load_update_settings_finish (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_quectel_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_shared_quectel_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_quectel_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemLocationSource mm_shared_quectel_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_quectel_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_quectel_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_quectel_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_quectel_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_quectel_time_check_support (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_quectel_time_check_support_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_QUECTEL_H */
diff --git a/plugins/quectel/tests/test-modem-helpers-quectel.c b/plugins/quectel/tests/test-modem-helpers-quectel.c
new file mode 100644
index 00000000..0e2c7420
--- /dev/null
+++ b/plugins/quectel/tests/test-modem-helpers-quectel.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details:
+ *
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+#include <math.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-quectel.h"
+
+/*****************************************************************************/
+/* Test ^CTZU test responses */
+
+typedef struct {
+ const gchar *response;
+ gboolean expect_supports_disable;
+ gboolean expect_supports_enable;
+ gboolean expect_supports_enable_update_rtc;
+} TestCtzuResponse;
+
+static const TestCtzuResponse test_ctzu_response[] = {
+ { "+CTZU: (0,1)", TRUE, TRUE, FALSE },
+ { "+CTZU: (0,1,3)", TRUE, TRUE, TRUE },
+};
+
+static void
+common_test_ctzu (const gchar *response,
+ gboolean expect_supports_disable,
+ gboolean expect_supports_enable,
+ gboolean expect_supports_enable_update_rtc)
+{
+ g_autoptr(GError) error = NULL;
+ gboolean res;
+ gboolean supports_disable = FALSE;
+ gboolean supports_enable = FALSE;
+ gboolean supports_enable_update_rtc = FALSE;
+
+ res = mm_quectel_parse_ctzu_test_response (response,
+ NULL,
+ &supports_disable,
+ &supports_enable,
+ &supports_enable_update_rtc,
+ &error);
+ g_assert_no_error (error);
+ g_assert (res);
+
+ g_assert_cmpuint (expect_supports_disable, ==, supports_disable);
+ g_assert_cmpuint (expect_supports_enable, ==, supports_enable);
+ g_assert_cmpuint (expect_supports_enable_update_rtc, ==, supports_enable_update_rtc);
+}
+
+static void
+test_ctzu (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_ctzu_response); i++)
+ common_test_ctzu (test_ctzu_response[i].response,
+ test_ctzu_response[i].expect_supports_disable,
+ test_ctzu_response[i].expect_supports_enable,
+ test_ctzu_response[i].expect_supports_enable_update_rtc);
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/quectel/ctzu", test_ctzu);
+
+ return g_test_run ();
+}
diff --git a/plugins/samsung/mm-broadband-modem-samsung.c b/plugins/samsung/mm-broadband-modem-samsung.c
index 50ec25c9..1ffc178f 100644
--- a/plugins/samsung/mm-broadband-modem-samsung.c
+++ b/plugins/samsung/mm-broadband-modem-samsung.c
@@ -28,9 +28,8 @@
#include "mm-broadband-modem-samsung.h"
#include "mm-broadband-bearer-icera.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
-G_DEFINE_TYPE (MMBroadbandModemSamsung, mm_broadband_modem_samsung, MM_TYPE_BROADBAND_MODEM_ICERA);
+G_DEFINE_TYPE (MMBroadbandModemSamsung, mm_broadband_modem_samsung, MM_TYPE_BROADBAND_MODEM_ICERA)
/*****************************************************************************/
/* Setup ports (Broadband modem class) */
@@ -48,7 +47,7 @@ setup_ports (MMBroadbandModem *self)
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Configure AT ports */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -73,6 +72,9 @@ mm_broadband_modem_samsung_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (AT) and Icera bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
/* We want to use DHCP always! */
MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, MM_BEARER_IP_METHOD_DHCP,
NULL);
diff --git a/plugins/samsung/mm-plugin-samsung.c b/plugins/samsung/mm-plugin-samsung.c
index 634c625d..18b0b9d3 100644
--- a/plugins/samsung/mm-plugin-samsung.c
+++ b/plugins/samsung/mm-plugin-samsung.c
@@ -27,23 +27,22 @@
#include "mm-plugin-samsung.h"
#include "mm-private-boxed-types.h"
#include "mm-broadband-modem-samsung.h"
-#include "mm-log.h"
G_DEFINE_TYPE (MMPluginSamsung, mm_plugin_samsung, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_samsung_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_samsung_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -61,10 +60,11 @@ mm_plugin_create (void)
{ 0, 0 } };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_SAMSUNG,
- MM_PLUGIN_NAME, "Samsung",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_PRODUCT_IDS, products,
MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_SEND_DELAY, (guint64) 0,
NULL));
}
diff --git a/plugins/sierra/77-mm-sierra.rules b/plugins/sierra/77-mm-sierra.rules
new file mode 100644
index 00000000..68350e8c
--- /dev/null
+++ b/plugins/sierra/77-mm-sierra.rules
@@ -0,0 +1,32 @@
+
+# do not edit this file, it will be overwritten on update
+ACTION!="add|change|move|bind", GOTO="mm_sierra_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1199", GOTO="mm_sierra"
+GOTO="mm_sierra_end"
+
+LABEL="mm_sierra"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Netgear AC341U: enable connection status polling explicitly
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9057", ENV{ID_MM_QMI_CONNECTION_STATUS_POLLING_ENABLE}="1"
+
+# EM7345: disable CPOL based features
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="a001", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
+
+# MC74XX: Add port hints
+# if 03: primary port
+# if 02: raw NMEA port
+# if 00: diag/qcdm port
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9071", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+
+# EM7565: Add port hints
+# if 03: primary port
+# if 02: raw NMEA port
+# if 00: diag/qcdm port
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9091", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9091", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1199", ATTRS{idProduct}=="9091", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+
+LABEL="mm_sierra_end"
diff --git a/plugins/sierra/mm-broadband-bearer-sierra.c b/plugins/sierra/mm-broadband-bearer-sierra.c
index 0d872199..b2c34c7c 100644
--- a/plugins/sierra/mm-broadband-bearer-sierra.c
+++ b/plugins/sierra/mm-broadband-bearer-sierra.c
@@ -29,8 +29,9 @@
#include "mm-base-modem-at.h"
#include "mm-broadband-bearer-sierra.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-sierra.h"
G_DEFINE_TYPE (MMBroadbandBearerSierra, mm_broadband_bearer_sierra, MM_TYPE_BROADBAND_BEARER);
@@ -45,6 +46,121 @@ enum {
};
/*****************************************************************************/
+/* Connection status monitoring */
+
+static MMBearerConnectionStatus
+load_connection_status_finish (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ }
+ return (MMBearerConnectionStatus)value;
+}
+
+static void
+scact_periodic_query_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ GList *pdp_active_list = NULL;
+ GList *l;
+ MMBearerConnectionStatus status = MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ guint cid;
+
+ cid = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (response)
+ pdp_active_list = mm_sierra_parse_scact_read_response (response, &error);
+
+ if (error) {
+ g_assert (!pdp_active_list);
+ g_prefix_error (&error, "Couldn't check current list of active PDP contexts: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (l = pdp_active_list; l; l = g_list_next (l)) {
+ MM3gppPdpContextActive *pdp_active;
+
+ /* We look for the specific CID */
+ pdp_active = (MM3gppPdpContextActive *)(l->data);
+ if (pdp_active->cid == cid) {
+ status = (pdp_active->active ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ break;
+ }
+ }
+ mm_3gpp_pdp_context_active_list_free (pdp_active_list);
+
+ /* PDP context not found? This shouldn't happen, error out */
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "PDP context not found in the known contexts list");
+ else
+ g_task_return_int (task, (gssize) status);
+ g_object_unref (task);
+}
+
+static void
+load_connection_status (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMBaseModem *modem = NULL;
+ MMPortSerialAt *port;
+ gint profile_id;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_object_get (MM_BASE_BEARER (self),
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+
+ /* If CID not defined, error out */
+ profile_id = mm_base_bearer_get_profile_id (self);
+ if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't load connection status: profile id not defined");
+ g_object_unref (task);
+ goto out;
+ }
+ g_task_set_task_data (task, GUINT_TO_POINTER ((guint)profile_id), NULL);
+
+ /* If no control port available, error out */
+ port = mm_base_modem_peek_best_at_port (modem, NULL);
+ if (!port) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't load connection status: no control port available");
+ g_object_unref (task);
+ goto out;
+ }
+
+ mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
+ port,
+ "!SCACT?",
+ 3,
+ FALSE, /* allow cached */
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) scact_periodic_query_ready,
+ task);
+
+out:
+ g_clear_object (&modem);
+}
+
+/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef enum {
@@ -56,27 +172,20 @@ typedef enum {
} Dial3gppStep;
typedef struct {
- MMBroadbandBearerSierra *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
guint cid;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
MMPort *data;
Dial3gppStep step;
} Dial3gppContext;
static void
-dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+dial_3gpp_context_free (Dial3gppContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
if (ctx->data)
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (Dial3gppContext, ctx);
}
@@ -85,103 +194,114 @@ dial_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void dial_3gpp_context_step (Dial3gppContext *ctx);
+static void dial_3gpp_context_step (GTask *task);
static void
parent_dial_3gpp_ready (MMBroadbandBearer *self,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
ctx->data = MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->dial_3gpp_finish (self, res, &error);
if (!ctx->data) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on */
ctx->step++;
- dial_3gpp_context_step (ctx);
+ dial_3gpp_context_step (task);
}
static void
scact_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on */
ctx->step++;
- dial_3gpp_context_step (ctx);
+ dial_3gpp_context_step (task);
}
static void
authenticate_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on */
ctx->step++;
- dial_3gpp_context_step (ctx);
+ dial_3gpp_context_step (task);
}
static void
cgatt_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on */
ctx->step++;
- dial_3gpp_context_step (ctx);
+ dial_3gpp_context_step (task);
}
static void
-dial_3gpp_context_step (Dial3gppContext *ctx)
+dial_3gpp_context_step (GTask *task)
{
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Dial operation has been cancelled");
- dial_3gpp_context_complete_and_free (ctx);
- return;
+ MMBroadbandBearerSierra *self;
+ Dial3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
}
switch (ctx->step) {
case DIAL_3GPP_STEP_FIRST:
- /* Fall down */
ctx->step++;
+ /* fall through */
case DIAL_3GPP_STEP_PS_ATTACH:
mm_base_modem_at_command_full (ctx->modem,
@@ -192,7 +312,7 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)cgatt_ready,
- ctx);
+ task);
return;
case DIAL_3GPP_STEP_AUTHENTICATE:
@@ -202,13 +322,13 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
const gchar *password;
MMBearerAllowedAuth allowed_auth;
- user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
if (!user || !password || allowed_auth == MM_BEARER_ALLOWED_AUTH_NONE) {
- mm_dbg ("Not using authentication");
- if (ctx->self->priv->is_icera)
+ mm_obj_dbg (self, "not using authentication");
+ if (self->priv->is_icera)
command = g_strdup_printf ("%%IPDPCFG=%d,0,0,\"\",\"\"", ctx->cid);
else
command = g_strdup_printf ("$QCPDPP=%d,0", ctx->cid);
@@ -218,32 +338,32 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
guint sierra_auth;
if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
- mm_dbg ("Using default (PAP) authentication method");
- sierra_auth = 1;
- } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
- mm_dbg ("Using PAP authentication method");
- sierra_auth = 1;
+ mm_obj_dbg (self, "using default (CHAP) authentication method");
+ sierra_auth = 2;
} else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) {
- mm_dbg ("Using CHAP authentication method");
+ mm_obj_dbg (self, "using CHAP authentication method");
sierra_auth = 2;
+ } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
+ mm_obj_dbg (self, "using PAP authentication method");
+ sierra_auth = 1;
} else {
gchar *str;
str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot use any of the specified authentication methods (%s)",
str);
g_free (str);
- dial_3gpp_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
quoted_user = mm_port_serial_at_quote_string (user);
quoted_password = mm_port_serial_at_quote_string (password);
- if (ctx->self->priv->is_icera) {
+ if (self->priv->is_icera) {
command = g_strdup_printf ("%%IPDPCFG=%d,0,%u,%s,%s",
ctx->cid,
sierra_auth,
@@ -269,13 +389,13 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)authenticate_ready,
- ctx);
+ task);
g_free (command);
return;
}
- /* Fall down */
ctx->step++;
+ /* fall through */
case DIAL_3GPP_STEP_CONNECT:
/* We need a net or AT data port */
@@ -287,33 +407,36 @@ dial_3gpp_context_step (Dial3gppContext *ctx)
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
- 10,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)scact_ready,
- ctx);
+ task);
g_free (command);
return;
}
/* Chain up parent's dialling if we don't have a net port */
MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->dial_3gpp (
- MM_BROADBAND_BEARER (ctx->self),
+ MM_BROADBAND_BEARER (self),
ctx->modem,
ctx->primary,
ctx->cid,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)parent_dial_3gpp_ready,
- ctx);
+ task);
return;
case DIAL_3GPP_STEP_LAST:
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->data),
- (GDestroyNotify)g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ g_object_ref (ctx->data),
+ g_object_unref);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
}
@@ -327,22 +450,20 @@ dial_3gpp (MMBroadbandBearer *self,
gpointer user_data)
{
Dial3gppContext *ctx;
+ GTask *task;
g_assert (primary != NULL);
ctx = g_slice_new0 (Dial3gppContext);
- ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- dial_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
ctx->step = DIAL_3GPP_STEP_FIRST;
- dial_3gpp_context_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
+
+ dial_3gpp_context_step (task);
}
/*****************************************************************************/
@@ -353,43 +474,44 @@ disconnect_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_disconnect_3gpp_ready (MMBroadbandBearer *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
if (!MM_BROADBAND_BEARER_CLASS (mm_broadband_bearer_sierra_parent_class)->disconnect_3gpp_finish (self, res, &error)) {
- mm_dbg ("Parent disconnection failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "parent disconnection failed (not fatal): %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-disconnect_scact_ready (MMBaseModem *modem,
+disconnect_scact_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearerSierra *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
/* Ignore errors for now */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- mm_dbg ("Disconnection failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "disconnection failed (not fatal): %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -402,14 +524,11 @@ disconnect_3gpp (MMBroadbandBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
g_assert (primary != NULL);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect_3gpp);
+ task = g_task_new (self, NULL, callback, user_data);
if (!MM_IS_PORT_SERIAL_AT (data)) {
gchar *command;
@@ -419,12 +538,12 @@ disconnect_3gpp (MMBroadbandBearer *self,
mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
primary,
command,
- 3,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)disconnect_scact_ready,
- result);
+ task);
g_free (command);
return;
}
@@ -438,7 +557,7 @@ disconnect_3gpp (MMBroadbandBearer *self,
data,
cid,
(GAsyncReadyCallback)parent_disconnect_3gpp_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -533,13 +652,22 @@ mm_broadband_bearer_sierra_init (MMBroadbandBearerSierra *self)
static void
mm_broadband_bearer_sierra_class_init (MMBroadbandBearerSierraClass *klass)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerSierraPrivate));
object_class->set_property = set_property;
object_class->get_property = get_property;
+
+ base_bearer_class->load_connection_status = load_connection_status;
+ base_bearer_class->load_connection_status_finish = load_connection_status_finish;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = load_connection_status;
+ base_bearer_class->reload_connection_status_finish = load_connection_status_finish;
+#endif
+
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
diff --git a/plugins/sierra/mm-broadband-modem-sierra-icera.c b/plugins/sierra/mm-broadband-modem-sierra-icera.c
index bd01cc4d..c7dfcf81 100644
--- a/plugins/sierra/mm-broadband-modem-sierra-icera.c
+++ b/plugins/sierra/mm-broadband-modem-sierra-icera.c
@@ -27,7 +27,7 @@
#include "mm-broadband-modem-sierra-icera.h"
#include "mm-iface-modem.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-common-sierra.h"
#include "mm-broadband-bearer-sierra.h"
@@ -44,31 +44,24 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New Sierra bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_sierra_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_sierra_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
@@ -77,20 +70,17 @@ modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("Creating Sierra bearer...");
+ mm_obj_dbg (self, "creating sierra bearer...");
mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM (self),
properties,
TRUE, /* is_icera */
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_sierra_new_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -120,6 +110,9 @@ mm_broadband_modem_sierra_icera_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Sierra bearer supports both NET and TTY */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/sierra/mm-broadband-modem-sierra.c b/plugins/sierra/mm-broadband-modem-sierra.c
index c0307458..518f8adb 100644
--- a/plugins/sierra/mm-broadband-modem-sierra.c
+++ b/plugins/sierra/mm-broadband-modem-sierra.c
@@ -26,8 +26,9 @@
#include "ModemManager.h"
#include "mm-broadband-modem-sierra.h"
#include "mm-base-modem-at.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
+#include "mm-common-helpers.h"
#include "mm-errors-types.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
@@ -108,7 +109,6 @@ load_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading unlock retries (sierra)...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CPINC?",
3,
@@ -381,10 +381,9 @@ typedef struct {
guint mask;
} AccessTechInfo;
-static void
-access_tech_set_result (GSimpleAsyncResult *simple,
- MMModemAccessTechnology act,
- guint mask)
+static AccessTechInfo *
+access_tech_info_new (MMModemAccessTechnology act,
+ guint mask)
{
AccessTechInfo *info;
@@ -392,7 +391,7 @@ access_tech_set_result (GSimpleAsyncResult *simple,
info->act = act;
info->mask = mask;
- g_simple_async_result_set_op_res_gpointer (simple, info, g_free);
+ return info;
}
static gboolean
@@ -404,27 +403,27 @@ load_access_technologies_finish (MMIfaceModem *self,
{
AccessTechInfo *info;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ info = g_task_propagate_pointer (G_TASK (res), error);
+ if (!info)
return FALSE;
- info = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- g_assert (info);
*access_technologies = info->act;
*mask = info->mask;
+ g_free (info);
return TRUE;
}
static void
access_tech_3gpp_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *response;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (!response)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
const gchar *p;
@@ -435,49 +434,53 @@ access_tech_3gpp_ready (MMBaseModem *self,
act = mm_string_to_access_tech (p + 1);
if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN)
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't parse access technologies result: '%s'",
response);
else
- access_tech_set_result (simple, act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
+ g_task_return_pointer (
+ task,
+ access_tech_info_new (act, MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK),
+ g_free);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-access_tech_cdma_ready (MMIfaceModemCdma *self,
+access_tech_cdma_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *response;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (!response)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
MMModemCdmaRegistrationState cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
MMModemCdmaRegistrationState evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
if (!parse_status (response, &cdma1x_state, &evdo_state, &act))
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't parse access technologies result: '%s'",
response);
else
- access_tech_set_result (simple, act, MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK);
+ g_task_return_pointer (
+ task,
+ access_tech_info_new (act, MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK),
+ g_free);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -485,12 +488,9 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_access_technologies);
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_3gpp (self)) {
mm_base_modem_at_command (MM_BASE_MODEM (self),
@@ -498,7 +498,7 @@ load_access_technologies (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)access_tech_3gpp_ready,
- result);
+ task);
return;
}
@@ -508,7 +508,7 @@ load_access_technologies (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)access_tech_cdma_ready,
- result);
+ task);
return;
}
@@ -523,16 +523,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -542,17 +539,15 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* CDMA-only modems don't support changing modes, default to parent's */
if (!mm_iface_modem_is_3gpp (self)) {
- g_simple_async_result_set_op_res_gpointer (simple, all, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
return;
}
@@ -594,13 +589,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
}
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -612,10 +606,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -635,35 +626,36 @@ load_current_modes_finish (MMIfaceModem *self,
{
LoadCurrentModesResult *result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
*allowed = result->allowed;
*preferred = result->preferred;
+ g_free (result);
return TRUE;
}
static void
selrat_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- LoadCurrentModesResult result;
+ LoadCurrentModesResult *result;
const gchar *response;
GError *error = NULL;
GRegex *r = NULL;
GMatchInfo *match_info = NULL;
- response = mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_full_finish (self, res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ result = g_new0 (LoadCurrentModesResult, 1);
+
/* Example response: !SELRAT: 03, UMTS 3G Preferred */
r = g_regex_new ("!SELRAT:\\s*(\\d+).*$", 0, 0, NULL);
g_assert (r != NULL);
@@ -674,51 +666,51 @@ selrat_query_ready (MMBaseModem *self,
if (mm_get_uint_from_match_info (match_info, 1, &mode) && mode <= 7) {
switch (mode) {
case 0:
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_NONE;
if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
- result.allowed |= MM_MODEM_MODE_4G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed |= MM_MODEM_MODE_4G;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 1:
- result.allowed = MM_MODEM_MODE_3G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_3G;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 2:
- result.allowed = MM_MODEM_MODE_2G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_2G;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 3:
/* in Sierra LTE devices, mode 3 is automatic, including LTE, no preference */
if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ result->preferred = MM_MODEM_MODE_NONE;
} else {
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_3G;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_3G;
}
break;
case 4:
/* in Sierra LTE devices, mode 4 is automatic, including LTE, no preference */
if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ result->preferred = MM_MODEM_MODE_NONE;
} else {
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_2G;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_2G;
}
break;
case 5:
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 6:
- result.allowed = MM_MODEM_MODE_4G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_4G;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 7:
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ result->preferred = MM_MODEM_MODE_NONE;
break;
default:
g_assert_not_reached ();
@@ -735,16 +727,16 @@ selrat_query_ready (MMBaseModem *self,
"Could not parse allowed mode response: Response didn't match: '%s'",
response);
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
- if (error)
- g_simple_async_result_take_error (simple, error);
- else
- g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ if (error) {
+ g_free (result);
+ g_task_return_error (task, error);
+ } else
+ g_task_return_pointer (task, result, g_free);
+
+ g_object_unref (task);
}
static void
@@ -752,35 +744,29 @@ load_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
MMPortSerialAt *primary;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (!mm_iface_modem_is_3gpp (self)) {
/* Cannot do this in CDMA modems */
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot load allowed modes in CDMA modems");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot load allowed modes in CDMA modems");
+ g_object_unref (task);
return;
}
/* Sierra secondary ports don't have full AT command interpreters */
primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
if (!primary || mm_port_get_connected (MM_PORT (primary))) {
- g_simple_async_result_set_error (
- result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CONNECTED,
- "Cannot load allowed modes while connected");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CONNECTED,
+ "Cannot load allowed modes while connected");
+ g_object_unref (task);
return;
}
@@ -792,7 +778,7 @@ load_current_modes (MMIfaceModem *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)selrat_query_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -803,23 +789,23 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
selrat_set_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
- if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error))
+ if (!mm_base_modem_at_command_full_finish (self, res, &error))
/* Let the error be critical. */
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -829,37 +815,31 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
MMPortSerialAt *primary;
gint idx = -1;
gchar *command;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (!mm_iface_modem_is_3gpp (self)) {
/* Cannot do this in CDMA modems */
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot set allowed modes in CDMA modems");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set allowed modes in CDMA modems");
+ g_object_unref (task);
return;
}
/* Sierra secondary ports don't have full AT command interpreters */
primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
if (!primary || mm_port_get_connected (MM_PORT (primary))) {
- g_simple_async_result_set_error (
- result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CONNECTED,
- "Cannot set allowed modes while connected");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CONNECTED,
+ "Cannot set allowed modes while connected");
+ g_object_unref (task);
return;
}
@@ -893,18 +873,17 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
+
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -917,7 +896,7 @@ set_current_modes (MMIfaceModem *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)selrat_set_ready,
- result);
+ task);
g_free (command);
}
@@ -929,15 +908,15 @@ modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
@@ -945,7 +924,7 @@ modem_after_sim_unlock (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
guint timeout = 8;
const gchar **drivers;
guint i;
@@ -961,12 +940,9 @@ modem_after_sim_unlock (MMIfaceModem *self,
timeout = 3;
}
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
+ task = g_task_new (self, NULL, callback, user_data);
- g_timeout_add_seconds (timeout, (GSourceFunc)after_sim_unlock_wait_cb, result);
+ g_timeout_add_seconds (timeout, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
@@ -977,28 +953,24 @@ modem_load_own_numbers_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_own_numbers_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GStrv numbers;
numbers = iface_modem_parent->load_own_numbers_finish (self, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple, numbers, NULL);
+ g_task_return_pointer (task, numbers, (GDestroyNotify)g_strfreev);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
#define MDN_TAG "MDN: "
@@ -1006,7 +978,7 @@ parent_load_own_numbers_ready (MMIfaceModem *self,
static void
own_numbers_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response, *p;
const gchar *numbers[2] = { NULL, NULL };
@@ -1032,15 +1004,14 @@ own_numbers_ready (MMBaseModem *self,
/* MDNs are 10 digits in length */
if (i != 10) {
- mm_warn ("Failed to parse MDN: expected 10 digits, got %d", i);
+ mm_obj_warn (self, "failed to parse MDN: expected 10 digits, got %d", i);
goto fallback;
}
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdupv ((gchar **) numbers),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task,
+ g_strdupv ((gchar **) numbers),
+ (GDestroyNotify)g_strfreev);
+ g_object_unref (task);
return;
fallback:
@@ -1048,7 +1019,7 @@ fallback:
iface_modem_parent->load_own_numbers (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_own_numbers_ready,
- simple);
+ task);
}
static void
@@ -1056,20 +1027,16 @@ modem_load_own_numbers (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- mm_dbg ("loading own numbers (Sierra)...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_own_numbers);
+ task = g_task_new (self, NULL, callback, user_data);
/* 3GPP modems can just run parent's own number loading */
if (mm_iface_modem_is_3gpp (self)) {
iface_modem_parent->load_own_numbers (
self,
(GAsyncReadyCallback)parent_load_own_numbers_ready,
- result);
+ task);
return;
}
@@ -1082,7 +1049,7 @@ modem_load_own_numbers (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)own_numbers_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1093,31 +1060,24 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New Sierra bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_sierra_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_sierra_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+
+ g_object_unref (task);
}
static void
@@ -1126,20 +1086,17 @@ modem_create_bearer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("Creating Sierra bearer...");
+ mm_obj_dbg (self, "creating Sierra bearer...");
mm_broadband_bearer_sierra_new (MM_BROADBAND_MODEM (self),
properties,
FALSE, /* is_icera */
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_sierra_new_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1174,22 +1131,21 @@ modem_power_down_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
modem_power_down_ready (MMBaseModem *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
/* Ignore errors for now; we're not sure if all Sierra CDMA devices support
* at!pcstate or 3GPP devices support +CFUN=4.
*/
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+ mm_base_modem_at_command_finish (self, res, NULL);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -1197,12 +1153,9 @@ modem_power_down (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_power_down);
+ task = g_task_new (self, NULL, callback, user_data);
/* For CDMA modems, run !pcstate */
if (mm_iface_modem_is_cdma_only (self)) {
@@ -1211,7 +1164,7 @@ modem_power_down (MMIfaceModem *self,
5,
FALSE,
(GAsyncReadyCallback)modem_power_down_ready,
- result);
+ task);
return;
}
@@ -1221,7 +1174,7 @@ modem_power_down (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)modem_power_down_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1247,51 +1200,51 @@ setup_registration_checks_finish (MMIfaceModemCdma *self,
{
SetupRegistrationChecksResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step;
*skip_qcdm_hdr_step = results->skip_qcdm_hdr_step;
*skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step;
*skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step;
*skip_detailed_registration_state = results->skip_detailed_registration_state;
+ g_free (results);
return TRUE;
}
static void
parent_setup_registration_checks_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
- SetupRegistrationChecksResults results = { 0 };
+ SetupRegistrationChecksResults *results;
+ results = g_new0 (SetupRegistrationChecksResults, 1);
if (!iface_modem_cdma_parent->setup_registration_checks_finish (self,
res,
- &results.skip_qcdm_call_manager_step,
- &results.skip_qcdm_hdr_step,
- &results.skip_at_cdma_service_status_step,
- &results.skip_at_cdma1x_serving_system_step,
- &results.skip_detailed_registration_state,
+ &results->skip_qcdm_call_manager_step,
+ &results->skip_qcdm_hdr_step,
+ &results->skip_at_cdma_service_status_step,
+ &results->skip_at_cdma1x_serving_system_step,
+ &results->skip_detailed_registration_state,
&error)) {
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+ g_free (results);
} else {
/* Skip +CSS */
- results.skip_at_cdma1x_serving_system_step = TRUE;
+ results->skip_at_cdma1x_serving_system_step = TRUE;
/* Skip +CAD */
- results.skip_at_cdma_service_status_step = TRUE;
+ results->skip_at_cdma_service_status_step = TRUE;
/* Force to always use the detailed registration checks, as we have
* !STATUS for that */
- results.skip_detailed_registration_state = FALSE;
+ results->skip_detailed_registration_state = FALSE;
- g_simple_async_result_set_op_res_gpointer (simple, &results, NULL);
+ g_task_return_pointer (task, results, g_free);
}
-
- /* All done. NOTE: complete NOT in idle! */
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -1299,17 +1252,14 @@ setup_registration_checks (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- setup_registration_checks);
+ task = g_task_new (self, NULL, callback, user_data);
/* Run parent's checks first */
iface_modem_cdma_parent->setup_registration_checks (self,
(GAsyncReadyCallback)parent_setup_registration_checks_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1320,22 +1270,6 @@ typedef struct {
MMModemCdmaRegistrationState detailed_evdo_state;
} DetailedRegistrationStateResults;
-typedef struct {
- MMBroadbandModemSierra *self;
- GSimpleAsyncResult *result;
- DetailedRegistrationStateResults state;
-} DetailedRegistrationStateContext;
-
-static void
-detailed_registration_state_context_complete_and_free (DetailedRegistrationStateContext *ctx)
-{
- /* Always not in idle! we're passing a struct in stack as result */
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
get_detailed_registration_state_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
@@ -1345,42 +1279,36 @@ get_detailed_registration_state_finish (MMIfaceModemCdma *self,
{
DetailedRegistrationStateResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*detailed_cdma1x_state = results->detailed_cdma1x_state;
*detailed_evdo_state = results->detailed_evdo_state;
+ g_free (results);
return TRUE;
}
static void
-status_ready (MMIfaceModemCdma *self,
+status_ready (MMBaseModem *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ DetailedRegistrationStateResults *results;
const gchar *response;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- /* If error, leave superclass' reg state alone if AT!STATUS isn't supported. */
- if (error) {
- g_error_free (error);
-
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
- return;
- }
+ results = g_task_get_task_data (task);
- parse_status (response,
- &(ctx->state.detailed_cdma1x_state),
- &(ctx->state.detailed_evdo_state),
- NULL);
-
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
+ /* If error, leave superclass' reg state alone if AT!STATUS isn't supported. */
+ response = mm_base_modem_at_command_finish (self, res, NULL);
+ if (response)
+ parse_status (response,
+ &(results->detailed_cdma1x_state),
+ &(results->detailed_evdo_state),
+ NULL);
+
+ g_task_return_pointer (task, g_memdup (results, sizeof (*results)), g_free);
+ g_object_unref (task);
}
static void
@@ -1390,24 +1318,307 @@ get_detailed_registration_state (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- DetailedRegistrationStateContext *ctx;
+ DetailedRegistrationStateResults *results;
+ GTask *task;
- /* Setup context */
- ctx = g_new0 (DetailedRegistrationStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_detailed_registration_state);
- ctx->state.detailed_cdma1x_state = cdma1x_state;
- ctx->state.detailed_evdo_state = evdo_state;
+ results = g_new0 (DetailedRegistrationStateResults, 1);
+ results->detailed_cdma1x_state = cdma1x_state;
+ results->detailed_evdo_state = evdo_state;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, results, g_free);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"!STATUS",
3,
FALSE,
(GAsyncReadyCallback)status_ready,
- ctx);
+ task);
+}
+
+/*****************************************************************************/
+/* Automatic activation (CDMA interface) */
+
+typedef enum {
+ CDMA_AUTOMATIC_ACTIVATION_STEP_FIRST,
+ CDMA_AUTOMATIC_ACTIVATION_STEP_UNLOCK,
+ CDMA_AUTOMATIC_ACTIVATION_STEP_CDV,
+ CDMA_AUTOMATIC_ACTIVATION_STEP_CHECK,
+ CDMA_AUTOMATIC_ACTIVATION_STEP_LAST
+} CdmaAutomaticActivationStep;
+
+typedef struct {
+ CdmaAutomaticActivationStep step;
+ gchar *carrier_code;
+} CdmaAutomaticActivationContext;
+
+static void
+cdma_automatic_activation_context_free (CdmaAutomaticActivationContext *ctx)
+{
+ g_free (ctx->carrier_code);
+ g_slice_free (CdmaAutomaticActivationContext, ctx);
+}
+
+static gboolean
+modem_cdma_activate_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void cdma_automatic_activation_step (GTask *task);
+
+static void
+automatic_activation_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ CdmaAutomaticActivationContext *ctx;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ cdma_automatic_activation_step (task);
+}
+
+static void
+cdma_automatic_activation_step (GTask *task)
+{
+ MMBroadbandModemSierra *self;
+ CdmaAutomaticActivationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case CDMA_AUTOMATIC_ACTIVATION_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case CDMA_AUTOMATIC_ACTIVATION_STEP_UNLOCK:
+ mm_obj_info (self, "activation step [1/4]: unlocking device");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "~NAMLCK=000000",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)automatic_activation_command_ready,
+ task);
+ return;
+
+ case CDMA_AUTOMATIC_ACTIVATION_STEP_CDV: {
+ gchar *command;
+
+ mm_obj_info (self, "activation step [2/4]: requesting OTASP");
+ command = g_strdup_printf ("+CDV%s", ctx->carrier_code);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 120,
+ FALSE,
+ (GAsyncReadyCallback)automatic_activation_command_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ case CDMA_AUTOMATIC_ACTIVATION_STEP_CHECK:
+ mm_obj_info (self, "activation step [3/4]: checking activation info");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "~NAMVAL?0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)automatic_activation_command_ready,
+ task);
+ return;
+
+ case CDMA_AUTOMATIC_ACTIVATION_STEP_LAST:
+ mm_obj_info (self, "activation step [4/4]: activation process finished");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_cdma_activate (MMIfaceModemCdma *self,
+ const gchar *carrier_code,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CdmaAutomaticActivationContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Setup context */
+ ctx = g_slice_new0 (CdmaAutomaticActivationContext);
+ ctx->carrier_code = g_strdup (carrier_code);
+ ctx->step = CDMA_AUTOMATIC_ACTIVATION_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_automatic_activation_context_free);
+
+ /* And start it */
+ cdma_automatic_activation_step (task);
+}
+
+/*****************************************************************************/
+/* Manual activation (CDMA interface) */
+
+typedef enum {
+ CDMA_MANUAL_ACTIVATION_STEP_FIRST,
+ CDMA_MANUAL_ACTIVATION_STEP_SPC,
+ CDMA_MANUAL_ACTIVATION_STEP_MDN_MIN,
+ CDMA_MANUAL_ACTIVATION_STEP_OTASP,
+ CDMA_MANUAL_ACTIVATION_STEP_CHECK,
+ CDMA_MANUAL_ACTIVATION_STEP_LAST
+} CdmaManualActivationStep;
+
+typedef struct {
+ CdmaManualActivationStep step;
+ MMCdmaManualActivationProperties *properties;
+} CdmaManualActivationContext;
+
+static gboolean
+modem_cdma_activate_manual_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void cdma_manual_activation_step (GTask *task);
+
+static void
+manual_activation_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ CdmaManualActivationContext *ctx;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ cdma_manual_activation_step (task);
+}
+
+static void
+cdma_manual_activation_step (GTask *task)
+{
+ MMBroadbandModemSierra *self;
+ CdmaManualActivationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case CDMA_MANUAL_ACTIVATION_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case CDMA_MANUAL_ACTIVATION_STEP_SPC: {
+ gchar *command;
+
+ mm_obj_info (self, "activation step [1/5]: unlocking device");
+ command = g_strdup_printf ("~NAMLCK=%s",
+ mm_cdma_manual_activation_properties_get_spc (ctx->properties));
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)manual_activation_command_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ case CDMA_MANUAL_ACTIVATION_STEP_MDN_MIN: {
+ gchar *command;
+
+ mm_obj_info (self, "activation step [2/5]: setting MDN/MIN/SID");
+ command = g_strdup_printf ("~NAMVAL=0,%s,%s,%" G_GUINT16_FORMAT ",65535",
+ mm_cdma_manual_activation_properties_get_mdn (ctx->properties),
+ mm_cdma_manual_activation_properties_get_min (ctx->properties),
+ mm_cdma_manual_activation_properties_get_sid (ctx->properties));
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 120,
+ FALSE,
+ (GAsyncReadyCallback)manual_activation_command_ready,
+ task);
+ g_free (command);
+ return;
+ }
+
+ case CDMA_MANUAL_ACTIVATION_STEP_OTASP:
+ mm_obj_info (self, "activation step [3/5]: requesting OTASP");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "!IOTASTART",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)manual_activation_command_ready,
+ task);
+ return;
+
+ case CDMA_MANUAL_ACTIVATION_STEP_CHECK:
+ mm_obj_info (self, "activation step [4/5]: checking activation info");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "~NAMVAL?0",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)manual_activation_command_ready,
+ task);
+ return;
+
+ case CDMA_MANUAL_ACTIVATION_STEP_LAST:
+ mm_obj_info (self, "activation step [5/5]: activation process finished");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_cdma_activate_manual (MMIfaceModemCdma *self,
+ MMCdmaManualActivationProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CdmaManualActivationContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Setup context */
+ ctx = g_slice_new0 (CdmaManualActivationContext);
+ ctx->properties = g_object_ref (properties);
+ ctx->step = CDMA_MANUAL_ACTIVATION_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_automatic_activation_context_free);
+
+ /* And start it */
+ cdma_manual_activation_step (task);
}
/*****************************************************************************/
@@ -1454,8 +1665,7 @@ parse_time (const gchar *response,
}
}
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
g_regex_unref (r);
return result;
}
@@ -1519,6 +1729,7 @@ modem_time_load_network_time (MMIfaceModemTime *self,
case TIME_METHOD_SYSTIME:
command = "!SYSTIME?";
break;
+ case TIME_METHOD_UNKNOWN:
default:
g_assert_not_reached ();
}
@@ -1544,18 +1755,17 @@ modem_time_check_support_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
- return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
modem_time_check_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GVariant *result;
-
- g_simple_async_result_set_op_res_gboolean (simple, FALSE);
+ gboolean supported = FALSE;
result = mm_base_modem_at_sequence_finish (self, res, NULL, &error);
if (!error && result) {
@@ -1563,24 +1773,26 @@ modem_time_check_ready (MMBaseModem *self,
sierra->priv->time_method = g_variant_get_uint32 (result);
if (sierra->priv->time_method != TIME_METHOD_UNKNOWN)
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ supported = TRUE;
}
g_clear_error (&error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, supported);
+ g_object_unref (task);
}
-static gboolean
-parse_time_reply (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+parse_time_reply (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
+ *result_error = NULL;
+
/* If error, try next command */
if (!error) {
if (strstr (command, "!TIME"))
@@ -1590,11 +1802,13 @@ parse_time_reply (MMBaseModem *self,
}
/* Stop sequence if we get a result, but not on errors */
- return *result ? TRUE : FALSE;
+ return (*result ?
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS :
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE);
}
static const MMBaseModemAtCommand time_check_sequence[] = {
- { "!TIME?", 3, FALSE, parse_time_reply }, /* 3GPP */
+ { "!TIME?", 3, FALSE, parse_time_reply }, /* 3GPP */
{ "!SYSTIME?", 3, FALSE, parse_time_reply }, /* CDMA */
{ NULL }
};
@@ -1604,20 +1818,13 @@ modem_time_check_support (MMIfaceModemTime *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_time_check_support);
-
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
time_check_sequence,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
(GAsyncReadyCallback)modem_time_check_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1647,6 +1854,9 @@ mm_broadband_modem_sierra_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Sierra bearer supports both NET and TTY */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -1703,6 +1913,10 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
iface->setup_registration_checks_finish = setup_registration_checks_finish;
iface->get_detailed_registration_state = get_detailed_registration_state;
iface->get_detailed_registration_state_finish = get_detailed_registration_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
diff --git a/plugins/sierra/mm-common-sierra.c b/plugins/sierra/mm-common-sierra.c
index 82a5d7ba..f366050b 100644
--- a/plugins/sierra/mm-common-sierra.c
+++ b/plugins/sierra/mm-common-sierra.c
@@ -15,6 +15,9 @@
* Copyright (C) 2012 Lanedo GmbH
*/
+#include <stdlib.h>
+#include <string.h>
+
#include "mm-common-sierra.h"
#include "mm-base-modem-at.h"
#include "mm-log.h"
@@ -24,6 +27,219 @@
static MMIfaceModem *iface_modem_parent;
/*****************************************************************************/
+/* Custom init and port type hints */
+
+#define TAG_SIERRA_APP_PORT "sierra-app-port"
+#define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok"
+
+gboolean
+mm_common_sierra_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error)
+{
+ MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ MMPortType ptype;
+
+ ptype = mm_port_probe_get_port_type (probe);
+
+ /* Is it a GSM secondary port? */
+ if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) {
+ if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK))
+ pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
+ else
+ pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ } else if (ptype == MM_PORT_TYPE_AT)
+ pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+
+ return mm_base_modem_grab_port (modem,
+ mm_port_probe_peek_port (probe),
+ ptype,
+ pflags,
+ error);
+}
+
+gboolean
+mm_common_sierra_port_probe_list_is_icera (GList *probes)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ /* Only assume the Icera probing check is valid IF the port is not
+ * secondary. This will skip the stupid ports which reply OK to every
+ * AT command, even the one we use to check for Icera support */
+ if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) &&
+ !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+typedef struct {
+ MMPortSerialAt *port;
+ guint retries;
+} SierraCustomInitContext;
+
+static void
+sierra_custom_init_context_free (SierraCustomInitContext *ctx)
+{
+ g_object_unref (ctx->port);
+ g_slice_free (SierraCustomInitContext, ctx);
+}
+
+gboolean
+mm_common_sierra_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void sierra_custom_init_step (GTask *task);
+
+static void
+gcap_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortProbe *probe;
+ SierraCustomInitContext *ctx;
+ const gchar *response;
+ GError *error = NULL;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* If consumed all tries and the last error was a timeout, assume the
+ * port is not AT */
+ if (ctx->retries == 0 &&
+ g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ mm_port_probe_set_result_at (probe, FALSE);
+ }
+ /* If reported a hard parse error, this port is definitely not an AT
+ * port, skip trying. */
+ else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) {
+ mm_port_probe_set_result_at (probe, FALSE);
+ ctx->retries = 0;
+ }
+ /* Some Icera-based devices (eg, USB305) have an AT-style port that
+ * replies to everything with ERROR, so tag as unsupported; sometimes
+ * the real AT ports do this too, so let a retry tag the port as
+ * supported if it responds correctly later. */
+ else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
+ mm_port_probe_set_result_at (probe, FALSE);
+ }
+
+ /* Just retry... */
+ sierra_custom_init_step (task);
+ goto out;
+ }
+
+ /* A valid reply to ATI tells us this is an AT port already */
+ mm_port_probe_set_result_at (probe, TRUE);
+
+ /* Sierra APPx ports have limited AT command parsers that just reply with
+ * "OK" to most commands. These can sometimes be used for PPP while the
+ * main port is used for status and control, but older modems tend to crash
+ * or fail PPP. So we whitelist modems that are known to allow PPP on the
+ * secondary APP ports.
+ */
+ if (strstr (response, "APP1")) {
+ g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
+
+ /* PPP-on-APP1-port whitelist */
+ if (strstr (response, "C885") ||
+ strstr (response, "USB 306") ||
+ strstr (response, "MC8790"))
+ g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
+
+ /* For debugging: let users figure out if their device supports PPP
+ * on the APP1 port or not.
+ */
+ if (getenv ("MM_SIERRA_APP1_PPP_OK")) {
+ mm_obj_dbg (probe, "APP1 PPP OK '%s'", response);
+ g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
+ }
+ } else if (strstr (response, "APP2") ||
+ strstr (response, "APP3") ||
+ strstr (response, "APP4")) {
+ /* Additional APP ports don't support most AT commands, so they cannot
+ * be used as the primary port.
+ */
+ g_object_set_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+out:
+ if (error)
+ g_error_free (error);
+}
+
+static void
+sierra_custom_init_step (GTask *task)
+{
+ MMPortProbe *probe;
+ SierraCustomInitContext *ctx;
+ GCancellable *cancellable;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ cancellable = g_task_get_cancellable (task);
+
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (cancellable)) {
+ mm_obj_dbg (probe, "no need to keep on running custom init");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (ctx->retries == 0) {
+ mm_obj_dbg (probe, "couldn't get port type hints");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->retries--;
+ mm_port_serial_at_command (
+ ctx->port,
+ "ATI",
+ 3,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ cancellable,
+ (GAsyncReadyCallback)gcap_ready,
+ task);
+}
+
+void
+mm_common_sierra_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SierraCustomInitContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new (SierraCustomInitContext);
+ ctx->port = g_object_ref (port);
+ ctx->retries = 3;
+
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sierra_custom_init_context_free);
+
+ sierra_custom_init_step (task);
+}
+
+/*****************************************************************************/
/* Modem power up (Modem interface) */
gboolean
@@ -31,22 +247,21 @@ mm_common_sierra_modem_power_up_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-sierra_power_up_wait_cb (GSimpleAsyncResult *result)
+sierra_power_up_wait_cb (GTask *task)
{
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
cfun_enable_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
guint i;
@@ -54,9 +269,8 @@ cfun_enable_ready (MMBaseModem *self,
gboolean is_new_sierra = FALSE;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -75,24 +289,23 @@ cfun_enable_ready (MMBaseModem *self,
}
}
- /* The modem object will be valid in the callback as 'result' keeps a
+ /* The modem object will be valid in the callback as 'task' keeps a
* reference to it. */
- g_timeout_add_seconds (is_new_sierra ? 5 : 10, (GSourceFunc)sierra_power_up_wait_cb, simple);
+ g_timeout_add_seconds (is_new_sierra ? 5 : 10, (GSourceFunc)sierra_power_up_wait_cb, task);
}
static void
pcstate_enable_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
/* Ignore errors for now; we're not sure if all Sierra CDMA devices support
* at!pcstate.
*/
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -100,12 +313,9 @@ mm_common_sierra_modem_power_up (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_common_sierra_modem_power_up);
+ task = g_task_new (self, NULL, callback, user_data);
/* For CDMA modems, run !pcstate */
if (mm_iface_modem_is_cdma_only (self)) {
@@ -114,12 +324,12 @@ mm_common_sierra_modem_power_up (MMIfaceModem *self,
5,
FALSE,
(GAsyncReadyCallback)pcstate_enable_ready,
- result);
+ task);
return;
}
- mm_warn ("Not in full functionality status, power-up command is needed. "
- "Note that it may reboot the modem.");
+ mm_obj_warn (self, "not in full functionality status, power-up command is needed");
+ mm_obj_warn (self, "device may be rebooted");
/* Try to go to full functionality mode without rebooting the system.
* Works well if we previously switched off the power with CFUN=4
@@ -129,7 +339,7 @@ mm_common_sierra_modem_power_up (MMIfaceModem *self,
10,
FALSE,
(GAsyncReadyCallback)cfun_enable_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -140,33 +350,38 @@ mm_common_sierra_load_power_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_POWER_STATE_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemPowerState)GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+ }
+ return (MMModemPowerState)value;
}
static void
parent_load_power_state_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
MMModemPowerState state;
state = iface_modem_parent->load_power_state_finish (self, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (state), NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, state);
+
+ g_object_unref (task);
}
static void
pcstate_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *result;
guint state;
@@ -174,40 +389,38 @@ pcstate_query_ready (MMBaseModem *self,
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!result) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Parse power state reply */
result = mm_strip_tag (result, "!PCSTATE:");
if (!mm_get_uint_from_str (result, &state)) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse !PCSTATE response '%s'",
- result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse !PCSTATE response '%s'",
+ result);
} else {
switch (state) {
case 0:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_LOW), NULL);
+ g_task_return_int (task, MM_MODEM_POWER_STATE_LOW);
break;
case 1:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_ON), NULL);
+ g_task_return_int (task, MM_MODEM_POWER_STATE_ON);
break;
default:
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled power state: '%u'",
- state);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled power state: '%u'",
+ state);
break;
}
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
void
@@ -215,12 +428,9 @@ mm_common_sierra_load_power_state (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_common_sierra_load_power_state);
+ task = g_task_new (self, NULL, callback, user_data);
/* Check power state with AT!PCSTATE? */
if (mm_iface_modem_is_cdma_only (self)) {
@@ -229,14 +439,14 @@ mm_common_sierra_load_power_state (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)pcstate_query_ready,
- result);
+ task);
return;
}
/* Otherwise run parent's */
iface_modem_parent->load_power_state (self,
(GAsyncReadyCallback)parent_load_power_state_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -277,7 +487,7 @@ mm_common_sierra_setup_ports (MMBroadbandModem *self)
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
diff --git a/plugins/sierra/mm-common-sierra.h b/plugins/sierra/mm-common-sierra.h
index ec206d2a..22471c0f 100644
--- a/plugins/sierra/mm-common-sierra.h
+++ b/plugins/sierra/mm-common-sierra.h
@@ -18,10 +18,27 @@
#ifndef MM_COMMON_SIERRA_H
#define MM_COMMON_SIERRA_H
+#include "mm-plugin.h"
#include "mm-broadband-modem.h"
#include "mm-iface-modem.h"
#include "mm-base-sim.h"
+gboolean mm_common_sierra_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error);
+
+gboolean mm_common_sierra_port_probe_list_is_icera (GList *probes);
+
+void mm_common_sierra_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_common_sierra_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error);
+
void mm_common_sierra_load_power_state (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data);
diff --git a/plugins/sierra/mm-modem-helpers-sierra.c b/plugins/sierra/mm-modem-helpers-sierra.c
new file mode 100644
index 00000000..ac07c9ee
--- /dev/null
+++ b/plugins/sierra/mm-modem-helpers-sierra.c
@@ -0,0 +1,82 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-sierra.h"
+
+GList *
+mm_sierra_parse_scact_read_response (const gchar *reply,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GRegex *r;
+ GMatchInfo *match_info;
+ GList *list;
+
+ if (!reply || !reply[0])
+ /* Nothing configured, all done */
+ return NULL;
+
+ list = NULL;
+ r = g_regex_new ("!SCACT:\\s*(\\d+),(\\d+)",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &inner_error);
+ g_assert (r);
+
+ g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error);
+ while (!inner_error && g_match_info_matches (match_info)) {
+ MM3gppPdpContextActive *pdp_active;
+ guint cid = 0;
+ guint aux = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse CID from reply: '%s'",
+ reply);
+ break;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux != 0 && aux != 1)) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse context status from reply: '%s'",
+ reply);
+ break;
+ }
+
+ pdp_active = g_slice_new0 (MM3gppPdpContextActive);
+ pdp_active->cid = cid;
+ pdp_active->active = (gboolean) aux;
+ list = g_list_prepend (list, pdp_active);
+
+ g_match_info_next (match_info, &inner_error);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ mm_3gpp_pdp_context_active_list_free (list);
+ g_propagate_error (error, inner_error);
+ g_prefix_error (error, "Couldn't properly parse list of active/inactive PDP contexts. ");
+ return NULL;
+ }
+
+ list = g_list_sort (list, (GCompareFunc) mm_3gpp_pdp_context_active_cmp);
+
+ return list;
+}
diff --git a/plugins/sierra/mm-modem-helpers-sierra.h b/plugins/sierra/mm-modem-helpers-sierra.h
new file mode 100644
index 00000000..f5c6cd2c
--- /dev/null
+++ b/plugins/sierra/mm-modem-helpers-sierra.h
@@ -0,0 +1,26 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_MODEM_HELPERS_SIERRA_H
+#define MM_MODEM_HELPERS_SIERRA_H
+
+#include <glib.h>
+#include <ModemManager.h>
+
+/* MM3gppPdpContextActive list */
+GList *mm_sierra_parse_scact_read_response (const gchar *reply,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_SIERRA_H */
diff --git a/plugins/sierra/mm-plugin-sierra-legacy.c b/plugins/sierra/mm-plugin-sierra-legacy.c
new file mode 100644
index 00000000..ebbca1b0
--- /dev/null
+++ b/plugins/sierra/mm-plugin-sierra-legacy.c
@@ -0,0 +1,98 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-sierra-legacy.h"
+#include "mm-common-sierra.h"
+#include "mm-broadband-modem-sierra.h"
+#include "mm-broadband-modem-sierra-icera.h"
+
+G_DEFINE_TYPE (MMPluginSierraLegacy, mm_plugin_sierra_legacy, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ if (mm_common_sierra_port_probe_list_is_icera (probes))
+ return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+
+ return MM_BASE_MODEM (mm_broadband_modem_sierra_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", NULL };
+ static const gchar *drivers[] = { "sierra", "sierra_net", NULL };
+ static const gchar *forbidden_drivers[] = { "qmi_wwan", "cdc_mbim", NULL };
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (mm_common_sierra_custom_init),
+ .finish = G_CALLBACK (mm_common_sierra_custom_init_finish),
+ };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_SIERRA_LEGACY,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_DRIVERS, drivers,
+ MM_PLUGIN_FORBIDDEN_DRIVERS, forbidden_drivers,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ MM_PLUGIN_ICERA_PROBE, TRUE,
+ MM_PLUGIN_REMOVE_ECHO, FALSE,
+ NULL));
+}
+
+static void
+mm_plugin_sierra_legacy_init (MMPluginSierraLegacy *self)
+{
+}
+
+static void
+mm_plugin_sierra_legacy_class_init (MMPluginSierraLegacyClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+ plugin_class->grab_port = mm_common_sierra_grab_port;
+}
diff --git a/plugins/sierra/mm-plugin-sierra-legacy.h b/plugins/sierra/mm-plugin-sierra-legacy.h
new file mode 100644
index 00000000..787118d6
--- /dev/null
+++ b/plugins/sierra/mm-plugin-sierra-legacy.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_SIERRA_LEGACY_H
+#define MM_PLUGIN_SIERRA_LEGACY_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_SIERRA_LEGACY (mm_plugin_sierra_legacy_get_type ())
+#define MM_PLUGIN_SIERRA_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacy))
+#define MM_PLUGIN_SIERRA_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacyClass))
+#define MM_IS_PLUGIN_SIERRA_LEGACY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY))
+#define MM_IS_PLUGIN_SIERRA_LEGACY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_SIERRA_LEGACY))
+#define MM_PLUGIN_SIERRA_LEGACY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_SIERRA_LEGACY, MMPluginSierraLegacyClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginSierraLegacy;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginSierraLegacyClass;
+
+GType mm_plugin_sierra_legacy_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_SIERRA_LEGACY_H */
diff --git a/plugins/sierra/mm-plugin-sierra.c b/plugins/sierra/mm-plugin-sierra.c
index 96f657af..2ce00f56 100644
--- a/plugins/sierra/mm-plugin-sierra.c
+++ b/plugins/sierra/mm-plugin-sierra.c
@@ -13,19 +13,19 @@
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2015 Aleksander Morgado <aleksander@aleksander.es>
*/
-#include <string.h>
#include <stdlib.h>
#include <gmodule.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-plugin-sierra.h"
-#include "mm-broadband-modem-sierra.h"
-#include "mm-broadband-modem-sierra-icera.h"
+#include "mm-broadband-modem.h"
+#include "mm-broadband-modem-xmm.h"
#if defined WITH_QMI
#include "mm-broadband-modem-qmi.h"
@@ -33,203 +33,19 @@
#if defined WITH_MBIM
#include "mm-broadband-modem-mbim.h"
+#include "mm-broadband-modem-mbim-xmm.h"
#endif
G_DEFINE_TYPE (MMPluginSierra, mm_plugin_sierra, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
-/* Custom init */
-
-#define TAG_SIERRA_APP_PORT "sierra-app-port"
-#define TAG_SIERRA_APP1_PPP_OK "sierra-app1-ppp-ok"
-
-typedef struct {
- MMPortProbe *probe;
- MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- guint retries;
-} SierraCustomInitContext;
-
-static void
-sierra_custom_init_context_complete_and_free (SierraCustomInitContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
- g_slice_free (SierraCustomInitContext, ctx);
-}
-
-static gboolean
-sierra_custom_init_finish (MMPortProbe *probe,
- GAsyncResult *result,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
-}
-
-static void sierra_custom_init_step (SierraCustomInitContext *ctx);
-
-static void
-gcap_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- SierraCustomInitContext *ctx)
-{
- const gchar *response;
- GError *error = NULL;
-
- response = mm_port_serial_at_command_finish (port, res, &error);
- if (error) {
- /* If consumed all tries and the last error was a timeout, assume the
- * port is not AT */
- if (ctx->retries == 0 &&
- g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- }
- /* If reported a hard parse error, this port is definitely not an AT
- * port, skip trying. */
- else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) {
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- ctx->retries = 0;
- }
- /* Some Icera-based devices (eg, USB305) have an AT-style port that
- * replies to everything with ERROR, so tag as unsupported; sometimes
- * the real AT ports do this too, so let a retry tag the port as
- * supported if it responds correctly later. */
- else if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
- mm_port_probe_set_result_at (ctx->probe, FALSE);
- }
-
- /* Just retry... */
- sierra_custom_init_step (ctx);
- goto out;
- }
-
- /* A valid reply to ATI tells us this is an AT port already */
- mm_port_probe_set_result_at (ctx->probe, TRUE);
-
- /* Sierra APPx ports have limited AT command parsers that just reply with
- * "OK" to most commands. These can sometimes be used for PPP while the
- * main port is used for status and control, but older modems tend to crash
- * or fail PPP. So we whitelist modems that are known to allow PPP on the
- * secondary APP ports.
- */
- if (strstr (response, "APP1")) {
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
-
- /* PPP-on-APP1-port whitelist */
- if (strstr (response, "C885") ||
- strstr (response, "USB 306") ||
- strstr (response, "MC8790"))
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
-
- /* For debugging: let users figure out if their device supports PPP
- * on the APP1 port or not.
- */
- if (getenv ("MM_SIERRA_APP1_PPP_OK")) {
- mm_dbg ("Sierra: APP1 PPP OK '%s'", response);
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP1_PPP_OK, GUINT_TO_POINTER (TRUE));
- }
- } else if (strstr (response, "APP2") ||
- strstr (response, "APP3") ||
- strstr (response, "APP4")) {
- /* Additional APP ports don't support most AT commands, so they cannot
- * be used as the primary port.
- */
- g_object_set_data (G_OBJECT (ctx->probe), TAG_SIERRA_APP_PORT, GUINT_TO_POINTER (TRUE));
- }
-
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sierra_custom_init_context_complete_and_free (ctx);
-
-out:
- if (error)
- g_error_free (error);
-}
-
-static void
-sierra_custom_init_step (SierraCustomInitContext *ctx)
-{
- /* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(Sierra) no need to keep on running custom init in '%s'",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sierra_custom_init_context_complete_and_free (ctx);
- return;
- }
-
- if (ctx->retries == 0) {
- mm_dbg ("(Sierra) Couldn't get port type hints from '%s'",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sierra_custom_init_context_complete_and_free (ctx);
- return;
- }
-
- ctx->retries--;
- mm_port_serial_at_command (
- ctx->port,
- "ATI",
- 3,
- FALSE, /* raw */
- FALSE, /* allow_cached */
- ctx->cancellable,
- (GAsyncReadyCallback)gcap_ready,
- ctx);
-}
-
-static void
-sierra_custom_init (MMPortProbe *probe,
- MMPortSerialAt *port,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SierraCustomInitContext *ctx;
-
- ctx = g_slice_new (SierraCustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- sierra_custom_init);
- ctx->probe = g_object_ref (probe);
- ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- ctx->retries = 3;
-
- sierra_custom_init_step (ctx);
-}
-
-/*****************************************************************************/
-
-static gboolean
-sierra_port_probe_list_is_icera (GList *probes)
-{
- GList *l;
-
- for (l = probes; l; l = g_list_next (l)) {
- /* Only assume the Icera probing check is valid IF the port is not
- * secondary. This will skip the stupid ports which reply OK to every
- * AT command, even the one we use to check for Icera support */
- if (mm_port_probe_is_icera (MM_PORT_PROBE (l->data)) &&
- !g_object_get_data (G_OBJECT (l->data), TAG_SIERRA_APP_PORT))
- return TRUE;
- }
-
- return FALSE;
-}
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -238,8 +54,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered Sierra modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered Sierra modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -249,8 +65,16 @@ create_modem (MMPlugin *self,
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
- mm_dbg ("MBIM-powered Sierra modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path,
+ if (mm_port_probe_list_is_xmm (probes)) {
+ mm_obj_dbg (self, "MBIM-powered XMM-based Sierra modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_xmm_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+ mm_obj_dbg (self, "MBIM-powered Sierra modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -258,47 +82,21 @@ create_modem (MMPlugin *self,
}
#endif
- if (sierra_port_probe_list_is_icera (probes))
- return MM_BASE_MODEM (mm_broadband_modem_sierra_icera_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
-
- return MM_BASE_MODEM (mm_broadband_modem_sierra_new (sysfs_path,
- drivers,
- mm_plugin_get_name (self),
- vendor,
- product));
-}
-
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
- MMPortType ptype;
-
- ptype = mm_port_probe_get_port_type (probe);
-
- /* Is it a GSM secondary port? */
- if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP_PORT)) {
- if (g_object_get_data (G_OBJECT (probe), TAG_SIERRA_APP1_PPP_OK))
- pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
- else
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else if (ptype == MM_PORT_TYPE_AT)
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ if (mm_port_probe_list_is_xmm (probes)) {
+ mm_obj_dbg (self, "XMM-based Sierra modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_xmm_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- ptype,
- pflags,
- error);
+ /* Fallback to default modem in the worst case */
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
}
/*****************************************************************************/
@@ -306,25 +104,21 @@ grab_port (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
- static const gchar *drivers[] = { "sierra", "sierra_net", NULL };
- static const MMAsyncMethod custom_init = {
- .async = G_CALLBACK (sierra_custom_init),
- .finish = G_CALLBACK (sierra_custom_init_finish),
- };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendor_ids[] = { 0x1199, 0 };
+ static const gchar *drivers[] = { "qmi_wwan", "cdc_mbim", NULL };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_SIERRA,
- MM_PLUGIN_NAME, "Sierra",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_DRIVERS, drivers,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
MM_PLUGIN_ALLOWED_QMI, TRUE,
MM_PLUGIN_ALLOWED_MBIM, TRUE,
- MM_PLUGIN_CUSTOM_INIT, &custom_init,
- MM_PLUGIN_ICERA_PROBE, TRUE,
- MM_PLUGIN_REMOVE_ECHO, FALSE,
+ MM_PLUGIN_XMM_PROBE, TRUE,
NULL));
}
@@ -339,5 +133,4 @@ mm_plugin_sierra_class_init (MMPluginSierraClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/sierra/mm-shared.c b/plugins/sierra/mm-shared.c
new file mode 100644
index 00000000..665aceca
--- /dev/null
+++ b/plugins/sierra/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Sierra)
diff --git a/plugins/sierra/mm-sim-sierra.c b/plugins/sierra/mm-sim-sierra.c
index 2c2c5ff6..2f3caa48 100644
--- a/plugins/sierra/mm-sim-sierra.c
+++ b/plugins/sierra/mm-sim-sierra.c
@@ -25,7 +25,6 @@
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
@@ -41,20 +40,13 @@ load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- gchar *iccid;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- iccid = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("loaded SIM identifier: %s", iccid);
- return g_strdup (iccid);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
iccid_read_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *response;
@@ -64,32 +56,29 @@ iccid_read_ready (MMBaseModem *modem,
response = mm_base_modem_at_command_finish (modem, res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
p = mm_strip_tag (response, "!ICCID:");
if (!p) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse !ICCID response: '%s'",
- response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse !ICCID response: '%s'",
+ response);
+ g_object_unref (task);
return;
}
parsed = mm_3gpp_parse_iccid (p, &local);
if (parsed)
- g_simple_async_result_set_op_res_gpointer (simple, parsed, g_free);
+ g_task_return_pointer (task, parsed, g_free);
else
- g_simple_async_result_take_error (simple, local);
+ g_task_return_error (task, local);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -98,22 +87,21 @@ load_sim_identifier (MMBaseSim *self,
gpointer user_data)
{
MMBaseModem *modem = NULL;
+ GTask *task;
g_object_get (self,
MM_BASE_SIM_MODEM, &modem,
NULL);
- mm_dbg ("loading (Sierra) SIM identifier...");
+ task = g_task_new (self, NULL, callback, user_data);
+
mm_base_modem_at_command (
modem,
"!ICCID?",
3,
FALSE,
(GAsyncReadyCallback)iccid_read_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_sim_identifier));
+ task);
g_object_unref (modem);
}
@@ -151,6 +139,7 @@ mm_sim_sierra_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
diff --git a/plugins/sierra/tests/test-modem-helpers-sierra.c b/plugins/sierra/tests/test-modem-helpers-sierra.c
new file mode 100644
index 00000000..b0c66496
--- /dev/null
+++ b/plugins/sierra/tests/test-modem-helpers-sierra.c
@@ -0,0 +1,130 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+#include <arpa/inet.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-sierra.h"
+
+/*****************************************************************************/
+/* Test !SCACT? responses */
+
+static void
+test_scact_read_results (const gchar *desc,
+ const gchar *reply,
+ MM3gppPdpContextActive *expected_results,
+ guint32 expected_results_len)
+{
+ GList *l;
+ GError *error = NULL;
+ GList *results;
+
+ g_debug ("\nTesting %s !SCACT response...\n", desc);
+
+ results = mm_sierra_parse_scact_read_response (reply, &error);
+ g_assert_no_error (error);
+ if (expected_results_len) {
+ g_assert (results);
+ g_assert_cmpuint (g_list_length (results), ==, expected_results_len);
+ }
+
+ for (l = results; l; l = g_list_next (l)) {
+ MM3gppPdpContextActive *pdp = l->data;
+ gboolean found = FALSE;
+ guint i;
+
+ for (i = 0; !found && i < expected_results_len; i++) {
+ MM3gppPdpContextActive *expected;
+
+ expected = &expected_results[i];
+ if (pdp->cid == expected->cid) {
+ found = TRUE;
+ g_assert_cmpuint (pdp->active, ==, expected->active);
+ }
+ }
+
+ g_assert (found == TRUE);
+ }
+
+ mm_3gpp_pdp_context_active_list_free (results);
+}
+
+static void
+test_scact_read_response_none (void)
+{
+ test_scact_read_results ("none", "", NULL, 0);
+}
+
+static void
+test_scact_read_response_single_inactive (void)
+{
+ const gchar *reply = "!SCACT: 1,0\r\n";
+ static MM3gppPdpContextActive expected[] = {
+ { 1, FALSE },
+ };
+
+ test_scact_read_results ("single inactive", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+static void
+test_scact_read_response_single_active (void)
+{
+ const gchar *reply = "!SCACT: 1,1\r\n";
+ static MM3gppPdpContextActive expected[] = {
+ { 1, TRUE },
+ };
+
+ test_scact_read_results ("single active", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+static void
+test_scact_read_response_multiple (void)
+{
+ const gchar *reply =
+ "!SCACT: 1,0\r\n"
+ "!SCACT: 4,1\r\n"
+ "!SCACT: 5,0\r\n";
+ static MM3gppPdpContextActive expected[] = {
+ { 1, FALSE },
+ { 4, TRUE },
+ { 5, FALSE },
+ };
+
+ test_scact_read_results ("multiple", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/sierra/scact/read/none", test_scact_read_response_none);
+ g_test_add_func ("/MM/sierra/scact/read/single-inactive", test_scact_read_response_single_inactive);
+ g_test_add_func ("/MM/sierra/scact/read/single-active", test_scact_read_response_single_active);
+ g_test_add_func ("/MM/sierra/scact/read/multiple", test_scact_read_response_multiple);
+
+ return g_test_run ();
+}
diff --git a/plugins/simtech/77-mm-simtech-port-types.rules b/plugins/simtech/77-mm-simtech-port-types.rules
index 2c70049e..e51a60dd 100644
--- a/plugins/simtech/77-mm-simtech-port-types.rules
+++ b/plugins/simtech/77-mm-simtech-port-types.rules
@@ -10,31 +10,50 @@
# *ser.inf lists the aux ports that may be used for PPP.
-ACTION!="add|change|move", GOTO="mm_simtech_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_simtech_port_types_end"
-
-SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e0e", GOTO="mm_alink_vendorcheck"
+ACTION!="add|change|move|bind", GOTO="mm_simtech_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1e0e", GOTO="mm_simtech_port_types"
GOTO="mm_simtech_port_types_end"
-LABEL="mm_alink_vendorcheck"
+LABEL="mm_simtech_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# A-LINK 3GU
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{ID_MM_SIMTECH_TAGGED}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="cefe", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# Prolink PH-300
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{ID_MM_SIMTECH_TAGGED}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9100", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# SCT UM300
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_SIMTECH_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_SIMTECH_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{ID_MM_SIMTECH_TAGGED}="1"
-
-GOTO="mm_simtech_port_types_end"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9200", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# SIM7000, SIM7100, SIM7600...
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9001", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AUDIO}="1"
+
+# SIM7070, SIM7080, SIM7090 (default layout)...
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
+# Disable CPOL based features
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9205", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
+
+
+# SIM7070, SIM7080, SIM7090 (secondary layout)...
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PPP}="1"
+# Disable CPOL based features
+ATTRS{idVendor}=="1e0e", ATTRS{idProduct}=="9206", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
LABEL="mm_simtech_port_types_end"
diff --git a/plugins/simtech/mm-broadband-modem-qmi-simtech.c b/plugins/simtech/mm-broadband-modem-qmi-simtech.c
new file mode 100644
index 00000000..c15ecd43
--- /dev/null
+++ b/plugins/simtech/mm-broadband-modem-qmi-simtech.c
@@ -0,0 +1,128 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-errors-types.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-broadband-modem-qmi-simtech.h"
+#include "mm-shared-simtech.h"
+
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+static void shared_simtech_init (MMSharedSimtech *iface);
+
+static MMIfaceModemLocation *iface_modem_location_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmiSimtech, mm_broadband_modem_qmi_simtech, MM_TYPE_BROADBAND_MODEM_QMI, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_SIMTECH, shared_simtech_init))
+
+/*****************************************************************************/
+
+MMBroadbandModemQmiSimtech *
+mm_broadband_modem_qmi_simtech_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* QMI modem supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_BROADBAND_MODEM_INDICATORS_DISABLED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_qmi_simtech_init (MMBroadbandModemQmiSimtech *self)
+{
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_simtech_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_simtech_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_simtech_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_simtech_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_simtech_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_simtech_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedSimtech *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->check_support = mm_shared_simtech_voice_check_support;
+ iface->check_support_finish = mm_shared_simtech_voice_check_support_finish;
+ iface->enable_unsolicited_events = mm_shared_simtech_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = mm_shared_simtech_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = mm_shared_simtech_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = mm_shared_simtech_voice_disable_unsolicited_events_finish;
+ iface->setup_unsolicited_events = mm_shared_simtech_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_simtech_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_simtech_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_simtech_voice_cleanup_unsolicited_events_finish;
+ iface->setup_in_call_audio_channel = mm_shared_simtech_voice_setup_in_call_audio_channel;
+ iface->setup_in_call_audio_channel_finish = mm_shared_simtech_voice_setup_in_call_audio_channel_finish;
+ iface->cleanup_in_call_audio_channel = mm_shared_simtech_voice_cleanup_in_call_audio_channel;
+ iface->cleanup_in_call_audio_channel_finish = mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish;
+}
+
+static MMIfaceModemVoice *
+peek_parent_voice_interface (MMSharedSimtech *self)
+{
+ return iface_modem_voice_parent;
+}
+
+static void
+shared_simtech_init (MMSharedSimtech *iface)
+{
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+ iface->peek_parent_voice_interface = peek_parent_voice_interface;
+}
+
+static void
+mm_broadband_modem_qmi_simtech_class_init (MMBroadbandModemQmiSimtechClass *klass)
+{
+}
diff --git a/plugins/simtech/mm-broadband-modem-qmi-simtech.h b/plugins/simtech/mm-broadband-modem-qmi-simtech.h
new file mode 100644
index 00000000..2f5b819b
--- /dev/null
+++ b/plugins/simtech/mm-broadband-modem-qmi-simtech.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_QMI_SIMTECH_QMI_H
+#define MM_BROADBAND_MODEM_QMI_SIMTECH_QMI_H
+
+#include "mm-broadband-modem-qmi.h"
+
+#define MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH (mm_broadband_modem_qmi_simtech_get_type ())
+#define MM_BROADBAND_MODEM_QMI_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtech))
+#define MM_BROADBAND_MODEM_QMI_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtechClass))
+#define MM_IS_BROADBAND_MODEM_QMI_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH))
+#define MM_IS_BROADBAND_MODEM_QMI_SIMTECH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH))
+#define MM_BROADBAND_MODEM_QMI_SIMTECH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_QMI_SIMTECH, MMBroadbandModemQmiSimtechClass))
+
+typedef struct _MMBroadbandModemQmiSimtech MMBroadbandModemQmiSimtech;
+typedef struct _MMBroadbandModemQmiSimtechClass MMBroadbandModemQmiSimtechClass;
+
+struct _MMBroadbandModemQmiSimtech {
+ MMBroadbandModemQmi parent;
+};
+
+struct _MMBroadbandModemQmiSimtechClass{
+ MMBroadbandModemQmiClass parent;
+};
+
+GType mm_broadband_modem_qmi_simtech_get_type (void);
+
+MMBroadbandModemQmiSimtech *mm_broadband_modem_qmi_simtech_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_QMI_SIMTECH_H */
diff --git a/plugins/simtech/mm-broadband-modem-simtech.c b/plugins/simtech/mm-broadband-modem-simtech.c
index 25594d8d..2ca0c6ae 100644
--- a/plugins/simtech/mm-broadband-modem-simtech.c
+++ b/plugins/simtech/mm-broadband-modem-simtech.c
@@ -28,44 +28,65 @@
#include "ModemManager.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-shared-simtech.h"
#include "mm-broadband-modem-simtech.h"
-static void iface_modem_init (MMIfaceModem *iface);
-static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+static void shared_simtech_init (MMSharedSimtech *iface);
-static MMIfaceModem *iface_modem_parent;
-static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemSimtech, mm_broadband_modem_simtech, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init))
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_SIMTECH, shared_simtech_init))
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED
+} FeatureSupport;
+
+struct _MMBroadbandModemSimtechPrivate {
+ FeatureSupport cnsmod_support;
+ FeatureSupport autocsq_support;
+ GRegex *cnsmod_regex;
+ GRegex *csq_regex;
+};
/*****************************************************************************/
/* Setup/Cleanup unsolicited events (3GPP interface) */
static MMModemAccessTechnology
-simtech_act_to_mm_act (int nsmod)
+simtech_act_to_mm_act (guint nsmod)
{
- if (nsmod == 1)
- return MM_MODEM_ACCESS_TECHNOLOGY_GSM;
- else if (nsmod == 2)
- return MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
- else if (nsmod == 3)
- return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
- else if (nsmod == 4)
- return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
- else if (nsmod == 5)
- return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
- else if (nsmod == 6)
- return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
- else if (nsmod == 7)
- return MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
-
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ static const MMModemAccessTechnology simtech_act_to_mm_act_map[] = {
+ [0] = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
+ [1] = MM_MODEM_ACCESS_TECHNOLOGY_GSM,
+ [2] = MM_MODEM_ACCESS_TECHNOLOGY_GPRS,
+ [3] = MM_MODEM_ACCESS_TECHNOLOGY_EDGE,
+ [4] = MM_MODEM_ACCESS_TECHNOLOGY_UMTS,
+ [5] = MM_MODEM_ACCESS_TECHNOLOGY_HSDPA,
+ [6] = MM_MODEM_ACCESS_TECHNOLOGY_HSUPA,
+ [7] = MM_MODEM_ACCESS_TECHNOLOGY_HSPA,
+ [8] = MM_MODEM_ACCESS_TECHNOLOGY_LTE,
+ };
+
+ return (nsmod < G_N_ELEMENTS (simtech_act_to_mm_act_map) ? simtech_act_to_mm_act_map[nsmod] : MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
}
static void
@@ -73,15 +94,33 @@ simtech_tech_changed (MMPortSerialAt *port,
GMatchInfo *match_info,
MMBroadbandModemSimtech *self)
{
- gchar *str;
+ guint simtech_act = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &simtech_act))
+ return;
+
+ mm_iface_modem_update_access_technologies (
+ MM_IFACE_MODEM (self),
+ simtech_act_to_mm_act (simtech_act),
+ MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
+}
+
+static void
+simtech_signal_changed (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemSimtech *self)
+{
+ guint quality = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &quality))
+ return;
- str = g_match_info_fetch (match_info, 1);
- if (str && str[0])
- mm_iface_modem_update_access_technologies (
- MM_IFACE_MODEM (self),
- simtech_act_to_mm_act (atoi (str)),
- MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
- g_free (str);
+ if (quality != 99)
+ quality = MM_CLAMP_HIGH (quality, 31) * 100 / 31;
+ else
+ quality = 0;
+
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
static void
@@ -90,56 +129,56 @@ set_unsolicited_events_handlers (MMBroadbandModemSimtech *self,
{
MMPortSerialAt *ports[2];
guint i;
- GRegex *regex;
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
- regex = g_regex_new ("\\r\\n\\+CNSMOD:\\s*(\\d)\\r\\n",
- G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
-
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
/* Access technology related */
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
- regex,
+ self->priv->cnsmod_regex,
enable ? (MMPortSerialAtUnsolicitedMsgFn)simtech_tech_changed : NULL,
enable ? self : NULL,
NULL);
- }
- g_regex_unref (regex);
+ /* Signal quality related */
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->csq_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)simtech_signal_changed : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
}
static gboolean
-modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_SIMTECH (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -147,47 +186,32 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
-
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
-
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_SIMTECH (self), FALSE);
@@ -195,200 +219,451 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
-/* Enabling unsolicited events (3GPP interface) */
+/* Enable unsolicited events (3GPP interface) */
+
+typedef enum {
+ ENABLE_UNSOLICITED_EVENTS_STEP_FIRST,
+ ENABLE_UNSOLICITED_EVENTS_STEP_PARENT,
+ ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_CNSMOD,
+ ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_CNSMOD,
+ ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_AUTOCSQ,
+ ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_AUTOCSQ,
+ ENABLE_UNSOLICITED_EVENTS_STEP_LAST,
+} EnableUnsolicitedEventsStep;
+
+typedef struct {
+ EnableUnsolicitedEventsStep step;
+} EnableUnsolicitedEventsContext;
static gboolean
-modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
+static void enable_unsolicited_events_context_step (GTask *task);
+
static void
-own_enable_unsolicited_events_ready (MMBaseModem *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+autocsq_set_enabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ EnableUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+ gboolean csq_urcs_enabled = FALSE;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't enable automatic signal quality reporting: %s", error->message);
+ g_error_free (error);
+ } else
+ csq_urcs_enabled = TRUE;
+
+ /* Disable access technology polling if we can use the +CSQ URCs */
+ g_object_set (self,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, csq_urcs_enabled,
+ NULL);
+
+ /* go to next step */
+ ctx->step++;
+ enable_unsolicited_events_context_step (task);
+}
- mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
- if (error)
- g_simple_async_result_take_error (simple, error);
+static void
+autocsq_test_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemSimtech *self;
+ EnableUnsolicitedEventsContext *ctx;
+
+ self = MM_BROADBAND_MODEM_SIMTECH (_self);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (_self, res, NULL))
+ self->priv->autocsq_support = FEATURE_NOT_SUPPORTED;
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ self->priv->autocsq_support = FEATURE_SUPPORTED;
+
+ /* go to next step */
+ ctx->step++;
+ enable_unsolicited_events_context_step (task);
}
-static const MMBaseModemAtCommand unsolicited_enable_sequence[] = {
- /* Autoreport access technology changes */
- { "+CNSMOD=1", 5, FALSE, NULL },
- /* Autoreport CSQ (first arg), and only report when it changes (second arg) */
- { "+AUTOCSQ=1,1", 5, FALSE, NULL },
- { NULL }
-};
+static void
+cnsmod_set_enabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnableUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+ gboolean cnsmod_urcs_enabled = FALSE;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't enable automatic access technology reporting: %s", error->message);
+ g_error_free (error);
+ } else
+ cnsmod_urcs_enabled = TRUE;
+
+ /* Disable access technology polling if we can use the +CNSMOD URCs */
+ g_object_set (self,
+ MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, cnsmod_urcs_enabled,
+ NULL);
+
+ /* go to next step */
+ ctx->step++;
+ enable_unsolicited_events_context_step (task);
+}
+
+static void
+cnsmod_test_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemSimtech *self;
+ EnableUnsolicitedEventsContext *ctx;
+
+ self = MM_BROADBAND_MODEM_SIMTECH (_self);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (_self, res, NULL))
+ self->priv->cnsmod_support = FEATURE_NOT_SUPPORTED;
+ else
+ self->priv->cnsmod_support = FEATURE_SUPPORTED;
+
+ /* go to next step */
+ ctx->step++;
+ enable_unsolicited_events_context_step (task);
+}
static void
parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ EnableUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- /* Our own enable now */
- mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- unsolicited_enable_sequence,
- NULL, /* response_processor_context */
- NULL, /* response_processor_context_free */
- NULL, /* cancellable */
- (GAsyncReadyCallback)own_enable_unsolicited_events_ready,
- simple);
+ /* go to next step */
+ ctx->step++;
+ enable_unsolicited_events_context_step (task);
}
static void
-modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+enable_unsolicited_events_context_step (GTask *task)
{
- GSimpleAsyncResult *result;
+ MMBroadbandModemSimtech *self;
+ EnableUnsolicitedEventsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case ENABLE_UNSOLICITED_EVENTS_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case ENABLE_UNSOLICITED_EVENTS_STEP_PARENT:
+ iface_modem_3gpp_parent->enable_unsolicited_events (
+ MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
+ task);
+ return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
+ case ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_CNSMOD:
+ if (self->priv->cnsmod_support == FEATURE_SUPPORT_UNKNOWN) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CNSMOD=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)cnsmod_test_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_CNSMOD:
+ if (self->priv->cnsmod_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ /* Autoreport +CNSMOD when it changes */
+ "+CNSMOD=1",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)cnsmod_set_enabled_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLE_UNSOLICITED_EVENTS_STEP_CHECK_SUPPORT_AUTOCSQ:
+ if (self->priv->autocsq_support == FEATURE_SUPPORT_UNKNOWN) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+AUTOCSQ=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)autocsq_test_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLE_UNSOLICITED_EVENTS_STEP_ENABLE_AUTOCSQ:
+ if (self->priv->autocsq_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ /* Autoreport+ CSQ (first arg), and only report when it changes (second arg) */
+ "+AUTOCSQ=1,1",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)autocsq_set_enabled_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
- /* Chain up parent's enable */
- iface_modem_3gpp_parent->enable_unsolicited_events (
- self,
- (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
- result);
+ case ENABLE_UNSOLICITED_EVENTS_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnableUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_new (EnableUnsolicitedEventsContext, 1);
+ ctx->step = ENABLE_UNSOLICITED_EVENTS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, g_free);
+
+ enable_unsolicited_events_context_step (task);
}
/*****************************************************************************/
-/* Disabling unsolicited events (3GPP interface) */
+/* Disable unsolicited events (3GPP interface) */
+
+typedef enum {
+ DISABLE_UNSOLICITED_EVENTS_STEP_FIRST,
+ DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_AUTOCSQ,
+ DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_CNSMOD,
+ DISABLE_UNSOLICITED_EVENTS_STEP_PARENT,
+ DISABLE_UNSOLICITED_EVENTS_STEP_LAST,
+} DisableUnsolicitedEventsStep;
+
+typedef struct {
+ DisableUnsolicitedEventsStep step;
+} DisableUnsolicitedEventsContext;
static gboolean
-modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static const MMBaseModemAtCommand unsolicited_disable_sequence[] = {
- { "+CNSMOD=0", 3, FALSE, NULL },
- { "+AUTOCSQ=0", 3, FALSE, NULL },
- { NULL }
-};
+static void disable_unsolicited_events_context_step (GTask *task);
static void
parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ DisableUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
- if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
- else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ ctx = g_task_get_task_data (task);
+
+ if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* go to next step */
+ ctx->step++;
+ disable_unsolicited_events_context_step (task);
}
static void
-own_disable_unsolicited_events_ready (MMBaseModem *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+cnsmod_set_disabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ DisableUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
- mm_base_modem_at_sequence_full_finish (self, res, NULL, &error);
- if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't disable automatic access technology reporting: %s", error->message);
+ g_error_free (error);
}
- /* Next, chain up parent's disable */
- iface_modem_3gpp_parent->disable_unsolicited_events (
- MM_IFACE_MODEM_3GPP (self),
- (GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
- simple);
+ /* go to next step */
+ ctx->step++;
+ disable_unsolicited_events_context_step (task);
}
static void
-modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+autocsq_set_disabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GSimpleAsyncResult *result;
+ DisableUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
+ ctx = g_task_get_task_data (task);
- /* Our own disable first */
- mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- unsolicited_disable_sequence,
- NULL, /* response_processor_context */
- NULL, /* response_processor_context_free */
- NULL, /* cancellable */
- (GAsyncReadyCallback)own_disable_unsolicited_events_ready,
- result);
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't disable automatic signal quality reporting: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* go to next step */
+ ctx->step++;
+ disable_unsolicited_events_context_step (task);
+}
+
+static void
+disable_unsolicited_events_context_step (GTask *task)
+{
+ MMBroadbandModemSimtech *self;
+ DisableUnsolicitedEventsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case DISABLE_UNSOLICITED_EVENTS_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_AUTOCSQ:
+ if (self->priv->autocsq_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+AUTOCSQ=0",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)autocsq_set_disabled_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLE_UNSOLICITED_EVENTS_STEP_DISABLE_CNSMOD:
+ if (self->priv->cnsmod_support == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CNSMOD=0",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)cnsmod_set_disabled_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLE_UNSOLICITED_EVENTS_STEP_PARENT:
+ iface_modem_3gpp_parent->disable_unsolicited_events (
+ MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
+ task);
+ return;
+
+ case DISABLE_UNSOLICITED_EVENTS_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisableUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_new (DisableUnsolicitedEventsContext, 1);
+ ctx->step = DISABLE_UNSOLICITED_EVENTS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, g_free);
+
+ disable_unsolicited_events_context_step (task);
}
/*****************************************************************************/
/* Load access technologies (Modem interface) */
static gboolean
-load_access_technologies_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemAccessTechnology *access_technologies,
- guint *mask,
- GError **error)
+load_access_technologies_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemAccessTechnology *access_technologies,
+ guint *mask,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ GError *inner_error = NULL;
+ gssize act;
+
+ act = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
- *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ *access_technologies = (MMModemAccessTechnology) act;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
}
static void
-cnsmod_query_ready (MMBroadbandModemSimtech *self,
+cnsmod_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response, *p;
- GError *error = NULL;
+ GError *error = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -397,42 +672,40 @@ cnsmod_query_ready (MMBroadbandModemSimtech *self,
p = strchr (p, ',');
if (!p || !isdigit (*(p + 1)))
- g_simple_async_result_set_error (
- operation_result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse the +CNSMOD response: '%s'",
response);
else
- g_simple_async_result_set_op_res_gpointer (
- operation_result,
- GUINT_TO_POINTER (simtech_act_to_mm_act (atoi (p + 1))),
- NULL);
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_int (task, simtech_act_to_mm_act (atoi (p + 1)));
+ g_object_unref (task);
}
static void
-load_access_technologies (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_access_technologies (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ MMBroadbandModemSimtech *self;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_access_technologies);
+ self = MM_BROADBAND_MODEM_SIMTECH (_self);
+ task = g_task_new (self, NULL, callback, user_data);
/* Launch query only for 3GPP modems */
- if (!mm_iface_modem_is_3gpp (self)) {
- g_simple_async_result_set_op_res_gpointer (
- result,
- GUINT_TO_POINTER (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (!mm_iface_modem_is_3gpp (_self)) {
+ g_task_return_int (task, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+ g_object_unref (task);
+ return;
+ }
+
+ g_assert (self->priv->cnsmod_support != FEATURE_SUPPORT_UNKNOWN);
+ if (self->priv->cnsmod_support == FEATURE_NOT_SUPPORTED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Loading access technologies with +CNSMOD is not supported");
+ g_object_unref (task);
return;
}
@@ -442,39 +715,114 @@ load_access_technologies (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)cnsmod_query_ready,
- result);
+ task);
+}
+
+/*****************************************************************************/
+/* Load signal quality (Modem interface) */
+
+static guint
+load_signal_quality_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), error);
+ return value < 0 ? 0 : value;
+}
+
+static void
+csq_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response, *p;
+ GError *error = NULL;
+ gint quality;
+ gint ber;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Given that we may have enabled AUTOCSQ support, it is totally possible
+ * to get an empty string at this point, because the +CSQ reply may have
+ * been processed as an URC already. If we ever see this, we should not return
+ * an error, because that would reset the reported signal quality to 0 :/
+ * So, in this case, return the last cached signal quality value. */
+ if (!response[0]) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "already refreshed via URCs");
+ g_object_unref (task);
+ return;
+ }
+
+ p = mm_strip_tag (response, "+CSQ:");
+ if (sscanf (p, "%d, %d", &quality, &ber)) {
+ if (quality != 99)
+ quality = CLAMP (quality, 0, 31) * 100 / 31;
+ else
+ quality = 0;
+ g_task_return_int (task, quality);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse signal quality results");
+ g_object_unref (task);
+}
+
+static void
+load_signal_quality (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CSQ",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)csq_query_ready,
+ task);
}
/*****************************************************************************/
/* Load supported modes (Modem interface) */
static GArray *
-load_supported_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- GError *error = NULL;
- GArray *all;
- GArray *combinations;
- GArray *filtered;
- MMModemModeCombination mode;
+ GError *error = NULL;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+ MMModemModeCombination mode;
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -502,28 +850,24 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
-load_supported_modes (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
/* Run parent's loading */
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -535,70 +879,61 @@ typedef struct {
} LoadCurrentModesResult;
typedef struct {
- MMBroadbandModemSimtech *self;
- GSimpleAsyncResult *result;
gint acqord;
gint modepref;
} LoadCurrentModesContext;
-static void
-load_current_modes_context_complete_and_free (LoadCurrentModesContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
-load_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemMode *allowed,
- MMModemMode *preferred,
- GError **error)
+load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
{
LoadCurrentModesResult *result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *allowed = result->allowed;
+ *allowed = result->allowed;
*preferred = result->preferred;
+ g_free (result);
return TRUE;
}
static void
cnmp_query_ready (MMBroadbandModemSimtech *self,
- GAsyncResult *res,
- LoadCurrentModesContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- LoadCurrentModesResult *result;
- const gchar *response, *p;
- GError *error = NULL;
+ LoadCurrentModesContext *ctx;
+ LoadCurrentModesResult *result;
+ const gchar *response, *p;
+ GError *error = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
- load_current_modes_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
p = mm_strip_tag (response, "+CNMP:");
if (!p) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse the mode preference response: '%s'",
- response);
- load_current_modes_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse the mode preference response: '%s'",
+ response);
+ g_object_unref (task);
return;
}
result = g_new (LoadCurrentModesResult, 1);
- result->allowed = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_NONE;
result->preferred = MM_MODEM_MODE_NONE;
ctx->modepref = atoi (p);
@@ -619,13 +954,14 @@ cnmp_query_ready (MMBroadbandModemSimtech *self,
result->preferred = MM_MODEM_MODE_3G;
break;
default:
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Unknown acquisition order preference: '%d'",
ctx->acqord);
- load_current_modes_context_complete_and_free (ctx);
+ g_object_unref (task);
+ g_free (result);
return;
}
break;
@@ -643,51 +979,51 @@ cnmp_query_ready (MMBroadbandModemSimtech *self,
break;
default:
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Unknown mode preference: '%d'",
ctx->modepref);
- load_current_modes_context_complete_and_free (ctx);
+ g_object_unref (task);
+ g_free (result);
return;
}
- /* Set final result and complete */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- result,
- g_free);
- load_current_modes_context_complete_and_free (ctx);
+ g_task_return_pointer (task, result, g_free);
+ g_object_unref (task);
}
static void
-cnaop_query_ready (MMBroadbandModemSimtech *self,
+cnaop_query_ready (MMBaseModem *self,
GAsyncResult *res,
- LoadCurrentModesContext *ctx)
+ GTask *task)
{
- const gchar *response, *p;
- GError *error = NULL;
+ LoadCurrentModesContext *ctx;
+ const gchar *response, *p;
+ GError *error = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
- load_current_modes_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
p = mm_strip_tag (response, "+CNAOP:");
if (p)
ctx->acqord = atoi (p);
if (ctx->acqord < 0 || ctx->acqord > 2) {
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Failed to parse the acquisition order response: '%s'",
response);
- load_current_modes_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
@@ -697,91 +1033,80 @@ cnaop_query_ready (MMBroadbandModemSimtech *self,
3,
FALSE,
(GAsyncReadyCallback)cnmp_query_ready,
- ctx);
+ task);
}
static void
-load_current_modes (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
LoadCurrentModesContext *ctx;
ctx = g_new (LoadCurrentModesContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_modes);
- ctx->acqord = -1;
+ ctx->acqord = -1;
ctx->modepref = -1;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CNAOP?",
3,
FALSE,
(GAsyncReadyCallback)cnaop_query_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* Set allowed modes (Modem interface) */
typedef struct {
- MMBroadbandModemSimtech *self;
- GSimpleAsyncResult *result;
guint nmp; /* mode preference */
guint naop; /* acquisition order */
} SetCurrentModesContext;
-static void
-set_current_modes_context_complete_and_free (SetCurrentModesContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
-set_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-cnaop_set_ready (MMBaseModem *self,
+cnaop_set_ready (MMBaseModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
- /* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-cnmp_set_ready (MMBaseModem *self,
+cnmp_set_ready (MMBaseModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- gchar *command;
+ SetCurrentModesContext *ctx;
+ GError *error = NULL;
+ gchar *command;
+
+ ctx = g_task_get_task_data (task);
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
/* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -792,33 +1117,30 @@ cnmp_set_ready (MMBaseModem *self,
3,
FALSE,
(GAsyncReadyCallback)cnaop_set_ready,
- ctx);
+ task);
g_free (command);
}
static void
-set_current_modes (MMIfaceModem *self,
- MMModemMode allowed,
- MMModemMode preferred,
- GAsyncReadyCallback callback,
- gpointer user_data)
+set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
SetCurrentModesContext *ctx;
- gchar *command;
-
- ctx = g_new (SetCurrentModesContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ gchar *command;
/* Defaults: automatic search */
- ctx->nmp = 2;
+ ctx = g_new (SetCurrentModesContext, 1);
+ ctx->nmp = 2;
ctx->naop = 0;
- if (allowed == MM_MODEM_MODE_ANY &&
- preferred == MM_MODEM_MODE_NONE) {
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) {
/* defaults nmp and naop */
} else if (allowed == MM_MODEM_MODE_2G) {
ctx->nmp = 13;
@@ -841,17 +1163,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- set_current_modes_context_complete_and_free (ctx);
return;
}
@@ -862,7 +1183,7 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)cnmp_set_ready,
- ctx);
+ task);
g_free (command);
}
@@ -894,28 +1215,39 @@ mm_broadband_modem_simtech_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ MM_BROADBAND_MODEM_INDICATORS_DISABLED, TRUE,
NULL);
}
static void
mm_broadband_modem_simtech_init (MMBroadbandModemSimtech *self)
{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_MODEM_SIMTECH,
+ MMBroadbandModemSimtechPrivate);
+
+ self->priv->cnsmod_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->autocsq_support = FEATURE_SUPPORT_UNKNOWN;
+
+ self->priv->cnsmod_regex = g_regex_new ("\\r\\n\\+CNSMOD:\\s*(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->csq_regex = g_regex_new ("\\r\\n\\+CSQ:\\s*(\\d+),(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
}
static void
-iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+finalize (GObject *object)
{
- iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+ MMBroadbandModemSimtech *self = MM_BROADBAND_MODEM_SIMTECH (object);
- iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events;
- iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
- iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events;
- iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+ g_regex_unref (self->priv->cnsmod_regex);
+ g_regex_unref (self->priv->csq_regex);
- iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
- iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
- iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events;
- iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish;
+ G_OBJECT_CLASS (mm_broadband_modem_simtech_parent_class)->finalize (object);
}
static void
@@ -923,6 +1255,8 @@ iface_modem_init (MMIfaceModem *iface)
{
iface_modem_parent = g_type_interface_peek_parent (iface);
+ iface->load_signal_quality = load_signal_quality;
+ iface->load_signal_quality_finish = load_signal_quality_finish;
iface->load_access_technologies = load_access_technologies;
iface->load_access_technologies_finish = load_access_technologies_finish;
iface->load_supported_modes = load_supported_modes;
@@ -934,9 +1268,84 @@ iface_modem_init (MMIfaceModem *iface)
}
static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+ iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+
+ iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+
+ iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_simtech_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_simtech_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_simtech_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_simtech_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_simtech_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_simtech_disable_location_gathering_finish;
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedSimtech *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->check_support = mm_shared_simtech_voice_check_support;
+ iface->check_support_finish = mm_shared_simtech_voice_check_support_finish;
+ iface->enable_unsolicited_events = mm_shared_simtech_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = mm_shared_simtech_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = mm_shared_simtech_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = mm_shared_simtech_voice_disable_unsolicited_events_finish;
+ iface->setup_unsolicited_events = mm_shared_simtech_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = mm_shared_simtech_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = mm_shared_simtech_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = mm_shared_simtech_voice_cleanup_unsolicited_events_finish;
+ iface->setup_in_call_audio_channel = mm_shared_simtech_voice_setup_in_call_audio_channel;
+ iface->setup_in_call_audio_channel_finish = mm_shared_simtech_voice_setup_in_call_audio_channel_finish;
+ iface->cleanup_in_call_audio_channel = mm_shared_simtech_voice_cleanup_in_call_audio_channel;
+ iface->cleanup_in_call_audio_channel_finish = mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish;
+
+}
+
+static MMIfaceModemVoice *
+peek_parent_voice_interface (MMSharedSimtech *self)
+{
+ return iface_modem_voice_parent;
+}
+
+static void
+shared_simtech_init (MMSharedSimtech *iface)
+{
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+ iface->peek_parent_voice_interface = peek_parent_voice_interface;
+}
+
+static void
mm_broadband_modem_simtech_class_init (MMBroadbandModemSimtechClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+ g_type_class_add_private (object_class, sizeof (MMBroadbandModemSimtechPrivate));
+
+ object_class->finalize = finalize;
+
broadband_modem_class->setup_ports = setup_ports;
}
diff --git a/plugins/simtech/mm-broadband-modem-simtech.h b/plugins/simtech/mm-broadband-modem-simtech.h
index 6ee32b1b..a2b57fea 100644
--- a/plugins/simtech/mm-broadband-modem-simtech.h
+++ b/plugins/simtech/mm-broadband-modem-simtech.h
@@ -29,9 +29,11 @@
typedef struct _MMBroadbandModemSimtech MMBroadbandModemSimtech;
typedef struct _MMBroadbandModemSimtechClass MMBroadbandModemSimtechClass;
+typedef struct _MMBroadbandModemSimtechPrivate MMBroadbandModemSimtechPrivate;
struct _MMBroadbandModemSimtech {
MMBroadbandModem parent;
+ MMBroadbandModemSimtechPrivate *priv;
};
struct _MMBroadbandModemSimtechClass{
diff --git a/plugins/simtech/mm-modem-helpers-simtech.c b/plugins/simtech/mm-modem-helpers-simtech.c
new file mode 100644
index 00000000..0403c145
--- /dev/null
+++ b/plugins/simtech/mm-modem-helpers-simtech.c
@@ -0,0 +1,209 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "ModemManager.h"
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+#include "mm-errors-types.h"
+#include "mm-modem-helpers-simtech.h"
+#include "mm-modem-helpers.h"
+
+
+/*****************************************************************************/
+/* +CLCC test parser
+ *
+ * Example (SIM7600E):
+ * AT+CLCC=?
+ * +CLCC: (0-1)
+ */
+
+gboolean
+mm_simtech_parse_clcc_test (const gchar *response,
+ gboolean *clcc_urcs_supported,
+ GError **error)
+{
+ g_assert (response);
+
+ response = mm_strip_tag (response, "+CLCC:");
+
+ /* 3GPP specifies that the output of AT+CLCC=? should be just OK, so support
+ * that */
+ if (!response[0]) {
+ *clcc_urcs_supported = FALSE;
+ return TRUE;
+ }
+
+ /* As per 3GPP TS 27.007, the AT+CLCC command doesn't expect any argument,
+ * as it only is designed to report the current call list, nothing else.
+ * In the case of the Simtech plugin, though, we are going to support +CLCC
+ * URCs that can be enabled/disabled via AT+CLCC=1/0. We therefore need to
+ * detect whether this URC management is possible or not, for now with a
+ * simple check looking for the specific "(0-1)" string.
+ */
+ if (!strncmp (response, "(0-1)", 5)) {
+ *clcc_urcs_supported = TRUE;
+ return TRUE;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "unexpected +CLCC test response: '%s'", response);
+ return FALSE;
+}
+
+/*****************************************************************************/
+
+GRegex *
+mm_simtech_get_clcc_urc_regex (void)
+{
+ return g_regex_new ("\\r\\n(\\+CLCC: .*\\r\\n)+",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+gboolean
+mm_simtech_parse_clcc_list (const gchar *str,
+ gpointer log_object,
+ GList **out_list,
+ GError **error)
+{
+ /* Parse the URC contents as a plain +CLCC response, but make sure to skip first
+ * EOL in the string because the plain +CLCC response would never have that.
+ */
+ return mm_3gpp_parse_clcc_response (mm_strip_tag (str, "\r\n"), log_object, out_list, error);
+}
+
+void
+mm_simtech_call_info_list_free (GList *call_info_list)
+{
+ mm_3gpp_call_info_list_free (call_info_list);
+}
+
+/*****************************************************************************/
+
+/*
+ * <CR><LF>VOICE CALL: BEGIN<CR><LF>
+ * <CR><LF>VOICE CALL: END: 000041<CR><LF>
+ */
+GRegex *
+mm_simtech_get_voice_call_urc_regex (void)
+{
+ return g_regex_new ("\\r\\nVOICE CALL:\\s*([A-Z]+)(?::\\s*(\\d+))?\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+gboolean
+mm_simtech_parse_voice_call_urc (GMatchInfo *match_info,
+ gboolean *start_or_stop,
+ guint *duration,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gchar *str;
+
+ str = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (!str) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't read voice call URC action");
+ goto out;
+ }
+
+ if (g_strcmp0 (str, "BEGIN") == 0) {
+ *start_or_stop = TRUE;
+ *duration = 0;
+ goto out;
+ }
+
+ if (g_strcmp0 (str, "END") == 0) {
+ *start_or_stop = FALSE;
+ if (!mm_get_uint_from_match_info (match_info, 2, duration))
+ *duration = 0;
+ goto out;
+ }
+
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown voice call URC action: %s", str);
+
+out:
+ g_free (str);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+/*
+ * <CR><LF>MISSED_CALL: 11:01AM 07712345678<CR><LF>
+ */
+GRegex *
+mm_simtech_get_missed_call_urc_regex (void)
+{
+ return g_regex_new ("\\r\\nMISSED_CALL:\\s*(.+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+gboolean
+mm_simtech_parse_missed_call_urc (GMatchInfo *match_info,
+ gchar **details,
+ GError **error)
+{
+ gchar *str;
+
+ str = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (!str) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't read missed call URC details");
+ return FALSE;
+ }
+
+ *details = str;
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+/*
+ * Using TWO <CR> instead of one...
+ * <CR><CR><LF>+CRING: VOICE<CR><CR><LF>
+ */
+GRegex *
+mm_simtech_get_cring_urc_regex (void)
+{
+ return g_regex_new ("(?:\\r)+\\n\\+CRING:\\s*(\\S+)(?:\\r)+\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+/*****************************************************************************/
+
+/*
+ * <CR><CR><LF>+RXDTMF: 8<CR><CR><LF>
+ * <CR><CR><LF>+RXDTMF: *<CR><CR><LF>
+ * <CR><CR><LF>+RXDTMF: 7<CR><CR><LF>
+ *
+ * Note! using TWO <CR> instead of one...
+ */
+GRegex *
+mm_simtech_get_rxdtmf_urc_regex (void)
+{
+ return g_regex_new ("(?:\\r)+\\n\\+RXDTMF:\\s*([0-9A-D\\*\\#])(?:\\r)+\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
diff --git a/plugins/simtech/mm-modem-helpers-simtech.h b/plugins/simtech/mm-modem-helpers-simtech.h
new file mode 100644
index 00000000..1949d2e7
--- /dev/null
+++ b/plugins/simtech/mm-modem-helpers-simtech.h
@@ -0,0 +1,67 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_MODEM_HELPERS_SIMTECH_H
+#define MM_MODEM_HELPERS_SIMTECH_H
+
+#include <glib.h>
+
+#include <ModemManager.h>
+#include <mm-base-bearer.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+/*****************************************************************************/
+/* +CLCC URC helpers */
+
+gboolean mm_simtech_parse_clcc_test (const gchar *response,
+ gboolean *clcc_urcs_supported,
+ GError **error);
+
+GRegex *mm_simtech_get_clcc_urc_regex (void);
+gboolean mm_simtech_parse_clcc_list (const gchar *str,
+ gpointer log_object,
+ GList **out_list,
+ GError **error);
+void mm_simtech_call_info_list_free (GList *call_info_list);
+
+/*****************************************************************************/
+/* VOICE CALL URC helpers */
+
+GRegex *mm_simtech_get_voice_call_urc_regex (void);
+gboolean mm_simtech_parse_voice_call_urc (GMatchInfo *match_info,
+ gboolean *start_or_stop,
+ guint *duration,
+ GError **error);
+
+/*****************************************************************************/
+/* MISSED_CALL URC helpers */
+
+GRegex *mm_simtech_get_missed_call_urc_regex (void);
+gboolean mm_simtech_parse_missed_call_urc (GMatchInfo *match_info,
+ gchar **details,
+ GError **error);
+
+/*****************************************************************************/
+/* Non-standard CRING URC helpers */
+
+GRegex *mm_simtech_get_cring_urc_regex (void);
+
+/*****************************************************************************/
+/* +RXDTMF URC helpers */
+
+GRegex *mm_simtech_get_rxdtmf_urc_regex (void);
+
+#endif /* MM_MODEM_HELPERS_SIMTECH_H */
diff --git a/plugins/simtech/mm-plugin-simtech.c b/plugins/simtech/mm-plugin-simtech.c
index a38942d3..99c7697d 100644
--- a/plugins/simtech/mm-plugin-simtech.c
+++ b/plugins/simtech/mm-plugin-simtech.c
@@ -21,106 +21,65 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-plugin-simtech.h"
#include "mm-broadband-modem-simtech.h"
+#if defined WITH_QMI
+#include "mm-broadband-modem-qmi-simtech.h"
+#endif
+
G_DEFINE_TYPE (MMPluginSimtech, mm_plugin_simtech, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_simtech_new (sysfs_path,
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered SimTech modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_simtech_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_simtech_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- GUdevDevice *port;
- MMPortType ptype;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-
- /* The Simtech plugin cannot do anything with non-AT non-QCDM ports */
- if (!mm_port_probe_is_at (probe) &&
- !mm_port_probe_is_qcdm (probe)) {
- g_set_error_literal (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Ignoring non-AT non-QCDM port");
- return FALSE;
- }
-
- port = mm_port_probe_peek_port (probe);
-
- /* Look for port type hints; just probing can't distinguish which port should
- * be the data/primary port on these devices. We have to tag them based on
- * what the Windows .INF files say the port layout should be.
- */
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_MODEM")) {
- mm_dbg ("Simtech: AT port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_PORT_TYPE_AUX")) {
- mm_dbg ("Simtech: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- }
-
- /* If the port was tagged by the udev rules but isn't a primary or secondary,
- * then ignore it to guard against race conditions if a device just happens
- * to show up with more than two AT-capable ports.
- */
- if (pflags == MM_PORT_SERIAL_AT_FLAG_NONE &&
- g_udev_device_get_property_as_boolean (port, "ID_MM_SIMTECH_TAGGED"))
- ptype = MM_PORT_TYPE_IGNORED;
- else
- ptype = mm_port_probe_get_port_type (probe);
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- ptype,
- pflags,
- error);
-}
-
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendor_ids[] = { 0x1e0e, /* A-Link (for now) */
0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_SIMTECH,
- MM_PLUGIN_NAME, "SimTech",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
MM_PLUGIN_ALLOWED_QCDM, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
NULL));
}
@@ -135,5 +94,4 @@ mm_plugin_simtech_class_init (MMPluginSimtechClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/simtech/mm-shared-simtech.c b/plugins/simtech/mm-shared-simtech.c
new file mode 100644
index 00000000..99c2346e
--- /dev/null
+++ b/plugins/simtech/mm-shared-simtech.c
@@ -0,0 +1,1261 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-iface-modem-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-shared-simtech.h"
+#include "mm-modem-helpers-simtech.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "shared-simtech-private-tag"
+static GQuark private_quark;
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED,
+} FeatureSupport;
+
+typedef struct {
+ /* location */
+ MMIfaceModemLocation *iface_modem_location_parent;
+ MMModemLocationSource supported_sources;
+ MMModemLocationSource enabled_sources;
+ FeatureSupport cgps_support;
+ /* voice */
+ MMIfaceModemVoice *iface_modem_voice_parent;
+ FeatureSupport cpcmreg_support;
+ FeatureSupport clcc_urc_support;
+ GRegex *clcc_urc_regex;
+ GRegex *voice_call_regex;
+ GRegex *missed_call_regex;
+ GRegex *cring_regex;
+ GRegex *rxdtmf_regex;
+} Private;
+
+static void
+private_free (Private *ctx)
+{
+ g_regex_unref (ctx->rxdtmf_regex);
+ g_regex_unref (ctx->cring_regex);
+ g_regex_unref (ctx->missed_call_regex);
+ g_regex_unref (ctx->voice_call_regex);
+ g_regex_unref (ctx->clcc_urc_regex);
+ g_slice_free (Private, ctx);
+}
+
+static Private *
+get_private (MMSharedSimtech *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = (g_quark_from_static_string (PRIVATE_TAG));
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ priv->supported_sources = MM_MODEM_LOCATION_SOURCE_NONE;
+ priv->enabled_sources = MM_MODEM_LOCATION_SOURCE_NONE;
+ priv->cgps_support = FEATURE_SUPPORT_UNKNOWN;
+ priv->cpcmreg_support = FEATURE_SUPPORT_UNKNOWN;
+ priv->clcc_urc_support = FEATURE_SUPPORT_UNKNOWN;
+ priv->clcc_urc_regex = mm_simtech_get_clcc_urc_regex ();
+ priv->voice_call_regex = mm_simtech_get_voice_call_urc_regex ();
+ priv->missed_call_regex = mm_simtech_get_missed_call_urc_regex ();
+ priv->cring_regex = mm_simtech_get_cring_urc_regex ();
+ priv->rxdtmf_regex = mm_simtech_get_rxdtmf_urc_regex ();
+
+ /* Setup parent class' MMIfaceModemLocation and MMIfaceModemVoice */
+
+ g_assert (MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_location_interface);
+ priv->iface_modem_location_parent = MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_location_interface (self);
+
+ g_assert (MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_voice_interface);
+ priv->iface_modem_voice_parent = MM_SHARED_SIMTECH_GET_INTERFACE (self)->peek_parent_voice_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+/*****************************************************************************/
+/* GPS trace received */
+
+static void
+trace_received (MMPortSerialGps *port,
+ const gchar *trace,
+ MMIfaceModemLocation *self)
+{
+ mm_iface_modem_location_gps_update (self, trace);
+}
+
+/*****************************************************************************/
+/* Location capabilities loading (Location interface) */
+
+MMModemLocationSource
+mm_shared_simtech_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize aux;
+
+ aux = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource) aux;
+}
+
+static void probe_gps_features (GTask *task);
+
+static void
+cgps_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!mm_base_modem_at_command_finish (self, res, NULL))
+ priv->cgps_support = FEATURE_NOT_SUPPORTED;
+ else
+ priv->cgps_support = FEATURE_SUPPORTED;
+
+ probe_gps_features (task);
+}
+
+static void
+probe_gps_features (GTask *task)
+{
+ MMSharedSimtech *self;
+ MMModemLocationSource sources;
+ Private *priv;
+
+ self = MM_SHARED_SIMTECH (g_task_get_source_object (task));
+ priv = get_private (self);
+
+ /* Need to check if CGPS supported... */
+ if (priv->cgps_support == FEATURE_SUPPORT_UNKNOWN) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "+CGPS=?", 3, TRUE, (GAsyncReadyCallback) cgps_test_ready, task);
+ return;
+ }
+
+ /* All GPS features probed */
+
+ /* Recover parent sources */
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ if (priv->cgps_support == FEATURE_SUPPORTED) {
+ mm_obj_dbg (self, "GPS commands supported: GPS capabilities enabled");
+
+ /* We only flag as supported by this implementation those sources not already
+ * supported by the parent implementation */
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_NMEA))
+ priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_NMEA;
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_RAW))
+ priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_RAW;
+ if (!(sources & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))
+ priv->supported_sources |= MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED;
+
+ sources |= priv->supported_sources;
+
+ /* Add handler for the NMEA traces in the GPS data port */
+ mm_port_serial_gps_add_trace_handler (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)),
+ (MMPortSerialGpsTraceFn)trace_received,
+ self,
+ NULL);
+ } else
+ mm_obj_dbg (self, "no GPS command supported: no GPS capabilities");
+
+ g_task_return_int (task, (gssize) sources);
+ g_object_unref (task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Now our own check. If we don't have any GPS port, we're done */
+ if (!mm_base_modem_peek_port_gps (MM_BASE_MODEM (self))) {
+ mm_obj_dbg (self, "no GPS data port found: no GPS capabilities");
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Cache sources supported by the parent */
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+
+ /* Probe all GPS features */
+ probe_gps_features (task);
+}
+
+void
+mm_shared_simtech_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->load_capabilities);
+ g_assert (priv->iface_modem_location_parent->load_capabilities_finish);
+
+ priv->iface_modem_location_parent->load_capabilities (self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Disable location gathering (Location interface) */
+
+gboolean
+mm_shared_simtech_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_cgps_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ Private *priv;
+ GError *error = NULL;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ mm_base_modem_at_command_finish (self, res, &error);
+
+ /* Only use the GPS port in NMEA/RAW setups */
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ /* Even if we get an error here, we try to close the GPS port */
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else {
+ priv->enabled_sources &= ~source;
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_simtech_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMModemLocationSource enabled_sources;
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent disable */
+ if (!(priv->supported_sources & source)) {
+ /* If disabling implemented by the parent, run it. */
+ if (priv->iface_modem_location_parent->disable_location_gathering &&
+ priv->iface_modem_location_parent->disable_location_gathering_finish) {
+ priv->iface_modem_location_parent->disable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_disable_location_gathering_ready,
+ task);
+ return;
+ }
+ /* Otherwise, we're done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED));
+
+ /* Flag as disabled to see how many others we would have left enabled */
+ enabled_sources = priv->enabled_sources;
+ enabled_sources &= ~source;
+
+ /* If there are still GPS-related sources enabled, do nothing else */
+ if (enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ priv->enabled_sources &= ~source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Stop GPS engine if all GPS-related sources are disabled */
+ g_assert (priv->cgps_support == FEATURE_SUPPORTED);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CGPS=0",
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) disable_cgps_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+gboolean
+mm_shared_simtech_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+enable_cgps_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Only use the GPS port in NMEA/RAW setups */
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ gps_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (!gps_port || !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_simtech_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent enable */
+ if (!(priv->supported_sources & source)) {
+ priv->iface_modem_location_parent->enable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED));
+
+ /* If GPS already started, store new flag and we're done */
+ if (priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ g_assert (priv->cgps_support == FEATURE_SUPPORTED);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CGPS=1,1",
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) enable_cgps_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common enable/disable voice unsolicited events */
+
+typedef struct {
+ gboolean enable;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ gchar *clcc_command;
+ gboolean clcc_primary_done;
+ gboolean clcc_secondary_done;
+} VoiceUnsolicitedEventsContext;
+
+static void
+voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx)
+{
+ g_clear_object (&ctx->secondary);
+ g_clear_object (&ctx->primary);
+ g_free (ctx->clcc_command);
+ g_slice_free (VoiceUnsolicitedEventsContext, ctx);
+}
+
+static gboolean
+common_voice_enable_disable_unsolicited_events_finish (MMSharedSimtech *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void run_voice_enable_disable_unsolicited_events (GTask *task);
+
+static void
+clcc_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't %s +CLCC reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
+ }
+
+ /* Continue on next port */
+ run_voice_enable_disable_unsolicited_events (task);
+}
+
+static void
+run_voice_enable_disable_unsolicited_events (GTask *task)
+{
+ MMSharedSimtech *self;
+ Private *priv;
+ VoiceUnsolicitedEventsContext *ctx;
+ MMPortSerialAt *port = NULL;
+
+ self = MM_SHARED_SIMTECH (g_task_get_source_object (task));
+ priv = get_private (self);
+ ctx = g_task_get_task_data (task);
+
+ /* If +CLCC URCs not supported, we're done */
+ if (priv->clcc_urc_support == FEATURE_NOT_SUPPORTED) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!ctx->clcc_primary_done && ctx->primary) {
+ mm_obj_dbg (self, "%s +CLCC extended list of current calls reporting in primary port...",
+ ctx->enable ? "enabling" : "disabling");
+ ctx->clcc_primary_done = TRUE;
+ port = ctx->primary;
+ } else if (!ctx->clcc_secondary_done && ctx->secondary) {
+ mm_obj_dbg (self, "%s +CLCC extended list of current calls reporting in secondary port...",
+ ctx->enable ? "enabling" : "disabling");
+ ctx->clcc_secondary_done = TRUE;
+ port = ctx->secondary;
+ }
+
+ if (port) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ port,
+ ctx->clcc_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)clcc_command_ready,
+ task);
+ return;
+ }
+
+ /* Fully done now */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_voice_enable_disable_unsolicited_events (MMSharedSimtech *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (VoiceUnsolicitedEventsContext);
+ ctx->enable = enable;
+ if (enable)
+ ctx->clcc_command = g_strdup ("+CLCC=1");
+ else
+ ctx->clcc_command = g_strdup ("+CLCC=0");
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free);
+
+ run_voice_enable_disable_unsolicited_events (task);
+}
+
+/*****************************************************************************/
+/* Disable unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_simtech_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!priv->iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't disable parent voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+voice_disable_unsolicited_events_ready (MMSharedSimtech *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ GError *error = NULL;
+
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't disable Simtech-specific voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->disable_unsolicited_events_finish);
+
+ /* Chain up parent's disable */
+ priv->iface_modem_voice_parent->disable_unsolicited_events (
+ MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready,
+ task);
+}
+
+void
+mm_shared_simtech_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* our own disabling first */
+ common_voice_enable_disable_unsolicited_events (MM_SHARED_SIMTECH (self),
+ FALSE,
+ (GAsyncReadyCallback) voice_disable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_simtech_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_enable_unsolicited_events_ready (MMSharedSimtech *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't enable Simtech-specific voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!priv->iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't enable parent voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* our own enabling next */
+ common_voice_enable_disable_unsolicited_events (MM_SHARED_SIMTECH (self),
+ TRUE,
+ (GAsyncReadyCallback) voice_enable_unsolicited_events_ready,
+ task);
+}
+
+void
+mm_shared_simtech_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->enable_unsolicited_events_finish);
+
+ /* chain up parent's enable first */
+ priv->iface_modem_voice_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common setup/cleanup voice unsolicited events */
+
+static void
+clcc_urc_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMSharedSimtech *self)
+{
+ gchar *full;
+ GError *error = NULL;
+ GList *call_info_list = NULL;
+
+ full = g_match_info_fetch (match_info, 0);
+
+ if (!mm_simtech_parse_clcc_list (full, self, &call_info_list, &error)) {
+ mm_obj_warn (self, "couldn't parse +CLCC list in URC: %s", error->message);
+ g_error_free (error);
+ } else
+ mm_iface_modem_voice_report_all_calls (MM_IFACE_MODEM_VOICE (self), call_info_list);
+
+ mm_simtech_call_info_list_free (call_info_list);
+ g_free (full);
+}
+
+static void
+missed_call_urc_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMSharedSimtech *self)
+{
+ GError *error = NULL;
+ gchar *details = NULL;
+
+ if (!mm_simtech_parse_missed_call_urc (match_info, &details, &error)) {
+ mm_obj_warn (self, "couldn't parse missed call URC: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ mm_obj_dbg (self, "missed call reported: %s", details);
+ g_free (details);
+}
+
+static void
+voice_call_urc_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMSharedSimtech *self)
+{
+ GError *error = NULL;
+ gboolean start_or_stop = FALSE; /* start = TRUE, stop = FALSE */
+ guint duration = 0;
+
+ if (!mm_simtech_parse_voice_call_urc (match_info, &start_or_stop, &duration, &error)) {
+ mm_obj_warn (self, "couldn't parse voice call URC: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ if (start_or_stop) {
+ mm_obj_dbg (self, "voice call started");
+ return;
+ }
+
+ if (duration) {
+ mm_obj_dbg (self, "voice call finished (duration: %us)", duration);
+ return;
+ }
+
+ mm_obj_dbg (self, "voice call finished");
+}
+
+static void
+cring_urc_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMSharedSimtech *self)
+{
+ MMCallInfo call_info;
+ g_autofree gchar *str = NULL;
+
+ /* We could have "VOICE" or "DATA". Now consider only "VOICE" */
+ str = mm_get_string_unquoted_from_match_info (info, 1);
+ mm_obj_dbg (self, "ringing (%s)", str);
+
+ call_info.index = 0;
+ call_info.direction = MM_CALL_DIRECTION_INCOMING;
+ call_info.state = MM_CALL_STATE_RINGING_IN;
+ call_info.number = NULL;
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+rxdtmf_urc_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMSharedSimtech *self)
+{
+ g_autofree gchar *dtmf = NULL;
+
+ dtmf = g_match_info_fetch (match_info, 1);
+ mm_obj_dbg (self, "received DTMF: %s", dtmf);
+ /* call index unknown */
+ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf);
+}
+
+static void
+common_voice_setup_cleanup_unsolicited_events (MMSharedSimtech *self,
+ gboolean enable)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ if (priv->clcc_urc_support == FEATURE_SUPPORTED)
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->clcc_urc_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)clcc_urc_received : NULL,
+ enable ? self : NULL,
+ NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->voice_call_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)voice_call_urc_received : NULL,
+ enable ? self : NULL,
+ NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->missed_call_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)missed_call_urc_received : NULL,
+ enable ? self : NULL,
+ NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->cring_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)cring_urc_received : NULL,
+ enable ? self : NULL,
+ NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ priv->rxdtmf_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)rxdtmf_urc_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_simtech_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't cleanup parent voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_simtech_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->cleanup_unsolicited_events_finish);
+
+ /* our own cleanup first */
+ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_SIMTECH (self), FALSE);
+
+ /* Chain up parent's cleanup */
+ priv->iface_modem_voice_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup unsolicited events (Voice interface) */
+
+gboolean
+mm_shared_simtech_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!priv->iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't setup parent voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* our own setup next */
+ common_voice_setup_cleanup_unsolicited_events (MM_SHARED_SIMTECH (self), TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_simtech_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events);
+ g_assert (priv->iface_modem_voice_parent->setup_unsolicited_events_finish);
+
+ /* chain up parent's setup first */
+ priv->iface_modem_voice_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* In-call audio channel setup/cleanup */
+
+gboolean
+mm_shared_simtech_voice_setup_in_call_audio_channel_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ MMPort **audio_port, /* optional */
+ MMCallAudioFormat **audio_format, /* optional */
+ GError **error)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ if (audio_format)
+ *audio_format = NULL;
+
+ if (audio_port) {
+ if (priv->cpcmreg_support == FEATURE_SUPPORTED)
+ *audio_port = MM_PORT (mm_base_modem_get_port_audio (MM_BASE_MODEM (self)));
+ else
+ *audio_port = NULL;
+ }
+
+ return TRUE;
+}
+
+gboolean
+mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cpcmreg_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_setup_cleanup_in_call_audio_channel (MMSharedSimtech *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Do nothing if CPCMREG isn't supported */
+ if (priv->cpcmreg_support != FEATURE_SUPPORTED) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ setup ? "+CPCMREG=1" : "+CPCMREG=0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) cpcmreg_set_ready,
+ task);
+}
+
+void
+mm_shared_simtech_voice_setup_in_call_audio_channel (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_setup_cleanup_in_call_audio_channel (MM_SHARED_SIMTECH (self), TRUE, callback, user_data);
+}
+
+void
+mm_shared_simtech_voice_cleanup_in_call_audio_channel (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_setup_cleanup_in_call_audio_channel (MM_SHARED_SIMTECH (self), FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Check if Voice supported (Voice interface) */
+
+gboolean
+mm_shared_simtech_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cpcmreg_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ priv->cpcmreg_support = (mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ?
+ FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED);
+ mm_obj_dbg (self, "modem %s USB audio control", (priv->cpcmreg_support == FEATURE_SUPPORTED) ? "supports" : "doesn't support");
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+clcc_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ GError *error = NULL;
+ const gchar *response;
+ gboolean clcc_urc_supported = FALSE;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+ if (response && !mm_simtech_parse_clcc_test (response, &clcc_urc_supported, &error)) {
+ mm_obj_dbg (self, "failed checking CLCC URC support: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ priv->clcc_urc_support = (clcc_urc_supported ? FEATURE_SUPPORTED : FEATURE_NOT_SUPPORTED);
+ mm_obj_dbg (self, "modem %s +CLCC URCs", (priv->clcc_urc_support == FEATURE_SUPPORTED) ? "supports" : "doesn't support");
+
+ /* If +CLCC URC supported we won't need polling in the parent */
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, (priv->clcc_urc_support == FEATURE_SUPPORTED),
+ NULL);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CPCMREG=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) cpcmreg_format_check_ready,
+ task);
+}
+
+static void
+parent_voice_check_support_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ Private *priv;
+ GError *error = NULL;
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ if (!priv->iface_modem_voice_parent->check_support_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* voice is supported, check if +CLCC URCs are available */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CLCC=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) clcc_format_check_ready,
+ task);
+}
+
+void
+mm_shared_simtech_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_SIMTECH (self));
+ g_assert (priv->iface_modem_voice_parent);
+ g_assert (priv->iface_modem_voice_parent->check_support);
+ g_assert (priv->iface_modem_voice_parent->check_support_finish);
+
+ /* chain up parent's setup first */
+ priv->iface_modem_voice_parent->check_support (
+ self,
+ (GAsyncReadyCallback)parent_voice_check_support_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static void
+shared_simtech_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_simtech_get_type (void)
+{
+ static GType shared_simtech_type = 0;
+
+ if (!G_UNLIKELY (shared_simtech_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedSimtech), /* class_size */
+ shared_simtech_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_simtech_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedSimtech", &info, 0);
+ g_type_interface_add_prerequisite (shared_simtech_type, MM_TYPE_IFACE_MODEM_LOCATION);
+ }
+
+ return shared_simtech_type;
+}
diff --git a/plugins/simtech/mm-shared-simtech.h b/plugins/simtech/mm-shared-simtech.h
new file mode 100644
index 00000000..37a221ca
--- /dev/null
+++ b/plugins/simtech/mm-shared-simtech.h
@@ -0,0 +1,129 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SHARED_SIMTECH_H
+#define MM_SHARED_SIMTECH_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-iface-modem-voice.h"
+
+#define MM_TYPE_SHARED_SIMTECH (mm_shared_simtech_get_type ())
+#define MM_SHARED_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_SIMTECH, MMSharedSimtech))
+#define MM_IS_SHARED_SIMTECH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_SIMTECH))
+#define MM_SHARED_SIMTECH_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_SIMTECH, MMSharedSimtech))
+
+typedef struct _MMSharedSimtech MMSharedSimtech;
+
+struct _MMSharedSimtech {
+ GTypeInterface g_iface;
+
+ /* Peek location interface of the parent class of the object */
+ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedSimtech *self);
+
+ /* Peek voice interface of the parent class of the object */
+ MMIfaceModemVoice * (* peek_parent_voice_interface) (MMSharedSimtech *self);
+};
+
+GType mm_shared_simtech_get_type (void);
+
+/*****************************************************************************/
+/* Location interface */
+
+void mm_shared_simtech_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemLocationSource mm_shared_simtech_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+
+/*****************************************************************************/
+/* Voice interface */
+
+void mm_shared_simtech_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_simtech_voice_setup_in_call_audio_channel (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_setup_in_call_audio_channel_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ MMPort **audio_port, /* optional */
+ MMCallAudioFormat **audio_format, /* optional */
+ GError **error);
+void mm_shared_simtech_voice_cleanup_in_call_audio_channel (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_simtech_voice_cleanup_in_call_audio_channel_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_SIMTECH_H */
diff --git a/plugins/simtech/tests/test-modem-helpers-simtech.c b/plugins/simtech/tests/test-modem-helpers-simtech.c
new file mode 100644
index 00000000..d5d774f2
--- /dev/null
+++ b/plugins/simtech/tests/test-modem-helpers-simtech.c
@@ -0,0 +1,343 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-simtech.h"
+
+/*****************************************************************************/
+/* Test +CLCC URCs */
+
+static void
+common_test_clcc_urc (const gchar *urc,
+ const MMCallInfo *expected_call_info_list,
+ guint expected_call_info_list_size)
+{
+ GError *error = NULL;
+ GRegex *clcc_regex = NULL;
+ gboolean result;
+ GMatchInfo *match_info = NULL;
+ gchar *str;
+ GList *call_info_list = NULL;
+ GList *l;
+
+ clcc_regex = mm_simtech_get_clcc_urc_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (clcc_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ /* read full matched content */
+ str = g_match_info_fetch (match_info, 0);
+ g_assert (str);
+
+ result = mm_simtech_parse_clcc_list (str, NULL, &call_info_list, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ g_debug ("found %u calls", g_list_length (call_info_list));
+
+ if (expected_call_info_list) {
+ g_assert (call_info_list);
+ g_assert_cmpuint (g_list_length (call_info_list), ==, expected_call_info_list_size);
+ } else
+ g_assert (!call_info_list);
+
+ for (l = call_info_list; l; l = g_list_next (l)) {
+ const MMCallInfo *call_info = (const MMCallInfo *)(l->data);
+ gboolean found = FALSE;
+ guint i;
+
+ g_debug ("call at index %u: direction %s, state %s, number %s",
+ call_info->index,
+ mm_call_direction_get_string (call_info->direction),
+ mm_call_state_get_string (call_info->state),
+ call_info->number ? call_info->number : "n/a");
+
+ for (i = 0; !found && i < expected_call_info_list_size; i++)
+ found = ((call_info->index == expected_call_info_list[i].index) &&
+ (call_info->direction == expected_call_info_list[i].direction) &&
+ (call_info->state == expected_call_info_list[i].state) &&
+ (g_strcmp0 (call_info->number, expected_call_info_list[i].number) == 0));
+
+ g_assert (found);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (clcc_regex);
+ g_free (str);
+
+ mm_simtech_call_info_list_free (call_info_list);
+}
+
+static void
+test_clcc_urc_single (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }
+ };
+
+ const gchar *urc =
+ "\r\n+CLCC: 1,1,0,0,0,\"123456789\",161"
+ "\r\n";
+
+ common_test_clcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_clcc_urc_multiple (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, NULL },
+ { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" },
+ { 3, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "987654321" },
+ };
+
+ const gchar *urc =
+ "\r\n+CLCC: 1,1,0,0,0" /* number unknown */
+ "\r\n+CLCC: 2,1,0,0,0,\"123456789\",161"
+ "\r\n+CLCC: 3,1,0,0,0,\"987654321\",161,\"Alice\""
+ "\r\n";
+
+ common_test_clcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_clcc_urc_complex (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" },
+ { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_WAITING, (gchar *) "987654321" },
+ };
+
+ const gchar *urc =
+ "\r\n^CIEV: 1,0" /* some different URC before our match */
+ "\r\n+CLCC: 1,1,0,0,0,\"123456789\",161"
+ "\r\n+CLCC: 2,1,5,0,0,\"987654321\",161"
+ "\r\n^CIEV: 1,0" /* some different URC after our match */
+ "\r\n";
+
+ common_test_clcc_urc (urc, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+/*****************************************************************************/
+
+static void
+common_test_voice_call_urc (const gchar *urc,
+ gboolean expected_start_or_stop,
+ guint expected_duration)
+{
+ GError *error = NULL;
+ gboolean start_or_stop = FALSE; /* start = TRUE, stop = FALSE */
+ guint duration = 0;
+ GRegex *voice_call_regex = NULL;
+ gboolean result;
+ GMatchInfo *match_info = NULL;
+
+ voice_call_regex = mm_simtech_get_voice_call_urc_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (voice_call_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ result = mm_simtech_parse_voice_call_urc (match_info, &start_or_stop, &duration, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ g_assert_cmpuint (expected_start_or_stop, ==, start_or_stop);
+ g_assert_cmpuint (expected_duration, ==, duration);
+
+ g_match_info_free (match_info);
+ g_regex_unref (voice_call_regex);
+}
+
+static void
+test_voice_call_begin_urc (void)
+{
+ common_test_voice_call_urc ("\r\nVOICE CALL: BEGIN\r\n", TRUE, 0);
+}
+
+static void
+test_voice_call_end_urc (void)
+{
+ common_test_voice_call_urc ("\r\nVOICE CALL: END\r\n", FALSE, 0);
+}
+
+static void
+test_voice_call_end_duration_urc (void)
+{
+ common_test_voice_call_urc ("\r\nVOICE CALL: END: 000041\r\n", FALSE, 41);
+}
+
+/*****************************************************************************/
+
+static void
+common_test_missed_call_urc (const gchar *urc,
+ const gchar *expected_details)
+{
+ GError *error = NULL;
+ gchar *details = NULL;
+ GRegex *missed_call_regex = NULL;
+ gboolean result;
+ GMatchInfo *match_info = NULL;
+
+ missed_call_regex = mm_simtech_get_missed_call_urc_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (missed_call_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ result = mm_simtech_parse_missed_call_urc (match_info, &details, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ g_assert_cmpstr (expected_details, ==, details);
+ g_free (details);
+
+ g_match_info_free (match_info);
+ g_regex_unref (missed_call_regex);
+}
+
+static void
+test_missed_call_urc (void)
+{
+ common_test_missed_call_urc ("\r\nMISSED_CALL: 11:01AM 07712345678\r\n", "11:01AM 07712345678");
+}
+
+/*****************************************************************************/
+
+static void
+common_test_cring_urc (const gchar *urc,
+ const gchar *expected_type)
+{
+ GError *error = NULL;
+ GRegex *cring_regex = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *type;
+ gboolean result;
+
+ cring_regex = mm_simtech_get_cring_urc_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (cring_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ type = g_match_info_fetch (match_info, 1);
+ g_assert (type);
+
+ g_assert_cmpstr (type, ==, expected_type);
+
+ g_match_info_free (match_info);
+ g_regex_unref (cring_regex);
+ g_free (type);
+}
+
+static void
+test_cring_urc_two_crs (void)
+{
+ common_test_cring_urc ("\r\r\n+CRING: VOICE\r\r\n", "VOICE");
+}
+
+static void
+test_cring_urc_one_cr (void)
+{
+ common_test_cring_urc ("\r\n+CRING: VOICE\r\n", "VOICE");
+}
+
+/*****************************************************************************/
+
+static void
+common_test_rxdtmf_urc (const gchar *urc,
+ const gchar *expected_str)
+{
+ GError *error = NULL;
+ GRegex *rxdtmf_regex = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *type;
+ gboolean result;
+
+ rxdtmf_regex = mm_simtech_get_rxdtmf_urc_regex ();
+
+ /* Same matching logic as done in MMSerialPortAt when processing URCs! */
+ result = g_regex_match_full (rxdtmf_regex, urc, -1, 0, 0, &match_info, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ type = g_match_info_fetch (match_info, 1);
+ g_assert (type);
+
+ g_assert_cmpstr (type, ==, expected_str);
+
+ g_match_info_free (match_info);
+ g_regex_unref (rxdtmf_regex);
+ g_free (type);
+}
+
+static void
+test_rxdtmf_urc_two_crs (void)
+{
+ common_test_rxdtmf_urc ("\r\r\n+RXDTMF: 8\r\r\n", "8");
+ common_test_rxdtmf_urc ("\r\r\n+RXDTMF: *\r\r\n", "*");
+ common_test_rxdtmf_urc ("\r\r\n+RXDTMF: #\r\r\n", "#");
+ common_test_rxdtmf_urc ("\r\r\n+RXDTMF: A\r\r\n", "A");
+}
+
+static void
+test_rxdtmf_urc_one_cr (void)
+{
+ common_test_rxdtmf_urc ("\r\n+RXDTMF: 8\r\n", "8");
+ common_test_rxdtmf_urc ("\r\n+RXDTMF: *\r\n", "*");
+ common_test_rxdtmf_urc ("\r\n+RXDTMF: #\r\n", "#");
+ common_test_rxdtmf_urc ("\r\n+RXDTMF: A\r\n", "A");
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/simtech/clcc/urc/single", test_clcc_urc_single);
+ g_test_add_func ("/MM/simtech/clcc/urc/multiple", test_clcc_urc_multiple);
+ g_test_add_func ("/MM/simtech/clcc/urc/complex", test_clcc_urc_complex);
+
+ g_test_add_func ("/MM/simtech/voicecall/urc/begin", test_voice_call_begin_urc);
+ g_test_add_func ("/MM/simtech/voicecall/urc/end", test_voice_call_end_urc);
+ g_test_add_func ("/MM/simtech/voicecall/urc/end-duration", test_voice_call_end_duration_urc);
+
+ g_test_add_func ("/MM/simtech/missedcall/urc", test_missed_call_urc);
+
+ g_test_add_func ("/MM/simtech/cring/urc/two-crs", test_cring_urc_two_crs);
+ g_test_add_func ("/MM/simtech/cring/urc/one-cr", test_cring_urc_one_cr);
+
+ g_test_add_func ("/MM/simtech/rxdtmf/urc/two-crs", test_rxdtmf_urc_two_crs);
+ g_test_add_func ("/MM/simtech/rxdtmf/urc/one-cr", test_rxdtmf_urc_one_cr);
+
+ return g_test_run ();
+}
diff --git a/plugins/symbol.map b/plugins/symbol.map
new file mode 100644
index 00000000..b2c9f9cf
--- /dev/null
+++ b/plugins/symbol.map
@@ -0,0 +1,8 @@
+{
+global:
+ mm_plugin_major_version*;
+ mm_plugin_minor_version*;
+ mm_plugin_create*;
+local:
+ *;
+};
diff --git a/plugins/telit/77-mm-telit-port-types.rules b/plugins/telit/77-mm-telit-port-types.rules
index f06822e1..212cce40 100644
--- a/plugins/telit/77-mm-telit-port-types.rules
+++ b/plugins/telit/77-mm-telit-port-types.rules
@@ -1,48 +1,143 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_telit_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_telit_port_types_end"
+ACTION!="add|change|move|bind", GOTO="mm_telit_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bc7", GOTO="mm_telit_generic"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="8087", GOTO="mm_telit_intel"
+GOTO="mm_telit_end"
-SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bc7", GOTO="mm_telit_vendorcheck"
-GOTO="mm_telit_port_types_end"
-
-LABEL="mm_telit_vendorcheck"
+LABEL="mm_telit_generic"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# UC864-E, UC864-E-AUTO, UC864-K, UC864-WD, UC864-WDU
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_TELIT_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{ID_MM_TELIT_TAGGED}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1003", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# UC864-G
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_TELIT_PORT_TYPE_NMEA}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_TELIT_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{ID_MM_TELIT_TAGGED}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1004", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# CC864-DUAL
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_TELIT_PORT_TYPE_NMEA}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_TELIT_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{ID_MM_TELIT_TAGGED}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1005", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# CC864-SINGLE, CC864-KPS
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_TELIT_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{ID_MM_TELIT_TAGGED}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1006", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# DE910-DUAL
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_TELIT_PORT_TYPE_NMEA}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_TELIT_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{ID_MM_TELIT_TAGGED}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
# CE910-DUAL
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_TELIT_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{ID_MM_TELIT_TAGGED}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1011", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# LE922, LM9x0
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LE922, LM9x0 (MBIM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# FN980
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# FN980 (MBIM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LN920
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LN920 (MBIM composition)
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{.MM_USBIFNUM}=="07", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LE910C1 with default usb cfg
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1201", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+# ME910C1
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1101", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# MEx10G1
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+# LE910S1 (RNDIS)
+# The following port is ignored since it's a diagnostic port for collecting proprietary modem traces
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7010", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# LE910S1 (ECM)
+# The following port is ignored since it's a diagnostic port for collecting proprietary modem traces
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="7011", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+# LM940/960 use alternate settings for 3G band management
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1042", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1043", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+
+# FN980 use alternate settings for 3G band management
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+
+# LN920 use alternate settings for 3G band management
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{ID_MM_TELIT_BND_ALTERNATE}="1"
+
+# LM940/960 initial port delay
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1040", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1041", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1042", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1043", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+
+# FN980 initial port delay
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1050", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1051", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+
+# LN920 initial port delay
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1060", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+ATTRS{idVendor}=="1bc7", ATTRS{idProduct}=="1061", ENV{ID_MM_TELIT_PORT_DELAY}="1"
+
+GOTO="mm_telit_end"
+
+LABEL="mm_telit_intel"
-# NOTE: Qualcomm Gobi-based devices like the LE920 should not be handled
-# by this plugin, but by the Gobi plugin.
+# Telit LN930, generic Intel vid:pid in MBIM mode
+ATTRS{idVendor}=="8087", ATTRS{idProduct}=="0911", ENV{ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED}="1"
-GOTO="mm_telit_port_types_end"
-LABEL="mm_telit_port_types_end"
+LABEL="mm_telit_end"
diff --git a/plugins/telit/mm-broadband-modem-mbim-telit.c b/plugins/telit/mm-broadband-modem-mbim-telit.c
new file mode 100644
index 00000000..3ac17bce
--- /dev/null
+++ b/plugins/telit/mm-broadband-modem-mbim-telit.c
@@ -0,0 +1,192 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-iface-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-broadband-modem-mbim-telit.h"
+#include "mm-modem-helpers-telit.h"
+#include "mm-shared-telit.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void shared_telit_init (MMSharedTelit *iface);
+
+static MMIfaceModem *iface_modem_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimTelit, mm_broadband_modem_mbim_telit, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_TELIT, shared_telit_init))
+
+/*****************************************************************************/
+/* Load supported modes (Modem interface) */
+
+static GArray *
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemModeCombination modes_combination;
+ MMModemMode modes_mask = MM_MODEM_MODE_NONE;
+ const gchar *response;
+ GArray *modes;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+ GError *error = NULL;
+ guint i;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_prefix_error (&error, "generic query of supported 3GPP networks with WS46=? failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ modes = mm_3gpp_parse_ws46_test_response (response, self, &error);
+ if (!modes) {
+ g_prefix_error (&error, "parsing WS46=? response failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (i = 0; i < modes->len; i++) {
+ MMModemMode mode;
+ g_autofree gchar *str = NULL;
+
+ mode = g_array_index (modes, MMModemMode, i);
+
+ modes_mask |= mode;
+
+ str = mm_modem_mode_build_string_from_mask (mode);
+ mm_obj_dbg (self, "device allows (3GPP) mode combination: %s", str);
+ }
+
+ g_array_unref (modes);
+
+ /* Build a mask with all supported modes */
+ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ modes_combination.allowed = modes_mask;
+ modes_combination.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (all, modes_combination);
+
+ /* Filter out those unsupported modes */
+ combinations = mm_telit_build_modes_list();
+ filtered = mm_filter_supported_modes (all, combinations, self);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+WS46=?",
+ 3,
+ TRUE, /* allow caching, it's a test command */
+ (GAsyncReadyCallback) load_supported_modes_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemMbimTelit *
+mm_broadband_modem_mbim_telit_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_TELIT,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_mbim_telit_init (MMBroadbandModemMbimTelit *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->set_current_bands = mm_shared_telit_modem_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_telit_modem_set_current_bands_finish;
+ iface->load_current_bands = mm_shared_telit_modem_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_telit_modem_load_current_bands_finish;
+ iface->load_supported_bands = mm_shared_telit_modem_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_telit_modem_load_supported_bands_finish;
+ iface->load_supported_modes = load_supported_modes;
+ iface->load_supported_modes_finish = load_supported_modes_finish;
+ iface->load_current_modes = mm_shared_telit_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_telit_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_telit_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_telit_set_current_modes_finish;
+}
+
+static MMIfaceModem *
+peek_parent_modem_interface (MMSharedTelit *self)
+{
+ return iface_modem_parent;
+}
+
+static void
+shared_telit_init (MMSharedTelit *iface)
+{
+ iface->peek_parent_modem_interface = peek_parent_modem_interface;
+}
+
+static void
+mm_broadband_modem_mbim_telit_class_init (MMBroadbandModemMbimTelitClass *klass)
+{
+}
diff --git a/plugins/telit/mm-broadband-modem-mbim-telit.h b/plugins/telit/mm-broadband-modem-mbim-telit.h
new file mode 100644
index 00000000..42b31746
--- /dev/null
+++ b/plugins/telit/mm-broadband-modem-mbim-telit.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#ifndef MM_BROADBAND_MODEM_MBIM_TELIT_H
+#define MM_BROADBAND_MODEM_MBIM_TELIT_H
+
+#include "mm-broadband-modem-mbim.h"
+
+#define MM_TYPE_BROADBAND_MODEM_MBIM_TELIT (mm_broadband_modem_mbim_telit_get_type ())
+#define MM_BROADBAND_MODEM_MBIM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelit))
+#define MM_BROADBAND_MODEM_MBIM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelitClass))
+#define MM_IS_BROADBAND_MODEM_MBIM_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT))
+#define MM_IS_BROADBAND_MODEM_MBIM_TELIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT))
+#define MM_BROADBAND_MODEM_MBIM_TELIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_TELIT, MMBroadbandModemMbimTelitClass))
+
+typedef struct _MMBroadbandModemMbimTelit MMBroadbandModemMbimTelit;
+typedef struct _MMBroadbandModemMbimTelitClass MMBroadbandModemMbimTelitClass;
+
+struct _MMBroadbandModemMbimTelit {
+ MMBroadbandModemMbim parent;
+};
+
+struct _MMBroadbandModemMbimTelitClass{
+ MMBroadbandModemMbimClass parent;
+};
+
+GType mm_broadband_modem_mbim_telit_get_type (void);
+
+MMBroadbandModemMbimTelit *mm_broadband_modem_mbim_telit_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_TELIT_H */
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
index 5e1dbff0..4e8a1912 100644
--- a/plugins/telit/mm-broadband-modem-telit.c
+++ b/plugins/telit/mm-broadband-modem-telit.c
@@ -24,19 +24,1069 @@
#include <ctype.h>
#include "ModemManager.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-location.h"
#include "mm-broadband-modem-telit.h"
+#include "mm-modem-helpers-telit.h"
+#include "mm-telit-enums-types.h"
+#include "mm-shared-telit.h"
static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void shared_telit_init (MMSharedTelit *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+
+static MMIfaceModem *iface_modem_parent;
+static MMIfaceModem3gpp *iface_modem_3gpp_parent;
+static MMIfaceModemLocation *iface_modem_location_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemTelit, mm_broadband_modem_telit, MM_TYPE_BROADBAND_MODEM, 0,
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init));
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_TELIT, shared_telit_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init));
+
+#define CSIM_UNLOCK_MAX_TIMEOUT 3
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_NOT_SUPPORTED,
+ FEATURE_SUPPORTED
+} FeatureSupport;
+
+struct _MMBroadbandModemTelitPrivate {
+ FeatureSupport csim_lock_support;
+ MMTelitQssStatus qss_status;
+ MMTelitCsimLockState csim_lock_state;
+ GTask *csim_lock_task;
+ guint csim_lock_timeout_id;
+ gboolean parse_qss;
+ MMModemLocationSource enabled_sources;
+};
+
+
+typedef struct {
+ MMModemLocationSource source;
+ guint gps_enable_step;
+} LocationGatheringContext;
+
+/*
+ * AT$GPSNMUN
+ * enable: 0 NMEA stream disabled (default)
+ * 1 NMEA stream enabled in the form $GPSNMUN: <nmea sentence><CR>
+ * 2 NMEA stream enabled in the form <nmea sentence><CR>
+ * 3 dedicated NMEA stream
+ * GGA: 0 disable (default), 1 enable
+ * GLL: 0 disable (default), 1 enable
+ * GSA: 0 disable (default), 1 enable
+ * GSV: 0 disable (default), 1 enable
+ * RMC: 0 disable (default), 1 enable
+ * VTG: 0 disable (default), 1 enable
+ */
+static const gchar *gps_enable[] = {
+ "$GPSP=1",
+ "$GPSNMUN=2,1,1,1,1,1,1"
+};
+
+static gboolean
+disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+gps_disabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LocationGatheringContext *ctx;
+ MMPortSerialGps *gps_port;
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ ctx = g_task_get_task_data (task);
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ /* Even if we get an error here, we try to close the GPS port */
+ gps_port = mm_base_modem_peek_port_gps (self);
+ if (gps_port)
+ mm_port_serial_close (MM_PORT_SERIAL (gps_port));
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemTelit *telit = MM_BROADBAND_MODEM_TELIT (self);
+ gboolean stop_gps = FALSE;
+ LocationGatheringContext *ctx;
+ GTask *task;
+
+ ctx = g_new (LocationGatheringContext, 1);
+ ctx->source = source;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ /* Only stop GPS engine if no GPS-related sources enabled */
+ if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ telit->priv->enabled_sources &= ~source;
+
+ if (!(telit->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ stop_gps = TRUE;
+ }
+
+ if (stop_gps) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$GPSP=0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_disabled_ready,
+ task);
+ return;
+ }
+ /* For any other location (e.g. 3GPP), or if still some GPS needed, just return */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+gps_enabled_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LocationGatheringContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ g_prefix_error (&error, "couldn't power up GNSS controller: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* After Receiver was powered up we still have to enable unsolicited NMEA events */
+ if (ctx->gps_enable_step < G_N_ELEMENTS (gps_enable)) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ gps_enable[ctx->gps_enable_step++],
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_enabled_ready,
+ task);
+ return;
+ }
+
+ mm_obj_dbg (self, "GNSS controller is powered up");
+
+ /* Only use the GPS port in NMEA/RAW setups */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ MMPortSerialGps *gps_port;
+
+ gps_port = mm_base_modem_peek_port_gps (self);
+ if (!gps_port ||
+ !mm_port_serial_open (MM_PORT_SERIAL (gps_port), &error)) {
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't open raw GPS serial port");
+ } else
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemTelit *self = MM_BROADBAND_MODEM_TELIT (_self);
+ LocationGatheringContext *ctx;
+ gboolean start_gps = FALSE;
+ GError *error = NULL;
+
+ if (!iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* Now our own enabling */
+ ctx = g_task_get_task_data (task);
+
+ /* NMEA, RAW and UNMANAGED are all enabled in the same way */
+ if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
+ /* Only start GPS engine if not done already */
+ if (!(self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)))
+ start_gps = TRUE;
+ self->priv->enabled_sources |= ctx->source;
+ }
+
+ if (start_gps && ctx->gps_enable_step < G_N_ELEMENTS (gps_enable)) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ gps_enable[ctx->gps_enable_step++],
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)gps_enabled_ready,
+ task);
+ return;
+ }
+ /* For any other location (e.g. 3GPP), or if GPS already running just return */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LocationGatheringContext *ctx;
+ GTask *task;
+
+ ctx = g_new (LocationGatheringContext, 1);
+ ctx->source = source;
+ ctx->gps_enable_step = 0;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ /* Chain up parent's gathering enable */
+ iface_modem_location_parent->enable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+}
+
+static void
+trace_received (MMPortSerialGps *port,
+ const gchar *trace,
+ MMIfaceModemLocation *self)
+{
+ mm_iface_modem_location_gps_update (self, trace);
+}
+
+static gboolean
+enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+setup_ports (MMBroadbandModem *self)
+{
+ MMPortSerialGps *gps_data_port;
+
+ /* Call parent's setup ports first always */
+ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_telit_parent_class)->setup_ports (self);
+
+ gps_data_port = mm_base_modem_peek_port_gps (MM_BASE_MODEM (self));
+ if (gps_data_port) {
+ /* It may happen that the modem was started with GPS already enabled,
+ * in this case GPSP AT command returns always error. Disable it for consistency
+ */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$GPSP=0", 3, FALSE, FALSE, NULL);
+
+ /* Add handler for the NMEA traces */
+ mm_port_serial_gps_add_trace_handler (gps_data_port,
+ (MMPortSerialGpsTraceFn)trace_received,
+ self,
+ NULL);
+ }
+}
+
+static MMModemLocationSource
+location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+gpsp_test_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMModemLocationSource sources;
+
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ mm_obj_dbg (self, "GPS controller not supported: %s", error->message);
+ g_clear_error (&error);
+ } else if (mm_base_modem_peek_port_gps (MM_BASE_MODEM (self)))
+ sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED);
+
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+
+ sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "$GPSP=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)gpsp_test_ready,
+ task);
+}
+
+static void
+location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Chain up parent's setup */
+ iface_modem_location_parent->load_capabilities (
+ self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* After Sim Unlock (Modem interface) */
+
+static gboolean
+modem_after_sim_unlock_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+after_sim_unlock_ready (GTask *task)
+{
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+modem_after_sim_unlock (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* A short delay is necessary with some SIMs when
+ they have just been unlocked. Using 1 second as secure margin. */
+ g_timeout_add_seconds (1, (GSourceFunc) after_sim_unlock_ready, task);
+}
+
+/*****************************************************************************/
+/* Setup SIM hot swap (Modem interface) */
+
+typedef enum {
+ QSS_SETUP_STEP_FIRST,
+ QSS_SETUP_STEP_QUERY,
+ QSS_SETUP_STEP_ENABLE_PRIMARY_PORT,
+ QSS_SETUP_STEP_ENABLE_SECONDARY_PORT,
+ QSS_SETUP_STEP_LAST
+} QssSetupStep;
+
+typedef struct {
+ QssSetupStep step;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ GError *primary_error;
+ GError *secondary_error;
+} QssSetupContext;
+
+static void qss_setup_step (GTask *task);
+static void pending_csim_unlock_complete (MMBroadbandModemTelit *self);
+
+static void
+telit_qss_unsolicited_handler (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemTelit *self)
+{
+ MMTelitQssStatus cur_qss_status;
+ MMTelitQssStatus prev_qss_status;
+
+ if (!mm_get_int_from_match_info (match_info, 1, (gint*)&cur_qss_status))
+ return;
+
+ prev_qss_status = self->priv->qss_status;
+ self->priv->qss_status = cur_qss_status;
+
+ if (self->priv->csim_lock_state >= CSIM_LOCK_STATE_LOCK_REQUESTED) {
+
+ if (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED) {
+ mm_obj_dbg (self, "QSS handler: #QSS=0 after +CSIM=1: CSIM locked!");
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_LOCKED;
+ }
+
+ if (prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) {
+ mm_obj_dbg (self, "QSS handler: #QSS>=1 after +CSIM=0: CSIM unlocked!");
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_UNLOCKED;
+
+ if (self->priv->csim_lock_timeout_id) {
+ g_source_remove (self->priv->csim_lock_timeout_id);
+ self->priv->csim_lock_timeout_id = 0;
+ }
+
+ pending_csim_unlock_complete (self);
+ }
+
+ return;
+ }
+
+ if (cur_qss_status != prev_qss_status)
+ mm_obj_dbg (self, "QSS handler: status changed %s -> %s",
+ mm_telit_qss_status_get_string (prev_qss_status),
+ mm_telit_qss_status_get_string (cur_qss_status));
+
+ if (self->priv->parse_qss == FALSE) {
+ mm_obj_dbg (self, "QSS handler: message ignored");
+ return;
+ }
+
+ if ((prev_qss_status == QSS_STATUS_SIM_REMOVED && cur_qss_status != QSS_STATUS_SIM_REMOVED) ||
+ (prev_qss_status > QSS_STATUS_SIM_REMOVED && cur_qss_status == QSS_STATUS_SIM_REMOVED)) {
+ mm_obj_info (self, "QSS handler: SIM swap detected");
+ mm_broadband_modem_sim_hot_swap_detected (MM_BROADBAND_MODEM (self));
+ }
+}
+
+static void
+qss_setup_context_free (QssSetupContext *ctx)
+{
+ g_clear_object (&(ctx->primary));
+ g_clear_object (&(ctx->secondary));
+ g_clear_error (&(ctx->primary_error));
+ g_clear_error (&(ctx->secondary_error));
+ g_slice_free (QssSetupContext, ctx);
+}
+
+static gboolean
+modem_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+telit_qss_enable_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QssSetupContext *ctx;
+ MMPortSerialAt *port;
+ GError **error;
+ GRegex *pattern;
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->step == QSS_SETUP_STEP_ENABLE_PRIMARY_PORT) {
+ port = ctx->primary;
+ error = &ctx->primary_error;
+ } else if (ctx->step == QSS_SETUP_STEP_ENABLE_SECONDARY_PORT) {
+ port = ctx->secondary;
+ error = &ctx->secondary_error;
+ } else
+ g_assert_not_reached ();
+
+ if (!mm_base_modem_at_command_full_finish (self, res, error)) {
+ mm_obj_warn (self, "QSS: error enabling unsolicited on port %s: %s", mm_port_get_device (MM_PORT (port)), (*error)->message);
+ goto next_step;
+ }
+
+ pattern = g_regex_new ("#QSS:\\s*([0-3])\\r\\n", G_REGEX_RAW, 0, NULL);
+ g_assert (pattern);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ port,
+ pattern,
+ (MMPortSerialAtUnsolicitedMsgFn)telit_qss_unsolicited_handler,
+ self,
+ NULL);
+ g_regex_unref (pattern);
+
+next_step:
+ ctx->step++;
+ qss_setup_step (task);
+}
+
+static void
+telit_qss_query_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemTelit *self;
+ GError *error = NULL;
+ const gchar *response;
+ MMTelitQssStatus qss_status;
+ QssSetupContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (_self);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "could not get \"#QSS?\" reply: %s", error->message);
+ g_error_free (error);
+ goto next_step;
+ }
+
+ qss_status = mm_telit_parse_qss_query (response, &error);
+ if (error) {
+ mm_obj_warn (self, "QSS query parse error: %s", error->message);
+ g_error_free (error);
+ goto next_step;
+ }
+
+ mm_obj_dbg (self, "QSS: current status is '%s'", mm_telit_qss_status_get_string (qss_status));
+ self->priv->qss_status = qss_status;
+
+next_step:
+ ctx->step++;
+ qss_setup_step (task);
+}
+
+static void
+telit_qss_support_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ QssSetupContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "#QSS command unsupported: '%s'", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ qss_setup_step (task);
+}
+
+static void
+qss_setup_step (GTask *task)
+{
+ QssSetupContext *ctx;
+ MMBroadbandModemTelit *self;
+
+ self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case QSS_SETUP_STEP_FIRST:
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#QSS=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) telit_qss_support_ready,
+ task);
+ return;
+ case QSS_SETUP_STEP_QUERY:
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#QSS?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) telit_qss_query_ready,
+ task);
+ return;
+ case QSS_SETUP_STEP_ENABLE_PRIMARY_PORT:
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->primary,
+ "#QSS=1",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) telit_qss_enable_ready,
+ task);
+ return;
+ case QSS_SETUP_STEP_ENABLE_SECONDARY_PORT:
+ if (ctx->secondary) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->secondary,
+ "#QSS=1",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) telit_qss_enable_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+ case QSS_SETUP_STEP_LAST:
+ /* If all enabling actions failed (either both, or only primary if
+ * there is no secondary), then we return an error */
+ if (ctx->primary_error &&
+ (ctx->secondary_error || !ctx->secondary))
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "QSS: couldn't enable unsolicited");
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QssSetupContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (QssSetupContext);
+ ctx->step = QSS_SETUP_STEP_FIRST;
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify) qss_setup_context_free);
+ qss_setup_step (task);
+}
+
+/*****************************************************************************/
+/* Load unlock retries (Modem interface)
+ *
+ * NOTE: the logic must make sure that LOAD_UNLOCK_RETRIES_STEP_UNLOCK is always
+ * run if LOAD_UNLOCK_RETRIES_STEP_LOCK has been run. Currently, the logic just
+ * runs all intermediate steps ignoring errors (i.e. not completing the
+ * operation if something fails), so the LOAD_UNLOCK_RETRIES_STEP_UNLOCK is
+ * always run.
+ */
+
+#define CSIM_LOCK_STR "+CSIM=1"
+#define CSIM_UNLOCK_STR "+CSIM=0"
+#define CSIM_QUERY_TIMEOUT 3
+
+typedef enum {
+ LOAD_UNLOCK_RETRIES_STEP_FIRST,
+ LOAD_UNLOCK_RETRIES_STEP_LOCK,
+ LOAD_UNLOCK_RETRIES_STEP_PARENT,
+ LOAD_UNLOCK_RETRIES_STEP_UNLOCK,
+ LOAD_UNLOCK_RETRIES_STEP_LAST
+} LoadUnlockRetriesStep;
+
+typedef struct {
+ MMUnlockRetries *retries;
+ LoadUnlockRetriesStep step;
+} LoadUnlockRetriesContext;
+
+static void load_unlock_retries_step (GTask *task);
+
+static void
+load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx)
+{
+ if (ctx->retries)
+ g_object_unref (ctx->retries);
+ g_slice_free (LoadUnlockRetriesContext, ctx);
+}
+
+static MMUnlockRetries *
+modem_load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (MMUnlockRetries *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+csim_unlock_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (_self);
+ ctx = g_task_get_task_data (task);
+
+ /* Ignore errors */
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response) {
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) {
+ self->priv->csim_lock_support = FEATURE_NOT_SUPPORTED;
+ }
+ mm_obj_warn (self, "couldn't unlock SIM card: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (self->priv->csim_lock_support != FEATURE_NOT_SUPPORTED)
+ self->priv->csim_lock_support = FEATURE_SUPPORTED;
+
+ ctx->step++;
+ load_unlock_retries_step (task);
+}
+
+static void
+parent_load_unlock_retries_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadUnlockRetriesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!(ctx->retries = iface_modem_parent->load_unlock_retries_finish (self, res, &error))) {
+ mm_obj_warn (self, "couldn't load unlock retries with generic logic: %s", error->message);
+ g_error_free (error);
+ }
+
+ ctx->step++;
+ load_unlock_retries_step (task);
+}
+
+static void
+csim_lock_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (_self);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response) {
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN)) {
+ self->priv->csim_lock_support = FEATURE_NOT_SUPPORTED;
+ mm_obj_warn (self, "couldn't lock SIM card: %s; continuing without CSIM lock", error->message);
+ g_error_free (error);
+ } else {
+ g_prefix_error (&error, "Couldn't lock SIM card: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ } else {
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_LOCK_REQUESTED;
+ }
+
+ if (self->priv->csim_lock_support != FEATURE_NOT_SUPPORTED) {
+ self->priv->csim_lock_support = FEATURE_SUPPORTED;
+ }
+
+ ctx->step++;
+ load_unlock_retries_step (task);
+}
+
+static void
+handle_csim_locking (GTask *task,
+ gboolean is_lock)
+{
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+
+ switch (self->priv->csim_lock_support) {
+ case FEATURE_SUPPORT_UNKNOWN:
+ case FEATURE_SUPPORTED:
+ if (is_lock) {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ CSIM_LOCK_STR,
+ CSIM_QUERY_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) csim_lock_ready,
+ task);
+ } else {
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ CSIM_UNLOCK_STR,
+ CSIM_QUERY_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) csim_unlock_ready,
+ task);
+ }
+ break;
+ case FEATURE_NOT_SUPPORTED:
+ mm_obj_dbg (self, "CSIM lock not supported by this modem; skipping %s command",
+ is_lock ? "lock" : "unlock");
+ ctx->step++;
+ load_unlock_retries_step (task);
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+pending_csim_unlock_complete (MMBroadbandModemTelit *self)
+{
+ LoadUnlockRetriesContext *ctx;
+
+ ctx = g_task_get_task_data (self->priv->csim_lock_task);
+
+ if (!ctx->retries) {
+ g_task_return_new_error (self->priv->csim_lock_task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not get any of the SIM unlock retries values");
+ } else {
+ g_task_return_pointer (self->priv->csim_lock_task, g_object_ref (ctx->retries), g_object_unref);
+ }
+
+ g_clear_object (&self->priv->csim_lock_task);
+}
+
+static gboolean
+csim_unlock_periodic_check (MMBroadbandModemTelit *self)
+{
+ if (self->priv->csim_lock_state != CSIM_LOCK_STATE_UNLOCKED)
+ mm_obj_warn (self, "CSIM is still locked after %d seconds; trying to continue anyway", CSIM_UNLOCK_MAX_TIMEOUT);
+
+ self->priv->csim_lock_timeout_id = 0;
+ pending_csim_unlock_complete (self);
+ g_object_unref (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+load_unlock_retries_step (GTask *task)
+{
+ MMBroadbandModemTelit *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = MM_BROADBAND_MODEM_TELIT (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+ switch (ctx->step) {
+ case LOAD_UNLOCK_RETRIES_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+ case LOAD_UNLOCK_RETRIES_STEP_LOCK:
+ handle_csim_locking (task, TRUE);
+ break;
+ case LOAD_UNLOCK_RETRIES_STEP_PARENT:
+ iface_modem_parent->load_unlock_retries (
+ MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)parent_load_unlock_retries_ready,
+ task);
+ break;
+ case LOAD_UNLOCK_RETRIES_STEP_UNLOCK:
+ handle_csim_locking (task, FALSE);
+ break;
+ case LOAD_UNLOCK_RETRIES_STEP_LAST:
+ self->priv->csim_lock_task = task;
+ if (self->priv->csim_lock_state == CSIM_LOCK_STATE_LOCKED) {
+ mm_obj_dbg (self, "CSIM is locked, waiting for #QSS=1");
+ self->priv->csim_lock_timeout_id = g_timeout_add_seconds (CSIM_UNLOCK_MAX_TIMEOUT,
+ (GSourceFunc) csim_unlock_periodic_check,
+ g_object_ref(self));
+ } else {
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_UNLOCKED;
+ pending_csim_unlock_complete (self);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+modem_load_unlock_retries (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ LoadUnlockRetriesContext *ctx;
+
+ g_assert (iface_modem_parent->load_unlock_retries);
+ g_assert (iface_modem_parent->load_unlock_retries_finish);
+
+ ctx = g_slice_new0 (LoadUnlockRetriesContext);
+ ctx->step = LOAD_UNLOCK_RETRIES_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free);
+
+ load_unlock_retries_step (task);
+}
+
+/*****************************************************************************/
+/* Modem after power up (Modem interface) */
+
+static gboolean
+modem_after_power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_after_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMBroadbandModemTelit *modem = MM_BROADBAND_MODEM_TELIT (self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "stop ignoring #QSS");
+ modem->priv->parse_qss = TRUE;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Modem power down (Modem interface) */
+
+static gboolean
+modem_power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+telit_modem_power_down_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "sgnore #QSS unsolicited during power down/low");
+ MM_BROADBAND_MODEM_TELIT (self)->priv->parse_qss = FALSE;
+ }
+
+ if (error) {
+ mm_obj_warn (self, "failed modem power down: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN=4",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback) telit_modem_power_down_ready,
+ task);
+}
/*****************************************************************************/
+/* Reset (Modem interface) */
+
+static gboolean
+modem_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT#REBOOT",
+ 8,
+ FALSE,
+ callback,
+ user_data);
+}
+/*****************************************************************************/
/* Load access technologies (Modem interface) */
static gboolean
@@ -60,23 +1110,30 @@ load_access_technologies_finish (MMIfaceModem *self,
return TRUE;
}
-static gboolean
-response_processor_psnt_ignore_at_errors (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+response_processor_psnt_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
- const gchar *psnt, *mode;
+ const gchar *psnt;
+ const gchar *mode;
+
+ *result = NULL;
+ *result_error = NULL;
if (error) {
/* Ignore AT errors (ie, ERROR or CMx ERROR) */
- if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command)
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
*result_error = g_error_copy (error);
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
psnt = mm_strip_tag (response, "#PSNT:");
@@ -85,16 +1142,28 @@ response_processor_psnt_ignore_at_errors (MMBaseModem *self,
switch (atoi (++mode)) {
case 0:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_GPRS);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
case 1:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EDGE);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
case 2:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UMTS);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
case 3:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 4:
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_LTE);
+ else
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ case 5:
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
+ *result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ }
+ /* Fall-through since #PSNT: 5 is not supported in other than lte modems */
default:
break;
}
@@ -105,41 +1174,46 @@ response_processor_psnt_ignore_at_errors (MMBaseModem *self,
MM_CORE_ERROR_FAILED,
"Failed to parse #PSNT response: '%s'",
response);
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
}
-static gboolean
-response_processor_service_ignore_at_errors (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+response_processor_service_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
- const gchar *service, *mode;
+ const gchar *service;
+
+ *result = NULL;
+ *result_error = NULL;
if (error) {
/* Ignore AT errors (ie, ERROR or CMx ERROR) */
- if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command)
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
*result_error = g_error_copy (error);
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
service = mm_strip_tag (response, "+SERVICE:");
- mode = strchr (service, ',');
- if (mode) {
- switch (atoi (++mode)) {
+ if (service) {
+ switch (atoi (service)) {
case 1:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_1XRTT);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
case 2:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EVDO0);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
case 3:
*result = g_variant_new_uint32 (MM_MODEM_ACCESS_TECHNOLOGY_EVDOA);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
default:
break;
}
@@ -150,12 +1224,12 @@ response_processor_service_ignore_at_errors (MMBaseModem *self,
MM_CORE_ERROR_FAILED,
"Failed to parse +SERVICE response: '%s'",
response);
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
}
static const MMBaseModemAtCommand access_tech_commands[] = {
- { "#PSNT?", 3, TRUE, response_processor_psnt_ignore_at_errors },
- { "+SERVICE?", 3, TRUE, response_processor_service_ignore_at_errors },
+ { "#PSNT?", 3, FALSE, response_processor_psnt_ignore_at_errors },
+ { "+SERVICE?", 3, FALSE, response_processor_service_ignore_at_errors },
{ NULL }
};
@@ -164,7 +1238,6 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading access technology (Telit)...");
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
access_tech_commands,
@@ -175,6 +1248,132 @@ load_access_technologies (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Load supported modes (Modem interface) */
+
+static GArray *
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+parent_load_supported_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+
+ all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
+ if (!all) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* CDMA-only modems don't support changing modes, default to parent's */
+ if (!mm_iface_modem_is_3gpp (self)) {
+ g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Filter out those unsupported modes */
+ combinations = mm_telit_build_modes_list();
+ filtered = mm_filter_supported_modes (all, combinations, self);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Run parent's loading */
+ iface_modem_parent->load_supported_modes (
+ MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)parent_load_supported_modes_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Enabling unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cind_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't enable custom +CIND settings: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't enable parent 3GPP unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Our own enable now */
+ mm_base_modem_at_command_full (
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)),
+ /* Enable +CIEV only for: signal, service, roam */
+ "AT+CIND=0,1,1,0,0,0,1,0,0",
+ 5,
+ FALSE,
+ FALSE,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)cind_set_ready,
+ task);
+}
+
+static void
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's enable */
+ iface_modem_3gpp_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
MMBroadbandModemTelit *
mm_broadband_modem_telit_new (const gchar *device,
@@ -189,22 +1388,93 @@ mm_broadband_modem_telit_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports AT only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
NULL);
}
static void
mm_broadband_modem_telit_init (MMBroadbandModemTelit *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_MODEM_TELIT,
+ MMBroadbandModemTelitPrivate);
+
+ self->priv->csim_lock_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->csim_lock_state = CSIM_LOCK_STATE_UNKNOWN;
+ self->priv->qss_status = QSS_STATUS_UNKNOWN;
+ self->priv->parse_qss = TRUE;
}
static void
iface_modem_init (MMIfaceModem *iface)
{
+ iface_modem_parent = g_type_interface_peek_parent (iface);
+
+ iface->set_current_bands = mm_shared_telit_modem_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_telit_modem_set_current_bands_finish;
+ iface->load_current_bands = mm_shared_telit_modem_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_telit_modem_load_current_bands_finish;
+ iface->load_supported_bands = mm_shared_telit_modem_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_telit_modem_load_supported_bands_finish;
+ iface->load_unlock_retries_finish = modem_load_unlock_retries_finish;
+ iface->load_unlock_retries = modem_load_unlock_retries;
+ iface->reset = modem_reset;
+ iface->reset_finish = modem_reset_finish;
+ iface->modem_after_power_up = modem_after_power_up;
+ iface->modem_after_power_up_finish = modem_after_power_up_finish;
+ iface->modem_power_down = modem_power_down;
+ iface->modem_power_down_finish = modem_power_down_finish;
iface->load_access_technologies = load_access_technologies;
iface->load_access_technologies_finish = load_access_technologies_finish;
+ iface->load_supported_modes = load_supported_modes;
+ iface->load_supported_modes_finish = load_supported_modes_finish;
+ iface->load_current_modes = mm_shared_telit_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_telit_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_telit_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_telit_set_current_modes_finish;
+ iface->modem_after_sim_unlock = modem_after_sim_unlock;
+ iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish;
+ iface->setup_sim_hot_swap = modem_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish;
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+ iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+
+ iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
+}
+
+static void
+shared_telit_init (MMSharedTelit *iface)
+{
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = location_load_capabilities;
+ iface->load_capabilities_finish = location_load_capabilities_finish;
+ iface->enable_location_gathering = enable_location_gathering;
+ iface->enable_location_gathering_finish = enable_location_gathering_finish;
+ iface->disable_location_gathering = disable_location_gathering;
+ iface->disable_location_gathering_finish = disable_location_gathering_finish;
}
static void
mm_broadband_modem_telit_class_init (MMBroadbandModemTelitClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandModemTelitPrivate));
+ broadband_modem_class->setup_ports = setup_ports;
}
diff --git a/plugins/telit/mm-broadband-modem-telit.h b/plugins/telit/mm-broadband-modem-telit.h
index 50e6365f..f68465e7 100644
--- a/plugins/telit/mm-broadband-modem-telit.h
+++ b/plugins/telit/mm-broadband-modem-telit.h
@@ -29,9 +29,11 @@
typedef struct _MMBroadbandModemTelit MMBroadbandModemTelit;
typedef struct _MMBroadbandModemTelitClass MMBroadbandModemTelitClass;
+typedef struct _MMBroadbandModemTelitPrivate MMBroadbandModemTelitPrivate;
struct _MMBroadbandModemTelit {
MMBroadbandModem parent;
+ MMBroadbandModemTelitPrivate *priv;
};
struct _MMBroadbandModemTelitClass{
diff --git a/plugins/telit/mm-common-telit.c b/plugins/telit/mm-common-telit.c
new file mode 100644
index 00000000..2e0b3801
--- /dev/null
+++ b/plugins/telit/mm-common-telit.c
@@ -0,0 +1,375 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+
+#include "mm-common-telit.h"
+#include "mm-log-object.h"
+#include "mm-serial-parsers.h"
+
+/*****************************************************************************/
+
+#define TAG_GETPORTCFG_SUPPORTED "getportcfg-supported"
+
+#define TAG_TELIT_MODEM_PORT "ID_MM_TELIT_PORT_TYPE_MODEM"
+#define TAG_TELIT_AUX_PORT "ID_MM_TELIT_PORT_TYPE_AUX"
+#define TAG_TELIT_NMEA_PORT "ID_MM_TELIT_PORT_TYPE_NMEA"
+
+#define TELIT_GE910_FAMILY_PID 0x0022
+
+/* The following number of retries of the port responsiveness
+ * check allows having up to 30 seconds of wait, that should
+ * be fine for most of the modems */
+#define TELIT_PORT_CHECK_RETRIES 6
+
+gboolean
+telit_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error)
+{
+ MMKernelDevice *port;
+ MMDevice *device;
+ MMPortType ptype;
+ MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ const gchar *subsys;
+
+ port = mm_port_probe_peek_port (probe);
+ ptype = mm_port_probe_get_port_type (probe);
+ device = mm_port_probe_peek_device (probe);
+ subsys = mm_port_probe_get_port_subsys (probe);
+
+ /* Just skip custom port identification for subsys different than tty */
+ if (!g_str_equal (subsys, "tty"))
+ goto out;
+
+ /* AT#PORTCFG (if supported) can be used for identifying the port layout */
+ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) != NULL) {
+ guint usbif;
+
+ usbif = (guint) mm_kernel_device_get_interface_number (port);
+ if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT))) {
+ mm_obj_dbg (self, "AT port '%s/%s' flagged as primary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ } else if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_AUX_PORT))) {
+ mm_obj_dbg (self, "AT port '%s/%s' flagged as secondary",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ } else if (usbif == GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT))) {
+ mm_obj_dbg (self, "port '%s/%s' flagged as NMEA",
+ mm_port_probe_get_port_subsys (probe),
+ mm_port_probe_get_port_name (probe));
+ ptype = MM_PORT_TYPE_GPS;
+ } else
+ ptype = MM_PORT_TYPE_IGNORED;
+ }
+
+out:
+ return mm_base_modem_grab_port (modem,
+ port,
+ ptype,
+ pflags,
+ error);
+}
+
+/*****************************************************************************/
+/* Custom init */
+
+typedef struct {
+ MMPortSerialAt *port;
+ gboolean getportcfg_done;
+ guint getportcfg_retries;
+ guint port_responsive_retries;
+} TelitCustomInitContext;
+
+gboolean
+telit_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void telit_custom_init_step (GTask *task);
+
+static gboolean
+cache_port_mode (MMPortProbe *probe,
+ MMDevice *device,
+ const gchar *reply)
+{
+ GRegex *r = NULL;
+ GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW;
+ GMatchInfo *match_info = NULL;
+ GError *error = NULL;
+ gboolean ret = FALSE;
+ guint portcfg_current;
+
+ /* #PORTCFG: <requested>,<active> */
+ r = g_regex_new ("#PORTCFG:\\s*(\\d+),(\\d+)", flags, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &error))
+ goto out;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &portcfg_current)) {
+ mm_obj_dbg (probe, "unrecognized #PORTCFG <active> value");
+ goto out;
+ }
+
+ /* Reference for port configurations:
+ * HE910/UE910/UL865 Families Ports Arrangements User Guide
+ * GE910 Family Ports Arrangements User Guide
+ */
+ switch (portcfg_current) {
+ case 0:
+ case 1:
+ case 4:
+ case 5:
+ case 7:
+ case 9:
+ case 10:
+ case 11:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00));
+ if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID)
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x02));
+ else
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x06));
+ break;
+ case 2:
+ case 3:
+ case 6:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00));
+ break;
+ case 8:
+ case 12:
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_MODEM_PORT, GUINT_TO_POINTER (0x00));
+ if (mm_device_get_product (device) == TELIT_GE910_FAMILY_PID) {
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x02));
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, GUINT_TO_POINTER (0x04));
+ } else {
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_AUX_PORT, GUINT_TO_POINTER (0x06));
+ g_object_set_data (G_OBJECT (device), TAG_TELIT_NMEA_PORT, GUINT_TO_POINTER (0x0a));
+ }
+ break;
+ default:
+ /* portcfg value not supported */
+ goto out;
+ }
+ ret = TRUE;
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+ if (error != NULL) {
+ mm_obj_dbg (probe, "error while matching #PORTCFG: %s", error->message);
+ g_error_free (error);
+ }
+ return ret;
+}
+
+static void
+getportcfg_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMPortProbe *probe;
+ TelitCustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ response = mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ mm_obj_dbg (probe, "couldn't get telit port mode: '%s'", error->message);
+
+ /* If ERROR or COMMAND NOT SUPPORT occur then do not retry the
+ * command.
+ */
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN))
+ ctx->getportcfg_done = TRUE;
+ } else {
+ MMDevice *device;
+
+ device = mm_port_probe_peek_device (probe);
+
+ /* Results are cached in the parent device object */
+ if (g_object_get_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED) == NULL) {
+ mm_obj_dbg (probe, "retrieving telit port mode layout");
+ if (cache_port_mode (probe, device, response)) {
+ g_object_set_data (G_OBJECT (device), TAG_GETPORTCFG_SUPPORTED, GUINT_TO_POINTER (TRUE));
+ ctx->getportcfg_done = TRUE;
+ }
+ }
+
+ /* Port answered to #PORTCFG, so mark it as being AT already */
+ mm_port_probe_set_result_at (probe, TRUE);
+ }
+
+ if (error)
+ g_error_free (error);
+
+ telit_custom_init_step (task);
+}
+
+static void
+telit_custom_init_context_free (TelitCustomInitContext *ctx)
+{
+ g_object_unref (ctx->port);
+ g_slice_free (TelitCustomInitContext, ctx);
+}
+
+static void
+telit_custom_init_step (GTask *task)
+{
+ MMKernelDevice *port;
+ MMPortProbe *probe;
+ TelitCustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ /* If cancelled, end */
+ if (g_cancellable_is_cancelled (g_task_get_cancellable (task))) {
+ mm_obj_dbg (probe, "no need to keep on running custom init");
+ goto out;
+ }
+
+ /* Try to get a port configuration from the modem: usb interface 00
+ * is always linked to an AT port
+ */
+ port = mm_port_probe_peek_port (probe);
+ if (!ctx->getportcfg_done && mm_kernel_device_get_interface_number (port) == 0) {
+ if (ctx->getportcfg_retries == 0)
+ goto out;
+ ctx->getportcfg_retries--;
+
+ mm_port_serial_at_command (
+ ctx->port,
+ "AT#PORTCFG?",
+ 2,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)getportcfg_ready,
+ task);
+ return;
+ }
+
+out:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void at_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task);
+
+static void
+wait_for_ready (GTask *task)
+{
+ TelitCustomInitContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->port_responsive_retries == 0) {
+ telit_custom_init_step (task);
+ return;
+ }
+ ctx->port_responsive_retries--;
+
+ mm_port_serial_at_command (
+ ctx->port,
+ "AT",
+ 5,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)at_ready,
+ task);
+}
+
+static void
+at_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortProbe *probe;
+ g_autoptr(GError) error = NULL;
+
+ probe = g_task_get_source_object (task);
+
+ mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* On a timeout or send error, wait */
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT) ||
+ g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED)) {
+ wait_for_ready (task);
+ return;
+ }
+ /* On an unknown error, make it fatal */
+ if (!mm_serial_parser_v1_is_known_error (error)) {
+ mm_obj_warn (probe, "custom port initialization logic failed: %s", error->message);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ /* When successful mark the port as AT and continue checking #PORTCFG */
+ mm_obj_dbg (probe, "port is AT");
+ mm_port_probe_set_result_at (probe, TRUE);
+ telit_custom_init_step (task);
+}
+
+void
+telit_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ TelitCustomInitContext *ctx;
+ GTask *task;
+ gboolean wait_needed;
+
+ ctx = g_slice_new (TelitCustomInitContext);
+ ctx->port = g_object_ref (port);
+ ctx->getportcfg_done = FALSE;
+ ctx->getportcfg_retries = 3;
+ ctx->port_responsive_retries = TELIT_PORT_CHECK_RETRIES;
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)telit_custom_init_context_free);
+
+ /* Some Telit modems require an initial delay for the ports to be responsive
+ * If no explicit tag is present, the modem does not need this step
+ * and can directly look for #PORTCFG support */
+ wait_needed = mm_kernel_device_get_global_property_as_boolean (mm_port_probe_peek_port (probe),
+ "ID_MM_TELIT_PORT_DELAY");
+ if (wait_needed) {
+ mm_obj_dbg (probe, "Start polling for port responsiveness");
+ wait_for_ready (task);
+ return;
+ }
+
+ telit_custom_init_step (task);
+}
diff --git a/plugins/telit/mm-common-telit.h b/plugins/telit/mm-common-telit.h
new file mode 100644
index 00000000..0c5dbe7a
--- /dev/null
+++ b/plugins/telit/mm-common-telit.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_COMMON_TELIT_H
+#define MM_COMMON_TELIT_H
+
+#include "glib.h"
+#include "mm-plugin.h"
+
+gboolean
+telit_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error);
+
+void
+telit_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean
+telit_grab_port (MMPlugin *self,
+ MMBaseModem *modem,
+ MMPortProbe *probe,
+ GError **error);
+
+#endif /* MM_COMMON_TELIT_H */
diff --git a/plugins/telit/mm-modem-helpers-telit.c b/plugins/telit/mm-modem-helpers-telit.c
new file mode 100644
index 00000000..d163bf1f
--- /dev/null
+++ b/plugins/telit/mm-modem-helpers-telit.c
@@ -0,0 +1,854 @@
+/* -*- 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) 2015-2019 Telit.
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-telit.h"
+
+/*****************************************************************************/
+/* AT#BND 2G values */
+
+#define MM_MODEM_BAND_TELIT_2G_FIRST MM_MODEM_BAND_EGSM
+#define MM_MODEM_BAND_TELIT_2G_LAST MM_MODEM_BAND_G850
+
+#define B2G_FLAG(band) (1 << (band - MM_MODEM_BAND_TELIT_2G_FIRST))
+
+/* Index of the array is the telit 2G band value [0-5]
+ * The bitmask value here is built from the 2G MMModemBand values right away. */
+static const guint32 telit_2g_to_mm_band_mask[] = {
+ [0] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS),
+ [1] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_PCS),
+ [2] = B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_G850),
+ [3] = B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850),
+ [4] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS),
+ [5] = B2G_FLAG (MM_MODEM_BAND_EGSM) + B2G_FLAG (MM_MODEM_BAND_DCS) + B2G_FLAG (MM_MODEM_BAND_PCS) + B2G_FLAG (MM_MODEM_BAND_G850),
+};
+
+/*****************************************************************************/
+/* AT#BND 3G values */
+
+/* NOTE: UTRAN_1 to UTRAN_9 enum values are NOT IN ORDER!
+ * E.g. numerically UTRAN_7 is after UTRAN_9...
+ *
+ * This array allows us to iterate them in a way which is a bit
+ * more friendly.
+ */
+static const guint band_utran_index[] = {
+ [MM_MODEM_BAND_UTRAN_1] = 1,
+ [MM_MODEM_BAND_UTRAN_2] = 2,
+ [MM_MODEM_BAND_UTRAN_3] = 3,
+ [MM_MODEM_BAND_UTRAN_4] = 4,
+ [MM_MODEM_BAND_UTRAN_5] = 5,
+ [MM_MODEM_BAND_UTRAN_6] = 6,
+ [MM_MODEM_BAND_UTRAN_7] = 7,
+ [MM_MODEM_BAND_UTRAN_8] = 8,
+ [MM_MODEM_BAND_UTRAN_9] = 9,
+ [MM_MODEM_BAND_UTRAN_10] = 10,
+ [MM_MODEM_BAND_UTRAN_11] = 11,
+ [MM_MODEM_BAND_UTRAN_12] = 12,
+ [MM_MODEM_BAND_UTRAN_13] = 13,
+ [MM_MODEM_BAND_UTRAN_14] = 14,
+ [MM_MODEM_BAND_UTRAN_19] = 19,
+ [MM_MODEM_BAND_UTRAN_20] = 20,
+ [MM_MODEM_BAND_UTRAN_21] = 21,
+ [MM_MODEM_BAND_UTRAN_22] = 22,
+ [MM_MODEM_BAND_UTRAN_25] = 25,
+ [MM_MODEM_BAND_UTRAN_26] = 26,
+ [MM_MODEM_BAND_UTRAN_32] = 32,
+};
+
+#define MM_MODEM_BAND_TELIT_3G_FIRST MM_MODEM_BAND_UTRAN_1
+#define MM_MODEM_BAND_TELIT_3G_LAST MM_MODEM_BAND_UTRAN_19
+
+#define B3G_NUM(band) band_utran_index[band]
+#define B3G_FLAG(band) ((B3G_NUM (band) > 0) ? (1LL << (B3G_NUM (band) - B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST))) : 0)
+
+/* Index of the arrays is the telit 3G band value.
+ * The bitmask value here is built from the 3G MMModemBand values right away.
+ *
+ * We have 2 different sets of bands that are different for values >=12, because
+ * the LM940/960 models support a different set of 3G bands.
+ */
+
+#define TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS 27
+static guint64 telit_3g_to_mm_band_mask_default[TELIT_3G_TO_MM_BAND_MASK_DEFAULT_N_ELEMENTS];
+
+#define TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS 20
+static guint64 telit_3g_to_mm_band_mask_alternate[TELIT_3G_TO_MM_BAND_MASK_ALTERNATE_N_ELEMENTS];
+
+static void
+initialize_telit_3g_to_mm_band_masks (void)
+{
+ static gboolean initialized = FALSE;
+
+ /* We need to initialize the arrays in runtime because gcc < 8 doesn't like initializing
+ * with operations that are using the band_utran_index constant array elements */
+
+ if (initialized)
+ return;
+
+ telit_3g_to_mm_band_mask_default[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1);
+ telit_3g_to_mm_band_mask_default[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2);
+ telit_3g_to_mm_band_mask_default[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4);
+ telit_3g_to_mm_band_mask_default[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
+ telit_3g_to_mm_band_mask_default[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[20] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[21] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6);
+ telit_3g_to_mm_band_mask_default[22] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_default[23] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
+ telit_3g_to_mm_band_mask_default[24] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_default[25] = B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+ telit_3g_to_mm_band_mask_default[26] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+
+ /* Initialize alternate 3G band set */
+ telit_3g_to_mm_band_mask_alternate[0] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1);
+ telit_3g_to_mm_band_mask_alternate[1] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2);
+ telit_3g_to_mm_band_mask_alternate[2] = B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[3] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[4] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[5] = B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[6] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[7] = B3G_FLAG (MM_MODEM_BAND_UTRAN_4);
+ telit_3g_to_mm_band_mask_alternate[8] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[9] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[10] = B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[11] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[12] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[13] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3);
+ telit_3g_to_mm_band_mask_alternate[14] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[15] = B3G_FLAG (MM_MODEM_BAND_UTRAN_3) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5);
+ telit_3g_to_mm_band_mask_alternate[16] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_3) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_4) + B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[17] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8);
+ telit_3g_to_mm_band_mask_alternate[18] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) + B3G_FLAG (MM_MODEM_BAND_UTRAN_9) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+ telit_3g_to_mm_band_mask_alternate[19] = B3G_FLAG (MM_MODEM_BAND_UTRAN_1) + B3G_FLAG (MM_MODEM_BAND_UTRAN_2) + B3G_FLAG (MM_MODEM_BAND_UTRAN_4) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_5) + B3G_FLAG (MM_MODEM_BAND_UTRAN_6) + B3G_FLAG (MM_MODEM_BAND_UTRAN_8) +
+ B3G_FLAG (MM_MODEM_BAND_UTRAN_9) + B3G_FLAG (MM_MODEM_BAND_UTRAN_19);
+}
+
+/*****************************************************************************/
+/* AT#BND 4G values
+ *
+ * The Telit-specific value for 4G bands is a bitmask of the band values, given
+ * in hexadecimal or decimal format.
+ */
+
+#define MM_MODEM_BAND_TELIT_4G_FIRST MM_MODEM_BAND_EUTRAN_1
+#define MM_MODEM_BAND_TELIT_4G_LAST MM_MODEM_BAND_EUTRAN_64
+
+#define B4G_FLAG(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_4G_FIRST))
+
+#define MM_MODEM_BAND_TELIT_EXT_4G_FIRST MM_MODEM_BAND_EUTRAN_65
+#define MM_MODEM_BAND_TELIT_EXT_4G_LAST MM_MODEM_BAND_EUTRAN_71
+
+#define B4G_FLAG_EXT(band) (((guint64) 1) << (band - MM_MODEM_BAND_TELIT_EXT_4G_FIRST))
+
+/*****************************************************************************/
+/* Set current bands helpers */
+
+gchar *
+mm_telit_build_bnd_request (GArray *bands_array,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ GError **error)
+{
+ guint32 mask2g = 0;
+ guint64 mask3g = 0;
+ guint64 mask4g = 0;
+ guint64 mask4gext = 0;
+ guint i;
+ gint flag2g = -1;
+ gint64 flag3g = -1;
+ gint64 flag4g = -1;
+ gchar *cmd;
+ const guint64 *telit_3g_to_mm_band_mask;
+ guint telit_3g_to_mm_band_mask_n_elements;
+
+ initialize_telit_3g_to_mm_band_masks ();
+
+ /* Select correct 3G band mask */
+ if (modem_alternate_3g_bands) {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
+ } else {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
+ }
+
+ for (i = 0; i < bands_array->len; i++) {
+ MMModemBand band;
+
+ band = g_array_index (bands_array, MMModemBand, i);
+
+ /* Convert 2G bands into a bitmask, to match against telit_2g_to_mm_band_mask. */
+ if (modem_is_2g && mm_common_band_is_gsm (band) &&
+ (band >= MM_MODEM_BAND_TELIT_2G_FIRST) && (band <= MM_MODEM_BAND_TELIT_2G_LAST))
+ mask2g += B2G_FLAG (band);
+
+ /* Convert 3G bands into a bitmask, to match against telit_3g_to_mm_band_mask. We use
+ * a 64-bit explicit bitmask so that all values fit correctly. */
+ if (modem_is_3g && mm_common_band_is_utran (band) &&
+ (B3G_NUM (band) >= B3G_NUM (MM_MODEM_BAND_TELIT_3G_FIRST)) && (B3G_NUM (band) <= B3G_NUM (MM_MODEM_BAND_TELIT_3G_LAST)))
+ mask3g += B3G_FLAG (band);
+
+ /* Convert 4G bands into a bitmask. We use a 64bit explicit bitmask so that
+ * all values fit correctly. */
+ if (modem_is_4g && mm_common_band_is_eutran (band)) {
+ if (band >= MM_MODEM_BAND_TELIT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_4G_LAST)
+ mask4g += B4G_FLAG (band);
+ else if (band >= MM_MODEM_BAND_TELIT_EXT_4G_FIRST && band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST)
+ mask4gext += B4G_FLAG_EXT (band);
+ }
+ }
+
+ /* Get 2G-specific telit value */
+ if (mask2g) {
+ for (i = 0; i < G_N_ELEMENTS (telit_2g_to_mm_band_mask); i++) {
+ if (mask2g == telit_2g_to_mm_band_mask[i]) {
+ flag2g = i;
+ break;
+ }
+ }
+ if (flag2g == -1) {
+ gchar *bands_str;
+
+ bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find matching 2G bands Telit value for band combination: '%s'", bands_str);
+ g_free (bands_str);
+ return NULL;
+ }
+ }
+
+ /* Get 3G-specific telit value */
+ if (mask3g) {
+ for (i = 0; i < telit_3g_to_mm_band_mask_n_elements; i++) {
+ if (mask3g == telit_3g_to_mm_band_mask[i]) {
+ flag3g = i;
+ break;
+ }
+ }
+ if (flag3g == -1) {
+ gchar *bands_str;
+
+ bands_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)(bands_array->data), bands_array->len);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find matching 3G bands Telit value for band combination: '%s'", bands_str);
+ g_free (bands_str);
+ return NULL;
+ }
+ }
+
+ /* Get 4G-specific telit band bitmask */
+ flag4g = (mask4g != 0) ? ((gint64)mask4g) : -1;
+
+ /* If the modem supports a given access tech, we must always give band settings
+ * for the specific tech */
+ if (modem_is_2g && flag2g == -1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "None or invalid 2G bands combination in the provided list");
+ return NULL;
+ }
+ if (modem_is_3g && flag3g == -1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "None or invalid 3G bands combination in the provided list");
+ return NULL;
+ }
+ if (modem_is_4g && mask4g == 0 && mask4gext == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "None or invalid 4G bands combination in the provided list");
+ return NULL;
+ }
+
+ if (modem_is_2g && !modem_is_3g && !modem_is_4g)
+ cmd = g_strdup_printf ("#BND=%d", flag2g);
+ else if (!modem_is_2g && modem_is_3g && !modem_is_4g)
+ cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT, flag3g);
+ else if (modem_is_2g && modem_is_3g && !modem_is_4g)
+ cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT, flag2g, flag3g);
+ else if (!modem_is_2g && !modem_is_3g && modem_is_4g) {
+ if (!modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_FORMAT, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=0,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", mask4g, mask4gext);
+ } else if (!modem_is_2g && modem_is_3g && modem_is_4g) {
+ if (!modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag3g, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=0,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag3g, mask4g, mask4gext);
+ } else if (modem_is_2g && !modem_is_3g && modem_is_4g) {
+ if (!modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_FORMAT, flag2g, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=%d,0,%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, mask4g, mask4gext);
+ } else if (modem_is_2g && modem_is_3g && modem_is_4g) {
+ if (!modem_ext_4g_bands)
+ cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_FORMAT, flag2g, flag3g, flag4g);
+ else
+ cmd = g_strdup_printf ("#BND=%d,%" G_GINT64_FORMAT ",%" G_GINT64_MODIFIER "x" ",%" G_GINT64_MODIFIER "x", flag2g, flag3g, mask4g, mask4gext);
+ } else
+ g_assert_not_reached ();
+
+ return cmd;
+}
+
+/*****************************************************************************/
+/* #BND response parser
+ *
+ * AT#BND=?
+ * #BND: <2G band flags range>,<3G band flags range>[, <4G band flags range>]
+ *
+ * where "band flags" is a list of numbers defining the supported bands.
+ * Note that the one Telit band flag may represent more than one MM band.
+ *
+ * e.g.
+ *
+ * #BND: (0-2),(3,4)
+ *
+ * (0,2) = 2G band flag 0 is EGSM + DCS
+ * = 2G band flag 1 is EGSM + PCS
+ * = 2G band flag 2 is DCS + G850
+ * (3,4) = 3G band flag 3 is U2100 + U1900 + U850
+ * = 3G band flag 4 is U1900 + U850
+ *
+ * Modems that supports 4G bands, return a range value(X-Y) where
+ * X: represent the lower supported band, such as X = 2^(B-1), being B = B1, B2,..., B32
+ * Y: is a 32 bit number resulting from a mask of all the supported bands:
+ * 1 - B1
+ * 2 - B2
+ * 4 - B3
+ * 8 - B4
+ * ...
+ * i - B(2exp(i-1))
+ * ...
+ * 2147483648 - B32
+ *
+ * e.g.
+ * (2-4106)
+ * 2 = 2^1 --> lower supported band B2
+ * 4106 = 2^1 + 2^3 + 2^12 --> the supported bands are B2, B4, B13
+ *
+ *
+ * AT#BND?
+ * #BND: <2G band flags>,<3G band flags>[, <4G band flags>]
+ *
+ * where "band flags" is a number defining the current bands.
+ * Note that the one Telit band flag may represent more than one MM band.
+ *
+ * e.g.
+ *
+ * #BND: 0,4
+ *
+ * 0 = 2G band flag 0 is EGSM + DCS
+ * 4 = 3G band flag 4 is U1900 + U850
+ *
+ * ----------------
+ *
+ * For modems such as LN920 the #BND configuration/response for LTE bands is different
+ * from what is explained above:
+ *
+ * AT#BND=<GSM_band>[,<UMTS_band>[,<LTE_band>[,<LTE_band_ext>]]]
+ *
+ * <LTE_band>: hex: Indicates the LTE supported bands expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits):
+ *
+ * Band number(Hex) Band i
+ * 0 disable
+ * 1 B1
+ * 2 B2
+ * 4 B3
+ * 8 B4
+ * ...
+ * ...
+ * 80000000 B32
+ * ...
+ * ...
+ * 800000000000 B48
+ *
+ * It can take value, 0 - 87A03B0F38DF: range of the sum of Band number (1+2+4 ...)
+ *
+ * <LTE_band_ext>: hex: Indicates the LTE supported bands from B65 expressed as the sum of Band number (1+2+8 ...) calculated as shown in the table (mask of 64 bits):
+ *
+ * Band number(Hex) Band i
+ * 0 disable
+ * 2 B66
+ * 40 B71
+ *
+ * It can take value, 0 - 42: range of the sum of Band number (2+40)
+ *
+ * Note: LTE_band and LTE_band_ext cannot be 0 at the same time
+ *
+ * Example output:
+ *
+ * AT#BND=?
+ * #BND: (0),(0-11,17,18),(87A03B0F38DF),(42)
+ *
+ * AT#BND?
+ * #BND: 0,18,87A03B0F38DF,42
+ *
+ */
+
+static gboolean
+telit_get_2g_mm_bands (GMatchInfo *match_info,
+ gpointer log_object,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GArray *values = NULL;
+ gchar *match_str = NULL;
+ guint i;
+
+ match_str = g_match_info_fetch_named (match_info, "Bands2G");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 2G band values from response");
+ goto out;
+ }
+
+ values = mm_parse_uint_list (match_str, &inner_error);
+ if (!values)
+ goto out;
+
+ for (i = 0; i < values->len; i++) {
+ guint value;
+
+ value = g_array_index (values, guint, i);
+ if (value < G_N_ELEMENTS (telit_2g_to_mm_band_mask)) {
+ guint j;
+
+ for (j = MM_MODEM_BAND_TELIT_2G_FIRST; j <= MM_MODEM_BAND_TELIT_2G_LAST; j++) {
+ if ((telit_2g_to_mm_band_mask[value] & B2G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j))
+ *bands = g_array_append_val (*bands, j);
+ }
+ } else
+ mm_obj_dbg (log_object, "unhandled telit 2G band value configuration: %u", value);
+ }
+
+out:
+ g_free (match_str);
+ g_clear_pointer (&values, g_array_unref);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+telit_get_3g_mm_bands (GMatchInfo *match_info,
+ gpointer log_object,
+ gboolean modem_alternate_3g_bands,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GArray *values = NULL;
+ gchar *match_str = NULL;
+ guint i;
+ const guint64 *telit_3g_to_mm_band_mask;
+ guint telit_3g_to_mm_band_mask_n_elements;
+
+ initialize_telit_3g_to_mm_band_masks ();
+
+ /* Select correct 3G band mask */
+ if (modem_alternate_3g_bands) {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_alternate;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_alternate);
+ } else {
+ telit_3g_to_mm_band_mask = telit_3g_to_mm_band_mask_default;
+ telit_3g_to_mm_band_mask_n_elements = G_N_ELEMENTS (telit_3g_to_mm_band_mask_default);
+ }
+
+ match_str = g_match_info_fetch_named (match_info, "Bands3G");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 3G band values from response");
+ goto out;
+ }
+
+ values = mm_parse_uint_list (match_str, &inner_error);
+ if (!values)
+ goto out;
+
+ for (i = 0; i < values->len; i++) {
+ guint value;
+
+ value = g_array_index (values, guint, i);
+
+ if (value < telit_3g_to_mm_band_mask_n_elements) {
+ guint j;
+
+ for (j = 0; j < G_N_ELEMENTS (band_utran_index); j++) {
+ /* ignore non-3G bands */
+ if (band_utran_index[j] == 0)
+ continue;
+
+ if ((telit_3g_to_mm_band_mask[value] & B3G_FLAG (j)) && !mm_common_bands_garray_lookup (*bands, j))
+ *bands = g_array_append_val (*bands, j);
+ }
+ } else
+ mm_obj_dbg (log_object, "unhandled telit 3G band value configuration: %u", value);
+ }
+
+out:
+ g_free (match_str);
+ g_clear_pointer (&values, g_array_unref);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+telit_get_4g_mm_bands (GMatchInfo *match_info,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ MMModemBand band;
+ gchar *match_str = NULL;
+ guint64 value;
+ gchar **tokens = NULL;
+
+ match_str = g_match_info_fetch_named (match_info, "Bands4G");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 4G band flags from response");
+ goto out;
+ }
+
+ /* splitting will never return NULL as string is not empty */
+ tokens = g_strsplit (match_str, "-", -1);
+
+ /* If this is a range, get upper threshold, which contains the total supported mask */
+ if (!mm_get_u64_from_str (tokens[1] ? tokens[1] : tokens[0], &value)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse 4G band mask from string: '%s'", match_str);
+ goto out;
+ }
+
+ for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) {
+ if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band))
+ g_array_append_val (*bands, band);
+ }
+
+out:
+ g_strfreev (tokens);
+ g_free (match_str);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+telit_get_ext_4g_mm_bands (GMatchInfo *match_info,
+ GArray **bands,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ MMModemBand band;
+ gchar *match_str = NULL;
+ gchar *match_str_ext = NULL;
+ guint64 value;
+
+ match_str = g_match_info_fetch_named (match_info, "Bands4G");
+ if (!match_str || match_str[0] == '\0') {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find 4G band hex mask flag from response");
+ goto out;
+ }
+
+ if (!mm_get_u64_from_hex_str (match_str, &value)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse 4G band hex mask from string: '%s'", match_str);
+ goto out;
+ }
+
+ for (band = MM_MODEM_BAND_TELIT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_4G_LAST; band++) {
+ if ((value & B4G_FLAG (band)) && !mm_common_bands_garray_lookup (*bands, band))
+ g_array_append_val (*bands, band);
+ }
+
+ /* extended bands */
+ match_str_ext = g_match_info_fetch_named (match_info, "Bands4GExt");
+ if (match_str_ext) {
+
+ if (!mm_get_u64_from_hex_str (match_str_ext, &value)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse 4G ext band mask from string: '%s'", match_str_ext);
+ goto out;
+ }
+
+ for (band = MM_MODEM_BAND_TELIT_EXT_4G_FIRST; band <= MM_MODEM_BAND_TELIT_EXT_4G_LAST; band++) {
+ if ((value & B4G_FLAG_EXT (band)) && !mm_common_bands_garray_lookup (*bands, band))
+ g_array_append_val (*bands, band);
+ }
+ }
+
+out:
+ g_free (match_str);
+ g_free (match_str_ext);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+typedef enum {
+ LOAD_BANDS_TYPE_SUPPORTED,
+ LOAD_BANDS_TYPE_CURRENT,
+} LoadBandsType;
+
+static gboolean
+bnd_response_has_ext_4g_bands (const gchar *response)
+{
+ g_auto(GStrv) tokens = NULL;
+
+ tokens = mm_split_string_groups (response);
+ return g_strv_length (tokens) == 4;
+}
+
+static GArray *
+common_parse_bnd_response (const gchar *response,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ LoadBandsType load_type,
+ gpointer log_object,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GArray *bands = NULL;
+ GMatchInfo *match_info = NULL;
+ GRegex *r;
+
+ if (!modem_ext_4g_bands) {
+ static const gchar *load_bands_regex[] = {
+ [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?(,\\s*\\((?P<Bands4G>[0-9\\-,]*)\\))?",
+ [LOAD_BANDS_TYPE_CURRENT] = "#BND:\\s*(?P<Bands2G>\\d+)(,\\s*(?P<Bands3G>\\d+))?(,\\s*(?P<Bands4G>\\d+))?",
+ };
+
+ r = g_regex_new (load_bands_regex[load_type], G_REGEX_RAW, 0, NULL);
+ } else {
+ static const gchar *load_bands_regex_hex[] = {
+ [LOAD_BANDS_TYPE_SUPPORTED] = "#BND:\\s*\\((?P<Bands2G>[0-9\\-,]*)\\)(,\\s*\\((?P<Bands3G>[0-9\\-,]*)\\))?(,\\s*\\((?P<Bands4G>[0-9A-F]+)\\))?(,\\s*\\((?P<Bands4GExt>[0-9A-F]+)\\))?",
+ [LOAD_BANDS_TYPE_CURRENT] = "#BND:\\s*(?P<Bands2G>\\d+)(,\\s*(?P<Bands3G>\\d+))?(,\\s*(?P<Bands4G>[0-9A-F]+))?(,\\s*(?P<Bands4GExt>[0-9A-F]+))?",
+ };
+
+ r = g_regex_new (load_bands_regex_hex[load_type], G_REGEX_RAW, 0, NULL);
+ }
+
+ g_assert (r);
+
+ if (!g_regex_match (r, response, 0, &match_info)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse response '%s'", response);
+ goto out;
+ }
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find matches in response '%s'", response);
+ goto out;
+ }
+
+ bands = g_array_new (TRUE, TRUE, sizeof (MMModemBand));
+
+ if (modem_is_2g && !telit_get_2g_mm_bands (match_info, log_object, &bands, &inner_error))
+ goto out;
+
+ if (modem_is_3g && !telit_get_3g_mm_bands (match_info, log_object, modem_alternate_3g_bands, &bands, &inner_error))
+ goto out;
+
+ if (modem_is_4g && !modem_ext_4g_bands && !telit_get_4g_mm_bands (match_info, &bands, &inner_error))
+ goto out;
+
+ if (modem_is_4g && modem_ext_4g_bands && !telit_get_ext_4g_mm_bands (match_info, &bands, &inner_error))
+ goto out;
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_clear_pointer (&bands, g_array_unref);
+ return NULL;
+ }
+
+ return bands;
+}
+
+GArray *
+mm_telit_parse_bnd_query_response (const gchar *response,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ gpointer log_object,
+ GError **error)
+{
+ return common_parse_bnd_response (response,
+ modem_is_2g, modem_is_3g, modem_is_4g,
+ modem_alternate_3g_bands,
+ modem_ext_4g_bands,
+ LOAD_BANDS_TYPE_CURRENT,
+ log_object,
+ error);
+}
+
+GArray *
+mm_telit_parse_bnd_test_response (const gchar *response,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean *modem_ext_4g_bands,
+ gpointer log_object,
+ GError **error)
+{
+ *modem_ext_4g_bands = bnd_response_has_ext_4g_bands (response);
+ return common_parse_bnd_response (response,
+ modem_is_2g, modem_is_3g, modem_is_4g,
+ modem_alternate_3g_bands,
+ *modem_ext_4g_bands,
+ LOAD_BANDS_TYPE_SUPPORTED,
+ log_object,
+ error);
+}
+
+/*****************************************************************************/
+/* #QSS? response parser */
+
+MMTelitQssStatus
+mm_telit_parse_qss_query (const gchar *response,
+ GError **error)
+{
+ gint qss_status;
+ gint qss_mode;
+
+ qss_status = QSS_STATUS_UNKNOWN;
+ if (sscanf (response, "#QSS: %d,%d", &qss_mode, &qss_status) != 2) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse \"#QSS?\" response: %s", response);
+ return QSS_STATUS_UNKNOWN;
+ }
+
+ switch (qss_status) {
+ case QSS_STATUS_SIM_REMOVED:
+ case QSS_STATUS_SIM_INSERTED:
+ case QSS_STATUS_SIM_INSERTED_AND_UNLOCKED:
+ case QSS_STATUS_SIM_INSERTED_AND_READY:
+ return (MMTelitQssStatus) qss_status;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown QSS status value given: %d", qss_status);
+ return QSS_STATUS_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+/* Supported modes list helper */
+
+GArray *
+mm_telit_build_modes_list (void)
+{
+ GArray *combinations;
+ MMModemModeCombination mode;
+
+ /* Build list of combinations for 3GPP devices */
+ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 7);
+
+ /* 2G only */
+ mode.allowed = MM_MODEM_MODE_2G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 3G only */
+ mode.allowed = MM_MODEM_MODE_3G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G and 3G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 4G only */
+ mode.allowed = MM_MODEM_MODE_4G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G and 4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 3G and 4G */
+ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ /* 2G, 3G and 4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+
+ return combinations;
+}
diff --git a/plugins/telit/mm-modem-helpers-telit.h b/plugins/telit/mm-modem-helpers-telit.h
new file mode 100644
index 00000000..718bb60d
--- /dev/null
+++ b/plugins/telit/mm-modem-helpers-telit.h
@@ -0,0 +1,68 @@
+/* -*- 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) 2015-2019 Telit.
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+#ifndef MM_MODEM_HELPERS_TELIT_H
+#define MM_MODEM_HELPERS_TELIT_H
+
+#include <glib.h>
+#include "ModemManager.h"
+
+/* #BND response parsers and request builder */
+GArray *mm_telit_parse_bnd_query_response (const gchar *response,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ gpointer log_object,
+ GError **error);
+GArray *mm_telit_parse_bnd_test_response (const gchar *response,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean *modem_ext_4g_bands,
+ gpointer log_object,
+ GError **error);
+gchar *mm_telit_build_bnd_request (GArray *bands_array,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ GError **error);
+
+/* #QSS? response parser */
+typedef enum { /*< underscore_name=mm_telit_qss_status >*/
+ QSS_STATUS_UNKNOWN = -1,
+ QSS_STATUS_SIM_REMOVED,
+ QSS_STATUS_SIM_INSERTED,
+ QSS_STATUS_SIM_INSERTED_AND_UNLOCKED,
+ QSS_STATUS_SIM_INSERTED_AND_READY,
+} MMTelitQssStatus;
+
+MMTelitQssStatus mm_telit_parse_qss_query (const gchar *response, GError **error);
+
+/* CSIM lock state */
+typedef enum { /*< underscore_name=mm_telit_csim_lock_state >*/
+ CSIM_LOCK_STATE_UNKNOWN,
+ CSIM_LOCK_STATE_UNLOCKED,
+ CSIM_LOCK_STATE_LOCK_REQUESTED,
+ CSIM_LOCK_STATE_LOCKED,
+} MMTelitCsimLockState;
+
+GArray *mm_telit_build_modes_list (void);
+
+#endif /* MM_MODEM_HELPERS_TELIT_H */
diff --git a/plugins/telit/mm-plugin-telit.c b/plugins/telit/mm-plugin-telit.c
index 47830956..3532d2fd 100644
--- a/plugins/telit/mm-plugin-telit.c
+++ b/plugins/telit/mm-plugin-telit.c
@@ -21,104 +21,92 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-port-enums-types.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-plugin-telit.h"
+#include "mm-common-telit.h"
#include "mm-broadband-modem-telit.h"
+
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+
+#if defined WITH_MBIM
+# include "mm-broadband-modem-mbim-telit.h"
+#endif
+
G_DEFINE_TYPE (MMPluginTelit, mm_plugin_telit, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_telit_new (sysfs_path,
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered Telit modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+#if defined WITH_MBIM
+ if (mm_port_probe_list_has_mbim_port (probes)) {
+ mm_obj_dbg (self, "MBIM-powered Telit modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_telit_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_telit_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- GUdevDevice *port;
- MMPortType ptype;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-
- port = mm_port_probe_peek_port (probe);
- ptype = mm_port_probe_get_port_type (probe);
-
- /* Look for port type hints; just probing can't distinguish which port should
- * be the data/primary port on these devices. We have to tag them based on
- * what the Windows .INF files say the port layout should be.
- */
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_TELIT_PORT_TYPE_MODEM")) {
- mm_dbg ("telit: AT port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_TELIT_PORT_TYPE_AUX")) {
- mm_dbg ("telit: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_TELIT_PORT_TYPE_NMEA")) {
- mm_dbg ("telit: port '%s/%s' flagged as NMEA",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- ptype = MM_PORT_TYPE_GPS;
- } else {
- /* If the port was tagged by the udev rules but isn't a primary or secondary,
- * then ignore it to guard against race conditions if a device just happens
- * to show up with more than two AT-capable ports.
- */
- ptype = MM_PORT_TYPE_IGNORED;
- }
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- ptype,
- pflags,
- error);
-}
-
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
/* Vendors: Telit */
static const guint16 vendor_ids[] = { 0x1bc7, 0 };
- /* Only handle TELIT tagged devices here. */
- static const gchar *udev_tags[] = {
- "ID_MM_TELIT_TAGGED",
- NULL
+ static const gchar *vendor_strings[] = { "telit", NULL };
+ /* Custom init for port identification */
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (telit_custom_init),
+ .finish = G_CALLBACK (telit_custom_init_finish),
};
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_TELIT,
- MM_PLUGIN_NAME, "Telit",
- MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
- MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
- MM_PLUGIN_ALLOWED_AT, TRUE,
- MM_PLUGIN_ALLOWED_UDEV_TAGS, udev_tags,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ MM_PLUGIN_ALLOWED_MBIM, TRUE,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
NULL));
}
@@ -133,5 +121,5 @@ mm_plugin_telit_class_init (MMPluginTelitClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
+ plugin_class->grab_port = telit_grab_port;
}
diff --git a/plugins/telit/mm-shared-telit.c b/plugins/telit/mm-shared-telit.c
new file mode 100644
index 00000000..af9dea3d
--- /dev/null
+++ b/plugins/telit/mm-shared-telit.c
@@ -0,0 +1,613 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-modem-helpers-telit.h"
+#include "mm-shared-telit.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "shared-telit-private-tag"
+static GQuark private_quark;
+
+typedef struct {
+ MMIfaceModem *iface_modem_parent;
+ gboolean alternate_3g_bands;
+ gboolean ext_4g_bands;
+ GArray *supported_bands;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ if (priv->supported_bands)
+ g_array_unref (priv->supported_bands);
+ g_slice_free (Private, priv);
+}
+
+static void
+initialize_alternate_3g_band (MMSharedTelit *self,
+ Private *priv)
+{
+ MMPort *primary;
+ MMKernelDevice *port;
+
+ primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
+ if (primary) {
+ port = mm_port_peek_kernel_device (primary);
+
+ /* Lookup for the tag specifying that we're using the alternate 3G band mapping */
+ priv->alternate_3g_bands = mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_TELIT_BND_ALTERNATE");
+ if (priv->alternate_3g_bands)
+ mm_obj_dbg (self, "telit modem using alternate 3G band mask setup");
+ }
+}
+
+static Private *
+get_private (MMSharedTelit *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ initialize_alternate_3g_band (self, priv);
+ /* ext_4g_bands field is initialized inside #BND=? response handler */
+
+ if (MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface)
+ priv->iface_modem_parent = MM_SHARED_TELIT_GET_INTERFACE (self)->peek_parent_modem_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+/*****************************************************************************/
+/* Load current mode (Modem interface) */
+
+gboolean
+mm_shared_telit_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ const gchar *response;
+ const gchar *str;
+ gint a;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return FALSE;
+
+ str = mm_strip_tag (response, "+WS46: ");
+
+ if (!sscanf (str, "%d", &a)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +WS46 response: '%s'",
+ response);
+ return FALSE;
+ }
+
+ *preferred = MM_MODEM_MODE_NONE;
+ switch (a) {
+ case 12:
+ *allowed = MM_MODEM_MODE_2G;
+ return TRUE;
+ case 22:
+ *allowed = MM_MODEM_MODE_3G;
+ return TRUE;
+ case 25:
+ if (mm_iface_modem_is_3gpp_lte (self))
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ else
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ return TRUE;
+ case 28:
+ *allowed = MM_MODEM_MODE_4G;
+ return TRUE;
+ case 29:
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ return TRUE;
+ case 30:
+ *allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+ return TRUE;
+ case 31:
+ *allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ return TRUE;
+ default:
+ break;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse unexpected +WS46 response: '%s'",
+ response);
+ return FALSE;
+}
+
+void
+mm_shared_telit_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+WS46?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load supported bands (Modem interface) */
+
+GArray *
+mm_shared_telit_modem_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else {
+ GArray *bands;
+
+ bands = mm_telit_parse_bnd_test_response (response,
+ mm_iface_modem_is_2g (MM_IFACE_MODEM (self)),
+ mm_iface_modem_is_3g (MM_IFACE_MODEM (self)),
+ mm_iface_modem_is_4g (MM_IFACE_MODEM (self)),
+ priv->alternate_3g_bands,
+ &priv->ext_4g_bands,
+ self,
+ &error);
+ if (!bands)
+ g_task_return_error (task, error);
+ else {
+ /* Store supported bands to be able to build ANY when setting */
+ priv->supported_bands = g_array_ref (bands);
+ if (priv->ext_4g_bands)
+ mm_obj_dbg (self, "telit modem using extended 4G band setup");
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ }
+ }
+
+ g_object_unref (task);
+}
+
+static void
+load_supported_bands_at (MMIfaceModem *self,
+ GTask *task)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#BND=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) load_supported_bands_ready,
+ task);
+}
+
+static void
+parent_load_supported_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GArray *bands;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ bands = priv->iface_modem_parent->load_supported_bands_finish (MM_IFACE_MODEM (self), res, &error);
+ if (bands) {
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+ } else {
+ mm_obj_dbg (self, "parent load supported bands failure, falling back to AT commands");
+ load_supported_bands_at (self, task);
+ g_clear_error (&error);
+ }
+}
+
+void
+mm_shared_telit_modem_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ if (priv->iface_modem_parent &&
+ priv->iface_modem_parent->load_supported_bands &&
+ priv->iface_modem_parent->load_supported_bands_finish) {
+ priv->iface_modem_parent->load_supported_bands (self,
+ (GAsyncReadyCallback) parent_load_supported_bands_ready,
+ task);
+ } else
+ load_supported_bands_at (self, task);
+}
+
+/*****************************************************************************/
+/* Load current bands (Modem interface) */
+
+GArray *
+mm_shared_telit_modem_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_current_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else {
+ GArray *bands;
+
+ bands = mm_telit_parse_bnd_query_response (response,
+ mm_iface_modem_is_2g (MM_IFACE_MODEM (self)),
+ mm_iface_modem_is_3g (MM_IFACE_MODEM (self)),
+ mm_iface_modem_is_4g (MM_IFACE_MODEM (self)),
+ priv->alternate_3g_bands,
+ priv->ext_4g_bands,
+ self,
+ &error);
+ if (!bands)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ }
+
+ g_object_unref (task);
+}
+
+static void
+load_current_bands_at (MMIfaceModem *self,
+ GTask *task)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "#BND?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) load_current_bands_ready,
+ task);
+}
+
+static void
+parent_load_current_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GArray *bands;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ bands = priv->iface_modem_parent->load_current_bands_finish (MM_IFACE_MODEM (self), res, &error);
+ if (bands) {
+ g_task_return_pointer (task, bands, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+ } else {
+ mm_obj_dbg (self, "parent load current bands failure, falling back to AT commands");
+ load_current_bands_at (self, task);
+ g_clear_error (&error);
+ }
+}
+
+void
+mm_shared_telit_modem_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ if (priv->iface_modem_parent &&
+ priv->iface_modem_parent->load_current_bands &&
+ priv->iface_modem_parent->load_current_bands_finish) {
+ priv->iface_modem_parent->load_current_bands (self,
+ (GAsyncReadyCallback) parent_load_current_bands_ready,
+ task);
+ } else
+ load_current_bands_at (self, task);
+}
+
+/*****************************************************************************/
+/* Set current bands (Modem interface) */
+
+gboolean
+mm_shared_telit_modem_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_current_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+set_current_bands_at (MMIfaceModem *self,
+ GTask *task)
+{
+ GError *error = NULL;
+ gchar *cmd;
+ Private *priv;
+ GArray *bands_array;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ bands_array = g_task_get_task_data (task);
+ g_assert (bands_array);
+
+ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ if (!priv->supported_bands) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't build ANY band settings: unknown supported bands");
+ g_object_unref (task);
+ return;
+ }
+ bands_array = priv->supported_bands;
+ }
+
+ cmd = mm_telit_build_bnd_request (bands_array,
+ mm_iface_modem_is_2g (self),
+ mm_iface_modem_is_3g (self),
+ mm_iface_modem_is_4g (self),
+ priv->alternate_3g_bands,
+ priv->ext_4g_bands,
+ &error);
+ if (!cmd) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_current_bands_ready,
+ task);
+ g_free (cmd);
+}
+
+static void
+parent_set_current_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ if (priv->iface_modem_parent->set_current_bands_finish (MM_IFACE_MODEM (self), res, &error)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ } else {
+ g_clear_error (&error);
+ set_current_bands_at (self, task);
+ }
+}
+
+void
+mm_shared_telit_modem_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_TELIT (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, g_array_ref (bands_array), (GDestroyNotify)g_array_unref);
+
+ if (priv->iface_modem_parent &&
+ priv->iface_modem_parent->set_current_bands &&
+ priv->iface_modem_parent->set_current_bands_finish) {
+ priv->iface_modem_parent->set_current_bands (self,
+ bands_array,
+ (GAsyncReadyCallback) parent_set_current_bands_ready,
+ task);
+ } else
+ set_current_bands_at (self, task);
+}
+
+/*****************************************************************************/
+/* Set current modes (Modem interface) */
+
+gboolean
+mm_shared_telit_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ws46_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ if (error)
+ /* Let the error be critical. */
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_telit_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *command;
+ gint ws46_mode = -1;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (allowed == MM_MODEM_MODE_2G)
+ ws46_mode = 12;
+ else if (allowed == MM_MODEM_MODE_3G)
+ ws46_mode = 22;
+ else if (allowed == MM_MODEM_MODE_4G)
+ ws46_mode = 28;
+ else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) {
+ if (mm_iface_modem_is_3gpp_lte (self))
+ ws46_mode = 29;
+ else
+ ws46_mode = 25;
+ } else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G))
+ ws46_mode = 30;
+ else if (allowed == (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G))
+ ws46_mode = 31;
+ else if (allowed == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G) ||
+ allowed == MM_MODEM_MODE_ANY)
+ ws46_mode = 25;
+
+ /* Telit modems do not support preferred mode selection */
+ if ((ws46_mode < 0) || (preferred != MM_MODEM_MODE_NONE)) {
+ gchar *allowed_str;
+ gchar *preferred_str;
+
+ allowed_str = mm_modem_mode_build_string_from_mask (allowed);
+ preferred_str = mm_modem_mode_build_string_from_mask (preferred);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_free (allowed_str);
+ g_free (preferred_str);
+
+ g_object_unref (task);
+ return;
+ }
+
+ command = g_strdup_printf ("AT+WS46=%d", ws46_mode);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) ws46_set_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+
+static void
+shared_telit_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_telit_get_type (void)
+{
+ static GType shared_telit_type = 0;
+
+ if (!G_UNLIKELY (shared_telit_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedTelit), /* class_size */
+ shared_telit_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_telit_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedTelit", &info, 0);
+ g_type_interface_add_prerequisite (shared_telit_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return shared_telit_type;
+}
diff --git a/plugins/telit/mm-shared-telit.h b/plugins/telit/mm-shared-telit.h
new file mode 100644
index 00000000..4cfaeb51
--- /dev/null
+++ b/plugins/telit/mm-shared-telit.h
@@ -0,0 +1,90 @@
+/* -*- 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) 2019 Daniele Palmas <dnlplm@gmail.com>
+ */
+
+#ifndef MM_SHARED_TELIT_H
+#define MM_SHARED_TELIT_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+
+#define MM_TYPE_SHARED_TELIT (mm_shared_telit_get_type ())
+#define MM_SHARED_TELIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit))
+#define MM_IS_SHARED_TELIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_TELIT))
+#define MM_SHARED_TELIT_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_TELIT, MMSharedTelit))
+
+typedef struct _MMSharedTelit MMSharedTelit;
+
+struct _MMSharedTelit {
+ GTypeInterface g_iface;
+
+ /* Peek modem interface of the parent class of the object */
+ MMIfaceModem * (* peek_parent_modem_interface) (MMSharedTelit *self);
+};
+
+GType mm_shared_telit_get_type (void);
+
+gboolean mm_shared_telit_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error);
+
+void mm_shared_telit_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_shared_telit_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void mm_shared_telit_modem_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GArray * mm_shared_telit_modem_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_modem_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GArray * mm_shared_telit_modem_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_shared_telit_modem_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_telit_modem_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+#endif /* MM_SHARED_TELIT_H */
diff --git a/plugins/telit/mm-shared.c b/plugins/telit/mm-shared.c
new file mode 100644
index 00000000..ad2843dd
--- /dev/null
+++ b/plugins/telit/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Telit)
diff --git a/plugins/telit/tests/test-mm-modem-helpers-telit.c b/plugins/telit/tests/test-mm-modem-helpers-telit.c
new file mode 100644
index 00000000..e957f7d6
--- /dev/null
+++ b/plugins/telit/tests/test-mm-modem-helpers-telit.c
@@ -0,0 +1,629 @@
+/* -*- 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) 2015-2019 Telit
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ *
+ */
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-telit.h"
+
+#include "test-helpers.h"
+
+/******************************************************************************/
+
+#define MAX_BANDS_LIST_LEN 17
+
+typedef struct {
+ const gchar *response;
+ gboolean modem_is_2g;
+ gboolean modem_is_3g;
+ gboolean modem_is_4g;
+ gboolean modem_alternate_3g_bands;
+ gboolean modem_ext_4g_bands;
+ guint mm_bands_len;
+ MMModemBand mm_bands [MAX_BANDS_LIST_LEN];
+} BndResponseTest;
+
+static BndResponseTest supported_band_mapping_tests [] = {
+ {
+ "#BND: (0-3)", TRUE, FALSE, FALSE, FALSE, FALSE, 4,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850 }
+ },
+ {
+ "#BND: (0-3),(0,2,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, 7,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0,3),(0,2,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, 7,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0,2),(0,2,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, 6,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0,2),(0-4,5,6)", TRUE, TRUE, FALSE, FALSE, FALSE, 7,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: (0-3),(0,2,5,6),(1-1)", TRUE, TRUE, TRUE, FALSE, FALSE, 8,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1 }
+ },
+ {
+ "#BND: (0),(0),(1-3)", TRUE, TRUE, TRUE, FALSE, FALSE, 5,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ {
+ "#BND: (0),(0),(1-3)", FALSE, FALSE, TRUE, FALSE, FALSE, 2,
+ { MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ /* 3G alternate band settings: default */
+ {
+ "#BND: (0),(0,2,5,6,12,25)", FALSE, TRUE, FALSE, FALSE, FALSE, 5,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_UTRAN_6,
+ MM_MODEM_BAND_UTRAN_19 }
+ },
+ /* 3G alternate band settings: alternate */
+ {
+ "#BND: (0),(0,2,5,6,12,13)", FALSE, TRUE, FALSE, TRUE, FALSE, 4,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_3,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ /* ME910 (2G+4G device)
+ * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111
+ */
+ {
+ "#BND: (0-5),(0),(1-168695967)", TRUE, FALSE, TRUE, FALSE, FALSE, 17,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_18,
+ MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20,
+ MM_MODEM_BAND_EUTRAN_26,
+ MM_MODEM_BAND_EUTRAN_28 }
+ },
+ /* 4G ext band settings: devices such as LN920 */
+ {
+ "#BND: (0),(0),(1003100185A),(42)", FALSE, TRUE, TRUE, FALSE, TRUE, 13,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_25,
+ MM_MODEM_BAND_EUTRAN_29,
+ MM_MODEM_BAND_EUTRAN_30,
+ MM_MODEM_BAND_EUTRAN_41,
+ MM_MODEM_BAND_EUTRAN_66,
+ MM_MODEM_BAND_EUTRAN_71 }
+ }
+};
+
+static void
+test_parse_supported_bands_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (supported_band_mapping_tests); i++) {
+ GError *error = NULL;
+ GArray *bands = NULL;
+ gboolean modem_ext_4g_bands;
+
+ bands = mm_telit_parse_bnd_test_response (supported_band_mapping_tests[i].response,
+ supported_band_mapping_tests[i].modem_is_2g,
+ supported_band_mapping_tests[i].modem_is_3g,
+ supported_band_mapping_tests[i].modem_is_4g,
+ supported_band_mapping_tests[i].modem_alternate_3g_bands,
+ &modem_ext_4g_bands,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (bands);
+ g_assert (supported_band_mapping_tests[i].modem_ext_4g_bands == modem_ext_4g_bands);
+
+ mm_test_helpers_compare_bands (bands,
+ supported_band_mapping_tests[i].mm_bands,
+ supported_band_mapping_tests[i].mm_bands_len);
+ g_array_unref (bands);
+ }
+}
+
+static BndResponseTest current_band_mapping_tests [] = {
+ {
+ "#BND: 0", TRUE, FALSE, FALSE, FALSE, FALSE, 2,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS }
+ },
+ {
+ "#BND: 0,5", TRUE, TRUE, FALSE, FALSE, FALSE, 3,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ {
+ "#BND: 1,3", TRUE, TRUE, FALSE, FALSE, FALSE, 5,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_5 }
+ },
+ {
+ "#BND: 2,7", TRUE, TRUE, FALSE, FALSE, FALSE, 3,
+ { MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_4 }
+ },
+ {
+ "#BND: 3,0,1", TRUE, TRUE, TRUE, FALSE, FALSE, 4,
+ { MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_1 }
+ },
+ {
+ "#BND: 0,0,3", TRUE, FALSE, TRUE, FALSE, FALSE, 4,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ {
+ "#BND: 0,0,3", FALSE, FALSE, TRUE, FALSE, FALSE, 2,
+ { MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2 }
+ },
+ /* 3G alternate band settings: default */
+ {
+ "#BND: 0,12", FALSE, TRUE, FALSE, FALSE, FALSE, 1,
+ { MM_MODEM_BAND_UTRAN_6 }
+ },
+ /* 3G alternate band settings: alternate */
+ {
+ "#BND: 0,12", FALSE, TRUE, FALSE, TRUE, FALSE, 4,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_UTRAN_3,
+ MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_8 }
+ },
+ /* ME910 (2G+4G device)
+ * 168695967: 0xA0E189F: 0000 1010 0000 1110 0001 1000 1001 1111
+ */
+ {
+ "#BND: 5,0,168695967", TRUE, FALSE, TRUE, FALSE, FALSE, 17,
+ { MM_MODEM_BAND_EGSM,
+ MM_MODEM_BAND_DCS,
+ MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_EUTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_18,
+ MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20,
+ MM_MODEM_BAND_EUTRAN_26,
+ MM_MODEM_BAND_EUTRAN_28 }
+ },
+ /* 4G ext band settings: devices such as LN920 */
+ {
+ "#BND: 0,0,1003100185A,42", FALSE, TRUE, TRUE, FALSE, TRUE, 13,
+ { MM_MODEM_BAND_UTRAN_1,
+ MM_MODEM_BAND_EUTRAN_2,
+ MM_MODEM_BAND_EUTRAN_4,
+ MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7,
+ MM_MODEM_BAND_EUTRAN_12,
+ MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_25,
+ MM_MODEM_BAND_EUTRAN_29,
+ MM_MODEM_BAND_EUTRAN_30,
+ MM_MODEM_BAND_EUTRAN_41,
+ MM_MODEM_BAND_EUTRAN_66,
+ MM_MODEM_BAND_EUTRAN_71 }
+ }
+};
+
+static void
+test_parse_current_bands_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (current_band_mapping_tests); i++) {
+ GError *error = NULL;
+ GArray *bands = NULL;
+
+ bands = mm_telit_parse_bnd_query_response (current_band_mapping_tests[i].response,
+ current_band_mapping_tests[i].modem_is_2g,
+ current_band_mapping_tests[i].modem_is_3g,
+ current_band_mapping_tests[i].modem_is_4g,
+ current_band_mapping_tests[i].modem_alternate_3g_bands,
+ current_band_mapping_tests[i].modem_ext_4g_bands,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert (bands);
+
+ mm_test_helpers_compare_bands (bands,
+ current_band_mapping_tests[i].mm_bands,
+ current_band_mapping_tests[i].mm_bands_len);
+ g_array_unref (bands);
+ }
+}
+
+/******************************************************************************/
+
+static void
+test_common_bnd_cmd (const gchar *expected_cmd,
+ gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ gboolean modem_alternate_3g_bands,
+ gboolean modem_ext_4g_bands,
+ GArray *bands_array)
+{
+ gchar *cmd;
+ GError *error = NULL;
+
+ cmd = mm_telit_build_bnd_request (bands_array,
+ modem_is_2g, modem_is_3g, modem_is_4g,
+ modem_alternate_3g_bands,
+ modem_ext_4g_bands,
+ &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (cmd, ==, expected_cmd);
+ g_free (cmd);
+}
+
+#define test_common_bnd_cmd_2g(EXPECTED_CMD, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, TRUE, FALSE, FALSE, FALSE, FALSE, BANDS_ARRAY)
+#define test_common_bnd_cmd_3g(EXPECTED_CMD, ALTERNATE, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, FALSE, TRUE, FALSE, ALTERNATE, FALSE, BANDS_ARRAY)
+#define test_common_bnd_cmd_4g(EXPECTED_CMD, EXTENDED, BANDS_ARRAY) test_common_bnd_cmd (EXPECTED_CMD, FALSE, FALSE, TRUE, FALSE, EXTENDED, BANDS_ARRAY)
+
+static void
+test_common_bnd_cmd_error (gboolean modem_is_2g,
+ gboolean modem_is_3g,
+ gboolean modem_is_4g,
+ GArray *bands_array,
+ MMCoreError expected_error)
+{
+ gchar *cmd;
+ GError *error = NULL;
+
+ cmd = mm_telit_build_bnd_request (bands_array,
+ modem_is_2g, modem_is_3g, modem_is_4g,
+ FALSE, FALSE,
+ &error);
+ g_assert_error (error, MM_CORE_ERROR, (gint)expected_error);
+ g_assert (!cmd);
+}
+
+#define test_common_bnd_cmd_2g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (TRUE, FALSE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_FAILED)
+#define test_common_bnd_cmd_3g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, TRUE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_FAILED)
+#define test_common_bnd_cmd_4g_invalid(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, FALSE, TRUE, BANDS_ARRAY, MM_CORE_ERROR_FAILED)
+#define test_common_bnd_cmd_2g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (TRUE, FALSE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND)
+#define test_common_bnd_cmd_3g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, TRUE, FALSE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND)
+#define test_common_bnd_cmd_4g_not_found(BANDS_ARRAY) test_common_bnd_cmd_error (FALSE, FALSE, TRUE, BANDS_ARRAY, MM_CORE_ERROR_NOT_FOUND)
+
+static void
+test_telit_get_2g_bnd_flag (void)
+{
+ GArray *bands_array;
+ MMModemBand egsm = MM_MODEM_BAND_EGSM;
+ MMModemBand dcs = MM_MODEM_BAND_DCS;
+ MMModemBand pcs = MM_MODEM_BAND_PCS;
+ MMModemBand g850 = MM_MODEM_BAND_G850;
+ MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1;
+ MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1;
+
+ /* Test Flag 0 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, dcs);
+ test_common_bnd_cmd_2g ("#BND=0", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 1 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, pcs);
+ test_common_bnd_cmd_2g ("#BND=1", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 2 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, g850);
+ g_array_append_val (bands_array, dcs);
+ test_common_bnd_cmd_2g ("#BND=2", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 3 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, g850);
+ g_array_append_val (bands_array, pcs);
+ test_common_bnd_cmd_2g ("#BND=3", bands_array);
+ g_array_unref (bands_array);
+
+ /* Test invalid band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, g850);
+ g_array_append_val (bands_array, egsm);
+ test_common_bnd_cmd_2g_invalid (bands_array);
+ g_array_unref (bands_array);
+
+ /* Test unmatched band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, eutran_i);
+ test_common_bnd_cmd_2g_not_found (bands_array);
+ g_array_unref (bands_array);
+}
+
+static void
+test_telit_get_3g_bnd_flag (void)
+{
+ GArray *bands_array;
+ MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1;
+ MMModemBand u1900 = MM_MODEM_BAND_UTRAN_2;
+ MMModemBand u1800 = MM_MODEM_BAND_UTRAN_3;
+ MMModemBand u850 = MM_MODEM_BAND_UTRAN_5;
+ MMModemBand u800 = MM_MODEM_BAND_UTRAN_6;
+ MMModemBand u900 = MM_MODEM_BAND_UTRAN_8;
+ MMModemBand u17iv = MM_MODEM_BAND_UTRAN_4;
+ MMModemBand u17ix = MM_MODEM_BAND_UTRAN_9;
+ MMModemBand egsm = MM_MODEM_BAND_EGSM;
+ MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1;
+
+ /* Test flag 0 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u2100);
+ test_common_bnd_cmd_3g ("#BND=0,0", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,0", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 1 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u1900);
+ test_common_bnd_cmd_3g ("#BND=0,1", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,1", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 2 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u850);
+ test_common_bnd_cmd_3g ("#BND=0,2", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,2", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 3 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 3);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, u1900);
+ g_array_append_val (bands_array, u850);
+ test_common_bnd_cmd_3g ("#BND=0,3", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,3", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 4 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, u1900);
+ g_array_append_val (bands_array, u850);
+ test_common_bnd_cmd_3g ("#BND=0,4", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,4", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 5 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u900);
+ test_common_bnd_cmd_3g ("#BND=0,5", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,5", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 6 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, u900);
+ test_common_bnd_cmd_3g ("#BND=0,6", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,6", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 7 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u17iv);
+ test_common_bnd_cmd_3g ("#BND=0,7", FALSE, bands_array);
+ test_common_bnd_cmd_3g ("#BND=0,7", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 12 in default */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u800);
+ test_common_bnd_cmd_3g ("#BND=0,12", FALSE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 12 in alternate */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4);
+ g_array_append_val (bands_array, u2100);
+ g_array_append_val (bands_array, u1800);
+ g_array_append_val (bands_array, u850);
+ g_array_append_val (bands_array, u900);
+ test_common_bnd_cmd_3g ("#BND=0,12", TRUE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test invalid band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, u17ix);
+ test_common_bnd_cmd_3g_invalid (bands_array);
+ g_array_unref (bands_array);
+
+ /* Test unmatched band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, eutran_i);
+ test_common_bnd_cmd_3g_not_found (bands_array);
+ g_array_unref (bands_array);
+}
+
+static void
+test_telit_get_4g_bnd_flag (void)
+{
+ GArray *bands_array;
+ MMModemBand eutran_i = MM_MODEM_BAND_EUTRAN_1;
+ MMModemBand eutran_ii = MM_MODEM_BAND_EUTRAN_2;
+ MMModemBand u2100 = MM_MODEM_BAND_UTRAN_1;
+ MMModemBand egsm = MM_MODEM_BAND_EGSM;
+
+ /* Test flag 1 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 1);
+ g_array_append_val (bands_array, eutran_i);
+ test_common_bnd_cmd_4g ("#BND=0,0,1", FALSE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test flag 3 */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, eutran_i);
+ g_array_append_val (bands_array, eutran_ii);
+ test_common_bnd_cmd_4g ("#BND=0,0,3", FALSE, bands_array);
+ g_array_unref (bands_array);
+
+ /* Test unmatched band array */
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 2);
+ g_array_append_val (bands_array, egsm);
+ g_array_append_val (bands_array, u2100);
+ test_common_bnd_cmd_4g_not_found (bands_array);
+ g_array_unref (bands_array);
+}
+
+/******************************************************************************/
+
+typedef struct {
+ const char* response;
+ MMTelitQssStatus expected_qss;
+ const char *error_message;
+} QssParseTest;
+
+static QssParseTest qss_parse_tests [] = {
+ {"#QSS: 0,0", QSS_STATUS_SIM_REMOVED, NULL},
+ {"#QSS: 1,0", QSS_STATUS_SIM_REMOVED, NULL},
+ {"#QSS: 0,1", QSS_STATUS_SIM_INSERTED, NULL},
+ {"#QSS: 0,2", QSS_STATUS_SIM_INSERTED_AND_UNLOCKED, NULL},
+ {"#QSS: 0,3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL},
+ {"#QSS:0,3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL},
+ {"#QSS: 0, 3", QSS_STATUS_SIM_INSERTED_AND_READY, NULL},
+ {"#QSS: 0", QSS_STATUS_UNKNOWN, "Could not parse \"#QSS?\" response: #QSS: 0"},
+ {"QSS:0,1", QSS_STATUS_UNKNOWN, "Could not parse \"#QSS?\" response: QSS:0,1"},
+ {"#QSS: 0,5", QSS_STATUS_UNKNOWN, "Unknown QSS status value given: 5"},
+};
+
+static void
+test_telit_parse_qss_query (void)
+{
+ MMTelitQssStatus actual_qss_status;
+ GError *error = NULL;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (qss_parse_tests); i++) {
+ actual_qss_status = mm_telit_parse_qss_query (qss_parse_tests[i].response, &error);
+
+ g_assert_cmpint (actual_qss_status, ==, qss_parse_tests[i].expected_qss);
+ if (qss_parse_tests[i].error_message) {
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+ g_assert_cmpstr (error->message, ==, qss_parse_tests[i].error_message);
+ g_clear_error (&error);
+ }
+ }
+}
+
+/******************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/telit/bands/supported/parse_bands_response", test_parse_supported_bands_response);
+ g_test_add_func ("/MM/telit/bands/current/parse_bands_response", test_parse_current_bands_response);
+ g_test_add_func ("/MM/telit/bands/current/set_bands/2g", test_telit_get_2g_bnd_flag);
+ g_test_add_func ("/MM/telit/bands/current/set_bands/3g", test_telit_get_3g_bnd_flag);
+ g_test_add_func ("/MM/telit/bands/current/set_bands/4g", test_telit_get_4g_bnd_flag);
+ g_test_add_func ("/MM/telit/qss/query", test_telit_parse_qss_query);
+ return g_test_run ();
+}
diff --git a/plugins/tests/test-fixture.c b/plugins/tests/test-fixture.c
index 81644ab0..29eb8d55 100644
--- a/plugins/tests/test-fixture.c
+++ b/plugins/tests/test-fixture.c
@@ -37,7 +37,7 @@ test_fixture_setup (TestFixture *fixture)
if (fixture->connection == NULL)
g_error ("Error getting connection to test bus: %s", error->message);
- /* Ping to autostart MM; wait up to 3s */
+ /* Ping to autostart MM; wait up to 30s */
result = g_dbus_connection_call_sync (fixture->connection,
"org.freedesktop.ModemManager1",
"/org/freedesktop/ModemManager1",
@@ -97,90 +97,67 @@ test_fixture_set_profile (TestFixture *fixture,
g_error ("Error setting test profile: %s", error->message);
}
-MMObject *
-test_fixture_get_modem (TestFixture *fixture)
+static MMObject *
+common_get_modem (TestFixture *fixture,
+ gboolean modem_expected)
{
- GError *error = NULL;
- MMManager *manager;
MMObject *found = NULL;
- guint wait_time = 0;
-
- /* Create manager */
- g_assert (fixture->connection != NULL);
- manager = mm_manager_new_sync (fixture->connection,
- G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
- NULL, /* cancellable */
- &error);
- if (!manager)
- g_error ("Couldn't create manager: %s", error->message);
+ guint wait_time = 0;
/* Find new modem object */
- while (!found) {
- GList *modems;
- guint n_modems;
+ while (TRUE) {
+ GError *error = NULL;
+ MMManager *manager;
+ GList *modems;
+ guint n_modems;
+ gboolean ready = FALSE;
+
+ /* Create manager on each loop, so that we don't require on an external
+ * global main context processing to receive the DBus property updates.
+ */
+ g_assert (fixture->connection != NULL);
+ manager = mm_manager_new_sync (fixture->connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+ NULL, /* cancellable */
+ &error);
+ if (!manager)
+ g_error ("Couldn't create manager: %s", error->message);
modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
n_modems = g_list_length (modems);
g_assert_cmpuint (n_modems, <=, 1);
- if (n_modems == 0) {
- /* Wait a bit before re-checking. We can do this kind of wait
- * because properties in the manager are updated in another
- * thread */
- g_assert_cmpuint (wait_time, <=, 20);
- wait_time++;
- sleep (1);
- } else
- found = MM_OBJECT (g_object_ref (modems->data));
-
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
- }
+ if ((guint)modem_expected == n_modems) {
+ if (modems) {
+ found = MM_OBJECT (g_object_ref (modems->data));
+ g_message ("Found modem at '%s'", mm_object_get_path (found));
+ }
+ ready = TRUE;
+ }
- g_message ("Found modem at '%s'", mm_object_get_path (found));
+ g_list_free_full (modems, g_object_unref);
+ g_object_unref (manager);
- g_object_unref (manager);
+ if (ready)
+ break;
+
+ /* Blocking wait */
+ g_assert_cmpuint (wait_time, <=, 20);
+ wait_time++;
+ sleep (1);
+ }
return found;
}
+MMObject *
+test_fixture_get_modem (TestFixture *fixture)
+{
+ return common_get_modem (fixture, TRUE);
+}
+
void
test_fixture_no_modem (TestFixture *fixture)
{
- GError *error = NULL;
- MMManager *manager;
- guint wait_time = 0;
- gboolean no_modems = FALSE;
-
- /* Create manager */
- g_assert (fixture->connection != NULL);
- manager = mm_manager_new_sync (fixture->connection,
- G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
- NULL, /* cancellable */
- &error);
- if (!manager)
- g_error ("Couldn't create manager: %s", error->message);
-
- /* Find new modem object */
- while (!no_modems) {
- GList *modems;
- guint n_modems;
-
- modems = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
- n_modems = g_list_length (modems);
- g_assert_cmpuint (n_modems, <=, 1);
-
- if (n_modems == 1) {
- /* Wait a bit before re-checking. We can do this kind of wait
- * because properties in the manager are updated in another
- * thread */
- g_assert_cmpuint (wait_time, <=, 20);
- wait_time++;
- sleep (1);
- } else
- no_modems = TRUE;
-
- g_list_free_full (modems, (GDestroyNotify) g_object_unref);
- }
-
- g_object_unref (manager);
+ common_get_modem (fixture, FALSE);
}
diff --git a/plugins/tests/test-helpers.c b/plugins/tests/test-helpers.c
new file mode 100644
index 00000000..8148d908
--- /dev/null
+++ b/plugins/tests/test-helpers.c
@@ -0,0 +1,52 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+
+#include "test-helpers.h"
+
+void
+mm_test_helpers_compare_bands (GArray *bands,
+ const MMModemBand *expected_bands,
+ guint n_expected_bands)
+{
+ gchar *bands_str;
+ GArray *expected_bands_array;
+ gchar *expected_bands_str;
+
+ if (!expected_bands || !n_expected_bands) {
+ g_assert (!bands);
+ return;
+ }
+
+ g_assert (bands);
+ mm_common_bands_garray_sort (bands);
+ bands_str = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len);
+
+ expected_bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_expected_bands);
+ g_array_append_vals (expected_bands_array, expected_bands, n_expected_bands);
+ mm_common_bands_garray_sort (expected_bands_array);
+ expected_bands_str = mm_common_build_bands_string ((MMModemBand *)(gpointer)(expected_bands_array->data), expected_bands_array->len);
+ g_array_unref (expected_bands_array);
+
+ g_assert_cmpstr (bands_str, ==, expected_bands_str);
+ g_free (bands_str);
+ g_free (expected_bands_str);
+}
diff --git a/plugins/tests/test-helpers.h b/plugins/tests/test-helpers.h
new file mode 100644
index 00000000..8362754c
--- /dev/null
+++ b/plugins/tests/test-helpers.h
@@ -0,0 +1,26 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef TEST_HELPERS_H
+#define TEST_HELPERS_H
+
+#include <glib.h>
+#include <libmm-glib.h>
+
+void mm_test_helpers_compare_bands (GArray *bands,
+ const MMModemBand *expected_bands,
+ guint n_expected_bands);
+
+#endif /* TEST_HELPERS_H */
diff --git a/plugins/tests/test-keyfiles.c b/plugins/tests/test-keyfiles.c
new file mode 100644
index 00000000..d528cac9
--- /dev/null
+++ b/plugins/tests/test-keyfiles.c
@@ -0,0 +1,79 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+#include <config.h>
+
+#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-log-test.h"
+
+/************************************************************/
+
+static void
+common_test (const gchar *keyfile_path)
+{
+ GKeyFile *keyfile;
+ GError *error = NULL;
+ gboolean ret;
+
+ if (!keyfile_path)
+ return;
+
+ keyfile = g_key_file_new ();
+ ret = g_key_file_load_from_file (keyfile, keyfile_path, G_KEY_FILE_NONE, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_key_file_unref (keyfile);
+}
+
+/* Dummy test to avoid compiler warning about common_test() being unused
+ * when none of the plugins enabled in build have custom key files. */
+static void
+test_dummy (void)
+{
+ common_test (NULL);
+}
+
+/************************************************************/
+
+#if defined ENABLE_PLUGIN_FOXCONN
+static void
+test_foxconn_t77w968 (void)
+{
+ common_test (TESTKEYFILE_FOXCONN_T77W968);
+}
+#endif
+
+/************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/MM/test-keyfiles/dummy", test_dummy);
+
+#if defined ENABLE_PLUGIN_FOXCONN
+ g_test_add_func ("/MM/test-keyfiles/foxconn/t77w968", test_foxconn_t77w968);
+#endif
+
+ return g_test_run ();
+}
diff --git a/plugins/tests/test-port-context.c b/plugins/tests/test-port-context.c
index cbf202f0..e96cff7b 100644
--- a/plugins/tests/test-port-context.c
+++ b/plugins/tests/test-port-context.c
@@ -28,6 +28,8 @@ struct _TestPortContext {
GCond ready_cond;
GMutex ready_mutex;
GMainLoop *loop;
+ GMainContext *context;
+ GSocket *socket;
GSocketService *socket_service;
GList *clients;
GHashTable *commands;
@@ -235,7 +237,7 @@ client_new (TestPortContext *self,
(GSourceFunc)connection_readable_cb,
client,
NULL);
- g_source_attach (client->connection_readable_source, NULL);
+ g_source_attach (client->connection_readable_source, self->context);
return client;
}
@@ -276,9 +278,12 @@ create_socket_service (TestPortContext *self)
address = (g_unix_socket_address_new_with_type (
self->name,
-1,
- G_UNIX_SOCKET_ADDRESS_ABSTRACT));
+ (g_str_has_prefix (self->name, "abstract:") ?
+ G_UNIX_SOCKET_ADDRESS_ABSTRACT :
+ G_UNIX_SOCKET_ADDRESS_PATH)));
if (!g_socket_bind (socket, address, TRUE, &error))
g_error ("Cannot bind socket: %s", error->message);
+ g_object_unref (address);
/* Listen */
if (!g_socket_listen (socket, &error))
@@ -296,46 +301,63 @@ create_socket_service (TestPortContext *self)
/* Start it */
g_socket_service_start (service);
- /* And store it */
+ /* And store both the service and the socket.
+ * Since GLib 2.42 the socket may not be explicitly closed when the
+ * listener is diposed, so we'll do it ourselves. */
self->socket_service = service;
+ self->socket = socket;
/* Signal that the thread is ready */
g_mutex_lock (&self->ready_mutex);
self->ready = TRUE;
g_cond_signal (&self->ready_cond);
g_mutex_unlock (&self->ready_mutex);
-
- if (socket)
- g_object_unref (socket);
- if (address)
- g_object_unref (address);
}
/*****************************************************************************/
+static gboolean
+cancel_loop_cb (TestPortContext *self)
+{
+ g_main_loop_quit (self->loop);
+ return FALSE;
+}
+
void
test_port_context_stop (TestPortContext *self)
{
g_assert (self->thread != NULL);
g_assert (self->loop != NULL);
+ g_assert (self->context != NULL);
- g_main_loop_quit (self->loop);
+ /* Cancel main loop of the port context thread, by scheduling an idle task
+ * in the thread-owned main context */
+ g_main_context_invoke (self->context, (GSourceFunc) cancel_loop_cb, self);
g_thread_join (self->thread);
- g_thread_unref (self->thread);
self->thread = NULL;
}
static gpointer
port_context_thread_func (TestPortContext *self)
{
+ g_assert (self->loop == NULL);
+ g_assert (self->context == NULL);
+
+ /* Define main context and loop for the thread */
+ self->context = g_main_context_new ();
+ self->loop = g_main_loop_new (self->context, FALSE);
+ g_main_context_push_thread_default (self->context);
+
+ /* Once the thread default context is setup, launch service */
create_socket_service (self);
- g_assert (self->loop == NULL);
- self->loop = g_main_loop_new (g_main_context_get_thread_default (), FALSE);
g_main_loop_run (self->loop);
+
g_main_loop_unref (self->loop);
self->loop = NULL;
+ g_main_context_unref (self->context);
+ self->context = NULL;
return NULL;
}
@@ -369,6 +391,15 @@ test_port_context_free (TestPortContext *self)
if (self->commands)
g_hash_table_unref (self->commands);
g_list_free_full (self->clients, (GDestroyNotify)client_free);
+ if (self->socket) {
+ GError *error = NULL;
+
+ if (!g_socket_close (self->socket, &error)) {
+ g_debug ("Couldn't close socket: %s", error->message);
+ g_error_free (error);
+ }
+ g_object_unref (self->socket);
+ }
if (self->socket_service) {
if (g_socket_service_is_active (self->socket_service))
g_socket_service_stop (self->socket_service);
diff --git a/plugins/tests/test-udev-rules.c b/plugins/tests/test-udev-rules.c
new file mode 100644
index 00000000..2f7190f1
--- /dev/null
+++ b/plugins/tests/test-udev-rules.c
@@ -0,0 +1,246 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#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-kernel-device-generic-rules.h"
+#include "mm-log-test.h"
+
+/************************************************************/
+
+static void
+common_test (const gchar *plugindir)
+{
+ GArray *rules;
+ GError *error = NULL;
+
+ if (!plugindir)
+ return;
+
+ rules = mm_kernel_device_generic_rules_load (plugindir, &error);
+ g_assert_no_error (error);
+ g_assert (rules);
+ g_assert (rules->len > 0);
+
+ g_array_unref (rules);
+}
+
+/* Dummy test to avoid compiler warning about common_test() being unused
+ * when none of the plugins enabled in build have custom udev rules. */
+static void
+test_dummy (void)
+{
+ common_test (NULL);
+}
+
+/************************************************************/
+
+#if defined ENABLE_PLUGIN_HUAWEI
+static void
+test_huawei (void)
+{
+ common_test (TESTUDEVRULESDIR_HUAWEI);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_MBM
+static void
+test_mbm (void)
+{
+ common_test (TESTUDEVRULESDIR_MBM);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_NOKIA_ICERA
+static void
+test_nokia_icera (void)
+{
+ common_test (TESTUDEVRULESDIR_NOKIA_ICERA);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_ZTE
+static void
+test_zte (void)
+{
+ common_test (TESTUDEVRULESDIR_ZTE);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_LONGCHEER
+static void
+test_longcheer (void)
+{
+ common_test (TESTUDEVRULESDIR_LONGCHEER);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_SIMTECH
+static void
+test_simtech (void)
+{
+ common_test (TESTUDEVRULESDIR_SIMTECH);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_X22X
+static void
+test_x22x (void)
+{
+ common_test (TESTUDEVRULESDIR_X22X);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_CINTERION
+static void
+test_cinterion (void)
+{
+ common_test (TESTUDEVRULESDIR_CINTERION);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_DELL
+static void
+test_dell (void)
+{
+ common_test (TESTUDEVRULESDIR_DELL);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_TELIT
+static void
+test_telit (void)
+{
+ common_test (TESTUDEVRULESDIR_TELIT);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_MTK
+static void
+test_mtk (void)
+{
+ common_test (TESTUDEVRULESDIR_MTK);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_HAIER
+static void
+test_haier (void)
+{
+ common_test (TESTUDEVRULESDIR_HAIER);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_FIBOCOM
+static void
+test_fibocom (void)
+{
+ common_test (TESTUDEVRULESDIR_FIBOCOM);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_QUECTEL
+static void
+test_quectel (void)
+{
+ common_test (TESTUDEVRULESDIR_QUECTEL);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_GOSUNCN
+static void
+test_gosuncn (void)
+{
+ common_test (TESTUDEVRULESDIR_GOSUNCN);
+}
+#endif
+
+#if defined ENABLE_PLUGIN_QCOM_SOC && defined WITH_QMI
+static void
+test_qcom_soc (void)
+{
+ common_test (TESTUDEVRULESDIR_QCOM_SOC);
+}
+#endif
+
+/************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/MM/test-udev-rules/dummy", test_dummy);
+
+#if defined ENABLE_PLUGIN_HUAWEI
+ g_test_add_func ("/MM/test-udev-rules/huawei", test_huawei);
+#endif
+#if defined ENABLE_PLUGIN_MBM
+ g_test_add_func ("/MM/test-udev-rules/mbm", test_mbm);
+#endif
+#if defined ENABLE_PLUGIN_NOKIA_ICERA
+ g_test_add_func ("/MM/test-udev-rules/nokia-icera", test_nokia_icera);
+#endif
+#if defined ENABLE_PLUGIN_ZTE
+ g_test_add_func ("/MM/test-udev-rules/zte", test_zte);
+#endif
+#if defined ENABLE_PLUGIN_LONGCHEER
+ g_test_add_func ("/MM/test-udev-rules/longcheer", test_longcheer);
+#endif
+#if defined ENABLE_PLUGIN_SIMTECH
+ g_test_add_func ("/MM/test-udev-rules/simtech", test_simtech);
+#endif
+#if defined ENABLE_PLUGIN_X22X
+ g_test_add_func ("/MM/test-udev-rules/x22x", test_x22x);
+#endif
+#if defined ENABLE_PLUGIN_CINTERION
+ g_test_add_func ("/MM/test-udev-rules/cinterion", test_cinterion);
+#endif
+#if defined ENABLE_PLUGIN_DELL
+ g_test_add_func ("/MM/test-udev-rules/dell", test_dell);
+#endif
+#if defined ENABLE_PLUGIN_TELIT
+ g_test_add_func ("/MM/test-udev-rules/telit", test_telit);
+#endif
+#if defined ENABLE_PLUGIN_MTK
+ g_test_add_func ("/MM/test-udev-rules/mtk", test_mtk);
+#endif
+#if defined ENABLE_PLUGIN_HAIER
+ g_test_add_func ("/MM/test-udev-rules/haier", test_haier);
+#endif
+#if defined ENABLE_PLUGIN_FIBOCOM
+ g_test_add_func ("/MM/test-udev-rules/fibocom", test_fibocom);
+#endif
+#if defined ENABLE_PLUGIN_QUECTEL
+ g_test_add_func ("/MM/test-udev-rules/quectel", test_quectel);
+#endif
+#if defined ENABLE_PLUGIN_GOSUNCN
+ g_test_add_func ("/MM/test-udev-rules/gosuncn", test_gosuncn);
+#endif
+#if defined ENABLE_PLUGIN_QCOM_SOC && defined WITH_QMI
+ g_test_add_func ("/MM/test-udev-rules/qcom-soc", test_qcom_soc);
+#endif
+
+ return g_test_run ();
+}
diff --git a/plugins/thuraya/mm-broadband-modem-thuraya.c b/plugins/thuraya/mm-broadband-modem-thuraya.c
new file mode 100644
index 00000000..92c8a8a2
--- /dev/null
+++ b/plugins/thuraya/mm-broadband-modem-thuraya.c
@@ -0,0 +1,284 @@
+/* -*- 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 Ammonit Measurement GmbH
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ * Copyright (C) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-errors-types.h"
+#include "mm-base-modem-at.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-messaging.h"
+#include "mm-broadband-modem-thuraya.h"
+#include "mm-broadband-bearer.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-thuraya.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemThuraya, mm_broadband_modem_thuraya, MM_TYPE_BROADBAND_MODEM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) );
+
+/*****************************************************************************/
+/* Operator Code and Name loading (3GPP interface) */
+
+static gchar *
+load_operator_code_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_operator_code (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, g_strdup ("90106"), g_free);
+ g_object_unref (task);
+}
+
+static gchar *
+load_operator_name_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_operator_name (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, g_strdup ("THURAYA"), g_free);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load supported modes (Modem inteface) */
+
+static GArray *
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GArray *combinations;
+ MMModemModeCombination mode;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Build list of combinations */
+ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+
+ /* Report any, Thuraya connections are packet-switched */
+ mode.allowed = MM_MODEM_MODE_ANY;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+
+ g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load supported SMS storages (Messaging interface) */
+
+typedef struct {
+ GArray *mem1;
+ GArray *mem2;
+ GArray *mem3;
+} SupportedStoragesResult;
+
+static void
+supported_storages_result_free (SupportedStoragesResult *result)
+{
+ if (result->mem1)
+ g_array_unref (result->mem1);
+ if (result->mem2)
+ g_array_unref (result->mem2);
+ if (result->mem3)
+ g_array_unref (result->mem3);
+ g_free (result);
+}
+
+static gboolean
+modem_messaging_load_supported_storages_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GArray **mem1,
+ GArray **mem2,
+ GArray **mem3,
+ GError **error)
+{
+ SupportedStoragesResult *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ *mem1 = g_array_ref (result->mem1);
+ *mem2 = g_array_ref (result->mem2);
+ *mem3 = g_array_ref (result->mem3);
+ supported_storages_result_free (result);
+ return TRUE;
+}
+
+static void
+cpms_format_check_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ SupportedStoragesResult *result;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ result = g_new0 (SupportedStoragesResult, 1);
+
+ /* Parse reply */
+ if (!mm_thuraya_3gpp_parse_cpms_test_response (response,
+ &result->mem1,
+ &result->mem2,
+ &result->mem3,
+ &error)) {
+ supported_storages_result_free (result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_pointer (task, result, (GDestroyNotify) supported_storages_result_free);
+ g_object_unref (task);
+}
+
+static void
+modem_messaging_load_supported_storages (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Check support storages */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CPMS=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)cpms_format_check_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemThuraya *
+mm_broadband_modem_thuraya_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_THURAYA,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_thuraya_init (MMBroadbandModemThuraya *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ /* No need to power-up/power-down the modem */
+ iface->load_power_state = NULL;
+ iface->load_power_state_finish = NULL;
+ iface->modem_power_up = NULL;
+ iface->modem_power_up_finish = NULL;
+ iface->modem_power_down = NULL;
+ iface->modem_power_down_finish = NULL;
+
+ /* Supported modes cannot be queried */
+ iface->load_supported_modes = load_supported_modes;
+ iface->load_supported_modes_finish = load_supported_modes_finish;
+}
+
+static void
+iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
+{
+ /* Fixed operator code and name to be reported */
+ iface->load_operator_name = load_operator_name;
+ iface->load_operator_name_finish = load_operator_name_finish;
+ iface->load_operator_code = load_operator_code;
+ iface->load_operator_code_finish = load_operator_code_finish;
+
+ /* Don't try to scan networks with AT+COPS=?.
+ * The Thuraya XT does not seem to properly support AT+COPS=?.
+ * When issuing this command, it seems to get sufficiently confused
+ * to drop the signal. Furthermore, it is useless anyway as there is only
+ * one network supported, Thuraya.
+ */
+ iface->scan_networks = NULL;
+ iface->scan_networks_finish = NULL;
+}
+
+static void
+iface_modem_messaging_init (MMIfaceModemMessaging *iface)
+{
+ iface->load_supported_storages = modem_messaging_load_supported_storages;
+ iface->load_supported_storages_finish = modem_messaging_load_supported_storages_finish;
+}
+
+static void
+mm_broadband_modem_thuraya_class_init (MMBroadbandModemThurayaClass *klass)
+{
+}
diff --git a/plugins/thuraya/mm-broadband-modem-thuraya.h b/plugins/thuraya/mm-broadband-modem-thuraya.h
new file mode 100644
index 00000000..42df9b9c
--- /dev/null
+++ b/plugins/thuraya/mm-broadband-modem-thuraya.h
@@ -0,0 +1,50 @@
+/* -*- 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) 2008 - 2009 Novell, Inc.
+ * Copyright (C) 2009 - 2011 Red Hat, Inc.
+ * Copyright (C) 2011 Google Inc.
+ * Copyright (C) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ */
+
+#ifndef MM_BROADBAND_MODEM_THURAYA_H
+#define MM_BROADBAND_MODEM_THURAYA_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_THURAYA (mm_broadband_modem_thuraya_get_type ())
+#define MM_BROADBAND_MODEM_THURAYA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThuraya))
+#define MM_BROADBAND_MODEM_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThurayaClass))
+#define MM_IS_BROADBAND_MODEM_THURAYA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA))
+#define MM_IS_BROADBAND_MODEM_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_THURAYA))
+#define MM_BROADBAND_MODEM_THURAYA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_THURAYA, MMBroadbandModemThurayaClass))
+
+typedef struct _MMBroadbandModemThuraya MMBroadbandModemThuraya;
+typedef struct _MMBroadbandModemThurayaClass MMBroadbandModemThurayaClass;
+
+struct _MMBroadbandModemThuraya {
+ MMBroadbandModem parent;
+};
+
+struct _MMBroadbandModemThurayaClass{
+ MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_thuraya_get_type (void);
+
+MMBroadbandModemThuraya *mm_broadband_modem_thuraya_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_THURAYA_H */
diff --git a/plugins/thuraya/mm-modem-helpers-thuraya.c b/plugins/thuraya/mm-modem-helpers-thuraya.c
new file mode 100644
index 00000000..0c713a18
--- /dev/null
+++ b/plugins/thuraya/mm-modem-helpers-thuraya.c
@@ -0,0 +1,148 @@
+/* -*- 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) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MMCLI
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-thuraya.h"
+
+/*************************************************************************/
+
+static MMSmsStorage
+storage_from_str (const gchar *str)
+{
+ if (g_str_equal (str, "SM"))
+ return MM_SMS_STORAGE_SM;
+ if (g_str_equal (str, "ME"))
+ return MM_SMS_STORAGE_ME;
+ if (g_str_equal (str, "MT"))
+ return MM_SMS_STORAGE_MT;
+ if (g_str_equal (str, "SR"))
+ return MM_SMS_STORAGE_SR;
+ if (g_str_equal (str, "BM"))
+ return MM_SMS_STORAGE_BM;
+ if (g_str_equal (str, "TA"))
+ return MM_SMS_STORAGE_TA;
+ return MM_SMS_STORAGE_UNKNOWN;
+}
+
+gboolean
+mm_thuraya_3gpp_parse_cpms_test_response (const gchar *reply,
+ GArray **mem1,
+ GArray **mem2,
+ GArray **mem3,
+ GError **error)
+{
+#define N_EXPECTED_GROUPS 3
+
+ gchar **splitp;
+ const gchar *splita[N_EXPECTED_GROUPS];
+ guint i;
+ g_auto(GStrv) split = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GArray) tmp1 = NULL;
+ g_autoptr(GArray) tmp2 = NULL;
+ g_autoptr(GArray) tmp3 = NULL;
+
+ g_assert (mem1 != NULL);
+ g_assert (mem2 != NULL);
+ g_assert (mem3 != NULL);
+
+ split = g_strsplit (mm_strip_tag (reply, "+CPMS:"), " ", -1);
+ if (!split) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't split +CPMS response");
+ return FALSE;
+ }
+
+ /* remove empty strings, and count non-empty strings */
+ i = 0;
+ for (splitp = split; *splitp; ++splitp) {
+ if (!**splitp)
+ continue;
+ if (i < N_EXPECTED_GROUPS)
+ splita[i] = *splitp;
+ ++i;
+ }
+
+ if (i != N_EXPECTED_GROUPS) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPMS response: invalid number of groups (%u != %u)",
+ i, N_EXPECTED_GROUPS);
+ return FALSE;
+ }
+
+ r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL);
+ g_assert (r);
+
+ for (i = 0; i < N_EXPECTED_GROUPS; i++) {
+ g_autoptr(GMatchInfo) match_info = NULL;
+ GArray *array;
+
+ /* We always return a valid array, even if it may be empty */
+ array = g_array_new (FALSE, FALSE, sizeof (MMSmsStorage));
+
+ /* Got a range group to match */
+ if (g_regex_match (r, splita[i], 0, &match_info)) {
+ while (g_match_info_matches (match_info)) {
+ g_autofree gchar *str = NULL;
+
+ str = g_match_info_fetch (match_info, 1);
+ if (str) {
+ MMSmsStorage storage;
+
+ storage = storage_from_str (str);
+ g_array_append_val (array, storage);
+ }
+
+ g_match_info_next (match_info, NULL);
+ }
+ }
+
+ if (!tmp1)
+ tmp1 = array;
+ else if (!tmp2)
+ tmp2 = array;
+ else if (!tmp3)
+ tmp3 = array;
+ else
+ g_assert_not_reached ();
+ }
+
+ /* Only return TRUE if all sets have been parsed correctly
+ * (even if the arrays may be empty) */
+ if (tmp1 && tmp2 && tmp3) {
+ *mem1 = g_steal_pointer (&tmp1);
+ *mem2 = g_steal_pointer (&tmp2);
+ *mem3 = g_steal_pointer (&tmp3);
+ return TRUE;
+ }
+
+ /* Otherwise, cleanup and return FALSE */
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPMS response: not all groups detected (mem1 %s, mem2 %s, mem3 %s)",
+ tmp1 ? "yes" : "no",
+ tmp2 ? "yes" : "no",
+ tmp3 ? "yes" : "no");
+ return FALSE;
+}
diff --git a/plugins/thuraya/mm-modem-helpers-thuraya.h b/plugins/thuraya/mm-modem-helpers-thuraya.h
new file mode 100644
index 00000000..33bb079f
--- /dev/null
+++ b/plugins/thuraya/mm-modem-helpers-thuraya.h
@@ -0,0 +1,28 @@
+/* -*- 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) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ *
+ */
+#ifndef MM_MODEM_HELPERS_THURAYA_H
+#define MM_MODEM_HELPERS_THURAYA_H
+
+#include <glib.h>
+
+/* AT+CPMS=? (Preferred SMS storage) response parser */
+gboolean mm_thuraya_3gpp_parse_cpms_test_response (const gchar *reply,
+ GArray **mem1,
+ GArray **mem2,
+ GArray **mem3,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_THURAYA_H */
diff --git a/plugins/thuraya/mm-plugin-thuraya.c b/plugins/thuraya/mm-plugin-thuraya.c
new file mode 100644
index 00000000..64e1b452
--- /dev/null
+++ b/plugins/thuraya/mm-plugin-thuraya.c
@@ -0,0 +1,84 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ * Copyright (C) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-plugin-thuraya.h"
+#include "mm-broadband-modem.h"
+#include "mm-broadband-modem-thuraya.h"
+#include "mm-private-boxed-types.h"
+
+G_DEFINE_TYPE (MMPluginThuraya, mm_plugin_thuraya, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ return MM_BASE_MODEM (mm_broadband_modem_thuraya_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", NULL };
+ static const guint16 vendor_ids[] = { 0x1a26, 0 };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_THURAYA,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_thuraya_init (MMPluginThuraya *self)
+{
+}
+
+static void
+mm_plugin_thuraya_class_init (MMPluginThurayaClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/thuraya/mm-plugin-thuraya.h b/plugins/thuraya/mm-plugin-thuraya.h
new file mode 100644
index 00000000..fb86090d
--- /dev/null
+++ b/plugins/thuraya/mm-plugin-thuraya.h
@@ -0,0 +1,48 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) 2011 - 2012 Ammonit Measurement GmbH
+ * Author: Aleksander Morgado <aleksander@lanedo.com>
+ * Copyright (C) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ */
+
+#ifndef MM_PLUGIN_THURAYA_H
+#define MM_PLUGIN_THURAYA_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_THURAYA (mm_plugin_thuraya_get_type ())
+#define MM_PLUGIN_THURAYA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_THURAYA, MMPluginThuraya))
+#define MM_PLUGIN_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_THURAYA, MMPluginThurayaClass))
+#define MM_IS_PLUGIN_THURAYA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_THURAYA))
+#define MM_IS_PLUGIN_THURAYA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_THURAYA))
+#define MM_PLUGIN_THURAYA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_THURAYA, MMPluginThurayaClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginThuraya;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginThurayaClass;
+
+GType mm_plugin_thuraya_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_THURAYA_H */
diff --git a/plugins/thuraya/tests/test-mm-modem-helpers-thuraya.c b/plugins/thuraya/tests/test-mm-modem-helpers-thuraya.c
new file mode 100644
index 00000000..bdc073d0
--- /dev/null
+++ b/plugins/thuraya/tests/test-mm-modem-helpers-thuraya.c
@@ -0,0 +1,106 @@
+/* -*- 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) 2016 Thomas Sailer <t.sailer@alumni.ethz.ch>
+ *
+ */
+#include <stdio.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-thuraya.h"
+#include "mm-log-test.h"
+
+/*****************************************************************************/
+/* Test CPMS response */
+
+static gboolean
+is_storage_supported (GArray *supported,
+ MMSmsStorage storage)
+{
+ guint i;
+
+ for (i = 0; i < supported->len; i++) {
+ if (storage == g_array_index (supported, MMSmsStorage, i))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+test_cpms_response_thuraya (void *f, gpointer d)
+{
+ /*
+ * First: ("ME","MT") 2-item group
+ * Second: "ME" 1 item
+ * Third: ("SM") 1-item group
+ */
+ const gchar *reply = "+CPMS: \"MT\",\"SM\",\"BM\",\"ME\",\"SR\", \"MT\",\"SM\",\"BM\",\"ME\",\"SR\", \"MT\",\"SM\",\"BM\",\"ME\",\"SR\" ";
+ GArray *mem1 = NULL;
+ GArray *mem2 = NULL;
+ GArray *mem3 = NULL;
+ GError *error = NULL;
+
+ g_debug ("Testing thuraya +CPMS=? response...");
+
+ g_assert (mm_thuraya_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 5);
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT));
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_SM));
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_BM));
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME));
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_SR));
+ g_assert_cmpuint (mem2->len, ==, 5);
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_MT));
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_SM));
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_BM));
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME));
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_SR));
+ g_assert_cmpuint (mem3->len, ==, 5);
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_MT));
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM));
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_BM));
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_ME));
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SR));
+
+ g_array_unref (mem1);
+ g_array_unref (mem2);
+ g_array_unref (mem3);
+}
+
+/*****************************************************************************/
+
+#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL)
+
+int main (int argc, char **argv)
+{
+ GTestSuite *suite;
+ gint result;
+
+ g_test_init (&argc, &argv, NULL);
+
+ suite = g_test_get_root ();
+
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_thuraya, NULL));
+
+ result = g_test_run ();
+
+ return result;
+}
diff --git a/plugins/tplink/77-mm-tplink-port-types.rules b/plugins/tplink/77-mm-tplink-port-types.rules
new file mode 100644
index 00000000..f5c8894e
--- /dev/null
+++ b/plugins/tplink/77-mm-tplink-port-types.rules
@@ -0,0 +1,15 @@
+# do not edit this file, it will be overwritten on update
+
+ACTION!="add|change|move|bind", GOTO="mm_tplink_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="2357", GOTO="mm_tplink_port_types"
+GOTO="mm_tplink_port_types_end"
+
+LABEL="mm_tplink_port_types"
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# D-Link DWM-222
+ATTRS{idVendor}=="2357", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="2357", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="2357", ATTRS{idProduct}=="9000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+
+LABEL="mm_tplink_port_types_end"
diff --git a/plugins/tplink/mm-plugin-tplink.c b/plugins/tplink/mm-plugin-tplink.c
new file mode 100644
index 00000000..4698ba16
--- /dev/null
+++ b/plugins/tplink/mm-plugin-tplink.c
@@ -0,0 +1,94 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-port-enums-types.h"
+#include "mm-log-object.h"
+#include "mm-plugin-tplink.h"
+#include "mm-broadband-modem.h"
+
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+
+G_DEFINE_TYPE (MMPluginTplink, mm_plugin_tplink, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *uid,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+#if defined WITH_QMI
+ if (mm_port_probe_list_has_qmi_port (probes)) {
+ mm_obj_dbg (self, "QMI-powered TP-Link modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+ }
+#endif
+
+ return MM_BASE_MODEM (mm_broadband_modem_new (uid,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
+ static const guint16 vendor_ids[] = { 0x2357, 0 };
+
+ return MM_PLUGIN (
+ g_object_new (MM_TYPE_PLUGIN_TPLINK,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_ALLOWED_QMI, TRUE,
+ NULL));
+}
+
+static void
+mm_plugin_tplink_init (MMPluginTplink *self)
+{
+}
+
+static void
+mm_plugin_tplink_class_init (MMPluginTplinkClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/tplink/mm-plugin-tplink.h b/plugins/tplink/mm-plugin-tplink.h
new file mode 100644
index 00000000..16dc5f5b
--- /dev/null
+++ b/plugins/tplink/mm-plugin-tplink.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_TPLINK_H
+#define MM_PLUGIN_TPLINK_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_TPLINK (mm_plugin_tplink_get_type ())
+#define MM_PLUGIN_TPLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_TPLINK, MMPluginTplink))
+#define MM_PLUGIN_TPLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_TPLINK, MMPluginTplinkClass))
+#define MM_IS_PLUGIN_TPLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_TPLINK))
+#define MM_IS_PLUGIN_TPLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_TPLINK))
+#define MM_PLUGIN_TPLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_TPLINK, MMPluginTplinkClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginTplink;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginTplinkClass;
+
+GType mm_plugin_tplink_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_TPLINK_H */
diff --git a/plugins/ublox/77-mm-ublox-port-types.rules b/plugins/ublox/77-mm-ublox-port-types.rules
new file mode 100644
index 00000000..c2a1ac99
--- /dev/null
+++ b/plugins/ublox/77-mm-ublox-port-types.rules
@@ -0,0 +1,78 @@
+# do not edit this file, it will be overwritten on update
+ACTION!="add|change|move|bind", GOTO="mm_ublox_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1546", GOTO="mm_ublox_port_types"
+GOTO="mm_ublox_port_types_end"
+
+LABEL="mm_ublox_port_types"
+
+SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
+
+# Fully ignore u-blox GPS devices
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a5", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a6", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a7", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a8", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="01a9", ENV{ID_MM_DEVICE_IGNORE}="1"
+
+# Toby-L4 port types
+# ttyACM0 (if #2): secondary (ignore)
+# ttyACM1 (if #4): debug port (ignore)
+# ttyACM2 (if #6): primary
+# Wait up to 20s for the +READY URC
+# ttyACM3 (if #8): AT port for FOTA (ignore)
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+
+# TOBY-L200
+# Wait up to 20s before probing AT ports
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1141", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1143", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1146", ENV{ID_MM_UBLOX_PORT_READY_DELAY}="20"
+
+# TOBY-R2 port types
+# ttyACM0 (if #0): primary
+# ttyACM1 (if #2): secondary
+# ttyACM2 (if #4): tertiary
+# ttyACM3 (if #6): GNSS Tunneling (ignore)
+# ttyACM4 (if #8): SIM Access Profile (ignore)
+# ttyACM5 (if #10): Primary Log for diagnostics (ignore)
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1107", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1107", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1107", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LARA-R2 port types
+# ttyACM0 (if #0): primary
+# ttyACM1 (if #2): secondary
+# ttyACM2 (if #4): tertiary
+# ttyACM3 (if #6): GNSS Tunneling (ignore)
+# ttyACM4 (if #8): SIM Access Profile (ignore)
+# ttyACM5 (if #10): Primary Log for diagnostics (ignore)
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="110a", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LISA-U2 / SARA-U2 port types
+# ttyACM0 (if #0): primary
+# ttyACM1 (if #2): secondary
+# ttyACM2 (if #4): tertiary
+# ttyACM3 (if #6): GNSS Tunneling (ignore)
+# ttyACM4 (if #8): Primary Log for diagnostics (ignore)
+# ttyACM5 (if #10): Secondary Log for diagnostics (ignore)
+# ttyACM6 (if #12): SAP (SIM Access Profile) (ignore)
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1102", ENV{.MM_USBIFNUM}=="0c", ENV{ID_MM_PORT_IGNORE}="1"
+
+# LISA-U2 / SARA-U2 (alternative configuration) port types
+# ttyACM0 (if #0): primary
+# ttyACM1 (if #2): GNSS Tunneling (ignore)
+# ttyACM2 (if #4): Primary Log for diagnostics (ignore)
+# ttyACM3 (if #6): SAP (SIM Access Profile) (ignore)
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1104", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1104", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1546", ATTRS{idProduct}=="1104", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_PORT_IGNORE}="1"
+
+LABEL="mm_ublox_port_types_end"
diff --git a/plugins/ublox/README b/plugins/ublox/README
new file mode 100644
index 00000000..573be2ce
--- /dev/null
+++ b/plugins/ublox/README
@@ -0,0 +1,162 @@
+
+The 'ublox' plugin is originally targeted for the u-blox TOBY-L2 series,
+although it may be used with other kind of devices likely without many issues.
+
+=====================================
+ USB profiles and networking modes
+=====================================
+
+The TOBY-L2 devices may work in multiple different USB profiles:
+
+ * AT+UUSBCONF=0: fairly back-compatible profile, where only cdc-acm TTYs are
+ exposed. ModemManager will default to PPP for the connection setup when in
+ this profile.
+
+ * AT+UUSBCONF=2: ECM profile, where multiple cdc-acm TTYs are exposed along
+ with a ECM network interface.
+
+ * AT+UUSBCONF=3: RNDIS profile, where one cdc-acm TTY and a RNDIS network
+ interface are exposed. This is the default factory-programmed value.
+
+When a profile with a network interface (ECM or RNDIS) is in use, the device may
+work in multiple networking modes:
+
+ * AT+UBMCONF=1: Router mode, with a built-in DHCP server running behind the
+ network interface. The network interface will be assigned an IP address from
+ a subnet managed by the device itself. This is the default factory-programmed
+ value. E.g.:
+
+ $ ip addr
+ 9: usb0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
+ link/ether 02:07:01:15:00:0b brd ff:ff:ff:ff:ff:ff
+ inet 192.168.1.100/24 brd 192.168.1.255 scope global dynamic usb0
+ valid_lft 43009sec preferred_lft 43009sec
+
+ $ ip route
+ default via 192.168.1.1 dev usb0 proto static metric 700
+ 192.168.1.0/24 dev usb0 proto kernel scope link src 192.168.1.100 metric 700
+
+ * AT+UBMCONF=2: Bridge mode, where static IP addressing and routing must be
+ performed once connected. The network interface will be assigned the same IP
+ address provided by the network operator. The plugin uses 'AT+UIPADDR=N' the
+ default gateway IP settings and 'AT++CGCONTRDP=N' to retrieve the interface
+ IP settings and DNS setup.
+
+ $ ip addr
+ 11: usb0: <BROADCAST,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 1000
+ link/ether 02:07:01:15:00:0b brd ff:ff:ff:ff:ff:ff
+ inet 47.59.109.26/32 brd 47.59.109.26 scope global usb0
+ valid_lft forever preferred_lft forever
+
+ $ ip route
+ default via 47.59.109.229 dev usb0 proto static metric 700
+ 47.59.109.26 dev usb0 proto kernel scope link src 47.59.109.26 metric 700
+ 47.59.109.229 dev usb0 proto static scope link metric 700
+
+The 'ublox' plugin in ModemManager works with any of the previous combinations
+seamlessly. It is assumed that the device doesn't change either USB profile or
+networking mode once it has been detected and processed by ModemManager.
+
+NOTE: If manually selecting different USB profiles or networking modes, remember
+to reset the module before assuming the new settings have been applied. E.g.,
+using plain mmcli commands:
+ $ sudo mmcli -m 0 --command="AT+UUSBCONF=3"
+ $ sudo mmcli -m 0 --reset
+
+=================================
+ Connection setup
+=================================
+
+The plugin allows to connect to specific APNs in the usual way (i.e. by creating
+a PDP context for the specific APN), and then activating the PDP context with
+'AT+CGACT=[CID]'.
+
+Authentication settings of the APN (user, password, authentication type) are
+also supported via the 'AT+UAUTHREQ' command.
+
+The plugin doesn't currently support reporting as auto-connected the default LTE
+bearer.
+
+========================================
+ Connection monitoring and statistics
+========================================
+
+The status of the connection of the specific PDP context is monitored
+periodically using 'AT+CGACT?', in order to detect network-originated
+disconnections. This implementation is given in the Generic broadband bearer
+implementation, and is not ublox-specific.
+
+If the device supports it, connection TX/RX statistics will also be periodically
+loaded using the AT+UGCNTRD command. Note, though, that the TOBY-L2 doesn't seem
+to support this information via control commands.
+
+===========================================
+ Supported and current mode combinations
+===========================================
+
+The full list of supported mode combinations is loaded using 'AT+URAT=?', and
+then filtered by device product name to remove technologies not supported in
+several devices. E.g. the standard TOBY-L2 list of supported mode combinations
+will include all 2G, 3G and 4G, but if the device is a L201, 2G support will be
+removed from the list.
+
+The current mode combination in use is loaded using 'AT+URAT?', and the setting
+may be changed using the 'AT+URAT=X' request.
+
+In order to be able to update this setting, the device will be put in low-power
+mode ('AT+CFUN=4'), then the setting update will be run, and finally the device
+will recover the previous functionality mode it was in (e.g. 'AT+CFUN=1' if it
+was in full functionality mode).
+
+===============================
+ Supported and current bands
+===============================
+
+The full list of supported bands is hardcoded based on the supported modes of the
+device. There is no runtime loading of which are the supported bands because the
+'AT+UBANDSEL=?' command gives different results depending on the current access
+technology (i.e. there is no single full list of supported bands reported).
+
+The current list of bands is loaded via the 'AT+UBANDSEL?' command, and the
+setting may be changed using the 'AT+UBANDSEL=X' request.
+
+In order to be able to update this setting, the device will be put in low-power
+mode ('AT+CFUN=4'), then the setting update will be run, and finally the device
+will recover the previous functionality mode it was in (e.g. 'AT+CFUN=1' if it
+was in full functionality mode).
+
+======================
+ Functionality mode
+======================
+
+The plugin implements a custom 'AT+CFUN?' response parser because it provides
+multiple modes that may be treated as 'low-power' by ModemManager (e.g. mode
+'0' is minimum functionality, mode '4' is airplane mode and mode '19' is
+minimum functionality with SIM deactivated).
+
+The plugin implements power-on ('AT+CFUN=1'), power-down ('AT+CFUN=4'), reset
+('AT+CFUN=16') and power-off ('AT+CPWROFF'). As usual, a reset will trigger a
+power cycle of the device, and the power-off will render the modem unusable
+until it's power cycled externally.
+
+====================================
+ Network registration and quality
+====================================
+
+The LTE specific 'AT+CEREG' registration checks will be enabled by default if
+the module supports LTE. Additionally, a custom 'CREG/CGREG/CEREG' state number
+parser is provided to support u-blox specific reported states (e.g. state '6'
+to indicate 'sms only registration' on the '+CREG' indications for the CS
+network when on LTE).
+
+The plugin implements the extended Signal interface, providing RSSI, RSCP, Ec/Io
+and RSRQ measurements obtained with the 'AT+CESQ' command.
+
+==================
+ PIN management
+==================
+
+A custom method to load the PIN/PUK remaining attempts is implemented based on
+the 'AT+UPINCNT' command.
+
+Have fun!
diff --git a/plugins/ublox/mm-broadband-bearer-ublox.c b/plugins/ublox/mm-broadband-bearer-ublox.c
new file mode 100644
index 00000000..daabdb2e
--- /dev/null
+++ b/plugins/ublox/mm-broadband-bearer-ublox.c
@@ -0,0 +1,1034 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-bearer-ublox.h"
+#include "mm-base-modem-at.h"
+#include "mm-log-object.h"
+#include "mm-ublox-enums-types.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-ublox.h"
+
+G_DEFINE_TYPE (MMBroadbandBearerUblox, mm_broadband_bearer_ublox, MM_TYPE_BROADBAND_BEARER)
+
+enum {
+ PROP_0,
+ PROP_USB_PROFILE,
+ PROP_NETWORKING_MODE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMBroadbandBearerUbloxPrivate {
+ MMUbloxUsbProfile profile;
+ MMUbloxNetworkingMode mode;
+ MMUbloxBearerAllowedAuth allowed_auths;
+ FeatureSupport statistics;
+ FeatureSupport cedata;
+};
+
+/*****************************************************************************/
+/* Common connection context and task */
+
+typedef struct {
+ MMBroadbandModem *modem;
+ MMPortSerialAt *primary;
+ MMPort *data;
+ guint cid;
+ gboolean auth_required;
+ MMBearerIpConfig *ip_config; /* For IPv4 settings */
+} CommonConnectContext;
+
+static void
+common_connect_context_free (CommonConnectContext *ctx)
+{
+ if (ctx->ip_config)
+ g_object_unref (ctx->ip_config);
+ if (ctx->data)
+ g_object_unref (ctx->data);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->primary);
+ g_slice_free (CommonConnectContext, ctx);
+}
+
+static GTask *
+common_connect_task_new (MMBroadbandBearerUblox *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ MMPort *data,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CommonConnectContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (CommonConnectContext);
+ ctx->modem = g_object_ref (modem);
+ ctx->primary = g_object_ref (primary);
+ ctx->cid = cid;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) common_connect_context_free);
+
+ /* We need a net data port */
+ if (data)
+ ctx->data = g_object_ref (data);
+ else {
+ ctx->data = mm_base_modem_get_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET);
+ if (!ctx->data) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
+ return NULL;
+ }
+ }
+
+ return task;
+}
+
+/*****************************************************************************/
+/* 3GPP IP config (sub-step of the 3GPP Connection sequence) */
+
+static gboolean
+get_ip_config_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ MMBearerIpConfig **ipv4_config,
+ MMBearerIpConfig **ipv6_config,
+ GError **error)
+{
+ MMBearerConnectResult *configs;
+ MMBearerIpConfig *ipv4;
+
+ configs = g_task_propagate_pointer (G_TASK (res), error);
+ if (!configs)
+ return FALSE;
+
+ /* Just IPv4 for now */
+ ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs);
+ g_assert (ipv4);
+ if (ipv4_config)
+ *ipv4_config = g_object_ref (ipv4);
+ if (ipv6_config)
+ *ipv6_config = NULL;
+ mm_bearer_connect_result_unref (configs);
+ return TRUE;
+}
+
+static void
+complete_get_ip_config_3gpp (GTask *task)
+{
+ CommonConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (mm_bearer_ip_config_get_method (ctx->ip_config) != MM_BEARER_IP_METHOD_UNKNOWN);
+ g_task_return_pointer (task,
+ mm_bearer_connect_result_new (ctx->data, ctx->ip_config, NULL),
+ (GDestroyNotify) mm_bearer_connect_result_unref);
+ g_object_unref (task);
+}
+
+static void
+cgcontrdp_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ const gchar *response;
+ GError *error = NULL;
+ CommonConnectContext *ctx;
+ gchar *local_address = NULL;
+ gchar *subnet = NULL;
+ gchar *dns_addresses[3] = { NULL, NULL, NULL };
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response || !mm_3gpp_parse_cgcontrdp_response (response,
+ NULL, /* cid */
+ NULL, /* bearer id */
+ NULL, /* apn */
+ &local_address,
+ &subnet,
+ NULL, /* gateway_address */
+ &dns_addresses[0],
+ &dns_addresses[1],
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "IPv4 address retrieved: %s", local_address);
+ mm_bearer_ip_config_set_address (ctx->ip_config, local_address);
+ mm_obj_dbg (self, "IPv4 subnet retrieved: %s", subnet);
+ mm_bearer_ip_config_set_prefix (ctx->ip_config, mm_netmask_to_cidr (subnet));
+ if (dns_addresses[0])
+ mm_obj_dbg (self, "primary DNS retrieved: %s", dns_addresses[0]);
+ if (dns_addresses[1])
+ mm_obj_dbg (self, "secondary DNS retrieved: %s", dns_addresses[1]);
+ mm_bearer_ip_config_set_dns (ctx->ip_config, (const gchar **) dns_addresses);
+
+ g_free (local_address);
+ g_free (subnet);
+ g_free (dns_addresses[0]);
+ g_free (dns_addresses[1]);
+
+ mm_obj_dbg (self, "finished IP settings retrieval for PDP context #%u...", ctx->cid);
+
+ complete_get_ip_config_3gpp (task);
+}
+
+static void
+uipaddr_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ const gchar *response;
+ gchar *cmd;
+ GError *error = NULL;
+ CommonConnectContext *ctx;
+ gchar *gw_ipv4_address = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response || !mm_ublox_parse_uipaddr_response (response,
+ NULL, /* cid */
+ NULL, /* if_name */
+ &gw_ipv4_address,
+ NULL, /* ipv4_subnet */
+ NULL, /* ipv6_global_address */
+ NULL, /* ipv6_link_local_address */
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "IPv4 gateway address retrieved: %s", gw_ipv4_address);
+ mm_bearer_ip_config_set_gateway (ctx->ip_config, gw_ipv4_address);
+ g_free (gw_ipv4_address);
+
+ cmd = g_strdup_printf ("+CGCONTRDP=%u", ctx->cid);
+ mm_obj_dbg (self, "gathering IP and DNS information for PDP context #%u...", ctx->cid);
+ mm_base_modem_at_command (MM_BASE_MODEM (modem),
+ cmd,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) cgcontrdp_ready,
+ task);
+ g_free (cmd);
+}
+
+static void
+get_ip_config_3gpp (MMBroadbandBearer *_self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ MMBearerIpFamily ip_family,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (_self);
+ GTask *task;
+ CommonConnectContext *ctx;
+
+ if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self),
+ MM_BROADBAND_MODEM (modem),
+ primary,
+ cid,
+ data,
+ NULL,
+ callback,
+ user_data)))
+ return;
+
+ ctx = g_task_get_task_data (task);
+ ctx->ip_config = mm_bearer_ip_config_new ();
+
+ /* If we're in BRIDGE mode, we need to ask for static IP addressing details:
+ * - AT+UIPADDR=[CID] will give us the default gateway address.
+ * - +CGCONTRDP?[CID] will give us the IP address, subnet and DNS addresses.
+ */
+ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_BRIDGE) {
+ gchar *cmd;
+
+ mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_STATIC);
+
+ cmd = g_strdup_printf ("+UIPADDR=%u", cid);
+ mm_obj_dbg (self, "gathering gateway information for PDP context #%u...", cid);
+ mm_base_modem_at_command (MM_BASE_MODEM (modem),
+ cmd,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) uipaddr_ready,
+ task);
+ g_free (cmd);
+ return;
+ }
+
+ /* If we're in ROUTER networking mode, we just need to request DHCP on the
+ * network interface. Early return with that result. */
+ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_ROUTER) {
+ mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_DHCP);
+ complete_get_ip_config_3gpp (task);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+/*****************************************************************************/
+/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
+
+static MMPort *
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_PORT (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+cedata_activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ MMBroadbandBearerUblox *self)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response) {
+ mm_obj_warn (self, "ECM data connection attempt failed: %s", error->message);
+ mm_base_bearer_report_connection_status (MM_BASE_BEARER (self),
+ MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ g_error_free (error);
+ }
+ /* we received a full bearer object reference */
+ g_object_unref (self);
+}
+
+static void
+cgact_activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ CommonConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+activate_3gpp (GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ CommonConnectContext *ctx;
+ g_autofree gchar *cmd = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* SARA-U2xx and LISA-U20x only expose one CDC-ECM interface. Hence,
+ * the fixed 0 as the interface index here. When we see modems with
+ * multiple interfaces, this needs to be revisited. */
+ if (self->priv->profile == MM_UBLOX_USB_PROFILE_ECM && self->priv->cedata == FEATURE_SUPPORTED) {
+ cmd = g_strdup_printf ("+UCEDATA=%u,0", ctx->cid);
+ mm_obj_dbg (self, "establishing ECM data connection for PDP context #%u...", ctx->cid);
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem),
+ cmd,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) cedata_activate_ready,
+ g_object_ref (self));
+
+ /* We'll mark the task done here since the modem expects the DHCP
+ discover packet while +UCEDATA runs. If the command fails, we'll
+ mark the bearer disconnected later in the callback. */
+ g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ cmd = g_strdup_printf ("+CGACT=1,%u", ctx->cid);
+ mm_obj_dbg (self, "activating PDP context #%u...", ctx->cid);
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem),
+ cmd,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) cgact_activate_ready,
+ task);
+}
+
+static void
+test_cedata_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ const gchar *response;
+
+ self = g_task_get_source_object (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, NULL);
+ if (response)
+ self->priv->cedata = FEATURE_SUPPORTED;
+ else
+ self->priv->cedata = FEATURE_UNSUPPORTED;
+ mm_obj_dbg (self, "+UCEDATA command%s available",
+ (self->priv->cedata == FEATURE_SUPPORTED) ? "" : " not");
+
+ activate_3gpp (task);
+}
+
+static void
+test_cedata (GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ CommonConnectContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* We don't need to test for +UCEDATA if we're not using CDC-ECM or if we
+ have tested before. Instead, we jump right to the activation. */
+ if (self->priv->profile != MM_UBLOX_USB_PROFILE_ECM || self->priv->cedata != FEATURE_SUPPORT_UNKNOWN) {
+ activate_3gpp (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "checking availability of +UCEDATA command...");
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem),
+ "+UCEDATA=?",
+ 3,
+ TRUE, /* allow_cached */
+ (GAsyncReadyCallback) test_cedata_ready,
+ task);
+}
+
+static void
+uauthreq_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response) {
+ CommonConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ /* If authentication required and the +UAUTHREQ failed, abort */
+ if (ctx->auth_required) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* Otherwise, ignore */
+ g_error_free (error);
+ }
+
+ test_cedata (task);
+}
+
+static void
+authenticate_3gpp (GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ CommonConnectContext *ctx;
+ g_autofree gchar *cmd = NULL;
+ MMBearerAllowedAuth allowed_auth;
+ gint ublox_auth = -1;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+
+ if (!ctx->auth_required) {
+ mm_obj_dbg (self, "not using authentication");
+ ublox_auth = 0;
+ goto out;
+ }
+
+ if (allowed_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN || allowed_auth == (MM_BEARER_ALLOWED_AUTH_PAP | MM_BEARER_ALLOWED_AUTH_CHAP)) {
+ mm_obj_dbg (self, "using automatic authentication method");
+ if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO)
+ ublox_auth = 3;
+ else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP)
+ ublox_auth = 2;
+ else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_PAP)
+ ublox_auth = 1;
+ else if (self->priv->allowed_auths & MM_UBLOX_BEARER_ALLOWED_AUTH_NONE)
+ ublox_auth = 0;
+ } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
+ mm_obj_dbg (self, "using PAP authentication method");
+ ublox_auth = 1;
+ } else if (allowed_auth & MM_BEARER_ALLOWED_AUTH_CHAP) {
+ mm_obj_dbg (self, "using CHAP authentication method");
+ ublox_auth = 2;
+ }
+
+out:
+
+ if (ublox_auth < 0) {
+ g_autofree gchar *str = NULL;
+
+ str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot use any of the specified authentication methods (%s)", str);
+ g_object_unref (task);
+ return;
+ }
+
+ if (ublox_auth > 0) {
+ const gchar *user;
+ const gchar *password;
+ g_autofree gchar *quoted_user = NULL;
+ g_autofree gchar *quoted_password = NULL;
+
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+
+ quoted_user = mm_port_serial_at_quote_string (user);
+ quoted_password = mm_port_serial_at_quote_string (password);
+
+ cmd = g_strdup_printf ("+UAUTHREQ=%u,%u,%s,%s",
+ ctx->cid,
+ ublox_auth,
+ quoted_user,
+ quoted_password);
+ } else
+ cmd = g_strdup_printf ("+UAUTHREQ=%u,0,\"\",\"\"", ctx->cid);
+
+ mm_obj_dbg (self, "setting up authentication preferences in PDP context #%u...", ctx->cid);
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem),
+ cmd,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) uauthreq_ready,
+ task);
+}
+
+static void
+uauthreq_test_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ const gchar *response;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response)
+ goto out;
+
+ self->priv->allowed_auths = mm_ublox_parse_uauthreq_test (response, self, &error);
+out:
+ if (error) {
+ CommonConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ /* If authentication required and the +UAUTHREQ test failed, abort */
+ if (ctx->auth_required) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* Otherwise, ignore and jump to test_cedata directly as no auth setup
+ * is needed */
+ g_error_free (error);
+ test_cedata (task);
+ return;
+ }
+
+ authenticate_3gpp (task);
+}
+
+static void
+check_supported_authentication_methods (GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ CommonConnectContext *ctx;
+ const gchar *user;
+ const gchar *password;
+ MMBearerAllowedAuth allowed_auth;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ allowed_auth = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+
+ /* Flag whether authentication is required. If it isn't, we won't fail
+ * connection attempt if the +UAUTHREQ command fails */
+ ctx->auth_required = (user && password && allowed_auth != MM_BEARER_ALLOWED_AUTH_NONE);
+
+ /* If we already cached the support, not do it again */
+ if (self->priv->allowed_auths != MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN) {
+ authenticate_3gpp (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "checking supported authentication methods...");
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->modem),
+ "+UAUTHREQ=?",
+ 10,
+ TRUE, /* allow cached */
+ (GAsyncReadyCallback) uauthreq_test_ready,
+ task);
+}
+
+static void
+dial_3gpp (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self),
+ MM_BROADBAND_MODEM (modem),
+ primary,
+ cid,
+ NULL, /* data, unused */
+ cancellable,
+ callback,
+ user_data)))
+ return;
+
+ check_supported_authentication_methods (task);
+}
+
+/*****************************************************************************/
+/* 3GPP disconnection */
+
+static gboolean
+disconnect_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cgact_deactivate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ const gchar *response;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (!response) {
+ /* TOBY-L4 and TOBY-L2 L2 don't allow to disconnect the last LTE bearer
+ * as that would imply de-registration from the LTE network, so we just
+ * assume that it's disconnected from the user point of view.
+ *
+ * TOBY-L4 reports this as a generic unknown Packet Domain Error, which
+ * is a bit unfortunate:
+ * AT+CGACT=0,1
+ * +CME ERROR: 148
+ *
+ * TOBY-L2 reports this as "LAST PDN disconnection not allowed" but using
+ * the legacy numeric value before 3GPP Rel 11 (i.e. 151 instead of 171).
+ * AT+CGACT=0,1
+ * +CME ERROR: 151
+ */
+ if (!g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN) &&
+ !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED) &&
+ !g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "ignored error when disconnecting last LTE bearer: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+disconnect_3gpp (MMBroadbandBearer *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ MMPort *data,
+ guint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ g_autofree gchar *cmd = NULL;
+
+ if (!(task = common_connect_task_new (MM_BROADBAND_BEARER_UBLOX (self),
+ MM_BROADBAND_MODEM (modem),
+ primary,
+ cid,
+ data,
+ NULL,
+ callback,
+ user_data)))
+ return;
+
+ cmd = g_strdup_printf ("+CGACT=0,%u", cid);
+ mm_obj_dbg (self, "deactivating PDP context #%u...", cid);
+ mm_base_modem_at_command (MM_BASE_MODEM (modem),
+ cmd,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback) cgact_deactivate_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Reload statistics */
+
+typedef struct {
+ guint64 bytes_rx;
+ guint64 bytes_tx;
+} StatsResult;
+
+static gboolean
+reload_stats_finish (MMBaseBearer *self,
+ guint64 *bytes_rx,
+ guint64 *bytes_tx,
+ GAsyncResult *res,
+ GError **error)
+{
+ StatsResult *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ if (bytes_rx)
+ *bytes_rx = result->bytes_rx;
+ if (bytes_tx)
+ *bytes_tx = result->bytes_tx;
+ g_free (result);
+ return TRUE;
+}
+
+static void
+ugcntrd_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+ const gchar *response;
+ GError *error = NULL;
+ guint64 tx_bytes = 0;
+ guint64 rx_bytes = 0;
+ gint cid;
+
+ self = MM_BROADBAND_BEARER_UBLOX (g_task_get_source_object (task));
+
+ cid = mm_base_bearer_get_profile_id (MM_BASE_BEARER (self));
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (response) {
+ if (cid == MM_3GPP_PROFILE_ID_UNKNOWN)
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown profile id");
+ else
+ mm_ublox_parse_ugcntrd_response_for_cid (response,
+ cid,
+ &tx_bytes, &rx_bytes,
+ NULL, NULL,
+ &error);
+ }
+
+ if (error) {
+ g_prefix_error (&error, "Couldn't load PDP context %u statistics: ", cid);
+ g_task_return_error (task, error);
+ } else {
+ StatsResult *result;
+
+ result = g_new (StatsResult, 1);
+ result->bytes_rx = rx_bytes;
+ result->bytes_tx = tx_bytes;
+ g_task_return_pointer (task, result, g_free);
+ }
+ g_object_unref (task);
+}
+
+static void
+run_reload_stats (MMBroadbandBearerUblox *self,
+ GTask *task)
+{
+ /* Unsupported? */
+ if (self->priv->statistics == FEATURE_UNSUPPORTED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Loading statistics isn't supported by this device");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Supported */
+ if (self->priv->statistics == FEATURE_SUPPORTED) {
+ MMBaseModem *modem = NULL;
+
+ g_object_get (MM_BASE_BEARER (self),
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (modem),
+ "+UGCNTRD",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) ugcntrd_ready,
+ task);
+ g_object_unref (modem);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+ugcntrd_test_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearerUblox *self;
+
+ self = MM_BROADBAND_BEARER_UBLOX (g_task_get_source_object (task));
+
+ if (!mm_base_modem_at_command_finish (modem, res, NULL))
+ self->priv->statistics = FEATURE_UNSUPPORTED;
+ else
+ self->priv->statistics = FEATURE_SUPPORTED;
+
+ run_reload_stats (self, task);
+}
+
+static void
+reload_stats (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (MM_BROADBAND_BEARER_UBLOX (self)->priv->statistics == FEATURE_SUPPORT_UNKNOWN) {
+ MMBaseModem *modem = NULL;
+
+ g_object_get (MM_BASE_BEARER (self),
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (modem),
+ "+UGCNTRD=?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) ugcntrd_test_ready,
+ task);
+ g_object_unref (modem);
+ return;
+ }
+
+ run_reload_stats (MM_BROADBAND_BEARER_UBLOX (self), task);
+}
+
+/*****************************************************************************/
+
+MMBaseBearer *
+mm_broadband_bearer_ublox_new_finish (GAsyncResult *res,
+ GError **error)
+{
+ GObject *source;
+ GObject *bearer;
+
+ source = g_async_result_get_source_object (res);
+ bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+ g_object_unref (source);
+
+ if (!bearer)
+ return NULL;
+
+ /* Only export valid bearers */
+ mm_base_bearer_export (MM_BASE_BEARER (bearer));
+
+ return MM_BASE_BEARER (bearer);
+}
+
+void
+mm_broadband_bearer_ublox_new (MMBroadbandModem *modem,
+ MMUbloxUsbProfile profile,
+ MMUbloxNetworkingMode mode,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_assert (mode == MM_UBLOX_NETWORKING_MODE_ROUTER || mode == MM_UBLOX_NETWORKING_MODE_BRIDGE);
+
+ g_async_initable_new_async (
+ MM_TYPE_BROADBAND_BEARER_UBLOX,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_BASE_BEARER_MODEM, modem,
+ MM_BASE_BEARER_CONFIG, config,
+ MM_BROADBAND_BEARER_UBLOX_USB_PROFILE, profile,
+ MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE, mode,
+ NULL);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (object);
+
+ switch (prop_id) {
+ case PROP_USB_PROFILE:
+ self->priv->profile = g_value_get_enum (value);
+ break;
+ case PROP_NETWORKING_MODE:
+ self->priv->mode = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandBearerUblox *self = MM_BROADBAND_BEARER_UBLOX (object);
+
+ switch (prop_id) {
+ case PROP_USB_PROFILE:
+ g_value_set_enum (value, self->priv->profile);
+ break;
+ case PROP_NETWORKING_MODE:
+ g_value_set_enum (value, self->priv->mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mm_broadband_bearer_ublox_init (MMBroadbandBearerUblox *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_BEARER_UBLOX,
+ MMBroadbandBearerUbloxPrivate);
+
+ /* Defaults */
+ self->priv->profile = MM_UBLOX_USB_PROFILE_UNKNOWN;
+ self->priv->mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN;
+ self->priv->allowed_auths = MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN;
+ self->priv->statistics = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->cedata = FEATURE_SUPPORT_UNKNOWN;
+}
+
+static void
+mm_broadband_bearer_ublox_class_init (MMBroadbandBearerUbloxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBaseBearerClass *base_bearer_class = MM_BASE_BEARER_CLASS (klass);
+ MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandBearerUbloxPrivate));
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ /* Note: the ublox plugin uses the generic AT+CGACT? based check to monitor
+ * the connection status (i.e. default load_connection_status()) */
+ base_bearer_class->reload_stats = reload_stats;
+ base_bearer_class->reload_stats_finish = reload_stats_finish;
+
+ broadband_bearer_class->disconnect_3gpp = disconnect_3gpp;
+ broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish;
+ broadband_bearer_class->dial_3gpp = dial_3gpp;
+ broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
+ broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
+ broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish;
+
+ properties[PROP_USB_PROFILE] =
+ g_param_spec_enum (MM_BROADBAND_BEARER_UBLOX_USB_PROFILE,
+ "USB profile",
+ "USB profile in use",
+ MM_TYPE_UBLOX_USB_PROFILE,
+ MM_UBLOX_USB_PROFILE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_USB_PROFILE, properties[PROP_USB_PROFILE]);
+
+ properties[PROP_NETWORKING_MODE] =
+ g_param_spec_enum (MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE,
+ "Networking mode",
+ "Networking mode in use",
+ MM_TYPE_UBLOX_NETWORKING_MODE,
+ MM_UBLOX_NETWORKING_MODE_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_NETWORKING_MODE, properties[PROP_NETWORKING_MODE]);
+}
diff --git a/plugins/ublox/mm-broadband-bearer-ublox.h b/plugins/ublox/mm-broadband-bearer-ublox.h
new file mode 100644
index 00000000..36c26894
--- /dev/null
+++ b/plugins/ublox/mm-broadband-bearer-ublox.h
@@ -0,0 +1,63 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_BEARER_UBLOX_H
+#define MM_BROADBAND_BEARER_UBLOX_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-bearer.h"
+#include "mm-modem-helpers-ublox.h"
+
+#define MM_TYPE_BROADBAND_BEARER_UBLOX (mm_broadband_bearer_ublox_get_type ())
+#define MM_BROADBAND_BEARER_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUblox))
+#define MM_BROADBAND_BEARER_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUbloxClass))
+#define MM_IS_BROADBAND_BEARER_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_UBLOX))
+#define MM_IS_BROADBAND_BEARER_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_UBLOX))
+#define MM_BROADBAND_BEARER_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_UBLOX, MMBroadbandBearerUbloxClass))
+
+#define MM_BROADBAND_BEARER_UBLOX_USB_PROFILE "broadband-bearer-ublox-usb-profile"
+#define MM_BROADBAND_BEARER_UBLOX_NETWORKING_MODE "broadband-bearer-ublox-networking-mode"
+
+typedef struct _MMBroadbandBearerUblox MMBroadbandBearerUblox;
+typedef struct _MMBroadbandBearerUbloxClass MMBroadbandBearerUbloxClass;
+typedef struct _MMBroadbandBearerUbloxPrivate MMBroadbandBearerUbloxPrivate;
+
+struct _MMBroadbandBearerUblox {
+ MMBroadbandBearer parent;
+ MMBroadbandBearerUbloxPrivate *priv;
+};
+
+struct _MMBroadbandBearerUbloxClass {
+ MMBroadbandBearerClass parent;
+};
+
+GType mm_broadband_bearer_ublox_get_type (void);
+
+void mm_broadband_bearer_ublox_new (MMBroadbandModem *modem,
+ MMUbloxUsbProfile profile,
+ MMUbloxNetworkingMode mode,
+ MMBearerProperties *config,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseBearer *mm_broadband_bearer_ublox_new_finish (GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_BROADBAND_BEARER_UBLOX_H */
diff --git a/plugins/ublox/mm-broadband-modem-ublox.c b/plugins/ublox/mm-broadband-modem-ublox.c
new file mode 100644
index 00000000..2ae152fc
--- /dev/null
+++ b/plugins/ublox/mm-broadband-modem-ublox.c
@@ -0,0 +1,1917 @@
+/* -*- 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) 2016-2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-base-modem-at.h"
+#include "mm-broadband-bearer.h"
+#include "mm-broadband-modem-ublox.h"
+#include "mm-broadband-bearer-ublox.h"
+#include "mm-sim-ublox.h"
+#include "mm-modem-helpers-ublox.h"
+#include "mm-ublox-enums-types.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *iface);
+
+static MMIfaceModemVoice *iface_modem_voice_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemUblox, mm_broadband_modem_ublox, MM_TYPE_BROADBAND_MODEM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init))
+
+
+struct _MMBroadbandModemUbloxPrivate {
+ /* USB profile in use */
+ MMUbloxUsbProfile profile;
+ gboolean profile_checked;
+ /* Networking mode in use */
+ MMUbloxNetworkingMode mode;
+ gboolean mode_checked;
+
+ /* Flag to specify whether a power operation is ongoing */
+ gboolean power_operation_ongoing;
+
+ /* Mode combination to apply if "any" requested */
+ MMModemMode any_allowed;
+
+ /* AT command configuration */
+ UbloxSupportConfig support_config;
+
+ /* Voice +UCALLSTAT support */
+ GRegex *ucallstat_regex;
+
+ FeatureSupport udtmfd_support;
+ GRegex *udtmfd_regex;
+
+ /* Regex to ignore */
+ GRegex *pbready_regex;
+};
+
+/*****************************************************************************/
+/* Per-model configuration loading */
+
+static void
+preload_support_config (MMBroadbandModemUblox *self)
+{
+ const gchar *model;
+ GError *error = NULL;
+
+ /* Make sure we load only once */
+ if (self->priv->support_config.loaded)
+ return;
+
+ model = mm_iface_modem_get_model (MM_IFACE_MODEM (self));
+
+ if (!mm_ublox_get_support_config (model, &self->priv->support_config, &error)) {
+ mm_obj_warn (self, "loading support configuration failed: %s", error->message);
+ g_error_free (error);
+
+ /* default to NOT SUPPORTED if unknown model */
+ self->priv->support_config.method = SETTINGS_UPDATE_METHOD_UNKNOWN;
+ self->priv->support_config.uact = FEATURE_UNSUPPORTED;
+ self->priv->support_config.ubandsel = FEATURE_UNSUPPORTED;
+ } else
+ mm_obj_dbg (self, "support configuration found for '%s'", model);
+
+ switch (self->priv->support_config.method) {
+ case SETTINGS_UPDATE_METHOD_CFUN:
+ mm_obj_dbg (self, " band update requires low-power mode");
+ break;
+ case SETTINGS_UPDATE_METHOD_COPS:
+ mm_obj_dbg (self, " band update requires explicit unregistration");
+ break;
+ case SETTINGS_UPDATE_METHOD_UNKNOWN:
+ /* not an error, this just means we don't need anything special */
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ switch (self->priv->support_config.uact) {
+ case FEATURE_SUPPORTED:
+ mm_obj_dbg (self, " UACT based band configuration supported");
+ break;
+ case FEATURE_UNSUPPORTED:
+ mm_obj_dbg (self, " UACT based band configuration unsupported");
+ break;
+ case FEATURE_SUPPORT_UNKNOWN:
+ default:
+ g_assert_not_reached();
+ }
+
+ switch (self->priv->support_config.ubandsel) {
+ case FEATURE_SUPPORTED:
+ mm_obj_dbg (self, " UBANDSEL based band configuration supported");
+ break;
+ case FEATURE_UNSUPPORTED:
+ mm_obj_dbg (self, " UBANDSEL based band configuration unsupported");
+ break;
+ case FEATURE_SUPPORT_UNKNOWN:
+ default:
+ g_assert_not_reached();
+ }
+}
+
+/*****************************************************************************/
+
+static gboolean
+acquire_power_operation (MMBroadbandModemUblox *self,
+ GError **error)
+{
+ if (self->priv->power_operation_ongoing) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY,
+ "An operation which requires power updates is currently in progress");
+ return FALSE;
+ }
+ self->priv->power_operation_ongoing = TRUE;
+ return TRUE;
+}
+
+static void
+release_power_operation (MMBroadbandModemUblox *self)
+{
+ g_assert (self->priv->power_operation_ongoing);
+ self->priv->power_operation_ongoing = FALSE;
+}
+
+/*****************************************************************************/
+/* Load supported bands (Modem interface) */
+
+static GArray *
+load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GError *error = NULL;
+ GArray *bands = NULL;
+ const gchar *model;
+
+ model = mm_iface_modem_get_model (self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ bands = mm_ublox_get_supported_bands (model, self, &error);
+ if (!bands)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load current bands (Modem interface) */
+
+static GArray *
+load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+uact_load_current_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ GArray *out;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ out = mm_ublox_parse_uact_response (response, &error);
+ if (!out) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_pointer (task, out, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+ubandsel_load_current_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+ const gchar *model;
+ GArray *out;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ model = mm_iface_modem_get_model (MM_IFACE_MODEM (self));
+ out = mm_ublox_parse_ubandsel_response (response, model, self, &error);
+ if (!out) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_pointer (task, out, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+load_current_bands (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+ GTask *task;
+
+ preload_support_config (self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->support_config.ubandsel == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+UBANDSEL?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)ubandsel_load_current_bands_ready,
+ task);
+ return;
+ }
+
+ if (self->priv->support_config.uact == FEATURE_SUPPORTED) {
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+UACT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)uact_load_current_bands_ready,
+ task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "loading current bands is unsupported");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Set allowed modes/bands (Modem interface) */
+
+typedef enum {
+ SET_CURRENT_MODES_BANDS_STEP_FIRST,
+ SET_CURRENT_MODES_BANDS_STEP_ACQUIRE,
+ SET_CURRENT_MODES_BANDS_STEP_CURRENT_POWER,
+ SET_CURRENT_MODES_BANDS_STEP_BEFORE_COMMAND,
+ SET_CURRENT_MODES_BANDS_STEP_COMMAND,
+ SET_CURRENT_MODES_BANDS_STEP_AFTER_COMMAND,
+ SET_CURRENT_MODES_BANDS_STEP_RELEASE,
+ SET_CURRENT_MODES_BANDS_STEP_LAST,
+} SetCurrentModesBandsStep;
+
+typedef struct {
+ SetCurrentModesBandsStep step;
+ gchar *command;
+ MMModemPowerState initial_state;
+ GError *saved_error;
+} SetCurrentModesBandsContext;
+
+static void
+set_current_modes_bands_context_free (SetCurrentModesBandsContext *ctx)
+{
+ g_assert (!ctx->saved_error);
+ g_free (ctx->command);
+ g_slice_free (SetCurrentModesBandsContext, ctx);
+}
+
+static void
+set_current_modes_bands_context_new (GTask *task,
+ gchar *command)
+{
+ SetCurrentModesBandsContext *ctx;
+
+ ctx = g_slice_new0 (SetCurrentModesBandsContext);
+ ctx->command = command;
+ ctx->initial_state = MM_MODEM_POWER_STATE_UNKNOWN;
+ ctx->step = SET_CURRENT_MODES_BANDS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) set_current_modes_bands_context_free);
+}
+
+static gboolean
+common_set_current_modes_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void set_current_modes_bands_step (GTask *task);
+
+static void
+set_current_modes_bands_reregister_in_network_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentModesBandsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* propagate the error if none already set */
+ mm_iface_modem_3gpp_reregister_in_network_finish (self, res, ctx->saved_error ? NULL : &ctx->saved_error);
+
+ /* Go to next step (release power operation) regardless of the result */
+ ctx->step++;
+ set_current_modes_bands_step (task);
+}
+
+static void
+set_current_modes_bands_after_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentModesBandsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* propagate the error if none already set */
+ mm_base_modem_at_command_finish (self, res, ctx->saved_error ? NULL : &ctx->saved_error);
+
+ /* Go to next step (release power operation) regardless of the result */
+ ctx->step++;
+ set_current_modes_bands_step (task);
+}
+
+static void
+set_current_modes_bands_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentModesBandsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &ctx->saved_error))
+ ctx->step = SET_CURRENT_MODES_BANDS_STEP_RELEASE;
+ else
+ ctx->step++;
+
+ set_current_modes_bands_step (task);
+}
+
+static void
+set_current_modes_bands_before_command_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentModesBandsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (self, res, &ctx->saved_error))
+ ctx->step = SET_CURRENT_MODES_BANDS_STEP_RELEASE;
+ else
+ ctx->step++;
+
+ set_current_modes_bands_step (task);
+}
+
+static void
+set_current_modes_bands_current_power_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+ SetCurrentModesBandsContext *ctx;
+ const gchar *response;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN);
+
+ response = mm_base_modem_at_command_finish (_self, res, &ctx->saved_error);
+ if (!response || !mm_ublox_parse_cfun_response (response, &ctx->initial_state, &ctx->saved_error))
+ ctx->step = SET_CURRENT_MODES_BANDS_STEP_RELEASE;
+ else
+ ctx->step++;
+
+ set_current_modes_bands_step (task);
+}
+
+static void
+set_current_modes_bands_step (GTask *task)
+{
+ MMBroadbandModemUblox *self;
+ SetCurrentModesBandsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_CURRENT_MODES_BANDS_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SET_CURRENT_MODES_BANDS_STEP_ACQUIRE:
+ mm_obj_dbg (self, "acquiring power operation...");
+ if (!acquire_power_operation (self, &ctx->saved_error)) {
+ ctx->step = SET_CURRENT_MODES_BANDS_STEP_LAST;
+ set_current_modes_bands_step (task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_CURRENT_MODES_BANDS_STEP_CURRENT_POWER:
+ /* If using CFUN, we check whether we're already in low-power mode.
+ * And if we are, we just skip triggering low-power mode ourselves.
+ */
+ if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN) {
+ mm_obj_dbg (self, "checking current power operation...");
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) set_current_modes_bands_current_power_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_CURRENT_MODES_BANDS_STEP_BEFORE_COMMAND:
+ /* If COPS required around the set command, run it unconditionally */
+ if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_COPS) {
+ mm_obj_dbg (self, "deregistering from the network for configuration change...");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+COPS=2",
+ 10,
+ FALSE,
+ (GAsyncReadyCallback) set_current_modes_bands_before_command_ready,
+ task);
+ return;
+ }
+ /* If CFUN required, check initial state before triggering low-power mode ourselves */
+ else if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN) {
+ /* Do nothing if already in low-power mode */
+ if (ctx->initial_state != MM_MODEM_POWER_STATE_LOW) {
+ mm_obj_dbg (self, "powering down for configuration change...");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CFUN=4",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) set_current_modes_bands_before_command_ready,
+ task);
+ return;
+ }
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case SET_CURRENT_MODES_BANDS_STEP_COMMAND:
+ mm_obj_dbg (self, "updating configuration...");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ ctx->command,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) set_current_modes_bands_command_ready,
+ task);
+ return;
+
+ case SET_CURRENT_MODES_BANDS_STEP_AFTER_COMMAND:
+ /* If COPS required around the set command, run it unconditionally */
+ if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_COPS) {
+ mm_iface_modem_3gpp_reregister_in_network (MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback) set_current_modes_bands_reregister_in_network_ready,
+ task);
+ return;
+ }
+ /* If CFUN required, see if we need to recover power */
+ else if (self->priv->support_config.method == SETTINGS_UPDATE_METHOD_CFUN) {
+ /* If we were in low-power mode before the change, do nothing, otherwise,
+ * full power mode back */
+ if (ctx->initial_state != MM_MODEM_POWER_STATE_LOW) {
+ mm_obj_dbg (self, "recovering power state after configuration change...");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CFUN=1",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) set_current_modes_bands_after_command_ready,
+ task);
+ return;
+ }
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_CURRENT_MODES_BANDS_STEP_RELEASE:
+ mm_obj_dbg (self, "releasing power operation...");
+ release_power_operation (self);
+ ctx->step++;
+ /* fall through */
+
+ case SET_CURRENT_MODES_BANDS_STEP_LAST:
+ if (ctx->saved_error) {
+ g_task_return_error (task, ctx->saved_error);
+ ctx->saved_error = NULL;
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *command;
+ GError *error = NULL;
+
+ preload_support_config (MM_BROADBAND_MODEM_UBLOX (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Handle ANY */
+ if (allowed == MM_MODEM_MODE_ANY)
+ allowed = MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed;
+
+ /* Build command */
+ command = mm_ublox_build_urat_set_command (allowed, preferred, &error);
+ if (!command) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ set_current_modes_bands_context_new (task, command);
+ set_current_modes_bands_step (task);
+}
+
+static void
+set_current_bands (MMIfaceModem *_self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+ GTask *task;
+ GError *error = NULL;
+ gchar *command = NULL;
+ const gchar *model;
+
+ preload_support_config (self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ model = mm_iface_modem_get_model (_self);
+
+ /* Build command */
+ if (self->priv->support_config.uact == FEATURE_SUPPORTED)
+ command = mm_ublox_build_uact_set_command (bands_array, &error);
+ else if (self->priv->support_config.ubandsel == FEATURE_SUPPORTED)
+ command = mm_ublox_build_ubandsel_set_command (bands_array, model, &error);
+
+ if (!command) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ set_current_modes_bands_context_new (task, command);
+ set_current_modes_bands_step (task);
+}
+
+/*****************************************************************************/
+/* Load current modes (Modem interface) */
+
+static gboolean
+load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return FALSE;
+
+ return mm_ublox_parse_urat_read_response (response, self, allowed, preferred, error);
+}
+
+static void
+load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+URAT?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load supported modes (Modem interface) */
+
+static GArray *
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ GArray *combinations;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return FALSE;
+
+ if (!(combinations = mm_ublox_parse_urat_test_response (response, self, error)))
+ return FALSE;
+
+ if (!(combinations = mm_ublox_filter_supported_modes (mm_iface_modem_get_model (self), combinations, self, error)))
+ return FALSE;
+
+ /* Decide and store which combination to apply when ANY requested */
+ MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed = mm_ublox_get_modem_mode_any (combinations);
+
+ /* If 4G supported, explicitly use +CEREG */
+ if (MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed & MM_MODEM_MODE_4G)
+ g_object_set (self, MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE, NULL);
+
+ return combinations;
+}
+
+static void
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+URAT=?",
+ 3,
+ TRUE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Power state loading (Modem interface) */
+
+static MMModemPowerState
+load_power_state_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (response)
+ mm_ublox_parse_cfun_response (response, &state, error);
+ return state;
+}
+
+static void
+load_power_state (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Modem power up/down/off (Modem interface) */
+
+static gboolean
+common_modem_power_operation_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+power_operation_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ release_power_operation (MM_BROADBAND_MODEM_UBLOX (self));
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_modem_power_operation (MMBroadbandModemUblox *self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Fail if there is already an ongoing power management operation */
+ if (!acquire_power_operation (self, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Use AT+CFUN=4 for power down, puts device in airplane mode */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 30,
+ FALSE,
+ (GAsyncReadyCallback) power_operation_ready,
+ task);
+}
+
+static void
+modem_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=16", callback, user_data);
+}
+
+static void
+modem_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CPWROFF", callback, user_data);
+}
+
+static void
+modem_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=4", callback, user_data);
+}
+
+static void
+modem_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=1", callback, user_data);
+}
+
+/*****************************************************************************/
+/* Load unlock retries (Modem interface) */
+
+static MMUnlockRetries *
+load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ MMUnlockRetries *retries;
+ guint pin_attempts = 0;
+ guint pin2_attempts = 0;
+ guint puk_attempts = 0;
+ guint puk2_attempts = 0;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response || !mm_ublox_parse_upincnt_response (response,
+ &pin_attempts, &pin2_attempts,
+ &puk_attempts, &puk2_attempts,
+ error))
+ return NULL;
+
+ retries = mm_unlock_retries_new ();
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin_attempts);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk_attempts);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2_attempts);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2_attempts);
+
+ return retries;
+}
+
+static void
+load_unlock_retries (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+UPINCNT",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Common enable/disable voice unsolicited events */
+
+typedef enum {
+ VOICE_UNSOLICITED_EVENTS_STEP_FIRST,
+ VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_PRIMARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_SECONDARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_PRIMARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_SECONDARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_LAST,
+} VoiceUnsolicitedEventsStep;
+
+typedef struct {
+ gboolean enable;
+ VoiceUnsolicitedEventsStep step;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ gchar *ucallstat_command;
+ gchar *udtmfd_command;
+} VoiceUnsolicitedEventsContext;
+
+static void
+voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx)
+{
+ g_clear_object (&ctx->secondary);
+ g_clear_object (&ctx->primary);
+ g_free (ctx->ucallstat_command);
+ g_free (ctx->udtmfd_command);
+ g_slice_free (VoiceUnsolicitedEventsContext, ctx);
+}
+
+static gboolean
+common_voice_enable_disable_unsolicited_events_finish (MMBroadbandModemUblox *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void voice_unsolicited_events_context_step (GTask *task);
+
+static void
+udtmfd_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't %s +UUDTMFD reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
+ }
+
+ ctx->step++;
+ voice_unsolicited_events_context_step (task);
+}
+
+static void
+ucallstat_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't %s +UCALLSTAT reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
+ }
+
+ ctx->step++;
+ voice_unsolicited_events_context_step (task);
+}
+
+static void
+voice_unsolicited_events_context_step (GTask *task)
+{
+ MMBroadbandModemUblox *self;
+ VoiceUnsolicitedEventsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case VOICE_UNSOLICITED_EVENTS_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_PRIMARY:
+ if (ctx->primary) {
+ mm_obj_dbg (self, "%s extended call status reporting in primary port...",
+ ctx->enable ? "enabling" : "disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->primary,
+ ctx->ucallstat_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)ucallstat_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_SECONDARY:
+ if (ctx->secondary) {
+ mm_obj_dbg (self, "%s extended call status reporting in secondary port...",
+ ctx->enable ? "enabling" : "disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->secondary,
+ ctx->ucallstat_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)ucallstat_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_PRIMARY:
+ if ((self->priv->udtmfd_support == FEATURE_SUPPORTED) && (ctx->primary)) {
+ mm_obj_dbg (self, "%s DTMF detection and reporting in primary port...",
+ ctx->enable ? "enabling" : "disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->primary,
+ ctx->udtmfd_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)udtmfd_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_SECONDARY:
+ if ((self->priv->udtmfd_support == FEATURE_SUPPORTED) && (ctx->secondary)) {
+ mm_obj_dbg (self, "%s DTMF detection and reporting in secondary port...",
+ ctx->enable ? "enabling" : "disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->secondary,
+ ctx->udtmfd_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)udtmfd_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+common_voice_enable_disable_unsolicited_events (MMBroadbandModemUblox *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (VoiceUnsolicitedEventsContext);
+ ctx->step = VOICE_UNSOLICITED_EVENTS_STEP_FIRST;
+ ctx->enable = enable;
+ if (enable) {
+ ctx->ucallstat_command = g_strdup ("+UCALLSTAT=1");
+ ctx->udtmfd_command = g_strdup ("+UDTMFD=1,2");
+ } else {
+ ctx->ucallstat_command = g_strdup ("+UCALLSTAT=0");
+ ctx->udtmfd_command = g_strdup ("+UDTMFD=0");
+ }
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free);
+
+ voice_unsolicited_events_context_step (task);
+}
+
+/*****************************************************************************/
+/* Enabling unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_enable_unsolicited_events_ready (MMBroadbandModemUblox *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "Couldn't enable u-blox-specific voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->enable_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self),
+ TRUE,
+ (GAsyncReadyCallback) voice_enable_unsolicited_events_ready,
+ task);
+}
+
+static void
+modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's enable */
+ iface_modem_voice_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Disabling unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->disable_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+voice_disable_unsolicited_events_ready (MMBroadbandModemUblox *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "Couldn't disable u-blox-specific voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ iface_modem_voice_parent->disable_unsolicited_events (
+ MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready,
+ task);
+}
+
+static void
+modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self),
+ FALSE,
+ (GAsyncReadyCallback) voice_disable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Common setup/cleanup voice unsolicited events */
+
+static void
+ucallstat_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemUblox *self)
+{
+ static const MMCallState ublox_call_state[] = {
+ [0] = MM_CALL_STATE_ACTIVE,
+ [1] = MM_CALL_STATE_HELD,
+ [2] = MM_CALL_STATE_DIALING, /* Dialing (MOC) */
+ [3] = MM_CALL_STATE_RINGING_OUT, /* Alerting (MOC) */
+ [4] = MM_CALL_STATE_RINGING_IN, /* Incoming (MTC) */
+ [5] = MM_CALL_STATE_WAITING, /* Waiting (MTC) */
+ [6] = MM_CALL_STATE_TERMINATED,
+ [7] = MM_CALL_STATE_ACTIVE, /* Treated same way as ACTIVE */
+ };
+
+ MMCallInfo call_info = { 0 };
+ guint aux;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &aux)) {
+ mm_obj_warn (self, "couldn't parse call index from +UCALLSTAT");
+ return;
+ }
+ call_info.index = aux;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &aux) ||
+ (aux >= G_N_ELEMENTS (ublox_call_state))) {
+ mm_obj_warn (self, "couldn't parse call state from +UCALLSTAT");
+ return;
+ }
+ call_info.state = ublox_call_state[aux];
+
+ /* guess direction for some of the states */
+ switch (call_info.state) {
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_OUT:
+ call_info.direction = MM_CALL_DIRECTION_OUTGOING;
+ break;
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_WAITING:
+ call_info.direction = MM_CALL_DIRECTION_INCOMING;
+ break;
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_ACTIVE:
+ case MM_CALL_STATE_HELD:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ call_info.direction = MM_CALL_DIRECTION_UNKNOWN;
+ break;
+ }
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+udtmfd_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemUblox *self)
+{
+ g_autofree gchar *dtmf = NULL;
+
+ dtmf = g_match_info_fetch (match_info, 1);
+ mm_obj_dbg (self, "received DTMF: %s", dtmf);
+ /* call index unknown */
+ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf);
+}
+
+static void
+common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemUblox *self,
+ gboolean enable)
+{
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ if (G_UNLIKELY (!self->priv->ucallstat_regex))
+ self->priv->ucallstat_regex = g_regex_new ("\\r\\n\\+UCALLSTAT:\\s*(\\d+),(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ if (G_UNLIKELY (!self->priv->udtmfd_regex))
+ self->priv->udtmfd_regex = g_regex_new ("\\r\\n\\+UUDTMFD:\\s*([0-9A-D\\*\\#])\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->ucallstat_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)ucallstat_received : NULL,
+ enable ? self : NULL,
+ NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->udtmfd_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)udtmfd_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "Couldn't cleanup parent voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* our own cleanup first */
+ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self), FALSE);
+
+ /* Chain up parent's cleanup */
+ iface_modem_voice_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_cleanup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_setup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "Couldn't setup parent voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* our own setup next */
+ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self), TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* chain up parent's setup first */
+ iface_modem_voice_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_voice_setup_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Create call (Voice interface) */
+
+static MMBaseCall *
+create_call (MMIfaceModemVoice *self,
+ MMCallDirection direction,
+ const gchar *number)
+{
+ return mm_base_call_new (MM_BASE_MODEM (self),
+ direction,
+ number,
+ TRUE, /* skip_incoming_timeout */
+ TRUE, /* supports_dialing_to_ringing */
+ TRUE); /* supports_ringing_to_active */
+}
+
+/*****************************************************************************/
+/* Check if Voice supported (Voice interface) */
+
+static gboolean
+modem_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+udtmfd_test_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+
+ self->priv->udtmfd_support = (!!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ?
+ FEATURE_SUPPORTED : FEATURE_UNSUPPORTED);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_check_support_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->check_support_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* voice is supported, check if +UDTMFD is available */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+UDTMFD=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) udtmfd_test_ready,
+ task);
+}
+
+static void
+modem_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* chain up parent's setup first */
+ iface_modem_voice_parent->check_support (
+ self,
+ (GAsyncReadyCallback)parent_voice_check_support_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Create Bearer (Modem interface) */
+
+typedef enum {
+ CREATE_BEARER_STEP_FIRST,
+ CREATE_BEARER_STEP_CHECK_PROFILE,
+ CREATE_BEARER_STEP_CHECK_MODE,
+ CREATE_BEARER_STEP_CREATE_BEARER,
+ CREATE_BEARER_STEP_LAST,
+} CreateBearerStep;
+
+typedef struct {
+ CreateBearerStep step;
+ MMBearerProperties *properties;
+ MMBaseBearer *bearer;
+ gboolean has_net;
+} CreateBearerContext;
+
+static void
+create_bearer_context_free (CreateBearerContext *ctx)
+{
+ g_clear_object (&ctx->bearer);
+ g_object_unref (ctx->properties);
+ g_slice_free (CreateBearerContext, ctx);
+}
+
+static MMBaseBearer *
+modem_create_bearer_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_BASE_BEARER (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void create_bearer_step (GTask *task);
+
+static void
+broadband_bearer_new_ready (GObject *source,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self;
+ CreateBearerContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (!ctx->bearer);
+ ctx->bearer = mm_broadband_bearer_new_finish (res, &error);
+ if (!ctx->bearer) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "new generic broadband bearer created at DBus path '%s'", mm_base_bearer_get_path (ctx->bearer));
+ ctx->step++;
+ create_bearer_step (task);
+}
+
+static void
+broadband_bearer_ublox_new_ready (GObject *source,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self;
+ CreateBearerContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (!ctx->bearer);
+ ctx->bearer = mm_broadband_bearer_ublox_new_finish (res, &error);
+ if (!ctx->bearer) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "new u-blox broadband bearer created at DBus path '%s'", mm_base_bearer_get_path (ctx->bearer));
+ ctx->step++;
+ create_bearer_step (task);
+}
+
+static void
+mode_check_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+ const gchar *response;
+ GError *error = NULL;
+ CreateBearerContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response) {
+ mm_obj_dbg (self, "couldn't load current networking mode: %s", error->message);
+ g_error_free (error);
+ } else if (!mm_ublox_parse_ubmconf_response (response, &self->priv->mode, &error)) {
+ mm_obj_dbg (self, "couldn't parse current networking mode response '%s': %s", response, error->message);
+ g_error_free (error);
+ } else {
+ g_assert (self->priv->mode != MM_UBLOX_NETWORKING_MODE_UNKNOWN);
+ mm_obj_dbg (self, "networking mode loaded: %s", mm_ublox_networking_mode_get_string (self->priv->mode));
+ }
+
+ /* If checking networking mode isn't supported, we'll fallback to
+ * assume the device is in router mode, which is the mode asking for
+ * less connection setup rules from our side (just request DHCP).
+ */
+ if (self->priv->mode == MM_UBLOX_NETWORKING_MODE_UNKNOWN && ctx->has_net) {
+ mm_obj_dbg (self, "fallback to default networking mode: router");
+ self->priv->mode = MM_UBLOX_NETWORKING_MODE_ROUTER;
+ }
+
+ self->priv->mode_checked = TRUE;
+
+ ctx->step++;
+ create_bearer_step (task);
+}
+
+static void
+profile_check_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+ const gchar *response;
+ GError *error = NULL;
+ CreateBearerContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response) {
+ mm_obj_dbg (self, "couldn't load current usb profile: %s", error->message);
+ g_error_free (error);
+ } else if (!mm_ublox_parse_uusbconf_response (response, &self->priv->profile, &error)) {
+ mm_obj_dbg (self, "couldn't parse current usb profile response '%s': %s", response, error->message);
+ g_error_free (error);
+ } else {
+ g_assert (self->priv->profile != MM_UBLOX_USB_PROFILE_UNKNOWN);
+ mm_obj_dbg (self, "usb profile loaded: %s", mm_ublox_usb_profile_get_string (self->priv->profile));
+ }
+
+ /* Assume the operation has been performed, even if it may have failed */
+ self->priv->profile_checked = TRUE;
+
+ ctx->step++;
+ create_bearer_step (task);
+}
+
+static void
+create_bearer_step (GTask *task)
+{
+ MMBroadbandModemUblox *self;
+ CreateBearerContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case CREATE_BEARER_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case CREATE_BEARER_STEP_CHECK_PROFILE:
+ if (!self->priv->profile_checked) {
+ mm_obj_dbg (self, "checking current USB profile...");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+UUSBCONF?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) profile_check_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CREATE_BEARER_STEP_CHECK_MODE:
+ if (!self->priv->mode_checked) {
+ mm_obj_dbg (self, "checking current networking mode...");
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+UBMCONF?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) mode_check_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CREATE_BEARER_STEP_CREATE_BEARER:
+ /* If we have a net interface, we'll create a u-blox bearer, unless for
+ * any reason we have the back-compatible profile selected. */
+ if ((self->priv->profile != MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE) && ctx->has_net) {
+ /* whenever there is a net port, we should have loaded a valid networking mode */
+ g_assert (self->priv->mode != MM_UBLOX_NETWORKING_MODE_UNKNOWN);
+ mm_obj_dbg (self, "creating u-blox broadband bearer (%s profile, %s mode)...",
+ mm_ublox_usb_profile_get_string (self->priv->profile),
+ mm_ublox_networking_mode_get_string (self->priv->mode));
+ mm_broadband_bearer_ublox_new (
+ MM_BROADBAND_MODEM (self),
+ self->priv->profile,
+ self->priv->mode,
+ ctx->properties,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) broadband_bearer_ublox_new_ready,
+ task);
+ return;
+ }
+
+ /* If usb profile is back-compatible already, or if there is no NET port
+ * available, create default generic bearer */
+ mm_obj_dbg (self, "creating generic broadband bearer...");
+ mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
+ ctx->properties,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) broadband_bearer_new_ready,
+ task);
+ return;
+
+ case CREATE_BEARER_STEP_LAST:
+ g_assert (ctx->bearer);
+ g_task_return_pointer (task, g_object_ref (ctx->bearer), g_object_unref);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+modem_create_bearer (MMIfaceModem *self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CreateBearerContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (CreateBearerContext);
+ ctx->step = CREATE_BEARER_STEP_FIRST;
+ ctx->properties = g_object_ref (properties);
+
+ /* Flag whether this modem has exposed a network interface */
+ ctx->has_net = !!mm_base_modem_peek_best_data_port (MM_BASE_MODEM (self), MM_PORT_TYPE_NET);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) create_bearer_context_free);
+ create_bearer_step (task);
+}
+
+/*****************************************************************************/
+/* Create SIM (Modem interface) */
+
+static MMBaseSim *
+modem_create_sim_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return mm_sim_ublox_new_finish (res, error);
+}
+
+static void
+modem_create_sim (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_sim_ublox_new (MM_BASE_MODEM (self),
+ NULL, /* cancellable */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Setup ports (Broadband modem class) */
+
+static void
+setup_ports (MMBroadbandModem *_self)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ /* Call parent's setup ports first always */
+ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_ublox_parent_class)->setup_ports (_self);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Configure AT ports */
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ g_object_set (ports[i],
+ MM_PORT_SERIAL_SEND_DELAY, (guint64) 0,
+ NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->pbready_regex,
+ NULL, NULL, NULL);
+ }
+}
+
+/*****************************************************************************/
+
+MMBroadbandModemUblox *
+mm_broadband_modem_ublox_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_UBLOX,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (TTY) and u-blox bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_ublox_init (MMBroadbandModemUblox *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_MODEM_UBLOX,
+ MMBroadbandModemUbloxPrivate);
+ self->priv->profile = MM_UBLOX_USB_PROFILE_UNKNOWN;
+ self->priv->mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN;
+ self->priv->any_allowed = MM_MODEM_MODE_NONE;
+ self->priv->support_config.loaded = FALSE;
+ self->priv->support_config.method = SETTINGS_UPDATE_METHOD_UNKNOWN;
+ self->priv->support_config.uact = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->support_config.ubandsel = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->udtmfd_support = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->pbready_regex = g_regex_new ("\\r\\n\\+PBREADY\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface->create_sim = modem_create_sim;
+ iface->create_sim_finish = modem_create_sim_finish;
+ iface->create_bearer = modem_create_bearer;
+ iface->create_bearer_finish = modem_create_bearer_finish;
+ iface->load_unlock_retries = load_unlock_retries;
+ iface->load_unlock_retries_finish = load_unlock_retries_finish;
+ iface->load_power_state = load_power_state;
+ iface->load_power_state_finish = load_power_state_finish;
+ iface->modem_power_up = modem_power_up;
+ iface->modem_power_up_finish = common_modem_power_operation_finish;
+ iface->modem_power_down = modem_power_down;
+ iface->modem_power_down_finish = common_modem_power_operation_finish;
+ iface->modem_power_off = modem_power_off;
+ iface->modem_power_off_finish = common_modem_power_operation_finish;
+ iface->reset = modem_reset;
+ iface->reset_finish = common_modem_power_operation_finish;
+ iface->load_supported_modes = load_supported_modes;
+ iface->load_supported_modes_finish = load_supported_modes_finish;
+ iface->load_current_modes = load_current_modes;
+ iface->load_current_modes_finish = load_current_modes_finish;
+ iface->set_current_modes = set_current_modes;
+ iface->set_current_modes_finish = common_set_current_modes_bands_finish;
+ iface->load_supported_bands = load_supported_bands;
+ iface->load_supported_bands_finish = load_supported_bands_finish;
+ iface->load_current_bands = load_current_bands;
+ iface->load_current_bands_finish = load_current_bands_finish;
+ iface->set_current_bands = set_current_bands;
+ iface->set_current_bands_finish = common_set_current_modes_bands_finish;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->check_support = modem_voice_check_support;
+ iface->check_support_finish = modem_voice_check_support_finish;
+ iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_voice_setup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_voice_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_voice_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_voice_disable_unsolicited_events_finish;
+
+ iface->create_call = create_call;
+}
+
+static void
+finalize (GObject *object)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (object);
+
+ g_regex_unref (self->priv->pbready_regex);
+
+ if (self->priv->ucallstat_regex)
+ g_regex_unref (self->priv->ucallstat_regex);
+ if (self->priv->udtmfd_regex)
+ g_regex_unref (self->priv->udtmfd_regex);
+
+ G_OBJECT_CLASS (mm_broadband_modem_ublox_parent_class)->finalize (object);
+}
+
+static void
+mm_broadband_modem_ublox_class_init (MMBroadbandModemUbloxClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandModemUbloxPrivate));
+
+ object_class->finalize = finalize;
+
+ broadband_modem_class->setup_ports = setup_ports;
+}
diff --git a/plugins/ublox/mm-broadband-modem-ublox.h b/plugins/ublox/mm-broadband-modem-ublox.h
new file mode 100644
index 00000000..f1c6bbcf
--- /dev/null
+++ b/plugins/ublox/mm-broadband-modem-ublox.h
@@ -0,0 +1,49 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_UBLOX_H
+#define MM_BROADBAND_MODEM_UBLOX_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_UBLOX (mm_broadband_modem_ublox_get_type ())
+#define MM_BROADBAND_MODEM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUblox))
+#define MM_BROADBAND_MODEM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUbloxClass))
+#define MM_IS_BROADBAND_MODEM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_UBLOX))
+#define MM_IS_BROADBAND_MODEM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_UBLOX))
+#define MM_BROADBAND_MODEM_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_UBLOX, MMBroadbandModemUbloxClass))
+
+typedef struct _MMBroadbandModemUblox MMBroadbandModemUblox;
+typedef struct _MMBroadbandModemUbloxClass MMBroadbandModemUbloxClass;
+typedef struct _MMBroadbandModemUbloxPrivate MMBroadbandModemUbloxPrivate;
+
+struct _MMBroadbandModemUblox {
+ MMBroadbandModem parent;
+ MMBroadbandModemUbloxPrivate *priv;
+};
+
+struct _MMBroadbandModemUbloxClass{
+ MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_ublox_get_type (void);
+
+MMBroadbandModemUblox *mm_broadband_modem_ublox_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_UBLOX_H */
diff --git a/plugins/ublox/mm-modem-helpers-ublox.c b/plugins/ublox/mm-modem-helpers-ublox.c
new file mode 100644
index 00000000..bb0e02ac
--- /dev/null
+++ b/plugins/ublox/mm-modem-helpers-ublox.c
@@ -0,0 +1,2065 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-ublox.h"
+
+/*****************************************************************************/
+/* +UPINCNT response parser */
+
+gboolean
+mm_ublox_parse_upincnt_response (const gchar *response,
+ guint *out_pin_attempts,
+ guint *out_pin2_attempts,
+ guint *out_puk_attempts,
+ guint *out_puk2_attempts,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint pin_attempts = 0;
+ guint pin2_attempts = 0;
+ guint puk_attempts = 0;
+ guint puk2_attempts = 0;
+ gboolean success = TRUE;
+
+ g_assert (out_pin_attempts);
+ g_assert (out_pin2_attempts);
+ g_assert (out_puk_attempts);
+ g_assert (out_puk2_attempts);
+
+ /* Response may be e.g.:
+ * +UPINCNT: 3,3,10,10
+ */
+ r = g_regex_new ("\\+UPINCNT: (\\d+),(\\d+),(\\d+),(\\d+)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ if (!mm_get_uint_from_match_info (match_info, 1, &pin_attempts)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't parse PIN attempts");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 2, &pin2_attempts)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't parse PIN2 attempts");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 3, &puk_attempts)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't parse PUK attempts");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 4, &puk2_attempts)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't parse PUK2 attempts");
+ goto out;
+ }
+ success = TRUE;
+ }
+
+out:
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (!success) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +UPINCNT response: '%s'", response);
+ return FALSE;
+ }
+
+ *out_pin_attempts = pin_attempts;
+ *out_pin2_attempts = pin2_attempts;
+ *out_puk_attempts = puk_attempts;
+ *out_puk2_attempts = puk2_attempts;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* UUSBCONF? response parser */
+
+gboolean
+mm_ublox_parse_uusbconf_response (const gchar *response,
+ MMUbloxUsbProfile *out_profile,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ MMUbloxUsbProfile profile = MM_UBLOX_USB_PROFILE_UNKNOWN;
+
+ g_assert (out_profile != NULL);
+
+ /* Response may be e.g.:
+ * +UUSBCONF: 3,"RNDIS",,"0x1146"
+ * +UUSBCONF: 2,"ECM",,"0x1143"
+ * +UUSBCONF: 0,"",,"0x1141"
+ *
+ * Note: we don't rely on the PID; assuming future new modules will
+ * have a different PID but they may keep the profile names.
+ */
+ r = g_regex_new ("\\+UUSBCONF: (\\d+),([^,]*),([^,]*),([^,]*)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ gchar *profile_name;
+
+ profile_name = mm_get_string_unquoted_from_match_info (match_info, 2);
+ if (profile_name && profile_name[0]) {
+ if (g_str_equal (profile_name, "RNDIS"))
+ profile = MM_UBLOX_USB_PROFILE_RNDIS;
+ else if (g_str_equal (profile_name, "ECM"))
+ profile = MM_UBLOX_USB_PROFILE_ECM;
+ else
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unknown USB profile: '%s'", profile_name);
+ } else
+ profile = MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE;
+ g_free (profile_name);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (profile == MM_UBLOX_USB_PROFILE_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse profile response");
+ return FALSE;
+ }
+
+ *out_profile = profile;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* UBMCONF? response parser */
+
+gboolean
+mm_ublox_parse_ubmconf_response (const gchar *response,
+ MMUbloxNetworkingMode *out_mode,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ MMUbloxNetworkingMode mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN;
+
+ g_assert (out_mode != NULL);
+
+ /* Response may be e.g.:
+ * +UBMCONF: 1
+ * +UBMCONF: 2
+ */
+ r = g_regex_new ("\\+UBMCONF: (\\d+)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ guint mode_id = 0;
+
+ if (mm_get_uint_from_match_info (match_info, 1, &mode_id)) {
+ switch (mode_id) {
+ case 1:
+ mode = MM_UBLOX_NETWORKING_MODE_ROUTER;
+ break;
+ case 2:
+ mode = MM_UBLOX_NETWORKING_MODE_BRIDGE;
+ break;
+ default:
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unknown mode id: '%u'", mode_id);
+ break;
+ }
+ }
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (mode == MM_UBLOX_NETWORKING_MODE_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse networking mode response");
+ return FALSE;
+ }
+
+ *out_mode = mode;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* UIPADDR=N response parser */
+
+gboolean
+mm_ublox_parse_uipaddr_response (const gchar *response,
+ guint *out_cid,
+ gchar **out_if_name,
+ gchar **out_ipv4_address,
+ gchar **out_ipv4_subnet,
+ gchar **out_ipv6_global_address,
+ gchar **out_ipv6_link_local_address,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint cid = 0;
+ gchar *if_name = NULL;
+ gchar *ipv4_address = NULL;
+ gchar *ipv4_subnet = NULL;
+ gchar *ipv6_global_address = NULL;
+ gchar *ipv6_link_local_address = NULL;
+
+ /* Response may be e.g.:
+ * +UIPADDR: 1,"ccinet0","5.168.120.13","255.255.255.0","",""
+ * +UIPADDR: 2,"ccinet1","","","2001::2:200:FF:FE00:0/64","FE80::200:FF:FE00:0/64"
+ * +UIPADDR: 3,"ccinet2","5.10.100.2","255.255.255.0","2001::1:200:FF:FE00:0/64","FE80::200:FF:FE00:0/64"
+ *
+ * We assume only ONE line is returned; because we request +UIPADDR with a specific N CID.
+ */
+ r = g_regex_new ("\\+UIPADDR: (\\d+),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match +UIPADDR response");
+ goto out;
+ }
+
+ if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing cid");
+ goto out;
+ }
+
+ if (out_if_name && !(if_name = mm_get_string_unquoted_from_match_info (match_info, 2))) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing interface name");
+ goto out;
+ }
+
+ /* Remaining strings are optional */
+
+ if (out_ipv4_address)
+ ipv4_address = mm_get_string_unquoted_from_match_info (match_info, 3);
+
+ if (out_ipv4_subnet)
+ ipv4_subnet = mm_get_string_unquoted_from_match_info (match_info, 4);
+
+ if (out_ipv6_global_address)
+ ipv6_global_address = mm_get_string_unquoted_from_match_info (match_info, 5);
+
+ if (out_ipv6_link_local_address)
+ ipv6_link_local_address = mm_get_string_unquoted_from_match_info (match_info, 6);
+
+out:
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_free (if_name);
+ g_free (ipv4_address);
+ g_free (ipv4_subnet);
+ g_free (ipv6_global_address);
+ g_free (ipv6_link_local_address);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_cid)
+ *out_cid = cid;
+ if (out_if_name)
+ *out_if_name = if_name;
+ if (out_ipv4_address)
+ *out_ipv4_address = ipv4_address;
+ if (out_ipv4_subnet)
+ *out_ipv4_subnet = ipv4_subnet;
+ if (out_ipv6_global_address)
+ *out_ipv6_global_address = ipv6_global_address;
+ if (out_ipv6_link_local_address)
+ *out_ipv6_link_local_address = ipv6_link_local_address;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* CFUN? response parser */
+
+gboolean
+mm_ublox_parse_cfun_response (const gchar *response,
+ MMModemPowerState *out_state,
+ GError **error)
+{
+ guint state;
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return FALSE;
+
+ switch (state) {
+ case 1:
+ *out_state = MM_MODEM_POWER_STATE_ON;
+ return TRUE;
+ case 0:
+ /* minimum functionality */
+ case 4:
+ /* airplane mode */
+ case 19:
+ /* minimum functionality with SIM deactivated */
+ *out_state = MM_MODEM_POWER_STATE_LOW;
+ return TRUE;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown +CFUN state: %u", state);
+ return FALSE;
+ }
+}
+
+/*****************************************************************************/
+/* URAT=? response parser */
+
+/* Index of the array is the ublox-specific value */
+static const MMModemMode ublox_combinations[] = {
+ ( MM_MODEM_MODE_2G ),
+ ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ),
+ ( MM_MODEM_MODE_3G ),
+ ( MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_2G | MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_4G ),
+};
+
+GArray *
+mm_ublox_parse_urat_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error)
+{
+ GArray *combinations = NULL;
+ GArray *selected = NULL;
+ GArray *preferred = NULL;
+ gchar **split;
+ guint split_len;
+ GError *inner_error = NULL;
+ guint i;
+
+ /*
+ * E.g.:
+ * AT+URAT=?
+ * +URAT: (0-6),(0,2,3)
+ */
+ response = mm_strip_tag (response, "+URAT:");
+ split = mm_split_string_groups (response);
+ split_len = g_strv_length (split);
+ if (split_len > 2 || split_len < 1) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected number of groups in +URAT=? response: %u", g_strv_length (split));
+ goto out;
+ }
+
+ /* The selected list must have values */
+ selected = mm_parse_uint_list (split[0], &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!selected) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No selected RAT values given in +URAT=? response");
+ goto out;
+ }
+
+ /* For our purposes, the preferred list may be empty */
+ preferred = mm_parse_uint_list (split[1], &inner_error);
+ if (inner_error)
+ goto out;
+
+ /* Build array of combinations */
+ combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination));
+
+ for (i = 0; i < selected->len; i++) {
+ guint selected_value;
+ MMModemModeCombination combination;
+ guint j;
+
+ selected_value = g_array_index (selected, guint, i);
+ if (selected_value >= G_N_ELEMENTS (ublox_combinations)) {
+ mm_obj_warn (log_object, "unexpected AcT value: %u", selected_value);
+ continue;
+ }
+
+ /* Combination without any preferred */
+ combination.allowed = ublox_combinations[selected_value];
+ combination.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, combination);
+
+ if (mm_count_bits_set (combination.allowed) == 1)
+ continue;
+
+ if (!preferred)
+ continue;
+
+ for (j = 0; j < preferred->len; j++) {
+ guint preferred_value;
+
+ preferred_value = g_array_index (preferred, guint, j);
+ if (preferred_value >= G_N_ELEMENTS (ublox_combinations)) {
+ mm_obj_warn (log_object, "unexpected AcT preferred value: %u", preferred_value);
+ continue;
+ }
+ combination.preferred = ublox_combinations[preferred_value];
+ if (mm_count_bits_set (combination.preferred) != 1) {
+ mm_obj_warn (log_object, "AcT preferred value should be a single AcT: %u", preferred_value);
+ continue;
+ }
+ if (!(combination.allowed & combination.preferred))
+ continue;
+ g_array_append_val (combinations, combination);
+ }
+ }
+
+ if (combinations->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No combinations built from +URAT=? response");
+ goto out;
+ }
+
+out:
+ g_strfreev (split);
+ if (selected)
+ g_array_unref (selected);
+ if (preferred)
+ g_array_unref (preferred);
+
+ if (inner_error) {
+ if (combinations)
+ g_array_unref (combinations);
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ return combinations;
+}
+
+typedef struct {
+ const gchar *model;
+ SettingsUpdateMethod method;
+ FeatureSupport uact;
+ FeatureSupport ubandsel;
+ MMModemMode mode;
+ MMModemBand bands_2g[4];
+ MMModemBand bands_3g[6];
+ MMModemBand bands_4g[12];
+} BandConfiguration;
+
+static const BandConfiguration band_configuration[] = {
+ {
+ .model = "SARA-G300",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G,
+ .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }
+ },
+ {
+ .model = "SARA-G310",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }
+ },
+ {
+ .model = "SARA-G340",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G,
+ .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS }
+ },
+ {
+ .model = "SARA-G350",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }
+ },
+ {
+ .model = "SARA-G450",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS }
+ },
+ {
+ .model = "LISA-U200",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
+ },
+ {
+ .model = "LISA-U201",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
+ },
+ {
+ .model = "LISA-U230",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
+ },
+ {
+ .model = "LISA-U260",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }
+ },
+ {
+ .model = "LISA-U270",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }
+ },
+ {
+ .model = "SARA-U201",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 }
+ },
+ {
+ .model = "SARA-U260",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }
+ },
+ {
+ .model = "SARA-U270",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 }
+ },
+ {
+ .model = "SARA-U280",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 }
+ },
+ {
+ .model = "MPCI-L201",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 }
+ },
+ {
+ .model = "MPCI-L200",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4,
+ MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_17 }
+ },
+ {
+ .model = "MPCI-L210",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }
+ },
+ {
+ .model = "MPCI-L220",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_19 }
+ },
+ {
+ .model = "MPCI-L280",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 }
+ },
+ {
+ .model = "TOBY-L200",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_4,
+ MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_17 }
+ },
+ {
+ .model = "TOBY-L201",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 }
+ },
+ {
+ .model = "TOBY-L210",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }
+ },
+ {
+ .model = "TOBY-L220",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_19 }
+ },
+ {
+ .model = "TOBY-L280",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 }
+ },
+ {
+ .model = "TOBY-L4006",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_SUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_2 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_29 }
+ },
+ {
+ .model = "TOBY-L4106",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_SUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7,
+ MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_38 }
+ },
+ {
+ .model = "TOBY-L4206",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_SUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_9,
+ MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_28 }
+ },
+ {
+ .model = "TOBY-L4906",
+ .method = SETTINGS_UPDATE_METHOD_CFUN,
+ .uact = FEATURE_SUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_39,
+ MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41 }
+ },
+ {
+ .model = "TOBY-R200",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_2,
+ MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_12 }
+ },
+ {
+ .model = "TOBY-R202",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_12 }
+ },
+ {
+ .model = "LARA-R202",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_2 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_12 }
+ },
+ {
+ .model = "LARA-R203",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_12 }
+ },
+ {
+ .model = "LARA-R204",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_13 }
+ },
+ {
+ .model = "LARA-R211",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_20 }
+ },
+ {
+ .model = "LARA-R280",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .bands_3g = { MM_MODEM_BAND_UTRAN_1 },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_28 }
+ },
+ {
+ .model = "LARA-R3121",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_20 }
+ },
+ {
+ .model = "SARA-N200",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_8 }
+ },
+ {
+ .model = "SARA-N201",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_5 }
+ },
+ {
+ .model = "SARA-N210",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_20 }
+ },
+ {
+ .model = "SARA-N211",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20 }
+ },
+ {
+ .model = "SARA-N280",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_SUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_28 }
+ },
+ {
+ .model = "SARA-R410M-52B",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13 }
+ },
+ {
+ .model = "SARA-R410M-02B",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_39 }
+ },
+ {
+ .model = "SARA-R412M-02B",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ .bands_2g = { MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS },
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_39 }
+ },
+ {
+ .model = "SARA-N410-02B",
+ .method = SETTINGS_UPDATE_METHOD_COPS,
+ .uact = FEATURE_UNSUPPORTED,
+ .ubandsel = FEATURE_UNSUPPORTED,
+ .mode = MM_MODEM_MODE_4G,
+ .bands_4g = { MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3,
+ MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_8,
+ MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_19,
+ MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_28 }
+ },
+};
+
+gboolean
+mm_ublox_get_support_config (const gchar *model,
+ UbloxSupportConfig *config,
+ GError **error)
+{
+ guint i;
+
+ if (!model) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Support configuration unknown for unknown model");
+ return FALSE;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
+ /* NOTE: matching by prefix! */
+ if (g_str_has_prefix (model, band_configuration[i].model)) {
+ config->loaded = TRUE;
+ config->method = band_configuration[i].method;
+ config->uact = band_configuration[i].uact;
+ config->ubandsel = band_configuration[i].ubandsel;
+ return TRUE;
+ }
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No support configuration found for modem: %s", model);
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* Supported modes loading */
+
+static MMModemMode
+supported_modes_per_model (const gchar *model)
+{
+ MMModemMode mode;
+ guint i;
+
+ mode = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G;
+
+ if (model) {
+ for (i = 0; i < G_N_ELEMENTS (band_configuration); i++)
+ if (g_str_has_prefix (model, band_configuration[i].model)) {
+ mode = band_configuration[i].mode;
+ return mode;;
+ }
+ }
+
+ return mode;
+}
+
+GArray *
+mm_ublox_filter_supported_modes (const gchar *model,
+ GArray *combinations,
+ gpointer logger,
+ GError **error)
+{
+ MMModemModeCombination mode;
+ GArray *all;
+ GArray *filtered;
+
+ /* Model not specified? */
+ if (!model)
+ return combinations;
+
+ /* AT+URAT=? lies; we need an extra per-device filtering, thanks u-blox.
+ * Don't know all PIDs for all devices, so model string based filtering... */
+
+ mode.allowed = supported_modes_per_model (model);
+ mode.preferred = MM_MODEM_MODE_NONE;
+
+ /* Nothing filtered? */
+ if (mode.allowed == supported_modes_per_model (NULL))
+ return combinations;
+
+ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ g_array_append_val (all, mode);
+ filtered = mm_filter_supported_modes (all, combinations, logger);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ /* Error if nothing left */
+ if (filtered->len == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No valid mode combinations built after filtering (model %s)", model);
+ g_array_unref (filtered);
+ return NULL;
+ }
+
+ return filtered;
+}
+
+/*****************************************************************************/
+/* Supported bands loading */
+
+GArray *
+mm_ublox_get_supported_bands (const gchar *model,
+ gpointer log_object,
+ GError **error)
+{
+ MMModemMode mode;
+ GArray *bands;
+ guint i, j;
+
+ mode = supported_modes_per_model (model);
+ bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+
+ for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
+ if (g_str_has_prefix (model, band_configuration[i].model)) {
+ mm_obj_dbg (log_object, "known supported bands found for model: %s", band_configuration[i].model);
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (band_configuration)) {
+ mm_obj_warn (log_object, "unknown model name given when looking for supported bands: %s", model);
+ return NULL;
+ }
+
+ mode = band_configuration[i].mode;
+
+ if (mode & MM_MODEM_MODE_2G) {
+ for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_2g) && band_configuration[i].bands_2g[j]; j++) {
+ bands = g_array_append_val (bands, band_configuration[i].bands_2g[j]);
+ }
+ }
+
+ if (mode & MM_MODEM_MODE_3G) {
+ for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_3g) && band_configuration[i].bands_3g[j]; j++) {
+ bands = g_array_append_val (bands, band_configuration[i].bands_3g[j]);
+ }
+ }
+
+ if (mode & MM_MODEM_MODE_4G) {
+ for (j = 0; j < G_N_ELEMENTS (band_configuration[i].bands_4g) && band_configuration[i].bands_4g[j]; j++) {
+ bands = g_array_append_val (bands, band_configuration[i].bands_4g[j]);
+ }
+ }
+
+ if (bands->len == 0) {
+ g_array_unref (bands);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No valid supported bands loaded");
+ return NULL;
+ }
+
+ return bands;
+}
+
+typedef struct {
+ guint num;
+ MMModemBand band[4];
+} NumToBand;
+
+/* 2G GSM Band Frequencies */
+static const NumToBand num_bands_2g [] = {
+ { .num = 850, .band = { MM_MODEM_BAND_G850 } },
+ { .num = 900, .band = { MM_MODEM_BAND_EGSM } },
+ { .num = 1900, .band = { MM_MODEM_BAND_PCS } },
+ { .num = 1800, .band = { MM_MODEM_BAND_DCS } },
+};
+
+/* 3G UMTS Band Frequencies */
+static const NumToBand num_bands_3g [] = {
+ { .num = 800, .band = { MM_MODEM_BAND_UTRAN_6 } },
+ { .num = 850, .band = { MM_MODEM_BAND_UTRAN_5 } },
+ { .num = 900, .band = { MM_MODEM_BAND_UTRAN_8 } },
+ { .num = 1700, .band = { MM_MODEM_BAND_UTRAN_4 } },
+ { .num = 1900, .band = { MM_MODEM_BAND_UTRAN_2 } },
+ { .num = 2100, .band = { MM_MODEM_BAND_UTRAN_1 } },
+};
+
+/* 4G LTE Band Frequencies */
+static const NumToBand num_bands_4g [] = {
+ { .num = 700, .band = { MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_17 } },
+ { .num = 800, .band = { MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_27 } },
+ { .num = 850, .band = { MM_MODEM_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_26 } },
+ { .num = 900, .band = { MM_MODEM_BAND_EUTRAN_8 } },
+ { .num = 1700, .band = { MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_10 } },
+ { .num = 1800, .band = { MM_MODEM_BAND_EUTRAN_3 } },
+ { .num = 1900, .band = { MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_39 } },
+ { .num = 2100, .band = { MM_MODEM_BAND_EUTRAN_1 } },
+ { .num = 2300, .band = { MM_MODEM_BAND_EUTRAN_40 } },
+ { .num = 2500, .band = { MM_MODEM_BAND_EUTRAN_41 } },
+ { .num = 2600, .band = { MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_38 } },
+};
+/*****************************************************************************/
+/* +UBANDSEL? response parser */
+
+static MMModemBand
+num_to_band_2g (guint num)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (num_bands_2g); i++) {
+ if (num == num_bands_2g[i].num)
+ return num_bands_2g[i].band[0];
+ }
+ return MM_MODEM_BAND_UNKNOWN;
+}
+
+static MMModemBand
+num_to_band_3g (guint num)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (num_bands_3g); i++) {
+ if (num == num_bands_3g[i].num)
+ return num_bands_3g[i].band[0];
+ }
+ return MM_MODEM_BAND_UNKNOWN;
+}
+
+static guint
+band_to_num (MMModemBand band)
+{
+ guint i, j;
+
+ /* Search 2G list */
+ for (i = 0; i < G_N_ELEMENTS (num_bands_2g); i++) {
+ for (j = 0; j < G_N_ELEMENTS (num_bands_2g[i].band) && num_bands_2g[i].band[j]; j++) {
+ if (band == num_bands_2g[i].band[j])
+ return num_bands_2g[i].num;
+ }
+ }
+
+ /* Search 3G list */
+ for (i = 0; i < G_N_ELEMENTS (num_bands_3g); i++) {
+ for (j = 0; j < G_N_ELEMENTS (num_bands_3g[i].band) && num_bands_3g[i].band[j]; j++) {
+ if (band == num_bands_3g[i].band[j])
+ return num_bands_3g[i].num;
+ }
+ }
+
+ /* Search 4G list */
+ for (i = 0; i < G_N_ELEMENTS (num_bands_4g); i++) {
+ for (j = 0; j < G_N_ELEMENTS (num_bands_4g[i].band) && num_bands_4g[i].band[j]; j++) {
+ if (band == num_bands_4g[i].band[j])
+ return num_bands_4g[i].num;
+ }
+ }
+
+ /* Should never happen */
+ return 0;
+}
+
+static void
+append_bands (GArray *bands,
+ guint ubandsel_value,
+ MMModemMode mode,
+ const gchar *model,
+ gpointer log_object)
+{
+ guint i, j, k, x;
+ MMModemBand band;
+
+ /* Find Modem Model Index in band_configuration */
+ for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
+ if (g_str_has_prefix (model, band_configuration[i].model)) {
+ mm_obj_dbg (log_object, "known bands found for model: %s", band_configuration[i].model);
+ break;
+ }
+ }
+
+ if (i == G_N_ELEMENTS (band_configuration)) {
+ mm_obj_warn (log_object, "unknown model name given when looking for bands: %s", model);
+ return;
+ }
+
+ if (mode & MM_MODEM_MODE_2G) {
+ band = num_to_band_2g (ubandsel_value);
+ if (band != MM_MODEM_BAND_UNKNOWN) {
+ for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_2g); x++) {
+ if (band_configuration[i].bands_2g[x] == band) {
+ g_array_append_val (bands, band);
+ break;
+ }
+ }
+ }
+ }
+
+ if (mode & MM_MODEM_MODE_3G) {
+ band = num_to_band_3g (ubandsel_value);
+ if (band != MM_MODEM_BAND_UNKNOWN) {
+ for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_3g); x++) {
+ if (band_configuration[i].bands_3g[x] == band) {
+ g_array_append_val (bands, band);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Note: The weird code segment below is to separate out specific LTE bands since
+ * UBANDSEL? reports back the frequency of the band and not the band itself.
+ */
+
+ if (mode & MM_MODEM_MODE_4G) {
+ for (j = 0; j < G_N_ELEMENTS (num_bands_4g); j++) {
+ if (ubandsel_value == num_bands_4g[j].num) {
+ for (k = 0; k < G_N_ELEMENTS (num_bands_4g[j].band); k++) {
+ band = num_bands_4g[j].band[k];
+ if (band != MM_MODEM_BAND_UNKNOWN) {
+ for (x = 0; x < G_N_ELEMENTS (band_configuration[i].bands_4g); x++) {
+ if (band_configuration[i].bands_4g[x] == band) {
+ g_array_append_val (bands, band);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+GArray *
+mm_ublox_parse_ubandsel_response (const gchar *response,
+ const gchar *model,
+ gpointer log_object,
+ GError **error)
+{
+ GArray *array_values = NULL;
+ GArray *array = NULL;
+ gchar *dupstr = NULL;
+ GError *inner_error = NULL;
+ guint i;
+ MMModemMode mode;
+
+ if (!g_str_has_prefix (response, "+UBANDSEL")) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +UBANDSEL response: '%s'", response);
+ goto out;
+ }
+
+ /* Response may be e.g.:
+ * +UBANDSEL: 850,900,1800,1900
+ */
+ dupstr = g_strchomp (g_strdup (mm_strip_tag (response, "+UBANDSEL:")));
+
+ array_values = mm_parse_uint_list (dupstr, &inner_error);
+ if (!array_values)
+ goto out;
+
+ /* Convert list of ubandsel numbers to MMModemBand values */
+ mode = supported_modes_per_model (model);
+ array = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+ for (i = 0; i < array_values->len; i++)
+ append_bands (array, g_array_index (array_values, guint, i), mode, model, log_object);
+
+ if (!array->len) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No known band selection values matched in +UBANDSEL response: '%s'", response);
+ goto out;
+ }
+
+out:
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_clear_pointer (&array, g_array_unref);
+ }
+ g_clear_pointer (&array_values, g_array_unref);
+ g_free (dupstr);
+ return array;
+}
+
+/*****************************************************************************/
+/* UBANDSEL=X command builder */
+
+static gint
+ubandsel_num_cmp (const guint *a, const guint *b)
+{
+ return (*a - *b);
+}
+
+gchar *
+mm_ublox_build_ubandsel_set_command (GArray *bands,
+ const gchar *model,
+ GError **error)
+{
+ GString *command = NULL;
+ GArray *ubandsel_nums;
+ guint num;
+ guint i, j, k;
+
+ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)
+ return g_strdup ("+UBANDSEL=0");
+
+ for (i = 0; i < G_N_ELEMENTS (band_configuration); i++) {
+ if (g_str_has_prefix (model, band_configuration[i].model))
+ break;
+ }
+
+ if (i == G_N_ELEMENTS (band_configuration)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown modem model %s", model);
+ return NULL;
+ }
+
+ ubandsel_nums = g_array_sized_new (FALSE, FALSE, sizeof (guint), bands->len);
+
+ for (j = 0; j < bands->len; j++) {
+ MMModemBand band;
+ gboolean found = FALSE;
+
+ band = g_array_index (bands, MMModemBand, j);
+
+ /* Check to see if band is supported by the model */
+ for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_2g) && band_configuration[i].bands_2g[k]; k++) {
+ if (band == band_configuration[i].bands_2g[k])
+ found = TRUE;
+ }
+
+ for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_3g) && band_configuration[i].bands_3g[k]; k++) {
+ if (band == band_configuration[i].bands_3g[k])
+ found = TRUE;
+ }
+
+ for (k = 0; !found && k < G_N_ELEMENTS (band_configuration[i].bands_4g) && band_configuration[i].bands_4g[k]; k++) {
+ if (band == band_configuration[i].bands_4g[k])
+ found = TRUE;
+ }
+
+ if (found) {
+ num = band_to_num (band);
+ g_assert (num != 0);
+ g_array_append_val (ubandsel_nums, num);
+ }
+ }
+
+ if (ubandsel_nums->len == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Given band combination is unsupported");
+ g_array_unref (ubandsel_nums);
+ return NULL;
+ }
+
+ if (ubandsel_nums->len > 1)
+ g_array_sort (ubandsel_nums, (GCompareFunc) ubandsel_num_cmp);
+
+ /* Build command */
+ command = g_string_new ("+UBANDSEL=");
+ for (i = 0; i < ubandsel_nums->len; i++)
+ g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", g_array_index (ubandsel_nums, guint, i));
+
+ return g_string_free (command, FALSE);
+}
+
+/*****************************************************************************/
+/* Get mode to apply when ANY */
+
+MMModemMode
+mm_ublox_get_modem_mode_any (const GArray *combinations)
+{
+ guint i;
+ MMModemMode any = MM_MODEM_MODE_NONE;
+ guint any_bits_set = 0;
+
+ for (i = 0; i < combinations->len; i++) {
+ MMModemModeCombination *combination;
+ guint bits_set;
+
+ combination = &g_array_index (combinations, MMModemModeCombination, i);
+ if (combination->preferred != MM_MODEM_MODE_NONE)
+ continue;
+ bits_set = mm_count_bits_set (combination->allowed);
+ if (bits_set > any_bits_set) {
+ any_bits_set = bits_set;
+ any = combination->allowed;
+ }
+ }
+
+ /* If combinations were processed via mm_ublox_parse_urat_test_response(),
+ * we're sure that there will be at least one combination with preferred
+ * 'none', so there must be some valid combination as result */
+ g_assert (any != MM_MODEM_MODE_NONE);
+ return any;
+}
+
+/*****************************************************************************/
+/* UACT common config */
+
+typedef struct {
+ guint num;
+ MMModemBand band;
+} UactBandConfig;
+
+static const UactBandConfig uact_band_config[] = {
+ /* GSM bands */
+ { .num = 900, .band = MM_MODEM_BAND_EGSM },
+ { .num = 1800, .band = MM_MODEM_BAND_DCS },
+ { .num = 1900, .band = MM_MODEM_BAND_PCS },
+ { .num = 850, .band = MM_MODEM_BAND_G850 },
+ { .num = 450, .band = MM_MODEM_BAND_G450 },
+ { .num = 480, .band = MM_MODEM_BAND_G480 },
+ { .num = 750, .band = MM_MODEM_BAND_G750 },
+ { .num = 380, .band = MM_MODEM_BAND_G380 },
+ { .num = 410, .band = MM_MODEM_BAND_G410 },
+ { .num = 710, .band = MM_MODEM_BAND_G710 },
+ { .num = 810, .band = MM_MODEM_BAND_G810 },
+ /* UMTS bands */
+ { .num = 1, .band = MM_MODEM_BAND_UTRAN_1 },
+ { .num = 2, .band = MM_MODEM_BAND_UTRAN_2 },
+ { .num = 3, .band = MM_MODEM_BAND_UTRAN_3 },
+ { .num = 4, .band = MM_MODEM_BAND_UTRAN_4 },
+ { .num = 5, .band = MM_MODEM_BAND_UTRAN_5 },
+ { .num = 6, .band = MM_MODEM_BAND_UTRAN_6 },
+ { .num = 7, .band = MM_MODEM_BAND_UTRAN_7 },
+ { .num = 8, .band = MM_MODEM_BAND_UTRAN_8 },
+ { .num = 9, .band = MM_MODEM_BAND_UTRAN_9 },
+ { .num = 10, .band = MM_MODEM_BAND_UTRAN_10 },
+ { .num = 11, .band = MM_MODEM_BAND_UTRAN_11 },
+ { .num = 12, .band = MM_MODEM_BAND_UTRAN_12 },
+ { .num = 13, .band = MM_MODEM_BAND_UTRAN_13 },
+ { .num = 14, .band = MM_MODEM_BAND_UTRAN_14 },
+ { .num = 19, .band = MM_MODEM_BAND_UTRAN_19 },
+ { .num = 20, .band = MM_MODEM_BAND_UTRAN_20 },
+ { .num = 21, .band = MM_MODEM_BAND_UTRAN_21 },
+ { .num = 22, .band = MM_MODEM_BAND_UTRAN_22 },
+ { .num = 25, .band = MM_MODEM_BAND_UTRAN_25 },
+ /* LTE bands */
+ { .num = 101, .band = MM_MODEM_BAND_EUTRAN_1 },
+ { .num = 102, .band = MM_MODEM_BAND_EUTRAN_2 },
+ { .num = 103, .band = MM_MODEM_BAND_EUTRAN_3 },
+ { .num = 104, .band = MM_MODEM_BAND_EUTRAN_4 },
+ { .num = 105, .band = MM_MODEM_BAND_EUTRAN_5 },
+ { .num = 106, .band = MM_MODEM_BAND_EUTRAN_6 },
+ { .num = 107, .band = MM_MODEM_BAND_EUTRAN_7 },
+ { .num = 108, .band = MM_MODEM_BAND_EUTRAN_8 },
+ { .num = 109, .band = MM_MODEM_BAND_EUTRAN_9 },
+ { .num = 110, .band = MM_MODEM_BAND_EUTRAN_10 },
+ { .num = 111, .band = MM_MODEM_BAND_EUTRAN_11 },
+ { .num = 112, .band = MM_MODEM_BAND_EUTRAN_12 },
+ { .num = 113, .band = MM_MODEM_BAND_EUTRAN_13 },
+ { .num = 114, .band = MM_MODEM_BAND_EUTRAN_14 },
+ { .num = 117, .band = MM_MODEM_BAND_EUTRAN_17 },
+ { .num = 118, .band = MM_MODEM_BAND_EUTRAN_18 },
+ { .num = 119, .band = MM_MODEM_BAND_EUTRAN_19 },
+ { .num = 120, .band = MM_MODEM_BAND_EUTRAN_20 },
+ { .num = 121, .band = MM_MODEM_BAND_EUTRAN_21 },
+ { .num = 122, .band = MM_MODEM_BAND_EUTRAN_22 },
+ { .num = 123, .band = MM_MODEM_BAND_EUTRAN_23 },
+ { .num = 124, .band = MM_MODEM_BAND_EUTRAN_24 },
+ { .num = 125, .band = MM_MODEM_BAND_EUTRAN_25 },
+ { .num = 126, .band = MM_MODEM_BAND_EUTRAN_26 },
+ { .num = 127, .band = MM_MODEM_BAND_EUTRAN_27 },
+ { .num = 128, .band = MM_MODEM_BAND_EUTRAN_28 },
+ { .num = 129, .band = MM_MODEM_BAND_EUTRAN_29 },
+ { .num = 130, .band = MM_MODEM_BAND_EUTRAN_30 },
+ { .num = 131, .band = MM_MODEM_BAND_EUTRAN_31 },
+ { .num = 132, .band = MM_MODEM_BAND_EUTRAN_32 },
+ { .num = 133, .band = MM_MODEM_BAND_EUTRAN_33 },
+ { .num = 134, .band = MM_MODEM_BAND_EUTRAN_34 },
+ { .num = 135, .band = MM_MODEM_BAND_EUTRAN_35 },
+ { .num = 136, .band = MM_MODEM_BAND_EUTRAN_36 },
+ { .num = 137, .band = MM_MODEM_BAND_EUTRAN_37 },
+ { .num = 138, .band = MM_MODEM_BAND_EUTRAN_38 },
+ { .num = 139, .band = MM_MODEM_BAND_EUTRAN_39 },
+ { .num = 140, .band = MM_MODEM_BAND_EUTRAN_40 },
+ { .num = 141, .band = MM_MODEM_BAND_EUTRAN_41 },
+ { .num = 142, .band = MM_MODEM_BAND_EUTRAN_42 },
+ { .num = 143, .band = MM_MODEM_BAND_EUTRAN_43 },
+ { .num = 144, .band = MM_MODEM_BAND_EUTRAN_44 },
+ { .num = 145, .band = MM_MODEM_BAND_EUTRAN_45 },
+ { .num = 146, .band = MM_MODEM_BAND_EUTRAN_46 },
+ { .num = 147, .band = MM_MODEM_BAND_EUTRAN_47 },
+ { .num = 148, .band = MM_MODEM_BAND_EUTRAN_48 },
+};
+
+static MMModemBand
+uact_num_to_band (guint num)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (uact_band_config); i++) {
+ if (num == uact_band_config[i].num)
+ return uact_band_config[i].band;
+ }
+ return MM_MODEM_BAND_UNKNOWN;
+}
+
+static guint
+uact_band_to_num (MMModemBand band)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (uact_band_config); i++) {
+ if (band == uact_band_config[i].band)
+ return uact_band_config[i].num;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* UACT? response parser */
+
+static GArray *
+uact_num_array_to_band_array (GArray *nums)
+{
+ GArray *bands = NULL;
+ guint i;
+
+ if (!nums)
+ return NULL;
+
+ bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), nums->len);
+ for (i = 0; i < nums->len; i++) {
+ MMModemBand band;
+
+ band = uact_num_to_band (g_array_index (nums, guint, i));
+ g_array_append_val (bands, band);
+ }
+
+ return bands;
+}
+
+GArray *
+mm_ublox_parse_uact_response (const gchar *response,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ GArray *nums = NULL;
+ GArray *bands = NULL;
+
+ /*
+ * AT+UACT?
+ * +UACT: ,,,900,1800,1,8,101,103,107,108,120,138
+ */
+ r = g_regex_new ("\\+UACT: ([^,]*),([^,]*),([^,]*),(.*)(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ gchar *bandstr;
+
+ bandstr = mm_get_string_unquoted_from_match_info (match_info, 4);
+ nums = mm_parse_uint_list (bandstr, &inner_error);
+ g_free (bandstr);
+ }
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ /* Convert to MMModemBand values */
+ if (nums) {
+ bands = uact_num_array_to_band_array (nums);
+ g_array_unref (nums);
+ }
+
+ if (!bands)
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No known band selection values matched in +UACT response: '%s'", response);
+
+ return bands;
+}
+
+/*****************************************************************************/
+/* UACT=? response parser */
+
+static GArray *
+parse_bands_from_string (const gchar *str,
+ const gchar *group,
+ gpointer log_object)
+{
+ GArray *bands = NULL;
+ GError *inner_error = NULL;
+ GArray *nums;
+
+ nums = mm_parse_uint_list (str, &inner_error);
+ if (nums) {
+ gchar *tmpstr;
+
+ bands = uact_num_array_to_band_array (nums);
+ tmpstr = mm_common_build_bands_string ((MMModemBand *)(gpointer)(bands->data), bands->len);
+ mm_obj_dbg (log_object, "modem reports support for %s bands: %s", group, tmpstr);
+ g_free (tmpstr);
+
+ g_array_unref (nums);
+ } else if (inner_error) {
+ mm_obj_warn (log_object, "couldn't parse list of supported %s bands: %s", group, inner_error->message);
+ g_clear_error (&inner_error);
+ }
+
+ return bands;
+}
+
+gboolean
+mm_ublox_parse_uact_test (const gchar *response,
+ gpointer log_object,
+ GArray **bands2g_out,
+ GArray **bands3g_out,
+ GArray **bands4g_out,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ const gchar *bands2g_str = NULL;
+ const gchar *bands3g_str = NULL;
+ const gchar *bands4g_str = NULL;
+ GArray *bands2g = NULL;
+ GArray *bands3g = NULL;
+ GArray *bands4g = NULL;
+ gchar **split = NULL;
+
+ g_assert (bands2g_out && bands3g_out && bands4g_out);
+
+ /*
+ * AT+UACT=?
+ * +UACT: ,,,(900,1800),(1,8),(101,103,107,108,120),(138)
+ */
+ r = g_regex_new ("\\+UACT: ([^,]*),([^,]*),([^,]*),(.*)(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (g_match_info_matches (match_info)) {
+ gchar *aux;
+ guint n_groups;
+
+ aux = mm_get_string_unquoted_from_match_info (match_info, 4);
+ split = mm_split_string_groups (aux);
+ n_groups = g_strv_length (split);
+ if (n_groups >= 1)
+ bands2g_str = split[0];
+ if (n_groups >= 2)
+ bands3g_str = split[1];
+ if (n_groups >= 3)
+ bands4g_str = split[2];
+ g_free (aux);
+ }
+
+ if (!bands2g_str && !bands3g_str && !bands4g_str) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "frequency groups not found: %s", response);
+ goto out;
+ }
+
+ bands2g = parse_bands_from_string (bands2g_str, "2G", log_object);
+ bands3g = parse_bands_from_string (bands3g_str, "3G", log_object);
+ bands4g = parse_bands_from_string (bands4g_str, "4G", log_object);
+
+ if (!bands2g->len && !bands3g->len && !bands4g->len) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "no supported frequencies reported: %s", response);
+ goto out;
+ }
+
+ /* success */
+
+out:
+ g_strfreev (split);
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ if (bands2g)
+ g_array_unref (bands2g);
+ if (bands3g)
+ g_array_unref (bands3g);
+ if (bands4g)
+ g_array_unref (bands4g);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *bands2g_out = bands2g;
+ *bands3g_out = bands3g;
+ *bands4g_out = bands4g;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* UACT=X command builder */
+
+gchar *
+mm_ublox_build_uact_set_command (GArray *bands,
+ GError **error)
+{
+ GString *command;
+
+ /* Build command */
+ command = g_string_new ("+UACT=,,,");
+
+ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)
+ g_string_append (command, "0");
+ else {
+ guint i;
+
+ for (i = 0; i < bands->len; i++) {
+ MMModemBand band;
+ guint num;
+
+ band = g_array_index (bands, MMModemBand, i);
+ num = uact_band_to_num (band);
+ if (!num) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Band unsupported by this plugin: %s", mm_modem_band_get_string (band));
+ g_string_free (command, TRUE);
+ return NULL;
+ }
+
+ g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", num);
+ }
+ }
+
+ return g_string_free (command, FALSE);
+}
+
+/*****************************************************************************/
+/* URAT? response parser */
+
+gboolean
+mm_ublox_parse_urat_read_response (const gchar *response,
+ gpointer log_object,
+ MMModemMode *out_allowed,
+ MMModemMode *out_preferred,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ MMModemMode allowed = MM_MODEM_MODE_NONE;
+ MMModemMode preferred = MM_MODEM_MODE_NONE;
+ gchar *allowed_str = NULL;
+ gchar *preferred_str = NULL;
+
+ g_assert (out_allowed != NULL && out_preferred != NULL);
+
+ /* Response may be e.g.:
+ * +URAT: 1,2
+ * +URAT: 1
+ */
+ r = g_regex_new ("\\+URAT: (\\d+)(?:,(\\d+))?(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ guint value = 0;
+
+ /* Selected item is mandatory */
+ if (!mm_get_uint_from_match_info (match_info, 1, &value)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't read AcT selected value");
+ goto out;
+ }
+ if (value >= G_N_ELEMENTS (ublox_combinations)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected AcT selected value: %u", value);
+ goto out;
+ }
+ allowed = ublox_combinations[value];
+ allowed_str = mm_modem_mode_build_string_from_mask (allowed);
+ mm_obj_dbg (log_object, "current allowed modes retrieved: %s", allowed_str);
+
+ /* Preferred item is optional */
+ if (mm_get_uint_from_match_info (match_info, 2, &value)) {
+ if (value >= G_N_ELEMENTS (ublox_combinations)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected AcT preferred value: %u", value);
+ goto out;
+ }
+ preferred = ublox_combinations[value];
+ preferred_str = mm_modem_mode_build_string_from_mask (preferred);
+ mm_obj_dbg (log_object, "current preferred modes retrieved: %s", preferred_str);
+ if (mm_count_bits_set (preferred) != 1) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "AcT preferred value should be a single AcT: %s", preferred_str);
+ goto out;
+ }
+ if (!(allowed & preferred)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "AcT preferred value (%s) not a subset of the allowed value (%s)",
+ preferred_str, allowed_str);
+ goto out;
+ }
+ }
+ }
+
+out:
+
+ g_free (allowed_str);
+ g_free (preferred_str);
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (allowed == MM_MODEM_MODE_NONE) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +URAT response: %s", response);
+ return FALSE;
+ }
+
+ *out_allowed = allowed;
+ *out_preferred = preferred;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* URAT=X command builder */
+
+static gboolean
+append_rat_value (GString *str,
+ MMModemMode mode,
+ GError **error)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (ublox_combinations); i++) {
+ if (ublox_combinations[i] == mode) {
+ g_string_append_printf (str, "%u", i);
+ return TRUE;
+ }
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No AcT value matches requested mode");
+ return FALSE;
+}
+
+gchar *
+mm_ublox_build_urat_set_command (MMModemMode allowed,
+ MMModemMode preferred,
+ GError **error)
+{
+ GString *command;
+
+ command = g_string_new ("+URAT=");
+ if (!append_rat_value (command, allowed, error)) {
+ g_string_free (command, TRUE);
+ return NULL;
+ }
+
+ if (preferred != MM_MODEM_MODE_NONE) {
+ g_string_append (command, ",");
+ if (!append_rat_value (command, preferred, error)) {
+ g_string_free (command, TRUE);
+ return NULL;
+ }
+ }
+
+ return g_string_free (command, FALSE);
+}
+
+/*****************************************************************************/
+/* +UAUTHREQ=? test parser */
+
+MMUbloxBearerAllowedAuth
+mm_ublox_parse_uauthreq_test (const char *response,
+ gpointer log_object,
+ GError **error)
+{
+ MMUbloxBearerAllowedAuth mask = MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN;
+ GError *inner_error = NULL;
+ GArray *allowed_auths = NULL;
+ gchar **split;
+ guint split_len;
+
+ /*
+ * Response may be like:
+ * AT+UAUTHREQ=?
+ * +UAUTHREQ: (1-4),(0-2),,
+ */
+ response = mm_strip_tag (response, "+UAUTHREQ:");
+ split = mm_split_string_groups (response);
+ split_len = g_strv_length (split);
+ if (split_len < 2) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected number of groups in +UAUTHREQ=? response: %u", g_strv_length (split));
+ goto out;
+ }
+
+ allowed_auths = mm_parse_uint_list (split[1], &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (allowed_auths) {
+ guint i;
+
+ for (i = 0; i < allowed_auths->len; i++) {
+ guint val;
+
+ val = g_array_index (allowed_auths, guint, i);
+ switch (val) {
+ case 0:
+ mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_NONE;
+ break;
+ case 1:
+ mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_PAP;
+ break;
+ case 2:
+ mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP;
+ break;
+ case 3:
+ mask |= MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO;
+ break;
+ default:
+ mm_obj_warn (log_object, "unexpected +UAUTHREQ value: %u", val);
+ break;
+ }
+ }
+ }
+
+ if (!mask) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No supported authentication methods in +UAUTHREQ=? response");
+ goto out;
+ }
+
+out:
+ g_strfreev (split);
+
+ if (allowed_auths)
+ g_array_unref (allowed_auths);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN;
+ }
+
+ return mask;
+}
+
+/*****************************************************************************/
+/* +UGCNTRD response parser */
+
+gboolean
+mm_ublox_parse_ugcntrd_response_for_cid (const gchar *response,
+ guint in_cid,
+ guint64 *out_session_tx_bytes,
+ guint64 *out_session_rx_bytes,
+ guint64 *out_total_tx_bytes,
+ guint64 *out_total_rx_bytes,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *inner_error = NULL;
+ guint64 session_tx_bytes = 0;
+ guint64 session_rx_bytes = 0;
+ guint64 total_tx_bytes = 0;
+ guint64 total_rx_bytes = 0;
+ gboolean matched = FALSE;
+
+ /* Response may be e.g.:
+ * +UGCNTRD: 31,2704,1819,2724,1839
+ * We assume only ONE line is returned.
+ */
+ r = g_regex_new ("\\+UGCNTRD:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ /* Report invalid CID given */
+ if (!in_cid) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid CID given");
+ goto out;
+ }
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ while (!inner_error && g_match_info_matches (match_info)) {
+ guint cid = 0;
+
+ /* Matched CID? */
+ if (!mm_get_uint_from_match_info (match_info, 1, &cid) || cid != in_cid) {
+ g_match_info_next (match_info, &inner_error);
+ continue;
+ }
+
+ if (out_session_tx_bytes && !mm_get_u64_from_match_info (match_info, 2, &session_tx_bytes)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing session TX bytes");
+ goto out;
+ }
+
+ if (out_session_rx_bytes && !mm_get_u64_from_match_info (match_info, 3, &session_rx_bytes)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing session RX bytes");
+ goto out;
+ }
+
+ if (out_total_tx_bytes && !mm_get_u64_from_match_info (match_info, 4, &total_tx_bytes)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing total TX bytes");
+ goto out;
+ }
+
+ if (out_total_rx_bytes && !mm_get_u64_from_match_info (match_info, 5, &total_rx_bytes)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing total RX bytes");
+ goto out;
+ }
+
+ matched = TRUE;
+ break;
+ }
+
+ if (!matched) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No statistics found for CID %u", in_cid);
+ goto out;
+ }
+
+out:
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_session_tx_bytes)
+ *out_session_tx_bytes = session_tx_bytes;
+ if (out_session_rx_bytes)
+ *out_session_rx_bytes = session_rx_bytes;
+ if (out_total_tx_bytes)
+ *out_total_tx_bytes = total_tx_bytes;
+ if (out_total_rx_bytes)
+ *out_total_rx_bytes = total_rx_bytes;
+ return TRUE;
+}
diff --git a/plugins/ublox/mm-modem-helpers-ublox.h b/plugins/ublox/mm-modem-helpers-ublox.h
new file mode 100644
index 00000000..06bba003
--- /dev/null
+++ b/plugins/ublox/mm-modem-helpers-ublox.h
@@ -0,0 +1,213 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_MODEM_HELPERS_UBLOX_H
+#define MM_MODEM_HELPERS_UBLOX_H
+
+#include <glib.h>
+#include <ModemManager.h>
+
+/*****************************************************************************/
+/* AT Commands Support */
+
+typedef enum {
+ FEATURE_SUPPORT_UNKNOWN,
+ FEATURE_SUPPORTED,
+ FEATURE_UNSUPPORTED,
+} FeatureSupport;
+
+typedef enum {
+ SETTINGS_UPDATE_METHOD_UNKNOWN,
+ SETTINGS_UPDATE_METHOD_CFUN,
+ SETTINGS_UPDATE_METHOD_COPS,
+} SettingsUpdateMethod;
+
+typedef struct UbloxSupportConfig {
+ gboolean loaded;
+ SettingsUpdateMethod method;
+ FeatureSupport uact;
+ FeatureSupport ubandsel;
+} UbloxSupportConfig;
+
+/*****************************************************************************/
+/* +UPINCNT response parser */
+
+gboolean mm_ublox_parse_upincnt_response (const gchar *response,
+ guint *out_pin_attempts,
+ guint *out_pin2_attempts,
+ guint *out_puk_attempts,
+ guint *out_puk2_attempts,
+ GError **error);
+
+/*****************************************************************************/
+/* UUSBCONF? response parser */
+
+typedef enum { /*< underscore_name=mm_ublox_usb_profile >*/
+ MM_UBLOX_USB_PROFILE_UNKNOWN,
+ MM_UBLOX_USB_PROFILE_RNDIS,
+ MM_UBLOX_USB_PROFILE_ECM,
+ MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE,
+} MMUbloxUsbProfile;
+
+gboolean mm_ublox_parse_uusbconf_response (const gchar *response,
+ MMUbloxUsbProfile *out_profile,
+ GError **error);
+
+/*****************************************************************************/
+/* UBMCONF? response parser */
+
+typedef enum { /*< underscore_name=mm_ublox_networking_mode >*/
+ MM_UBLOX_NETWORKING_MODE_UNKNOWN,
+ MM_UBLOX_NETWORKING_MODE_ROUTER,
+ MM_UBLOX_NETWORKING_MODE_BRIDGE,
+} MMUbloxNetworkingMode;
+
+gboolean mm_ublox_parse_ubmconf_response (const gchar *response,
+ MMUbloxNetworkingMode *out_mode,
+ GError **error);
+
+/*****************************************************************************/
+/* UIPADDR=N response parser */
+
+gboolean mm_ublox_parse_uipaddr_response (const gchar *response,
+ guint *out_cid,
+ gchar **out_if_name,
+ gchar **out_ipv4_address,
+ gchar **out_ipv4_subnet,
+ gchar **out_ipv6_global_address,
+ gchar **out_ipv6_link_local_address,
+ GError **error);
+
+/*****************************************************************************/
+/* CFUN? response parser */
+
+gboolean mm_ublox_parse_cfun_response (const gchar *response,
+ MMModemPowerState *out_state,
+ GError **error);
+
+/*****************************************************************************/
+/* URAT=? response parser */
+
+GArray *mm_ublox_parse_urat_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error);
+
+/*****************************************************************************/
+/* Model-based config support loading */
+
+gboolean mm_ublox_get_support_config (const gchar *model,
+ UbloxSupportConfig *config,
+ GError **error);
+
+/*****************************************************************************/
+/* Model-based supported modes filtering */
+
+GArray *mm_ublox_filter_supported_modes (const gchar *model,
+ GArray *combinations,
+ gpointer logger,
+ GError **error);
+
+/*****************************************************************************/
+/* Model-based supported bands loading */
+
+GArray *mm_ublox_get_supported_bands (const gchar *model,
+ gpointer log_object,
+ GError **error);
+
+/*****************************************************************************/
+/* UBANDSEL? response parser */
+
+GArray *mm_ublox_parse_ubandsel_response (const gchar *response,
+ const gchar *model,
+ gpointer log_object,
+ GError **error);
+
+/*****************************************************************************/
+/* UBANDSEL=X command builder */
+
+gchar *mm_ublox_build_ubandsel_set_command (GArray *bands,
+ const gchar *model,
+ GError **error);
+
+/*****************************************************************************/
+/* UACT? response parser */
+
+GArray *mm_ublox_parse_uact_response (const gchar *response,
+ GError **error);
+
+/*****************************************************************************/
+/* UACT=? test parser */
+
+gboolean mm_ublox_parse_uact_test (const gchar *response,
+ gpointer log_object,
+ GArray **bands_2g,
+ GArray **bands_3g,
+ GArray **bands_4g,
+ GError **error);
+
+/*****************************************************************************/
+/* UACT=X command builder */
+
+gchar *mm_ublox_build_uact_set_command (GArray *bands,
+ GError **error);
+
+/*****************************************************************************/
+/* Get mode to apply when ANY */
+
+MMModemMode mm_ublox_get_modem_mode_any (const GArray *combinations);
+
+/*****************************************************************************/
+/* URAT? response parser */
+
+gboolean mm_ublox_parse_urat_read_response (const gchar *response,
+ gpointer log_object,
+ MMModemMode *out_allowed,
+ MMModemMode *out_preferred,
+ GError **error);
+
+/*****************************************************************************/
+/* URAT=X command builder */
+
+gchar *mm_ublox_build_urat_set_command (MMModemMode allowed,
+ MMModemMode preferred,
+ GError **error);
+
+/*****************************************************************************/
+/* +UAUTHREQ=? test parser */
+
+typedef enum { /*< underscore_name=mm_ublox_bearer_allowed_auth >*/
+ MM_UBLOX_BEARER_ALLOWED_AUTH_UNKNOWN = 0,
+ MM_UBLOX_BEARER_ALLOWED_AUTH_NONE = 1 << 0,
+ MM_UBLOX_BEARER_ALLOWED_AUTH_PAP = 1 << 1,
+ MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP = 1 << 2,
+ MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO = 1 << 3,
+} MMUbloxBearerAllowedAuth;
+
+MMUbloxBearerAllowedAuth mm_ublox_parse_uauthreq_test (const char *response,
+ gpointer log_object,
+ GError **error);
+
+/*****************************************************************************/
+/* +UGCNTRD response parser */
+
+gboolean mm_ublox_parse_ugcntrd_response_for_cid (const gchar *response,
+ guint in_cid,
+ guint64 *session_tx_bytes,
+ guint64 *session_rx_bytes,
+ guint64 *total_tx_bytes,
+ guint64 *total_rx_bytes,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_UBLOX_H */
diff --git a/plugins/ublox/mm-plugin-ublox.c b/plugins/ublox/mm-plugin-ublox.c
new file mode 100644
index 00000000..569054b9
--- /dev/null
+++ b/plugins/ublox/mm-plugin-ublox.c
@@ -0,0 +1,264 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+#include <gmodule.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-serial-parsers.h"
+#include "mm-broadband-modem-ublox.h"
+#include "mm-plugin-ublox.h"
+
+G_DEFINE_TYPE (MMPluginUblox, mm_plugin_ublox, MM_TYPE_PLUGIN)
+
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
+
+/*****************************************************************************/
+
+static MMBaseModem *
+create_modem (MMPlugin *self,
+ const gchar *sysfs_path,
+ const gchar **drivers,
+ guint16 vendor,
+ guint16 product,
+ GList *probes,
+ GError **error)
+{
+ return MM_BASE_MODEM (mm_broadband_modem_ublox_new (sysfs_path,
+ drivers,
+ mm_plugin_get_name (self),
+ vendor,
+ product));
+}
+
+/*****************************************************************************/
+/* Custom init context */
+
+typedef struct {
+ MMPortSerialAt *port;
+ GRegex *ready_regex;
+ guint timeout_id;
+ gint wait_timeout_secs;
+} CustomInitContext;
+
+static void
+custom_init_context_free (CustomInitContext *ctx)
+{
+ g_assert (!ctx->timeout_id);
+ g_regex_unref (ctx->ready_regex);
+ g_object_unref (ctx->port);
+ g_slice_free (CustomInitContext, ctx);
+}
+
+static gboolean
+ublox_custom_init_finish (MMPortProbe *probe,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static gboolean
+ready_timeout (GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ ctx->timeout_id = 0;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port, ctx->ready_regex,
+ NULL, NULL, NULL);
+
+ mm_obj_dbg (probe, "timed out waiting for READY unsolicited message");
+
+ /* not an error really, we didn't probe anything yet, that's all */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+ready_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ mm_obj_dbg (probe, "received READY: port is AT");
+
+ /* Flag as an AT port right away */
+ mm_port_probe_set_result_at (probe, TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+wait_for_ready (GTask *task)
+{
+ CustomInitContext *ctx;
+ MMPortProbe *probe;
+
+ ctx = g_task_get_task_data (task);
+ probe = g_task_get_source_object (task);
+
+ mm_obj_dbg (probe, "waiting for READY unsolicited message...");
+
+ /* Configure a regex on the TTY, so that we stop the custom init
+ * as soon as +READY URC is received */
+ mm_port_serial_at_add_unsolicited_msg_handler (ctx->port,
+ ctx->ready_regex,
+ (MMPortSerialAtUnsolicitedMsgFn) ready_received,
+ task,
+ NULL);
+
+ mm_obj_dbg (probe, "waiting %d seconds for init timeout", ctx->wait_timeout_secs);
+
+ /* Otherwise, let the custom init timeout in some seconds. */
+ ctx->timeout_id = g_timeout_add_seconds (ctx->wait_timeout_secs, (GSourceFunc) ready_timeout, task);
+}
+
+static void
+quick_at_ready (MMPortSerialAt *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortProbe *probe;
+ g_autoptr(GError) error = NULL;
+
+ probe = g_task_get_source_object (task);
+
+ mm_port_serial_at_command_finish (port, res, &error);
+ if (error) {
+ /* On a timeout error, wait for READY URC */
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
+ wait_for_ready (task);
+ return;
+ }
+ /* On an unknown error, make it fatal */
+ if (!mm_serial_parser_v1_is_known_error (error)) {
+ mm_obj_warn (probe, "custom port initialization logic failed: %s", error->message);
+ goto out;
+ }
+ }
+
+ mm_obj_dbg (probe, "port is AT");
+ mm_port_probe_set_result_at (probe, TRUE);
+
+out:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+ublox_custom_init (MMPortProbe *probe,
+ MMPortSerialAt *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CustomInitContext *ctx;
+ gint wait_timeout_secs;
+
+ task = g_task_new (probe, cancellable, callback, user_data);
+
+ /* If no explicit READY_DELAY configured, we don't need a custom init procedure */
+ wait_timeout_secs = mm_kernel_device_get_property_as_int (mm_port_probe_peek_port (probe), "ID_MM_UBLOX_PORT_READY_DELAY");
+ if (wait_timeout_secs <= 0) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (CustomInitContext);
+ ctx->wait_timeout_secs = wait_timeout_secs;
+ ctx->port = g_object_ref (port);
+ ctx->ready_regex = g_regex_new ("\\r\\n\\+AT:\\s*READY\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) custom_init_context_free);
+
+ /* If the device hasn't been plugged in right away, we assume it was already
+ * running for some time. We validate the assumption with a quick AT probe,
+ * and if it times out, we run the explicit READY wait from scratch (e.g.
+ * to cope with the case where MM starts after the TTY has been exposed but
+ * where the device was also just reseted) */
+ if (!mm_device_get_hotplugged (mm_port_probe_peek_device (probe))) {
+ mm_port_serial_at_command (ctx->port,
+ "AT",
+ 1,
+ FALSE, /* raw */
+ FALSE, /* allow_cached */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)quick_at_ready,
+ task);
+ return;
+ }
+
+ /* Device hotplugged and has a defined ready delay, wait for READY URC */
+ wait_for_ready (task);
+}
+
+/*****************************************************************************/
+
+G_MODULE_EXPORT MMPlugin *
+mm_plugin_create (void)
+{
+ static const gchar *subsystems[] = { "tty", "net", NULL };
+ static const guint16 vendor_ids[] = { 0x1546, 0 };
+ static const gchar *vendor_strings[] = { "u-blox", NULL };
+ static const MMAsyncMethod custom_init = {
+ .async = G_CALLBACK (ublox_custom_init),
+ .finish = G_CALLBACK (ublox_custom_init_finish),
+ };
+
+ return MM_PLUGIN (g_object_new (MM_TYPE_PLUGIN_UBLOX,
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
+ MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
+ MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
+ MM_PLUGIN_ALLOWED_VENDOR_STRINGS, vendor_strings,
+ MM_PLUGIN_ALLOWED_AT, TRUE,
+ MM_PLUGIN_SEND_DELAY, (guint64) 0,
+ MM_PLUGIN_CUSTOM_INIT, &custom_init,
+ NULL));
+}
+
+static void
+mm_plugin_ublox_init (MMPluginUblox *self)
+{
+}
+
+static void
+mm_plugin_ublox_class_init (MMPluginUbloxClass *klass)
+{
+ MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
+
+ plugin_class->create_modem = create_modem;
+}
diff --git a/plugins/ublox/mm-plugin-ublox.h b/plugins/ublox/mm-plugin-ublox.h
new file mode 100644
index 00000000..adfc6247
--- /dev/null
+++ b/plugins/ublox/mm-plugin-ublox.h
@@ -0,0 +1,40 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PLUGIN_UBLOX_H
+#define MM_PLUGIN_UBLOX_H
+
+#include "mm-plugin.h"
+
+#define MM_TYPE_PLUGIN_UBLOX (mm_plugin_ublox_get_type ())
+#define MM_PLUGIN_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_UBLOX, MMPluginUblox))
+#define MM_PLUGIN_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_UBLOX, MMPluginUbloxClass))
+#define MM_IS_PLUGIN_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_UBLOX))
+#define MM_IS_PLUGIN_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_UBLOX))
+#define MM_PLUGIN_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_UBLOX, MMPluginUbloxClass))
+
+typedef struct {
+ MMPlugin parent;
+} MMPluginUblox;
+
+typedef struct {
+ MMPluginClass parent;
+} MMPluginUbloxClass;
+
+GType mm_plugin_ublox_get_type (void);
+
+G_MODULE_EXPORT MMPlugin *mm_plugin_create (void);
+
+#endif /* MM_PLUGIN_UBLOX_H */
diff --git a/plugins/ublox/mm-sim-ublox.c b/plugins/ublox/mm-sim-ublox.c
new file mode 100644
index 00000000..5850767e
--- /dev/null
+++ b/plugins/ublox/mm-sim-ublox.c
@@ -0,0 +1,163 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-base-modem-at.h"
+
+#include "mm-sim-ublox.h"
+
+G_DEFINE_TYPE (MMSimUblox, mm_sim_ublox, MM_TYPE_BASE_SIM)
+
+/*****************************************************************************/
+/* SIM identifier loading */
+
+static gchar *
+load_sim_identifier_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+parent_load_sim_identifier_ready (MMSimUblox *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ gchar *simid;
+
+ simid = MM_BASE_SIM_CLASS (mm_sim_ublox_parent_class)->load_sim_identifier_finish (MM_BASE_SIM (self), res, &error);
+ if (simid)
+ g_task_return_pointer (task, simid, g_free);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+ccid_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ const gchar *response;
+ gchar *parsed;
+
+ response = mm_base_modem_at_command_finish (modem, res, NULL);
+ if (!response)
+ goto error;
+
+ response = mm_strip_tag (response, "+CCID:");
+ if (!response)
+ goto error;
+
+ parsed = mm_3gpp_parse_iccid (response, NULL);
+ if (parsed) {
+ g_task_return_pointer (task, parsed, g_free);
+ g_object_unref (task);
+ return;
+ }
+
+error:
+ /* Chain up to parent method to for devices that don't support +CCID properly */
+ self = g_task_get_source_object (task);
+ MM_BASE_SIM_CLASS (mm_sim_ublox_parent_class)->load_sim_identifier (self,
+ (GAsyncReadyCallback) parent_load_sim_identifier_ready,
+ task);
+}
+
+static void
+load_sim_identifier (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBaseModem *modem = NULL;
+
+ g_object_get (self,
+ MM_BASE_SIM_MODEM, &modem,
+ NULL);
+ mm_base_modem_at_command (
+ modem,
+ "+CCID",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback)ccid_ready,
+ g_task_new (self, NULL, callback, user_data));
+ g_object_unref (modem);
+}
+
+/*****************************************************************************/
+
+MMBaseSim *
+mm_sim_ublox_new_finish (GAsyncResult *res,
+ GError **error)
+{
+ GObject *source;
+ GObject *sim;
+
+ source = g_async_result_get_source_object (res);
+ sim = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error);
+ g_object_unref (source);
+
+ if (!sim)
+ return NULL;
+
+ /* Only export valid SIMs */
+ mm_base_sim_export (MM_BASE_SIM (sim));
+
+ return MM_BASE_SIM (sim);
+}
+
+void
+mm_sim_ublox_new (MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (MM_TYPE_SIM_UBLOX,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ callback,
+ user_data,
+ MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
+ NULL);
+}
+
+static void
+mm_sim_ublox_init (MMSimUblox *self)
+{
+}
+
+static void
+mm_sim_ublox_class_init (MMSimUbloxClass *klass)
+{
+ MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
+
+ base_sim_class->load_sim_identifier = load_sim_identifier;
+ base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
+}
diff --git a/plugins/ublox/mm-sim-ublox.h b/plugins/ublox/mm-sim-ublox.h
new file mode 100644
index 00000000..31e2c98b
--- /dev/null
+++ b/plugins/ublox/mm-sim-ublox.h
@@ -0,0 +1,51 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SIM_UBLOX_H
+#define MM_SIM_UBLOX_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-base-sim.h"
+
+#define MM_TYPE_SIM_UBLOX (mm_sim_ublox_get_type ())
+#define MM_SIM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SIM_UBLOX, MMSimUblox))
+#define MM_SIM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SIM_UBLOX, MMSimUbloxClass))
+#define MM_IS_SIM_UBLOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SIM_UBLOX))
+#define MM_IS_SIM_UBLOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SIM_UBLOX))
+#define MM_SIM_UBLOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SIM_UBLOX, MMSimUbloxClass))
+
+typedef struct _MMSimUblox MMSimUblox;
+typedef struct _MMSimUbloxClass MMSimUbloxClass;
+
+struct _MMSimUblox {
+ MMBaseSim parent;
+};
+
+struct _MMSimUbloxClass {
+ MMBaseSimClass parent;
+};
+
+GType mm_sim_ublox_get_type (void);
+
+void mm_sim_ublox_new (MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseSim *mm_sim_ublox_new_finish (GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SIM_UBLOX_H */
diff --git a/plugins/ublox/tests/test-modem-helpers-ublox.c b/plugins/ublox/tests/test-modem-helpers-ublox.c
new file mode 100644
index 00000000..2d662877
--- /dev/null
+++ b/plugins/ublox/tests/test-modem-helpers-ublox.c
@@ -0,0 +1,1026 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+#include <arpa/inet.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-ublox.h"
+
+#include "test-helpers.h"
+
+/*****************************************************************************/
+/* Test +UPINCNT responses */
+
+typedef struct {
+ const gchar *str;
+ guint pin_attempts;
+ guint pin2_attempts;
+ guint puk_attempts;
+ guint puk2_attempts;
+} UpinCntResponseTest;
+
+static const UpinCntResponseTest upincnt_response_tests[] = {
+ { .str = "+UPINCNT: 3,3,10,10\r\n",
+ .pin_attempts = 3,
+ .pin2_attempts = 3,
+ .puk_attempts = 10,
+ .puk2_attempts = 10
+ },
+ { .str = "+UPINCNT: 0,3,5,5\r\n",
+ .pin_attempts = 0,
+ .pin2_attempts = 3,
+ .puk_attempts = 5,
+ .puk2_attempts = 5
+ },
+ { .str = "+UPINCNT: 0,0,0,0\r\n",
+ .pin_attempts = 0,
+ .pin2_attempts = 0,
+ .puk_attempts = 0,
+ .puk2_attempts = 0
+ },
+};
+
+static void
+test_upincnt_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (upincnt_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint pin_attempts = G_MAXUINT;
+ guint pin2_attempts = G_MAXUINT;
+ guint puk_attempts = G_MAXUINT;
+ guint puk2_attempts = G_MAXUINT;
+
+ success = mm_ublox_parse_upincnt_response (upincnt_response_tests[i].str,
+ &pin_attempts, &pin2_attempts,
+ &puk_attempts, &puk2_attempts,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (upincnt_response_tests[i].pin_attempts, ==, pin_attempts);
+ g_assert_cmpuint (upincnt_response_tests[i].pin2_attempts, ==, pin2_attempts);
+ g_assert_cmpuint (upincnt_response_tests[i].puk_attempts, ==, puk_attempts);
+ g_assert_cmpuint (upincnt_response_tests[i].puk2_attempts, ==, puk2_attempts);
+ }
+}
+
+/*****************************************************************************/
+/* Test UUSBCONF? responses */
+
+typedef struct {
+ const gchar *str;
+ MMUbloxUsbProfile profile;
+} UusbconfResponseTest;
+
+static const UusbconfResponseTest uusbconf_response_tests[] = {
+ {
+ .str = "+UUSBCONF: 3,\"RNDIS\",,\"0x1146\"\r\n",
+ .profile = MM_UBLOX_USB_PROFILE_RNDIS
+ },
+ {
+ .str = "+UUSBCONF: 2,\"ECM\",,\"0x1143\"\r\n",
+ .profile = MM_UBLOX_USB_PROFILE_ECM
+ },
+ {
+ .str = "+UUSBCONF: 0,\"\",,\"0x1141\"\r\n",
+ .profile = MM_UBLOX_USB_PROFILE_BACK_COMPATIBLE
+ },
+};
+
+static void
+test_uusbconf_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (uusbconf_response_tests); i++) {
+ MMUbloxUsbProfile profile = MM_UBLOX_USB_PROFILE_UNKNOWN;
+ GError *error = NULL;
+ gboolean success;
+
+ success = mm_ublox_parse_uusbconf_response (uusbconf_response_tests[i].str, &profile, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (uusbconf_response_tests[i].profile, ==, profile);
+ }
+}
+
+/*****************************************************************************/
+/* Test UBMCONF? responses */
+
+typedef struct {
+ const gchar *str;
+ MMUbloxNetworkingMode mode;
+} UbmconfResponseTest;
+
+static const UbmconfResponseTest ubmconf_response_tests[] = {
+ {
+ .str = "+UBMCONF: 1\r\n",
+ .mode = MM_UBLOX_NETWORKING_MODE_ROUTER
+ },
+ {
+ .str = "+UBMCONF: 2\r\n",
+ .mode = MM_UBLOX_NETWORKING_MODE_BRIDGE
+ },
+};
+
+static void
+test_ubmconf_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (ubmconf_response_tests); i++) {
+ MMUbloxNetworkingMode mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN;
+ GError *error = NULL;
+ gboolean success;
+
+ success = mm_ublox_parse_ubmconf_response (ubmconf_response_tests[i].str, &mode, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (ubmconf_response_tests[i].mode, ==, mode);
+ }
+}
+
+/*****************************************************************************/
+/* Test UIPADDR=N responses */
+
+typedef struct {
+ const gchar *str;
+ guint cid;
+ const gchar *if_name;
+ const gchar *ipv4_address;
+ const gchar *ipv4_subnet;
+ const gchar *ipv6_global_address;
+ const gchar *ipv6_link_local_address;
+} UipaddrResponseTest;
+
+static const UipaddrResponseTest uipaddr_response_tests[] = {
+ {
+ .str = "+UIPADDR: 1,\"ccinet0\",\"5.168.120.13\",\"255.255.255.0\",\"\",\"\"",
+ .cid = 1,
+ .if_name = "ccinet0",
+ .ipv4_address = "5.168.120.13",
+ .ipv4_subnet = "255.255.255.0",
+ },
+ {
+ .str = "+UIPADDR: 2,\"ccinet1\",\"\",\"\",\"2001::1:200:FF:FE00:0/64\",\"FE80::200:FF:FE00:0/64\"",
+ .cid = 2,
+ .if_name = "ccinet1",
+ .ipv6_global_address = "2001::1:200:FF:FE00:0/64",
+ .ipv6_link_local_address = "FE80::200:FF:FE00:0/64",
+ },
+ {
+ .str = "+UIPADDR: 3,\"ccinet2\",\"5.10.100.2\",\"255.255.255.0\",\"2001::1:200:FF:FE00:0/64\",\"FE80::200:FF:FE00:0/64\"",
+ .cid = 3,
+ .if_name = "ccinet2",
+ .ipv4_address = "5.10.100.2",
+ .ipv4_subnet = "255.255.255.0",
+ .ipv6_global_address = "2001::1:200:FF:FE00:0/64",
+ .ipv6_link_local_address = "FE80::200:FF:FE00:0/64",
+ },
+};
+
+static void
+test_uipaddr_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (uipaddr_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint cid = G_MAXUINT;
+ gchar *if_name = NULL;
+ gchar *ipv4_address = NULL;
+ gchar *ipv4_subnet = NULL;
+ gchar *ipv6_global_address = NULL;
+ gchar *ipv6_link_local_address = NULL;
+
+ success = mm_ublox_parse_uipaddr_response (uipaddr_response_tests[i].str,
+ &cid,
+ &if_name,
+ &ipv4_address,
+ &ipv4_subnet,
+ &ipv6_global_address,
+ &ipv6_link_local_address,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (uipaddr_response_tests[i].cid, ==, cid);
+ g_assert_cmpstr (uipaddr_response_tests[i].if_name, ==, if_name);
+ g_assert_cmpstr (uipaddr_response_tests[i].ipv4_address, ==, ipv4_address);
+ g_assert_cmpstr (uipaddr_response_tests[i].ipv4_subnet, ==, ipv4_subnet);
+ g_assert_cmpstr (uipaddr_response_tests[i].ipv6_global_address, ==, ipv6_global_address);
+ g_assert_cmpstr (uipaddr_response_tests[i].ipv6_link_local_address, ==, ipv6_link_local_address);
+
+ g_free (if_name);
+ g_free (ipv4_address);
+ g_free (ipv4_subnet);
+ g_free (ipv6_global_address);
+ g_free (ipv6_link_local_address);
+ }
+}
+
+/*****************************************************************************/
+/* Test CFUN? response */
+
+typedef struct {
+ const gchar *str;
+ MMModemPowerState state;
+} CfunQueryTest;
+
+static const CfunQueryTest cfun_query_tests[] = {
+ { "+CFUN: 1", MM_MODEM_POWER_STATE_ON },
+ { "+CFUN: 1,0", MM_MODEM_POWER_STATE_ON },
+ { "+CFUN: 0", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 0,0", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 4", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 4,0", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 19", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 19,0", MM_MODEM_POWER_STATE_LOW },
+};
+
+static void
+test_cfun_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cfun_query_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN;
+
+ success = mm_ublox_parse_cfun_response (cfun_query_tests[i].str, &state, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cfun_query_tests[i].state, ==, state);
+ }
+}
+
+/*****************************************************************************/
+/* Test URAT=? responses and model based filtering */
+
+static void
+compare_combinations (const gchar *response,
+ const gchar *model,
+ const MMModemModeCombination *expected_combinations,
+ guint n_expected_combinations)
+{
+ GArray *combinations;
+ GError *error = NULL;
+ guint i;
+
+ combinations = mm_ublox_parse_urat_test_response (response, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (combinations);
+
+ combinations = mm_ublox_filter_supported_modes (model, combinations, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (combinations);
+
+ g_assert_cmpuint (combinations->len, ==, n_expected_combinations);
+
+ for (i = 0; i < combinations->len; i++) {
+ MMModemModeCombination combination;
+ guint j;
+ gboolean found = FALSE;
+
+ combination = g_array_index (combinations, MMModemModeCombination, i);
+ for (j = 0; !found && j < n_expected_combinations; j++)
+ found = (combination.allowed == expected_combinations[j].allowed &&
+ combination.preferred == expected_combinations[j].preferred);
+ g_assert (found);
+ }
+
+ g_array_unref (combinations);
+}
+
+static void
+test_urat_test_response_2g (void)
+{
+ static const MMModemModeCombination expected_combinations[] = {
+ { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE }
+ };
+
+ compare_combinations ("+URAT: 0", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+ compare_combinations ("+URAT: 0,0", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+ compare_combinations ("+URAT: (0)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+ compare_combinations ("+URAT: (0),(0)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+}
+
+static void
+test_urat_test_response_2g3g (void)
+{
+ static const MMModemModeCombination expected_combinations[] = {
+ { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G },
+ };
+
+ compare_combinations ("+URAT: (0,1,2),(0,2)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+ compare_combinations ("+URAT: (0-2),(0,2)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+}
+
+static void
+test_urat_test_response_2g3g4g (void)
+{
+ static const MMModemModeCombination expected_combinations[] = {
+ { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G },
+
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+ };
+
+ compare_combinations ("+URAT: (0,1,2,3,4,5,6),(0,2,3)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+ compare_combinations ("+URAT: (0-6),(0,2,3)", NULL, expected_combinations, G_N_ELEMENTS (expected_combinations));
+}
+
+static void
+test_mode_filtering_toby_l201 (void)
+{
+ static const MMModemModeCombination expected_combinations[] = {
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+ };
+
+ compare_combinations ("+URAT: (0-6),(0,2,3)", "TOBY-L201", expected_combinations, G_N_ELEMENTS (expected_combinations));
+}
+
+static void
+test_mode_filtering_lisa_u200 (void)
+{
+ static const MMModemModeCombination expected_combinations[] = {
+ { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G },
+ };
+
+ compare_combinations ("+URAT: (0-6),(0,2,3)", "LISA-U200", expected_combinations, G_N_ELEMENTS (expected_combinations));
+}
+
+static void
+test_mode_filtering_sara_u280 (void)
+{
+ static const MMModemModeCombination expected_combinations[] = {
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ };
+
+ compare_combinations ("+URAT: (0-6),(0,2,3)", "SARA-U280", expected_combinations, G_N_ELEMENTS (expected_combinations));
+}
+
+/*****************************************************************************/
+/* URAT? response parser and URAT=X command builder */
+
+typedef struct {
+ const gchar *command;
+ const gchar *response;
+ MMModemMode allowed;
+ MMModemMode preferred;
+} UratTest;
+
+static const UratTest urat_tests[] = {
+ {
+ .command = "+URAT=1,2",
+ .response = "+URAT: 1,2\r\n",
+ .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G),
+ .preferred = MM_MODEM_MODE_3G,
+ },
+ {
+ .command = "+URAT=4,0",
+ .response = "+URAT: 4,0\r\n",
+ .allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G),
+ .preferred = MM_MODEM_MODE_2G,
+ },
+ {
+ .command = "+URAT=0",
+ .response = "+URAT: 0\r\n",
+ .allowed = MM_MODEM_MODE_2G,
+ .preferred = MM_MODEM_MODE_NONE,
+ },
+ {
+ .command = "+URAT=6",
+ .response = "+URAT: 6\r\n",
+ .allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G),
+ .preferred = MM_MODEM_MODE_NONE,
+ },
+};
+
+static void
+test_urat_read_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (urat_tests); i++) {
+ MMModemMode allowed = MM_MODEM_MODE_NONE;
+ MMModemMode preferred = MM_MODEM_MODE_NONE;
+ GError *error = NULL;
+ gboolean success;
+
+ success = mm_ublox_parse_urat_read_response (urat_tests[i].response, NULL,
+ &allowed, &preferred, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (urat_tests[i].allowed, ==, allowed);
+ g_assert_cmpuint (urat_tests[i].preferred, ==, preferred);
+ }
+}
+
+static void
+test_urat_write_command (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (urat_tests); i++) {
+ gchar *command;
+ GError *error = NULL;
+
+ command = mm_ublox_build_urat_set_command (urat_tests[i].allowed, urat_tests[i].preferred, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (command, ==, urat_tests[i].command);
+ g_free (command);
+ }
+}
+
+/*****************************************************************************/
+/* Test +UBANDSEL? response parser */
+
+static void
+common_validate_ubandsel_response (const gchar *str,
+ const MMModemBand *expected_bands,
+ const gchar *model,
+ guint n_expected_bands)
+{
+ GError *error = NULL;
+ GArray *bands;
+
+ bands = mm_ublox_parse_ubandsel_response (str, model, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (bands);
+
+ mm_test_helpers_compare_bands (bands, expected_bands, n_expected_bands);
+ g_array_unref (bands);
+}
+
+static void
+test_ubandsel_response_four (void)
+{
+ const MMModemBand expected_bands[] = {
+ /* 700 */ MM_MODEM_BAND_EUTRAN_4,
+ /* 1700 */ MM_MODEM_BAND_EUTRAN_13
+ };
+
+ common_validate_ubandsel_response ("+UBANDSEL: 700,1700\r\n", expected_bands, "LARA-R204", G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_ubandsel_response_three (void)
+{
+ const MMModemBand expected_bands[] = {
+ /* 800 */ MM_MODEM_BAND_UTRAN_6,
+ /* 850 */ MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_5,
+ /* 900 */ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_UTRAN_8,
+ /* 1900 */ MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_2,
+ /* 2100 */ MM_MODEM_BAND_UTRAN_1
+ };
+
+ common_validate_ubandsel_response ("+UBANDSEL: 800,850,900,1900,2100\r\n", expected_bands, "SARA-U201", G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_ubandsel_response_two (void)
+{
+ const MMModemBand expected_bands[] = {
+ /* 850 */ MM_MODEM_BAND_G850,
+ /* 900 */ MM_MODEM_BAND_EGSM,
+ /* 1800 */ MM_MODEM_BAND_DCS,
+ /* 1900 */ MM_MODEM_BAND_PCS
+ };
+
+ common_validate_ubandsel_response ("+UBANDSEL: 850,900,1800,1900\r\n", expected_bands, "SARA-G310", G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_ubandsel_response_one (void)
+{
+ const MMModemBand expected_bands[] = {
+ /* 850 */ MM_MODEM_BAND_G850, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_EUTRAN_5,
+ /* 1700 */ MM_MODEM_BAND_EUTRAN_4,
+ /* 1900 */ MM_MODEM_BAND_PCS, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_EUTRAN_2
+ };
+
+ common_validate_ubandsel_response ("+UBANDSEL: 850,1700,1900\r\n", expected_bands, "TOBY-R200", G_N_ELEMENTS (expected_bands));
+}
+
+/*****************************************************************************/
+/* +UBANDSEL=x command builder */
+
+static void
+common_validate_ubandsel_request (const MMModemBand *bands,
+ guint n_bands,
+ const gchar *model,
+ const gchar *expected_request)
+{
+ GError *error = NULL;
+ GArray *bands_array;
+ gchar *request;
+
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands);
+ g_array_append_vals (bands_array, bands, n_bands);
+
+ request = mm_ublox_build_ubandsel_set_command (bands_array, model, &error);
+ g_assert_no_error (error);
+ g_assert (request);
+
+ g_assert_cmpstr (request, ==, expected_request);
+
+ g_array_unref (bands_array);
+ g_free (request);
+}
+
+static void
+test_ubandsel_request_any (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_ANY
+ };
+
+ common_validate_ubandsel_request (bands, G_N_ELEMENTS (bands), "TOBY-R200", "+UBANDSEL=0");
+}
+
+static void
+test_ubandsel_request_2g (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS
+ };
+
+ common_validate_ubandsel_request (bands, G_N_ELEMENTS (bands), "SARA-G310", "+UBANDSEL=850,900,1800,1900");
+}
+
+static void
+test_ubandsel_request_1800 (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_DCS, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_EUTRAN_3
+ };
+
+ common_validate_ubandsel_request (bands, G_N_ELEMENTS (bands), "TOBY-R200", "+UBANDSEL=1800");
+}
+
+/*****************************************************************************/
+/* Test +UACT? response parser */
+
+static void
+common_validate_uact_response (const gchar *str,
+ const MMModemBand *expected_bands,
+ guint n_expected_bands)
+{
+ GError *error = NULL;
+ GArray *bands;
+
+ bands = mm_ublox_parse_uact_response (str, &error);
+
+ if (n_expected_bands > 0) {
+ g_assert (bands);
+ g_assert_no_error (error);
+ mm_test_helpers_compare_bands (bands, expected_bands, n_expected_bands);
+ g_array_unref (bands);
+ } else {
+ g_assert (!bands);
+ g_assert (error);
+ g_error_free (error);
+ }
+}
+
+static void
+test_uact_response_empty_list (void)
+{
+ common_validate_uact_response ("", NULL, 0);
+ common_validate_uact_response ("+UACT: ,,,\r\n", NULL, 0);
+}
+
+static void
+test_uact_response_2g (void)
+{
+ const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS,
+ };
+
+ common_validate_uact_response ("+UACT: ,,,900,1800,1900,850\r\n",
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_uact_response_2g3g (void)
+{
+ const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_7, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_9,
+ };
+
+ common_validate_uact_response ("+UACT: ,,,900,1800,1900,850,1,2,3,4,5,6,7,8,9\r\n",
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_uact_response_2g3g4g (void)
+{
+ const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_G850, MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS,
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_3, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5,
+ MM_MODEM_BAND_UTRAN_6, MM_MODEM_BAND_UTRAN_7, MM_MODEM_BAND_UTRAN_8, MM_MODEM_BAND_UTRAN_9,
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_6, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_9,
+ };
+
+ common_validate_uact_response ("+UACT: ,,,900,1800,1900,850,1,2,3,4,5,6,7,8,9,101,102,103,104,105,106,107,108,109\r\n",
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+/*****************************************************************************/
+/* Test +UACT=? test parser */
+
+static void
+common_validate_uact_test (const gchar *str,
+ const MMModemBand *expected_bands_2g,
+ guint n_expected_bands_2g,
+ const MMModemBand *expected_bands_3g,
+ guint n_expected_bands_3g,
+ const MMModemBand *expected_bands_4g,
+ guint n_expected_bands_4g)
+{
+ GError *error = NULL;
+ gboolean result;
+ GArray *bands_2g = NULL;
+ GArray *bands_3g = NULL;
+ GArray *bands_4g = NULL;
+
+ result = mm_ublox_parse_uact_test (str, NULL, &bands_2g, &bands_3g, &bands_4g, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ mm_test_helpers_compare_bands (bands_2g, expected_bands_2g, n_expected_bands_2g);
+ if (bands_2g)
+ g_array_unref (bands_2g);
+ mm_test_helpers_compare_bands (bands_3g, expected_bands_3g, n_expected_bands_3g);
+ if (bands_3g)
+ g_array_unref (bands_3g);
+ mm_test_helpers_compare_bands (bands_4g, expected_bands_4g, n_expected_bands_4g);
+ if (bands_4g)
+ g_array_unref (bands_4g);
+}
+
+static void
+test_uact_test_2g (void)
+{
+ const MMModemBand expected_bands_2g[] = {
+ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS
+ };
+
+ common_validate_uact_test ("+UACT: ,,,(900,1800)\r\n",
+ expected_bands_2g, G_N_ELEMENTS (expected_bands_2g),
+ NULL, 0,
+ NULL, 0);
+}
+
+static void
+test_uact_test_2g3g (void)
+{
+ const MMModemBand expected_bands_2g[] = {
+ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS
+ };
+ const MMModemBand expected_bands_3g[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8
+ };
+
+ common_validate_uact_test ("+UACT: ,,,(900,1800),(1,8)\r\n",
+ expected_bands_2g, G_N_ELEMENTS (expected_bands_2g),
+ expected_bands_3g, G_N_ELEMENTS (expected_bands_3g),
+ NULL, 0);
+}
+
+static void
+test_uact_test_2g3g4g (void)
+{
+ const MMModemBand expected_bands_2g[] = {
+ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS
+ };
+ const MMModemBand expected_bands_3g[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8
+ };
+ const MMModemBand expected_bands_4g[] = {
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20
+ };
+
+ common_validate_uact_test ("+UACT: ,,,(900,1800),(1,8),(101,103,107,108,120)\r\n",
+ expected_bands_2g, G_N_ELEMENTS (expected_bands_2g),
+ expected_bands_3g, G_N_ELEMENTS (expected_bands_3g),
+ expected_bands_4g, G_N_ELEMENTS (expected_bands_4g));
+}
+
+static void
+test_uact_test_2g3g4g_2 (void)
+{
+ const MMModemBand expected_bands_2g[] = {
+ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS
+ };
+ const MMModemBand expected_bands_3g[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8
+ };
+ const MMModemBand expected_bands_4g[] = {
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20
+ };
+
+ common_validate_uact_test ("+UACT: ,,,(900,1800),(1,8),(101,103,107,108,120),(138)\r\n",
+ expected_bands_2g, G_N_ELEMENTS (expected_bands_2g),
+ expected_bands_3g, G_N_ELEMENTS (expected_bands_3g),
+ expected_bands_4g, G_N_ELEMENTS (expected_bands_4g));
+}
+
+/*****************************************************************************/
+/* +UACT=x command builder */
+
+static void
+common_validate_uact_request (const MMModemBand *bands,
+ guint n_bands,
+ const gchar *expected_request)
+{
+ GError *error = NULL;
+ GArray *bands_array;
+ gchar *request;
+
+ bands_array = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands);
+ g_array_append_vals (bands_array, bands, n_bands);
+
+ request = mm_ublox_build_uact_set_command (bands_array, &error);
+ g_assert_no_error (error);
+ g_assert (request);
+
+ g_assert_cmpstr (request, ==, expected_request);
+
+ g_array_unref (bands_array);
+ g_free (request);
+}
+
+static void
+test_uact_request_any (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_ANY
+ };
+
+ common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,0");
+}
+
+static void
+test_uact_request_2g (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS,
+ };
+
+ common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,900,1800");
+}
+
+static void
+test_uact_request_3g (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_8,
+ };
+
+ common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,1,8");
+}
+
+static void
+test_uact_request_4g (void)
+{
+ const MMModemBand bands[] = {
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_20
+ };
+
+ common_validate_uact_request (bands, G_N_ELEMENTS (bands), "+UACT=,,,101,103,107,108,120");
+}
+
+/*****************************************************************************/
+/* Test +UAUTHREQ=? responses */
+
+static void
+common_validate_uauthreq_test (const gchar *str,
+ MMUbloxBearerAllowedAuth expected_allowed_auths)
+{
+ GError *error = NULL;
+ MMUbloxBearerAllowedAuth allowed_auths;
+
+ allowed_auths = mm_ublox_parse_uauthreq_test (str, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (allowed_auths, ==, expected_allowed_auths);
+}
+
+static void
+test_uauthreq_tobyl4 (void)
+{
+ common_validate_uauthreq_test ("+UAUTHREQ: (1-4),(0-2),,",
+ (MM_UBLOX_BEARER_ALLOWED_AUTH_NONE |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_PAP |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP));
+}
+
+static void
+test_uauthreq_with_auto (void)
+{
+ common_validate_uauthreq_test ("+UAUTHREQ: (1-4),(0-3),,",
+ (MM_UBLOX_BEARER_ALLOWED_AUTH_NONE |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_PAP |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_AUTO));
+}
+
+static void
+test_uauthreq_less_fields (void)
+{
+ common_validate_uauthreq_test ("+UAUTHREQ: (1-4),(0-2)",
+ (MM_UBLOX_BEARER_ALLOWED_AUTH_NONE |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_PAP |
+ MM_UBLOX_BEARER_ALLOWED_AUTH_CHAP));
+}
+
+/*****************************************************************************/
+/* Test +UGCNTRD responses */
+
+typedef struct {
+ const gchar *str;
+ guint cid;
+ guint64 session_tx_bytes;
+ guint64 session_rx_bytes;
+ guint64 total_tx_bytes;
+ guint64 total_rx_bytes;
+} UgcntrdResponseTest;
+
+static const UgcntrdResponseTest ugcntrd_response_tests[] = {
+ {
+ .str = "+UGCNTRD: 1, 100, 0, 100, 0",
+ .cid = 1,
+ .session_tx_bytes = 100,
+ .session_rx_bytes = 0,
+ .total_tx_bytes = 100,
+ .total_rx_bytes = 0
+ },
+ {
+ .str = "+UGCNTRD: 31,2704,1819,2724,1839",
+ .cid = 31,
+ .session_tx_bytes = 2704,
+ .session_rx_bytes = 1819,
+ .total_tx_bytes = 2724,
+ .total_rx_bytes = 1839
+ },
+ {
+ .str = "+UGCNTRD: 1, 100, 0, 100, 0\r\n"
+ "+UGCNTRD: 31,2704,1819,2724,1839\r\n",
+ .cid = 1,
+ .session_tx_bytes = 100,
+ .session_rx_bytes = 0,
+ .total_tx_bytes = 100,
+ .total_rx_bytes = 0
+ },
+ {
+ .str = "+UGCNTRD: 1, 100, 0, 100, 0\r\n"
+ "+UGCNTRD: 31,2704,1819,2724,1839\r\n",
+ .cid = 31,
+ .session_tx_bytes = 2704,
+ .session_rx_bytes = 1819,
+ .total_tx_bytes = 2724,
+ .total_rx_bytes = 1839
+ },
+ {
+ .str = "+UGCNTRD: 2,1397316870,113728263578,1397316870,113728263578\r\n",
+ .cid = 2,
+ .session_tx_bytes = 1397316870ULL,
+ .session_rx_bytes = 113728263578ULL,
+ .total_tx_bytes = 1397316870ULL,
+ .total_rx_bytes = 113728263578ULL
+ }
+};
+
+static void
+test_ugcntrd_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (ugcntrd_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint64 session_tx_bytes = 0;
+ guint64 session_rx_bytes = 0;
+ guint64 total_tx_bytes = 0;
+ guint64 total_rx_bytes = 0;
+
+ success = mm_ublox_parse_ugcntrd_response_for_cid (ugcntrd_response_tests[i].str,
+ ugcntrd_response_tests[i].cid,
+ &session_tx_bytes,
+ &session_rx_bytes,
+ &total_tx_bytes,
+ &total_rx_bytes,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (ugcntrd_response_tests[i].session_tx_bytes, ==, session_tx_bytes);
+ g_assert_cmpuint (ugcntrd_response_tests[i].session_rx_bytes, ==, session_rx_bytes);
+ g_assert_cmpuint (ugcntrd_response_tests[i].total_tx_bytes, ==, total_tx_bytes);
+ g_assert_cmpuint (ugcntrd_response_tests[i].total_rx_bytes, ==, total_rx_bytes);
+ }
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/ublox/upincnt/response", test_upincnt_response);
+ g_test_add_func ("/MM/ublox/uusbconf/response", test_uusbconf_response);
+ g_test_add_func ("/MM/ublox/ubmconf/response", test_ubmconf_response);
+ g_test_add_func ("/MM/ublox/uipaddr/response", test_uipaddr_response);
+ g_test_add_func ("/MM/ublox/cfun/response", test_cfun_response);
+ g_test_add_func ("/MM/ublox/urat/test/response/2g", test_urat_test_response_2g);
+ g_test_add_func ("/MM/ublox/urat/test/response/2g3g", test_urat_test_response_2g3g);
+ g_test_add_func ("/MM/ublox/urat/test/response/2g3g4g", test_urat_test_response_2g3g4g);
+ g_test_add_func ("/MM/ublox/urat/test/response/toby-l201", test_mode_filtering_toby_l201);
+ g_test_add_func ("/MM/ublox/urat/test/response/lisa-u200", test_mode_filtering_lisa_u200);
+ g_test_add_func ("/MM/ublox/urat/test/response/sara-u280", test_mode_filtering_sara_u280);
+ g_test_add_func ("/MM/ublox/urat/read/response", test_urat_read_response);
+ g_test_add_func ("/MM/ublox/urat/write/command", test_urat_write_command);
+ g_test_add_func ("/MM/ublox/ubandsel/response/one", test_ubandsel_response_one);
+ g_test_add_func ("/MM/ublox/ubandsel/response/two", test_ubandsel_response_two);
+ g_test_add_func ("/MM/ublox/ubandsel/response/three", test_ubandsel_response_three);
+ g_test_add_func ("/MM/ublox/ubandsel/response/four", test_ubandsel_response_four);
+ g_test_add_func ("/MM/ublox/ubandsel/request/any", test_ubandsel_request_any);
+ g_test_add_func ("/MM/ublox/ubandsel/request/2g", test_ubandsel_request_2g);
+ g_test_add_func ("/MM/ublox/ubandsel/request/1800", test_ubandsel_request_1800);
+ g_test_add_func ("/MM/ublox/uact/response/empty-list", test_uact_response_empty_list);
+ g_test_add_func ("/MM/ublox/uact/response/2g", test_uact_response_2g);
+ g_test_add_func ("/MM/ublox/uact/response/2g3g", test_uact_response_2g3g);
+ g_test_add_func ("/MM/ublox/uact/response/2g3g4g", test_uact_response_2g3g4g);
+ g_test_add_func ("/MM/ublox/uact/test/2g", test_uact_test_2g);
+ g_test_add_func ("/MM/ublox/uact/test/2g3g", test_uact_test_2g3g);
+ g_test_add_func ("/MM/ublox/uact/test/2g3g4g", test_uact_test_2g3g4g);
+ g_test_add_func ("/MM/ublox/uact/test/2g3g4g/2", test_uact_test_2g3g4g_2);
+ g_test_add_func ("/MM/ublox/uact/request/any", test_uact_request_any);
+ g_test_add_func ("/MM/ublox/uact/request/2g", test_uact_request_2g);
+ g_test_add_func ("/MM/ublox/uact/request/3g", test_uact_request_3g);
+ g_test_add_func ("/MM/ublox/uact/request/4g", test_uact_request_4g);
+ g_test_add_func ("/MM/ublox/uauthreq/test/tobyl4", test_uauthreq_tobyl4);
+ g_test_add_func ("/MM/ublox/uauthreq/test/with-auto", test_uauthreq_with_auto);
+ g_test_add_func ("/MM/ublox/uauthreq/test/less-fields", test_uauthreq_less_fields);
+ g_test_add_func ("/MM/ublox/ugcntrd/response", test_ugcntrd_response);
+
+ return g_test_run ();
+}
diff --git a/plugins/via/mm-broadband-modem-via.c b/plugins/via/mm-broadband-modem-via.c
index 881b9b00..6a8b4508 100644
--- a/plugins/via/mm-broadband-modem-via.c
+++ b/plugins/via/mm-broadband-modem-via.c
@@ -24,7 +24,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-errors-types.h"
#include "mm-base-modem-at.h"
@@ -67,80 +67,75 @@ typedef struct {
} SetupRegistrationChecksResults;
static gboolean
-setup_registration_checks_finish (MMIfaceModemCdma *self,
- GAsyncResult *res,
- gboolean *skip_qcdm_call_manager_step,
- gboolean *skip_qcdm_hdr_step,
- gboolean *skip_at_cdma_service_status_step,
- gboolean *skip_at_cdma1x_serving_system_step,
- gboolean *skip_detailed_registration_state,
- GError **error)
+setup_registration_checks_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ gboolean *skip_qcdm_call_manager_step,
+ gboolean *skip_qcdm_hdr_step,
+ gboolean *skip_at_cdma_service_status_step,
+ gboolean *skip_at_cdma1x_serving_system_step,
+ gboolean *skip_detailed_registration_state,
+ GError **error)
{
SetupRegistrationChecksResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step;
- *skip_qcdm_hdr_step = results->skip_qcdm_hdr_step;
- *skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step;
+ *skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step;
+ *skip_qcdm_hdr_step = results->skip_qcdm_hdr_step;
+ *skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step;
*skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step;
- *skip_detailed_registration_state = results->skip_detailed_registration_state;
+ *skip_detailed_registration_state = results->skip_detailed_registration_state;
+
+ g_free (results);
+
return TRUE;
}
static void
parent_setup_registration_checks_ready (MMIfaceModemCdma *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
- SetupRegistrationChecksResults results = { 0 };
+ GError *error = NULL;
+ SetupRegistrationChecksResults *results;
+
+ results = g_new0 (SetupRegistrationChecksResults, 1);
if (!iface_modem_cdma_parent->setup_registration_checks_finish (self,
res,
- &results.skip_qcdm_call_manager_step,
- &results.skip_qcdm_hdr_step,
- &results.skip_at_cdma_service_status_step,
- &results.skip_at_cdma1x_serving_system_step,
- &results.skip_detailed_registration_state,
+ &results->skip_qcdm_call_manager_step,
+ &results->skip_qcdm_hdr_step,
+ &results->skip_at_cdma_service_status_step,
+ &results->skip_at_cdma1x_serving_system_step,
+ &results->skip_detailed_registration_state,
&error)) {
- g_simple_async_result_take_error (simple, error);
+ g_free (results);
+ g_task_return_error (task, error);
} else {
/* Skip +CSS */
- results.skip_at_cdma1x_serving_system_step = TRUE;
+ results->skip_at_cdma1x_serving_system_step = TRUE;
/* Skip +CAD */
- results.skip_at_cdma_service_status_step = TRUE;
-
+ results->skip_at_cdma_service_status_step = TRUE;
/* Force to always use the detailed registration checks, as we have
* ^SYSINFO for that */
- results.skip_detailed_registration_state = FALSE;
-
- g_simple_async_result_set_op_res_gpointer (simple, &results, NULL);
+ results->skip_detailed_registration_state = FALSE;
+ g_task_return_pointer (task, results, g_free);
}
-
- /* All done. NOTE: complete NOT in idle! */
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-setup_registration_checks (MMIfaceModemCdma *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+setup_registration_checks (MMIfaceModemCdma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- setup_registration_checks);
-
/* Run parent's checks first */
- iface_modem_cdma_parent->setup_registration_checks (self,
- (GAsyncReadyCallback)parent_setup_registration_checks_ready,
- result);
+ iface_modem_cdma_parent->setup_registration_checks (
+ self,
+ (GAsyncReadyCallback)parent_setup_registration_checks_ready,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -151,61 +146,48 @@ typedef struct {
MMModemCdmaRegistrationState detailed_evdo_state;
} DetailedRegistrationStateResults;
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- DetailedRegistrationStateResults state;
-} DetailedRegistrationStateContext;
-
-static void
-detailed_registration_state_context_complete_and_free (DetailedRegistrationStateContext *ctx)
-{
- /* Always not in idle! we're passing a struct in stack as result */
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
-get_detailed_registration_state_finish (MMIfaceModemCdma *self,
- GAsyncResult *res,
- MMModemCdmaRegistrationState *detailed_cdma1x_state,
- MMModemCdmaRegistrationState *detailed_evdo_state,
- GError **error)
+get_detailed_registration_state_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ MMModemCdmaRegistrationState *detailed_cdma1x_state,
+ MMModemCdmaRegistrationState *detailed_evdo_state,
+ GError **error)
{
DetailedRegistrationStateResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*detailed_cdma1x_state = results->detailed_cdma1x_state;
- *detailed_evdo_state = results->detailed_evdo_state;
+ *detailed_evdo_state = results->detailed_evdo_state;
+ g_free (results);
return TRUE;
}
static void
-sysinfo_ready (MMIfaceModemCdma *self,
+sysinfo_ready (MMBaseModem *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
+
{
- GError *error = NULL;
- const gchar *response;
- GRegex *r;
- GMatchInfo *match_info;
+ DetailedRegistrationStateResults *ctx;
+ DetailedRegistrationStateResults *results;
+ const gchar *response;
+ GRegex *r;
+ GMatchInfo *match_info;
+ MMModemCdmaRegistrationState reg_state;
+ guint val = 0;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ ctx = g_task_get_task_data (task);
- /* If error, leave superclass' reg state alone if AT^SYSINFO isn't supported. */
- if (error) {
- g_error_free (error);
+ /* Set input detailed states as fallback */
+ results = g_memdup (ctx, sizeof (*ctx));
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
- return;
- }
+ /* If error, leave superclass' reg state alone if AT^SYSINFO isn't supported. */
+ response = mm_base_modem_at_command_finish (self, res, NULL);
+ if (!response)
+ goto out;
response = mm_strip_tag (response, "^SYSINFO:");
@@ -215,82 +197,77 @@ sysinfo_ready (MMIfaceModemCdma *self,
g_assert (r != NULL);
/* Try to parse the results */
-
g_regex_match (r, response, 0, &match_info);
-
if (g_match_info_get_match_count (match_info) < 6) {
- mm_warn ("Via: failed to parse ^SYSINFO response: '%s'", response);
- } else {
- MMModemCdmaRegistrationState reg_state;
- guint val = 0;
-
- /* At this point the generic code already knows we've been registered */
- reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
-
- if (mm_get_uint_from_match_info (match_info, 1, &val)) {
- if (val == 2) {
- /* Service available, check roaming state */
- val = 0;
- if (mm_get_uint_from_match_info (match_info, 3, &val)) {
- if (val == 0)
- reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
- else if (val == 1)
- reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
- }
+ mm_obj_warn (self, "failed to parse ^SYSINFO response: '%s'", response);
+ goto out;
+ }
+
+ /* At this point the generic code already knows we've been registered */
+ reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_REGISTERED;
+
+ if (mm_get_uint_from_match_info (match_info, 1, &val)) {
+ if (val == 2) {
+ /* Service available, check roaming state */
+ val = 0;
+ if (mm_get_uint_from_match_info (match_info, 3, &val)) {
+ if (val == 0)
+ reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
+ else if (val == 1)
+ reg_state = MM_MODEM_CDMA_REGISTRATION_STATE_ROAMING;
}
}
+ }
- /* Check service type */
- val = 0;
- if (mm_get_uint_from_match_info (match_info, 4, &val)) {
- if (val == 2) /* CDMA */
- ctx->state.detailed_cdma1x_state = reg_state;
- else if (val == 4) /* HDR */
- ctx->state.detailed_evdo_state = reg_state;
- else if (val == 8) { /* Hybrid */
- ctx->state.detailed_cdma1x_state = reg_state;
- ctx->state.detailed_evdo_state = reg_state;
- }
- } else {
- /* Say we're registered to something even though sysmode parsing failed */
- mm_dbg ("SYSMODE parsing failed: assuming registered at least in CDMA1x");
- ctx->state.detailed_cdma1x_state = reg_state;
+ /* Check service type */
+ val = 0;
+ if (mm_get_uint_from_match_info (match_info, 4, &val)) {
+ if (val == 2) /* CDMA */
+ results->detailed_cdma1x_state = reg_state;
+ else if (val == 4) /* HDR */
+ results->detailed_evdo_state = reg_state;
+ else if (val == 8) { /* Hybrid */
+ results->detailed_cdma1x_state = reg_state;
+ results->detailed_evdo_state = reg_state;
}
+ } else {
+ /* Say we're registered to something even though sysmode parsing failed */
+ mm_obj_dbg (self, "SYSMODE parsing failed: assuming registered at least in CDMA1x");
+ results->detailed_cdma1x_state = reg_state;
}
g_match_info_free (match_info);
g_regex_unref (r);
- /* NOTE: always complete NOT in idle here */
- g_simple_async_result_set_op_res_gpointer (ctx->result, &ctx->state, NULL);
- detailed_registration_state_context_complete_and_free (ctx);
+out:
+ g_task_return_pointer (task, results, NULL);
+ g_object_unref (task);
}
static void
-get_detailed_registration_state (MMIfaceModemCdma *self,
- MMModemCdmaRegistrationState cdma1x_state,
- MMModemCdmaRegistrationState evdo_state,
- GAsyncReadyCallback callback,
- gpointer user_data)
+get_detailed_registration_state (MMIfaceModemCdma *self,
+ MMModemCdmaRegistrationState cdma1x_state,
+ MMModemCdmaRegistrationState evdo_state,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- DetailedRegistrationStateContext *ctx;
+ GTask *task;
+ DetailedRegistrationStateResults *ctx;
/* Setup context */
- ctx = g_new0 (DetailedRegistrationStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- get_detailed_registration_state);
- ctx->state.detailed_cdma1x_state = cdma1x_state;
- ctx->state.detailed_evdo_state = evdo_state;
+ ctx = g_new0 (DetailedRegistrationStateResults, 1);
+ ctx->detailed_cdma1x_state = cdma1x_state;
+ ctx->detailed_evdo_state = evdo_state;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"^SYSINFO",
3,
FALSE,
(GAsyncReadyCallback)sysinfo_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -304,8 +281,8 @@ handle_evdo_quality_change (MMPortSerialAt *port,
guint quality = 0;
if (mm_get_uint_from_match_info (match_info, 1, &quality)) {
- quality = CLAMP (quality, 0, 100);
- mm_dbg ("EVDO signal quality: %u", quality);
+ quality = MM_CLAMP_HIGH (quality, 100);
+ mm_obj_dbg (self, "EVDO signal quality: %u", quality);
mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
}
}
@@ -321,7 +298,7 @@ set_unsolicited_events_handlers (MMBroadbandModemVia *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -340,26 +317,24 @@ modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_cdma_setup_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_cdma_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_VIA (self), TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -367,33 +342,26 @@ modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_setup_unsolicited_events);
-
/* Chain up parent's setup */
iface_modem_cdma_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cdma_setup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
static void
parent_cdma_cleanup_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_cdma_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -401,13 +369,6 @@ modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_cleanup_unsolicited_events);
-
/* Our own cleanup first */
set_unsolicited_events_handlers (MM_BROADBAND_MODEM_VIA (self), FALSE);
@@ -415,7 +376,7 @@ modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self,
iface_modem_cdma_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cdma_cleanup_unsolicited_events_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -431,7 +392,7 @@ set_ignored_unsolicited_events_handlers (MMBroadbandModemVia *self)
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
@@ -502,6 +463,9 @@ mm_broadband_modem_via_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/via/mm-plugin-via.c b/plugins/via/mm-plugin-via.c
index 3a06b5f8..f12c6508 100644
--- a/plugins/via/mm-plugin-via.c
+++ b/plugins/via/mm-plugin-via.c
@@ -27,23 +27,22 @@
#include "mm-broadband-modem-via.h"
#include "mm-plugin-via.h"
-#include "mm-log.h"
G_DEFINE_TYPE (MMPluginVia, mm_plugin_via, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_via_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_via_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -56,13 +55,13 @@ G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
static const gchar *subsystems[] = { "tty", NULL };
- static const mm_str_pair product_strings[] = { { "via", "cbp7" },
- { "fusion", "2770p" },
- { NULL, NULL } };
+ static const mm_str_pair product_strings[] = { { (gchar *) "via", (gchar *) "cbp7" },
+ { (gchar *) "fusion", (gchar *) "2770p" },
+ { NULL, NULL } };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_VIA,
- MM_PLUGIN_NAME, "Via CBP7",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_PRODUCT_STRINGS, product_strings,
MM_PLUGIN_ALLOWED_AT, TRUE,
diff --git a/plugins/wavecom/mm-broadband-modem-wavecom.c b/plugins/wavecom/mm-broadband-modem-wavecom.c
index fd335570..8659e1c4 100644
--- a/plugins/wavecom/mm-broadband-modem-wavecom.c
+++ b/plugins/wavecom/mm-broadband-modem-wavecom.c
@@ -27,7 +27,7 @@
#include <libmm-glib.h>
#include "ModemManager.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-serial-parsers.h"
#include "mm-modem-helpers.h"
#include "mm-iface-modem.h"
@@ -74,50 +74,45 @@ typedef struct {
MMModemBand mm_band;
} WavecomBand3G;
static const WavecomBand3G bands_3g[] = {
- { (1 << 0), MM_MODEM_BAND_U2100 },
- { (1 << 1), MM_MODEM_BAND_U1900 },
- { (1 << 2), MM_MODEM_BAND_U1800 },
- { (1 << 3), MM_MODEM_BAND_U17IV },
- { (1 << 4), MM_MODEM_BAND_U850 },
- { (1 << 5), MM_MODEM_BAND_U800 },
- { (1 << 6), MM_MODEM_BAND_U2600 },
- { (1 << 7), MM_MODEM_BAND_U900 },
- { (1 << 8), MM_MODEM_BAND_U17IX }
+ { (1 << 0), MM_MODEM_BAND_UTRAN_1 },
+ { (1 << 1), MM_MODEM_BAND_UTRAN_2 },
+ { (1 << 2), MM_MODEM_BAND_UTRAN_3 },
+ { (1 << 3), MM_MODEM_BAND_UTRAN_4 },
+ { (1 << 4), MM_MODEM_BAND_UTRAN_5 },
+ { (1 << 5), MM_MODEM_BAND_UTRAN_6 },
+ { (1 << 6), MM_MODEM_BAND_UTRAN_7 },
+ { (1 << 7), MM_MODEM_BAND_UTRAN_8 },
+ { (1 << 8), MM_MODEM_BAND_UTRAN_9 }
};
/*****************************************************************************/
/* Load supported modes (Modem interface) */
static GArray *
-load_supported_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-supported_ms_classes_query_ready (MMBaseModem *self,
+supported_ms_classes_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- GArray *all;
- GArray *combinations;
- GArray *filtered;
- const gchar *response;
- GError *error = NULL;
- MMModemModeCombination mode;
- MMModemMode mode_all;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+ const gchar *response;
+ GError *error = NULL;
+ MMModemModeCombination mode;
+ MMModemMode mode_all;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -134,13 +129,12 @@ supported_ms_classes_query_ready (MMBaseModem *self,
/* If none received, error */
if (mode_all == MM_MODEM_MODE_NONE) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get supported mobile station classes: '%s'",
- response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get supported mobile station classes: '%s'",
+ response);
+ g_object_unref (task);
return;
}
@@ -182,30 +176,30 @@ supported_ms_classes_query_ready (MMBaseModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
-load_supported_modes (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
mm_base_modem_at_command (
MM_BASE_MODEM (self),
"+CGCLASS=?",
3,
FALSE,
(GAsyncReadyCallback)supported_ms_classes_query_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ task);
}
/*****************************************************************************/
@@ -217,45 +211,44 @@ typedef struct {
} LoadCurrentModesResult;
static gboolean
-load_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemMode *allowed,
- MMModemMode *preferred,
- GError **error)
+load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
{
- LoadCurrentModesResult *result;
+ g_autofree LoadCurrentModesResult *result = NULL;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
-
- *allowed = result->allowed;
+ *allowed = result->allowed;
*preferred = result->preferred;
return TRUE;
}
static void
-wwsm_read_ready (MMBaseModem *self,
+wwsm_read_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- GRegex *r;
- GMatchInfo *match_info = NULL;
- LoadCurrentModesResult result;
- const gchar *response;
- GError *error = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autofree LoadCurrentModesResult *result = NULL;
+ const gchar *response;
+ GError *error = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- result.allowed = MM_MODEM_MODE_NONE;
- result.preferred = MM_MODEM_MODE_NONE;
+ result = g_new0 (LoadCurrentModesResult, 1);
+ result->allowed = MM_MODEM_MODE_NONE;
+ result->preferred = MM_MODEM_MODE_NONE;
/* Possible responses:
* +WWSM: 0 (2G only)
@@ -267,35 +260,35 @@ wwsm_read_ready (MMBaseModem *self,
r = g_regex_new ("\\r\\n\\+WWSM: ([0-2])(,([0-2]))?.*$", 0, 0, NULL);
g_assert (r != NULL);
- if (g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
+ if (g_regex_match (r, response, 0, &match_info)) {
guint allowed = 0;
if (mm_get_uint_from_match_info (match_info, 1, &allowed)) {
switch (allowed) {
case 0:
- result.allowed = MM_MODEM_MODE_2G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_2G;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 1:
- result.allowed = MM_MODEM_MODE_3G;
- result.preferred = MM_MODEM_MODE_NONE;
+ result->allowed = MM_MODEM_MODE_3G;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 2: {
guint preferred = 0;
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
/* 3, to avoid the comma */
if (mm_get_uint_from_match_info (match_info, 3, &preferred)) {
switch (preferred) {
case 0:
- result.preferred = MM_MODEM_MODE_NONE;
+ result->preferred = MM_MODEM_MODE_NONE;
break;
case 1:
- result.preferred = MM_MODEM_MODE_2G;
+ result->preferred = MM_MODEM_MODE_2G;
break;
case 2:
- result.preferred = MM_MODEM_MODE_3G;
+ result->preferred = MM_MODEM_MODE_3G;
break;
default:
g_warn_if_reached ();
@@ -311,36 +304,30 @@ wwsm_read_ready (MMBaseModem *self,
}
}
- if (result.allowed == MM_MODEM_MODE_NONE)
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unknown wireless data service reply: '%s'",
- response);
+ if (result->allowed == MM_MODEM_MODE_NONE)
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unknown wireless data service reply: '%s'",
+ response);
else
- g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-
- g_regex_unref (r);
- if (match_info)
- g_match_info_free (match_info);
+ g_task_return_pointer (task, g_steal_pointer (&result), g_free);
+ g_object_unref (task);
}
static void
-current_ms_class_ready (MMBaseModem *self,
+current_ms_class_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- LoadCurrentModesResult result;
- const gchar *response;
- GError *error = NULL;
+ g_autofree LoadCurrentModesResult *result = NULL;
+ const gchar *response;
+ GError *error = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -349,136 +336,128 @@ current_ms_class_ready (MMBaseModem *self,
if (strncmp (response,
WAVECOM_MS_CLASS_A_IDSTR,
strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) {
- mm_dbg ("Modem configured as a Class A mobile station");
+ mm_obj_dbg (self, "configured as a Class A mobile station");
/* For 3G devices, query WWSM status */
- mm_base_modem_at_command (MM_BASE_MODEM (self),
+ mm_base_modem_at_command (self,
"+WWSM?",
3,
FALSE,
(GAsyncReadyCallback)wwsm_read_ready,
- simple);
+ task);
return;
}
- result.allowed = MM_MODEM_MODE_NONE;
- result.preferred = MM_MODEM_MODE_NONE;
+ result = g_new0 (LoadCurrentModesResult, 1);
+ result->allowed = MM_MODEM_MODE_NONE;
+ result->preferred = MM_MODEM_MODE_NONE;
if (strncmp (response,
WAVECOM_MS_CLASS_B_IDSTR,
strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) {
- mm_dbg ("Modem configured as a Class B mobile station");
- result.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS);
- result.preferred = MM_MODEM_MODE_2G;
+ mm_obj_dbg (self, "configured as a Class B mobile station");
+ result->allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_CS);
+ result->preferred = MM_MODEM_MODE_2G;
} else if (strncmp (response,
WAVECOM_MS_CLASS_CG_IDSTR,
strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) {
- mm_dbg ("Modem configured as a Class CG mobile station");
- result.allowed = MM_MODEM_MODE_2G;
- result.preferred = MM_MODEM_MODE_NONE;
+ mm_obj_dbg (self, "configured as a Class CG mobile station");
+ result->allowed = MM_MODEM_MODE_2G;
+ result->preferred = MM_MODEM_MODE_NONE;
} else if (strncmp (response,
WAVECOM_MS_CLASS_CC_IDSTR,
strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) {
- mm_dbg ("Modem configured as a Class CC mobile station");
- result.allowed = MM_MODEM_MODE_CS;
- result.preferred = MM_MODEM_MODE_NONE;
+ mm_obj_dbg (self, "configured as a Class CC mobile station");
+ result->allowed = MM_MODEM_MODE_CS;
+ result->preferred = MM_MODEM_MODE_NONE;
}
- if (result.allowed == MM_MODEM_MODE_NONE)
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unknown mobile station class: '%s'",
- response);
+ if (result->allowed == MM_MODEM_MODE_NONE)
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unknown mobile station class: '%s'",
+ response);
else
- g_simple_async_result_set_op_res_gpointer (simple, &result, NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, g_steal_pointer (&result), g_free);
+ g_object_unref (task);
}
static void
-load_current_modes (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CGCLASS?",
3,
FALSE,
(GAsyncReadyCallback)current_ms_class_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Set allowed modes (Modem interface) */
typedef struct {
- MMBroadbandModemWavecom *self;
- GSimpleAsyncResult *result;
gchar *cgclass_command;
gchar *wwsm_command;
} SetCurrentModesContext;
static void
-set_current_modes_context_complete_and_free (SetCurrentModesContext *ctx)
+set_current_modes_context_free (SetCurrentModesContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx->cgclass_command);
g_free (ctx->wwsm_command);
g_free (ctx);
}
static gboolean
-set_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-wwsm_update_ready (MMBaseModem *self,
+wwsm_update_ready (MMBaseModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
GError *error = NULL;
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ mm_base_modem_at_command_finish (self, res, &error);
if (error)
- /* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
-
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-cgclass_update_ready (MMBaseModem *self,
+cgclass_update_ready (MMBaseModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ SetCurrentModesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!ctx->wwsm_command) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -487,24 +466,23 @@ cgclass_update_ready (MMBaseModem *self,
3,
FALSE,
(GAsyncReadyCallback)wwsm_update_ready,
- ctx);
+ task);
}
static void
-set_current_modes (MMIfaceModem *self,
- MMModemMode allowed,
- MMModemMode preferred,
- GAsyncReadyCallback callback,
- gpointer user_data)
+set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
SetCurrentModesContext *ctx;
+ task = g_task_new (self, NULL, callback, user_data);
+
ctx = g_new0 (SetCurrentModesContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) set_current_modes_context_free);
/* Handle ANY/NONE */
if (allowed == MM_MODEM_MODE_ANY && preferred == MM_MODEM_MODE_NONE) {
@@ -540,22 +518,18 @@ set_current_modes (MMIfaceModem *self,
}
if (!ctx->cgclass_command) {
- gchar *allowed_str;
- gchar *preferred_str;
+ g_autofree gchar *allowed_str = NULL;
+ g_autofree gchar *preferred_str = NULL;
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
- g_free (allowed_str);
- g_free (preferred_str);
-
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str, preferred_str);
+ g_object_unref (task);
return;
}
@@ -564,34 +538,29 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)cgclass_update_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* Load supported bands (Modem interface) */
static GArray *
-load_supported_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- /* Never fails */
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-load_supported_bands (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
GArray *bands;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_bands);
+ task = g_task_new (self, NULL, callback, user_data);
/* We do assume that we already know if the modem is 2G-only, 3G-only or
* 2G+3G. This is checked quite before trying to load supported bands. */
@@ -604,16 +573,15 @@ load_supported_bands (MMIfaceModem *self,
/* Add 3G-specific bands */
if (mm_iface_modem_is_3g (self)) {
bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 10);
- _g_array_insert_enum (bands, 0, MMModemBand, MM_MODEM_BAND_U2100);
- _g_array_insert_enum (bands, 1, MMModemBand, MM_MODEM_BAND_U1800);
- _g_array_insert_enum (bands, 2, MMModemBand, MM_MODEM_BAND_U17IV);
- _g_array_insert_enum (bands, 3, MMModemBand, MM_MODEM_BAND_U800);
- _g_array_insert_enum (bands, 4, MMModemBand, MM_MODEM_BAND_U850);
- _g_array_insert_enum (bands, 5, MMModemBand, MM_MODEM_BAND_U900);
- _g_array_insert_enum (bands, 6, MMModemBand, MM_MODEM_BAND_U900);
- _g_array_insert_enum (bands, 7, MMModemBand, MM_MODEM_BAND_U17IX);
- _g_array_insert_enum (bands, 8, MMModemBand, MM_MODEM_BAND_U1900);
- _g_array_insert_enum (bands, 9, MMModemBand, MM_MODEM_BAND_U2600);
+ _g_array_insert_enum (bands, 0, MMModemBand, MM_MODEM_BAND_UTRAN_1);
+ _g_array_insert_enum (bands, 1, MMModemBand, MM_MODEM_BAND_UTRAN_2);
+ _g_array_insert_enum (bands, 2, MMModemBand, MM_MODEM_BAND_UTRAN_3);
+ _g_array_insert_enum (bands, 3, MMModemBand, MM_MODEM_BAND_UTRAN_4);
+ _g_array_insert_enum (bands, 4, MMModemBand, MM_MODEM_BAND_UTRAN_5);
+ _g_array_insert_enum (bands, 5, MMModemBand, MM_MODEM_BAND_UTRAN_6);
+ _g_array_insert_enum (bands, 6, MMModemBand, MM_MODEM_BAND_UTRAN_7);
+ _g_array_insert_enum (bands, 7, MMModemBand, MM_MODEM_BAND_UTRAN_8);
+ _g_array_insert_enum (bands, 8, MMModemBand, MM_MODEM_BAND_UTRAN_9);
}
/* Add 2G-specific bands */
else {
@@ -624,44 +592,35 @@ load_supported_bands (MMIfaceModem *self,
_g_array_insert_enum (bands, 3, MMModemBand, MM_MODEM_BAND_G850);
}
- g_simple_async_result_set_op_res_gpointer (result,
- bands,
- (GDestroyNotify)g_array_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_pointer (task, bands, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Load current bands (Modem interface) */
static GArray *
-load_current_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-get_2g_band_ready (MMBroadbandModemWavecom *self,
+get_2g_band_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
const gchar *p;
- GError *error = NULL;
- GArray *bands_array = NULL;
+ GError *error = NULL;
+ GArray *bands_array = NULL;
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -683,37 +642,31 @@ get_2g_band_ready (MMBroadbandModemWavecom *self,
}
if (!bands_array)
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse current bands reply: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse current bands reply: '%s'",
+ response);
else
- g_simple_async_result_set_op_res_gpointer (operation_result,
- bands_array,
- (GDestroyNotify)g_array_unref);
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_pointer (task, bands_array, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
}
static void
-get_3g_band_ready (MMBroadbandModemWavecom *self,
+get_3g_band_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
const gchar *p;
- GError *error = NULL;
- GArray *bands_array = NULL;
- guint32 wavecom_band;
+ GError *error = NULL;
+ GArray *bands_array = NULL;
+ guint32 wavecom_band;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -742,18 +695,14 @@ get_3g_band_ready (MMBroadbandModemWavecom *self,
}
if (!bands_array)
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse current bands reply: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse current bands reply: '%s'",
+ response);
else
- g_simple_async_result_set_op_res_gpointer (operation_result,
- bands_array,
- (GDestroyNotify)g_array_unref);
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_pointer (task, bands_array, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
}
static void
@@ -761,12 +710,9 @@ load_current_bands (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_current_bands);
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_3g (self))
mm_base_modem_at_command (MM_BASE_MODEM (self),
@@ -774,54 +720,52 @@ load_current_bands (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)get_3g_band_ready,
- result);
+ task);
else
mm_base_modem_at_command (MM_BASE_MODEM (self),
"AT+WMBS?",
3,
FALSE,
(GAsyncReadyCallback)get_2g_band_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Set current_bands (Modem interface) */
static gboolean
-set_current_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-wmbs_set_ready (MMBaseModem *self,
+wmbs_set_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
- /* Let the error be critical */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
-
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-set_bands_3g (MMIfaceModem *self,
- GArray *bands_array,
- GSimpleAsyncResult *result)
+set_bands_3g (GTask *task,
+ GArray *bands_array)
{
- GArray *bands_array_final;
- guint wavecom_band = 0;
- guint i;
- gchar *bands_string;
- gchar *cmd;
+ MMBroadbandModemWavecom *self;
+ guint wavecom_band = 0;
+ guint i;
+ g_autoptr(GArray) bands_array_final = NULL;
+ g_autofree gchar *cmd = NULL;
+
+ self = g_task_get_source_object (task);
/* The special case of ANY should be treated separately. */
if (bands_array->len == 1 &&
@@ -846,44 +790,39 @@ set_bands_3g (MMIfaceModem *self,
}
}
- bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data,
- bands_array_final->len);
- g_array_unref (bands_array_final);
-
if (wavecom_band == 0) {
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "The given band combination is not supported: '%s'",
- bands_string);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- g_free (bands_string);
+ g_autofree gchar *bands_string = NULL;
+
+ bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array_final->data, bands_array_final->len);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "The given band combination is not supported: '%s'",
+ bands_string);
+ g_object_unref (task);
return;
}
- mm_dbg ("Setting new bands to use: '%s'", bands_string);
cmd = g_strdup_printf ("+WMBS=\"%u\",1", wavecom_band);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
3,
FALSE,
(GAsyncReadyCallback)wmbs_set_ready,
- result);
- g_free (cmd);
- g_free (bands_string);
+ task);
}
static void
-set_bands_2g (MMIfaceModem *self,
- GArray *bands_array,
- GSimpleAsyncResult *result)
+set_bands_2g (GTask *task,
+ GArray *bands_array)
{
- GArray *bands_array_final;
- gchar wavecom_band = '\0';
- guint i;
- gchar *bands_string;
- gchar *cmd;
+ MMBroadbandModemWavecom *self;
+ gchar wavecom_band = '\0';
+ guint i;
+ g_autofree gchar *cmd = NULL;
+ g_autoptr(GArray) bands_array_final = NULL;
+
+ self = g_task_get_source_object (task);
/* If the iface properly checked the given list against the supported bands,
* it's not possible to get an array longer than 4 here. */
@@ -906,54 +845,47 @@ set_bands_2g (MMIfaceModem *self,
bands_array_final = g_array_ref (bands_array);
for (i = 0; wavecom_band == '\0' && i < G_N_ELEMENTS (bands_2g); i++) {
- GArray *supported_combination;
+ g_autoptr(GArray) supported_combination = NULL;
supported_combination = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), bands_2g[i].n_mm_bands);
g_array_append_vals (supported_combination, bands_2g[i].mm_bands, bands_2g[i].n_mm_bands);
/* Check if the given array is exactly one of the supported combinations */
- if (mm_common_bands_garray_cmp (bands_array_final, supported_combination))
+ if (mm_common_bands_garray_cmp (bands_array_final, supported_combination)) {
wavecom_band = bands_2g[i].wavecom_band;
-
- g_array_unref (supported_combination);
+ break;
+ }
}
- bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data,
- bands_array_final->len);
- g_array_unref (bands_array_final);
-
if (wavecom_band == '\0') {
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "The given band combination is not supported: '%s'",
- bands_string);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- g_free (bands_string);
+ g_autofree gchar *bands_string = NULL;
+
+ bands_string = mm_common_build_bands_string ((MMModemBand *)(gpointer)bands_array_final->data, bands_array_final->len);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "The given band combination is not supported: '%s'",
+ bands_string);
+ g_object_unref (task);
return;
}
- mm_dbg ("Setting new bands to use: '%s'", bands_string);
cmd = g_strdup_printf ("+WMBS=%c,1", wavecom_band);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
3,
FALSE,
(GAsyncReadyCallback)wmbs_set_ready,
- result);
-
- g_free (cmd);
- g_free (bands_string);
+ task);
}
static void
-set_current_bands (MMIfaceModem *self,
- GArray *bands_array,
- GAsyncReadyCallback callback,
- gpointer user_data)
+set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
/* The bands that we get here are previously validated by the interface, and
* that means that ALL the bands given here were also given in the list of
@@ -961,15 +893,12 @@ set_current_bands (MMIfaceModem *self,
* will end up being valid, as not all combinations are possible. E.g,
* Wavecom modems supporting only 2G have specific combinations allowed.
*/
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_bands);
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_3g (self))
- set_bands_3g (self, bands_array, result);
+ set_bands_3g (task, bands_array);
else
- set_bands_2g (self, bands_array, result);
+ set_bands_2g (task, bands_array);
}
/*****************************************************************************/
@@ -1050,65 +979,51 @@ load_access_technologies (MMIfaceModem *self,
/*****************************************************************************/
/* Register in network (3GPP interface) */
-typedef struct {
- MMBroadbandModemWavecom *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gchar *operator_id;
-} RegisterInNetworkContext;
-
-static void
-register_in_network_context_complete_and_free (RegisterInNetworkContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_free (ctx->operator_id);
- g_slice_free (RegisterInNetworkContext, ctx);
-}
-
static gboolean
-register_in_network_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_registration_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- RegisterInNetworkContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->register_in_network_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-run_parent_registration (RegisterInNetworkContext *ctx)
+run_parent_registration (GTask *task)
{
+ MMBroadbandModemWavecom *self;
+ const gchar *operator_id;
+
+ self = g_task_get_source_object (task);
+ operator_id = g_task_get_task_data (task);
+
iface_modem_3gpp_parent->register_in_network (
- MM_IFACE_MODEM_3GPP (ctx->self),
- ctx->operator_id,
- ctx->cancellable,
+ MM_IFACE_MODEM_3GPP (self),
+ operator_id,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)parent_registration_ready,
- ctx);
+ task);
}
static gboolean
parse_network_registration_mode (const gchar *reply,
- guint *mode)
+ guint *mode)
{
- GRegex *r;
- GMatchInfo *match_info;
- gboolean parsed = FALSE;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
g_assert (mode != NULL);
@@ -1119,72 +1034,58 @@ parse_network_registration_mode (const gchar *reply,
g_assert (r != NULL);
g_regex_match (r, reply, 0, &match_info);
- if (g_match_info_matches (match_info) &&
- mm_get_uint_from_match_info (match_info, 1, mode))
- parsed = TRUE;
- g_match_info_free (match_info);
- g_regex_unref (r);
-
- return parsed;
+ return (g_match_info_matches (match_info) && mm_get_uint_from_match_info (match_info, 1, mode));
}
static void
-cops_ready (MMBaseModem *self,
+cops_ready (MMBaseModem *self,
GAsyncResult *res,
- RegisterInNetworkContext *ctx)
+ GTask *task)
{
const gchar *response;
- GError *error = NULL;
- guint mode;
+ GError *error = NULL;
+ guint mode;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (ctx->result, error);
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!parse_network_registration_mode (response, &mode)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse current network registration mode");
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse current network registration mode: '%s'",
+ response);
+ g_object_unref (task);
return;
}
- /* If the modem is already configured for automatic registration, don't do
- * anything else */
- if (mode == 0) {
- mm_dbg ("Device is already in automatic registration mode, not requesting it again");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ /* If the modem is not configured for automatic registration, run parent */
+ if (mode != 0) {
+ run_parent_registration (task);
return;
}
- /* Otherwise, run parent's implementation */
- run_parent_registration (ctx);
+ mm_obj_dbg (self, "device is already in automatic registration mode, not requesting it again");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- RegisterInNetworkContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
- ctx = g_slice_new0 (RegisterInNetworkContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- register_in_network);
- ctx->operator_id = g_strdup (operator_id);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ /* Store operator id as task data */
+ g_task_set_task_data (task, g_strdup (operator_id), g_free);
/* If requesting automatic registration, we first need to query what the
* current mode is. We must NOT send +COPS=0 if it already is in 0 mode,
@@ -1196,74 +1097,45 @@ register_in_network (MMIfaceModem3gpp *self,
3,
FALSE,
(GAsyncReadyCallback)cops_ready,
- ctx);
+ task);
return;
}
/* Otherwise, run parent's implementation right away */
- run_parent_registration (ctx);
+ run_parent_registration (task);
}
/*****************************************************************************/
/* After SIM unlock (Modem interface) */
static gboolean
-modem_after_sim_unlock_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_after_sim_unlock_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-after_sim_unlock_wait_cb (GSimpleAsyncResult *result)
+after_sim_unlock_wait_cb (GTask *task)
{
- g_simple_async_result_complete (result);
- g_object_unref (result);
- return FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
-modem_after_sim_unlock (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_after_sim_unlock (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
/* A short wait is necessary for SIM to become ready, otherwise reloading
* facility lock states may fail with a +CME ERROR: 515 error.
*/
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
-
- g_timeout_add_seconds (5, (GSourceFunc)after_sim_unlock_wait_cb, result);
-}
-
-/*****************************************************************************/
-/* Flow control (Modem interface) */
-
-static gboolean
-setup_flow_control_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
-}
-
-static void
-setup_flow_control (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- /* Wavecom doesn't have XOFF/XON flow control, so we enable RTS/CTS */
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+IFC=2,2",
- 3,
- FALSE,
- callback,
- user_data);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_timeout_add_seconds (5, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
@@ -1282,8 +1154,8 @@ modem_power_up (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_warn ("Not in full functionality status, power-up command is needed. "
- "Note that it may reboot the modem.");
+ mm_obj_warn (self, "not in full functionality status, power-up command is needed");
+ mm_obj_warn (self, "the device maybe rebooted");
/* Try to go to full functionality mode without rebooting the system.
* Works well if we previously switched off the power with CFUN=4
@@ -1359,7 +1231,7 @@ setup_ports (MMBroadbandModem *self)
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_wavecom_parent_class)->setup_ports (self);
/* Set 9600 baudrate by default in the AT port */
- mm_dbg ("Baudrate will be set to 9600 bps...");
+ mm_obj_dbg (self, "baudrate will be set to 9600 bps...");
primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
if (!primary)
return;
@@ -1393,6 +1265,9 @@ mm_broadband_modem_wavecom_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -1420,8 +1295,6 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_access_technologies_finish = load_access_technologies_finish;
iface->modem_after_sim_unlock = modem_after_sim_unlock;
iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish;
- iface->setup_flow_control = setup_flow_control;
- iface->setup_flow_control_finish = setup_flow_control_finish;
iface->modem_power_up = modem_power_up;
iface->modem_power_up_finish = modem_power_up_finish;
iface->modem_power_down = modem_power_down;
diff --git a/plugins/wavecom/mm-plugin-wavecom.c b/plugins/wavecom/mm-plugin-wavecom.c
index 39fa27a7..22b1ba4c 100644
--- a/plugins/wavecom/mm-plugin-wavecom.c
+++ b/plugins/wavecom/mm-plugin-wavecom.c
@@ -33,21 +33,21 @@
G_DEFINE_TYPE (MMPluginWavecom, mm_plugin_wavecom, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
GList *probes,
GError **error)
{
- return MM_BASE_MODEM (mm_broadband_modem_wavecom_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_wavecom_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -65,7 +65,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_WAVECOM,
- MM_PLUGIN_NAME, "Wavecom",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_FORBIDDEN_DRIVERS, forbidden_drivers,
diff --git a/plugins/x22x/77-mm-x22x-port-types.rules b/plugins/x22x/77-mm-x22x-port-types.rules
index cb4ca12f..40e7677a 100644
--- a/plugins/x22x/77-mm-x22x-port-types.rules
+++ b/plugins/x22x/77-mm-x22x-port-types.rules
@@ -8,9 +8,7 @@
# aux ports that may be either AT-capable or not but cannot be used for PPP.
-ACTION!="add|change|move", GOTO="mm_x22x_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_x22x_port_types_end"
-
+ACTION!="add|change|move|bind", GOTO="mm_x22x_port_types_end"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="1bbb", GOTO="mm_x22x_generic_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{idVendor}=="0b3c", GOTO="mm_x22x_olivetti_vendorcheck"
GOTO="mm_x22x_port_types_end"
@@ -21,27 +19,34 @@ LABEL="mm_x22x_generic_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Alcatel X200
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_X22X_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0000", ENV{ID_MM_X22X_TAGGED}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_X22X_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="0017", ENV{ID_MM_X22X_TAGGED}="1"
# Archos G9
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_X22X_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_X22X_PORT_TYPE_NMEA}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_X22X_PORT_TYPE_VOICE}="1"
-ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_X22X_PORT_TYPE_NMEA}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_X22X_PORT_TYPE_VOICE}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="00B7", ENV{ID_MM_X22X_TAGGED}="1"
+# Alcaltel X602D
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_PORT_IGNORE}="1"
+ATTRS{idVendor}=="1bbb", ATTRS{idProduct}=="022c", ENV{ID_MM_X22X_TAGGED}="1"
+
GOTO="mm_x22x_port_types_end"
# Olivetti devices ---------------------------
@@ -50,12 +55,12 @@ LABEL="mm_x22x_olivetti_vendorcheck"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
# Olicard 200
-ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_X22X_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
-ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_X22X_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
ATTRS{idVendor}=="0b3c", ATTRS{idProduct}=="c005", ENV{ID_MM_X22X_TAGGED}="1"
GOTO="mm_x22x_port_types_end"
diff --git a/plugins/x22x/mm-broadband-modem-x22x.c b/plugins/x22x/mm-broadband-modem-x22x.c
index ee611f2c..c4744262 100644
--- a/plugins/x22x/mm-broadband-modem-x22x.c
+++ b/plugins/x22x/mm-broadband-modem-x22x.c
@@ -38,6 +38,13 @@ static MMIfaceModem *iface_modem_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemX22x, mm_broadband_modem_x22x, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init))
+struct _MMBroadbandModemX22xPrivate {
+ GRegex *mode_regex;
+ GRegex *sysinfo_regex;
+ GRegex *specc_regex;
+ GRegex *sperror_regex;
+};
+
/*****************************************************************************/
/* Load supported modes (Modem interface) */
@@ -46,16 +53,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -65,9 +69,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Build list of combinations for 3GPP devices */
@@ -87,13 +90,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
g_array_append_val (combinations, mode);
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -105,10 +107,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -201,24 +200,24 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBroadbandModemX22x *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -228,14 +227,11 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint syssel = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (allowed == MM_MODEM_MODE_2G)
syssel = 1;
@@ -253,18 +249,16 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -275,7 +269,7 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -306,7 +300,6 @@ load_access_technologies (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading access technology (x22x)...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+SSND?",
3,
@@ -316,6 +309,52 @@ load_access_technologies (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Setup ports (Broadband modem class) */
+
+static void
+set_ignored_unsolicited_events_handlers (MMBroadbandModemX22x *self)
+{
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Enable/disable unsolicited events in given port */
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->mode_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->sysinfo_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->specc_regex,
+ NULL, NULL, NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->sperror_regex,
+ NULL, NULL, NULL);
+ }
+}
+
+static void
+setup_ports (MMBroadbandModem *self)
+{
+ /* Call parent's setup ports first always */
+ MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_x22x_parent_class)->setup_ports (self);
+
+ /* Unsolicited messages to always ignore */
+ set_ignored_unsolicited_events_handlers (MM_BROADBAND_MODEM_X22X (self));
+}
+
+/*****************************************************************************/
MMBroadbandModemX22x *
mm_broadband_modem_x22x_new (const gchar *device,
@@ -330,12 +369,39 @@ mm_broadband_modem_x22x_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
static void
mm_broadband_modem_x22x_init (MMBroadbandModemX22x *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_MODEM_X22X,
+ MMBroadbandModemX22xPrivate);
+
+ self->priv->mode_regex = g_regex_new ("\\r\\n\\^MODE:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->sysinfo_regex = g_regex_new ("\\r\\n\\^SYSINFO:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->specc_regex = g_regex_new ("\\r\\n\\+SPECC\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ self->priv->sperror_regex = g_regex_new ("\\r\\n\\+SPERROR:.+\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMBroadbandModemX22x *self = MM_BROADBAND_MODEM_X22X (object);
+
+ g_regex_unref (self->priv->mode_regex);
+ g_regex_unref (self->priv->sysinfo_regex);
+ g_regex_unref (self->priv->specc_regex);
+ g_regex_unref (self->priv->sperror_regex);
+ G_OBJECT_CLASS (mm_broadband_modem_x22x_parent_class)->finalize (object);
}
static void
@@ -356,4 +422,12 @@ iface_modem_init (MMIfaceModem *iface)
static void
mm_broadband_modem_x22x_class_init (MMBroadbandModemX22xClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandModemX22xPrivate));
+
+ object_class->finalize = finalize;
+
+ broadband_modem_class->setup_ports = setup_ports;
}
diff --git a/plugins/x22x/mm-broadband-modem-x22x.h b/plugins/x22x/mm-broadband-modem-x22x.h
index f61e301d..74c2b48a 100644
--- a/plugins/x22x/mm-broadband-modem-x22x.h
+++ b/plugins/x22x/mm-broadband-modem-x22x.h
@@ -29,9 +29,11 @@
typedef struct _MMBroadbandModemX22x MMBroadbandModemX22x;
typedef struct _MMBroadbandModemX22xClass MMBroadbandModemX22xClass;
+typedef struct _MMBroadbandModemX22xPrivate MMBroadbandModemX22xPrivate;
struct _MMBroadbandModemX22x {
MMBroadbandModem parent;
+ MMBroadbandModemX22xPrivate *priv;
};
struct _MMBroadbandModemX22xClass{
diff --git a/plugins/x22x/mm-plugin-x22x.c b/plugins/x22x/mm-plugin-x22x.c
index db5f25b7..9bfc62d2 100644
--- a/plugins/x22x/mm-plugin-x22x.c
+++ b/plugins/x22x/mm-plugin-x22x.c
@@ -21,7 +21,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-plugin-x22x.h"
#include "mm-broadband-modem-x22x.h"
@@ -32,57 +32,52 @@
G_DEFINE_TYPE (MMPluginX22x, mm_plugin_x22x, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom init */
typedef struct {
- MMPortProbe *probe;
MMPortSerialAt *port;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
guint retries;
} X22xCustomInitContext;
static void
-x22x_custom_init_context_complete_and_free (X22xCustomInitContext *ctx)
+x22x_custom_init_context_free (X22xCustomInitContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
-
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->port);
- g_object_unref (ctx->probe);
- g_object_unref (ctx->result);
g_slice_free (X22xCustomInitContext, ctx);
}
static gboolean
x22x_custom_init_finish (MMPortProbe *probe,
- GAsyncResult *result,
- GError **error)
+ GAsyncResult *result,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
-static void x22x_custom_init_step (X22xCustomInitContext *ctx);
+static void x22x_custom_init_step (GTask *task);
static void
gmr_ready (MMPortSerialAt *port,
- GAsyncResult *res,
- X22xCustomInitContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
+ MMPortProbe *probe;
const gchar *p;
const gchar *response;
- GError *error = NULL;
+ GError *error = NULL;
+
+ probe = g_task_get_source_object (task);
response = mm_port_serial_at_command_finish (port, res, &error);
if (error) {
+ g_error_free (error);
/* Just retry... */
- x22x_custom_init_step (ctx);
- goto out;
+ x22x_custom_init_step (task);
+ return;
}
/* Note the lack of a ':' on the GMR; the X200 doesn't send one */
@@ -93,42 +88,44 @@ gmr_ready (MMPortSerialAt *port,
* So use that to determine if the device is an X200, which this plugin
* does supports.
*/
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Not supported with the X22X plugin");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Not supported with the X22X plugin");
} else {
- mm_dbg ("(X22X) device is supported by this plugin");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ mm_obj_dbg (probe, "(X22X) device is supported by this plugin");
+ g_task_return_boolean (task, TRUE);
}
-
- x22x_custom_init_context_complete_and_free (ctx);
-
-out:
- if (error)
- g_error_free (error);
+ g_object_unref (task);
}
static void
-x22x_custom_init_step (X22xCustomInitContext *ctx)
+x22x_custom_init_step (GTask *task)
{
+ MMPortProbe *probe;
+ X22xCustomInitContext *ctx;
+ GCancellable *cancellable;
+
+ probe = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ cancellable = g_task_get_cancellable (task);
+
/* If cancelled, end */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- mm_dbg ("(X22X) no need to keep on running custom init in (%s)",
- mm_port_get_device (MM_PORT (ctx->port)));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- x22x_custom_init_context_complete_and_free (ctx);
+ if (g_cancellable_is_cancelled (cancellable)) {
+ mm_obj_dbg (probe, "(X22X) no need to keep on running custom init");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
if (ctx->retries == 0) {
/* In this case, we need the AT command result to decide whether we can
* support this modem or not, so really fail if we didn't get it. */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get device revision information");
- x22x_custom_init_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get device revision information");
+ g_object_unref (task);
return;
}
@@ -139,9 +136,9 @@ x22x_custom_init_step (X22xCustomInitContext *ctx)
3,
FALSE, /* raw */
FALSE, /* allow_cached */
- ctx->cancellable,
+ cancellable,
(GAsyncReadyCallback)gmr_ready,
- ctx);
+ task);
}
static void
@@ -153,17 +150,16 @@ x22x_custom_init (MMPortProbe *probe,
{
MMDevice *device;
X22xCustomInitContext *ctx;
+ GTask *task;
ctx = g_slice_new (X22xCustomInitContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (probe),
- callback,
- user_data,
- x22x_custom_init);
- ctx->probe = g_object_ref (probe);
ctx->port = g_object_ref (port);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
ctx->retries = 3;
+ task = g_task_new (probe, cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, FALSE);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)x22x_custom_init_context_free);
+
/* TCT/Alcatel in their infinite wisdom assigned the same USB VID/PID to
* the x060s (Longcheer firmware) and the x200 (X22X, this plugin) and thus
* we can't tell them apart via udev rules. Worse, they both report the
@@ -177,19 +173,19 @@ x22x_custom_init (MMPortProbe *probe,
if (mm_device_get_vendor (device) != 0x1bbb ||
mm_device_get_product (device) != 0x0000) {
/* If not exactly this vendor/product, just skip */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- x22x_custom_init_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- x22x_custom_init_step (ctx);
+ x22x_custom_init_step (task);
}
/*****************************************************************************/
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -198,8 +194,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered X22X modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered X22X modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -207,65 +203,19 @@ create_modem (MMPlugin *self,
}
#endif
- return MM_BASE_MODEM (mm_broadband_modem_x22x_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_x22x_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
}
-static gboolean
-grab_port (MMPlugin *self,
- MMBaseModem *modem,
- MMPortProbe *probe,
- GError **error)
-{
- GUdevDevice *port;
- MMPortType ptype;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
-
- port = mm_port_probe_peek_port (probe);
- ptype = mm_port_probe_get_port_type (probe);
-
- if (ptype == MM_PORT_TYPE_AT) {
- /* Look for port type hints; just probing can't distinguish which port should
- * be the data/primary port on these devices. We have to tag them based on
- * what the Windows .INF files say the port layout should be.
- */
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_X22X_PORT_TYPE_MODEM")) {
- mm_dbg ("x22x: AT port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_X22X_PORT_TYPE_AUX")) {
- mm_dbg ("x22x: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- } else {
- /* If the port was tagged by the udev rules but isn't a primary or secondary,
- * then ignore it to guard against race conditions if a device just happens
- * to show up with more than two AT-capable ports.
- */
- ptype = MM_PORT_TYPE_IGNORED;
- }
- }
-
- return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- ptype,
- pflags,
- error);
-}
-
/*****************************************************************************/
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
/* Vendors: TAMobile and Olivetti */
static const guint16 vendor_ids[] = { 0x1bbb, 0x0b3c, 0 };
/* Only handle X22X tagged devices here. */
@@ -280,7 +230,7 @@ mm_plugin_create (void)
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_X22X,
- MM_PLUGIN_NAME, "X22X",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_ALLOWED_AT, TRUE,
@@ -301,5 +251,4 @@ mm_plugin_x22x_class_init (MMPluginX22xClass *klass)
MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass);
plugin_class->create_modem = create_modem;
- plugin_class->grab_port = grab_port;
}
diff --git a/plugins/xmm/mm-broadband-modem-mbim-xmm.c b/plugins/xmm/mm-broadband-modem-mbim-xmm.c
new file mode 100644
index 00000000..381432f3
--- /dev/null
+++ b/plugins/xmm/mm-broadband-modem-mbim-xmm.c
@@ -0,0 +1,139 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-broadband-modem-mbim-xmm.h"
+#include "mm-shared-xmm.h"
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void shared_xmm_init (MMSharedXmm *iface);
+
+static MMIfaceModemLocation *iface_modem_location_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimXmm, mm_broadband_modem_mbim_xmm, MM_TYPE_BROADBAND_MODEM_MBIM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init))
+
+/*****************************************************************************/
+
+MMBroadbandModemMbimXmm *
+mm_broadband_modem_mbim_xmm_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_MBIM_XMM,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, TRUE,
+#endif
+ NULL);
+}
+
+static void
+mm_broadband_modem_mbim_xmm_init (MMBroadbandModemMbimXmm *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface->load_supported_modes = mm_shared_xmm_load_supported_modes;
+ iface->load_supported_modes_finish = mm_shared_xmm_load_supported_modes_finish;
+ iface->load_current_modes = mm_shared_xmm_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_xmm_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_xmm_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_xmm_set_current_modes_finish;
+
+ iface->load_supported_bands = mm_shared_xmm_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_xmm_load_supported_bands_finish;
+ iface->load_current_bands = mm_shared_xmm_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_xmm_load_current_bands_finish;
+ iface->set_current_bands = mm_shared_xmm_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_xmm_set_current_bands_finish;
+
+ /* power up/down already managed via MBIM */
+ iface->modem_power_off = mm_shared_xmm_power_off;
+ iface->modem_power_off_finish = mm_shared_xmm_power_off_finish;
+ iface->reset = mm_shared_xmm_reset;
+ iface->reset_finish = mm_shared_xmm_reset_finish;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_xmm_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish;
+ iface->load_supl_server = mm_shared_xmm_location_load_supl_server;
+ iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish;
+ iface->set_supl_server = mm_shared_xmm_location_set_supl_server;
+ iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish;
+}
+
+static MMBroadbandModemClass *
+peek_parent_broadband_modem_class (MMSharedXmm *self)
+{
+ return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_xmm_parent_class);
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedXmm *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+shared_xmm_init (MMSharedXmm *iface)
+{
+ iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+}
+
+static void
+mm_broadband_modem_mbim_xmm_class_init (MMBroadbandModemMbimXmmClass *klass)
+{
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports;
+}
diff --git a/plugins/xmm/mm-broadband-modem-mbim-xmm.h b/plugins/xmm/mm-broadband-modem-mbim-xmm.h
new file mode 100644
index 00000000..88e87cb7
--- /dev/null
+++ b/plugins/xmm/mm-broadband-modem-mbim-xmm.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_MBIM_XMM_H
+#define MM_BROADBAND_MODEM_MBIM_XMM_H
+
+#include "mm-broadband-modem-mbim.h"
+
+#define MM_TYPE_BROADBAND_MODEM_MBIM_XMM (mm_broadband_modem_mbim_xmm_get_type ())
+#define MM_BROADBAND_MODEM_MBIM_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MMBroadbandModemMbimXmm))
+#define MM_BROADBAND_MODEM_MBIM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MMBroadbandModemMbimXmmClass))
+#define MM_IS_BROADBAND_MODEM_MBIM_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM))
+#define MM_IS_BROADBAND_MODEM_MBIM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_MBIM_XMM))
+#define MM_BROADBAND_MODEM_MBIM_XMM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_MBIM_XMM, MMBroadbandModemMbimXmmClass))
+
+typedef struct _MMBroadbandModemMbimXmm MMBroadbandModemMbimXmm;
+typedef struct _MMBroadbandModemMbimXmmClass MMBroadbandModemMbimXmmClass;
+
+struct _MMBroadbandModemMbimXmm {
+ MMBroadbandModemMbim parent;
+};
+
+struct _MMBroadbandModemMbimXmmClass{
+ MMBroadbandModemMbimClass parent;
+};
+
+GType mm_broadband_modem_mbim_xmm_get_type (void);
+
+MMBroadbandModemMbimXmm *mm_broadband_modem_mbim_xmm_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_XMM_H */
diff --git a/plugins/xmm/mm-broadband-modem-xmm.c b/plugins/xmm/mm-broadband-modem-xmm.c
new file mode 100644
index 00000000..7698ec66
--- /dev/null
+++ b/plugins/xmm/mm-broadband-modem-xmm.c
@@ -0,0 +1,151 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include "ModemManager.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-location.h"
+#include "mm-broadband-modem-xmm.h"
+#include "mm-shared-xmm.h"
+
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void shared_xmm_init (MMSharedXmm *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+
+static MMIfaceModemLocation *iface_modem_location_parent;
+
+G_DEFINE_TYPE_EXTENDED (MMBroadbandModemXmm, mm_broadband_modem_xmm, MM_TYPE_BROADBAND_MODEM, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIGNAL, iface_modem_signal_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_LOCATION, iface_modem_location_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_XMM, shared_xmm_init))
+
+/*****************************************************************************/
+
+MMBroadbandModemXmm *
+mm_broadband_modem_xmm_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id)
+{
+ return g_object_new (MM_TYPE_BROADBAND_MODEM_XMM,
+ MM_BASE_MODEM_DEVICE, device,
+ MM_BASE_MODEM_DRIVERS, drivers,
+ MM_BASE_MODEM_PLUGIN, plugin,
+ MM_BASE_MODEM_VENDOR_ID, vendor_id,
+ MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ NULL);
+}
+
+static void
+mm_broadband_modem_xmm_init (MMBroadbandModemXmm *self)
+{
+}
+
+static void
+iface_modem_init (MMIfaceModem *iface)
+{
+ iface->load_supported_modes = mm_shared_xmm_load_supported_modes;
+ iface->load_supported_modes_finish = mm_shared_xmm_load_supported_modes_finish;
+ iface->load_current_modes = mm_shared_xmm_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_xmm_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_xmm_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_xmm_set_current_modes_finish;
+
+ iface->load_supported_bands = mm_shared_xmm_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_xmm_load_supported_bands_finish;
+ iface->load_current_bands = mm_shared_xmm_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_xmm_load_current_bands_finish;
+ iface->set_current_bands = mm_shared_xmm_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_xmm_set_current_bands_finish;
+
+ iface->load_power_state = mm_shared_xmm_load_power_state;
+ iface->load_power_state_finish = mm_shared_xmm_load_power_state_finish;
+ iface->modem_power_up = mm_shared_xmm_power_up;
+ iface->modem_power_up_finish = mm_shared_xmm_power_up_finish;
+ iface->modem_power_down = mm_shared_xmm_power_down;
+ iface->modem_power_down_finish = mm_shared_xmm_power_down_finish;
+ iface->modem_power_off = mm_shared_xmm_power_off;
+ iface->modem_power_off_finish = mm_shared_xmm_power_off_finish;
+ iface->reset = mm_shared_xmm_reset;
+ iface->reset_finish = mm_shared_xmm_reset_finish;
+}
+
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_xmm_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_xmm_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_xmm_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_xmm_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_xmm_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_xmm_disable_location_gathering_finish;
+ iface->load_supl_server = mm_shared_xmm_location_load_supl_server;
+ iface->load_supl_server_finish = mm_shared_xmm_location_load_supl_server_finish;
+ iface->set_supl_server = mm_shared_xmm_location_set_supl_server;
+ iface->set_supl_server_finish = mm_shared_xmm_location_set_supl_server_finish;
+}
+
+static MMBroadbandModemClass *
+peek_parent_broadband_modem_class (MMSharedXmm *self)
+{
+ return MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_xmm_parent_class);
+}
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedXmm *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+iface_modem_signal_init (MMIfaceModemSignal *iface)
+{
+ iface->check_support = mm_shared_xmm_signal_check_support;
+ iface->check_support_finish = mm_shared_xmm_signal_check_support_finish;
+ iface->load_values = mm_shared_xmm_signal_load_values;
+ iface->load_values_finish = mm_shared_xmm_signal_load_values_finish;
+}
+
+static void
+shared_xmm_init (MMSharedXmm *iface)
+{
+ iface->peek_parent_broadband_modem_class = peek_parent_broadband_modem_class;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+}
+
+static void
+mm_broadband_modem_xmm_class_init (MMBroadbandModemXmmClass *klass)
+{
+ MMBroadbandModemClass *broadband_modem_class = MM_BROADBAND_MODEM_CLASS (klass);
+
+ broadband_modem_class->setup_ports = mm_shared_xmm_setup_ports;
+}
diff --git a/plugins/xmm/mm-broadband-modem-xmm.h b/plugins/xmm/mm-broadband-modem-xmm.h
new file mode 100644
index 00000000..f63a4bfc
--- /dev/null
+++ b/plugins/xmm/mm-broadband-modem-xmm.h
@@ -0,0 +1,47 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_BROADBAND_MODEM_XMM_H
+#define MM_BROADBAND_MODEM_XMM_H
+
+#include "mm-broadband-modem.h"
+
+#define MM_TYPE_BROADBAND_MODEM_XMM (mm_broadband_modem_xmm_get_type ())
+#define MM_BROADBAND_MODEM_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_XMM, MMBroadbandModemXmm))
+#define MM_BROADBAND_MODEM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_XMM, MMBroadbandModemXmmClass))
+#define MM_IS_BROADBAND_MODEM_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_XMM))
+#define MM_IS_BROADBAND_MODEM_XMM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_XMM))
+#define MM_BROADBAND_MODEM_XMM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_XMM, MMBroadbandModemXmmClass))
+
+typedef struct _MMBroadbandModemXmm MMBroadbandModemXmm;
+typedef struct _MMBroadbandModemXmmClass MMBroadbandModemXmmClass;
+
+struct _MMBroadbandModemXmm {
+ MMBroadbandModem parent;
+};
+
+struct _MMBroadbandModemXmmClass{
+ MMBroadbandModemClass parent;
+};
+
+GType mm_broadband_modem_xmm_get_type (void);
+
+MMBroadbandModemXmm *mm_broadband_modem_xmm_new (const gchar *device,
+ const gchar **drivers,
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+#endif /* MM_BROADBAND_MODEM_XMM_H */
diff --git a/plugins/xmm/mm-modem-helpers-xmm.c b/plugins/xmm/mm-modem-helpers-xmm.c
new file mode 100644
index 00000000..9b3933d2
--- /dev/null
+++ b/plugins/xmm/mm-modem-helpers-xmm.c
@@ -0,0 +1,1012 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+
+#include "mm-log.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-xmm.h"
+#include "mm-signal.h"
+
+/*****************************************************************************/
+/* XACT common config */
+
+typedef struct {
+ guint num;
+ MMModemBand band;
+} XactBandConfig;
+
+static const XactBandConfig xact_band_config[] = {
+ /* GSM bands */
+ { .num = 900, .band = MM_MODEM_BAND_EGSM },
+ { .num = 1800, .band = MM_MODEM_BAND_DCS },
+ { .num = 1900, .band = MM_MODEM_BAND_PCS },
+ { .num = 850, .band = MM_MODEM_BAND_G850 },
+ { .num = 450, .band = MM_MODEM_BAND_G450 },
+ { .num = 480, .band = MM_MODEM_BAND_G480 },
+ { .num = 750, .band = MM_MODEM_BAND_G750 },
+ { .num = 380, .band = MM_MODEM_BAND_G380 },
+ { .num = 410, .band = MM_MODEM_BAND_G410 },
+ { .num = 710, .band = MM_MODEM_BAND_G710 },
+ { .num = 810, .band = MM_MODEM_BAND_G810 },
+ /* UMTS bands */
+ { .num = 1, .band = MM_MODEM_BAND_UTRAN_1 },
+ { .num = 2, .band = MM_MODEM_BAND_UTRAN_2 },
+ { .num = 3, .band = MM_MODEM_BAND_UTRAN_3 },
+ { .num = 4, .band = MM_MODEM_BAND_UTRAN_4 },
+ { .num = 5, .band = MM_MODEM_BAND_UTRAN_5 },
+ { .num = 6, .band = MM_MODEM_BAND_UTRAN_6 },
+ { .num = 7, .band = MM_MODEM_BAND_UTRAN_7 },
+ { .num = 8, .band = MM_MODEM_BAND_UTRAN_8 },
+ { .num = 9, .band = MM_MODEM_BAND_UTRAN_9 },
+ { .num = 10, .band = MM_MODEM_BAND_UTRAN_10 },
+ { .num = 11, .band = MM_MODEM_BAND_UTRAN_11 },
+ { .num = 12, .band = MM_MODEM_BAND_UTRAN_12 },
+ { .num = 13, .band = MM_MODEM_BAND_UTRAN_13 },
+ { .num = 14, .band = MM_MODEM_BAND_UTRAN_14 },
+ { .num = 19, .band = MM_MODEM_BAND_UTRAN_19 },
+ { .num = 20, .band = MM_MODEM_BAND_UTRAN_20 },
+ { .num = 21, .band = MM_MODEM_BAND_UTRAN_21 },
+ { .num = 22, .band = MM_MODEM_BAND_UTRAN_22 },
+ { .num = 25, .band = MM_MODEM_BAND_UTRAN_25 },
+ /* LTE bands */
+ { .num = 101, .band = MM_MODEM_BAND_EUTRAN_1 },
+ { .num = 102, .band = MM_MODEM_BAND_EUTRAN_2 },
+ { .num = 103, .band = MM_MODEM_BAND_EUTRAN_3 },
+ { .num = 104, .band = MM_MODEM_BAND_EUTRAN_4 },
+ { .num = 105, .band = MM_MODEM_BAND_EUTRAN_5 },
+ { .num = 106, .band = MM_MODEM_BAND_EUTRAN_6 },
+ { .num = 107, .band = MM_MODEM_BAND_EUTRAN_7 },
+ { .num = 108, .band = MM_MODEM_BAND_EUTRAN_8 },
+ { .num = 109, .band = MM_MODEM_BAND_EUTRAN_9 },
+ { .num = 110, .band = MM_MODEM_BAND_EUTRAN_10 },
+ { .num = 111, .band = MM_MODEM_BAND_EUTRAN_11 },
+ { .num = 112, .band = MM_MODEM_BAND_EUTRAN_12 },
+ { .num = 113, .band = MM_MODEM_BAND_EUTRAN_13 },
+ { .num = 114, .band = MM_MODEM_BAND_EUTRAN_14 },
+ { .num = 117, .band = MM_MODEM_BAND_EUTRAN_17 },
+ { .num = 118, .band = MM_MODEM_BAND_EUTRAN_18 },
+ { .num = 119, .band = MM_MODEM_BAND_EUTRAN_19 },
+ { .num = 120, .band = MM_MODEM_BAND_EUTRAN_20 },
+ { .num = 121, .band = MM_MODEM_BAND_EUTRAN_21 },
+ { .num = 122, .band = MM_MODEM_BAND_EUTRAN_22 },
+ { .num = 123, .band = MM_MODEM_BAND_EUTRAN_23 },
+ { .num = 124, .band = MM_MODEM_BAND_EUTRAN_24 },
+ { .num = 125, .band = MM_MODEM_BAND_EUTRAN_25 },
+ { .num = 126, .band = MM_MODEM_BAND_EUTRAN_26 },
+ { .num = 127, .band = MM_MODEM_BAND_EUTRAN_27 },
+ { .num = 128, .band = MM_MODEM_BAND_EUTRAN_28 },
+ { .num = 129, .band = MM_MODEM_BAND_EUTRAN_29 },
+ { .num = 130, .band = MM_MODEM_BAND_EUTRAN_30 },
+ { .num = 131, .band = MM_MODEM_BAND_EUTRAN_31 },
+ { .num = 132, .band = MM_MODEM_BAND_EUTRAN_32 },
+ { .num = 133, .band = MM_MODEM_BAND_EUTRAN_33 },
+ { .num = 134, .band = MM_MODEM_BAND_EUTRAN_34 },
+ { .num = 135, .band = MM_MODEM_BAND_EUTRAN_35 },
+ { .num = 136, .band = MM_MODEM_BAND_EUTRAN_36 },
+ { .num = 137, .band = MM_MODEM_BAND_EUTRAN_37 },
+ { .num = 138, .band = MM_MODEM_BAND_EUTRAN_38 },
+ { .num = 139, .band = MM_MODEM_BAND_EUTRAN_39 },
+ { .num = 140, .band = MM_MODEM_BAND_EUTRAN_40 },
+ { .num = 141, .band = MM_MODEM_BAND_EUTRAN_41 },
+ { .num = 142, .band = MM_MODEM_BAND_EUTRAN_42 },
+ { .num = 143, .band = MM_MODEM_BAND_EUTRAN_43 },
+ { .num = 144, .band = MM_MODEM_BAND_EUTRAN_44 },
+ { .num = 145, .band = MM_MODEM_BAND_EUTRAN_45 },
+ { .num = 146, .band = MM_MODEM_BAND_EUTRAN_46 },
+ { .num = 147, .band = MM_MODEM_BAND_EUTRAN_47 },
+ { .num = 148, .band = MM_MODEM_BAND_EUTRAN_48 },
+ { .num = 149, .band = MM_MODEM_BAND_EUTRAN_49 },
+ { .num = 150, .band = MM_MODEM_BAND_EUTRAN_50 },
+ { .num = 151, .band = MM_MODEM_BAND_EUTRAN_51 },
+ { .num = 152, .band = MM_MODEM_BAND_EUTRAN_52 },
+ { .num = 153, .band = MM_MODEM_BAND_EUTRAN_53 },
+ { .num = 154, .band = MM_MODEM_BAND_EUTRAN_54 },
+ { .num = 155, .band = MM_MODEM_BAND_EUTRAN_55 },
+ { .num = 156, .band = MM_MODEM_BAND_EUTRAN_56 },
+ { .num = 157, .band = MM_MODEM_BAND_EUTRAN_57 },
+ { .num = 158, .band = MM_MODEM_BAND_EUTRAN_58 },
+ { .num = 159, .band = MM_MODEM_BAND_EUTRAN_59 },
+ { .num = 160, .band = MM_MODEM_BAND_EUTRAN_60 },
+ { .num = 161, .band = MM_MODEM_BAND_EUTRAN_61 },
+ { .num = 162, .band = MM_MODEM_BAND_EUTRAN_62 },
+ { .num = 163, .band = MM_MODEM_BAND_EUTRAN_63 },
+ { .num = 164, .band = MM_MODEM_BAND_EUTRAN_64 },
+ { .num = 165, .band = MM_MODEM_BAND_EUTRAN_65 },
+ { .num = 166, .band = MM_MODEM_BAND_EUTRAN_66 },
+};
+
+#define XACT_NUM_IS_BAND_2G(num) (num > 300)
+#define XACT_NUM_IS_BAND_3G(num) (num < 100)
+#define XACT_NUM_IS_BAND_4G(num) (num > 100 && num < 300)
+
+static MMModemBand
+xact_num_to_band (guint num)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xact_band_config); i++) {
+ if (num == xact_band_config[i].num)
+ return xact_band_config[i].band;
+ }
+ return MM_MODEM_BAND_UNKNOWN;
+}
+
+static guint
+xact_band_to_num (MMModemBand band)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xact_band_config); i++) {
+ if (band == xact_band_config[i].band)
+ return xact_band_config[i].num;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/* XACT=? response parser */
+
+/* Index of the array is the XMM-specific value */
+static const MMModemMode xmm_modes[] = {
+ ( MM_MODEM_MODE_2G ),
+ ( MM_MODEM_MODE_3G ),
+ ( MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G ),
+ ( MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_2G | MM_MODEM_MODE_4G ),
+ ( MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G ),
+};
+
+gboolean
+mm_xmm_parse_xact_test_response (const gchar *response,
+ gpointer log_object,
+ GArray **modes_out,
+ GArray **bands_out,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GArray *modes = NULL;
+ GArray *all_modes = NULL;
+ GArray *filtered = NULL;
+ GArray *supported = NULL;
+ GArray *preferred = NULL;
+ GArray *bands = NULL;
+ gchar **split = NULL;
+ guint i;
+
+ MMModemModeCombination all = {
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE
+ };
+
+ g_assert (modes_out && bands_out);
+
+ /*
+ * AT+XACT=?
+ * +XACT: (0-6),(0-2),0,1,2,4,5,8,101,102,103,104,105,107,108,111,...
+ */
+ response = mm_strip_tag (response, "+XACT:");
+ split = mm_split_string_groups (response);
+
+ if (g_strv_length (split) < 3) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing fields");
+ goto out;
+ }
+
+ /* First group is list of supported modes */
+ supported = mm_parse_uint_list (split[0], &inner_error);
+ if (inner_error)
+ goto out;
+ if (!supported) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing modes");
+ goto out;
+ }
+
+ /* Second group is list of possible preferred modes.
+ * For our purposes, the preferred list may be empty */
+ preferred = mm_parse_uint_list (split[1], &inner_error);
+ if (inner_error)
+ goto out;
+
+ /* Build array of modes */
+ modes = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination));
+
+ for (i = 0; i < supported->len; i++) {
+ guint supported_value;
+ MMModemModeCombination combination;
+ guint j;
+
+ supported_value = g_array_index (supported, guint, i);
+
+ if (supported_value >= G_N_ELEMENTS (xmm_modes)) {
+ mm_obj_warn (log_object, "unexpected AcT supported value: %u", supported_value);
+ continue;
+ }
+
+ /* Combination without any preferred */
+ combination.allowed = xmm_modes[supported_value];
+ combination.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (modes, combination);
+
+ if (mm_count_bits_set (combination.allowed) == 1)
+ continue;
+
+ if (!preferred)
+ continue;
+
+ for (j = 0; j < preferred->len; j++) {
+ guint preferred_value;
+
+ preferred_value = g_array_index (preferred, guint, j);
+ if (preferred_value >= G_N_ELEMENTS (xmm_modes)) {
+ mm_obj_warn (log_object, "unexpected AcT preferred value: %u", preferred_value);
+ continue;
+ }
+ combination.preferred = xmm_modes[preferred_value];
+ if (mm_count_bits_set (combination.preferred) != 1) {
+ mm_obj_warn (log_object, "AcT preferred value should be a single AcT: %u", preferred_value);
+ continue;
+ }
+ if (!(combination.allowed & combination.preferred))
+ continue;
+ g_array_append_val (modes, combination);
+ }
+ }
+
+ if (modes->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No modes list built from +XACT=? response");
+ goto out;
+ }
+
+ /* Build array of bands */
+ bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+
+ /*
+ * The next element at index 2 may be '0'. We will just treat that field as
+ * any other band field as '0' isn't a supported band, we'll just ignore it.
+ */
+ for (i = 2; split[i]; i++) {
+ MMModemBand band;
+ guint num;
+
+ if (!mm_get_uint_from_str (split[i], &num)) {
+ mm_obj_warn (log_object, "unexpected band value: %s", split[i]);
+ continue;
+ }
+
+ if (num == 0)
+ continue;
+
+ band = xact_num_to_band (num);
+ if (band == MM_MODEM_BAND_UNKNOWN) {
+ mm_obj_warn (log_object, "unsupported band value: %s", split[i]);
+ continue;
+ }
+
+ g_array_append_val (bands, band);
+
+ if (XACT_NUM_IS_BAND_2G (num))
+ all.allowed |= MM_MODEM_MODE_2G;
+ if (XACT_NUM_IS_BAND_3G (num))
+ all.allowed |= MM_MODEM_MODE_3G;
+ if (XACT_NUM_IS_BAND_4G (num))
+ all.allowed |= MM_MODEM_MODE_4G;
+ }
+
+ if (bands->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No bands list built from +XACT=? response");
+ goto out;
+ }
+
+ /* AT+XACT lies about the supported modes, e.g. it may report 2G supported
+ * for 3G+4G only devices. So, filter out unsupported modes based on the
+ * supported bands */
+ all_modes = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ g_array_append_val (all_modes, all);
+
+ filtered = mm_filter_supported_modes (all_modes, modes, log_object);
+ if (!filtered || filtered->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Empty supported mode list after frequency band filtering");
+ goto out;
+ }
+
+ /* success */
+
+out:
+ if (modes)
+ g_array_unref (modes);
+ if (all_modes)
+ g_array_unref (all_modes);
+ if (supported)
+ g_array_unref (supported);
+ if (preferred)
+ g_array_unref (preferred);
+ g_strfreev (split);
+
+ if (inner_error) {
+ if (filtered)
+ g_array_unref (filtered);
+ if (bands)
+ g_array_unref (bands);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ g_assert (filtered);
+ *modes_out = filtered;
+ g_assert (bands);
+ *bands_out = bands;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* AT+XACT? response parser */
+
+gboolean
+mm_xmm_parse_xact_query_response (const gchar *response,
+ MMModemModeCombination *mode_out,
+ GArray **bands_out,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ GArray *bands = NULL;
+ guint i;
+
+ MMModemModeCombination mode = {
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ };
+
+ /* At least one */
+ g_assert (mode_out || bands_out);
+
+ /*
+ * AT+XACT?
+ * +XACT: 4,1,2,1,2,4,5,8,101,102,103,104,105,107,108,111,...
+ *
+ * Note: the first 3 fields corresponde to allowed and preferred modes. Only the
+ * first one of those 3 first fields is mandatory, the other two may be empty.
+ */
+ r = g_regex_new ("\\+XACT: (\\d+),([^,]*),([^,]*),(.*)(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ if (mode_out) {
+ guint xmm_mode;
+
+ /* Number at index 1 */
+ mm_get_uint_from_match_info (match_info, 1, &xmm_mode);
+ if (xmm_mode >= G_N_ELEMENTS (xmm_modes)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported XACT AcT value: %u", xmm_mode);
+ goto out;
+ }
+ mode.allowed = xmm_modes[xmm_mode];
+
+ /* Number at index 2 */
+ if (mm_count_bits_set (mode.allowed) > 1 && mm_get_uint_from_match_info (match_info, 2, &xmm_mode)) {
+ if (xmm_mode >= G_N_ELEMENTS (xmm_modes)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unsupported XACT preferred AcT value: %u", xmm_mode);
+ goto out;
+ }
+ mode.preferred = xmm_modes[xmm_mode];
+ }
+
+ /* Number at index 3: ignored */
+ }
+
+ if (bands_out) {
+ gchar *bandstr;
+ GArray *nums;
+
+ /* Bands start at index 4 */
+ bandstr = mm_get_string_unquoted_from_match_info (match_info, 4);
+ nums = mm_parse_uint_list (bandstr, &inner_error);
+ g_free (bandstr);
+
+ if (inner_error)
+ goto out;
+ if (!nums) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Invalid XACT? response");
+ goto out;
+ }
+
+ bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), nums->len);
+ for (i = 0; i < nums->len; i++) {
+ MMModemBand band;
+
+ band = xact_num_to_band (g_array_index (nums, guint, i));
+ if (band != MM_MODEM_BAND_UNKNOWN)
+ g_array_append_val (bands, band);
+ }
+ g_array_unref (nums);
+
+ if (bands->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing current band list");
+ goto out;
+ }
+ }
+ }
+
+ /* success */
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ if (bands)
+ g_array_unref (bands);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (mode_out) {
+ g_assert (mode.allowed != MM_MODEM_MODE_NONE);
+ mode_out->allowed = mode.allowed;
+ mode_out->preferred = mode.preferred;
+ }
+
+ if (bands_out) {
+ g_assert (bands);
+ *bands_out = bands;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* AT+XACT=X command builder */
+
+static gboolean
+append_rat_value (GString *str,
+ MMModemMode mode,
+ GError **error)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xmm_modes); i++) {
+ if (xmm_modes[i] == mode) {
+ g_string_append_printf (str, "%u", i);
+ return TRUE;
+ }
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No AcT value matches requested mode");
+ return FALSE;
+}
+
+gchar *
+mm_xmm_build_xact_set_command (const MMModemModeCombination *mode,
+ const GArray *bands,
+ GError **error)
+{
+ GString *command;
+
+ /* At least one required */
+ g_assert (mode || bands);
+
+ /* Build command */
+ command = g_string_new ("+XACT=");
+
+ /* Mode is optional. If not given, we set all fields as empty */
+ if (mode) {
+ /* Allowed mask */
+ if (!append_rat_value (command, mode->allowed, error)) {
+ g_string_free (command, TRUE);
+ return NULL;
+ }
+
+ /* Preferred */
+ if (mode->preferred != MM_MODEM_MODE_NONE) {
+ g_string_append (command, ",");
+ if (!append_rat_value (command, mode->preferred, error)) {
+ g_string_free (command, TRUE);
+ return NULL;
+ }
+ /* We never set <PreferredAct2> because that is anyway not part of
+ * ModemManager's API. In modems with triple GSM/UMTS/LTE mode, the
+ * <PreferredAct2> is always the highest of the remaining ones. E.g.
+ * if "2G+3G+4G allowed with 2G preferred", the second preferred one
+ * would be 4G, not 3G. */
+ g_string_append (command, ",");
+ } else
+ g_string_append (command, ",,");
+ } else
+ g_string_append (command, ",,");
+
+ if (bands) {
+ g_string_append (command, ",");
+ /* Automatic band selection */
+ if (bands->len == 1 && g_array_index (bands, MMModemBand, 0) == MM_MODEM_BAND_ANY)
+ g_string_append (command, "0");
+ else {
+ guint i;
+
+ for (i = 0; i < bands->len; i++) {
+ MMModemBand band;
+ guint num;
+
+ band = g_array_index (bands, MMModemBand, i);
+ num = xact_band_to_num (band);
+ if (!num) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Band unsupported by this plugin: %s", mm_modem_band_get_string (band));
+ g_string_free (command, TRUE);
+ return NULL;
+ }
+
+ g_string_append_printf (command, "%s%u", i == 0 ? "" : ",", num);
+ }
+ }
+ }
+
+ return g_string_free (command, FALSE);
+}
+
+/*****************************************************************************/
+/* Get mode to apply when ANY */
+
+MMModemMode
+mm_xmm_get_modem_mode_any (const GArray *combinations)
+{
+ guint i;
+ MMModemMode any = MM_MODEM_MODE_NONE;
+ guint any_bits_set = 0;
+
+ for (i = 0; i < combinations->len; i++) {
+ MMModemModeCombination *combination;
+ guint bits_set;
+
+ combination = &g_array_index (combinations, MMModemModeCombination, i);
+ if (combination->preferred != MM_MODEM_MODE_NONE)
+ continue;
+ bits_set = mm_count_bits_set (combination->allowed);
+ if (bits_set > any_bits_set) {
+ any_bits_set = bits_set;
+ any = combination->allowed;
+ }
+ }
+
+ /* If combinations were processed via mm_xmm_parse_uact_test_response(),
+ * we're sure that there will be at least one combination with preferred
+ * 'none', so there must be some valid combination as result */
+ g_assert (any != MM_MODEM_MODE_NONE);
+ return any;
+}
+
+/*****************************************************************************/
+/* +XCESQ? response parser */
+
+gboolean
+mm_xmm_parse_xcesq_query_response (const gchar *response,
+ guint *out_rxlev,
+ guint *out_ber,
+ guint *out_rscp,
+ guint *out_ecn0,
+ guint *out_rsrq,
+ guint *out_rsrp,
+ gint *out_rssnr,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint rxlev = 99;
+ guint ber = 99;
+ guint rscp = 255;
+ guint ecn0 = 255;
+ guint rsrq = 255;
+ guint rsrp = 255;
+ gint rssnr = 255;
+ gboolean success = FALSE;
+
+ g_assert (out_rxlev);
+ g_assert (out_ber);
+ g_assert (out_rscp);
+ g_assert (out_ecn0);
+ g_assert (out_rsrq);
+ g_assert (out_rsrp);
+ g_assert (out_rssnr);
+
+ /* Response may be e.g.:
+ * +XCESQ: 0,99,99,255,255,24,51,18
+ * +XCESQ: 0,99,99,46,31,255,255,255
+ * +XCESQ: 0,99,99,255,255,17,45,-2
+ */
+ r = g_regex_new ("\\+XCESQ: (\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(-?\\d+)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ /* Ignore "n" value */
+ if (!mm_get_uint_from_match_info (match_info, 2, &rxlev)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RXLEV");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 3, &ber)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BER");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 4, &rscp)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 5, &ecn0)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read Ec/N0");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 6, &rsrq)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 7, &rsrp)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP");
+ goto out;
+ }
+ if (!mm_get_int_from_match_info (match_info, 8, &rssnr)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSSNR");
+ goto out;
+ }
+ success = TRUE;
+ }
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (!success) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +XCESQ response: %s", response);
+ return FALSE;
+ }
+
+ *out_rxlev = rxlev;
+ *out_ber = ber;
+ *out_rscp = rscp;
+ *out_ecn0 = ecn0;
+ *out_rsrq = rsrq;
+ *out_rsrp = rsrp;
+ *out_rssnr = rssnr;
+ return TRUE;
+}
+
+static gboolean
+rssnr_level_to_rssnr (gint rssnr_level,
+ gpointer log_object,
+ gdouble *out_rssnr)
+{
+ if (rssnr_level <= 100 &&
+ rssnr_level >= -100) {
+ *out_rssnr = rssnr_level / 2.0;
+ return TRUE;
+ }
+
+ if (rssnr_level != 255)
+ mm_obj_warn (log_object, "unexpected RSSNR level: %u", rssnr_level);
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* Get extended signal information */
+
+gboolean
+mm_xmm_xcesq_response_to_signal_info (const gchar *response,
+ gpointer log_object,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ GError **error)
+{
+ guint rxlev = 0;
+ guint ber = 0;
+ guint rscp_level = 0;
+ guint ecn0_level = 0;
+ guint rsrq_level = 0;
+ guint rsrp_level = 0;
+ gint rssnr_level = 0;
+ gdouble rssi = MM_SIGNAL_UNKNOWN;
+ gdouble rscp = MM_SIGNAL_UNKNOWN;
+ gdouble ecio = MM_SIGNAL_UNKNOWN;
+ gdouble rsrq = MM_SIGNAL_UNKNOWN;
+ gdouble rsrp = MM_SIGNAL_UNKNOWN;
+ gdouble rssnr = MM_SIGNAL_UNKNOWN;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+
+ if (!mm_xmm_parse_xcesq_query_response (response,
+ &rxlev, &ber,
+ &rscp_level, &ecn0_level,
+ &rsrq_level, &rsrp_level,
+ &rssnr_level, error))
+ return FALSE;
+
+ /* GERAN RSSI */
+ if (mm_3gpp_rxlev_to_rssi (rxlev, log_object, &rssi)) {
+ gsm = mm_signal_new ();
+ mm_signal_set_rssi (gsm, rssi);
+ }
+
+ /* ignore BER */
+
+ /* UMTS RSCP */
+ if (mm_3gpp_rscp_level_to_rscp (rscp_level, log_object, &rscp)) {
+ umts = mm_signal_new ();
+ mm_signal_set_rscp (umts, rscp);
+ }
+
+ /* UMTS EcIo (assumed EcN0) */
+ if (mm_3gpp_ecn0_level_to_ecio (ecn0_level, log_object, &ecio)) {
+ if (!umts)
+ umts = mm_signal_new ();
+ mm_signal_set_ecio (umts, ecio);
+ }
+
+ /* Calculate RSSI if we have ecio and rscp */
+ if (umts && ecio != -G_MAXDOUBLE && rscp != -G_MAXDOUBLE) {
+ mm_signal_set_rssi (umts, rscp - ecio);
+ }
+
+ /* LTE RSRQ */
+ if (mm_3gpp_rsrq_level_to_rsrq (rsrq_level, log_object, &rsrq)) {
+ lte = mm_signal_new ();
+ mm_signal_set_rsrq (lte, rsrq);
+ }
+
+ /* LTE RSRP */
+ if (mm_3gpp_rsrp_level_to_rsrp (rsrp_level, log_object, &rsrp)) {
+ if (!lte)
+ lte = mm_signal_new ();
+ mm_signal_set_rsrp (lte, rsrp);
+ }
+
+ /* LTE RSSNR */
+ if (rssnr_level_to_rssnr (rssnr_level, log_object, &rssnr)) {
+ if (!lte)
+ lte = mm_signal_new ();
+ mm_signal_set_snr (lte, rssnr);
+ }
+
+ if (!gsm && !umts && !lte) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't build detailed signal info");
+ return FALSE;
+ }
+
+ if (out_gsm)
+ *out_gsm = gsm;
+ if (out_umts)
+ *out_umts = umts;
+ if (out_lte)
+ *out_lte = lte;
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* AT+XLCSLSR=? response parser */
+
+static gboolean
+number_group_contains_value (const gchar *group,
+ const gchar *group_name,
+ guint value,
+ GError **error)
+{
+ GArray *aux;
+ guint i;
+ gboolean found;
+
+ aux = mm_parse_uint_list (group, NULL);
+ if (!aux) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported +XLCSLSR format: invalid %s field format", group_name);
+ return FALSE;
+ }
+
+ found = FALSE;
+ for (i = 0; i < aux->len; i++) {
+ guint value_i;
+
+ value_i = g_array_index (aux, guint, i);
+ if (value == value_i) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ g_array_unref (aux);
+ return found;
+}
+
+gboolean
+mm_xmm_parse_xlcslsr_test_response (const gchar *response,
+ gboolean *transport_protocol_invalid_supported,
+ gboolean *transport_protocol_supl_supported,
+ gboolean *standalone_position_mode_supported,
+ gboolean *ms_assisted_based_position_mode_supported,
+ gboolean *loc_response_type_nmea_supported,
+ gboolean *gnss_type_gps_glonass_supported,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ gchar **groups = NULL;
+ GError *inner_error = NULL;
+
+ /*
+ * AT+XLCSLSR=?
+ * +XLCSLSR:(0-2),(0-3), ,(0,1), ,(0,1),(0 -7200),(0-255),(0-1),(0-2),(1-256),(0,1)
+ * transport_protocol: 2 (invalid) or 1 (supl)
+ * pos_mode: 3 (standalone) or 2 (ms assisted/based)
+ * client_id: <empty>
+ * client_id_type: <empty>
+ * mlc_number: <empty>
+ * mlc_number_type: <empty>
+ * interval: 1 (seconds)
+ * service_type_id: <empty>
+ * pseudonym_indicator: <empty>
+ * loc_response_type: 1 (NMEA strings)
+ * nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG)
+ * gnss_type: 0 (GPS or GLONASS)
+ */
+ response = mm_strip_tag (response, "+XLCSLSR:");
+ groups = mm_split_string_groups (response);
+
+ /* We expect 12 groups */
+ if (g_strv_length (groups) < 12) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported +XLCSLSR format: expected 12 fields");
+ goto out;
+ }
+
+ if (transport_protocol_invalid_supported) {
+ *transport_protocol_invalid_supported = number_group_contains_value (groups[0],
+ "transport protocol",
+ 2, /* invalid */
+ &inner_error);
+ if (inner_error)
+ goto out;
+ }
+
+ if (transport_protocol_supl_supported) {
+ *transport_protocol_supl_supported = number_group_contains_value (groups[0],
+ "transport protocol",
+ 1, /* supl */
+ &inner_error);
+ if (inner_error)
+ goto out;
+ }
+
+ if (standalone_position_mode_supported) {
+ *standalone_position_mode_supported = number_group_contains_value (groups[1],
+ "position mode",
+ 3, /* standalone */
+ &inner_error);
+ if (inner_error)
+ goto out;
+ }
+
+ if (ms_assisted_based_position_mode_supported) {
+ *ms_assisted_based_position_mode_supported = number_group_contains_value (groups[1],
+ "position mode",
+ 2, /* ms assisted/based */
+ &inner_error);
+ if (inner_error)
+ goto out;
+ }
+
+ if (loc_response_type_nmea_supported) {
+ *loc_response_type_nmea_supported = number_group_contains_value (groups[9],
+ "location response type",
+ 1, /* NMEA */
+ &inner_error);
+ if (inner_error)
+ goto out;
+ }
+
+ if (gnss_type_gps_glonass_supported) {
+ *gnss_type_gps_glonass_supported = number_group_contains_value (groups[11],
+ "gnss type",
+ 0, /* GPS/GLONASS */
+ &inner_error);
+ if (inner_error)
+ goto out;
+ }
+
+ ret = TRUE;
+
+ out:
+ g_strfreev (groups);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* AT+XLCSSLP? response parser */
+
+gboolean
+mm_xmm_parse_xlcsslp_query_response (const gchar *response,
+ gchar **supl_address,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ gchar *address = NULL;
+ guint port = 0;
+
+ /*
+ * E.g.:
+ * +XLCSSLP:1,"www.spirent-lcs.com",7275
+ */
+
+ r = g_regex_new ("\\+XLCSSLP:\\s*(\\d+),([^,]*),(\\d+)(?:\\r\\n)?",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ guint type;
+
+ /* We only support types 0 (IPv4) and 1 (FQDN) */
+ mm_get_uint_from_match_info (match_info, 1, &type);
+ if (type != 0 && type != 1) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported SUPL server address type (%u) in response: %s", type, response);
+ goto out;
+ }
+
+ address = mm_get_string_unquoted_from_match_info (match_info, 2);
+ mm_get_uint_from_match_info (match_info, 3, &port);
+ if (!port) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid SUPL address port number in response: %s", response);
+ goto out;
+ }
+ }
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (supl_address)
+ *supl_address = g_strdup_printf ("%s:%u", address, port);
+ g_free (address);
+
+ return TRUE;
+}
diff --git a/plugins/xmm/mm-modem-helpers-xmm.h b/plugins/xmm/mm-modem-helpers-xmm.h
new file mode 100644
index 00000000..a18f0667
--- /dev/null
+++ b/plugins/xmm/mm-modem-helpers-xmm.h
@@ -0,0 +1,75 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_MODEM_HELPERS_XMM_H
+#define MM_MODEM_HELPERS_XMM_H
+
+#include <glib.h>
+#include <ModemManager.h>
+
+/* AT+XACT=? response parser */
+gboolean mm_xmm_parse_xact_test_response (const gchar *response,
+ gpointer logger,
+ GArray **modes_out,
+ GArray **bands_out,
+ GError **error);
+
+/* AT+XACT? response parser */
+gboolean mm_xmm_parse_xact_query_response (const gchar *response,
+ MMModemModeCombination *mode_out,
+ GArray **bands_out,
+ GError **error);
+
+/* AT+XACT=X command builder */
+gchar *mm_xmm_build_xact_set_command (const MMModemModeCombination *mode,
+ const GArray *bands,
+ GError **error);
+
+/* Mode to apply when ANY */
+MMModemMode mm_xmm_get_modem_mode_any (const GArray *combinations);
+
+gboolean mm_xmm_parse_xcesq_query_response (const gchar *response,
+ guint *out_rxlev,
+ guint *out_ber,
+ guint *out_rscp,
+ guint *out_ecn0,
+ guint *out_rsrq,
+ guint *out_rsrp,
+ gint *out_rssnr,
+ GError **error);
+
+gboolean mm_xmm_xcesq_response_to_signal_info (const gchar *response,
+ gpointer log_object,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ GError **error);
+
+/* AT+XLCSLSR=? response parser */
+gboolean mm_xmm_parse_xlcslsr_test_response (const gchar *response,
+ gboolean *transport_protocol_invalid_supported,
+ gboolean *transport_protocol_supl_supported,
+ gboolean *standalone_position_mode_supported,
+ gboolean *ms_assisted_based_position_mode_supported,
+ gboolean *loc_response_type_nmea_supported,
+ gboolean *gnss_type_gps_glonass_supported,
+ GError **error);
+
+/* AT+XLCSSLP? response parser */
+gboolean mm_xmm_parse_xlcsslp_query_response (const gchar *response,
+ gchar **supl_address,
+ GError **error);
+
+#endif /* MM_MODEM_HELPERS_XMM_H */
diff --git a/plugins/xmm/mm-shared-xmm.c b/plugins/xmm/mm-shared-xmm.c
new file mode 100644
index 00000000..2bf5e8e2
--- /dev/null
+++ b/plugins/xmm/mm-shared-xmm.c
@@ -0,0 +1,1553 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <arpa/inet.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-location.h"
+#include "mm-base-modem.h"
+#include "mm-base-modem-at.h"
+#include "mm-shared-xmm.h"
+#include "mm-modem-helpers-xmm.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "shared-xmm-private-tag"
+static GQuark private_quark;
+
+typedef enum {
+ GPS_ENGINE_STATE_OFF,
+ GPS_ENGINE_STATE_STANDALONE,
+ GPS_ENGINE_STATE_AGPS_MSA,
+ GPS_ENGINE_STATE_AGPS_MSB,
+} GpsEngineState;
+
+typedef struct {
+ /* Broadband modem class support */
+ MMBroadbandModemClass *broadband_modem_class_parent;
+
+ /* Modem interface support */
+ GArray *supported_modes;
+ GArray *supported_bands;
+ MMModemMode allowed_modes;
+
+ /* Location interface support */
+ MMIfaceModemLocation *iface_modem_location_parent;
+ MMModemLocationSource supported_sources;
+ MMModemLocationSource enabled_sources;
+ GpsEngineState gps_engine_state;
+ MMPortSerialAt *gps_port;
+ GRegex *xlsrstop_regex;
+ GRegex *nmea_regex;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_clear_object (&priv->gps_port);
+ if (priv->supported_modes)
+ g_array_unref (priv->supported_modes);
+ if (priv->supported_bands)
+ g_array_unref (priv->supported_bands);
+ g_regex_unref (priv->xlsrstop_regex);
+ g_regex_unref (priv->nmea_regex);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMSharedXmm *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ priv->gps_engine_state = GPS_ENGINE_STATE_OFF;
+
+ /* Setup regex for URCs */
+ priv->xlsrstop_regex = g_regex_new ("\\r\\n\\+XLSRSTOP:(.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ priv->nmea_regex = g_regex_new ("(?:\\r\\n)?(?:\\r\\n)?(\\$G.*)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ /* Setup parent class' MMBroadbandModemClass */
+ g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class);
+ priv->broadband_modem_class_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_broadband_modem_class (self);
+
+ /* Setup parent class' MMIfaceModemLocation */
+ g_assert (MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface);
+ priv->iface_modem_location_parent = MM_SHARED_XMM_GET_INTERFACE (self)->peek_parent_location_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+/*****************************************************************************/
+/* Supported modes/bands (Modem interface) */
+
+GArray *
+mm_shared_xmm_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ Private *priv;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return NULL;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->supported_modes);
+ return g_array_ref (priv->supported_modes);
+}
+
+GArray *
+mm_shared_xmm_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ Private *priv;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return NULL;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->supported_bands);
+ return g_array_ref (priv->supported_bands);
+}
+
+static void
+xact_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response ||
+ !mm_xmm_parse_xact_test_response (response,
+ self,
+ &priv->supported_modes,
+ &priv->supported_bands,
+ &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_load_supported_modes_bands (GTask *task)
+{
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (g_task_get_source_object (task)),
+ "+XACT=?",
+ 3,
+ TRUE, /* allow caching */
+ (GAsyncReadyCallback)xact_test_ready,
+ task);
+}
+
+void
+mm_shared_xmm_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!priv->supported_modes) {
+ common_load_supported_modes_bands (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!priv->supported_bands) {
+ common_load_supported_modes_bands (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Current modes (Modem interface) */
+
+gboolean
+mm_shared_xmm_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ MMModemModeCombination *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ *allowed = result->allowed;
+ *preferred = result->preferred;
+ g_free (result);
+ return TRUE;
+}
+
+static void
+xact_query_modes_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+ MMModemModeCombination *result;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ result = g_new0 (MMModemModeCombination, 1);
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_xmm_parse_xact_query_response (response, result, NULL, &error)) {
+ priv->allowed_modes = MM_MODEM_MODE_NONE;
+ g_free (result);
+ g_task_return_error (task, error);
+ } else {
+ priv->allowed_modes = result->allowed;
+ g_task_return_pointer (task, result, g_free);
+ }
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+XACT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xact_query_modes_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Current bands (Modem interface) */
+
+GArray *
+mm_shared_xmm_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+
+static void
+xact_query_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ GArray *result = NULL;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response ||
+ !mm_xmm_parse_xact_query_response (response, NULL, &result, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, result, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+XACT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xact_query_bands_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set current modes (Modem interface) */
+
+gboolean
+mm_shared_xmm_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xact_set_modes_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMModemModeCombination mode;
+ gchar *command;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (allowed != MM_MODEM_MODE_ANY) {
+ mode.allowed = allowed;
+ mode.preferred = preferred;
+ } else {
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ mode.allowed = mm_xmm_get_modem_mode_any (priv->supported_modes);
+ mode.preferred = MM_MODEM_MODE_NONE;
+ }
+
+ command = mm_xmm_build_xact_set_command (&mode, NULL, &error);
+ if (!command) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)xact_set_modes_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* Set current bands (Modem interface) */
+
+gboolean
+mm_shared_xmm_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xact_set_bands_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static gchar *
+validate_and_build_command_set_current_bands (MMSharedXmm *self,
+ const GArray *bands_array,
+ const GArray *supported_modes,
+ MMModemMode allowed_modes,
+ GError **error)
+{
+ gboolean band_2g_found = FALSE;
+ gboolean band_3g_found = FALSE;
+ gboolean band_4g_found = FALSE;
+ GArray *unapplied_bands;
+ GError *inner_error = NULL;
+ guint i;
+
+ /* ANY applies only to the currently selected modes */
+ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ MMModemModeCombination mode;
+ MMModemMode unapplied;
+
+ /* If we are enabling automatic band selection to a mode combination that does not include
+ * all supported modes, warn about it because automatic band selection wouldn't be executed
+ * for the non-selected modes.
+ *
+ * This is a known limitation of the modem firmware.
+ */
+ unapplied = mm_xmm_get_modem_mode_any (supported_modes) & ~(allowed_modes);
+ if (unapplied != MM_MODEM_MODE_NONE) {
+ g_autofree gchar *str = NULL;
+
+ str = mm_modem_mode_build_string_from_mask (unapplied);
+ mm_obj_warn (self, "automatic band selection not applied to non-current modes %s", str);
+ }
+
+ /* Nothing else to validate, go build the command right away */
+
+ /* We must create the set command with an explicit set of allowed modes.
+ * We pass NONE as preferred, but that WON'T change the currently selected preferred mode,
+ * it will be ignored when the command is processed as an empty field will be given */
+ mode.allowed = allowed_modes;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ return mm_xmm_build_xact_set_command (&mode, bands_array, error);
+ }
+
+ unapplied_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
+ for (i = 0; i < bands_array->len; i++) {
+ MMModemBand band;
+
+ band = g_array_index (bands_array, MMModemBand, i);
+ if (mm_common_band_is_eutran (band)) {
+ band_4g_found = TRUE;
+ if (!(allowed_modes & MM_MODEM_MODE_4G))
+ g_array_append_val (unapplied_bands, band);
+ }
+ if (mm_common_band_is_utran (band)) {
+ band_3g_found = TRUE;
+ if (!(allowed_modes & MM_MODEM_MODE_3G))
+ g_array_append_val (unapplied_bands, band);
+ }
+ if (mm_common_band_is_gsm (band)) {
+ band_2g_found = TRUE;
+ if (!(allowed_modes & MM_MODEM_MODE_2G))
+ g_array_append_val (unapplied_bands, band);
+ }
+ }
+
+ /* If 2G selected, there must be at least one 2G band */
+ if ((allowed_modes & MM_MODEM_MODE_2G) && !band_2g_found) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "At least one GSM band is required when 2G mode is allowed");
+ goto out;
+ }
+
+ /* If 3G selected, there must be at least one 3G band */
+ if ((allowed_modes & MM_MODEM_MODE_3G) && !band_3g_found) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "At least one UTRAN band is required when 3G mode is allowed");
+ goto out;
+ }
+
+ /* If 4G selected, there must be at least one 4G band */
+ if ((allowed_modes & MM_MODEM_MODE_4G) && !band_4g_found) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "At least one E-UTRAN band is required when 4G mode is allowed");
+ goto out;
+ }
+
+ /* Don't try to modify bands for modes that are not enabled */
+ if (unapplied_bands->len > 0) {
+ gchar *str;
+
+ str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)unapplied_bands->data, unapplied_bands->len);
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot update bands for modes not currently allowed: %s", str);
+ g_free (str);
+ goto out;
+ }
+
+out:
+ if (unapplied_bands)
+ g_array_unref (unapplied_bands);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ return mm_xmm_build_xact_set_command (NULL, bands_array, error);
+}
+
+void
+mm_shared_xmm_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *command = NULL;
+ GError *error = NULL;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Setting bands requires additional validation rules based on the
+ * currently selected list of allowed modes */
+ priv = get_private (MM_SHARED_XMM (self));
+ if (priv->allowed_modes == MM_MODEM_MODE_NONE) {
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot set bands if allowed modes are unknown");
+ goto out;
+ }
+
+ command = validate_and_build_command_set_current_bands (MM_SHARED_XMM (self),
+ bands_array,
+ priv->supported_modes,
+ priv->allowed_modes,
+ &error);
+
+out:
+ if (!command) {
+ g_assert (error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ command,
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)xact_set_bands_ready,
+ task);
+ g_free (command);
+}
+
+/*****************************************************************************/
+/* Power state loading (Modem interface) */
+
+MMModemPowerState
+mm_shared_xmm_load_power_state_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ guint state;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+
+ switch (state) {
+ case 1:
+ return MM_MODEM_POWER_STATE_ON;
+ case 4:
+ return MM_MODEM_POWER_STATE_LOW;
+ default:
+ break;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown +CFUN state: %u", state);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+}
+
+void
+mm_shared_xmm_load_power_state (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CFUN?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Modem power up/down/off (Modem interface) */
+
+static gboolean
+common_modem_power_operation_finish (MMSharedXmm *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+power_operation_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_modem_power_operation (MMSharedXmm *self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ command,
+ 30,
+ FALSE,
+ (GAsyncReadyCallback) power_operation_ready,
+ task);
+}
+
+gboolean
+mm_shared_xmm_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=16", callback, user_data);
+}
+
+gboolean
+mm_shared_xmm_power_off_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CPWROFF", callback, user_data);
+}
+
+gboolean
+mm_shared_xmm_power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=4", callback, user_data);
+}
+
+gboolean
+mm_shared_xmm_power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_modem_power_operation_finish (MM_SHARED_XMM (self), res, error);
+}
+
+void
+mm_shared_xmm_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_modem_power_operation (MM_SHARED_XMM (self), "+CFUN=1", callback, user_data);
+}
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
+
+gboolean
+mm_shared_xmm_signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+void
+mm_shared_xmm_signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+XCESQ=?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load extended signal information (Signal interface) */
+
+gboolean
+mm_shared_xmm_signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
+{
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response || !mm_xmm_xcesq_response_to_signal_info (response, self, gsm, umts, lte, error))
+ return FALSE;
+
+ if (cdma)
+ *cdma = NULL;
+ if (evdo)
+ *evdo = NULL;
+ if (nr5g)
+ *nr5g = NULL;
+
+ return TRUE;
+}
+
+void
+mm_shared_xmm_signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+XCESQ?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load capabilities (Location interface) */
+
+MMModemLocationSource
+mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+xlcslsr_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+ gboolean transport_protocol_invalid_supported;
+ gboolean transport_protocol_supl_supported;
+ gboolean standalone_position_mode_supported;
+ gboolean ms_assisted_based_position_mode_supported;
+ gboolean loc_response_type_nmea_supported;
+ gboolean gnss_type_gps_glonass_supported;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ /* Recover parent sources */
+ sources = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response ||
+ !mm_xmm_parse_xlcslsr_test_response (response,
+ &transport_protocol_invalid_supported,
+ &transport_protocol_supl_supported,
+ &standalone_position_mode_supported,
+ &ms_assisted_based_position_mode_supported,
+ &loc_response_type_nmea_supported,
+ &gnss_type_gps_glonass_supported,
+ &error)) {
+ mm_obj_dbg (self, "XLCSLSR based GPS control unsupported: %s", error->message);
+ g_clear_error (&error);
+ } else if (!transport_protocol_invalid_supported ||
+ !standalone_position_mode_supported ||
+ !loc_response_type_nmea_supported ||
+ !gnss_type_gps_glonass_supported) {
+ mm_obj_dbg (self, "XLCSLSR based GPS control unsupported: protocol invalid %s, standalone %s, nmea %s, gps/glonass %s",
+ transport_protocol_invalid_supported ? "supported" : "unsupported",
+ standalone_position_mode_supported ? "supported" : "unsupported",
+ loc_response_type_nmea_supported ? "supported" : "unsupported",
+ gnss_type_gps_glonass_supported ? "supported" : "unsupported");
+ } else {
+ mm_obj_dbg (self, "XLCSLSR based GPS control supported");
+ priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW);
+
+ if (transport_protocol_supl_supported && ms_assisted_based_position_mode_supported) {
+ mm_obj_dbg (self, "XLCSLSR based A-GPS control supported");
+ priv->supported_sources |= (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB);
+ } else {
+ mm_obj_dbg (self, "XLCSLSR based A-GPS control unsupported: protocol supl %s, ms assisted/based %s",
+ transport_protocol_supl_supported ? "supported" : "unsupported",
+ ms_assisted_based_position_mode_supported ? "supported" : "unsupported");
+ }
+
+ sources |= priv->supported_sources;
+ }
+
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+static void
+run_xlcslsr_test (GTask *task)
+{
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (g_task_get_source_object (task)),
+ "+XLCSLSR=?",
+ 3,
+ TRUE, /* allow caching */
+ (GAsyncReadyCallback)xlcslsr_test_ready,
+ task);
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If parent already supports GPS sources, we won't do anything else */
+ if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ mm_obj_dbg (self, "no need to run XLCSLSR based location gathering");
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Cache sources supported by the parent */
+ g_task_set_task_data (task, GUINT_TO_POINTER (sources), NULL);
+ run_xlcslsr_test (task);
+}
+
+void
+mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_assert (priv->iface_modem_location_parent);
+
+ if (!priv->iface_modem_location_parent->load_capabilities ||
+ !priv->iface_modem_location_parent->load_capabilities_finish) {
+ /* no parent capabilities */
+ g_task_set_task_data (task, GUINT_TO_POINTER (MM_MODEM_LOCATION_SOURCE_NONE), NULL);
+ run_xlcslsr_test (task);
+ return;
+ }
+
+ priv->iface_modem_location_parent->load_capabilities (self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* GPS engine state selection */
+
+static void
+nmea_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMSharedXmm *self)
+{
+ gchar *trace;
+
+ trace = g_match_info_fetch (info, 1);
+ mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), trace);
+ g_free (trace);
+}
+
+static gboolean
+gps_engine_state_select_finish (MMSharedXmm *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xlcslsr_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GpsEngineState state;
+ const gchar *response;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ response = mm_base_modem_at_command_full_finish (self, res, &error);
+ if (!response) {
+ g_clear_object (&priv->gps_port);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ state = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ g_assert (priv->gps_port);
+ mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port,
+ priv->nmea_regex,
+ (MMPortSerialAtUnsolicitedMsgFn)nmea_received,
+ self,
+ NULL);
+ priv->gps_engine_state = state;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+gps_engine_start (GTask *task)
+{
+ GpsEngineState state;
+ MMSharedXmm *self;
+ Private *priv;
+ guint transport_protocol = 0;
+ guint pos_mode = 0;
+ gchar *cmd;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+ state = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* Look for an AT port to use for GPS. Prefer secondary port if there is one,
+ * otherwise use primary */
+ g_assert (!priv->gps_port);
+ priv->gps_port = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ if (!priv->gps_port) {
+ priv->gps_port = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ if (!priv->gps_port) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No valid port found to control GPS");
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ switch (state) {
+ case GPS_ENGINE_STATE_STANDALONE:
+ transport_protocol = 2;
+ pos_mode = 3;
+ break;
+ case GPS_ENGINE_STATE_AGPS_MSB:
+ transport_protocol = 1;
+ pos_mode = 1;
+ break;
+ case GPS_ENGINE_STATE_AGPS_MSA:
+ transport_protocol = 1;
+ pos_mode = 2;
+ break;
+ case GPS_ENGINE_STATE_OFF:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ /*
+ * AT+XLCSLSR
+ * transport_protocol: 2 (invalid) or 1 (supl)
+ * pos_mode: 3 (standalone), 1 (msb) or 2 (msa)
+ * client_id: <empty>
+ * client_id_type: <empty>
+ * mlc_number: <empty>
+ * mlc_number_type: <empty>
+ * interval: 1 (seconds)
+ * service_type_id: <empty>
+ * pseudonym_indicator: <empty>
+ * loc_response_type: 1 (NMEA strings)
+ * nmea_mask: 118 (01110110: GGA,GSA,GSV,RMC,VTG)
+ * gnss_type: 0 (GPS or GLONASS)
+ */
+ g_assert (priv->gps_port);
+ cmd = g_strdup_printf ("AT+XLCSLSR=%u,%u,,,,,1,,,1,118,0", transport_protocol, pos_mode);
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ priv->gps_port,
+ cmd,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)xlcslsr_ready,
+ task);
+ g_free (cmd);
+}
+
+static void
+xlsrstop_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GpsEngineState state;
+ GError *error = NULL;
+ Private *priv;
+
+ mm_base_modem_at_command_full_finish (self, res, &error);
+
+ priv = get_private (MM_SHARED_XMM (self));
+ state = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ g_assert (priv->gps_port);
+ mm_port_serial_at_add_unsolicited_msg_handler (priv->gps_port, priv->nmea_regex, NULL, NULL, NULL);
+ g_clear_object (&priv->gps_port);
+ priv->gps_engine_state = GPS_ENGINE_STATE_OFF;
+
+ /* If already reached requested state, we're done */
+ if (state == priv->gps_engine_state) {
+ /* If we had an error when requesting this specific state, report it */
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Ignore errors if the stop operation was an intermediate one */
+ g_clear_error (&error);
+
+ /* Otherwise, start with new state */
+ gps_engine_start (task);
+}
+
+static void
+gps_engine_stop (GTask *task)
+{
+ MMSharedXmm *self;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ g_assert (priv->gps_port);
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ priv->gps_port,
+ "+XLSRSTOP",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)xlsrstop_ready,
+ task);
+}
+
+static void
+gps_engine_state_select (MMSharedXmm *self,
+ GpsEngineState state,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (state), NULL);
+
+ priv = get_private (self);
+
+ /* If already in the requested state, we're done */
+ if (state == priv->gps_engine_state) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If states are different we always STOP first */
+ if (priv->gps_engine_state != GPS_ENGINE_STATE_OFF) {
+ gps_engine_stop (task);
+ return;
+ }
+
+ /* If GPS already stopped, go on to START right away */
+ g_assert (state != GPS_ENGINE_STATE_OFF);
+ gps_engine_start (task);
+}
+
+static GpsEngineState
+gps_engine_state_get_expected (MMModemLocationSource sources)
+{
+ /* If at lease one of GPS nmea/raw sources enabled, engine started */
+ if (sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
+ /* If MSA A-GPS is enabled, MSA mode */
+ if (sources & MM_MODEM_LOCATION_SOURCE_AGPS_MSA)
+ return GPS_ENGINE_STATE_AGPS_MSA;
+ /* If MSB A-GPS is enabled, MSB mode */
+ if (sources & MM_MODEM_LOCATION_SOURCE_AGPS_MSB)
+ return GPS_ENGINE_STATE_AGPS_MSB;
+ /* Otherwise, STANDALONE */
+ return GPS_ENGINE_STATE_STANDALONE;
+ }
+ /* If no GPS nmea/raw sources enabled, engine stopped */
+ return GPS_ENGINE_STATE_OFF;
+}
+
+/*****************************************************************************/
+/* Disable location gathering (Location interface) */
+
+gboolean
+mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_gps_engine_state_select_ready (MMSharedXmm *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!gps_engine_state_select_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv->enabled_sources &= ~source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_disable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent disable */
+ if (!(priv->supported_sources & source)) {
+ /* If disabling implemented by the parent, run it. */
+ if (priv->iface_modem_location_parent->disable_location_gathering &&
+ priv->iface_modem_location_parent->disable_location_gathering_finish) {
+ priv->iface_modem_location_parent->disable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_disable_location_gathering_ready,
+ task);
+ return;
+ }
+ /* Otherwise, we're done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB));
+
+ /* Update engine based on the expected sources */
+ gps_engine_state_select (MM_SHARED_XMM (self),
+ gps_engine_state_get_expected (priv->enabled_sources & ~source),
+ (GAsyncReadyCallback) disable_gps_engine_state_select_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable location gathering (Location interface) */
+
+gboolean
+mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+enable_gps_engine_state_select_ready (MMSharedXmm *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ if (!gps_engine_state_select_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv->enabled_sources |= source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_XMM (self));
+
+ g_assert (priv->iface_modem_location_parent);
+ if (!priv->iface_modem_location_parent->enable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->iface_modem_location_parent);
+
+ /* Only consider request if it applies to one of the sources we are
+ * supporting, otherwise run parent enable */
+ if (priv->iface_modem_location_parent->enable_location_gathering &&
+ priv->iface_modem_location_parent->enable_location_gathering_finish &&
+ !(priv->supported_sources & source)) {
+ priv->iface_modem_location_parent->enable_location_gathering (self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+ return;
+ }
+
+ /* We only expect GPS sources here */
+ g_assert (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB));
+
+ /* Update engine based on the expected sources */
+ gps_engine_state_select (MM_SHARED_XMM (self),
+ gps_engine_state_get_expected (priv->enabled_sources | source),
+ (GAsyncReadyCallback) enable_gps_engine_state_select_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Location: Load SUPL server */
+
+gchar *
+mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+xlcsslp_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ gchar *supl_address;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_xmm_parse_xlcsslp_query_response (response, &supl_address, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, supl_address, g_free);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+XLCSSLP?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xlcsslp_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+xlcsslp_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_xmm_location_set_supl_server (MMIfaceModemLocation *self,
+ const gchar *supl,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *cmd = NULL;
+ gchar *fqdn = NULL;
+ guint32 ip;
+ guint16 port;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_parse_supl_address (supl, &fqdn, &ip, &port, NULL);
+ g_assert (port);
+ if (fqdn)
+ cmd = g_strdup_printf ("+XLCSSLP=1,%s,%u", fqdn, port);
+ else if (ip) {
+ struct in_addr a = { .s_addr = ip };
+ gchar buf[INET_ADDRSTRLEN + 1] = { 0 };
+
+ /* we got 'ip' from inet_pton(), so this next step should always succeed */
+ g_assert (inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1));
+ cmd = g_strdup_printf ("+XLCSSLP=0,%s,%u", buf, port);
+ } else
+ g_assert_not_reached ();
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)xlcsslp_set_ready,
+ task);
+ g_free (cmd);
+ g_free (fqdn);
+}
+
+/*****************************************************************************/
+
+void
+mm_shared_xmm_setup_ports (MMBroadbandModem *self)
+{
+ Private *priv;
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ priv = get_private (MM_SHARED_XMM (self));
+ g_assert (priv->broadband_modem_class_parent);
+ g_assert (priv->broadband_modem_class_parent->setup_ports);
+
+ /* Parent setup first always */
+ priv->broadband_modem_class_parent->setup_ports (self);
+
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Setup primary and secondary ports */
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
+ if (!ports[i])
+ continue;
+
+ /* After running AT+XLSRSTOP we may get an unsolicited response
+ * reporting its status, we just ignore it. */
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ priv->xlsrstop_regex,
+ NULL, NULL, NULL);
+
+
+
+ /* make sure GPS is stopped in case it was left enabled */
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ports[i],
+ "+XLSRSTOP",
+ 3, FALSE, FALSE, NULL, NULL, NULL);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+shared_xmm_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_xmm_get_type (void)
+{
+ static GType shared_xmm_type = 0;
+
+ if (!G_UNLIKELY (shared_xmm_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedXmm), /* class_size */
+ shared_xmm_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_xmm_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedXmm", &info, 0);
+ g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM);
+ g_type_interface_add_prerequisite (shared_xmm_type, MM_TYPE_IFACE_MODEM_LOCATION);
+ }
+
+ return shared_xmm_type;
+}
diff --git a/plugins/xmm/mm-shared-xmm.h b/plugins/xmm/mm-shared-xmm.h
new file mode 100644
index 00000000..a1f51639
--- /dev/null
+++ b/plugins/xmm/mm-shared-xmm.h
@@ -0,0 +1,184 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SHARED_XMM_H
+#define MM_SHARED_XMM_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-location.h"
+
+#define MM_TYPE_SHARED_XMM (mm_shared_xmm_get_type ())
+#define MM_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm))
+#define MM_IS_SHARED_XMM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_XMM))
+#define MM_SHARED_XMM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_XMM, MMSharedXmm))
+
+typedef struct _MMSharedXmm MMSharedXmm;
+
+struct _MMSharedXmm {
+ GTypeInterface g_iface;
+
+ /* Peek broadband modem class of the parent class of the object */
+ MMBroadbandModemClass * (* peek_parent_broadband_modem_class) (MMSharedXmm *self);
+
+ /* Peek location interface of the parent class of the object */
+ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedXmm *self);
+};
+
+GType mm_shared_xmm_get_type (void);
+
+/* Shared XMM device setup */
+
+void mm_shared_xmm_setup_ports (MMBroadbandModem *self);
+
+/* Shared XMM device management support */
+
+void mm_shared_xmm_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_xmm_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error);
+void mm_shared_xmm_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_xmm_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_xmm_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_xmm_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_xmm_load_power_state (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemPowerState mm_shared_xmm_load_power_state_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_power_off_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_shared_xmm_signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_shared_xmm_signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error);
+void mm_shared_xmm_signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+void mm_shared_xmm_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemLocationSource mm_shared_xmm_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_disable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_location_load_supl_server (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *mm_shared_xmm_location_load_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_xmm_location_set_supl_server (MMIfaceModemLocation *self,
+ const gchar *supl,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_xmm_location_set_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_XMM_H */
diff --git a/plugins/xmm/mm-shared.c b/plugins/xmm/mm-shared.c
new file mode 100644
index 00000000..203f0fbb
--- /dev/null
+++ b/plugins/xmm/mm-shared.c
@@ -0,0 +1,20 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-shared.h"
+
+MM_SHARED_DEFINE_MAJOR_VERSION
+MM_SHARED_DEFINE_MINOR_VERSION
+MM_SHARED_DEFINE_NAME(Xmm)
diff --git a/plugins/xmm/tests/test-modem-helpers-xmm.c b/plugins/xmm/tests/test-modem-helpers-xmm.c
new file mode 100644
index 00000000..e40ffcab
--- /dev/null
+++ b/plugins/xmm/tests/test-modem-helpers-xmm.c
@@ -0,0 +1,780 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <locale.h>
+#include <math.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log-test.h"
+#include "mm-modem-helpers.h"
+#include "mm-modem-helpers-xmm.h"
+
+#define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \
+ g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
+
+/*****************************************************************************/
+/* Test XACT=? responses */
+
+static void
+validate_xact_test_response (const gchar *response,
+ const MMModemModeCombination *expected_modes,
+ guint n_expected_modes,
+ const MMModemBand *expected_bands,
+ guint n_expected_bands)
+{
+ GError *error = NULL;
+ GArray *modes = NULL;
+ GArray *bands = NULL;
+ gboolean ret;
+ guint i;
+
+ ret = mm_xmm_parse_xact_test_response (response, NULL, &modes, &bands, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpuint (modes->len, ==, n_expected_modes);
+ for (i = 0; i < modes->len; i++) {
+ MMModemModeCombination mode;
+ guint j;
+ gboolean found = FALSE;
+
+ mode = g_array_index (modes, MMModemModeCombination, i);
+ for (j = 0; !found && j < n_expected_modes; j++)
+ found = (mode.allowed == expected_modes[j].allowed && mode.preferred == expected_modes[j].preferred);
+ g_assert (found);
+ }
+ g_array_unref (modes);
+
+ g_assert_cmpuint (bands->len, ==, n_expected_bands);
+ for (i = 0; i < bands->len; i++) {
+ MMModemBand band;
+ guint j;
+ gboolean found = FALSE;
+
+ band = g_array_index (bands, MMModemBand, i);
+ for (j = 0; !found && j < n_expected_bands; j++)
+ found = (band == expected_bands[j]);
+ g_assert (found);
+ }
+ g_array_unref (bands);
+}
+
+static void
+test_xact_test_4g_only (void)
+{
+ const gchar *response =
+ "+XACT: "
+ "(0-6),(0-2),0,"
+ "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166";
+
+ static const MMModemModeCombination expected_modes[] = {
+ { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ };
+
+ static const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21,
+ MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38,
+ MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66
+ };
+
+ /* NOTE: 2G and 3G modes are reported in XACT but no 2G or 3G frequencies supported */
+ validate_xact_test_response (response,
+ expected_modes, G_N_ELEMENTS (expected_modes),
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_xact_test_3g_4g (void)
+{
+ const gchar *response =
+ "+XACT: "
+ "(0-6),(0-2),0,"
+ "1,2,4,5,8,"
+ "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166";
+
+ static const MMModemModeCombination expected_modes[] = {
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+ };
+
+ static const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21,
+ MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38,
+ MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66
+ };
+
+ /* NOTE: 2G modes are reported in XACT but no 2G frequencies supported */
+ validate_xact_test_response (response,
+ expected_modes, G_N_ELEMENTS (expected_modes),
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_xact_test_2g_3g_4g (void)
+{
+ const gchar *response =
+ "+XACT: "
+ "(0-6),(0-2),0,"
+ "900,1800,1900,850,"
+ "1,2,4,5,8,"
+ "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166";
+
+ static const MMModemModeCombination expected_modes[] = {
+ { MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_2G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_3G },
+ { MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G, MM_MODEM_MODE_4G },
+ };
+
+ static const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850,
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21,
+ MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38,
+ MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66
+ };
+
+ validate_xact_test_response (response,
+ expected_modes, G_N_ELEMENTS (expected_modes),
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+/*****************************************************************************/
+/* Test XACT? responses */
+
+static void
+validate_xact_query_response (const gchar *response,
+ const MMModemModeCombination *expected_mode,
+ const MMModemBand *expected_bands,
+ guint n_expected_bands)
+{
+ GError *error = NULL;
+ GArray *bands = NULL;
+ gboolean ret;
+ guint i;
+
+ MMModemModeCombination mode = {
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ };
+
+ ret = mm_xmm_parse_xact_query_response (response, &mode, &bands, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpuint (mode.allowed, ==, expected_mode->allowed);
+ g_assert_cmpuint (mode.preferred, ==, expected_mode->preferred);
+
+ g_assert_cmpuint (bands->len, ==, n_expected_bands);
+ for (i = 0; i < bands->len; i++) {
+ MMModemBand band;
+ guint j;
+ gboolean found = FALSE;
+
+ band = g_array_index (bands, MMModemBand, i);
+ for (j = 0; !found && j < n_expected_bands; j++)
+ found = (band == expected_bands[j]);
+ g_assert (found);
+ }
+ g_array_unref (bands);
+}
+
+static void
+test_xact_query_3g_only (void)
+{
+ const gchar *response =
+ "+XACT: "
+ "1,1,,"
+ "1,2,4,5,8,"
+ "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166";
+
+ static const MMModemModeCombination expected_mode = {
+ .allowed = MM_MODEM_MODE_3G,
+ .preferred = MM_MODEM_MODE_NONE
+ };
+
+ static const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21,
+ MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38,
+ MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66
+ };
+
+ validate_xact_query_response (response,
+ &expected_mode,
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+static void
+test_xact_query_3g_4g (void)
+{
+ const gchar *response =
+ "+XACT: "
+ "4,1,2,"
+ "1,2,4,5,8,"
+ "101,102,103,104,105,107,108,111,112,113,117,118,119,120,121,126,128,129,130,138,139,140,141,166";
+
+ static const MMModemModeCombination expected_mode = {
+ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_3G
+ };
+
+ static const MMModemBand expected_bands[] = {
+ MM_MODEM_BAND_UTRAN_1, MM_MODEM_BAND_UTRAN_2, MM_MODEM_BAND_UTRAN_4, MM_MODEM_BAND_UTRAN_5, MM_MODEM_BAND_UTRAN_8,
+ MM_MODEM_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_5,
+ MM_MODEM_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_13,
+ MM_MODEM_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_21,
+ MM_MODEM_BAND_EUTRAN_26, MM_MODEM_BAND_EUTRAN_28, MM_MODEM_BAND_EUTRAN_29, MM_MODEM_BAND_EUTRAN_30, MM_MODEM_BAND_EUTRAN_38,
+ MM_MODEM_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_66
+ };
+
+ validate_xact_query_response (response,
+ &expected_mode,
+ expected_bands, G_N_ELEMENTS (expected_bands));
+}
+
+/*****************************************************************************/
+
+#define XACT_SET_TEST_MAX_BANDS 6
+
+typedef struct {
+ MMModemMode allowed;
+ MMModemMode preferred;
+ MMModemBand bands[XACT_SET_TEST_MAX_BANDS];
+ const gchar *expected_command;
+} XactSetTest;
+
+static const XactSetTest set_tests[] = {
+ {
+ /* 2G-only, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=0,,"
+ },
+ {
+ /* 3G-only, no explicit bands */
+ .allowed = MM_MODEM_MODE_3G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=1,,"
+ },
+ {
+ /* 4G-only, no explicit bands */
+ .allowed = MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=2,,"
+ },
+ {
+ /* 2G+3G, none preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=3,,"
+ },
+ {
+ /* 2G+3G, 2G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .preferred = MM_MODEM_MODE_2G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=3,0,"
+ },
+ {
+ /* 2G+3G, 3G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ .preferred = MM_MODEM_MODE_3G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=3,1,"
+ },
+ {
+ /* 3G+4G, none preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=4,,"
+ },
+ {
+ /* 3G+4G, 3G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_3G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=4,1,"
+ },
+ {
+ /* 3G+4G, 4G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_4G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=4,2,"
+ },
+ {
+ /* 2G+4G, none preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=5,,"
+ },
+ {
+ /* 2G+4G, 2G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_2G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=5,0,"
+ },
+ {
+ /* 2G+4G, 4G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_4G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=5,2,"
+ },
+ {
+ /* 2G+3G+4G, none preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=6,,"
+ },
+ {
+ /* 2G+3G+4G, 2G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_2G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=6,0,"
+ },
+ {
+ /* 2G+3G+4G, 3G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_3G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=6,1,"
+ },
+ {
+ /* 2G+3G+4G, 4G preferred, no explicit bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_4G,
+ .bands = { [0] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=6,2,"
+ },
+ {
+ /* 2G bands, no explicit modes */
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_EGSM,
+ [1] = MM_MODEM_BAND_DCS,
+ [2] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=,,,900,1800"
+ },
+ {
+ /* 3G bands, no explicit modes */
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_UTRAN_1,
+ [1] = MM_MODEM_BAND_UTRAN_2,
+ [2] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=,,,1,2"
+ },
+ {
+ /* 4G bands, no explicit modes */
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_EUTRAN_1,
+ [1] = MM_MODEM_BAND_EUTRAN_2,
+ [2] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=,,,101,102"
+ },
+ {
+ /* 2G, 3G and 4G bands, no explicit modes */
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_EGSM,
+ [1] = MM_MODEM_BAND_DCS,
+ [2] = MM_MODEM_BAND_UTRAN_1,
+ [3] = MM_MODEM_BAND_UTRAN_2,
+ [4] = MM_MODEM_BAND_EUTRAN_1,
+ [5] = MM_MODEM_BAND_EUTRAN_2 },
+ .expected_command = "+XACT=,,,900,1800,1,2,101,102"
+ },
+ {
+ /* Auto bands, no explicit modes */
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ .bands = { [0] = MM_MODEM_BAND_ANY,
+ [1] = MM_MODEM_BAND_UNKNOWN },
+ .expected_command = "+XACT=,,,0"
+ },
+
+ {
+ /* 2G+3G+4G with 4G preferred, and 2G+3G+4G bands */
+ .allowed = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ .preferred = MM_MODEM_MODE_4G,
+ .bands = { [0] = MM_MODEM_BAND_EGSM,
+ [1] = MM_MODEM_BAND_DCS,
+ [2] = MM_MODEM_BAND_UTRAN_1,
+ [3] = MM_MODEM_BAND_UTRAN_2,
+ [4] = MM_MODEM_BAND_EUTRAN_1,
+ [5] = MM_MODEM_BAND_EUTRAN_2 },
+ .expected_command = "+XACT=6,2,,900,1800,1,2,101,102"
+ },
+};
+
+static void
+validate_xact_set_command (const MMModemMode allowed,
+ const MMModemMode preferred,
+ const MMModemBand *bands,
+ guint n_bands,
+ const gchar *expected_command)
+{
+ gchar *command;
+ MMModemModeCombination mode;
+ GArray *bandsarray = NULL;
+ GError *error = NULL;
+
+ if (n_bands)
+ bandsarray = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), n_bands), bands, n_bands);
+
+ mode.allowed = allowed;
+ mode.preferred = preferred;
+
+ command = mm_xmm_build_xact_set_command ((mode.allowed != MM_MODEM_MODE_NONE) ? &mode : NULL, bandsarray, &error);
+ g_assert_no_error (error);
+ g_assert (command);
+
+ g_assert_cmpstr (command, == , expected_command);
+
+ g_free (command);
+ if (bandsarray)
+ g_array_unref (bandsarray);
+}
+
+static void
+test_xact_set (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (set_tests); i++) {
+ guint n_bands = 0;
+ guint j;
+
+ for (j = 0; j < XACT_SET_TEST_MAX_BANDS; j++) {
+ if (set_tests[i].bands[j] != MM_MODEM_BAND_UNKNOWN)
+ n_bands++;
+ }
+
+ validate_xact_set_command (set_tests[i].allowed,
+ set_tests[i].preferred,
+ set_tests[i].bands,
+ n_bands,
+ set_tests[i].expected_command);
+ }
+}
+
+/*****************************************************************************/
+/* Test +XCESQ responses */
+
+typedef struct {
+ const gchar *str;
+
+ gboolean gsm_info;
+ guint rxlev;
+ gdouble rssi;
+ guint ber;
+
+ gboolean umts_info;
+ guint rscp_level;
+ gdouble rscp;
+ guint ecn0_level;
+ gdouble ecio;
+
+ gboolean lte_info;
+ guint rsrq_level;
+ gdouble rsrq;
+ guint rsrp_level;
+ gdouble rsrp;
+ gint rssnr_level;
+ gdouble rssnr;
+} XCesqResponseTest;
+
+static const XCesqResponseTest xcesq_response_tests[] = {
+ {
+ .str = "+XCESQ: 0,99,99,255,255,19,46,32",
+ .gsm_info = FALSE, .rxlev = 99, .ber = 99,
+ .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255,
+ .lte_info = TRUE, .rsrq_level = 19, .rsrq = -10.5, .rsrp_level = 46, .rsrp = -95.0, .rssnr_level = 32, .rssnr = 16.0
+ },
+ {
+ .str = "+XCESQ: 0,99,99,255,255,19,46,-32",
+ .gsm_info = FALSE, .rxlev = 99, .ber = 99,
+ .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255,
+ .lte_info = TRUE, .rsrq_level = 19, .rsrq = -10.5, .rsrp_level = 46, .rsrp = -95.0, .rssnr_level = -32, .rssnr = -16.0
+ },
+ {
+ .str = "+XCESQ: 0,99,99,255,255,16,47,28",
+ .gsm_info = FALSE, .rxlev = 99, .ber = 99,
+ .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255,
+ .lte_info = TRUE, .rsrq_level = 16, .rsrq = -12.0, .rsrp_level = 47, .rsrp = -94.0, .rssnr_level = 28, .rssnr = 14.0
+ },
+ {
+ .str = "+XCESQ: 0,99,99,41,29,255,255,255",
+ .gsm_info = FALSE, .rxlev = 99, .ber = 99,
+ .umts_info = TRUE, .rscp_level = 41, .rscp = -80.0, .ecn0_level = 29, .ecio = -10.0,
+ .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255, .rssnr_level = 255
+ },
+ {
+ .str = "+XCESQ: 0,10,6,255,255,255,255,255",
+ .gsm_info = TRUE, .rxlev = 10, .rssi = -101.0, .ber = 6,
+ .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255,
+ .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255, .rssnr_level = 255
+ }
+};
+
+static void
+test_xcesq_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xcesq_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint rxlev = G_MAXUINT;
+ guint ber = G_MAXUINT;
+ guint rscp = G_MAXUINT;
+ guint ecn0 = G_MAXUINT;
+ guint rsrq = G_MAXUINT;
+ guint rsrp = G_MAXUINT;
+ gint rssnr = G_MAXUINT;
+
+ success = mm_xmm_parse_xcesq_query_response (xcesq_response_tests[i].str,
+ &rxlev, &ber,
+ &rscp, &ecn0,
+ &rsrq, &rsrp,
+ &rssnr, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_assert_cmpuint (xcesq_response_tests[i].rxlev, ==, rxlev);
+ g_assert_cmpuint (xcesq_response_tests[i].ber, ==, ber);
+ g_assert_cmpuint (xcesq_response_tests[i].rscp_level, ==, rscp);
+ g_assert_cmpuint (xcesq_response_tests[i].ecn0_level, ==, ecn0);
+ g_assert_cmpuint (xcesq_response_tests[i].rsrq_level, ==, rsrq);
+ g_assert_cmpuint (xcesq_response_tests[i].rsrp_level, ==, rsrp);
+ g_assert_cmpuint (xcesq_response_tests[i].rssnr_level, ==, rssnr);
+ }
+}
+
+static void
+test_xcesq_response_to_signal (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xcesq_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+
+ success = mm_xmm_xcesq_response_to_signal_info (xcesq_response_tests[i].str,
+ NULL,
+ &gsm, &umts, &lte,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ if (xcesq_response_tests[i].gsm_info) {
+ g_assert (gsm);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), xcesq_response_tests[i].rssi, 0.1);
+ g_object_unref (gsm);
+ } else
+ g_assert (!gsm);
+
+ if (xcesq_response_tests[i].umts_info) {
+ g_assert (umts);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), xcesq_response_tests[i].rscp, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), xcesq_response_tests[i].ecio, 0.1);
+ g_object_unref (umts);
+ } else
+ g_assert (!umts);
+
+ if (xcesq_response_tests[i].lte_info) {
+ g_assert (lte);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), xcesq_response_tests[i].rsrq, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), xcesq_response_tests[i].rsrp, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_snr (lte), xcesq_response_tests[i].rssnr, 0.1);
+ g_object_unref (lte);
+ } else
+ g_assert (!lte);
+ }
+}
+
+/*****************************************************************************/
+/* AT+XLCSLSR=? response parser */
+
+typedef struct {
+ const gchar *response;
+ gboolean expected_transport_protocol_invalid_supported;
+ gboolean expected_transport_protocol_supl_supported;
+ gboolean expected_standalone_position_mode_supported;
+ gboolean expected_ms_assisted_based_position_mode_supported;
+ gboolean expected_loc_response_type_nmea_supported;
+ gboolean expected_gnss_type_gps_glonass_supported;
+} XlcslsrTest;
+
+static XlcslsrTest xlcslsr_tests[] = {
+ {
+ "+XLCSLSR:(0-2),(0-3), ,(0-1), ,(0-1),(0-7200),(0-255),(0-1),(0-2),(1-256),(0-1)",
+ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
+ },
+ {
+ "+XLCSLSR:(0,1,2),(0,1,2,3), ,(0,1), ,(0,1),(0-7200),(0-255),(0,1),(0,1,2),(1-256),(0,1)",
+ TRUE, TRUE, TRUE, TRUE, TRUE, TRUE,
+ },
+ {
+ "+XLCSLSR:(0-1),(0-2), ,(0,1), ,(0,1),(0 -7200),(0-255),(0-1),(0),(1-256),(1)",
+ FALSE, TRUE, FALSE, TRUE, FALSE, FALSE
+ },
+};
+
+static void
+test_xlcslsr_test (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xlcslsr_tests); i++) {
+ GError *error = NULL;
+ gboolean ret;
+ gboolean transport_protocol_invalid_supported;
+ gboolean transport_protocol_supl_supported;
+ gboolean standalone_position_mode_supported;
+ gboolean ms_assisted_based_position_mode_supported;
+ gboolean loc_response_type_nmea_supported;
+ gboolean gnss_type_gps_glonass_supported;
+
+ ret = mm_xmm_parse_xlcslsr_test_response (xlcslsr_tests[i].response,
+ &transport_protocol_invalid_supported,
+ &transport_protocol_supl_supported,
+ &standalone_position_mode_supported,
+ &ms_assisted_based_position_mode_supported,
+ &loc_response_type_nmea_supported,
+ &gnss_type_gps_glonass_supported,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert (transport_protocol_invalid_supported == xlcslsr_tests[i].expected_transport_protocol_invalid_supported);
+ g_assert (transport_protocol_supl_supported == xlcslsr_tests[i].expected_transport_protocol_supl_supported);
+ g_assert (standalone_position_mode_supported == xlcslsr_tests[i].expected_standalone_position_mode_supported);
+ g_assert (ms_assisted_based_position_mode_supported == xlcslsr_tests[i].expected_ms_assisted_based_position_mode_supported);
+ g_assert (loc_response_type_nmea_supported == xlcslsr_tests[i].expected_loc_response_type_nmea_supported);
+ g_assert (gnss_type_gps_glonass_supported == xlcslsr_tests[i].expected_gnss_type_gps_glonass_supported);
+ }
+}
+
+/*****************************************************************************/
+/* AT+XLCSSLP? response parser */
+
+typedef struct {
+ const gchar *response;
+ const gchar *expected;
+} XlcsslpQuery;
+
+static XlcsslpQuery xlcsslp_queries[] = {
+ {
+ "+XLCSSLP:1,\"www.spirent-lcs.com\",7275",
+ "www.spirent-lcs.com:7275"
+ },
+ {
+ "+XLCSSLP:0,\"123.123.123.123\",7275",
+ "123.123.123.123:7275"
+ },
+};
+
+static void
+test_xlcsslp_queries (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (xlcsslp_queries); i++) {
+ GError *error = NULL;
+ gchar *supl_server = NULL;
+ gboolean ret;
+
+ ret = mm_xmm_parse_xlcsslp_query_response (xlcsslp_queries[i].response,
+ &supl_server,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpstr (supl_server, ==, xlcsslp_queries[i].expected);
+ g_free (supl_server);
+ }
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/xmm/xact/test/4g-only", test_xact_test_4g_only);
+ g_test_add_func ("/MM/xmm/xact/test/3g-4g", test_xact_test_3g_4g);
+ g_test_add_func ("/MM/xmm/xact/test/2g-3g-4g", test_xact_test_2g_3g_4g);
+
+ g_test_add_func ("/MM/xmm/xact/query/3g-only", test_xact_query_3g_only);
+ g_test_add_func ("/MM/xmm/xact/query/3g-4g", test_xact_query_3g_4g);
+
+ g_test_add_func ("/MM/xmm/xact/set", test_xact_set);
+
+ g_test_add_func ("/MM/xmm/xcesq/query_response", test_xcesq_response);
+ g_test_add_func ("/MM/xmm/xcesq/query_response_to_signal", test_xcesq_response_to_signal);
+
+ g_test_add_func ("/MM/xmm/xlcslsr/test", test_xlcslsr_test);
+
+ g_test_add_func ("/MM/xmm/xlcsslp/query", test_xlcsslp_queries);
+
+ return g_test_run ();
+}
diff --git a/plugins/zte/77-mm-zte-port-types.rules b/plugins/zte/77-mm-zte-port-types.rules
index f9d62d8c..46a83aca 100644
--- a/plugins/zte/77-mm-zte-port-types.rules
+++ b/plugins/zte/77-mm-zte-port-types.rules
@@ -1,193 +1,197 @@
# do not edit this file, it will be overwritten on update
-ACTION!="add|change|move", GOTO="mm_zte_port_types_end"
-SUBSYSTEM!="tty", GOTO="mm_zte_port_types_end"
-
-SUBSYSTEMS=="usb", ATTRS{idVendor}=="19d2", GOTO="mm_zte_port_types_vendorcheck"
+ACTION!="add|change|move|bind", GOTO="mm_zte_port_types_end"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="19d2", GOTO="mm_zte_port_types"
GOTO="mm_zte_port_types_end"
-LABEL="mm_zte_port_types_vendorcheck"
+LABEL="mm_zte_port_types"
SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0001", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
+
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0002", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0004", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0005", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0006", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0007", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0008", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0009", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="000A", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0012", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0015", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0016", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0017", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0018", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0019", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0021", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0024", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0025", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0030", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0031", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0033", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0037", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0039", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0042", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0043", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0048", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0049", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0052", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0054", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0055", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0058", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0058", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0057", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0058", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0058", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0061", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0063", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0064", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0078", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0078", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0066", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0078", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0078", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0091", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0091", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0082", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0091", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0091", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0104", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0106", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0108", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0113", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0117", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0118", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0122", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0122", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0121", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0122", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0122", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0123", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="05", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0124", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0125", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0126", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0156", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0156", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="0128", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1007", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1008", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1010", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1254", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1268", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1268", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="02", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="00", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="1515", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2002", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="03", ENV{ID_MM_ZTE_PORT_TYPE_MODEM}="1"
-ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="01", ENV{ID_MM_ZTE_PORT_TYPE_AUX}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="03", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+ATTRS{idVendor}=="19d2", ATTRS{idProduct}=="2003", ENV{.MM_USBIFNUM}=="01", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_SECONDARY}="1"
# Icera-based devices that use DHCP, not AT%IPDPADDR
ATTRS{product}=="K3805-z", ENV{ID_MM_ZTE_ICERA_DHCP}="1"
diff --git a/plugins/zte/mm-broadband-modem-zte-icera.c b/plugins/zte/mm-broadband-modem-zte-icera.c
index 8a6f9302..66aea942 100644
--- a/plugins/zte/mm-broadband-modem-zte-icera.c
+++ b/plugins/zte/mm-broadband-modem-zte-icera.c
@@ -29,7 +29,6 @@
#include "mm-common-zte.h"
#include "mm-broadband-modem-zte-icera.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
@@ -51,28 +50,26 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self),
MM_BROADBAND_MODEM_ZTE_ICERA (self)->priv->unsolicited_setup,
TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -80,33 +77,29 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ task);
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -114,12 +107,9 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own cleanup first */
mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self),
@@ -130,7 +120,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -163,6 +153,9 @@ mm_broadband_modem_zte_icera_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer (AT) and Icera bearer (NET) supported */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
diff --git a/plugins/zte/mm-broadband-modem-zte.c b/plugins/zte/mm-broadband-modem-zte.c
index 17658e00..f4cb774d 100644
--- a/plugins/zte/mm-broadband-modem-zte.c
+++ b/plugins/zte/mm-broadband-modem-zte.c
@@ -24,7 +24,6 @@
#include <ctype.h>
#include "ModemManager.h"
-#include "mm-log.h"
#include "mm-errors-types.h"
#include "mm-modem-helpers.h"
#include "mm-base-modem-at.h"
@@ -56,15 +55,13 @@ load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return (MMUnlockRetries *) g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_unlock_retries_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -72,10 +69,8 @@ load_unlock_retries_ready (MMBaseModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- mm_dbg ("Couldn't query unlock retries: '%s'", error->message);
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -86,18 +81,15 @@ load_unlock_retries_ready (MMBaseModem *self,
retries = mm_unlock_retries_new ();
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1);
mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1);
- g_simple_async_result_set_op_res_gpointer (operation_result,
- retries,
- (GDestroyNotify)g_object_unref);
+ g_task_return_pointer (task, retries, g_object_unref);
} else {
- g_simple_async_result_set_error (operation_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid unlock retries response: '%s'",
- response);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Invalid unlock retries response: '%s'",
+ response);
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_object_unref (task);
}
static void
@@ -111,52 +103,41 @@ load_unlock_retries (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)load_unlock_retries_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_unlock_retries));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* After SIM unlock (Modem interface) */
typedef struct {
- MMBroadbandModemZte *self;
- GSimpleAsyncResult *result;
guint retries;
} ModemAfterSimUnlockContext;
-static void
-modem_after_sim_unlock_context_complete_and_free (ModemAfterSimUnlockContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
modem_after_sim_unlock_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void modem_after_sim_unlock_context_step (ModemAfterSimUnlockContext *ctx);
+static void modem_after_sim_unlock_context_step (GTask *task);
static gboolean
-cpms_timeout_cb (ModemAfterSimUnlockContext *ctx)
+cpms_timeout_cb (GTask *task)
{
+ ModemAfterSimUnlockContext *ctx;
+
+ ctx = g_task_get_task_data (task);
ctx->retries--;
- modem_after_sim_unlock_context_step (ctx);
- return FALSE;
+ modem_after_sim_unlock_context_step (task);
+ return G_SOURCE_REMOVE;
}
static void
cpms_try_ready (MMBaseModem *self,
GAsyncResult *res,
- ModemAfterSimUnlockContext *ctx)
+ GTask *task)
{
GError *error = NULL;
@@ -165,7 +146,7 @@ cpms_try_ready (MMBaseModem *self,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY)) {
/* Retry in 2 seconds */
- g_timeout_add_seconds (2, (GSourceFunc)cpms_timeout_cb, ctx);
+ g_timeout_add_seconds (2, (GSourceFunc)cpms_timeout_cb, task);
g_error_free (error);
return;
}
@@ -174,30 +155,49 @@ cpms_try_ready (MMBaseModem *self,
g_error_free (error);
/* Well, we're done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- modem_after_sim_unlock_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_after_sim_unlock_context_step (ModemAfterSimUnlockContext *ctx)
+modem_after_sim_unlock_context_step (GTask *task)
{
+ MMBroadbandModemZte *self;
+ ModemAfterSimUnlockContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (ctx->retries == 0) {
/* Well... just return without error */
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Consumed all attempts to wait for SIM not being busy");
- modem_after_sim_unlock_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CPMS?",
3,
FALSE,
(GAsyncReadyCallback)cpms_try_ready,
- ctx);
+ task);
+}
+
+static gboolean
+after_sim_unlock_wait_cb (GTask *task)
+{
+ /* Attempt to disable floods of "+ZUSIMR:2" unsolicited responses that
+ * eventually fill up the device's buffers and make it crash. Normally
+ * done during probing, but if the device has a PIN enabled it won't
+ * accept the +CPMS? during the probe and we have to do it here.
+ */
+ modem_after_sim_unlock_context_step (task);
+
+ return G_SOURCE_REMOVE;
}
static void
@@ -206,21 +206,15 @@ modem_after_sim_unlock (MMIfaceModem *self,
gpointer user_data)
{
ModemAfterSimUnlockContext *ctx;
+ GTask *task;
- ctx = g_new0 (ModemAfterSimUnlockContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_after_sim_unlock);
+ ctx = g_new (ModemAfterSimUnlockContext, 1);
ctx->retries = 3;
- /* Attempt to disable floods of "+ZUSIMR:2" unsolicited responses that
- * eventually fill up the device's buffers and make it crash. Normally
- * done during probing, but if the device has a PIN enabled it won't
- * accept the +CPMS? during the probe and we have to do it here.
- */
- modem_after_sim_unlock_context_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ g_timeout_add_seconds (1, (GSourceFunc)after_sim_unlock_wait_cb, task);
}
/*****************************************************************************/
@@ -257,16 +251,13 @@ load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
GArray *all;
@@ -276,9 +267,8 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
all = iface_modem_parent->load_supported_modes_finish (self, res, &error);
if (!all) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -319,13 +309,12 @@ parent_load_supported_modes_ready (MMIfaceModem *self,
}
/* Filter out those unsupported modes */
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, self);
g_array_unref (all);
g_array_unref (combinations);
- g_simple_async_result_set_op_res_gpointer (simple, filtered, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
}
static void
@@ -337,10 +326,7 @@ load_supported_modes (MMIfaceModem *self,
iface_modem_parent->load_supported_modes (
MM_IFACE_MODEM (self),
(GAsyncReadyCallback)parent_load_supported_modes_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_supported_modes));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -425,8 +411,7 @@ load_current_modes_finish (MMIfaceModem *self,
g_assert_not_reached ();
done:
- if (match_info)
- g_match_info_free (match_info);
+ g_match_info_free (match_info);
if (r)
g_regex_unref (r);
@@ -454,13 +439,13 @@ set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allowed_mode_update_ready (MMBroadbandModemZte *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
GError *error = NULL;
@@ -468,11 +453,10 @@ allowed_mode_update_ready (MMBroadbandModemZte *self,
if (error)
/* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (operation_result, TRUE);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -482,15 +466,12 @@ set_current_modes (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
gint cm_mode = -1;
gint pref_acq = -1;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_modes);
+ task = g_task_new (self, NULL, callback, user_data);
if (allowed == MM_MODEM_MODE_2G) {
cm_mode = 1;
@@ -526,18 +507,17 @@ set_current_modes (MMIfaceModem *self,
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Requested mode (allowed: '%s', preferred: '%s') not "
- "supported by the modem.",
- allowed_str,
- preferred_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Requested mode (allowed: '%s', preferred: '%s') not "
+ "supported by the modem.",
+ allowed_str,
+ preferred_str);
+ g_object_unref (task);
+
g_free (allowed_str);
g_free (preferred_str);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
return;
}
@@ -548,7 +528,7 @@ set_current_modes (MMIfaceModem *self,
3,
FALSE,
(GAsyncReadyCallback)allowed_mode_update_ready,
- result);
+ task);
g_free (command);
}
@@ -616,28 +596,26 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
/* Our own setup now */
mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self),
MM_BROADBAND_MODEM_ZTE (self)->priv->unsolicited_setup,
TRUE);
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
+ g_task_return_boolean (task, TRUE);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -645,33 +623,29 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Chain up parent's setup */
iface_modem_3gpp_parent->setup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
- result);
+ task);
}
static void
parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res), TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -679,12 +653,9 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
/* Our own cleanup first */
mm_common_zte_set_unsolicited_events_handlers (MM_BROADBAND_MODEM (self),
@@ -695,7 +666,7 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
iface_modem_3gpp_parent->cleanup_unsolicited_events (
self,
(GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -728,6 +699,10 @@ mm_broadband_modem_zte_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
+ MM_BROADBAND_MODEM_INDICATORS_DISABLED, TRUE,
NULL);
}
diff --git a/plugins/zte/mm-common-zte.c b/plugins/zte/mm-common-zte.c
index 2a183e62..5c992c22 100644
--- a/plugins/zte/mm-common-zte.c
+++ b/plugins/zte/mm-common-zte.c
@@ -106,7 +106,7 @@ mm_common_zte_set_unsolicited_events_handlers (MMBroadbandModem *self,
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
/* Enable unsolicited events in given port */
- for (i = 0; i < 2; i++) {
+ for (i = 0; i < G_N_ELEMENTS (ports); i++) {
if (!ports[i])
continue;
diff --git a/plugins/zte/mm-plugin-zte.c b/plugins/zte/mm-plugin-zte.c
index fff1a4c3..27dcb4fa 100644
--- a/plugins/zte/mm-plugin-zte.c
+++ b/plugins/zte/mm-plugin-zte.c
@@ -21,7 +21,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-plugin-zte.h"
#include "mm-broadband-modem-zte.h"
#include "mm-broadband-modem-zte-icera.h"
@@ -36,8 +36,8 @@
G_DEFINE_TYPE (MMPluginZte, mm_plugin_zte, MM_TYPE_PLUGIN)
-int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
-int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+MM_PLUGIN_DEFINE_MAJOR_VERSION
+MM_PLUGIN_DEFINE_MINOR_VERSION
/*****************************************************************************/
/* Custom commands for AT probing */
@@ -61,7 +61,7 @@ static const MMPortProbeAtCommand custom_at_probe[] = {
static MMBaseModem *
create_modem (MMPlugin *self,
- const gchar *sysfs_path,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -70,8 +70,8 @@ create_modem (MMPlugin *self,
{
#if defined WITH_QMI
if (mm_port_probe_list_has_qmi_port (probes)) {
- mm_dbg ("QMI-powered ZTE modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_qmi_new (sysfs_path,
+ mm_obj_dbg (self, "QMI-powered ZTE modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_qmi_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -81,8 +81,8 @@ create_modem (MMPlugin *self,
#if defined WITH_MBIM
if (mm_port_probe_list_has_mbim_port (probes)) {
- mm_dbg ("MBIM-powered ZTE modem found...");
- return MM_BASE_MODEM (mm_broadband_modem_mbim_new (sysfs_path,
+ mm_obj_dbg (self, "MBIM-powered ZTE modem found...");
+ return MM_BASE_MODEM (mm_broadband_modem_mbim_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -91,13 +91,13 @@ create_modem (MMPlugin *self,
#endif
if (mm_port_probe_list_is_icera (probes))
- return MM_BASE_MODEM (mm_broadband_modem_zte_icera_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_zte_icera_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
product));
- return MM_BASE_MODEM (mm_broadband_modem_zte_new (sysfs_path,
+ return MM_BASE_MODEM (mm_broadband_modem_zte_new (uid,
drivers,
mm_plugin_get_name (self),
vendor,
@@ -110,8 +110,7 @@ grab_port (MMPlugin *self,
MMPortProbe *probe,
GError **error)
{
- GUdevDevice *port;
- MMPortSerialAtFlag pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
+ MMKernelDevice *port;
MMPortType ptype;
port = mm_port_probe_peek_port (probe);
@@ -126,34 +125,17 @@ grab_port (MMPlugin *self,
return FALSE;
}
- if (mm_port_probe_is_at (probe)) {
- /* Look for port type hints */
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_MODEM")) {
- mm_dbg ("ZTE: AT port '%s/%s' flagged as primary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
- } else if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_PORT_TYPE_AUX")) {
- mm_dbg ("ZTE: AT port '%s/%s' flagged as secondary",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
- }
- }
-
- if (g_udev_device_get_property_as_boolean (port, "ID_MM_ZTE_ICERA_DHCP")) {
- mm_dbg ("ZTE: Icera-based modem will use DHCP");
+ if (mm_kernel_device_get_global_property_as_boolean (port, "ID_MM_ZTE_ICERA_DHCP")) {
+ mm_obj_dbg (self, "icera-based modem will use DHCP");
g_object_set (modem,
MM_BROADBAND_MODEM_ICERA_DEFAULT_IP_METHOD, MM_BEARER_IP_METHOD_DHCP,
NULL);
}
return mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ port,
ptype,
- pflags,
+ MM_PORT_SERIAL_AT_FLAG_NONE,
error);
}
@@ -162,12 +144,12 @@ grab_port (MMPlugin *self,
G_MODULE_EXPORT MMPlugin *
mm_plugin_create (void)
{
- static const gchar *subsystems[] = { "tty", "net", "usb", NULL };
+ static const gchar *subsystems[] = { "tty", "net", "usbmisc", NULL };
static const guint16 vendor_ids[] = { 0x19d2, 0 };
return MM_PLUGIN (
g_object_new (MM_TYPE_PLUGIN_ZTE,
- MM_PLUGIN_NAME, "ZTE",
+ MM_PLUGIN_NAME, MM_MODULE_NAME,
MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems,
MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids,
MM_PLUGIN_CUSTOM_AT_PROBE, custom_at_probe,
diff --git a/po/LINGUAS b/po/LINGUAS
index bb23b109..9a35a286 100644
--- a/po/LINGUAS
+++ b/po/LINGUAS
@@ -1,2 +1,19 @@
+cs
+da
+de
+fi
+fr
+fur
+he
+hu
+id
+it
+lt
+pl
+pt_BR
+ru
+sk
+sv
+tr
uk
-de \ No newline at end of file
+zh_CN
diff --git a/po/Makevars b/po/Makevars
new file mode 100644
index 00000000..a7d07754
--- /dev/null
+++ b/po/Makevars
@@ -0,0 +1,78 @@
+# Makefile variables for PO directory in any package using GNU gettext.
+
+# Usually the message domain is the same as the package name.
+DOMAIN = $(PACKAGE)
+
+# These two variables depend on the location of this directory.
+subdir = po
+top_builddir = ..
+
+# These options get passed to xgettext.
+XGETTEXT_OPTIONS = --from-code=UTF-8 --keyword=_ --keyword=N_ --keyword=C_:1c,2 --keyword=NC_:1c,2 --keyword=g_dngettext:2,3
+
+# This is the copyright holder that gets inserted into the header of the
+# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
+# package. (Note that the msgstr strings, extracted from the package's
+# sources, belong to the copyright holder of the package.) Translators are
+# expected to transfer the copyright for their translations to this person
+# or entity, or to disclaim their copyright. The empty string stands for
+# the public domain; in this case the translators are expected to disclaim
+# their copyright.
+COPYRIGHT_HOLDER = The ModemManager developers.
+
+# This tells whether or not to prepend "GNU " prefix to the package
+# name that gets inserted into the header of the $(DOMAIN).pot file.
+# Possible values are "yes", "no", or empty. If it is empty, try to
+# detect it automatically by scanning the files in $(top_srcdir) for
+# "GNU packagename" string.
+PACKAGE_GNU = no
+
+# This is the email address or URL to which the translators shall report
+# bugs in the untranslated strings:
+# - Strings which are not entire sentences, see the maintainer guidelines
+# in the GNU gettext documentation, section 'Preparing Strings'.
+# - Strings which use unclear terms or require additional context to be
+# understood.
+# - Strings which make invalid assumptions about notation of date, time or
+# money.
+# - Pluralisation problems.
+# - Incorrect English spelling.
+# - Incorrect formatting.
+# It can be your email address, or a mailing list address where translators
+# can write to without being subscribed, or the URL of a web page through
+# which the translators can contact you.
+MSGID_BUGS_ADDRESS =
+
+# This is the list of locale categories, beyond LC_MESSAGES, for which the
+# message catalogs shall be used. It is usually empty.
+EXTRA_LOCALE_CATEGORIES =
+
+# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt'
+# context. Possible values are "yes" and "no". Set this to yes if the
+# package uses functions taking also a message context, like pgettext(), or
+# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument.
+USE_MSGCTXT = no
+
+# These options get passed to msgmerge.
+# Useful options are in particular:
+# --previous to keep previous msgids of translated messages,
+# --quiet to reduce the verbosity.
+MSGMERGE_OPTIONS = --quiet
+
+# These options get passed to msginit.
+# If you want to disable line wrapping when writing PO files, add
+# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and
+# MSGINIT_OPTIONS.
+MSGINIT_OPTIONS =
+
+# This tells whether or not to regenerate a PO file when $(DOMAIN).pot
+# has changed. Possible values are "yes" and "no". Set this to no if
+# the POT file is checked in the repository and the version control
+# program ignores timestamps.
+PO_DEPENDS_ON_POT = yes
+
+# This tells whether or not to forcibly update $(DOMAIN).pot and
+# regenerate PO files on "make dist". Possible values are "yes" and
+# "no". Set this to no if the POT file and PO files are maintained
+# externally.
+DIST_DEPENDS_ON_UPDATE_PO = no
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9725a1e5..e1be0d06 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,4 +1,4 @@
-[encoding: UTF-8]
# List of source files containing translatable strings.
# Please keep this file sorted alphabetically.
data/org.freedesktop.ModemManager1.policy.in.in
+src/mm-sleep-monitor.c
diff --git a/po/cs.po b/po/cs.po
new file mode 100644
index 00000000..9f61993e
--- /dev/null
+++ b/po/cs.po
@@ -0,0 +1,120 @@
+# Czech translation for ModemManager.
+# Copyright (C) 2017 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+# Marek Černocký <marek@manet.cz>, 2017.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2017-10-21 15:32+0200\n"
+"Last-Translator: Marek Černocký <marek@manet.cz>\n"
+"Language-Team: čeština <gnome-cs-list@gnome.org>\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
+"X-Generator: Gtranslator 2.91.7\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Ovládat démona pro správu modemů"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Systémová zásada brání v ovládání Správy modemů."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Odemykat a ovládat mobilní širokopásmové zařízení"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Systémová zásada brání v odemknutí nebo v ovládání mobilního širokopásmového "
+"zařízení."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Přidávat, měnit a mazat kontakty v mobilním zařízení"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Systémová zásada brání v přidání, změně nebo smazání kontaktů v tomto "
+"zařízení."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Odesílat, ukládat, měnit a mazat textové zprávy"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Systémová zásada brání v odesílání nebo v manipulaci s textovými zprávami na "
+"tomto zařízení."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Přijímat příchozí hovory nebo začínat odchozí hovory"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Systémová zásada brání v hlasových hovorech."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr ""
+"Systémová zásada brání v dotazování na informace o síti a na služby, nebo "
+"brání v jejich využívání."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"Povolovat sdělování a zobrazování geografické polohy a informací o pozici"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Systémová zásada brání v povolení sdělování a v zobrazení informací o "
+"geografické poloze."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Dotazovat se na informace o síti a na služby a využívat je"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Systémová zásada brání v dotazování na informace o síti a na služby, nebo "
+"brání v jejich využívání."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr ""
+"Dotazovat se na firmware a spravovat jej na mobilním širokopásmovém zařízení"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Systémová zásada brání v dotázání na firmware nebo brání v jeho správě na "
+"tomto zařízení."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "Správa modemů potřebuje resetovat zařízení"
diff --git a/po/da.po b/po/da.po
new file mode 100644
index 00000000..b1aa2ff7
--- /dev/null
+++ b/po/da.po
@@ -0,0 +1,113 @@
+# Danish translation for ModemManager.
+# Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# scootergrisen, 2019.
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-02-10 16:46+0200\n"
+"Last-Translator: scootergrisen\n"
+"Language-Team: Danish\n"
+"Language: da\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Styr Modem Manager-dæmonen"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Systempolitikken forhindrer styring af Modem Manager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Lås op for og styr en mobilt bredbånd-enhed"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Systempolitikken forhindrer oplåsning og styring af mobilt bredbånd-enheden."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Tilføj, rediger og slet mobilt bredbånd-kontakter"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Systempolitikken forhindrer tilføjelse, redigering eller sletning af "
+"enhedens kontakter."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Send, gem, rediger og slet tekstbeskeder"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Systempolitikken forhindrer afsendelse eller manipulering af enhedens "
+"tekstbeskeder."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Accepter indkommende stemmeopkald eller start udgående stemmeopkald."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Systempolitikken forhindrer stemmeopkald."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr ""
+"Systempolitikken forhindrer forespørgsel eller anvendelse af "
+"netværksinformation og -tjenester."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Aktivér og vis information om geografisk placering og positition"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Systempolitikken forhindrer aktivering og visning af information geografisk "
+"placering."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Forespørg og anvend netværksinformation og -tjenester"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Systempolitikken forhindrer forespørgsel eller anvendelse af "
+"netværksinformation og -tjenester."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Forespørg og håndter firmware på en mobilt bredbånd-enhed"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Systempolitikken forhindrer forespørgsel og håndtering af enhedens firmware."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager har brug for at nulstille enhederne"
diff --git a/po/de.po b/po/de.po
index d11e4fe5..29d434bb 100644
--- a/po/de.po
+++ b/po/de.po
@@ -7,8 +7,9 @@
msgid ""
msgstr ""
"Project-Id-Version: ModemManager\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-01-06 21:12+0100\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
"PO-Revision-Date: 2014-01-06 21:23+0100\n"
"Last-Translator: Mario Blättermann <mario.blaettermann@gmail.com>\n"
"Language-Team: German <debian-l10n-german@lists.debian.org>\n"
@@ -18,65 +19,86 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 1.5.4\n"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:1
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
msgid "Control the Modem Manager daemon"
msgstr "Den Modem-Manager-Daemon steuern"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:2
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
msgid "System policy prevents controlling the Modem Manager."
msgstr "Die Systemrichtlinien verhindern die Steuerung von ModemManager."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:3
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
msgid "Unlock and control a mobile broadband device"
msgstr "Ein mobiles Breitbandgerät entsperren und steuern"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:4
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
msgid ""
"System policy prevents unlocking or controlling the mobile broadband device."
msgstr ""
"Die Systemrichtlinien verhindern das Entsperren oder Steuern des mobilen "
"Breitbandgerätes."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:5
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
msgid "Add, modify, and delete mobile broadband contacts"
msgstr "Kontakte für mobiles Breitband hinzufügen, ändern und löschen"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:6
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
msgid ""
"System policy prevents adding, modifying, or deleting this device's contacts."
msgstr ""
"Die Systemrichtlinien verhindern das Hinzufügen, Ändern oder Löschen der "
"Kontakte dieses Gerätes."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:7
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
msgid "Send, save, modify, and delete text messages"
msgstr "Textnachrichten senden, speichern, bearbeiten und löschen"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:8
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+#, fuzzy
msgid ""
-"System policy prevents sending or maniuplating this device's text messages."
+"System policy prevents sending or manipulating this device's text messages."
msgstr ""
"Die Systemrichtlinien verhindern das Senden oder Bearbeiten der "
"Textnachrichten dieses Gerätes."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:9
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+#, fuzzy
+msgid "System policy prevents voice calls."
+msgstr "Die Systemrichtlinien verhindern die Steuerung von ModemManager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr ""
+"Die Systemrichtlinien verhindern die Abfrage der Netzwerkinformationen und -"
+"dienste."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
msgid "Enable and view geographic location and positioning information"
msgstr ""
"Informationen zum geografischen Standort und Positionierung aktivieren und "
"anzeigen"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:10
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
msgid ""
"System policy prevents enabling or viewing geographic location information."
msgstr ""
"Die Systemrichtlinien verhindern das Aktivieren oder Ändern der "
"Informationen zum geografischen Standort."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:11
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
msgid "Query and utilize network information and services"
msgstr "Netzwerkinformationen und -dienste abfragen und nutzen"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:12
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
msgid ""
"System policy prevents querying or utilizing network information and "
"services."
@@ -84,12 +106,16 @@ msgstr ""
"Die Systemrichtlinien verhindern die Abfrage der Netzwerkinformationen und -"
"dienste."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:13
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
msgid "Query and manage firmware on a mobile broadband device"
msgstr "Firmware auf mobilen Breitbandgeräten abfragen und verwalten"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:14
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
msgid "System policy prevents querying or managing this device's firmware."
msgstr ""
"Die Systemrichtlinien verhindern die Abfrage oder Verwaltung der Firmware "
"dieses Gerätes."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr ""
diff --git a/po/fi.po b/po/fi.po
new file mode 100644
index 00000000..e5d68cf3
--- /dev/null
+++ b/po/fi.po
@@ -0,0 +1,116 @@
+# Finnish translation for ModemManager.
+# Copyright (C) 2020 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# JRfi <starman@starman.fi>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2021-01-08 03:28+0000\n"
+"PO-Revision-Date: 2020-08-08 18:58+0300\n"
+"Last-Translator: JR-Fi <starman@starman.fi>\n"
+"Language-Team: Finnish <lokalisointi-lista@googlegroups.com>\n"
+"Language: fi\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.0.6\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Hallitse Modem Manager taustaprosessia"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr ""
+"Järjestelmän sääntö estää Modem Managerin hallinta-asetusten muuttamisen."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Avaa ja hallitse mobiilia laajakaistalaitetta"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Järjestelmän sääntö estää avaamasta tai hallitsemasta mobiilia "
+"laajakaistalaitetta."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Lisää, muuta ja poista mobiilin laajakaistan yhteystietoja"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Järjestelmän sääntö estää lisäämästä, muuttamasta tai poistamasta tämän "
+"laitteen yhteystietoja."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Lähetä, talleta, muuta ja poista tekstiviestejä"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Järjestelmän sääntö estää lähettämästä tai muokkaamasta tämän laitteen "
+"tekstiviestejä."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Hyväksy tulevat puhelut tai aloita (ääni)puheluita."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Järjestelmän sääntö estää äänipuhelut."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Tarkista verkon aika ja aikavyöhyketiedot"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr ""
+"Järjestelmän sääntö estää tarkastamasta verkon aikaa ja aikavyöhyketietoja."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Salli ja katso sijainti- ja paikkatiedot"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Järjestelmän sääntö estää sallimasta tai katsomasta sijainti- ja "
+"paikkatietoja"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Tarkista ja käytä verkon tietoja ja palveluita"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Järjestelmän sääntö estää tarkistamasta tai käyttämästä verkon tietoja ja "
+"palveluita."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Tarkista ja hallitse mobiilin laajakaistalaitteen laiteohjelmistoa"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Järjestelmän sääntö estää tarkistamasta ja hallitsemasta tämän laitteen "
+"laiteohjelmistoa."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "Modem Managerin pitää uudelleen käynnistää laitteita"
diff --git a/po/fr.po b/po/fr.po
new file mode 100644
index 00000000..77f6922b
--- /dev/null
+++ b/po/fr.po
@@ -0,0 +1,117 @@
+# ModemManager translation to French.
+# Copyright (C) 2018 Listed translators
+# This file is distributed under the same license as the ModemManager package.
+# Claude Paroz <claude@2xlibre.net>, 2018
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2018-08-18 16:17+0200\n"
+"Last-Translator: Claude Paroz <claude@2xlibre.net>\n"
+"Language-Team: French <gnomefr@traduc.org>\n"
+"Language: fr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Contrôle le service Modem Manager"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "La politique système empêche le contrôle de Modem Manager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Déverrouiller et contrôler un périphérique mobile à large bande"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"La politique système empêche le verrouillage et le contrôle d’un "
+"périphérique mobile à large bande."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Ajouter, modifier et supprimer des contacts de connexions mobiles"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"La politique système empêche l’ajout, la modification ou la suppression des "
+"contacts de cet appareil."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Envoyer, enregistrer, modifier et supprimer des messages textuels"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"La politique système empêche l’envoi ou la manipulation des messages "
+"textuels ce cet appareil."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+"Accepter des appels vocaux entrants ou initier des appels vocaux sortants."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "La politique système empêche les appels vocaux."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr ""
+"La politique système empêche l’interrogation et l’utilisation des "
+"informations et des services du réseau."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Activer et voir les informations de position géographique"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"La politique système empêche d’activer ou de voir les informations de "
+"position géographique."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Interroger et utiliser les informations et services du réseau"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"La politique système empêche l’interrogation et l’utilisation des "
+"informations et des services du réseau."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Interroger et gérer le matériel d’un périphérique mobile à large bande"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"La politique système empêche l’interrogation et la gestion du matériel de ce "
+"périphérique."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager a besoin de réinitialiser les périphériques"
diff --git a/po/fur.po b/po/fur.po
new file mode 100644
index 00000000..49632901
--- /dev/null
+++ b/po/fur.po
@@ -0,0 +1,120 @@
+# Friulian translation for ModemManager.
+# Copyright (C) 2018 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Fabio Tomat <f.t.public@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2018-03-25 17:20+0200\n"
+"Last-Translator: Fabio Tomat <f.t.public@gmail.com>\n"
+"Language-Team: Friulian <f.t.public@gmail.com>\n"
+"Language: fur\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.0.6\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Controle il demoni di Modem Manager"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "La politiche dal sisteme e impedìs il control di Modem Manager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Sbloche e controle un dispositîf a bande largje mobile"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"La politiche dal sisteme e impedìs di sblocâ o controlâ il dispositîf a "
+"bande largje mobile."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Zonte, modifiche e elimine i contats de bande largje mobile"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"La politiche dal sisteme e impedìs di zontâ, modificâ o eliminâ i contats di "
+"chest dispositîf."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Invie, salve, modifiche e elimine i messaçs di test"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"La politiche dal sisteme e impedìs di inviâ o manipolâ i messaçs di test di "
+"chest dispositîf."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+"Acete lis clamadis vocâls in jentrade o tacâ clamadis vocâls in jessude."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "La politiche dal sisteme e impedìs lis clamadis vocâls."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr ""
+"La politiche dal sisteme e impedìs la interogazion e la utilizazion di "
+"informazions di rêt e servizis."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"Abilite e viôt la posizion gjeografiche e lis informazions su la posizion"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"La politiche dal sisteme e impedìs di abilitâ o viodi lis informazions su la "
+"posizion gjeografiche."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Interoghe e dopre lis informazions di rêt e i servizis"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"La politiche dal sisteme e impedìs la interogazion e la utilizazion di "
+"informazions di rêt e servizis."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr ""
+"Interoghe e gjestìs il firmware suntun dispositîf a bande largje mobile"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"La politiche dal sisteme e impedìs di interogâ o gjestî il firmware di chest "
+"dispositîf."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager al à bisugne di ristabilî/azerâ i dispositîfs"
diff --git a/po/he.po b/po/he.po
new file mode 100644
index 00000000..485579e5
--- /dev/null
+++ b/po/he.po
@@ -0,0 +1,105 @@
+# Hebrew translation of Modem Manager
+# Copyright (C) 2020 Free Software Foundation, Inc.
+# This file is distributed under the same license as the Modem Manager package.
+#
+# Yaron Shahrabani <sh.yaron@gmail.com>, 2020.
+msgid ""
+msgstr ""
+"Project-Id-Version: Modem Manager\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2020-02-08 18:14+0200\n"
+"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
+"Language-Team: Hebrew <kde-i18n-he@kde.org>\n"
+"Language: he\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: GitLab Editor\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "שליטה בסוכן מנהל המודמים"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "מדיניות מערכת מונעת שליטה במנהל המודמים."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "שחרור ושליטה במכשיר לרשת תקשורת סלולרית"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid "System policy prevents unlocking or controlling the mobile broadband device."
+msgstr "מדיניות המערכת מונעת שחרור או שליטה בהתקן רשת התקשורת האלחוטית."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "ניתן להוסיף, לערוך ולמחוק אנשי קשר בהתקן רשת תקשורת אלחוטית"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr "מדיניות המערכת מונעת הוספה, עריכה או מחיקה של אנשי הקשר במכשיר הזה."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "שליחה, שמירה, עריכה ומחיקה של הודעות טקסט"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"מדיניות המערכת מונעת שליחה או עריכה של המסרונים בהתקן הזה."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "לקבל שיחות נכנסות או להוציא שיחות."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "מדיניות המערכת מונעת שיחות קוליות."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "לתשאל את פרטי השעה ואזור הזמן מהרשת"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "מדיניות מערכת מונעת תשאול פרטי זמן מהרשת."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"להפעיל ולהציג פרטי מיקום גאוגרפי"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"מדיניות מערכת מונעת הפעלה או הצגה של פרטי מיקום גאוגרפי."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "תשאול ושימוש בשירותי ומידע על הרשת"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"מדיניות המערכת מונעת תשאול או שימוש במידע על הרשת או שירותים שלה."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "תשאול וניהול קושחה על התקן רשת תקשורת סלולרית"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr "מדיניות המערכת מונעת תשאול או ניהול של קושחת ההתקן הזה."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "על ModemManager לאפס התקנים"
diff --git a/po/hu.po b/po/hu.po
new file mode 100644
index 00000000..476e86fa
--- /dev/null
+++ b/po/hu.po
@@ -0,0 +1,118 @@
+# Hungarian translation for modemmanager.
+# Copyright (C) 2017, 2021. Free Software Foundation, Inc.
+# This file is distributed under the same license as the modemmanager package.
+#
+# Gabor Kelemen <kelemeng at ubuntu dot com>, 2017.
+# Balázs Úr <ur.balazs at fsf dot hu>, 2021.
+msgid ""
+msgstr ""
+"Project-Id-Version: modemmanager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 15:29+0000\n"
+"PO-Revision-Date: 2021-03-17 00:52+0100\n"
+"Last-Translator: Balázs Úr <ur.balazs at fsf dot hu>\n"
+"Language-Team: Hungarian <gnome-hu-list at gnome dot org>\n"
+"Language: hu\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 19.12.3\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Modemkezelő démon vezérlése"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "A rendszer házirendje nem teszi lehetővé a Modemkezelő vezérlését."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Mobil széles sávú készülék feloldása és vezérlése"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"A rendszer házirendje nem teszi lehetővé a mobil széles sávú készülék "
+"feloldását vagy vezérlését."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Névjegyek hozzáadása, módosítása és eltávolítása"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"A rendszer házirendje nem teszi lehetővé ezen az eszközön a névjegyek "
+"hozzáadását, módosítását és eltávolítását."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Szöveges üzenetek küldése, mentése, módosítása és törlése"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"A rendszer házirendje nem teszi lehetővé a szöveges üzenetek küldését vagy "
+"kezelését."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Bejövő hívások fogadása vagy kimenő hívások indítása."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "A rendszer házirendje nem teszi lehetővé a hívásokat."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Hálózati idő és időzóna-információk lekérdezése"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr ""
+"A rendszer házirendje nem teszi lehetővé a hálózati idő információinak "
+"lekérdezését."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"Földrajzi helyzetmeghatározás bekapcsolása és az információk megtekintése"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"A rendszer házirendje nem teszi lehetővé a földrajzi helyzetmeghatározás "
+"bekapcsolását vagy az információk megtekintését."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Hálózati információk és szolgáltatások lekérdezése és használata"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"A rendszer házirendje nem teszi lehetővé a hálózati információk és "
+"szolgáltatások lekérdezését és használatát."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Firmware lekérdezése és kezelése a mobil széles sávú eszközön"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"A rendszer lekérdezése és használata lehetővé a firmware lekérdezését és "
+"kezelését az eszközön."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "A Modemkezelőnek alapállapotba kell állítania eszközöket"
diff --git a/po/id.po b/po/id.po
new file mode 100644
index 00000000..1823aad7
--- /dev/null
+++ b/po/id.po
@@ -0,0 +1,111 @@
+# Indonesian translation for ModemManager.
+# Copyright (C) 2018 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Andika Triwidada <andika@gmail.com>, 2018.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2020-03-31 18:14+0700\n"
+"Last-Translator: Andika Triwidada <andika@gmail.com>\n"
+"Language-Team: Indonesian <gnome-l10n-id@googlegroups.com>\n"
+"Language: id\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.3\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Mengendalikan daemon Manajer Modem"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Kebijakan sistem mencegah pengendalian Manajer Modem."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Buka kunci dan kendalikan suatu peranti data seluler"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Kebijakan sistem mencegah membuka kunci atau mengendalikan peranti data "
+"seluler."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Tambah, ubah, dan hapus kontak data seluler"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Kebijakan sistem mencegah menambah, mengubah, atau menghapus kontak-kontak "
+"peranti ini."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Kirim, simpan, ubah, dan hapus pesan-pesan teks"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Kebijakan sistem mencegah pengiriman atau manipulasi pesan-pesan teks "
+"peranti ini."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Terima panggilan suara masuk atau mulai pemanggilan suara keluar."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Kebijakan sistem mencegah panggilan suara."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Tanyakan waktu jaringan dan informasi zona waktu"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "Kebijakan sistem mencegah kuiri informasi waktu jaringan."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Fungsikan dan tilik lokasi geografis dan informasi posisi"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Kebijakan sistem mencegah memfungsikan atau menilik informasi lokasi "
+"geografis."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Kuiri dan manfaatkan layanan dan informasi jaringan"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Kebijakan sistem mencegah kuiri atau pemanfaatan layanan dan informasi "
+"jaringan."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Kuiri dan kelola firmware pada suatu peranti data seluler"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr "Kebijakan sistem mencegah kuiri atau pengelolaan firmware peranti ini."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager perlu mereset peranti"
diff --git a/po/it.po b/po/it.po
new file mode 100644
index 00000000..194559f2
--- /dev/null
+++ b/po/it.po
@@ -0,0 +1,118 @@
+# Italian translation for ModemManager.
+# Copyright (C) 2018, 2020 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Milo Casagrande <milo@milo.name>, 2018, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2020-03-10 20:15+0100\n"
+"Last-Translator: Milo Casagrande <milo@milo.name>\n"
+"Language-Team: Italian <gnome-it-list@gnome.org>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.2.4\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Controlla il demone di «Modem Manager»"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "La politica di sistema impedisce il controllo di «Modem Manager»"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Sblocca e controlla un dispositivo mobile a banda larga"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"La politica di sistema impedisce di sbloccare o controllare il dispositivo "
+"mobile a banda larga."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Aggiunge, modifica ed elimina contatti mobili a banda larga"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"La politica di sistema impedisce di aggiungere, modificare o eliminare i "
+"contatti di questo dispositivo."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Invia, salva, modifica ed elimina messaggi di testo"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"La politica di sistema impedisce di inviare o manipolare i messaggi di testo "
+"di questo dispositivo."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Accetta chiamate vocali in arrivo o avvia chiamate vocali."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "La politica di sistema impedisce di effettuare chiamate vocali."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Interroga le informazioni sull'ora di rete e sul fuso orario"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr ""
+"La politica di sistema impedisce di interrogare le informazioni sull'ora di "
+"rete"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"Abilita e visualizza informazioni di geolocalizzazione e posizionamento"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"La politica di sistema impedisce di abilitare o visualizzare informazioni di "
+"geolocalizzazione."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Interroga e utilizza informazioni e servizi della rete"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"La politica di sistema impedisce di interrogare o di utilizzare le "
+"informazioni e i servizi della rete."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr ""
+"Interroga e gestisce il firmware su un dispositivo mobile a banda larga"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"La politica di sistema impedisce di interrogare o gestire il firmware di "
+"questo dispositivo."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager deve reimpostare i dispositivi"
diff --git a/po/lt.po b/po/lt.po
new file mode 100644
index 00000000..e51b74dc
--- /dev/null
+++ b/po/lt.po
@@ -0,0 +1,122 @@
+# Lithuanian translation for ModemManager.
+# Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Moo, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-04-13 22:08+0300\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: lt\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && (n%100<11 || n%100>19) ? 0 : n"
+"%10>=2 && n%10<=9 && (n%100<11 || n%100>19) ? 1 : 2);\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Valdyti modemo tvarkytuvės tarnybą"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Sistemos politika neleidžia valdyti modemo tvarkytuvę."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Atrakinti ir valdyti mobiliojo plačiajuosčio ryšio įrenginį"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Sistemos politika neleidžia atrakinti ir valdyti mobiliojo plačiajuosčio "
+"ryšio įrenginį."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr ""
+"Pridėti, modifikuoti bei ištrinti mobiliojo plačiajuosčio ryšio kontaktus"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Sistemos politika neleidžia pridėti, modifikuoti ar ištrinti šio įrenginio "
+"kontaktus."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Siųsti, įrašyti, modifikuoti bei ištrinti tekstines žinutes"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Sistemos politika neleidžia siųsti ar valdyti šio įrenginio tekstines "
+"žinutes."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+"Atsiliepti į gaunamus balso skambučius ar inicijuoti išsiunčiamuosius balso "
+"skambučius."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Sistemos politika neleidžia balso skambučių."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr ""
+"Sistemos politika neleidžia užklausti ar panaudoti tinklo informacija ir "
+"paslaugas."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Įjungti ir rodyti geografinės vietos bei pozicionavimo informaciją"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Sistemos politika neleidžia įjungti ar rodyti geografinės vietos informaciją."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Užklausti ir panaudoti tinklo informacija bei paslaugas"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Sistemos politika neleidžia užklausti ar panaudoti tinklo informacija ir "
+"paslaugas."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr ""
+"Užklausti ir tvarkyti programinę aparatinę įrangą mobiliojo plačiajuosčio "
+"ryšio įrenginyje"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Sistemos politika neleidžia užklausti ar tvarkyti šio įrenginio programinę "
+"aparatinę įrangą."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager turi atstatyti įrenginius"
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 00000000..19183725
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+i18n.gettext(mm_name, preset: 'glib')
diff --git a/po/pl.po b/po/pl.po
new file mode 100644
index 00000000..4f63ccec
--- /dev/null
+++ b/po/pl.po
@@ -0,0 +1,119 @@
+# Polish translation for ModemManager.
+# Copyright © 2017-2019 the ModemManager authors.
+# This file is distributed under the same license as the ModemManager package.
+# Piotr Drąg <piotrdrag@gmail.com>, 2017-2019.
+# Aviary.pl <community-poland@mozilla.org>, 2017-2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-09-28 15:02+0200\n"
+"Last-Translator: Piotr Drąg <piotrdrag@gmail.com>\n"
+"Language-Team: Polish <community-poland@mozilla.org>\n"
+"Language: pl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
+"|| n%100>=20) ? 1 : 2);\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Sterowanie usługą ModemManager"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Ustawienia systemu uniemożliwiają sterowanie usługą ModemManager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Odblokowanie i sterowanie urządzeniem komórkowym"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Ustawienia systemu uniemożliwiają odblokowanie lub sterowanie urządzeniem "
+"komórkowym."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Dodawanie, modyfikowanie i usuwanie kontaktów urządzenia komórkowego"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Ustawienia systemu uniemożliwiają dodawanie, modyfikowanie lub usuwanie "
+"kontaktów tego urządzenia."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Wysyłanie, zapisywanie, modyfikowanie i usuwanie wiadomości SMS"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Ustawienia systemu uniemożliwiają wysyłanie lub manipulowanie wiadomościami "
+"SMS tego urządzenia."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Przyjmowanie połączeń przychodzących lub dzwonienie"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Ustawienia systemu uniemożliwiają dzwonienie."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Odpytywanie informacji o czasie sieciowym i strefie czasowej"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr ""
+"Ustawienia systemu uniemożliwiają odpytywanie informacji o czasie sieciowym."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"Włączanie i wyświetlanie informacji o położeniu geograficznym "
+"i pozycjonowaniu"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Ustawienia systemu uniemożliwiają włączanie lub wyświetlanie informacji "
+"o położeniu geograficznym."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Odpytywanie i używanie informacji i usług sieciowych"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Ustawienia systemu uniemożliwiają odpytywanie lub używanie informacji "
+"i usług sieciowych."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr ""
+"Odpytywanie i zarządzanie oprogramowaniem sprzętowym urządzenia komórkowego"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Ustawienia systemu uniemożliwiają odpytywanie lub zarządzanie "
+"oprogramowaniem sprzętowym tego urządzenia."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "Usługa ModemManager musi ponownie uruchomić urządzenia"
diff --git a/po/pt_BR.po b/po/pt_BR.po
new file mode 100644
index 00000000..12617ce2
--- /dev/null
+++ b/po/pt_BR.po
@@ -0,0 +1,115 @@
+# Brazilian Portuguese translation for ModemManager.
+# Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Rafael Fontenelle <rafaelff@gnome.org>, 2017-2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-11-25 00:17-0300\n"
+"Last-Translator: Rafael Fontenelle <rafaelff@gnome.org>\n"
+"Language-Team: Brazilian Portuguese <gnome-pt_br-list@gnome.org>\n"
+"Language: pt_BR\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"X-Generator: Gtranslator 3.32.0\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Controlar o daemon do ModemManager"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "A política de sistema impede de controlar o ModemManager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Desbloquear e controlar um dispositivo de banda larga móvel"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"A política de sistema impede de desbloquear ou controlar o dispositivo de "
+"banda larga móvel."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Adicionar, modificar e excluir contatos de banda larga móvel"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"A política de sistema impede de adicionar, modificar ou excluir os contatos "
+"deste dispositivo."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Enviar, salvar, modificar e excluir mensagens de texto"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"A política de sistema impede de enviar ou manipular as mensagens de texto "
+"deste dispositivo."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Aceitar chamadas de voz recebidas ou iniciar chamadas de voz de saída."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "A política de sistema impede chamadas de voz."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Consultar informações de hora e fuso horário da rede"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr ""
+"A política de sistema impede de consultar informações de horário da rede."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Habilitar e ver informações de posicionamento e localização geográfica"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"A política de sistema impede de habilitar ou ver informações de localização "
+"geográfica."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Consultar ou utilizar serviços e informações de rede."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"A política de sistema impede de consultar ou utilizar serviços e informações "
+"de rede."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Consultar e gerenciar firmware em um dispositivo de banda larga móvel"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"A política de sistema impede de consultar ou gerenciar o firmware do "
+"dispositivo."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "O ModemManager precisa reiniciar os dispositivos"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644
index 00000000..6849f1cc
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,118 @@
+# Russian translation for ModemManager.
+# Copyright (C) 2020 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Артемий Судаков <finziyr@yandex.ru>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2020-04-04 21:26+0300\n"
+"Last-Translator: Артемий Судаков <finziyr@yandex.ru>\n"
+"Language-Team: Russian <gnome-cyr@gnome.org>\n"
+"Language: ru\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
+"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+"X-Generator: Poedit 2.3\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Настроить сервис Modem Manager"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Системная политика не позволяет управлять Modem Manager'ом."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Разблокировка и управление мобильным широкополосным устройством"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Системная политика предотвращает разблокировку или управление мобильным "
+"широкополосным устройством."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Добавить, изменить и удалить мобильные широкополосные контакты"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Системная политика запрещает добавление, изменение или удаление контактов "
+"этого устройства."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Отправить, сохранить, изменить и удалить текстовые сообщения"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Системная политика запрещает отправку или манипулирование текстовыми "
+"сообщениями этого устройства."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+"Принимать входящие голосовые звонки или начать исходящие голосовые звонки."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Системная политика запрещает голосовые звонки."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Запрос информации о времени и часовых поясах в сети"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "Системная политика запрещает запрашивать информацию о времени в сети."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr ""
+"Включить и просмотреть географическое местоположение и информацию о "
+"местоположении"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Системная политика запрещает включение или просмотр информации о "
+"географическом местоположении."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Запрос, использование сетевой информации и услуг"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Системная политика не позволяет запрашивать или использовать сетевую "
+"информацию и сервисы."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Запрос и управление прошивкой на мобильном широкополосном устройстве"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Системная политика не позволяет запрашивать или управлять прошивкой этого "
+"устройства."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager'у необходимо перезагрузить устройства"
diff --git a/po/sk.po b/po/sk.po
new file mode 100644
index 00000000..8f05d66e
--- /dev/null
+++ b/po/sk.po
@@ -0,0 +1,117 @@
+# Slovak translation for ModemManager.
+# Copyright (C) 2017 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Dušan Kazik <prescott66@gmail.com>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-05-26 15:30+0000\n"
+"PO-Revision-Date: 2020-09-17 15:00+0200\n"
+"Last-Translator: Dušan Kazik <prescott66@gmail.com>\n"
+"Language-Team: Slovak <gnome-sk-list@gnome.org>\n"
+"Language: sk\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n==1) ? 1 : (n>=2 && n<=4) ? 2 : 0;\n"
+"X-Generator: Poedit 2.4.1\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Ovládanie služby správcu modemov"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Politika systému zabraňuje ovládaniu správcu modemov."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Odomknutie a ovládanie mobilného širokopásmového zariadenia"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Politika systému zabraňuje odomknutiu alebo ovládaniu mobilného "
+"širokopásmového zariadenia."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr ""
+"Pridanie, úprava a odstránenie kontaktov mobilného širokopásmového zariadenia"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Politika systému zabraňuje pridaniu, úprave, alebo odstráneniu kontaktov v "
+"tomto zariadení."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Odoslanie, uloženie, úprava a odstránenie textových správ"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Politika systému zabraňuje odoslaniu, alebo manipulácii textových správ v "
+"tomto zariadení."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+"Prijatie prichádzajúcich hovorov, alebo zahájenie odchádzajúcich hlasových "
+"hovorov."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Politika systému zabraňuje hlasovým hovorom."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Požadovanie informácií o sieťovom čase a časovej zóne"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "Politika systému zabraňuje požadovaniu informácií o sieťovom čase."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Povolenie a zobrazenie geografickej polohy a informácií o pozícii"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Politika systému zabraňuje povoleniu, alebo zobrazeniu informácií o "
+"geografickej polohe."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Požadovanie a spracovanie sieťových informácií a služieb"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Politika systému zabraňuje požadovaniu, alebo spracovaniu sieťových "
+"informácií a službám."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Požadovanie a správa firmvéru mobilného širokopásmového zariadenia"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Politika systému zabraňuje požadovaniu, alebo správe firmvéru tohto "
+"zariadenia."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "Služba ModemManager vyžaduje obnovenie zariadení" \ No newline at end of file
diff --git a/po/sv.po b/po/sv.po
new file mode 100644
index 00000000..8317d606
--- /dev/null
+++ b/po/sv.po
@@ -0,0 +1,115 @@
+# Swedish translation for ModemManager.
+# Copyright (C) 2017, 2020 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Josef Andersson <l10nl18nsweja@gmail.com>, 2017.
+# Anders Jonsson <anders.jonsson@norsjovallen.se>, 2020.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 15:29+0000\n"
+"PO-Revision-Date: 2020-09-18 23:56+0200\n"
+"Last-Translator: Anders Jonsson <anders.jonsson@norsjovallen.se>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 2.4.1\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Kontrollera demonen för Modem Manager"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "En systempolicy förhindrar kontroll av Modem Manager."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Lås upp och kontrollera en mobil bredbandsenhet"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"En systempolicy hindrar upplåsning eller kontroll över den mobila "
+"bredbandsenheten."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Lägg till, ändra och ta bort mobila bredbandskontakter"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"En systempolicy förhindrar att lägga till, ändra och ta bort denna enhets "
+"kontakter."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Skicka, spara, ändra och ta bort meddelanden"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"En systempolicy förhindrar att skicka eller ändra denna enhets "
+"textmeddelanden."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Acceptera inkommande röstsamtal eller påbörja utgående röstsamtal."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "En systempolicy förhindrar röstsamtal."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Fråga efter nätverkstid och tidzonsinformation"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "En systempolicy förhindrar frågande av nätverkstidsinformation."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Aktivera och visa geografisk plats samt positioneringsinformation"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"En systempolicy förhindrar aktivering eller visning av geografisk "
+"platsinformation."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Fråga efter och nyttja nätverksinformation och tjänster"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"En systempolicy förhindrar frågande och nyttjande av nätverksinformation och "
+"tjänster."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Fråga efter och hantera fast programvara för en mobil bredbandsenhet"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"En systempolicy förhindrar att fråga och hantera denna enhets fasta "
+"programvara."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager behöver starta om enheter"
diff --git a/po/tr.po b/po/tr.po
new file mode 100644
index 00000000..151d511c
--- /dev/null
+++ b/po/tr.po
@@ -0,0 +1,114 @@
+# Turkish translation for ModemManager.
+# Copyright (C) 2017-2019 the ModemManager authors.
+# This file is distributed under the same license as the ModemManager package.
+# Emin Tufan Çetin <etcetin@gmail.com>, 2018, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-11-10 09:05+0300\n"
+"Last-Translator: Emin Tufan Çetin <etcetin@gmail.com>\n"
+"Language-Team: Türkçe <gnome-turk@gnome.org>\n"
+"Language: tr\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Gtranslator 2.91.7\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "Modem Manager artalan uygulamasını denetle"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "Sistem ilkesi Modem Manager'ı denetlemeyi engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "Mobil geniş bant aygıtının kilidini aç ve denetle"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr ""
+"Sistem ilkesi mobil geniş bant aygıtını denetlemeyi veya kilidini açmayı "
+"engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "Mobil geniş bant kişileri ekle, düzenle ve sil"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr ""
+"Sistem ilkesi bu aygıtın kişi eklemesini, düzenlemesini veya silmesini "
+"engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "Metin ileti gönder, kaydet, düzenle ve sil"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr ""
+"Sistem ilkesi bu aygıtın metin iletiler göndermesini veya işlemesini "
+"engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "Gelen sesli çağrıları kabul et veya giden sesli çağrılar başlat."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Sistem ilkesi sesli çağrıları engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Ağ zaman ve saat dilimi bilgisini sorgula"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "Sistem ilkesi ağ zaman bilgisini sorgulamayı engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "Coğrafi konum ve konumlandırma bilgisini etkinleştir ve gör"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr ""
+"Sistem ilkesi coğrafi konum bilgisini etkinleştirmeyi ve göstermeyi "
+"engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "Ağ bilgisi ve hizmetleri sorgula ve yararlan"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr ""
+"Sistem ilkesi ağ bilgisini ve hizmetleri sorgulamayı veya yararlanmayı "
+"engelliyor."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "Mobil geniş bant aygıtındaki donanım yazılımını sorgula ve yönet"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr ""
+"Sistem ilkesi bu aygıtın donanım yazılımını sorgulamayı veya yönetmeyi "
+"engelliyor."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager'in aygıtları sıfırlaması gerekiyor"
diff --git a/po/uk.po b/po/uk.po
index b4c8c5d8..45fe05d7 100644
--- a/po/uk.po
+++ b/po/uk.po
@@ -2,13 +2,14 @@
# Copyright (C) 2013 Free Software Foundation, Inc.
# This file is distributed under the same license as the Modem Manager package.
#
-# Yuri Chornoivan <yurchor@ukr.net>, 2013.
+# Yuri Chornoivan <yurchor@ukr.net>, 2013, 2017, 2019.
msgid ""
msgstr ""
"Project-Id-Version: Modem Manager\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-10-19 18:13+0300\n"
-"PO-Revision-Date: 2013-10-19 18:17+0300\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-11-10 14:35+0200\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
"Language: uk\n"
@@ -17,67 +18,84 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Lokalize 1.5\n"
+"X-Generator: Lokalize 19.11.70\n"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:1
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
msgid "Control the Modem Manager daemon"
msgstr "Керування фоновою службою Modem Manager"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:2
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
msgid "System policy prevents controlling the Modem Manager."
msgstr "Правила системи перешкоджають керування Modem Manager."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:3
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
msgid "Unlock and control a mobile broadband device"
msgstr "Розблокувати пристрій мобільної широкосмугової мережі і керувати ним"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:4
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
msgid ""
"System policy prevents unlocking or controlling the mobile broadband device."
msgstr ""
"Правила системи забороняють розблокування і керування пристроями "
"широкосмугових мобільних мереж."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:5
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
msgid "Add, modify, and delete mobile broadband contacts"
msgstr ""
"Додати, внести зміни і вилучити контакти пристрою мобільних широкосмугових "
"мереж"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:6
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
msgid ""
"System policy prevents adding, modifying, or deleting this device's contacts."
msgstr ""
"Правила системи перешкоджають додаванню, внесенню змін та вилученню записів "
"контактів на цьому пристрої."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:7
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
msgid "Send, save, modify, and delete text messages"
msgstr "Надіслати, зберегти, внести зміни або вилучити текстові повідомлення"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:8
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
msgid ""
-"System policy prevents sending or maniuplating this device's text messages."
+"System policy prevents sending or manipulating this device's text messages."
msgstr ""
"Правила системи забороняють надсилання або керування текстовими "
"повідомленнями цього пристрою."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:9
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr ""
+"Приймати вхідні голосові виклики і розпочинати вихідні голосові виклики."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "Правила системи перешкоджають голосовим викликам."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr "Надсилання запиту щодо часу і часового поясу мережі"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+msgid "System policy prevents querying network time information."
+msgstr "Правила системи забороняють надсилання запитів щодо часу мережі."
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
msgid "Enable and view geographic location and positioning information"
msgstr ""
"Увімкнути або переглянути дані щодо географічного розташування і позиціювання"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:10
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
msgid ""
"System policy prevents enabling or viewing geographic location information."
msgstr ""
"Правила системи забороняють вмикання або перегляд даних щодо розташування."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:11
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
msgid "Query and utilize network information and services"
msgstr "Надіслати запит і використати дані щодо мережі і служби"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:12
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
msgid ""
"System policy prevents querying or utilizing network information and "
"services."
@@ -85,14 +103,18 @@ msgstr ""
"Правила системи забороняють надсилання запитів і використання даних щодо "
"мережі і служб."
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:13
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
msgid "Query and manage firmware on a mobile broadband device"
msgstr ""
"Опитування та керування мікропрограмою на пристрої мобільної широкосмугової "
"мережі"
-#: ../data/org.freedesktop.ModemManager1.policy.in.in.h:14
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
msgid "System policy prevents querying or managing this device's firmware."
msgstr ""
"Правила системи перешкоджають опитуванню або керування мікропрограмою цього "
"пристрою."
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "ModemManager потребує відновлення початкового стану пристроїв"
diff --git a/po/zh_CN.po b/po/zh_CN.po
new file mode 100644
index 00000000..05f616aa
--- /dev/null
+++ b/po/zh_CN.po
@@ -0,0 +1,103 @@
+# Chinese (China) translation for ModemManager.
+# Copyright (C) 2019 ModemManager's COPYRIGHT HOLDER
+# This file is distributed under the same license as the ModemManager package.
+# Estel Zhang <callmebedrockdigger@gmail.com>, 2018.
+# 王滋涵 Zephyr Waitzman <i@wi24rd.ml>, 2019.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ModemManager master\n"
+"Report-Msgid-Bugs-To: https://gitlab.freedesktop.org/mobile-broadband/"
+"ModemManager/issues\n"
+"POT-Creation-Date: 2020-09-18 10:30+0200\n"
+"PO-Revision-Date: 2019-05-03 00:10+0800\n"
+"Last-Translator: 王滋涵 <i@wi24rd.ml>\n"
+"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 2.2.1\n"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:13
+msgid "Control the Modem Manager daemon"
+msgstr "控制调制解调器管理器守护进程"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:14
+msgid "System policy prevents controlling the Modem Manager."
+msgstr "系统策略禁止控制调制解调器管理器。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:22
+msgid "Unlock and control a mobile broadband device"
+msgstr "解锁并控制移动宽带设备"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:23
+msgid ""
+"System policy prevents unlocking or controlling the mobile broadband device."
+msgstr "系统策略禁止解锁或控制移动宽带设备。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:31
+msgid "Add, modify, and delete mobile broadband contacts"
+msgstr "添加、修改或删除移动宽带联系人"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:32
+msgid ""
+"System policy prevents adding, modifying, or deleting this device's contacts."
+msgstr "系统策略禁止添加、修改或删除移动宽带联系人。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:40
+msgid "Send, save, modify, and delete text messages"
+msgstr "发送、保存修改或删除文本消息"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:41
+msgid ""
+"System policy prevents sending or manipulating this device's text messages."
+msgstr "系统策略禁止发送或操作此设备的文本消息。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:49
+msgid "Accept incoming voice calls or start outgoing voice calls."
+msgstr "接听传入的语音呼叫或者开始传出语音呼叫。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:50
+msgid "System policy prevents voice calls."
+msgstr "系统策略禁止语音呼叫。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:58
+msgid "Query network time and timezone information"
+msgstr ""
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:59
+#, fuzzy
+msgid "System policy prevents querying network time information."
+msgstr "系统策略禁止查询和利用网络信息和服务。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:67
+msgid "Enable and view geographic location and positioning information"
+msgstr "启用和查看地理位置和定位信息"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:68
+msgid ""
+"System policy prevents enabling or viewing geographic location information."
+msgstr "系统策略禁止启用和查看地理位置和定位信息。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:76
+msgid "Query and utilize network information and services"
+msgstr "查询和利用网络信息和服务"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:77
+msgid ""
+"System policy prevents querying or utilizing network information and "
+"services."
+msgstr "系统策略禁止查询和利用网络信息和服务。"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:85
+msgid "Query and manage firmware on a mobile broadband device"
+msgstr "在移动宽带设备上查询和管理固件"
+
+#: data/org.freedesktop.ModemManager1.policy.in.in:86
+msgid "System policy prevents querying or managing this device's firmware."
+msgstr "系统策略禁止在移动宽带设备上查询和管理固件。"
+
+#: src/mm-sleep-monitor.c:125
+msgid "ModemManager needs to reset devices"
+msgstr "调制解调器管理器需要重置设备"
diff --git a/src/77-mm-pcmcia-device-blacklist.rules b/src/77-mm-pcmcia-device-blacklist.rules
deleted file mode 100644
index f0f8474f..00000000
--- a/src/77-mm-pcmcia-device-blacklist.rules
+++ /dev/null
@@ -1,9 +0,0 @@
-# do not edit this file, it will be overwritten on update
-
-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
deleted file mode 100644
index faf4472b..00000000
--- a/src/77-mm-platform-serial-whitelist.rules
+++ /dev/null
@@ -1,13 +0,0 @@
-# do not edit this file, it will be overwritten on update
-
-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
-# are legacy devices that won't like probing. But often on embedded
-# systems serial ports are provided by platform devices.
-
-# Allow atmel_usart
-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
deleted file mode 100644
index 5076ea0d..00000000
--- a/src/77-mm-usb-device-blacklist.rules
+++ /dev/null
@@ -1,131 +0,0 @@
-# do not edit this file, it will be overwritten on update
-
-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"
-
-# APC UPS devices
-ATTRS{idVendor}=="051d", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Sweex 1000VA
-ATTRS{idVendor}=="0925", ATTRS{idProduct}=="1234", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Agiler UPS
-ATTRS{idVendor}=="05b8", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Krauler UP-M500VA
-ATTRS{idVendor}=="0001", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Ablerex 625L USB
-ATTRS{idVendor}=="ffff", ATTRS{idProduct}=="0000", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Belkin F6C1200-UNV
-ATTRS{idVendor}=="0665", ATTRS{idProduct}=="5161", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Various Liebert and Phoenixtec Power devices
-ATTRS{idVendor}=="06da", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Unitek Alpha 1200Sx
-ATTRS{idVendor}=="0f03", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Various Tripplite devices
-ATTRS{idVendor}=="09ae", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Various MGE Office Protection Systems devices
-ATTRS{idVendor}=="0463", ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="0463", ATTRS{idProduct}=="ffff", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# CyberPower 900AVR/BC900D
-ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0005", ENV{ID_MM_DEVICE_IGNORE}="1"
-# CyberPower CP1200AVR/BC1200D
-ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0501", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Various Belkin devices
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0980", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0900", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0910", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0912", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0551", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0751", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0375", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="1100", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# HP R/T 2200 INTL (like SMART2200RMXL2U)
-ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f0a", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Powerware devices
-ATTRS{idVendor}=="0592", ATTRS{idProduct}=="0002", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Palm Treo 700/900/etc
-# Shouldn't be probed themselves, but you can install programs like
-# "MobileStream USB Modem" which changes the USB PID of the device to something
-# that isn't blacklisted.
-ATTRS{idVendor}=="0830", ATTRS{idProduct}=="0061", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# GlobalScaleTechnologies SheevaPlug
-ATTRS{idVendor}=="9e88", ATTRS{idProduct}=="9e8f", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Atmel Corp at91sam SAMBA bootloader
-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"
-
-# All devices from the Swiss Federal Institute of Technology
-ATTRS{idVendor}=="0617", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# West Mountain Radio devices
-ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="814a", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="814b", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="2405", ATTRS{idProduct}=="0003", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Arduinos
-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"
-
-# All devices from Pololu Corporation
-# except some possible future products.
-ATTRS{idVendor}=="1ffb", ENV{ID_MM_DEVICE_IGNORE}="1"
-ATTRS{idVendor}=="1ffb", ATTRS{idProduct}=="00ad", ENV{ID_MM_DEVICE_IGNORE}="0"
-ATTRS{idVendor}=="1ffb", ATTRS{idProduct}=="00ae", ENV{ID_MM_DEVICE_IGNORE}="0"
-
-# Altair U-Boot device
-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"
-
-# All devices from 3D Robotics
-ATTRS{idVendor}=="26ac", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# empiriKit science lab controller device
-ATTRS{idVendor}=="0425", ATTRS{idProduct}=="0408", ENV{ID_MM_DEVICE_IGNORE}="1"
-
-# Infineon Flashloader used by Intel XMM modem bootloader
-ATTRS{idVendor}=="8087", ATTRS{idProduct}=="0716", 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
deleted file mode 100644
index 3f3e090e..00000000
--- a/src/77-mm-usb-serial-adapters-greylist.rules
+++ /dev/null
@@ -1,38 +0,0 @@
-# do not edit this file, it will be overwritten on update
-
-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"
-
-# Belkin F5U183 Serial Adapter
-ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0103", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
-
-# FTDI-based serial adapters
-# FTDI does USB to serial converter ICs; and it's very likely that they'll
-# never do modems themselves, so it should be safe to add a rule only based
-# on the vendor Id.
-ATTRS{idVendor}=="0403", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
-
-# ATEN Intl UC-232A (Prolific)
-ATTRS{idVendor}=="0557", ATTRS{idProduct}=="2008", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
-
-# Prolific USB to Serial adapter
-ATTRS{idVendor}=="067b", ATTRS{idProduct}=="2303", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
-
-# Magic Control Technology Corp adapters
-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"
-
-# Atmel Corp. LUFA USB to Serial Adapter Project (Arduino)
-ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="204b", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
-
-# Netchip Technology, Inc. Linux-USB Serial Gadget (CDC ACM mode)
-ATTRS{idVendor}=="0525", ATTRS{idProduct}=="a4a7", ENV{ID_MM_DEVICE_MANUAL_SCAN_ONLY}="1"
-
-LABEL="mm_usb_serial_adapters_greylist_end"
diff --git a/src/80-mm-candidate.rules b/src/80-mm-candidate.rules
index 2e938d7a..2688092b 100644
--- a/src/80-mm-candidate.rules
+++ b/src/80-mm-candidate.rules
@@ -7,11 +7,35 @@
# that don't have this tag. MM will still get the udev 'add' event for the
# device a short while later and then process it as normal.
-ACTION!="add|change|move", GOTO="mm_candidate_end"
+ACTION!="add|change|move|bind", GOTO="mm_candidate_end"
+
+# Opening bound but disconnected Bluetooth RFCOMM ttys would initiate the
+# connection. Don't do that.
+KERNEL=="rfcomm*", DEVPATH=="*/virtual/*", GOTO="mm_candidate_end"
SUBSYSTEM=="tty", ENV{ID_MM_CANDIDATE}="1"
SUBSYSTEM=="net", ENV{ID_MM_CANDIDATE}="1"
-KERNEL=="cdc-wdm*", SUBSYSTEM=="usb", ENV{ID_MM_CANDIDATE}="1"
-KERNEL=="cdc-wdm*", SUBSYSTEM=="usbmisc", ENV{ID_MM_CANDIDATE}="1"
+KERNEL=="cdc-wdm[0-9]*", SUBSYSTEM=="usbmisc", ENV{ID_MM_CANDIDATE}="1"
+
+# WWAN subsystem port handling
+# - All USB devices ignored for now, only PCI devices expected
+# - Only "wwan_port" device types processed (single ports); we fully ignore
+# the "wwan_dev" device type (full device, not just one port)
+# - FIREHOSE ports are used for firmware updates and not managed by ModemManager
+SUBSYSTEMS=="usb", GOTO="mm_candidate_end"
+SUBSYSTEM=="wwan", ENV{DEVTYPE}=="wwan_dev", GOTO="mm_candidate_end"
+SUBSYSTEM=="wwan", ENV{ID_MM_CANDIDATE}="1"
+SUBSYSTEM=="wwan", ATTR{type}=="AT", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+SUBSYSTEM=="wwan", ATTR{type}=="MBIM", ENV{ID_MM_PORT_TYPE_MBIM}="1"
+SUBSYSTEM=="wwan", ATTR{type}=="QMI", ENV{ID_MM_PORT_TYPE_QMI}="1"
+SUBSYSTEM=="wwan", ATTR{type}=="QCDM", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+SUBSYSTEM=="wwan", ATTR{type}=="FIREHOSE", ENV{ID_MM_PORT_IGNORE}="1"
+
+# Linux 5.13 does not have "type" attribute yet, match kernel name instead
+SUBSYSTEM=="wwan", KERNEL=="*MBIM", ENV{ID_MM_PORT_TYPE_MBIM}="1"
+SUBSYSTEM=="wwan", KERNEL=="*QMI", ENV{ID_MM_PORT_TYPE_QMI}="1"
+SUBSYSTEM=="wwan", KERNEL=="*AT", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1"
+SUBSYSTEM=="wwan", KERNEL=="*QCDM", ENV{ID_MM_PORT_TYPE_QCDM}="1"
+SUBSYSTEM=="wwan", KERNEL=="*FIREHOSE", ENV{ID_MM_PORT_IGNORE}="1"
LABEL="mm_candidate_end"
diff --git a/src/Makefile.am b/src/Makefile.am
index 6912b00e..b4899e4c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,28 +1,109 @@
-SUBDIRS=. tests
-udevrulesdir = $(UDEV_BASE_DIR)/rules.d
-udevrules_DATA = \
- 77-mm-usb-device-blacklist.rules \
- 77-mm-pcmcia-device-blacklist.rules \
- 77-mm-platform-serial-whitelist.rules \
- 77-mm-usb-serial-adapters-greylist.rules \
- 80-mm-candidate.rules
+SUBDIRS = . tests
-noinst_LTLIBRARIES = libmodem-helpers.la libport.la
+################################################################################
+# common
+################################################################################
-libmodem_helpers_la_CPPFLAGS = \
+sbin_PROGRAMS =
+noinst_LTLIBRARIES =
+EXTRA_DIST =
+BUILT_SOURCES =
+CLEANFILES =
+
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
$(MM_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ $(GUDEV_CFLAGS) \
-I$(top_srcdir) \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
+ -I$(top_srcdir)/libqcdm/src \
-I$(top_srcdir)/libmm-glib \
- -I${top_srcdir}/libmm-glib/generated \
- -I${top_builddir}/libmm-glib/generated
+ -I${top_builddir}/libmm-glib/generated \
+ -I${top_builddir}/libmm-glib/generated/tests \
+ -I$(srcdir)/kerneldevice \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(MM_LIBS) \
+ $(CODE_COVERAGE_LDFLAGS) \
+ $(GUDEV_LIBS) \
+ $(NULL)
+
+if WITH_QRTR
+AM_CFLAGS += $(QRTR_CFLAGS)
+AM_LDFLAGS += $(QRTR_LIBS)
+endif
+
+if WITH_QMI
+AM_CFLAGS += $(QMI_CFLAGS)
+AM_LDFLAGS += $(QMI_LIBS)
+endif
+
+if WITH_MBIM
+AM_CFLAGS += $(MBIM_CFLAGS)
+AM_LDFLAGS += $(MBIM_LIBS)
+endif
+
+if WITH_POLKIT
+AM_CFLAGS += $(POLKIT_CFLAGS)
+AM_LDFLAGS += $(POLKIT_LIBS)
+endif
+
+if WITH_SYSTEMD_JOURNAL
+AM_CFLAGS += $(LIBSYSTEMD_CFLAGS)
+AM_LDFLAGS += $(LIBSYSTEMD_LIBS)
+endif
+
+################################################################################
+# generic udev rules
+################################################################################
+
+udevrulesdir = $(UDEV_BASE_DIR)/rules.d
+udevrules_DATA = \
+ 80-mm-candidate.rules \
+ $(NULL)
-libmodem_helpers_la_LIBADD = \
- $(top_builddir)/libmm-glib/libmm-glib.la
+EXTRA_DIST += $(udevrules_DATA)
-libmodem_helpers_la_SOURCES = \
+################################################################################
+# helpers library
+################################################################################
+
+noinst_LTLIBRARIES += libhelpers.la
+
+HELPER_ENUMS_INPUTS = \
+ $(srcdir)/mm-sms-part.h \
+ $(srcdir)/mm-modem-helpers.h \
+ $(NULL)
+
+HELPER_ENUMS_GENERATED = \
+ mm-helper-enums-types.h \
+ mm-helper-enums-types.c \
+ $(NULL)
+
+mm-helper-enums-types.h: Makefile.am $(HELPER_ENUMS_INPUTS) $(top_srcdir)/build-aux/mm-enums-types.h.template
+ $(AM_V_GEN) $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-sms-part.h\"\n#include \"mm-modem-helpers.h\"\n#ifndef __MM_HELPER_ENUMS_TYPES_H__\n#define __MM_HELPER_ENUMS_TYPES_H__\n" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
+ --ftail "#endif /* __MM_HELPER_ENUMS_TYPES_H__ */\n" \
+ $(HELPER_ENUMS_INPUTS) > $@
+
+mm-helper-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-types.c.template mm-helper-enums-types.h
+ $(AM_V_GEN) $(GLIB_MKENUMS) \
+ --fhead "#include \"mm-helper-enums-types.h\"" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
+ $(HELPER_ENUMS_INPUTS) > $@
+
+libhelpers_la_SOURCES = \
+ mm-log-object.h \
+ mm-log-object.c \
+ mm-log.c \
+ mm-log.h \
+ mm-log-test.h \
mm-error-helpers.c \
mm-error-helpers.h \
mm-modem-helpers.c \
@@ -34,58 +115,107 @@ libmodem_helpers_la_SOURCES = \
mm-sms-part-3gpp.h \
mm-sms-part-3gpp.c \
mm-sms-part-cdma.h \
- mm-sms-part-cdma.c
+ mm-sms-part-cdma.c \
+ $(NULL)
+
+nodist_libhelpers_la_SOURCES = $(HELPER_ENUMS_GENERATED)
-# Additional QMI support in libmodem-helpers
if WITH_QMI
-libmodem_helpers_la_SOURCES += \
+libhelpers_la_SOURCES += \
mm-modem-helpers-qmi.c \
- mm-modem-helpers-qmi.h
-libmodem_helpers_la_CPPFLAGS += $(QMI_CFLAGS)
+ mm-modem-helpers-qmi.h \
+ $(NULL)
endif
-# Additional MBIM support in libmodem-helpers
if WITH_MBIM
-libmodem_helpers_la_SOURCES += \
+libhelpers_la_SOURCES += \
mm-modem-helpers-mbim.c \
- mm-modem-helpers-mbim.h
-libmodem_helpers_la_CPPFLAGS += $(MBIM_CFLAGS)
+ mm-modem-helpers-mbim.h \
+ $(NULL)
endif
-# libport specific enum types
-PORT_ENUMS = \
+# Request to build enum types before anything else
+BUILT_SOURCES += $(HELPER_ENUMS_GENERATED)
+CLEANFILES += $(HELPER_ENUMS_GENERATED)
+
+################################################################################
+# kerneldevice library
+################################################################################
+
+noinst_LTLIBRARIES += libkerneldevice.la
+
+libkerneldevice_la_CPPFLAGS = \
+ -DUDEVRULESDIR=\"$(udevrulesdir)\" \
+ $(NULL)
+
+libkerneldevice_la_SOURCES = \
+ kerneldevice/mm-kernel-device.h \
+ kerneldevice/mm-kernel-device.c \
+ kerneldevice/mm-kernel-device-helpers.h \
+ kerneldevice/mm-kernel-device-helpers.c \
+ kerneldevice/mm-kernel-device-generic.h \
+ kerneldevice/mm-kernel-device-generic.c \
+ kerneldevice/mm-kernel-device-generic-rules.h \
+ kerneldevice/mm-kernel-device-generic-rules.c \
+ $(NULL)
+
+if WITH_QRTR
+libkerneldevice_la_SOURCES += \
+ kerneldevice/mm-kernel-device-qrtr.h \
+ kerneldevice/mm-kernel-device-qrtr.c \
+ $(NULL)
+endif
+
+if WITH_UDEV
+libkerneldevice_la_SOURCES += \
+ kerneldevice/mm-kernel-device-udev.h \
+ kerneldevice/mm-kernel-device-udev.c \
+ $(NULL)
+endif
+
+libkerneldevice_la_LIBADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(builddir)/libhelpers.la \
+ $(NULL)
+
+################################################################################
+# ports library
+################################################################################
+
+noinst_LTLIBRARIES += libport.la
+
+PORT_ENUMS_INPUTS = \
$(srcdir)/mm-port.h \
- $(srcdir)/mm-port-serial-at.h
+ $(srcdir)/mm-port-serial-at.h \
+ $(NULL)
-mm-port-enums-types.h: Makefile.am $(PORT_ENUMS) $(top_srcdir)/build-aux/mm-enums-template.h
+if WITH_QMI
+PORT_ENUMS_INPUTS += $(srcdir)/mm-port-qmi.h
+endif
+
+PORT_ENUMS_GENERATED = \
+ mm-port-enums-types.h \
+ mm-port-enums-types.c \
+ $(NULL)
+
+mm-port-enums-types.h: Makefile.am $(PORT_ENUMS_INPUTS) $(top_srcdir)/build-aux/mm-enums-types.h.template
$(AM_V_GEN) $(GLIB_MKENUMS) \
- --fhead "#include \"mm-port.h\"\n#include \"mm-port-serial-at.h\"\n#ifndef __MM_PORT_ENUMS_TYPES_H__\n#define __MM_PORT_ENUMS_TYPES_H__\n" \
- --template $(top_srcdir)/build-aux/mm-enums-template.h \
+ --fhead "#include \"config.h\"\n#include \"mm-port.h\"\n#include \"mm-port-serial-at.h\"\n#if defined WITH_QMI\n#include \"mm-port-qmi.h\"\n#endif\n#ifndef __MM_PORT_ENUMS_TYPES_H__\n#define __MM_PORT_ENUMS_TYPES_H__\n" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
--ftail "#endif /* __MM_PORT_ENUMS_TYPES_H__ */\n" \
- $(PORT_ENUMS) > $@
+ $(PORT_ENUMS_INPUTS) > $@
-mm-port-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-template.c mm-port-enums-types.h
+mm-port-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-types.c.template mm-port-enums-types.h
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#include \"mm-port-enums-types.h\"" \
- --template $(top_srcdir)/build-aux/mm-enums-template.c \
- $(PORT_ENUMS) > $@
-
-libport_la_CPPFLAGS = \
- $(MM_CFLAGS) \
- -I$(top_srcdir) \
- -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
-
-nodist_libport_la_SOURCES = \
- mm-port-enums-types.h \
- mm-port-enums-types.c
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
+ $(PORT_ENUMS_INPUTS) > $@
libport_la_SOURCES = \
mm-port.c \
mm-port.h \
+ mm-port-net.c \
+ mm-port-net.h \
mm-port-serial.c \
mm-port-serial.h \
mm-port-serial-at.c \
@@ -93,82 +223,96 @@ libport_la_SOURCES = \
mm-port-serial-qcdm.c \
mm-port-serial-qcdm.h \
mm-port-serial-gps.c \
- mm-port-serial-gps.h
+ mm-port-serial-gps.h \
+ mm-serial-parsers.c \
+ mm-serial-parsers.h \
+ mm-netlink.h \
+ mm-netlink.c \
+ $(NULL)
+
+nodist_libport_la_SOURCES = $(PORT_ENUMS_GENERATED)
-# Additional QMI support in libserial
if WITH_QMI
libport_la_SOURCES += \
mm-port-qmi.c \
- mm-port-qmi.h
-libport_la_CPPFLAGS += $(QMI_CFLAGS)
+ mm-port-qmi.h \
+ $(NULL)
endif
-# Additional MBIM support in libserial
if WITH_MBIM
libport_la_SOURCES += \
mm-port-mbim.c \
- mm-port-mbim.h
-libport_la_CPPFLAGS += $(MBIM_CFLAGS)
+ mm-port-mbim.h \
+ $(NULL)
endif
-# Daemon specific enum types
-DAEMON_ENUMS = \
+libport_la_LIBADD = \
+ $(top_builddir)/libqcdm/src/libqcdm.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(builddir)/libkerneldevice.la \
+ $(NULL)
+
+# Request to build enum types before anything else
+BUILT_SOURCES += $(PORT_ENUMS_GENERATED)
+CLEANFILES += $(PORT_ENUMS_GENERATED)
+
+################################################################################
+# ModemManager daemon
+################################################################################
+
+sbin_PROGRAMS += ModemManager
+
+DAEMON_ENUMS_INPUTS = \
+ $(srcdir)/mm-filter.h \
$(srcdir)/mm-base-bearer.h \
- $(srcdir)/mm-port-probe.h
+ $(srcdir)/mm-port-probe.h \
+ $(NULL)
-mm-daemon-enums-types.h: Makefile.am $(DAEMON_ENUMS) $(top_srcdir)/build-aux/mm-enums-template.h
+DAEMON_ENUMS_GENERATED = \
+ mm-daemon-enums-types.h \
+ mm-daemon-enums-types.c \
+ $(NULL)
+
+mm-daemon-enums-types.h: Makefile.am $(DAEMON_ENUMS_INPUTS) $(top_srcdir)/build-aux/mm-enums-types.h.template
$(AM_V_GEN) $(GLIB_MKENUMS) \
- --fhead "#include \"mm-base-bearer.h\"\n#include \"mm-port-probe.h\"\n#ifndef __MM_DAEMON_ENUMS_TYPES_H__\n#define __MM_DAEMON_ENUMS_TYPES_H__\n" \
- --template $(top_srcdir)/build-aux/mm-enums-template.h \
+ --fhead "#include \"mm-filter.h\"\n#include \"mm-base-bearer.h\"\n#include \"mm-port-probe.h\"\n#ifndef __MM_DAEMON_ENUMS_TYPES_H__\n#define __MM_DAEMON_ENUMS_TYPES_H__\n" \
+ --template $(top_srcdir)/build-aux/mm-enums-types.h.template \
--ftail "#endif /* __MM_DAEMON_ENUMS_TYPES_H__ */\n" \
- $(DAEMON_ENUMS) > $@
+ $(DAEMON_ENUMS_INPUTS) > $@
-mm-daemon-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-template.c mm-daemon-enums-types.h
+mm-daemon-enums-types.c: Makefile.am $(top_srcdir)/build-aux/mm-enums-types.c.template mm-daemon-enums-types.h
$(AM_V_GEN) $(GLIB_MKENUMS) \
--fhead "#include \"mm-daemon-enums-types.h\"" \
- --template $(top_srcdir)/build-aux/mm-enums-template.c \
- $(DAEMON_ENUMS) > $@
+ --template $(top_srcdir)/build-aux/mm-enums-types.c.template \
+ $(DAEMON_ENUMS_INPUTS) > $@
-sbin_PROGRAMS = ModemManager
+# Request to build enum types before anything else
+BUILT_SOURCES += $(DAEMON_ENUMS_GENERATED)
+CLEANFILES += $(DAEMON_ENUMS_GENERATED)
ModemManager_CPPFLAGS = \
- $(MM_CFLAGS) \
- $(GUDEV_CFLAGS) \
- -I$(top_srcdir) \
- -I$(top_srcdir)/include \
- -I$(top_builddir)/include \
- -I$(top_srcdir)/libmm-glib \
- -I$(top_builddir)/libmm-glib \
- -I${top_srcdir}/libmm-glib/generated \
- -I${top_builddir}/libmm-glib/generated \
- -I${top_srcdir}/libmm-glib/generated/tests \
- -I${top_builddir}/libmm-glib/generated/tests \
- -DPLUGINDIR=\"$(pkglibdir)\"
+ -DPLUGINDIR=\"$(pkglibdir)\" \
+ -DMM_COMPILATION \
+ $(NULL)
ModemManager_LDADD = \
- $(MM_LIBS) \
- $(GUDEV_LIBS) \
- $(builddir)/libmodem-helpers.la \
- $(builddir)/libport.la \
$(top_builddir)/libqcdm/src/libqcdm.la \
- $(top_builddir)/libmm-glib/generated/tests/libmm-test-generated.la
-
-nodist_ModemManager_SOURCES = \
- mm-daemon-enums-types.h \
- mm-daemon-enums-types.c
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(top_builddir)/libmm-glib/generated/tests/libmm-test-generated.la \
+ $(builddir)/libport.la \
+ $(NULL)
ModemManager_SOURCES = \
main.c \
mm-context.h \
mm-context.c \
- mm-log.c \
- mm-log.h \
+ mm-utils.h \
mm-private-boxed-types.h \
mm-private-boxed-types.c \
- mm-auth.h \
- mm-auth.c \
mm-auth-provider.h \
mm-auth-provider.c \
+ mm-filter.h \
+ mm-filter.c \
mm-base-manager.c \
mm-base-manager.h \
mm-device.c \
@@ -189,12 +333,18 @@ ModemManager_SOURCES = \
mm-base-modem.c \
mm-base-sms.h \
mm-base-sms.c \
+ mm-base-call.h \
+ mm-base-call.c \
mm-sms-list.h \
mm-sms-list.c \
+ mm-call-list.h \
+ mm-call-list.c \
mm-iface-modem.h \
mm-iface-modem.c \
mm-iface-modem-3gpp.h \
mm-iface-modem-3gpp.c \
+ mm-iface-modem-3gpp-profile-manager.h \
+ mm-iface-modem-3gpp-profile-manager.c \
mm-iface-modem-3gpp-ussd.h \
mm-iface-modem-3gpp-ussd.c \
mm-iface-modem-cdma.h \
@@ -205,50 +355,59 @@ ModemManager_SOURCES = \
mm-iface-modem-location.c \
mm-iface-modem-messaging.h \
mm-iface-modem-messaging.c \
+ mm-iface-modem-voice.h \
+ mm-iface-modem-voice.c \
mm-iface-modem-time.h \
mm-iface-modem-time.c \
mm-iface-modem-firmware.h \
mm-iface-modem-firmware.c \
+ mm-iface-modem-sar.h \
+ mm-iface-modem-sar.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
-
-# Additional dependency rules
-mm-base-bearer.c: mm-daemon-enums-types.h
+ mm-plugin.h \
+ mm-shared.h \
+ $(NULL)
-# Additional Polkit support
-if WITH_POLKIT
+if WITH_QRTR
ModemManager_SOURCES += \
- mm-auth-provider-polkit.c \
- mm-auth-provider-polkit.h
-ModemManager_LDADD += $(POLKIT_LIBS)
-ModemManager_CPPFLAGS += $(POLKIT_CFLAGS)
+ mm-qrtr-bus-watcher.h \
+ mm-qrtr-bus-watcher.c \
+ $(NULL)
+endif
+
+nodist_ModemManager_SOURCES = $(DAEMON_ENUMS_GENERATED)
+
+# Additional suspend/resume support via systemd
+if WITH_SYSTEMD_SUSPEND_RESUME
+ModemManager_SOURCES += mm-sleep-monitor.h mm-sleep-monitor.c
endif
# Additional QMI support in ModemManager
if WITH_QMI
ModemManager_SOURCES += \
+ mm-shared-qmi.h \
+ mm-shared-qmi.c \
mm-sms-qmi.h \
mm-sms-qmi.c \
mm-sim-qmi.h \
mm-sim-qmi.c \
+ mm-call-qmi.h \
+ mm-call-qmi.c \
mm-bearer-qmi.h \
mm-bearer-qmi.c \
mm-broadband-modem-qmi.h \
- mm-broadband-modem-qmi.c
-ModemManager_CPPFLAGS += $(QMI_CFLAGS)
-ModemManager_LDADD += $(QMI_LIBS)
+ mm-broadband-modem-qmi.c \
+ $(NULL)
endif
# Additional MBIM support in ModemManager
@@ -261,16 +420,6 @@ ModemManager_SOURCES += \
mm-bearer-mbim.h \
mm-bearer-mbim.c \
mm-broadband-modem-mbim.h \
- mm-broadband-modem-mbim.c
-ModemManager_CPPFLAGS += $(MBIM_CFLAGS)
-ModemManager_LDADD += $(MBIM_LIBS)
+ mm-broadband-modem-mbim.c \
+ $(NULL)
endif
-
-EXTRA_DIST = \
- $(udevrules_DATA)
-
-CLEANFILES = \
- mm-daemon-enums-types.h \
- mm-daemon-enums-types.c \
- mm-port-enums-types.h \
- mm-port-enums-types.c
diff --git a/src/kerneldevice/mm-kernel-device-generic-rules.c b/src/kerneldevice/mm-kernel-device-generic-rules.c
new file mode 100644
index 00000000..2e955a8c
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-generic-rules.c
@@ -0,0 +1,442 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-log.h"
+#include "mm-kernel-device-generic-rules.h"
+
+static void
+udev_rule_match_clear (MMUdevRuleMatch *rule_match)
+{
+ g_free (rule_match->parameter);
+ g_free (rule_match->value);
+}
+
+static void
+udev_rule_clear (MMUdevRule *rule)
+{
+ switch (rule->result.type) {
+ case MM_UDEV_RULE_RESULT_TYPE_PROPERTY:
+ g_free (rule->result.content.property.name);
+ g_free (rule->result.content.property.value);
+ break;
+ case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG:
+ case MM_UDEV_RULE_RESULT_TYPE_LABEL:
+ g_free (rule->result.content.tag);
+ break;
+ case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX:
+ case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN:
+ default:
+ break;
+ }
+
+ if (rule->conditions)
+ g_array_unref (rule->conditions);
+}
+
+static gboolean
+split_item (const gchar *item,
+ gchar **out_left,
+ gchar **out_operator,
+ gchar **out_right,
+ GError **error)
+{
+ const gchar *aux;
+ gchar *left = NULL;
+ gchar *operator = NULL;
+ gchar *right = NULL;
+ GError *inner_error = NULL;
+
+ g_assert (item && out_left && out_operator && out_right);
+
+ /* Get left/operator/right */
+ if (((aux = strstr (item, "==")) != NULL) || ((aux = strstr (item, "!=")) != NULL)) {
+ operator = g_strndup (aux, 2);
+ right = g_strdup (aux + 2);
+ } else if ((aux = strstr (item, "=")) != NULL) {
+ operator = g_strndup (aux, 1);
+ right = g_strdup (aux + 1);
+ } else {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid rule item, missing operator: '%s'", item);
+ goto out;
+ }
+
+ left = g_strndup (item, (aux - item));
+ g_strstrip (left);
+ if (!left[0]) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid rule item, missing left field: '%s'", item);
+ goto out;
+ }
+
+ g_strdelimit (right, "\"", ' ');
+ g_strstrip (right);
+ if (!right[0]) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid rule item, missing right field: '%s'", item);
+ goto out;
+ }
+
+out:
+ if (inner_error) {
+ g_free (left);
+ g_free (operator);
+ g_free (right);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *out_left = left;
+ *out_operator = operator;
+ *out_right = right;
+ return TRUE;
+}
+
+static gboolean
+load_rule_result (MMUdevRuleResult *rule_result,
+ const gchar *item,
+ GError **error)
+{
+ gchar *left;
+ gchar *operator;
+ gchar *right;
+ GError *inner_error = NULL;
+ gsize left_len;
+
+ if (!split_item (item, &left, &operator, &right, error))
+ return FALSE;
+
+ if (!g_str_equal (operator, "=")) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid rule result operator: '%s'", item);
+ goto out;
+ }
+
+ if (g_str_equal (left, "LABEL")) {
+ rule_result->type = MM_UDEV_RULE_RESULT_TYPE_LABEL;
+ rule_result->content.tag = right;
+ right = NULL;
+ goto out;
+ }
+
+ if (g_str_equal (left, "GOTO")) {
+ rule_result->type = MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG;
+ rule_result->content.tag = right;
+ right = NULL;
+ goto out;
+ }
+
+ left_len = strlen (left);
+ if (g_str_has_prefix (left, "ENV{") && left[left_len - 1] == '}') {
+ rule_result->type = MM_UDEV_RULE_RESULT_TYPE_PROPERTY;
+ rule_result->content.property.name = g_strndup (left + 4, left_len - 5);
+ rule_result->content.property.value = right;
+ right = NULL;
+ goto out;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid rule result parameter: '%s'", item);
+
+out:
+ g_free (left);
+ g_free (operator);
+ g_free (right);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+load_rule_match (MMUdevRuleMatch *rule_match,
+ const gchar *item,
+ GError **error)
+{
+ gchar *left;
+ gchar *operator;
+ gchar *right;
+
+ if (!split_item (item, &left, &operator, &right, error))
+ return FALSE;
+
+ if (g_str_equal (operator, "=="))
+ rule_match->type = MM_UDEV_RULE_MATCH_TYPE_EQUAL;
+ else if (g_str_equal (operator, "!="))
+ rule_match->type = MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL;
+ else {
+ g_free (left);
+ g_free (operator);
+ g_free (right);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid rule match, wrong match type: '%s'", item);
+
+ return FALSE;
+ }
+
+ g_free (operator);
+ rule_match->parameter = left;
+ rule_match->value = right;
+ return TRUE;
+}
+
+static gboolean
+load_rule_from_line (MMUdevRule *rule,
+ const gchar *line,
+ GError **error)
+{
+ gchar **split;
+ guint n_items;
+ GError *inner_error = NULL;
+
+ split = g_strsplit (line, ",", -1);
+ n_items = g_strv_length (split);
+
+ /* Conditions */
+ if (n_items > 1) {
+ guint i;
+
+ rule->conditions = g_array_sized_new (FALSE, FALSE, sizeof (MMUdevRuleMatch), n_items - 1);
+ g_array_set_clear_func (rule->conditions, (GDestroyNotify) udev_rule_match_clear);
+
+ /* All items except for the last one are conditions */
+ for (i = 0; !inner_error && i < (n_items - 1); i++) {
+ MMUdevRuleMatch rule_match = { 0 };
+
+ /* If condition correctly preloaded, add it to the rule */
+ if (!load_rule_match (&rule_match, split[i], &inner_error))
+ goto out;
+ g_assert (rule_match.type != MM_UDEV_RULE_MATCH_TYPE_UNKNOWN);
+ g_assert (rule_match.parameter);
+ g_assert (rule_match.value);
+ g_array_append_val (rule->conditions, rule_match);
+ }
+ }
+
+ /* Last item, the result */
+ if (!load_rule_result (&rule->result, split[n_items - 1], &inner_error))
+ goto out;
+
+ g_assert ((rule->result.type == MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG && rule->result.content.tag) ||
+ (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_LABEL && rule->result.content.tag) ||
+ (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_PROPERTY && rule->result.content.property.name && rule->result.content.property.value));
+
+out:
+ g_strfreev (split);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static gboolean
+process_goto_tags (GArray *rules,
+ guint first_rule_index,
+ GError **error)
+{
+ guint i;
+
+ for (i = first_rule_index; i < rules->len; i++) {
+ MMUdevRule *rule;
+
+ rule = &g_array_index (rules, MMUdevRule, i);
+ if (rule->result.type == MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG) {
+ guint j;
+ guint label_index = 0;
+
+ for (j = i + 1; j < rules->len; j++) {
+ MMUdevRule *walker;
+
+ walker = &g_array_index (rules, MMUdevRule, j);
+ if (walker->result.type == MM_UDEV_RULE_RESULT_TYPE_LABEL &&
+ g_str_equal (rule->result.content.tag, walker->result.content.tag)) {
+
+ if (label_index) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "More than one label '%s' found", rule->result.content.tag);
+ return FALSE;
+ }
+
+ label_index = j;
+ }
+ }
+
+ if (!label_index) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find label '%s'", rule->result.content.tag);
+ return FALSE;
+ }
+
+ rule->result.type = MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX;
+ g_free (rule->result.content.tag);
+ rule->result.content.index = label_index;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+load_rules_from_file (GArray *rules,
+ const gchar *path,
+ GError **error)
+{
+ GFile *file;
+ GFileInputStream *fistream;
+ GDataInputStream *distream = NULL;
+ GError *inner_error = NULL;
+ gchar *line;
+ guint first_rule_index;
+
+ first_rule_index = rules->len;
+
+ file = g_file_new_for_path (path);
+ fistream = g_file_read (file, NULL, &inner_error);
+ if (!fistream)
+ goto out;
+
+ distream = g_data_input_stream_new (G_INPUT_STREAM (fistream));
+
+ while (((line = g_data_input_stream_read_line_utf8 (distream, NULL, NULL, &inner_error)) != NULL) && !inner_error) {
+ const gchar *aux;
+
+ aux = line;
+ while (*aux == ' ')
+ aux++;
+ if (*aux != '#' && *aux != '\0') {
+ MMUdevRule rule = { 0 };
+
+ if (load_rule_from_line (&rule, aux, &inner_error))
+ g_array_append_val (rules, rule);
+ else
+ udev_rule_clear (&rule);
+ }
+ g_free (line);
+ }
+
+out:
+
+ if (distream)
+ g_object_unref (distream);
+ if (fistream)
+ g_object_unref (fistream);
+ g_object_unref (file);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (first_rule_index < rules->len && !process_goto_tags (rules, first_rule_index, error))
+ return FALSE;
+
+ return TRUE;
+}
+
+static GList *
+list_rule_files (const gchar *rules_dir_path)
+{
+ static const gchar *expected_rules_prefix[] = { "77-mm-", "78-mm-", "79-mm-", "80-mm-" };
+ GFile *udevrulesdir;
+ GFileEnumerator *enumerator;
+ GList *children = NULL;
+
+ udevrulesdir = g_file_new_for_path (rules_dir_path);
+ enumerator = g_file_enumerate_children (udevrulesdir,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (enumerator) {
+ GFileInfo *info;
+
+ /* If we get any kind of error, assume we need to stop enumerating */
+ while ((info = g_file_enumerator_next_file (enumerator, NULL, NULL)) != NULL) {
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (expected_rules_prefix); i++) {
+ if (g_str_has_prefix (g_file_info_get_name (info), expected_rules_prefix[i])) {
+ children = g_list_prepend (children, g_build_path (G_DIR_SEPARATOR_S, rules_dir_path, g_file_info_get_name (info), NULL));
+ break;
+ }
+ }
+ g_object_unref (info);
+ }
+ g_object_unref (enumerator);
+ }
+ g_object_unref (udevrulesdir);
+
+ return g_list_sort (children, (GCompareFunc) g_strcmp0);
+}
+
+GArray *
+mm_kernel_device_generic_rules_load (const gchar *rules_dir,
+ GError **error)
+{
+ GList *rule_files, *l;
+ GArray *rules;
+ GError *inner_error = NULL;
+
+ rules = g_array_new (FALSE, FALSE, sizeof (MMUdevRule));
+ g_array_set_clear_func (rules, (GDestroyNotify) udev_rule_clear);
+
+ /* List rule files in rules dir */
+ rule_files = list_rule_files (rules_dir);
+ if (!rule_files) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No rule files found in '%s'", rules_dir);
+ goto out;
+ }
+
+ /* Iterate over rule files */
+ for (l = rule_files; l; l = g_list_next (l)) {
+ if (!load_rules_from_file (rules, (const gchar *)(l->data), &inner_error))
+ goto out;
+ }
+
+ /* Fail if no rules were loaded */
+ if (rules->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No rules loaded");
+ goto out;
+ }
+
+out:
+ if (rule_files)
+ g_list_free_full (rule_files, g_free);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_array_unref (rules);
+ return NULL;
+ }
+
+ return rules;
+}
diff --git a/src/kerneldevice/mm-kernel-device-generic-rules.h b/src/kerneldevice/mm-kernel-device-generic-rules.h
new file mode 100644
index 00000000..db570d19
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-generic-rules.h
@@ -0,0 +1,62 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+ MM_UDEV_RULE_MATCH_TYPE_UNKNOWN,
+ MM_UDEV_RULE_MATCH_TYPE_EQUAL,
+ MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL,
+} MMUdevRuleMatchType;
+
+typedef struct {
+ MMUdevRuleMatchType type;
+ gchar *parameter;
+ gchar *value;
+} MMUdevRuleMatch;
+
+typedef enum {
+ MM_UDEV_RULE_RESULT_TYPE_UNKNOWN,
+ MM_UDEV_RULE_RESULT_TYPE_PROPERTY,
+ MM_UDEV_RULE_RESULT_TYPE_LABEL,
+ MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX,
+ MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG, /* internal use only */
+} MMUdevRuleResultType;
+
+typedef struct {
+ gchar *name;
+ gchar *value;
+} MMUdevRuleResultProperty;
+
+typedef struct {
+ MMUdevRuleResultType type;
+ union {
+ MMUdevRuleResultProperty property;
+ gchar *tag;
+ guint index;
+ } content;
+} MMUdevRuleResult;
+
+typedef struct {
+ GArray *conditions;
+ MMUdevRuleResult result;
+} MMUdevRule;
+
+GArray *mm_kernel_device_generic_rules_load (const gchar *rules_dir,
+ GError **error);
+
+G_END_DECLS
diff --git a/src/kerneldevice/mm-kernel-device-generic.c b/src/kerneldevice/mm-kernel-device-generic.c
new file mode 100644
index 00000000..bfdd8c37
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-generic.c
@@ -0,0 +1,1281 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include <ModemManager-tags.h>
+
+#include "mm-kernel-device-generic.h"
+#include "mm-kernel-device-generic-rules.h"
+#include "mm-kernel-device-helpers.h"
+#include "mm-log-object.h"
+#include "mm-utils.h"
+
+#if !defined UDEVRULESDIR
+# error UDEVRULESDIR is not defined
+#endif
+
+static void initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMKernelDeviceGeneric, mm_kernel_device_generic, MM_TYPE_KERNEL_DEVICE, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init))
+
+enum {
+ PROP_0,
+ PROP_PROPERTIES,
+ PROP_RULES,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMKernelDeviceGenericPrivate {
+ /* Input properties */
+ MMKernelEventProperties *properties;
+ /* Rules to apply */
+ GArray *rules;
+
+ /* Contents from sysfs */
+ gchar **drivers;
+ gchar **subsystems;
+ gchar *sysfs_path;
+ gchar *wwandev_sysfs_path;
+ gchar *interface_sysfs_path;
+ guint8 interface_class;
+ guint8 interface_subclass;
+ guint8 interface_protocol;
+ guint8 interface_number;
+ gchar *interface_description;
+ gchar *physdev_sysfs_path;
+ guint16 physdev_vid;
+ guint16 physdev_pid;
+ guint16 physdev_revision;
+ gchar *physdev_manufacturer;
+ gchar *physdev_product;
+};
+
+static gboolean
+has_sysfs_attribute (const gchar *path,
+ const gchar *attribute)
+{
+ g_autofree gchar *aux_filepath = NULL;
+
+ aux_filepath = g_strdup_printf ("%s/%s", path, attribute);
+ return g_file_test (aux_filepath, G_FILE_TEST_EXISTS);
+}
+
+static gchar *
+read_sysfs_attribute_as_string (const gchar *path,
+ const gchar *attribute)
+{
+ g_autofree gchar *aux = NULL;
+ gchar *contents = NULL;
+
+ aux = g_strdup_printf ("%s/%s", path, attribute);
+ if (g_file_get_contents (aux, &contents, NULL, NULL)) {
+ g_strdelimit (contents, "\r\n", ' ');
+ g_strstrip (contents);
+ }
+ return contents;
+}
+
+static guint
+read_sysfs_attribute_as_hex (const gchar *path,
+ const gchar *attribute)
+{
+ g_autofree gchar *contents = NULL;
+ guint val = 0;
+
+ contents = read_sysfs_attribute_as_string (path, attribute);
+ if (contents)
+ mm_get_uint_from_hex_str (contents, &val);
+ return val;
+}
+
+static gchar *
+read_sysfs_attribute_link_basename (const gchar *path,
+ const gchar *attribute)
+{
+ g_autofree gchar *aux_filepath = NULL;
+ g_autofree gchar *canonicalized_path = NULL;
+
+ aux_filepath = g_strdup_printf ("%s/%s", path, attribute);
+ if (!g_file_test (aux_filepath, G_FILE_TEST_EXISTS))
+ return NULL;
+
+ canonicalized_path = realpath (aux_filepath, NULL);
+ return g_path_get_basename (canonicalized_path);
+}
+
+static gchar *
+lookup_sysfs_attribute_as_string (MMKernelDeviceGeneric *self,
+ const gchar *attribute,
+ gboolean iterate)
+{
+ g_autofree gchar *iter = NULL;
+
+ /* if there is no parent sysfs path set, we look for the attribute
+ * only in the port sysfs path */
+ if (!self->priv->physdev_sysfs_path)
+ return read_sysfs_attribute_as_string (self->priv->sysfs_path, attribute);
+
+ iter = g_strdup (self->priv->sysfs_path);
+ while (iter) {
+ g_autofree gchar *parent = NULL;
+ gchar *value;
+
+ /* return first one found */
+ if ((value = read_sysfs_attribute_as_string (iter, attribute)) != NULL)
+ return value;
+ else if (!iterate)
+ break;
+
+ if (g_strcmp0 (iter, self->priv->physdev_sysfs_path) == 0)
+ break;
+ parent = g_path_get_dirname (iter);
+ g_clear_pointer (&iter, g_free);
+ iter = g_steal_pointer (&parent);
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+/* Load contents */
+
+static void
+preload_sysfs_path (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *tmp = NULL;
+
+ if (self->priv->sysfs_path)
+ return;
+
+ /* sysfs can be built directly using subsystem and name; e.g. for subsystem
+ * usbmisc and name cdc-wdm0:
+ * $ realpath /sys/class/usbmisc/cdc-wdm0
+ * /sys/devices/pci0000:00/0000:00:1d.0/usb4/4-1/4-1.3/4-1.3:1.8/usbmisc/cdc-wdm0
+ */
+ tmp = g_strdup_printf ("/sys/class/%s/%s",
+ mm_kernel_event_properties_get_subsystem (self->priv->properties),
+ mm_kernel_event_properties_get_name (self->priv->properties));
+
+ self->priv->sysfs_path = realpath (tmp, NULL);
+ if (!self->priv->sysfs_path || !g_file_test (self->priv->sysfs_path, G_FILE_TEST_EXISTS)) {
+ mm_obj_warn (self, "invalid sysfs path read for %s/%s",
+ mm_kernel_event_properties_get_subsystem (self->priv->properties),
+ mm_kernel_event_properties_get_name (self->priv->properties));
+ g_clear_pointer (&self->priv->sysfs_path, g_free);
+ }
+
+ if (self->priv->sysfs_path) {
+ const gchar *devpath;
+
+ mm_obj_dbg (self, "sysfs path: %s", self->priv->sysfs_path);
+ devpath = (g_str_has_prefix (self->priv->sysfs_path, "/sys") ?
+ &self->priv->sysfs_path[4] :
+ self->priv->sysfs_path);
+ g_object_set_data_full (G_OBJECT (self), "DEVPATH", g_strdup (devpath), g_free);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+preload_common_properties (MMKernelDeviceGeneric *self)
+{
+ if (self->priv->interface_sysfs_path) {
+ mm_obj_dbg (self, " ID_USB_INTERFACE_NUM: 0x%02x", self->priv->interface_number);
+ g_object_set_data_full (G_OBJECT (self), "ID_USB_INTERFACE_NUM", g_strdup_printf ("%02x", self->priv->interface_number), g_free);
+ }
+
+ if (self->priv->physdev_product) {
+ mm_obj_dbg (self, " ID_MODEL: %s", self->priv->physdev_product);
+ g_object_set_data_full (G_OBJECT (self), "ID_MODEL", g_strdup (self->priv->physdev_product), g_free);
+ }
+
+ if (self->priv->physdev_manufacturer) {
+ mm_obj_dbg (self, " ID_VENDOR: %s", self->priv->physdev_manufacturer);
+ g_object_set_data_full (G_OBJECT (self), "ID_VENDOR", g_strdup (self->priv->physdev_manufacturer), g_free);
+ }
+
+ if (self->priv->physdev_sysfs_path) {
+ mm_obj_dbg (self, " ID_VENDOR_ID: 0x%04x", self->priv->physdev_vid);
+ g_object_set_data_full (G_OBJECT (self), "ID_VENDOR_ID", g_strdup_printf ("%04x", self->priv->physdev_vid), g_free);
+ mm_obj_dbg (self, " ID_MODEL_ID: 0x%04x", self->priv->physdev_pid);
+ g_object_set_data_full (G_OBJECT (self), "ID_MODEL_ID", g_strdup_printf ("%04x", self->priv->physdev_pid), g_free);
+ mm_obj_dbg (self, " ID_REVISION: 0x%04x", self->priv->physdev_revision);
+ g_object_set_data_full (G_OBJECT (self), "ID_REVISION", g_strdup_printf ("%04x", self->priv->physdev_revision), g_free);
+ }
+}
+
+static void
+ptr_array_add_sysfs_attribute_link_basename (GPtrArray *array,
+ const gchar *sysfs_path,
+ const gchar *attribute,
+ gchar **out_value)
+{
+ g_autofree gchar *value = NULL;
+
+ g_assert (array && sysfs_path && attribute);
+ value = read_sysfs_attribute_link_basename (sysfs_path, attribute);
+
+ if (out_value)
+ *out_value = g_strdup (value);
+ if (value && !g_ptr_array_find_with_equal_func (array, value, g_str_equal, NULL))
+ g_ptr_array_add (array, g_steal_pointer (&value));
+
+}
+
+static void
+preload_contents_other (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *lower_device_name = NULL;
+ GPtrArray *drivers;
+ GPtrArray *subsystems;
+
+ /* For any other kind of bus (or the absence of one, as in virtual devices),
+ * assume this is a single port device and don't try to match multiple ports
+ * together. Also, obviously, no vendor, product, revision or interface. */
+
+ drivers = g_ptr_array_sized_new (2);
+ ptr_array_add_sysfs_attribute_link_basename (drivers, self->priv->sysfs_path, "driver", NULL);
+ g_ptr_array_add (drivers, NULL);
+ self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE);
+
+ subsystems = g_ptr_array_sized_new (2);
+ ptr_array_add_sysfs_attribute_link_basename (subsystems, self->priv->sysfs_path, "subsystem", NULL);
+ g_ptr_array_add (subsystems, NULL);
+ self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE);
+
+ /* But look for a lower real physical device, as we may have one */
+ lower_device_name = mm_kernel_device_get_lower_device_name (self->priv->sysfs_path);
+ if (lower_device_name) {
+ g_autoptr(MMKernelDevice) lower_kernel_device = NULL;
+ g_autoptr(MMKernelEventProperties) props = NULL;
+ g_autoptr(GError) error = NULL;
+ const gchar *subsystem;
+
+ subsystem = mm_kernel_device_get_subsystem (MM_KERNEL_DEVICE (self));
+
+ props = mm_kernel_event_properties_new ();
+ mm_kernel_event_properties_set_subsystem (props, subsystem);
+ mm_kernel_event_properties_set_name (props, lower_device_name);
+
+ lower_kernel_device = mm_kernel_device_generic_new (props, &error);
+ if (!lower_kernel_device) {
+ mm_obj_dbg (self, "couldn't find lower device: %s/%s", subsystem, lower_device_name);
+ } else {
+ mm_obj_dbg (self, "setting up lower device: %s/%s", subsystem, lower_device_name);
+ g_object_set (self,
+ "lower-device", lower_kernel_device,
+ NULL);
+ }
+ }
+}
+
+static void
+preload_contents_platform (MMKernelDeviceGeneric *self,
+ const gchar *platform)
+{
+ g_autofree gchar *iter = NULL;
+ GPtrArray *drivers;
+ GPtrArray *subsystems;
+
+ drivers = g_ptr_array_sized_new (3);
+ subsystems = g_ptr_array_sized_new (3);
+
+ iter = g_strdup (self->priv->sysfs_path);
+ while (iter && (g_strcmp0 (iter, "/") != 0)) {
+ gchar *parent;
+ g_autofree gchar *current_subsystem = NULL;
+
+ ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL);
+ ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", &current_subsystem);
+
+ /* Take first parent with the given platform subsystem as physical device */
+ current_subsystem = read_sysfs_attribute_link_basename (iter, "subsystem");
+ if (!self->priv->physdev_sysfs_path && (g_strcmp0 (current_subsystem, platform) == 0)) {
+ self->priv->physdev_sysfs_path = g_strdup (iter);
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ parent = g_path_get_dirname (iter);
+ g_clear_pointer (&iter, g_free);
+ iter = parent;
+ }
+
+ g_ptr_array_add (drivers, NULL);
+ self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE);
+ g_ptr_array_add (subsystems, NULL);
+ self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE);
+}
+
+static void
+preload_contents_pcmcia (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *iter = NULL;
+ GPtrArray *drivers;
+ GPtrArray *subsystems;
+ gboolean pcmcia_subsystem_found = FALSE;
+
+ drivers = g_ptr_array_sized_new (3);
+ subsystems = g_ptr_array_sized_new (3);
+
+ iter = g_strdup (self->priv->sysfs_path);
+ while (iter && (g_strcmp0 (iter, "/") != 0)) {
+ g_autofree gchar *parent = NULL;
+ g_autofree gchar *parent_subsystem = NULL;
+ g_autofree gchar *current_subsystem = NULL;
+
+ ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL);
+ ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", &current_subsystem);
+
+ if (g_strcmp0 (current_subsystem, "pcmcia") == 0)
+ pcmcia_subsystem_found = TRUE;
+
+ parent = g_path_get_dirname (iter);
+ if (parent)
+ parent_subsystem = read_sysfs_attribute_link_basename (parent, "subsystem");
+
+ if (pcmcia_subsystem_found && parent_subsystem && (g_strcmp0 (parent_subsystem, "pcmcia") != 0)) {
+ self->priv->physdev_sysfs_path = g_strdup (iter);
+ self->priv->physdev_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "manf_id");
+ self->priv->physdev_pid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "card_id");
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ g_clear_pointer (&iter, g_free);
+ iter = g_steal_pointer (&parent);
+ }
+
+ g_ptr_array_add (drivers, NULL);
+ self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE);
+ g_ptr_array_add (subsystems, NULL);
+ self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE);
+}
+
+static void
+preload_contents_pci (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *iter = NULL;
+ GPtrArray *drivers;
+ GPtrArray *subsystems;
+
+ drivers = g_ptr_array_sized_new (4);
+ subsystems = g_ptr_array_sized_new (4);
+
+ iter = g_strdup (self->priv->sysfs_path);
+ while (iter && (g_strcmp0 (iter, "/") != 0)) {
+ g_autofree gchar *current_subsystem = NULL;
+ gchar *parent;
+
+ ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL);
+ ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", &current_subsystem);
+
+ /* the PCI channel specific devices have their own drivers and
+ * subsystems, we can rely on the physical device being the first
+ * one that reports the 'pci' subsystem */
+ if (!self->priv->physdev_sysfs_path && (g_strcmp0 (current_subsystem, "pci") == 0)) {
+ self->priv->physdev_sysfs_path = g_strdup (iter);
+ self->priv->physdev_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "vendor");
+ self->priv->physdev_pid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "device");
+ self->priv->physdev_revision = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "revision");
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ parent = g_path_get_dirname (iter);
+ g_clear_pointer (&iter, g_free);
+ iter = parent;
+ }
+
+ g_ptr_array_add (drivers, NULL);
+ self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE);
+ g_ptr_array_add (subsystems, NULL);
+ self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE);
+}
+
+static void
+preload_contents_usb (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *iter = NULL;
+ GPtrArray *drivers;
+ GPtrArray *subsystems;
+
+ drivers = g_ptr_array_sized_new (4);
+ subsystems = g_ptr_array_sized_new (4);
+
+ iter = g_strdup (self->priv->sysfs_path);
+ while (iter && (g_strcmp0 (iter, "/") != 0)) {
+ gchar *parent;
+
+ ptr_array_add_sysfs_attribute_link_basename (drivers, iter, "driver", NULL);
+ ptr_array_add_sysfs_attribute_link_basename (subsystems, iter, "subsystem", NULL);
+
+ /* is this the USB interface? */
+ if (!self->priv->interface_sysfs_path && has_sysfs_attribute (iter, "bInterfaceClass")) {
+ self->priv->interface_sysfs_path = g_strdup (iter);
+ self->priv->interface_class = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceClass");
+ self->priv->interface_subclass = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceSubClass");
+ self->priv->interface_protocol = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceProtocol");
+ self->priv->interface_number = read_sysfs_attribute_as_hex (self->priv->interface_sysfs_path, "bInterfaceNumber");
+ self->priv->interface_description = read_sysfs_attribute_as_string (self->priv->interface_sysfs_path, "interface");
+ }
+ /* is this the USB physdev? */
+ else if (!self->priv->physdev_sysfs_path && has_sysfs_attribute (iter, "idVendor")) {
+ self->priv->physdev_sysfs_path = g_strdup (iter);
+ self->priv->physdev_vid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "idVendor");
+ self->priv->physdev_pid = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "idProduct");
+ self->priv->physdev_revision = read_sysfs_attribute_as_hex (self->priv->physdev_sysfs_path, "bcdDevice");
+ self->priv->physdev_manufacturer = read_sysfs_attribute_as_string (self->priv->physdev_sysfs_path, "manufacturer");
+ self->priv->physdev_product = read_sysfs_attribute_as_string (self->priv->physdev_sysfs_path, "product");
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ parent = g_path_get_dirname (iter);
+ g_clear_pointer (&iter, g_free);
+ iter = parent;
+ }
+
+ g_ptr_array_add (drivers, NULL);
+ self->priv->drivers = (gchar **) g_ptr_array_free (drivers, FALSE);
+ g_ptr_array_add (subsystems, NULL);
+ self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE);
+}
+
+static void
+preload_contents_wwan (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *iter = NULL;
+
+ /* Find the first parent device subsystem */
+ iter = g_path_get_dirname(self->priv->sysfs_path);
+ while (iter && (g_strcmp0 (iter, "/") != 0)) {
+ g_autofree gchar *current_subsystem = NULL;
+ gchar *parent;
+
+ current_subsystem = read_sysfs_attribute_link_basename (iter, "subsystem");
+ if (current_subsystem) {
+ if (g_strcmp0 (current_subsystem, "wwan") == 0)
+ self->priv->wwandev_sysfs_path = g_strdup (iter);
+ break;
+ }
+
+ parent = g_path_get_dirname (iter);
+ g_clear_pointer (&iter, g_free);
+ iter = parent;
+ }
+}
+
+static gchar *
+find_device_bus_subsystem (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *iter = NULL;
+
+ iter = g_strdup (self->priv->sysfs_path);
+ while (iter && (g_strcmp0 (iter, "/") != 0)) {
+ g_autofree gchar *subsys = NULL;
+ gchar *parent;
+
+ subsys = read_sysfs_attribute_link_basename (iter, "subsystem");
+
+ /* stop search as soon as we find a parent object
+ * of one of the supported bus subsystems */
+ if (subsys &&
+ ((g_strcmp0 (subsys, "usb") == 0) ||
+ (g_strcmp0 (subsys, "pcmcia") == 0) ||
+ (g_strcmp0 (subsys, "pci") == 0) ||
+ (g_strcmp0 (subsys, "platform") == 0) ||
+ (g_strcmp0 (subsys, "pnp") == 0) ||
+ (g_strcmp0 (subsys, "sdio") == 0)))
+ return g_steal_pointer (&subsys);
+
+ parent = g_path_get_dirname (iter);
+ g_clear_pointer (&iter, g_free);
+ iter = parent;
+ }
+
+ /* no more parents to check */
+ return NULL;
+}
+
+static void
+preload_contents (MMKernelDeviceGeneric *self)
+{
+ g_autofree gchar *bus_subsys = NULL;
+
+ if (self->priv->sysfs_path)
+ return;
+
+ preload_sysfs_path (self);
+ if (!self->priv->sysfs_path)
+ return;
+
+ bus_subsys = find_device_bus_subsystem (self);
+ if (g_strcmp0 (bus_subsys, "usb") == 0)
+ preload_contents_usb (self);
+ else if (g_strcmp0 (bus_subsys, "pcmcia") == 0)
+ preload_contents_pcmcia (self);
+ else if (g_strcmp0 (bus_subsys, "pci") == 0)
+ preload_contents_pci (self);
+ else if ((g_strcmp0 (bus_subsys, "platform") == 0) ||
+ (g_strcmp0 (bus_subsys, "pnp") == 0) ||
+ (g_strcmp0 (bus_subsys, "sdio") == 0))
+ preload_contents_platform (self, bus_subsys);
+ else
+ preload_contents_other (self);
+
+ preload_contents_wwan (self); /* wwan is bus agnostic class */
+
+ if (!bus_subsys)
+ return;
+
+ mm_obj_dbg (self, "port contents loaded:");
+ mm_obj_dbg (self, " bus: %s", bus_subsys ? bus_subsys : "n/a");
+ if (self->priv->interface_sysfs_path) {
+ mm_obj_dbg (self, " interface: %s", self->priv->interface_sysfs_path);
+ mm_obj_dbg (self, " interface class: %02x", self->priv->interface_class);
+ mm_obj_dbg (self, " interface subclass: %02x", self->priv->interface_subclass);
+ mm_obj_dbg (self, " interface protocol: %02x", self->priv->interface_protocol);
+ mm_obj_dbg (self, " interface number: %02x", self->priv->interface_number);
+ }
+ if (self->priv->interface_description)
+ mm_obj_dbg (self, " interface description: %s", self->priv->interface_description);
+ if (self->priv->physdev_sysfs_path)
+ mm_obj_dbg (self, " device: %s", self->priv->physdev_sysfs_path);
+ if (self->priv->subsystems) {
+ g_autofree gchar *subsystems_str = NULL;
+
+ subsystems_str = g_strjoinv (", ", self->priv->subsystems);
+ mm_obj_dbg (self, " subsystems: %s", subsystems_str);
+ }
+ if (self->priv->drivers) {
+ g_autofree gchar *drivers_str = NULL;
+
+ drivers_str = g_strjoinv (", ", self->priv->drivers);
+ mm_obj_dbg (self, " drivers: %s", drivers_str);
+ }
+ if (self->priv->physdev_vid)
+ mm_obj_dbg (self, " vendor: %04x", self->priv->physdev_vid);
+ if (self->priv->physdev_pid)
+ mm_obj_dbg (self, " product: %04x", self->priv->physdev_pid);
+ if (self->priv->physdev_revision)
+ mm_obj_dbg (self, " revision: %04x", self->priv->physdev_revision);
+ if (self->priv->physdev_manufacturer)
+ mm_obj_dbg (self, " manufacturer: %s", self->priv->physdev_manufacturer);
+ if (self->priv->physdev_product)
+ mm_obj_dbg (self, " product: %s", self->priv->physdev_product);
+
+ preload_common_properties (self);
+}
+
+/*****************************************************************************/
+
+static const gchar *
+kernel_device_get_subsystem (MMKernelDevice *self)
+{
+ return mm_kernel_event_properties_get_subsystem (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
+}
+
+static const gchar *
+kernel_device_get_name (MMKernelDevice *self)
+{
+ return mm_kernel_event_properties_get_name (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties);
+}
+
+static const gchar *
+kernel_device_get_sysfs_path (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path;
+}
+
+static const gchar *
+kernel_device_get_wwandev_sysfs_path (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->wwandev_sysfs_path;
+}
+
+static gint
+kernel_device_get_interface_number (MMKernelDevice *self)
+{
+ return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_number;
+}
+
+static gint
+kernel_device_get_interface_class (MMKernelDevice *self)
+{
+ return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_class;
+}
+
+static gint
+kernel_device_get_interface_subclass (MMKernelDevice *self)
+{
+ return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_subclass;
+}
+
+static gint
+kernel_device_get_interface_protocol (MMKernelDevice *self)
+{
+ return (gint) MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_protocol;
+}
+
+static const gchar *
+kernel_device_get_interface_sysfs_path (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_sysfs_path;
+}
+
+static const gchar *
+kernel_device_get_interface_description (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->interface_description;
+}
+
+static const gchar *
+kernel_device_get_physdev_uid (MMKernelDevice *self)
+{
+ const gchar *uid;
+
+ /* Prefer the one coming in the properties, if any */
+ if ((uid = mm_kernel_event_properties_get_uid (MM_KERNEL_DEVICE_GENERIC (self)->priv->properties)) != NULL)
+ return uid;
+
+ /* Try to load from properties set */
+ if ((uid = mm_kernel_device_get_property (self, ID_MM_PHYSDEV_UID)) != NULL)
+ return uid;
+
+ /* Use physical device path, if any */
+ if (MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path)
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path;
+
+ /* If there is no physdev sysfs path, e.g. for platform ports, use the device sysfs itself */
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path;
+}
+
+static const gchar *
+kernel_device_get_driver (MMKernelDevice *_self)
+{
+ MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (_self);
+
+ return (self->priv->drivers ? self->priv->drivers[0] : NULL);
+}
+
+static guint16
+kernel_device_get_physdev_vid (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_vid;
+}
+
+static guint16
+kernel_device_get_physdev_pid (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_pid;
+}
+
+static guint16
+kernel_device_get_physdev_revision (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_revision;
+}
+
+static const gchar *
+kernel_device_get_physdev_sysfs_path (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_sysfs_path;
+}
+
+static const gchar *
+kernel_device_get_physdev_subsystem (MMKernelDevice *_self)
+{
+ MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (_self);
+ guint len;
+
+ len = (self->priv->subsystems ? g_strv_length (self->priv->subsystems) : 0);
+ return (len > 0 ? self->priv->subsystems[len - 1] : NULL);
+}
+
+static const gchar *
+kernel_device_get_physdev_manufacturer (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_manufacturer;
+}
+
+static const gchar *
+kernel_device_get_physdev_product (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_GENERIC (self)->priv->physdev_product;
+}
+
+static gboolean
+kernel_device_cmp (MMKernelDevice *a,
+ MMKernelDevice *b)
+{
+ return (!g_strcmp0 (mm_kernel_device_get_subsystem (a), mm_kernel_device_get_subsystem (b)) &&
+ !g_strcmp0 (mm_kernel_device_get_name (a), mm_kernel_device_get_name (b)));
+}
+
+/*****************************************************************************/
+
+static gboolean
+string_match (MMKernelDeviceGeneric *self,
+ const gchar *str,
+ const gchar *pattern)
+{
+ g_autoptr(GError) inner_error = NULL;
+ g_autoptr(GRegex) regex = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
+
+ regex = g_regex_new (pattern, 0, 0, &inner_error);
+ if (!regex) {
+ mm_obj_warn (self, "invalid pattern in rule '%s': %s", pattern, inner_error->message);
+ return FALSE;
+ }
+ g_regex_match_full (regex, str, -1, 0, 0, &match_info, &inner_error);
+ if (inner_error) {
+ mm_obj_warn (self, "couldn't apply pattern match in rule '%s': %s", pattern, inner_error->message);
+ return FALSE;
+ }
+
+ if (!g_match_info_matches (match_info))
+ return FALSE;
+
+ mm_obj_dbg (self, "pattern '%s' matched: '%s'", pattern, str);
+ return TRUE;
+}
+
+static gboolean
+check_condition (MMKernelDeviceGeneric *self,
+ MMUdevRuleMatch *match)
+{
+ gboolean condition_equal;
+
+ condition_equal = (match->type == MM_UDEV_RULE_MATCH_TYPE_EQUAL);
+
+ /* We only apply 'add' rules */
+ if (g_str_equal (match->parameter, "ACTION"))
+ return ((!!strstr (match->value, "add")) == condition_equal);
+
+ /* Exact SUBSYSTEM match */
+ if (g_str_equal (match->parameter, "SUBSYSTEM"))
+ return ((self->priv->subsystems && !g_strcmp0 (self->priv->subsystems[0], match->value)) == condition_equal);
+
+ /* Loose SUBSYSTEMS match */
+ if (g_str_equal (match->parameter, "SUBSYSTEMS"))
+ return ((self->priv->subsystems && g_strv_contains ((const gchar * const *) self->priv->subsystems, match->value)) == condition_equal);
+
+ /* Exact DRIVER match */
+ if (g_str_equal (match->parameter, "DRIVER"))
+ return ((self->priv->drivers && !g_strcmp0 (self->priv->drivers[0], match->value)) == condition_equal);
+
+ /* Loose DRIVERS match */
+ if (g_str_equal (match->parameter, "DRIVERS"))
+ return ((self->priv->drivers && g_strv_contains ((const gchar * const *) self->priv->drivers, match->value)) == condition_equal);
+
+ /* Device name checks */
+ if (g_str_equal (match->parameter, "KERNEL"))
+ return (string_match (self, mm_kernel_device_get_name (MM_KERNEL_DEVICE (self)), match->value) == condition_equal);
+
+ /* Device sysfs path checks; we allow both a direct match and a prefix patch */
+ if (g_str_equal (match->parameter, "DEVPATH")) {
+ gchar *prefix_match = NULL;
+ gboolean result = FALSE;
+
+ /* If sysfs path invalid (e.g. path doesn't exist), no match */
+ if (!self->priv->sysfs_path)
+ return FALSE;
+
+ /* If not already doing a prefix match, do an implicit one. This is so that
+ * we can add properties to the usb_device owning all ports, and then apply
+ * the property to all ports individually processed here. */
+ if (match->value[0] && match->value[strlen (match->value) - 1] != '*')
+ prefix_match = g_strdup_printf ("%s/*", match->value);
+
+ if (string_match (self, self->priv->sysfs_path, match->value) == condition_equal) {
+ result = TRUE;
+ goto out;
+ }
+
+ if (prefix_match && string_match (self, self->priv->sysfs_path, prefix_match) == condition_equal) {
+ result = TRUE;
+ goto out;
+ }
+
+ if (g_str_has_prefix (self->priv->sysfs_path, "/sys")) {
+ if (string_match (self, &self->priv->sysfs_path[4], match->value) == condition_equal) {
+ result = TRUE;
+ goto out;
+ }
+ if (prefix_match && string_match (self, &self->priv->sysfs_path[4], prefix_match) == condition_equal) {
+ result = TRUE;
+ goto out;
+ }
+ }
+ out:
+ g_free (prefix_match);
+ return result;
+ }
+
+ /* Attributes checks */
+ if (g_str_has_prefix (match->parameter, "ATTR")) {
+ gchar *attribute;
+ gchar *contents = NULL;
+ gboolean result = FALSE;
+ guint val;
+
+ attribute = g_strdup (&match->parameter[5]);
+ g_strdelimit (attribute, "{}", ' ');
+ g_strstrip (attribute);
+
+ /* VID/PID directly from our API */
+ if (g_str_equal (attribute, "idVendor") || g_str_equal (attribute, "vendor"))
+ result = ((mm_get_uint_from_hex_str (match->value, &val)) &&
+ ((mm_kernel_device_get_physdev_vid (MM_KERNEL_DEVICE (self)) == val) == condition_equal));
+ else if (g_str_equal (attribute, "idProduct") || g_str_equal (attribute, "device"))
+ result = ((mm_get_uint_from_hex_str (match->value, &val)) &&
+ ((mm_kernel_device_get_physdev_pid (MM_KERNEL_DEVICE (self)) == val) == condition_equal));
+ /* manufacturer in the physdev */
+ else if (g_str_equal (attribute, "manufacturer"))
+ result = ((self->priv->physdev_manufacturer && g_str_equal (self->priv->physdev_manufacturer, match->value)) == condition_equal);
+ /* product in the physdev */
+ else if (g_str_equal (attribute, "product"))
+ result = ((self->priv->physdev_product && g_str_equal (self->priv->physdev_product, match->value)) == condition_equal);
+ /* interface class/subclass/protocol/number in the interface */
+ else if (g_str_equal (attribute, "bInterfaceClass"))
+ result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
+ ((self->priv->interface_class == val) == condition_equal)));
+ else if (g_str_equal (attribute, "bInterfaceSubClass"))
+ result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
+ ((self->priv->interface_subclass == val) == condition_equal)));
+ else if (g_str_equal (attribute, "bInterfaceProtocol"))
+ result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
+ ((self->priv->interface_protocol == val) == condition_equal)));
+ else if (g_str_equal (attribute, "bInterfaceNumber"))
+ result = (g_str_equal (match->value, "?*") || ((mm_get_uint_from_hex_str (match->value, &val)) &&
+ ((self->priv->interface_number == val) == condition_equal)));
+ else {
+ g_autofree gchar *found_value = NULL;
+
+ found_value = lookup_sysfs_attribute_as_string (self, attribute, g_str_has_prefix (match->parameter, "ATTRS"));
+ result = ((found_value && g_str_equal (found_value, match->value)) == condition_equal);
+ }
+
+ g_free (contents);
+ g_free (attribute);
+ return result;
+ }
+
+ /* Previously set property checks */
+ if (g_str_has_prefix (match->parameter, "ENV")) {
+ gchar *property;
+ gboolean result = FALSE;
+
+ property = g_strdup (&match->parameter[3]);
+ g_strdelimit (property, "{}", ' ');
+ g_strstrip (property);
+
+ result = ((!g_strcmp0 ((const gchar *) g_object_get_data (G_OBJECT (self), property), match->value)) == condition_equal);
+
+ g_free (property);
+ return result;
+ }
+
+ mm_obj_warn (self, "unknown match condition parameter: %s", match->parameter);
+ return FALSE;
+}
+
+static guint
+check_rule (MMKernelDeviceGeneric *self,
+ guint rule_i)
+{
+ MMUdevRule *rule;
+ gboolean apply = TRUE;
+
+ g_assert (rule_i < self->priv->rules->len);
+
+ rule = &g_array_index (self->priv->rules, MMUdevRule, rule_i);
+ if (rule->conditions) {
+ guint condition_i;
+
+ for (condition_i = 0; condition_i < rule->conditions->len; condition_i++) {
+ MMUdevRuleMatch *match;
+
+ match = &g_array_index (rule->conditions, MMUdevRuleMatch, condition_i);
+ if (!check_condition (self, match)) {
+ apply = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (apply) {
+ switch (rule->result.type) {
+ case MM_UDEV_RULE_RESULT_TYPE_PROPERTY: {
+ gchar *property_value_read = NULL;
+
+ if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceClass}"))
+ property_value_read = g_strdup_printf ("%02x", self->priv->interface_class);
+ else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceSubClass}"))
+ property_value_read = g_strdup_printf ("%02x", self->priv->interface_subclass);
+ else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceProtocol}"))
+ property_value_read = g_strdup_printf ("%02x", self->priv->interface_protocol);
+ else if (g_str_equal (rule->result.content.property.value, "$attr{bInterfaceNumber}"))
+ property_value_read = g_strdup_printf ("%02x", self->priv->interface_number);
+
+ /* add new property */
+ mm_obj_dbg (self, "property added: %s=%s",
+ rule->result.content.property.name,
+ property_value_read ? property_value_read : rule->result.content.property.value);
+
+ if (!property_value_read)
+ /* NOTE: we keep a reference to the list of rules ourselves, so it isn't
+ * an issue if we re-use the same string (i.e. without g_strdup-ing it)
+ * as a property value. */
+ g_object_set_data (G_OBJECT (self),
+ rule->result.content.property.name,
+ rule->result.content.property.value);
+ else
+ g_object_set_data_full (G_OBJECT (self),
+ rule->result.content.property.name,
+ property_value_read,
+ g_free);
+ break;
+ }
+
+ case MM_UDEV_RULE_RESULT_TYPE_LABEL:
+ /* noop */
+ break;
+
+ case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX:
+ /* Jump to a new index */
+ return rule->result.content.index;
+
+ case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG:
+ case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ }
+
+ /* Go to the next rule */
+ return rule_i + 1;
+}
+
+static void
+preload_rule_properties (MMKernelDeviceGeneric *self)
+{
+ guint i;
+
+ g_assert (self->priv->rules);
+ g_assert (self->priv->rules->len > 0);
+
+ /* Start to process rules */
+ i = 0;
+ while (i < self->priv->rules->len) {
+ guint next_rule;
+
+ next_rule = check_rule (self, i);
+ i = next_rule;
+ }
+}
+
+static void
+check_preload (MMKernelDeviceGeneric *self)
+{
+ /* Only preload when properties and rules are set */
+ if (!self->priv->properties || !self->priv->rules)
+ return;
+
+ /* Don't preload on "remove" actions, where we don't have the device any more */
+ if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove") == 0)
+ return;
+
+ /* Don't preload for devices in the 'virtual' subsystem */
+ if (g_strcmp0 (mm_kernel_event_properties_get_subsystem (self->priv->properties), "virtual") == 0)
+ return;
+
+ mm_obj_dbg (self, "preloading contents and properties...");
+ preload_contents (self);
+ preload_rule_properties (self);
+}
+
+static gboolean
+kernel_device_has_property (MMKernelDevice *self,
+ const gchar *property)
+{
+ return !!g_object_get_data (G_OBJECT (self), property);
+}
+
+static const gchar *
+kernel_device_get_property (MMKernelDevice *self,
+ const gchar *property)
+{
+ return g_object_get_data (G_OBJECT (self), property);
+}
+
+/*****************************************************************************/
+
+static gchar *
+build_attribute_data_key (const gchar *attribute)
+{
+ return g_strdup_printf ("ATTR:%s", attribute);
+}
+
+static gboolean
+kernel_device_has_attribute (MMKernelDevice *self,
+ const gchar *attribute)
+{
+ return has_sysfs_attribute (MM_KERNEL_DEVICE_GENERIC (self)->priv->sysfs_path, attribute);
+}
+
+static const gchar *
+kernel_device_get_attribute (MMKernelDevice *_self,
+ const gchar *attribute)
+{
+ MMKernelDeviceGeneric *self;
+ g_autofree gchar *key = NULL;
+ gchar *value = NULL;
+
+ self = MM_KERNEL_DEVICE_GENERIC (_self);
+
+ key = build_attribute_data_key (attribute);
+ value = g_object_get_data (G_OBJECT (self), key);
+ if (!value) {
+ value = read_sysfs_attribute_as_string (self->priv->sysfs_path, attribute);
+ if (value)
+ g_object_set_data_full (G_OBJECT (self), key, value, g_free);
+ }
+ return (const gchar *) value;
+}
+
+/*****************************************************************************/
+
+MMKernelDevice *
+mm_kernel_device_generic_new_with_rules (MMKernelEventProperties *props,
+ GArray *rules,
+ GError **error)
+{
+ /* Note: we allow NULL rules, e.g. for virtual devices */
+
+ return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_GENERIC,
+ NULL,
+ error,
+ "properties", props,
+ "rules", rules,
+ NULL));
+}
+
+MMKernelDevice *
+mm_kernel_device_generic_new (MMKernelEventProperties *props,
+ GError **error)
+{
+ static GArray *rules = NULL;
+
+ /* We only try to load the default list of rules once */
+ if (G_UNLIKELY (!rules)) {
+ rules = mm_kernel_device_generic_rules_load (UDEVRULESDIR, error);
+ if (!rules)
+ return NULL;
+ }
+
+ return mm_kernel_device_generic_new_with_rules (props, rules, error);
+}
+
+/*****************************************************************************/
+
+static void
+mm_kernel_device_generic_init (MMKernelDeviceGeneric *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericPrivate);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
+
+ switch (prop_id) {
+ case PROP_PROPERTIES:
+ g_assert (!self->priv->properties);
+ self->priv->properties = g_value_dup_object (value);
+ break;
+ case PROP_RULES:
+ g_assert (!self->priv->rules);
+ self->priv->rules = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
+
+ switch (prop_id) {
+ case PROP_PROPERTIES:
+ g_value_set_object (value, self->priv->properties);
+ break;
+ case PROP_RULES:
+ g_value_set_boxed (value, self->priv->rules);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (initable);
+ const gchar *subsystem;
+
+ check_preload (self);
+
+ subsystem = mm_kernel_device_get_subsystem (MM_KERNEL_DEVICE (self));
+ if (!subsystem) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "subsystem is mandatory in kernel device");
+ return FALSE;
+ }
+
+ if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "name is mandatory in kernel device");
+ return FALSE;
+ }
+
+ /* sysfs path is mandatory as output, and will only be given if the
+ * specified device exists; but only if this wasn't a 'remove' event
+ * and not a virtual device.
+ */
+ if (self->priv->properties &&
+ g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove") &&
+ g_strcmp0 (mm_kernel_event_properties_get_subsystem (self->priv->properties), "virtual") &&
+ !self->priv->sysfs_path) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "device %s/%s not found",
+ mm_kernel_event_properties_get_subsystem (self->priv->properties),
+ mm_kernel_event_properties_get_name (self->priv->properties));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+ MMKernelDeviceGeneric *self = MM_KERNEL_DEVICE_GENERIC (object);
+
+ g_clear_pointer (&self->priv->physdev_product, g_free);
+ g_clear_pointer (&self->priv->physdev_manufacturer, g_free);
+ g_clear_pointer (&self->priv->physdev_sysfs_path, g_free);
+ g_clear_pointer (&self->priv->interface_description, g_free);
+ g_clear_pointer (&self->priv->interface_sysfs_path, g_free);
+ g_clear_pointer (&self->priv->sysfs_path, g_free);
+ g_clear_pointer (&self->priv->drivers, g_strfreev);
+ g_clear_pointer (&self->priv->subsystems, g_strfreev);
+ g_clear_pointer (&self->priv->rules, g_array_unref);
+ g_clear_object (&self->priv->properties);
+
+ G_OBJECT_CLASS (mm_kernel_device_generic_parent_class)->dispose (object);
+}
+
+static void
+initable_iface_init (GInitableIface *iface)
+{
+ iface->init = initable_init;
+}
+
+static void
+mm_kernel_device_generic_class_init (MMKernelDeviceGenericClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMKernelDeviceGenericPrivate));
+
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ kernel_device_class->get_subsystem = kernel_device_get_subsystem;
+ kernel_device_class->get_name = kernel_device_get_name;
+ kernel_device_class->get_driver = kernel_device_get_driver;
+ kernel_device_class->get_sysfs_path = kernel_device_get_sysfs_path;
+ kernel_device_class->get_wwandev_sysfs_path = kernel_device_get_wwandev_sysfs_path;
+ kernel_device_class->get_physdev_uid = kernel_device_get_physdev_uid;
+ kernel_device_class->get_physdev_vid = kernel_device_get_physdev_vid;
+ kernel_device_class->get_physdev_pid = kernel_device_get_physdev_pid;
+ kernel_device_class->get_physdev_revision = kernel_device_get_physdev_revision;
+ kernel_device_class->get_physdev_sysfs_path = kernel_device_get_physdev_sysfs_path;
+ kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem;
+ kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer;
+ kernel_device_class->get_physdev_product = kernel_device_get_physdev_product;
+ kernel_device_class->get_interface_number = kernel_device_get_interface_number;
+ kernel_device_class->get_interface_class = kernel_device_get_interface_class;
+ kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass;
+ kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol;
+ kernel_device_class->get_interface_sysfs_path = kernel_device_get_interface_sysfs_path;
+ kernel_device_class->get_interface_description = kernel_device_get_interface_description;
+ kernel_device_class->cmp = kernel_device_cmp;
+ kernel_device_class->has_property = kernel_device_has_property;
+ kernel_device_class->get_property = kernel_device_get_property;
+ kernel_device_class->has_attribute = kernel_device_has_attribute;
+ kernel_device_class->get_attribute = kernel_device_get_attribute;
+
+ /* Device-wide properties are stored per-port in the generic backend */
+ kernel_device_class->has_global_property = kernel_device_has_property;
+ kernel_device_class->get_global_property = kernel_device_get_property;
+
+ properties[PROP_PROPERTIES] =
+ g_param_spec_object ("properties",
+ "Properties",
+ "Generic kernel event properties",
+ MM_TYPE_KERNEL_EVENT_PROPERTIES,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]);
+
+ properties[PROP_RULES] =
+ g_param_spec_boxed ("rules",
+ "Rules",
+ "List of rules to apply",
+ G_TYPE_ARRAY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_RULES, properties[PROP_RULES]);
+}
diff --git a/src/kerneldevice/mm-kernel-device-generic.h b/src/kerneldevice/mm-kernel-device-generic.h
new file mode 100644
index 00000000..983a6271
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-generic.h
@@ -0,0 +1,56 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ */
+
+#ifndef MM_KERNEL_DEVICE_GENERIC_H
+#define MM_KERNEL_DEVICE_GENERIC_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-kernel-device.h"
+
+#define MM_TYPE_KERNEL_DEVICE_GENERIC (mm_kernel_device_generic_get_type ())
+#define MM_KERNEL_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGeneric))
+#define MM_KERNEL_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericClass))
+#define MM_IS_KERNEL_DEVICE_GENERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC))
+#define MM_IS_KERNEL_DEVICE_GENERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_GENERIC))
+#define MM_KERNEL_DEVICE_GENERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE_GENERIC, MMKernelDeviceGenericClass))
+
+typedef struct _MMKernelDeviceGeneric MMKernelDeviceGeneric;
+typedef struct _MMKernelDeviceGenericClass MMKernelDeviceGenericClass;
+typedef struct _MMKernelDeviceGenericPrivate MMKernelDeviceGenericPrivate;
+
+struct _MMKernelDeviceGeneric {
+ MMKernelDevice parent;
+ MMKernelDeviceGenericPrivate *priv;
+};
+
+struct _MMKernelDeviceGenericClass {
+ MMKernelDeviceClass parent;
+};
+
+GType mm_kernel_device_generic_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDeviceGeneric, g_object_unref)
+
+MMKernelDevice *mm_kernel_device_generic_new (MMKernelEventProperties *properties,
+ GError **error);
+MMKernelDevice *mm_kernel_device_generic_new_with_rules (MMKernelEventProperties *properties,
+ GArray *rules,
+ GError **error);
+
+#endif /* MM_KERNEL_DEVICE_GENERIC_H */
diff --git a/src/kerneldevice/mm-kernel-device-helpers.c b/src/kerneldevice/mm-kernel-device-helpers.c
new file mode 100644
index 00000000..5b97303a
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-helpers.c
@@ -0,0 +1,61 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "mm-kernel-device-helpers.h"
+
+gchar *
+mm_kernel_device_get_lower_device_name (const gchar *sysfs_path)
+{
+ g_autoptr(GFile) dirfile = NULL;
+ g_autoptr(GFileEnumerator) direnum = NULL;
+
+ dirfile = g_file_new_for_path (sysfs_path);
+ direnum = g_file_enumerate_children (dirfile,
+ G_FILE_ATTRIBUTE_STANDARD_NAME,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ if (!direnum)
+ return NULL;
+
+ while (TRUE) {
+ GFileInfo *info;
+ g_autofree gchar *filename = NULL;
+ g_autofree gchar *link_path = NULL;
+ g_autofree gchar *real_path = NULL;
+
+ if (!g_file_enumerator_iterate (direnum, &info, NULL, NULL, NULL) || !info)
+ break;
+
+ filename = g_file_info_get_attribute_as_string (info, G_FILE_ATTRIBUTE_STANDARD_NAME);
+ if (!filename || !g_str_has_prefix (filename, "lower_"))
+ continue;
+
+ link_path = g_strdup_printf ("%s/%s", sysfs_path, filename);
+ real_path = realpath (link_path, NULL);
+ if (!real_path)
+ continue;
+
+ return g_path_get_basename (real_path);
+ }
+
+ return NULL;
+}
diff --git a/src/kerneldevice/mm-kernel-device-helpers.h b/src/kerneldevice/mm-kernel-device-helpers.h
new file mode 100644
index 00000000..48a86233
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-helpers.h
@@ -0,0 +1,26 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_KERNEL_DEVICE_HELPERS_H
+#define MM_KERNEL_DEVICE_HELPERS_H
+
+#include <glib.h>
+
+/* For virtual devices that keep a "lower" link to the parent physical device
+ * they're based on, get the name of that parent physical device.
+ * (e.g. lower_device_name(qmimux0) == wwan0) */
+gchar *mm_kernel_device_get_lower_device_name (const gchar *sysfs_path);
+
+#endif /* MM_KERNEL_DEVICE_HERLPERS_H */
diff --git a/src/kerneldevice/mm-kernel-device-qrtr.c b/src/kerneldevice/mm-kernel-device-qrtr.c
new file mode 100644
index 00000000..1abfebb0
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-qrtr.c
@@ -0,0 +1,232 @@
+/* -*- 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 2020 Google LLC
+ */
+
+#include <string.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include <ModemManager-tags.h>
+
+#include "mm-kernel-device-qrtr.h"
+
+G_DEFINE_TYPE (MMKernelDeviceQrtr, mm_kernel_device_qrtr, MM_TYPE_KERNEL_DEVICE)
+
+enum {
+ PROP_0,
+ PROP_QRTR_NODE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMKernelDeviceQrtrPrivate {
+ QrtrNode *node;
+ gchar *name;
+ gchar *physdev_uid;
+};
+
+/*****************************************************************************/
+
+gchar *
+mm_kernel_device_qrtr_helper_build_name (guint32 node_id)
+{
+ return g_strdup_printf ("qrtr%u", node_id);
+}
+
+/*****************************************************************************/
+
+QrtrNode *
+mm_kernel_device_qrtr_get_node (MMKernelDeviceQrtr *self)
+{
+ return g_object_ref (self->priv->node);
+}
+
+/*****************************************************************************/
+
+static gboolean
+kernel_device_cmp (MMKernelDevice *_a,
+ MMKernelDevice *_b)
+{
+ MMKernelDeviceQrtr *a;
+ MMKernelDeviceQrtr *b;
+
+ a = MM_KERNEL_DEVICE_QRTR (_a);
+ b = MM_KERNEL_DEVICE_QRTR (_b);
+
+ return qrtr_node_get_id (a->priv->node) == qrtr_node_get_id (b->priv->node);
+}
+
+static gboolean
+kernel_device_has_property (MMKernelDevice *_self,
+ const gchar *property)
+{
+ MMKernelDeviceQrtr *self;
+
+ self = MM_KERNEL_DEVICE_QRTR (_self);
+
+ return !!g_object_get_data (G_OBJECT (self), property);
+}
+
+static const gchar *
+kernel_device_get_property (MMKernelDevice *_self,
+ const gchar *property)
+{
+ MMKernelDeviceQrtr *self;
+
+ self = MM_KERNEL_DEVICE_QRTR (_self);
+
+ return g_object_get_data (G_OBJECT (self), property);
+}
+
+static const gchar *
+kernel_device_get_driver (MMKernelDevice *_self)
+{
+ return MM_KERNEL_DEVICE_QRTR_DRIVER;
+}
+
+static const gchar *
+kernel_device_get_name (MMKernelDevice *_self)
+{
+ MMKernelDeviceQrtr *self;
+
+ self = MM_KERNEL_DEVICE_QRTR (_self);
+ if (!self->priv->name)
+ self->priv->name = mm_kernel_device_qrtr_helper_build_name (qrtr_node_get_id (self->priv->node));
+
+ return self->priv->name;
+}
+
+static const gchar *
+kernel_device_get_physdev_uid (MMKernelDevice *_self)
+{
+ return MM_KERNEL_DEVICE_QRTR_PHYSDEV_UID;
+}
+
+static const gchar *
+kernel_device_get_subsystem (MMKernelDevice *_self)
+{
+ return MM_KERNEL_DEVICE_QRTR_SUBSYSTEM;
+}
+/*****************************************************************************/
+
+MMKernelDevice *
+mm_kernel_device_qrtr_new (QrtrNode *qrtr_node)
+{
+ MMKernelDevice *self;
+
+ self = MM_KERNEL_DEVICE (g_object_new (MM_TYPE_KERNEL_DEVICE_QRTR,
+ "qrtr-node", qrtr_node,
+ NULL));
+ return self;
+}
+
+/*****************************************************************************/
+
+static void
+mm_kernel_device_qrtr_init (MMKernelDeviceQrtr *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtrPrivate);
+
+ /* Set properties*/
+ g_object_set_data_full (G_OBJECT (self), ID_MM_PORT_TYPE_QMI, g_strdup ("true"), g_free);
+ g_object_set_data_full (G_OBJECT (self), ID_MM_CANDIDATE, g_strdup ("1"), g_free);
+ /* For now we're assuming that QRTR ports are available exclusively on Qualcomm SoCs */
+ g_object_set_data_full (G_OBJECT (self), "ID_MM_QCOM_SOC", g_strdup ("1"), g_free);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDeviceQrtr *self = MM_KERNEL_DEVICE_QRTR (object);
+
+ switch (prop_id) {
+ case PROP_QRTR_NODE:
+ g_assert (!self->priv->node);
+ self->priv->node = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDeviceQrtr *self = MM_KERNEL_DEVICE_QRTR (object);
+
+ switch (prop_id) {
+ case PROP_QRTR_NODE:
+ g_value_set_object (value, self->priv->node);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ MMKernelDeviceQrtr *self = MM_KERNEL_DEVICE_QRTR (object);
+
+ g_clear_pointer (&self->priv->name, g_free);
+ g_clear_pointer (&self->priv->physdev_uid, g_free);
+ g_object_unref (self->priv->node);
+
+ G_OBJECT_CLASS (mm_kernel_device_qrtr_parent_class)->dispose (object);
+}
+
+static void
+mm_kernel_device_qrtr_class_init (MMKernelDeviceQrtrClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMKernelDeviceQrtrPrivate));
+
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ kernel_device_class->get_driver = kernel_device_get_driver;
+ kernel_device_class->get_name = kernel_device_get_name;
+ kernel_device_class->get_physdev_uid = kernel_device_get_physdev_uid;
+ kernel_device_class->get_subsystem = kernel_device_get_subsystem;
+ kernel_device_class->cmp = kernel_device_cmp;
+ kernel_device_class->has_property = kernel_device_has_property;
+ kernel_device_class->get_property = kernel_device_get_property;
+
+ /* Device-wide properties are stored per-port in the qrtr backend */
+ kernel_device_class->has_global_property = kernel_device_has_property;
+ kernel_device_class->get_global_property = kernel_device_get_property;
+
+ properties[PROP_QRTR_NODE] =
+ g_param_spec_object ("qrtr-node",
+ "qrtr node",
+ "Node object as reported by QrtrNode",
+ QRTR_TYPE_NODE,
+ G_PARAM_READWRITE);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+}
diff --git a/src/kerneldevice/mm-kernel-device-qrtr.h b/src/kerneldevice/mm-kernel-device-qrtr.h
new file mode 100644
index 00000000..26ad0748
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-qrtr.h
@@ -0,0 +1,68 @@
+/* -*- 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 2020 Google LLC
+ */
+
+#ifndef MM_KERNEL_DEVICE_QRTR_H
+#define MM_KERNEL_DEVICE_QRTR_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <libqrtr-glib.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-kernel-device.h"
+
+/* Driver string reported for all QRTR nodes; not really a kernel driver */
+#define MM_KERNEL_DEVICE_QRTR_DRIVER "qrtr"
+
+/* Subsytem string reported for all QRTR nodes; not really a kernel subsystem */
+#define MM_KERNEL_DEVICE_QRTR_SUBSYSTEM "qrtr"
+
+/* Physical device UID string reported for all QRTR nodes; equal to the UID
+ * used in the 'qcom-soc' plugin, which is the only one supporting QRTR nodes
+ * for now. This UID must be equal for all ports on the same modem, and so for
+ * Qualcomm SoCs we use the same plugin name as common string. */
+#define MM_KERNEL_DEVICE_QRTR_PHYSDEV_UID "qcom-soc"
+
+/* Helper to create a unique device name from the QRTR node id */
+gchar *mm_kernel_device_qrtr_helper_build_name (guint32 node_id);
+
+#define MM_TYPE_KERNEL_DEVICE_QRTR (mm_kernel_device_qrtr_get_type ())
+#define MM_KERNEL_DEVICE_QRTR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtr))
+#define MM_KERNEL_DEVICE_QRTR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtrClass))
+#define MM_IS_KERNEL_DEVICE_QRTR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_QRTR))
+#define MM_IS_KERNEL_DEVICE_QRTR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_QRTR))
+#define MM_KERNEL_DEVICE_QRTR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE_QRTR, MMKernelDeviceQrtrClass))
+
+typedef struct _MMKernelDeviceQrtr MMKernelDeviceQrtr;
+typedef struct _MMKernelDeviceQrtrClass MMKernelDeviceQrtrClass;
+typedef struct _MMKernelDeviceQrtrPrivate MMKernelDeviceQrtrPrivate;
+
+struct _MMKernelDeviceQrtr {
+ MMKernelDevice parent;
+ MMKernelDeviceQrtrPrivate *priv;
+};
+
+struct _MMKernelDeviceQrtrClass {
+ MMKernelDeviceClass parent;
+};
+
+QrtrNode *mm_kernel_device_qrtr_get_node (MMKernelDeviceQrtr *self);
+
+GType mm_kernel_device_qrtr_get_type (void);
+MMKernelDevice *mm_kernel_device_qrtr_new (QrtrNode *qrtr_node);
+
+#endif /* MM_KERNEL_DEVICE_QRTR_H */
diff --git a/src/kerneldevice/mm-kernel-device-udev.c b/src/kerneldevice/mm-kernel-device-udev.c
new file mode 100644
index 00000000..19389f66
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-udev.c
@@ -0,0 +1,850 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <string.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include <ModemManager-tags.h>
+
+#include "mm-kernel-device-udev.h"
+#include "mm-kernel-device-helpers.h"
+#include "mm-log-object.h"
+
+static void initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMKernelDeviceUdev, mm_kernel_device_udev, MM_TYPE_KERNEL_DEVICE, 0,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init))
+
+enum {
+ PROP_0,
+ PROP_UDEV_CLIENT,
+ PROP_UDEV_DEVICE,
+ PROP_PROPERTIES,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMKernelDeviceUdevPrivate {
+ GUdevClient *client;
+ GUdevDevice *device;
+
+ GUdevDevice *interface;
+ GUdevDevice *physdev;
+ guint16 vendor;
+ guint16 product;
+ guint16 revision;
+ gchar *driver;
+
+ MMKernelEventProperties *properties;
+};
+
+/*****************************************************************************/
+
+static guint
+udev_device_get_sysfs_attr_as_hex (GUdevDevice *device,
+ const gchar *attribute)
+{
+ const gchar *attr;
+ guint val = 0;
+
+ attr = g_udev_device_get_sysfs_attr (device, attribute);
+ if (attr)
+ mm_get_uint_from_hex_str (attr, &val);
+ return val;
+}
+
+/*****************************************************************************/
+
+static void
+preload_contents_other (MMKernelDeviceUdev *self)
+{
+ g_autofree gchar *lower_device_name = NULL;
+
+ /* For any other kind of bus (or the absence of one, as in virtual devices),
+ * assume this is a single port device and don't try to match multiple ports
+ * together. Also, obviously, no vendor, product, revision or interface. */
+ self->priv->driver = g_strdup (g_udev_device_get_driver (self->priv->device));
+
+ /* But look for a lower real physical device, as we may have one */
+ lower_device_name = mm_kernel_device_get_lower_device_name (g_udev_device_get_sysfs_path (self->priv->device));
+ if (lower_device_name) {
+ g_autoptr(GUdevDevice) lower_device = NULL;
+ const gchar *subsystem;
+
+ subsystem = g_udev_device_get_subsystem (self->priv->device);
+ lower_device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, lower_device_name);
+ if (!lower_device) {
+ mm_obj_dbg (self, "couldn't find lower device: %s/%s", subsystem, lower_device_name);
+ } else {
+ g_autoptr(MMKernelDevice) lower_kernel_device = NULL;
+
+ mm_obj_dbg (self, "setting up lower device: %s/%s", subsystem, lower_device_name);
+ lower_kernel_device = mm_kernel_device_udev_new (self->priv->client, lower_device);
+ g_object_set (self,
+ "lower-device", lower_kernel_device,
+ NULL);
+ }
+ }
+}
+
+static void
+preload_contents_platform (MMKernelDeviceUdev *self,
+ const gchar *platform)
+{
+ g_autoptr(GUdevDevice) iter = NULL;
+
+ iter = g_object_ref (self->priv->device);
+ while (iter) {
+ GUdevDevice *parent;
+
+ /* Store the first driver found */
+ if (!self->priv->driver)
+ self->priv->driver = g_strdup (g_udev_device_get_driver (iter));
+
+ /* Take first parent with the given platform subsystem as physical device */
+ if (!self->priv->physdev && (g_strcmp0 (g_udev_device_get_subsystem (iter), platform) == 0)) {
+ self->priv->physdev = g_object_ref (iter);
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ parent = g_udev_device_get_parent (iter);
+ g_clear_object (&iter);
+ iter = parent;
+ }
+}
+
+static void
+preload_contents_pcmcia (MMKernelDeviceUdev *self)
+{
+ g_autoptr(GUdevDevice) iter = NULL;
+ gboolean pcmcia_subsystem_found = FALSE;
+
+ iter = g_object_ref (self->priv->device);
+ while (iter) {
+ g_autoptr(GUdevDevice) parent = NULL;
+
+ /* Store the first driver found */
+ if (!self->priv->driver)
+ self->priv->driver = g_strdup (g_udev_device_get_driver (iter));
+
+ if (g_strcmp0 (g_udev_device_get_subsystem (iter), "pcmcia") == 0)
+ pcmcia_subsystem_found = TRUE;
+
+ /* If the parent of this PCMCIA device is no longer part of
+ * the PCMCIA subsystem, we want to stop since we're looking
+ * for the base PCMCIA device, not the PCMCIA controller which
+ * is usually PCI or some other bus type.
+ */
+ parent = g_udev_device_get_parent (iter);
+
+ if (pcmcia_subsystem_found && parent && (g_strcmp0 (g_udev_device_get_subsystem (parent), "pcmcia") != 0)) {
+ self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "manf_id");
+ self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "card_id");
+ self->priv->physdev = g_object_ref (iter);
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ g_clear_object (&iter);
+ iter = g_steal_pointer (&parent);
+ }
+}
+
+static void
+preload_contents_pci (MMKernelDeviceUdev *self)
+{
+ g_autoptr(GUdevDevice) iter = NULL;
+
+ iter = g_object_ref (self->priv->device);
+ while (iter) {
+ GUdevDevice *parent;
+
+ /* Store the first driver found */
+ if (!self->priv->driver)
+ self->priv->driver = g_strdup (g_udev_device_get_driver (iter));
+
+ /* the PCI channel specific devices have their own drivers and
+ * subsystems, we can rely on the physical device being the first
+ * one that reports the 'pci' subsystem */
+ if (!self->priv->physdev && (g_strcmp0 (g_udev_device_get_subsystem (iter), "pci") == 0)) {
+ self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "vendor");
+ self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "device");
+ self->priv->revision = udev_device_get_sysfs_attr_as_hex (iter, "revision");
+ self->priv->physdev = g_object_ref (iter);
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ parent = g_udev_device_get_parent (iter);
+ g_clear_object (&iter);
+ iter = parent;
+ }
+}
+
+static void
+preload_contents_usb (MMKernelDeviceUdev *self)
+{
+ g_autoptr(GUdevDevice) iter = NULL;
+
+ iter = g_object_ref (self->priv->device);
+ while (iter) {
+ GUdevDevice *parent;
+ const gchar *devtype;
+
+ devtype = g_udev_device_get_devtype (iter);
+
+ /* is this the USB interface? */
+ if (!self->priv->interface && (g_strcmp0 (devtype, "usb_interface") == 0)) {
+ self->priv->interface = g_object_ref (iter);
+ self->priv->driver = g_strdup (g_udev_device_get_driver (iter));
+ }
+
+ /* is this the USB physdev? */
+ if (!self->priv->physdev && (g_strcmp0 (devtype, "usb_device") == 0)) {
+ self->priv->vendor = udev_device_get_sysfs_attr_as_hex (iter, "idVendor");
+ self->priv->product = udev_device_get_sysfs_attr_as_hex (iter, "idProduct");
+ self->priv->revision = udev_device_get_sysfs_attr_as_hex (iter, "bcdDevice");
+ self->priv->physdev = g_object_ref (iter);
+ /* stop traversing as soon as the physical device is found */
+ break;
+ }
+
+ parent = g_udev_device_get_parent (iter);
+ g_clear_object (&iter);
+ iter = parent;
+ }
+}
+
+static gchar *
+find_device_bus_subsystem (MMKernelDeviceUdev *self)
+{
+ g_autoptr(GUdevDevice) iter = NULL;
+
+ iter = g_object_ref (self->priv->device);
+ while (iter) {
+ const gchar *subsys;
+ GUdevDevice *parent;
+
+ /* stop search as soon as we find a parent object
+ * of one of the supported bus subsystems */
+ subsys = g_udev_device_get_subsystem (iter);
+ if ((g_strcmp0 (subsys, "usb") == 0) ||
+ (g_strcmp0 (subsys, "pcmcia") == 0) ||
+ (g_strcmp0 (subsys, "pci") == 0) ||
+ (g_strcmp0 (subsys, "platform") == 0) ||
+ (g_strcmp0 (subsys, "pnp") == 0) ||
+ (g_strcmp0 (subsys, "sdio") == 0))
+ return g_strdup (subsys);
+
+ parent = g_udev_device_get_parent (iter);
+ g_clear_object (&iter);
+ iter = parent;
+ }
+
+ /* no more parents to check */
+ return NULL;
+}
+
+static void
+preload_contents (MMKernelDeviceUdev *self)
+{
+ g_autofree gchar *bus_subsys = NULL;
+
+ bus_subsys = find_device_bus_subsystem (self);
+ if (g_strcmp0 (bus_subsys, "usb") == 0)
+ preload_contents_usb (self);
+ else if (g_strcmp0 (bus_subsys, "pcmcia") == 0)
+ preload_contents_pcmcia (self);
+ else if (g_strcmp0 (bus_subsys, "pci") == 0)
+ preload_contents_pci (self);
+ else if ((g_strcmp0 (bus_subsys, "platform") == 0) ||
+ (g_strcmp0 (bus_subsys, "pnp") == 0) ||
+ (g_strcmp0 (bus_subsys, "sdio") == 0))
+ preload_contents_platform (self, bus_subsys);
+ else
+ preload_contents_other (self);
+
+ if (!bus_subsys)
+ return;
+
+ mm_obj_dbg (self, "port contents loaded:");
+ mm_obj_dbg (self, " bus: %s", bus_subsys ? bus_subsys : "n/a");
+ if (self->priv->interface)
+ mm_obj_dbg (self, " interface: %s", g_udev_device_get_sysfs_path (self->priv->interface));
+ if (self->priv->physdev)
+ mm_obj_dbg (self, " device: %s", g_udev_device_get_sysfs_path (self->priv->physdev));
+ if (self->priv->driver)
+ mm_obj_dbg (self, " driver: %s", self->priv->driver);
+ if (self->priv->vendor)
+ mm_obj_dbg (self, " vendor: %04x", self->priv->vendor);
+ if (self->priv->product)
+ mm_obj_dbg (self, " product: %04x", self->priv->product);
+ if (self->priv->revision)
+ mm_obj_dbg (self, " revision: %04x", self->priv->revision);
+}
+
+/*****************************************************************************/
+
+static const gchar *
+kernel_device_get_subsystem (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+
+ if (self->priv->device)
+ return g_udev_device_get_subsystem (self->priv->device);
+
+ g_assert (self->priv->properties);
+ return mm_kernel_event_properties_get_subsystem (self->priv->properties);
+}
+
+static const gchar *
+kernel_device_get_name (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+
+ if (self->priv->device)
+ return g_udev_device_get_name (self->priv->device);
+
+ g_assert (self->priv->properties);
+ return mm_kernel_event_properties_get_name (self->priv->properties);
+}
+
+static const gchar *
+kernel_device_get_driver (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_UDEV (self)->priv->driver;
+}
+
+static const gchar *
+kernel_device_get_sysfs_path (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->device ?
+ g_udev_device_get_sysfs_path (self->priv->device) :
+ NULL);
+}
+
+static const gchar *
+kernel_device_get_wwandev_sysfs_path (MMKernelDevice *_self)
+{
+ g_autoptr(GUdevDevice) parent = NULL;
+ MMKernelDeviceUdev *self;
+ const gchar *subsys;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ parent = g_udev_device_get_parent (self->priv->device);
+
+ if (!parent)
+ return NULL;
+
+ subsys = g_udev_device_get_subsystem (parent);
+
+ if (!subsys || g_strcmp0 (subsys, "wwan"))
+ return NULL;
+
+ return g_udev_device_get_sysfs_path (parent);
+}
+
+static const gchar *
+kernel_device_get_physdev_uid (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+ const gchar *uid = NULL;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+
+ /* Prefer the one coming in the properties, if any */
+ if (self->priv->properties) {
+ if ((uid = mm_kernel_event_properties_get_uid (self->priv->properties)) != NULL)
+ return uid;
+ }
+
+ /* Try to load from properties set on the physical device */
+ if ((uid = mm_kernel_device_get_global_property (_self, ID_MM_PHYSDEV_UID)) != NULL)
+ return uid;
+
+ /* Use physical device sysfs path, if any */
+ if (self->priv->physdev && (uid = g_udev_device_get_sysfs_path (self->priv->physdev)) != NULL)
+ return uid;
+
+ /* If there is no physical device sysfs path, use the device sysfs itself */
+ g_assert (self->priv->device);
+ return g_udev_device_get_sysfs_path (self->priv->device);
+}
+
+static guint16
+kernel_device_get_physdev_vid (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_UDEV (self)->priv->vendor;
+}
+
+static guint16
+kernel_device_get_physdev_pid (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_UDEV (self)->priv->product;
+}
+
+static guint16
+kernel_device_get_physdev_revision (MMKernelDevice *self)
+{
+ return MM_KERNEL_DEVICE_UDEV (self)->priv->revision;
+}
+
+static const gchar *
+kernel_device_get_physdev_sysfs_path (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->physdev ? g_udev_device_get_sysfs_path (self->priv->physdev) : NULL);
+}
+
+static const gchar *
+kernel_device_get_physdev_subsystem (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->physdev ? g_udev_device_get_subsystem (self->priv->physdev) : NULL);
+}
+
+static const gchar *
+kernel_device_get_physdev_manufacturer (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->physdev ? g_udev_device_get_sysfs_attr (self->priv->physdev, "manufacturer") : NULL);
+}
+
+static const gchar *
+kernel_device_get_physdev_product (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->physdev ? g_udev_device_get_sysfs_attr (self->priv->physdev, "product") : NULL);
+}
+
+static gint
+kernel_device_get_interface_number (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceNumber") : -1);
+}
+
+static gint
+kernel_device_get_interface_class (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceClass") : -1);
+}
+
+static gint
+kernel_device_get_interface_subclass (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceSubClass") : -1);
+}
+
+static gint
+kernel_device_get_interface_protocol (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? (gint) udev_device_get_sysfs_attr_as_hex (self->priv->interface, "bInterfaceProtocol") : -1);
+}
+
+static const gchar *
+kernel_device_get_interface_sysfs_path (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? g_udev_device_get_sysfs_path (self->priv->interface) : NULL);
+}
+
+static const gchar *
+kernel_device_get_interface_description (MMKernelDevice *_self)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->interface ? g_udev_device_get_sysfs_attr (self->priv->interface, "interface") : NULL);
+}
+
+static gboolean
+kernel_device_cmp (MMKernelDevice *_a,
+ MMKernelDevice *_b)
+{
+ MMKernelDeviceUdev *a;
+ MMKernelDeviceUdev *b;
+
+ a = MM_KERNEL_DEVICE_UDEV (_a);
+ b = MM_KERNEL_DEVICE_UDEV (_b);
+
+ if (a->priv->device && b->priv->device) {
+ if (g_udev_device_has_property (a->priv->device, "DEVPATH_OLD") &&
+ g_str_has_suffix (g_udev_device_get_sysfs_path (b->priv->device),
+ g_udev_device_get_property (a->priv->device, "DEVPATH_OLD")))
+ return TRUE;
+
+ if (g_udev_device_has_property (b->priv->device, "DEVPATH_OLD") &&
+ g_str_has_suffix (g_udev_device_get_sysfs_path (a->priv->device),
+ g_udev_device_get_property (b->priv->device, "DEVPATH_OLD")))
+ return TRUE;
+
+ return !g_strcmp0 (g_udev_device_get_sysfs_path (a->priv->device), g_udev_device_get_sysfs_path (b->priv->device));
+ }
+
+ return (!g_strcmp0 (mm_kernel_device_get_subsystem (_a), mm_kernel_device_get_subsystem (_b)) &&
+ !g_strcmp0 (mm_kernel_device_get_name (_a), mm_kernel_device_get_name (_b)));
+}
+
+static gboolean
+kernel_device_has_property (MMKernelDevice *_self,
+ const gchar *property)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->device ? g_udev_device_has_property (self->priv->device, property) : FALSE);
+}
+
+static const gchar *
+kernel_device_get_property (MMKernelDevice *_self,
+ const gchar *property)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ return (self->priv->device ? g_udev_device_get_property (self->priv->device, property) : NULL);
+}
+
+static gboolean
+kernel_device_has_global_property (MMKernelDevice *_self,
+ const gchar *property)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ if (self->priv->physdev && g_udev_device_has_property (self->priv->physdev, property))
+ return TRUE;
+
+ return kernel_device_has_property (_self, property);
+}
+
+static const gchar *
+kernel_device_get_global_property (MMKernelDevice *_self,
+ const gchar *property)
+{
+ MMKernelDeviceUdev *self;
+ const gchar *str;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+
+ if (self->priv->physdev &&
+ g_udev_device_has_property (self->priv->physdev, property) &&
+ (str = g_udev_device_get_property (self->priv->physdev, property)) != NULL)
+ return str;
+
+ return kernel_device_get_property (_self, property);
+}
+
+/*****************************************************************************/
+
+static gboolean
+kernel_device_has_attribute (MMKernelDevice *_self,
+ const gchar *attribute)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ if (!self->priv->device)
+ return FALSE;
+
+ return g_udev_device_has_sysfs_attr (self->priv->device, attribute);
+}
+
+static const gchar *
+kernel_device_get_attribute (MMKernelDevice *_self,
+ const gchar *attribute)
+{
+ MMKernelDeviceUdev *self;
+
+ self = MM_KERNEL_DEVICE_UDEV (_self);
+ if (!self->priv->device)
+ return NULL;
+
+ return g_udev_device_get_sysfs_attr (self->priv->device, attribute);
+}
+
+/*****************************************************************************/
+
+MMKernelDevice *
+mm_kernel_device_udev_new (GUdevClient *udev_client,
+ GUdevDevice *udev_device)
+{
+ GError *error = NULL;
+ MMKernelDevice *self;
+
+
+ self = MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV,
+ NULL,
+ &error,
+ "udev-client", udev_client,
+ "udev-device", udev_device,
+ NULL));
+ g_assert_no_error (error);
+ return self;
+}
+
+/*****************************************************************************/
+
+MMKernelDevice *
+mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client,
+ MMKernelEventProperties *props,
+ GError **error)
+{
+ return MM_KERNEL_DEVICE (g_initable_new (MM_TYPE_KERNEL_DEVICE_UDEV,
+ NULL,
+ error,
+ "udev-client", udev_client,
+ "properties", props,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+mm_kernel_device_udev_init (MMKernelDeviceUdev *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevPrivate);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
+
+ switch (prop_id) {
+ case PROP_UDEV_CLIENT:
+ g_assert (!self->priv->client);
+ self->priv->client = g_value_dup_object (value);
+ break;
+ case PROP_UDEV_DEVICE:
+ g_assert (!self->priv->device);
+ self->priv->device = g_value_dup_object (value);
+ break;
+ case PROP_PROPERTIES:
+ g_assert (!self->priv->properties);
+ self->priv->properties = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
+
+ switch (prop_id) {
+ case PROP_UDEV_CLIENT:
+ g_value_set_object (value, self->priv->client);
+ break;
+ case PROP_UDEV_DEVICE:
+ g_value_set_object (value, self->priv->device);
+ break;
+ case PROP_PROPERTIES:
+ g_value_set_object (value, self->priv->properties);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static gboolean
+initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (initable);
+ const gchar *subsystem;
+ const gchar *name;
+
+ if (!self->priv->client) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "missing client in kernel device");
+ return FALSE;
+ }
+
+ /* When created from a GUdevDevice, we're done */
+ if (self->priv->device) {
+ preload_contents (self);
+ return TRUE;
+ }
+
+ /* Otherwise, we do need properties with subsystem and name */
+ if (!self->priv->properties) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "missing properties in kernel device");
+ return FALSE;
+ }
+
+ subsystem = mm_kernel_event_properties_get_subsystem (self->priv->properties);
+ if (!subsystem) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "subsystem is mandatory in kernel device");
+ return FALSE;
+ }
+
+ name = mm_kernel_event_properties_get_name (self->priv->properties);
+ if (!mm_kernel_device_get_name (MM_KERNEL_DEVICE (self))) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "name is mandatory in kernel device");
+ return FALSE;
+ }
+
+ /* On remove events, we don't look for the GUdevDevice */
+ if (g_strcmp0 (mm_kernel_event_properties_get_action (self->priv->properties), "remove")) {
+ g_assert (!self->priv->device);
+ self->priv->device = g_udev_client_query_by_subsystem_and_name (self->priv->client, subsystem, name);
+ if (!self->priv->device) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "device %s/%s not found",
+ subsystem,
+ name);
+ return FALSE;
+ }
+ }
+
+ if (self->priv->device)
+ preload_contents (self);
+ return TRUE;
+}
+
+static void
+dispose (GObject *object)
+{
+ MMKernelDeviceUdev *self = MM_KERNEL_DEVICE_UDEV (object);
+
+ g_clear_pointer (&self->priv->driver, g_free);
+ g_clear_object (&self->priv->physdev);
+ g_clear_object (&self->priv->interface);
+ g_clear_object (&self->priv->device);
+ g_clear_object (&self->priv->client);
+ g_clear_object (&self->priv->properties);
+
+ G_OBJECT_CLASS (mm_kernel_device_udev_parent_class)->dispose (object);
+}
+
+static void
+initable_iface_init (GInitableIface *iface)
+{
+ iface->init = initable_init;
+}
+
+static void
+mm_kernel_device_udev_class_init (MMKernelDeviceUdevClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMKernelDeviceClass *kernel_device_class = MM_KERNEL_DEVICE_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMKernelDeviceUdevPrivate));
+
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ kernel_device_class->get_subsystem = kernel_device_get_subsystem;
+ kernel_device_class->get_name = kernel_device_get_name;
+ kernel_device_class->get_driver = kernel_device_get_driver;
+ kernel_device_class->get_sysfs_path = kernel_device_get_sysfs_path;
+ kernel_device_class->get_wwandev_sysfs_path = kernel_device_get_wwandev_sysfs_path;
+ kernel_device_class->get_physdev_uid = kernel_device_get_physdev_uid;
+ kernel_device_class->get_physdev_vid = kernel_device_get_physdev_vid;
+ kernel_device_class->get_physdev_pid = kernel_device_get_physdev_pid;
+ kernel_device_class->get_physdev_revision = kernel_device_get_physdev_revision;
+ kernel_device_class->get_physdev_sysfs_path = kernel_device_get_physdev_sysfs_path;
+ kernel_device_class->get_physdev_subsystem = kernel_device_get_physdev_subsystem;
+ kernel_device_class->get_physdev_manufacturer = kernel_device_get_physdev_manufacturer;
+ kernel_device_class->get_physdev_product = kernel_device_get_physdev_product;
+ kernel_device_class->get_interface_number = kernel_device_get_interface_number;
+ kernel_device_class->get_interface_class = kernel_device_get_interface_class;
+ kernel_device_class->get_interface_subclass = kernel_device_get_interface_subclass;
+ kernel_device_class->get_interface_protocol = kernel_device_get_interface_protocol;
+ kernel_device_class->get_interface_sysfs_path = kernel_device_get_interface_sysfs_path;
+ kernel_device_class->get_interface_description = kernel_device_get_interface_description;
+ kernel_device_class->cmp = kernel_device_cmp;
+ kernel_device_class->has_property = kernel_device_has_property;
+ kernel_device_class->get_property = kernel_device_get_property;
+ kernel_device_class->has_global_property = kernel_device_has_global_property;
+ kernel_device_class->get_global_property = kernel_device_get_global_property;
+ kernel_device_class->has_attribute = kernel_device_has_attribute;
+ kernel_device_class->get_attribute = kernel_device_get_attribute;
+
+ properties[PROP_UDEV_DEVICE] =
+ g_param_spec_object ("udev-device",
+ "udev device",
+ "Device object as reported by GUdev",
+ G_UDEV_TYPE_DEVICE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]);
+
+ properties[PROP_UDEV_CLIENT] =
+ g_param_spec_object ("udev-client",
+ "udev client",
+ "GUdev client",
+ G_UDEV_TYPE_CLIENT,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_UDEV_CLIENT, properties[PROP_UDEV_CLIENT]);
+
+ properties[PROP_PROPERTIES] =
+ g_param_spec_object ("properties",
+ "Properties",
+ "Generic kernel event properties",
+ MM_TYPE_KERNEL_EVENT_PROPERTIES,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_PROPERTIES, properties[PROP_PROPERTIES]);
+}
diff --git a/src/kerneldevice/mm-kernel-device-udev.h b/src/kerneldevice/mm-kernel-device-udev.h
new file mode 100644
index 00000000..93153b3b
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device-udev.h
@@ -0,0 +1,57 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ */
+
+#ifndef MM_KERNEL_DEVICE_UDEV_H
+#define MM_KERNEL_DEVICE_UDEV_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gudev/gudev.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-kernel-device.h"
+
+#define MM_TYPE_KERNEL_DEVICE_UDEV (mm_kernel_device_udev_get_type ())
+#define MM_KERNEL_DEVICE_UDEV(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdev))
+#define MM_KERNEL_DEVICE_UDEV_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevClass))
+#define MM_IS_KERNEL_DEVICE_UDEV(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE_UDEV))
+#define MM_IS_KERNEL_DEVICE_UDEV_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE_UDEV))
+#define MM_KERNEL_DEVICE_UDEV_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE_UDEV, MMKernelDeviceUdevClass))
+
+typedef struct _MMKernelDeviceUdev MMKernelDeviceUdev;
+typedef struct _MMKernelDeviceUdevClass MMKernelDeviceUdevClass;
+typedef struct _MMKernelDeviceUdevPrivate MMKernelDeviceUdevPrivate;
+
+struct _MMKernelDeviceUdev {
+ MMKernelDevice parent;
+ MMKernelDeviceUdevPrivate *priv;
+};
+
+struct _MMKernelDeviceUdevClass {
+ MMKernelDeviceClass parent;
+};
+
+GType mm_kernel_device_udev_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDeviceUdev, g_object_unref)
+
+MMKernelDevice *mm_kernel_device_udev_new (GUdevClient *udev_client,
+ GUdevDevice *udev_device);
+MMKernelDevice *mm_kernel_device_udev_new_from_properties (GUdevClient *udev_client,
+ MMKernelEventProperties *properties,
+ GError **error);
+
+#endif /* MM_KERNEL_DEVICE_UDEV_H */
diff --git a/src/kerneldevice/mm-kernel-device.c b/src/kerneldevice/mm-kernel-device.c
new file mode 100644
index 00000000..64380bab
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device.c
@@ -0,0 +1,491 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ */
+
+#include <config.h>
+#include <string.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-kernel-device.h"
+#include "mm-log-object.h"
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MMKernelDevice, mm_kernel_device, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+enum {
+ PROP_0,
+ PROP_LOWER_DEVICE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMKernelDevicePrivate {
+ MMKernelDevice *lower_device;
+};
+
+/*****************************************************************************/
+
+const gchar *
+mm_kernel_device_get_subsystem (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_subsystem ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_subsystem (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_name (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_name ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_name (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_driver (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_driver ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_driver (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_sysfs_path (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_sysfs_path ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_sysfs_path (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_wwandev_sysfs_path (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_wwandev_sysfs_path ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_wwandev_sysfs_path (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_physdev_uid (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_uid (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_uid ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_uid (self) :
+ NULL);
+}
+
+guint16
+mm_kernel_device_get_physdev_vid (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_vid (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_vid ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_vid (self) :
+ 0);
+}
+
+guint16
+mm_kernel_device_get_physdev_pid (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_pid (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_pid ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_pid (self) :
+ 0);
+}
+
+guint16
+mm_kernel_device_get_physdev_revision (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_revision (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_revision ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_revision (self) :
+ 0);
+}
+
+const gchar *
+mm_kernel_device_get_physdev_subsystem (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_subsystem (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_subsystem ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_subsystem (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_physdev_sysfs_path (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_sysfs_path (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_sysfs_path ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_sysfs_path (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_physdev_manufacturer (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_manufacturer (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_manufacturer ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_manufacturer (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_physdev_product (MMKernelDevice *self)
+{
+ /* when a lower device is available, physdev info taken from it */
+ if (self->priv->lower_device)
+ return mm_kernel_device_get_physdev_product (self->priv->lower_device);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_product ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_physdev_product (self) :
+ NULL);
+}
+
+MMKernelDevice *
+mm_kernel_device_peek_lower_device (MMKernelDevice *self)
+{
+ return self->priv->lower_device;
+}
+
+gint
+mm_kernel_device_get_interface_number (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_number ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_number (self) :
+ -1);
+}
+
+gint
+mm_kernel_device_get_interface_class (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_class ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_class (self) :
+ -1);
+}
+
+gint
+mm_kernel_device_get_interface_subclass (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_subclass ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_subclass (self) :
+ -1);
+}
+
+gint
+mm_kernel_device_get_interface_protocol (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_protocol ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_protocol (self) :
+ -1);
+}
+
+const gchar *
+mm_kernel_device_get_interface_sysfs_path (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_sysfs_path ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_sysfs_path (self) :
+ NULL);
+}
+
+const gchar *
+mm_kernel_device_get_interface_description (MMKernelDevice *self)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_description ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_interface_description (self) :
+ NULL);
+}
+
+gboolean
+mm_kernel_device_cmp (MMKernelDevice *a,
+ MMKernelDevice *b)
+{
+ if (G_OBJECT_TYPE (a) != G_OBJECT_TYPE (b))
+ return FALSE;
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (a)->cmp ?
+ MM_KERNEL_DEVICE_GET_CLASS (a)->cmp (a, b) :
+ FALSE);
+}
+
+gboolean
+mm_kernel_device_has_property (MMKernelDevice *self,
+ const gchar *property)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_property ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->has_property (self, property) :
+ FALSE);
+}
+
+const gchar *
+mm_kernel_device_get_property (MMKernelDevice *self,
+ const gchar *property)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_property ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_property (self, property) :
+ NULL);
+}
+
+gboolean
+mm_kernel_device_get_property_as_boolean (MMKernelDevice *self,
+ const gchar *property)
+{
+ const gchar *value;
+
+ value = mm_kernel_device_get_property (self, property);
+ return (value && mm_common_get_boolean_from_string (value, NULL));
+}
+
+gint
+mm_kernel_device_get_property_as_int (MMKernelDevice *self,
+ const gchar *property)
+{
+ const gchar *value;
+ gint aux;
+
+ value = mm_kernel_device_get_property (self, property);
+ return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0);
+}
+
+guint
+mm_kernel_device_get_property_as_int_hex (MMKernelDevice *self,
+ const gchar *property)
+{
+ const gchar *value;
+ guint aux;
+
+ value = mm_kernel_device_get_property (self, property);
+ return ((value && mm_get_uint_from_hex_str (value, &aux)) ? aux : 0);
+}
+
+gboolean
+mm_kernel_device_has_global_property (MMKernelDevice *self,
+ const gchar *property)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_global_property ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->has_global_property (self, property) :
+ FALSE);
+}
+
+const gchar *
+mm_kernel_device_get_global_property (MMKernelDevice *self,
+ const gchar *property)
+{
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_global_property ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_global_property (self, property) :
+ NULL);
+}
+
+gboolean
+mm_kernel_device_get_global_property_as_boolean (MMKernelDevice *self,
+ const gchar *property)
+{
+ const gchar *value;
+
+ value = mm_kernel_device_get_global_property (self, property);
+ return (value && mm_common_get_boolean_from_string (value, NULL));
+}
+
+gint
+mm_kernel_device_get_global_property_as_int (MMKernelDevice *self,
+ const gchar *property)
+{
+ const gchar *value;
+ gint aux;
+
+ value = mm_kernel_device_get_global_property (self, property);
+ return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0);
+}
+
+guint
+mm_kernel_device_get_global_property_as_int_hex (MMKernelDevice *self,
+ const gchar *property)
+{
+ const gchar *value;
+ guint aux;
+
+ value = mm_kernel_device_get_global_property (self, property);
+ return ((value && mm_get_uint_from_hex_str (value, &aux)) ? aux : 0);
+}
+
+gboolean
+mm_kernel_device_has_attribute (MMKernelDevice *self,
+ const gchar *attribute)
+{
+ g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), FALSE);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->has_attribute ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->has_attribute (self, attribute) :
+ FALSE);
+}
+
+const gchar *
+mm_kernel_device_get_attribute (MMKernelDevice *self,
+ const gchar *attribute)
+{
+ g_return_val_if_fail (MM_IS_KERNEL_DEVICE (self), NULL);
+
+ return (MM_KERNEL_DEVICE_GET_CLASS (self)->get_attribute ?
+ MM_KERNEL_DEVICE_GET_CLASS (self)->get_attribute (self, attribute) :
+ NULL);
+}
+
+gboolean
+mm_kernel_device_get_attribute_as_boolean (MMKernelDevice *self,
+ const gchar *attribute)
+{
+ const gchar *value;
+
+ value = mm_kernel_device_get_attribute (self, attribute);
+ return (value && mm_common_get_boolean_from_string (value, NULL));
+}
+
+gint
+mm_kernel_device_get_attribute_as_int (MMKernelDevice *self,
+ const gchar *attribute)
+{
+ const gchar *value;
+ gint aux;
+
+ value = mm_kernel_device_get_attribute (self, attribute);
+ return ((value && mm_get_int_from_str (value, &aux)) ? aux : 0);
+}
+
+guint
+mm_kernel_device_get_attribute_as_int_hex (MMKernelDevice *self,
+ const gchar *attribute)
+{
+ const gchar *value;
+ guint aux;
+
+ value = mm_kernel_device_get_attribute (self, attribute);
+ return ((value && mm_get_uint_from_hex_str (value, &aux)) ? aux : 0);
+}
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMKernelDevice *self;
+
+ self = MM_KERNEL_DEVICE (_self);
+ return g_strdup (mm_kernel_device_get_name (self));
+}
+
+/*****************************************************************************/
+
+static void
+mm_kernel_device_init (MMKernelDevice *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_KERNEL_DEVICE, MMKernelDevicePrivate);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDevice *self = MM_KERNEL_DEVICE (object);
+
+ switch (prop_id) {
+ case PROP_LOWER_DEVICE:
+ g_clear_object (&self->priv->lower_device);
+ self->priv->lower_device = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMKernelDevice *self = MM_KERNEL_DEVICE (object);
+
+ switch (prop_id) {
+ case PROP_LOWER_DEVICE:
+ g_value_set_object (value, self->priv->lower_device);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
+dispose (GObject *object)
+{
+ MMKernelDevice *self = MM_KERNEL_DEVICE (object);
+
+ g_clear_object (&self->priv->lower_device);
+
+ G_OBJECT_CLASS (mm_kernel_device_parent_class)->dispose (object);
+}
+
+static void
+mm_kernel_device_class_init (MMKernelDeviceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMKernelDevicePrivate));
+
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ properties[PROP_LOWER_DEVICE] =
+ g_param_spec_object ("lower-device",
+ "lower device",
+ "Lower real device, when this is a virtual one",
+ MM_TYPE_KERNEL_DEVICE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_LOWER_DEVICE, properties[PROP_LOWER_DEVICE]);
+}
diff --git a/src/kerneldevice/mm-kernel-device.h b/src/kerneldevice/mm-kernel-device.h
new file mode 100644
index 00000000..2eed71f1
--- /dev/null
+++ b/src/kerneldevice/mm-kernel-device.h
@@ -0,0 +1,125 @@
+/* -*- 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) 2016 Velocloud, Inc.
+ */
+
+#ifndef MM_KERNEL_DEVICE_H
+#define MM_KERNEL_DEVICE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define MM_TYPE_KERNEL_DEVICE (mm_kernel_device_get_type ())
+#define MM_KERNEL_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_KERNEL_DEVICE, MMKernelDevice))
+#define MM_KERNEL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_KERNEL_DEVICE, MMKernelDeviceClass))
+#define MM_IS_KERNEL_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_KERNEL_DEVICE))
+#define MM_IS_KERNEL_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_KERNEL_DEVICE))
+#define MM_KERNEL_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_KERNEL_DEVICE, MMKernelDeviceClass))
+
+typedef struct _MMKernelDevice MMKernelDevice;
+typedef struct _MMKernelDeviceClass MMKernelDeviceClass;
+typedef struct _MMKernelDevicePrivate MMKernelDevicePrivate;
+
+struct _MMKernelDevice {
+ GObject parent;
+ MMKernelDevicePrivate *priv;
+};
+
+struct _MMKernelDeviceClass {
+ GObjectClass parent;
+
+ const gchar * (* get_subsystem) (MMKernelDevice *self);
+ const gchar * (* get_name) (MMKernelDevice *self);
+ const gchar * (* get_driver) (MMKernelDevice *self);
+ const gchar * (* get_sysfs_path) (MMKernelDevice *self);
+
+ const gchar * (* get_wwandev_sysfs_path) (MMKernelDevice *self);
+
+ gint (* get_interface_number) (MMKernelDevice *self);
+ gint (* get_interface_class) (MMKernelDevice *self);
+ gint (* get_interface_subclass) (MMKernelDevice *self);
+ gint (* get_interface_protocol) (MMKernelDevice *self);
+ const gchar * (* get_interface_sysfs_path) (MMKernelDevice *self);
+ const gchar * (* get_interface_description) (MMKernelDevice *self);
+
+ const gchar * (* get_physdev_uid) (MMKernelDevice *self);
+ guint16 (* get_physdev_vid) (MMKernelDevice *self);
+ guint16 (* get_physdev_pid) (MMKernelDevice *self);
+ guint16 (* get_physdev_revision) (MMKernelDevice *self);
+ const gchar * (* get_physdev_sysfs_path) (MMKernelDevice *self);
+ const gchar * (* get_physdev_subsystem) (MMKernelDevice *self);
+ const gchar * (* get_physdev_manufacturer) (MMKernelDevice *self);
+ const gchar * (* get_physdev_product) (MMKernelDevice *self);
+
+ gboolean (* cmp) (MMKernelDevice *a, MMKernelDevice *b);
+
+ gboolean (* has_property) (MMKernelDevice *self, const gchar *property);
+ const gchar * (* get_property) (MMKernelDevice *self, const gchar *property);
+ gboolean (* has_global_property) (MMKernelDevice *self, const gchar *property);
+ const gchar * (* get_global_property) (MMKernelDevice *self, const gchar *property);
+ gboolean (* has_attribute) (MMKernelDevice *self, const gchar *attribute);
+ const gchar * (* get_attribute) (MMKernelDevice *self, const gchar *attribute);
+};
+
+GType mm_kernel_device_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMKernelDevice, g_object_unref)
+
+const gchar *mm_kernel_device_get_subsystem (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_name (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_driver (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_sysfs_path (MMKernelDevice *self);
+
+const gchar *mm_kernel_device_get_wwandev_sysfs_path (MMKernelDevice *self);
+
+gint mm_kernel_device_get_interface_number (MMKernelDevice *self);
+gint mm_kernel_device_get_interface_class (MMKernelDevice *self);
+gint mm_kernel_device_get_interface_subclass (MMKernelDevice *self);
+gint mm_kernel_device_get_interface_protocol (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_interface_sysfs_path (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_interface_description (MMKernelDevice *self);
+
+const gchar *mm_kernel_device_get_physdev_uid (MMKernelDevice *self);
+guint16 mm_kernel_device_get_physdev_vid (MMKernelDevice *self);
+guint16 mm_kernel_device_get_physdev_pid (MMKernelDevice *self);
+guint16 mm_kernel_device_get_physdev_revision (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_physdev_sysfs_path (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_physdev_subsystem (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_physdev_manufacturer (MMKernelDevice *self);
+const gchar *mm_kernel_device_get_physdev_product (MMKernelDevice *self);
+
+MMKernelDevice *mm_kernel_device_peek_lower_device (MMKernelDevice *self);
+
+gboolean mm_kernel_device_cmp (MMKernelDevice *a, MMKernelDevice *b);
+
+/* Standard properties are usually associated to single ports */
+gboolean mm_kernel_device_has_property (MMKernelDevice *self, const gchar *property);
+const gchar *mm_kernel_device_get_property (MMKernelDevice *self, const gchar *property);
+gboolean mm_kernel_device_get_property_as_boolean (MMKernelDevice *self, const gchar *property);
+gint mm_kernel_device_get_property_as_int (MMKernelDevice *self, const gchar *property);
+guint mm_kernel_device_get_property_as_int_hex (MMKernelDevice *self, const gchar *property);
+
+/* Global properties are usually associated to full devices */
+gboolean mm_kernel_device_has_global_property (MMKernelDevice *self, const gchar *property);
+const gchar *mm_kernel_device_get_global_property (MMKernelDevice *self, const gchar *property);
+gboolean mm_kernel_device_get_global_property_as_boolean (MMKernelDevice *self, const gchar *property);
+gint mm_kernel_device_get_global_property_as_int (MMKernelDevice *self, const gchar *property);
+guint mm_kernel_device_get_global_property_as_int_hex (MMKernelDevice *self, const gchar *property);
+
+/* Attributes in sysfs */
+gboolean mm_kernel_device_has_attribute (MMKernelDevice *self, const gchar *attribute);
+const gchar *mm_kernel_device_get_attribute (MMKernelDevice *self, const gchar *attribute);
+gboolean mm_kernel_device_get_attribute_as_boolean (MMKernelDevice *self, const gchar *attribute);
+gint mm_kernel_device_get_attribute_as_int (MMKernelDevice *self, const gchar *attribute);
+guint mm_kernel_device_get_attribute_as_int_hex (MMKernelDevice *self, const gchar *attribute);
+
+#endif /* MM_KERNEL_DEVICE_H */
diff --git a/src/main.c b/src/main.c
index a5ff6644..a9221cdc 100644
--- a/src/main.c
+++ b/src/main.c
@@ -26,10 +26,15 @@
#include "ModemManager.h"
-#include "mm-base-manager.h"
+#define MM_LOG_NO_OBJECT
#include "mm-log.h"
+#include "mm-base-manager.h"
#include "mm-context.h"
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+# include "mm-sleep-monitor.h"
+#endif
+
/* Maximum time to wait for all modems to get disabled and removed */
#define MAX_SHUTDOWN_TIME_SECS 20
@@ -39,7 +44,7 @@ static MMBaseManager *manager;
static gboolean
quit_cb (gpointer user_data)
{
- mm_info ("Caught signal, shutting down...");
+ mm_info ("caught signal, shutting down...");
if (manager)
g_object_set (manager, MM_BASE_MANAGER_CONNECTION, NULL, NULL);
@@ -47,10 +52,35 @@ quit_cb (gpointer user_data)
if (loop)
g_idle_add ((GSourceFunc) g_main_loop_quit, loop);
else
- _exit (0);
+ exit (0);
return FALSE;
}
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+static void
+sleeping_cb (MMSleepMonitor *sleep_monitor)
+{
+ mm_dbg ("removing devices... (sleeping)");
+ mm_base_manager_shutdown (manager, FALSE);
+}
+
+static void
+resuming_cb (MMSleepMonitor *sleep_monitor)
+{
+ mm_dbg ("re-scanning (resuming)");
+ mm_base_manager_start (manager, FALSE);
+}
+
+static void
+resuming_quick_cb (MMSleepMonitor *sleep_monitor)
+{
+ mm_dbg ("syncing modem state (quick resuming)");
+ mm_base_manager_sync (manager);
+}
+
+#endif
+
static void
bus_acquired_cb (GDBusConnection *connection,
const gchar *name,
@@ -58,17 +88,19 @@ bus_acquired_cb (GDBusConnection *connection,
{
GError *error = NULL;
- mm_dbg ("Bus acquired, creating manager...");
+ mm_dbg ("bus acquired, creating manager...");
/* Create Manager object */
g_assert (!manager);
manager = mm_base_manager_new (connection,
mm_context_get_test_plugin_dir (),
- !mm_context_get_test_no_auto_scan (),
+ !mm_context_get_no_auto_scan (),
+ mm_context_get_filter_policy (),
+ mm_context_get_initial_kernel_events (),
mm_context_get_test_enable (),
&error);
if (!manager) {
- mm_warn ("Could not create manager: %s", error->message);
+ mm_warn ("could not create manager: %s", error->message);
g_error_free (error);
g_main_loop_quit (loop);
return;
@@ -80,7 +112,7 @@ name_acquired_cb (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
- mm_dbg ("Service name '%s' was acquired", name);
+ mm_dbg ("service name '%s' was acquired", name);
/* Launch automatic scan for devices */
g_assert (manager);
@@ -95,10 +127,9 @@ name_lost_cb (GDBusConnection *connection,
/* Note that we're not allowing replacement, so once the name acquired, the
* process won't lose it. */
if (!name)
- mm_warn ("Could not get the system bus. Make sure "
- "the message bus daemon is running!");
+ mm_warn ("could not get the system bus; make sure the message bus daemon is running!");
else
- mm_warn ("Could not acquire the '%s' service name", name);
+ mm_warn ("could not acquire the '%s' service name", name);
if (manager)
g_object_set (manager, MM_BASE_MANAGER_CONNECTION, NULL, NULL);
@@ -106,35 +137,52 @@ name_lost_cb (GDBusConnection *connection,
g_main_loop_quit (loop);
}
+static void
+register_dbus_errors (void)
+{
+ /* This method will always return success once during runtime */
+ if (!mm_common_register_errors ())
+ return;
+
+ /* We no longer use MM_CORE_ERROR_CANCELLED in the daemon, we rely on
+ * G_IO_ERROR_CANCELLED internally */
+ g_dbus_error_unregister_error (MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED, MM_CORE_ERROR_DBUS_PREFIX ".Cancelled");
+ g_dbus_error_register_error (G_IO_ERROR, G_IO_ERROR_CANCELLED, MM_CORE_ERROR_DBUS_PREFIX ".Cancelled");
+}
+
int
main (int argc, char *argv[])
{
GMainLoop *inner;
- GError *err = NULL;
- guint name_id;
-
- g_type_init ();
+ GError *error = NULL;
+ guint name_id;
/* Setup application context */
mm_context_init (argc, argv);
if (!mm_log_setup (mm_context_get_log_level (),
mm_context_get_log_file (),
- mm_context_get_timestamps (),
- mm_context_get_relative_timestamps (),
- mm_context_get_debug (),
- &err)) {
- g_warning ("Failed to set up logging: %s", err->message);
- g_error_free (err);
+ mm_context_get_log_journal (),
+ mm_context_get_log_timestamps (),
+ mm_context_get_log_relative_timestamps (),
+ &error)) {
+ g_printerr ("error: failed to set up logging: %s\n", error->message);
+ g_error_free (error);
exit (1);
}
g_unix_signal_add (SIGTERM, quit_cb, NULL);
g_unix_signal_add (SIGINT, quit_cb, NULL);
+ /* Early register all known errors */
+ register_dbus_errors ();
+
mm_info ("ModemManager (version " MM_DIST_VERSION ") starting in %s bus...",
mm_context_get_test_session () ? "session" : "system");
+ /* Detect runtime charset conversion support */
+ mm_modem_charsets_init ();
+
/* Acquire name, don't allow replacement */
name_id = g_bus_own_name (mm_context_get_test_session () ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM,
MM_DBUS_SERVICE,
@@ -144,6 +192,24 @@ main (int argc, char *argv[])
name_lost_cb,
NULL,
NULL);
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ {
+ MMSleepMonitor *sleep_monitor;
+
+ if (mm_context_get_test_no_suspend_resume())
+ mm_dbg ("Suspend/resume support disabled at runtime");
+ else if (mm_context_get_test_quick_suspend_resume()) {
+ mm_dbg ("Quick suspend/resume hooks enabled");
+ sleep_monitor = mm_sleep_monitor_get ();
+ g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_quick_cb), NULL);
+ } else {
+ mm_dbg ("Full suspend/resume hooks enabled");
+ sleep_monitor = mm_sleep_monitor_get ();
+ g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_SLEEPING, G_CALLBACK (sleeping_cb), NULL);
+ g_signal_connect (sleep_monitor, MM_SLEEP_MONITOR_RESUMING, G_CALLBACK (resuming_cb), NULL);
+ }
+ }
+#endif
/* Go into the main loop */
loop = g_main_loop_new (NULL, FALSE);
@@ -157,7 +223,7 @@ main (int argc, char *argv[])
if (manager) {
GTimer *timer;
- mm_base_manager_shutdown (manager);
+ mm_base_manager_shutdown (manager, TRUE);
/* Wait for all modems to be disabled and removed, but don't wait
* forever: if disabling the modems takes longer than 20s, just
@@ -172,8 +238,7 @@ main (int argc, char *argv[])
}
if (mm_base_manager_num_modems (manager))
- mm_warn ("Disabling modems took too long, "
- "shutting down with '%u' modems around",
+ mm_warn ("disabling modems took too long, shutting down with %u modems around",
mm_base_manager_num_modems (manager));
g_object_unref (manager);
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 00000000..d0c2b3a6
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,319 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+# helpers library
+src_inc = include_directories('.')
+kerneldevice_inc = include_directories('kerneldevice')
+
+headers = files(
+ 'mm-modem-helpers.h',
+ 'mm-sms-part.h',
+)
+
+enums_types = 'mm-helper-enums-types'
+
+enums_sources = []
+enums_sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: headers,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-helper-enums-types.h"',
+)
+
+enums_sources += gnome.mkenums(
+ enums_types + '.h',
+ sources: headers,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include "mm-sms-part.h"\n#include "mm-modem-helpers.h"\n#ifndef __MM_HELPER_ENUMS_TYPES_H__\n#define __MM_HELPER_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_HELPER_ENUMS_TYPES_H__ */\n',
+)
+
+sources = files(
+ 'mm-charsets.c',
+ 'mm-error-helpers.c',
+ 'mm-log.c',
+ 'mm-log-object.c',
+ 'mm-modem-helpers.c',
+ 'mm-sms-part-3gpp.c',
+ 'mm-sms-part.c',
+ 'mm-sms-part-cdma.c',
+)
+
+incs = [
+ top_inc,
+ # FIXME: only necessary if qmi is enabled?
+ kerneldevice_inc,
+]
+
+deps = [
+ libmm_glib_dep,
+ mbim_glib_dep,
+ qmi_glib_dep,
+]
+
+private_deps = []
+
+if enable_qmi
+ sources += files('mm-modem-helpers-qmi.c')
+endif
+
+if enable_mbim
+ sources += files('mm-modem-helpers-mbim.c')
+endif
+
+if enable_systemd_journal
+ private_deps += libsystemd_dep
+endif
+
+libhelpers = static_library(
+ 'helpers',
+ sources: sources + enums_sources,
+ include_directories: incs,
+ dependencies: deps + private_deps,
+)
+
+libhelpers_dep = declare_dependency(
+ sources: enums_sources[1],
+ include_directories: ['.', kerneldevice_inc],
+ dependencies: deps,
+ link_with: libhelpers,
+)
+
+# kerneldevice library
+sources = files(
+ 'kerneldevice/mm-kernel-device.c',
+ 'kerneldevice/mm-kernel-device-generic.c',
+ 'kerneldevice/mm-kernel-device-generic-rules.c',
+ 'kerneldevice/mm-kernel-device-helpers.c',
+)
+
+deps = [
+ gudev_dep,
+ libhelpers_dep,
+ qrtr_glib_dep,
+]
+
+if enable_qrtr
+ sources += files('kerneldevice/mm-kernel-device-qrtr.c')
+endif
+
+if enable_udev
+ sources += files('kerneldevice/mm-kernel-device-udev.c')
+endif
+
+libkerneldevice = static_library(
+ 'kerneldevice',
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: '-DUDEVRULESDIR="@0@"'.format(udev_rulesdir),
+)
+
+libkerneldevice_dep = declare_dependency(
+ dependencies: deps,
+ link_with: libkerneldevice,
+)
+
+# ports library
+headers = files(
+ 'mm-port.h',
+ 'mm-port-serial-at.h',
+)
+
+sources = files(
+ 'mm-netlink.c',
+ 'mm-port.c',
+ 'mm-port-net.c',
+ 'mm-port-serial-at.c',
+ 'mm-port-serial.c',
+ 'mm-port-serial-gps.c',
+ 'mm-port-serial-qcdm.c',
+ 'mm-serial-parsers.c',
+)
+
+deps = [libkerneldevice_dep]
+
+private_deps = [gio_unix_dep]
+
+if enable_qmi
+ headers += files('mm-port-qmi.h')
+
+ sources += files('mm-port-qmi.c')
+endif
+
+if enable_mbim
+ sources += files('mm-port-mbim.c')
+endif
+
+enums_types = 'mm-port-enums-types'
+
+enums_sources = []
+enums_sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: headers,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-port-enums-types.h"',
+)
+
+enums_sources += gnome.mkenums(
+ enums_types + '.h',
+ sources: headers,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include "config.h"\n#include "mm-port.h"\n#include "mm-port-serial-at.h"\n#if defined WITH_QMI\n#include "mm-port-qmi.h"\n#endif\n#ifndef __MM_PORT_ENUMS_TYPES_H__\n#define __MM_PORT_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_PORT_ENUMS_TYPES_H__ */\n',
+)
+
+libport = static_library(
+ 'port',
+ sources: sources + enums_sources,
+ include_directories: top_inc,
+ dependencies: deps + private_deps,
+)
+
+libport_dep = declare_dependency(
+ sources: enums_sources[1],
+ include_directories: '.',
+ dependencies: deps,
+ link_with: libport,
+)
+
+# ModemManager daemon
+headers = files(
+ 'mm-base-bearer.h',
+ 'mm-filter.h',
+ 'mm-port-probe.h',
+)
+
+sources = files(
+ 'main.c',
+ 'mm-auth-provider.c',
+ 'mm-base-bearer.c',
+ 'mm-base-call.c',
+ 'mm-base-manager.c',
+ 'mm-base-modem-at.c',
+ 'mm-base-modem.c',
+ 'mm-base-sim.c',
+ 'mm-base-sms.c',
+ 'mm-bearer-list.c',
+ 'mm-broadband-bearer.c',
+ 'mm-broadband-modem.c',
+ 'mm-call-list.c',
+ 'mm-context.c',
+ 'mm-device.c',
+ 'mm-filter.c',
+ 'mm-iface-modem-3gpp.c',
+ 'mm-iface-modem-3gpp-profile-manager.c',
+ 'mm-iface-modem-3gpp-ussd.c',
+ 'mm-iface-modem.c',
+ 'mm-iface-modem-cdma.c',
+ 'mm-iface-modem-firmware.c',
+ 'mm-iface-modem-location.c',
+ 'mm-iface-modem-messaging.c',
+ 'mm-iface-modem-oma.c',
+ 'mm-iface-modem-sar.c',
+ 'mm-iface-modem-signal.c',
+ 'mm-iface-modem-simple.c',
+ 'mm-iface-modem-time.c',
+ 'mm-iface-modem-voice.c',
+ 'mm-plugin.c',
+ 'mm-plugin-manager.c',
+ 'mm-port-probe.c',
+ 'mm-port-probe-at.c',
+ 'mm-private-boxed-types.c',
+ 'mm-sms-list.c',
+)
+
+enums_types = 'mm-daemon-enums-types'
+
+sources += gnome.mkenums(
+ enums_types + '.c',
+ sources: headers,
+ c_template: build_aux_dir / enums_types + '.c.template',
+ fhead: '#include "mm-daemon-enums-types.h"',
+)
+
+sources += gnome.mkenums(
+ enums_types + '.h',
+ sources: headers,
+ h_template: build_aux_dir / enums_types + '.h.template',
+ fhead: '#include "mm-filter.h"\n#include "mm-base-bearer.h"\n#include "mm-port-probe.h"\n#ifndef __MM_DAEMON_ENUMS_TYPES_H__\n#define __MM_DAEMON_ENUMS_TYPES_H__\n',
+ ftail: '#endif /* __MM_DAEMON_ENUMS_TYPES_H__ */\n',
+)
+
+deps = [
+ gmodule_dep,
+ libmm_test_generated_dep,
+ libport_dep,
+ libqcdm_dep,
+]
+
+c_args = [
+ '-DMM_COMPILATION',
+ '-DPLUGINDIR="@0@"'.format(mm_prefix / mm_pkglibdir),
+]
+
+if enable_qrtr
+ sources += files('mm-qrtr-bus-watcher.c')
+endif
+
+# Additional suspend/resume support via systemd
+if enable_systemd_suspend_resume
+ sources += files('mm-sleep-monitor.c')
+
+ deps += [
+ gio_unix_dep,
+ libsystemd_dep,
+ ]
+endif
+
+if enable_polkit
+ deps += polkit_gobject_dep
+endif
+
+# Additional QMI support in ModemManager
+if enable_qmi
+ sources += files(
+ 'mm-bearer-qmi.c',
+ 'mm-broadband-modem-qmi.c',
+ 'mm-call-qmi.c',
+ 'mm-shared-qmi.c',
+ 'mm-sim-qmi.c',
+ 'mm-sms-qmi.c',
+ )
+endif
+
+# Additional MBIM support in ModemManager
+if enable_mbim
+ sources += files(
+ 'mm-bearer-mbim.c',
+ 'mm-broadband-modem-mbim.c',
+ 'mm-sim-mbim.c',
+ 'mm-sms-mbim.c',
+ )
+endif
+
+executable(
+ 'ModemManager',
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ c_args: c_args,
+ install: true,
+ install_dir: mm_sbindir,
+)
+
+pkg.generate(
+ version: mm_version,
+ name: mm_name,
+ description: 'Common headers provided by ModemManager',
+ subdirs: mm_name,
+ variables: 'exec_prefix=${prefix}',
+)
+
+# generic udev rules
+install_data(
+ '80-mm-candidate.rules',
+ install_dir: udev_rulesdir,
+)
+
+subdir('tests')
diff --git a/src/mm-auth-provider-polkit.c b/src/mm-auth-provider-polkit.c
deleted file mode 100644
index 34f6c770..00000000
--- a/src/mm-auth-provider-polkit.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/* -*- 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) 2010 - 2012 Red Hat, Inc.
- * Copyright (C) 2012 Google, Inc.
- */
-
-#include <polkit/polkit.h>
-
-#include <config.h>
-
-#include <ModemManager.h>
-#include "mm-errors-types.h"
-
-#include "mm-log.h"
-#include "mm-auth-provider-polkit.h"
-
-G_DEFINE_TYPE (MMAuthProviderPolkit, mm_auth_provider_polkit, MM_TYPE_AUTH_PROVIDER)
-
-struct _MMAuthProviderPolkitPrivate {
- PolkitAuthority *authority;
-};
-
-/*****************************************************************************/
-
-MMAuthProvider *
-mm_auth_provider_polkit_new (void)
-{
- return g_object_new (MM_TYPE_AUTH_PROVIDER_POLKIT, NULL);
-}
-
-/*****************************************************************************/
-
-typedef struct {
- MMAuthProvider *self;
- GCancellable *cancellable;
- PolkitSubject *subject;
- gchar *authorization;
- GDBusMethodInvocation *invocation;
- GSimpleAsyncResult *result;
-} AuthorizeContext;
-
-static void
-authorize_context_complete_and_free (AuthorizeContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->invocation);
- g_object_unref (ctx->subject);
- g_object_unref (ctx->self);
- g_free (ctx->authorization);
- g_free (ctx);
-}
-
-static gboolean
-authorize_finish (MMAuthProvider *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-check_authorization_ready (PolkitAuthority *authority,
- GAsyncResult *res,
- AuthorizeContext *ctx)
-{
- PolkitAuthorizationResult *pk_result;
- GError *error = NULL;
-
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "PolicyKit authorization attempt cancelled");
- authorize_context_complete_and_free (ctx);
- return;
- }
-
- pk_result = polkit_authority_check_authorization_finish (authority, res, &error);
- if (!pk_result) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "PolicyKit authorization failed: '%s'",
- error->message);
- g_error_free (error);
- } else {
- if (polkit_authorization_result_get_is_authorized (pk_result))
- /* Good! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- else if (polkit_authorization_result_get_is_challenge (pk_result))
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNAUTHORIZED,
- "PolicyKit authorization failed: challenge needed for '%s'",
- ctx->authorization);
- else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNAUTHORIZED,
- "PolicyKit authorization failed: not authorized for '%s'",
- ctx->authorization);
- g_object_unref (pk_result);
- }
-
- authorize_context_complete_and_free (ctx);
-}
-
-static void
-authorize (MMAuthProvider *self,
- GDBusMethodInvocation *invocation,
- const gchar *authorization,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- MMAuthProviderPolkit *polkit = MM_AUTH_PROVIDER_POLKIT (self);
- AuthorizeContext *ctx;
-
- /* When creating the object, we actually allowed errors when looking for the
- * authority. If that is the case, we'll just forbid any incoming
- * authentication request */
- if (!polkit->priv->authority) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "PolicyKit authorization error: "
- "'authority not found'");
- return;
- }
-
- ctx = g_new (AuthorizeContext, 1);
- ctx->self = g_object_ref (self);
- ctx->invocation = g_object_ref (invocation);
- ctx->authorization = g_strdup (authorization);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- authorize);
- ctx->subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (ctx->invocation));
-
- polkit_authority_check_authorization (polkit->priv->authority,
- ctx->subject,
- authorization,
- NULL, /* details */
- POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
- ctx->cancellable,
- (GAsyncReadyCallback)check_authorization_ready,
- ctx);
-}
-
-/*****************************************************************************/
-
-static void
-mm_auth_provider_polkit_init (MMAuthProviderPolkit *self)
-{
- GError *error = NULL;
-
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_AUTH_PROVIDER_POLKIT,
- MMAuthProviderPolkitPrivate);
-
- self->priv->authority = polkit_authority_get_sync (NULL, &error);
- if (!self->priv->authority) {
- /* NOTE: we failed to create the polkit authority, but we still create
- * our AuthProvider. Every request will fail, though. */
- mm_warn ("failed to create PolicyKit authority: '%s'",
- error ? error->message : "unknown");
- g_clear_error (&error);
- }
-}
-
-static void
-dispose (GObject *object)
-{
- g_clear_object (&(MM_AUTH_PROVIDER_POLKIT (object)->priv->authority));
-
- G_OBJECT_CLASS (mm_auth_provider_polkit_parent_class)->dispose (object);
-}
-
-static void
-mm_auth_provider_polkit_class_init (MMAuthProviderPolkitClass *class)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (class);
- MMAuthProviderClass *auth_provider_class = MM_AUTH_PROVIDER_CLASS (class);
-
- g_type_class_add_private (class, sizeof (MMAuthProviderPolkitPrivate));
-
- /* Virtual methods */
- object_class->dispose = dispose;
- auth_provider_class->authorize = authorize;
- auth_provider_class->authorize_finish = authorize_finish;
-}
diff --git a/src/mm-auth-provider-polkit.h b/src/mm-auth-provider-polkit.h
deleted file mode 100644
index 5d020dc7..00000000
--- a/src/mm-auth-provider-polkit.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/* -*- 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) 2010 - 2012 Red Hat, Inc.
- * Copyright (C) 2012 Google, Inc.
- */
-
-#ifndef MM_AUTH_PROVIDER_POLKIT_H
-#define MM_AUTH_PROVIDER_POLKIT_H
-
-#include "mm-auth-provider.h"
-
-#define MM_TYPE_AUTH_PROVIDER_POLKIT (mm_auth_provider_polkit_get_type ())
-#define MM_AUTH_PROVIDER_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkit))
-#define MM_AUTH_PROVIDER_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitClass))
-#define MM_IS_AUTH_PROVIDER_POLKIT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT))
-#define MM_IS_AUTH_PROVIDER_POLKIT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_AUTH_PROVIDER_POLKIT))
-#define MM_AUTH_PROVIDER_POLKIT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_AUTH_PROVIDER_POLKIT, MMAuthProviderPolkitClass))
-
-typedef struct _MMAuthProviderPolkit MMAuthProviderPolkit;
-typedef struct _MMAuthProviderPolkitClass MMAuthProviderPolkitClass;
-typedef struct _MMAuthProviderPolkitPrivate MMAuthProviderPolkitPrivate;
-
-struct _MMAuthProviderPolkit {
- MMAuthProvider parent;
- MMAuthProviderPolkitPrivate *priv;
-};
-
-struct _MMAuthProviderPolkitClass {
- MMAuthProviderClass parent;
-};
-
-GType mm_auth_provider_polkit_get_type (void);
-
-MMAuthProvider *mm_auth_provider_polkit_new (void);
-
-#endif /* MM_AUTH_PROVIDER_POLKIT_H */
diff --git a/src/mm-auth-provider.c b/src/mm-auth-provider.c
index 4ef56234..5b4b13f3 100644
--- a/src/mm-auth-provider.c
+++ b/src/mm-auth-provider.c
@@ -12,91 +12,209 @@
*
* Copyright (C) 2010 - 2012 Red Hat, Inc.
* Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
+#include <config.h>
+
+#include <ModemManager.h>
+#include "mm-errors-types.h"
+#include "mm-log-object.h"
+#include "mm-utils.h"
#include "mm-auth-provider.h"
-G_DEFINE_TYPE (MMAuthProvider, mm_auth_provider, G_TYPE_OBJECT)
+#if defined WITH_POLKIT
+# include <polkit/polkit.h>
+#endif
+
+struct _MMAuthProvider {
+ GObject parent;
+#if defined WITH_POLKIT
+ PolkitAuthority *authority;
+#endif
+};
+
+struct _MMAuthProviderClass {
+ GObjectClass parent;
+};
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMAuthProvider, mm_auth_provider, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
/*****************************************************************************/
-MMAuthProvider *
-mm_auth_provider_new (void)
+gboolean
+mm_auth_provider_authorize_finish (MMAuthProvider *self,
+ GAsyncResult *res,
+ GError **error)
{
- return g_object_new (MM_TYPE_AUTH_PROVIDER, NULL);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-/*****************************************************************************/
+#if defined WITH_POLKIT
-gboolean
-mm_auth_provider_authorize_finish (MMAuthProvider *self,
- GAsyncResult *res,
- GError **error)
+typedef struct {
+ PolkitSubject *subject;
+ gchar *authorization;
+ GDBusMethodInvocation *invocation;
+} AuthorizeContext;
+
+static void
+authorize_context_free (AuthorizeContext *ctx)
{
- g_return_val_if_fail (MM_IS_AUTH_PROVIDER (self), FALSE);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->subject);
+ g_free (ctx->authorization);
+ g_free (ctx);
+}
- return MM_AUTH_PROVIDER_GET_CLASS (self)->authorize_finish (self, res, error);
+static void
+check_authorization_ready (PolkitAuthority *authority,
+ GAsyncResult *res,
+ GTask *task)
+{
+ PolkitAuthorizationResult *pk_result;
+ GError *error = NULL;
+ AuthorizeContext *ctx;
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+ pk_result = polkit_authority_check_authorization_finish (authority, res, &error);
+ if (!pk_result) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "PolicyKit authorization failed: '%s'",
+ error->message);
+ g_error_free (error);
+ } else {
+ if (polkit_authorization_result_get_is_authorized (pk_result))
+ /* Good! */
+ g_task_return_boolean (task, TRUE);
+ else if (polkit_authorization_result_get_is_challenge (pk_result))
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNAUTHORIZED,
+ "PolicyKit authorization failed: challenge needed for '%s'",
+ ctx->authorization);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNAUTHORIZED,
+ "PolicyKit authorization failed: not authorized for '%s'",
+ ctx->authorization);
+ g_object_unref (pk_result);
+ }
+
+ g_object_unref (task);
}
+#endif
void
-mm_auth_provider_authorize (MMAuthProvider *self,
+mm_auth_provider_authorize (MMAuthProvider *self,
GDBusMethodInvocation *invocation,
- const gchar *authorization,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ const gchar *authorization,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- g_return_if_fail (MM_IS_AUTH_PROVIDER (self));
-
- MM_AUTH_PROVIDER_GET_CLASS (self)->authorize (self,
- invocation,
- authorization,
- cancellable,
- callback,
- user_data);
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+#if defined WITH_POLKIT
+ {
+ AuthorizeContext *ctx;
+
+ /* When creating the object, we actually allowed errors when looking for the
+ * authority. If that is the case, we'll just forbid any incoming
+ * authentication request */
+ if (!self->authority) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "PolicyKit authorization error: 'authority not found'");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_new (AuthorizeContext, 1);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->authorization = g_strdup (authorization);
+ ctx->subject = polkit_system_bus_name_new (g_dbus_method_invocation_get_sender (ctx->invocation));
+ g_task_set_task_data (task, ctx, (GDestroyNotify)authorize_context_free);
+
+ polkit_authority_check_authorization (self->authority,
+ ctx->subject,
+ authorization,
+ NULL, /* details */
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION,
+ cancellable,
+ (GAsyncReadyCallback)check_authorization_ready,
+ task);
+ }
+#else
+ /* Just create the result and complete it */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+#endif
}
/*****************************************************************************/
-static gboolean
-authorize_finish (MMAuthProvider *self,
- GAsyncResult *res,
- GError **error)
+static gchar *
+log_object_build_id (MMLogObject *_self)
{
- /* Null auth; everything passes */
- return TRUE;
+ return g_strdup ("auth-provider");
}
+/*****************************************************************************/
+
static void
-authorize (MMAuthProvider *self,
- GDBusMethodInvocation *invocation,
- const gchar *authorization,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_auth_provider_init (MMAuthProvider *self)
{
- GSimpleAsyncResult *result;
-
- /* Just create the result and complete it */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- authorize);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+#if defined WITH_POLKIT
+ {
+ GError *error = NULL;
+
+ self->authority = polkit_authority_get_sync (NULL, &error);
+ if (!self->authority) {
+ /* NOTE: we failed to create the polkit authority, but we still create
+ * our AuthProvider. Every request will fail, though. */
+ mm_obj_warn (self, "failed to create PolicyKit authority: '%s'",
+ error ? error->message : "unknown");
+ g_clear_error (&error);
+ }
+ }
+#endif
}
-/*****************************************************************************/
+static void
+dispose (GObject *object)
+{
+#if defined WITH_POLKIT
+ g_clear_object (&(MM_AUTH_PROVIDER (object)->authority));
+#endif
+
+ G_OBJECT_CLASS (mm_auth_provider_parent_class)->dispose (object);
+}
static void
-mm_auth_provider_init (MMAuthProvider *self)
+log_object_iface_init (MMLogObjectInterface *iface)
{
+ iface->build_id = log_object_build_id;
}
static void
mm_auth_provider_class_init (MMAuthProviderClass *class)
{
- /* Virtual methods */
- class->authorize = authorize;
- class->authorize_finish = authorize_finish;
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ object_class->dispose = dispose;
}
+
+MM_DEFINE_SINGLETON_GETTER (MMAuthProvider, mm_auth_provider_get, MM_TYPE_AUTH_PROVIDER)
diff --git a/src/mm-auth-provider.h b/src/mm-auth-provider.h
index 5470414e..c33e42ce 100644
--- a/src/mm-auth-provider.h
+++ b/src/mm-auth-provider.h
@@ -17,6 +17,7 @@
#ifndef MM_AUTH_PROVIDER_H
#define MM_AUTH_PROVIDER_H
+#include <config.h>
#include <gio/gio.h>
#define MM_TYPE_AUTH_PROVIDER (mm_auth_provider_get_type ())
@@ -31,45 +32,27 @@
#define MM_AUTHORIZATION_DEVICE_CONTROL "org.freedesktop.ModemManager1.Device.Control"
#define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager1.Contacts"
#define MM_AUTHORIZATION_MESSAGING "org.freedesktop.ModemManager1.Messaging"
+#define MM_AUTHORIZATION_VOICE "org.freedesktop.ModemManager1.Voice"
#define MM_AUTHORIZATION_USSD "org.freedesktop.ModemManager1.USSD"
#define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager1.Location"
+#define MM_AUTHORIZATION_TIME "org.freedesktop.ModemManager1.Time"
#define MM_AUTHORIZATION_FIRMWARE "org.freedesktop.ModemManager1.Firmware"
-typedef struct _MMAuthProvider MMAuthProvider;
-typedef struct _MMAuthProviderClass MMAuthProviderClass;
-
-struct _MMAuthProvider {
- GObject parent;
-};
-
-struct _MMAuthProviderClass {
- GObjectClass parent;
-
- /* Perform authorization checks in this request (async).
- * Returns TRUE if authorized, FALSE if error is set. */
- void (* authorize) (MMAuthProvider *self,
- GDBusMethodInvocation *invocation,
- const gchar *authorization,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (* authorize_finish) (MMAuthProvider *self,
- GAsyncResult *res,
- GError **error);
-};
-
-GType mm_auth_provider_get_type (void);
-
-MMAuthProvider *mm_auth_provider_new (void);
-
-void mm_auth_provider_authorize (MMAuthProvider *self,
- GDBusMethodInvocation *invocation,
- const gchar *authorization,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_auth_provider_authorize_finish (MMAuthProvider *self,
- GAsyncResult *res,
- GError **error);
+typedef struct _MMAuthProvider MMAuthProvider;
+typedef struct _MMAuthProviderClass MMAuthProviderClass;
+typedef struct _MMAuthProviderPrivate MMAuthProviderPrivate;
+
+GType mm_auth_provider_get_type (void);
+MMAuthProvider *mm_auth_provider_get (void);
+
+void mm_auth_provider_authorize (MMAuthProvider *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *authorization,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_auth_provider_authorize_finish (MMAuthProvider *self,
+ GAsyncResult *res,
+ GError **error);
#endif /* MM_AUTH_PROVIDER_H */
diff --git a/src/mm-auth.c b/src/mm-auth.c
deleted file mode 100644
index 939c0af5..00000000
--- a/src/mm-auth.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/* -*- 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) 2010 - 2012 Red Hat, Inc.
- * Copyright (C) 2012 Google, Inc.
- */
-
-#include <string.h>
-
-#include "config.h"
-
-#include "mm-auth.h"
-#include "mm-auth-provider.h"
-
-#ifdef WITH_POLKIT
-#include "mm-auth-provider-polkit.h"
-#endif
-
-static MMAuthProvider *authp = NULL;
-
-MMAuthProvider *
-mm_auth_get_provider (void)
-{
- if (!authp) {
-#if WITH_POLKIT
- authp = mm_auth_provider_polkit_new ();
-#else
- authp = mm_auth_provider_new ();
-#endif
- }
-
- g_assert (authp);
-
- /* We'll keep the refcount of this object controlled, in order to have
- * clean shutdowns */
- return g_object_ref (authp);
-}
-
-void
-mm_auth_shutdown (void)
-{
- /* Clear the last reference of the auth provider if it was ever set */
- g_clear_object (&authp);
-}
diff --git a/src/mm-base-bearer.c b/src/mm-base-bearer.c
index 1b1c7b31..e5990b96 100644
--- a/src/mm-base-bearer.c
+++ b/src/mm-base-bearer.c
@@ -13,7 +13,8 @@
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2011 Red Hat, Inc.
* Copyright (C) 2011 Google, Inc.
- * Copyright (C) 2011 - 2014 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2015 Azimut Electronics
+ * Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -34,20 +35,32 @@
#include "mm-base-bearer.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
+#include "mm-error-helpers.h"
+#include "mm-bearer-stats.h"
/* We require up to 20s to get a proper IP when using PPP */
#define BEARER_IP_TIMEOUT_DEFAULT 20
#define BEARER_DEFERRED_UNREGISTRATION_TIMEOUT 15
-G_DEFINE_TYPE (MMBaseBearer, mm_base_bearer, MM_GDBUS_TYPE_BEARER_SKELETON);
+#define BEARER_STATS_UPDATE_TIMEOUT 30
+
+/* Initial connectivity check after 30s, then each 5s */
+#define BEARER_CONNECTION_MONITOR_INITIAL_TIMEOUT 30
+#define BEARER_CONNECTION_MONITOR_TIMEOUT 5
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBaseBearer, mm_base_bearer, MM_GDBUS_TYPE_BEARER_SKELETON, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
typedef enum {
CONNECTION_FORBIDDEN_REASON_NONE,
CONNECTION_FORBIDDEN_REASON_UNREGISTERED,
CONNECTION_FORBIDDEN_REASON_ROAMING,
+ CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY,
CONNECTION_FORBIDDEN_REASON_LAST
} ConnectionForbiddenReason;
@@ -58,7 +71,6 @@ enum {
PROP_MODEM,
PROP_STATUS,
PROP_CONFIG,
- PROP_DEFAULT_IP_FAMILY,
PROP_LAST
};
@@ -67,22 +79,30 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBaseBearerPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
+ guint dbus_id;
+
/* The modem which owns this BEARER */
MMBaseModem *modem;
/* The path where the BEARER object is exported */
gchar *path;
/* Status of this bearer */
MMBearerStatus status;
+ /* Whether we must ignore all disconnection updates if they're
+ * detected by ModemManager itself. */
+ gboolean ignore_disconnection_reports;
/* Configuration of the bearer */
MMBearerProperties *config;
- /* Default IP family of this bearer */
- MMBearerIpFamily default_ip_family;
/* Cancellable for connect() */
GCancellable *connect_cancellable;
/* handler id for the disconnect + cancel connect request */
gulong disconnect_signal_handler;
+ /* Connection status monitoring */
+ guint connection_monitor_id;
+ /* Flag to specify whether connection monitoring is supported or not */
+ gboolean load_connection_status_unsupported;
+
/*-- 3GPP specific --*/
guint deferred_3gpp_unregistration_id;
/* Reason if 3GPP connection is forbidden */
@@ -97,6 +117,15 @@ struct _MMBaseBearerPrivate {
/* Handler IDs for the registration state change signals */
guint id_cdma1x_registration_change;
guint id_evdo_registration_change;
+
+ /* The stats object to expose */
+ MMBearerStats *stats;
+ /* Handler id for the stats update timeout */
+ guint stats_update_id;
+ /* Timer to measure the duration of the connection */
+ GTimer *duration_timer;
+ /* Flag to specify whether reloading stats is supported or not */
+ gboolean reload_stats_unsupported;
};
/*****************************************************************************/
@@ -104,7 +133,8 @@ struct _MMBaseBearerPrivate {
static const gchar *connection_forbidden_reason_str [CONNECTION_FORBIDDEN_REASON_LAST] = {
"none",
"Not registered in the network",
- "Registered in roaming network, and roaming not allowed"
+ "Registered in roaming network, and roaming not allowed",
+ "Emergency services only",
};
/*****************************************************************************/
@@ -112,10 +142,9 @@ static const gchar *connection_forbidden_reason_str [CONNECTION_FORBIDDEN_REASON
void
mm_base_bearer_export (MMBaseBearer *self)
{
- static guint id = 0;
gchar *path;
- path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", id++);
+ path = g_strdup_printf (MM_DBUS_BEARER_PREFIX "/%d", self->priv->dbus_id);
g_object_set (self,
MM_BASE_BEARER_PATH, path,
NULL);
@@ -125,8 +154,345 @@ mm_base_bearer_export (MMBaseBearer *self)
/*****************************************************************************/
static void
+connection_monitor_stop (MMBaseBearer *self)
+{
+ if (self->priv->connection_monitor_id) {
+ g_source_remove (self->priv->connection_monitor_id);
+ self->priv->connection_monitor_id = 0;
+ }
+}
+
+static void
+load_connection_status_ready (MMBaseBearer *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ MMBearerConnectionStatus status;
+
+ status = MM_BASE_BEARER_GET_CLASS (self)->load_connection_status_finish (self, res, &error);
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN) {
+ /* Only warn if not reporting an "unsupported" error */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_warn (self, "checking if connected failed: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* If we're being told that connection monitoring is unsupported, just
+ * ignore the error and remove the timeout. */
+ mm_obj_dbg (self, "connection monitoring is unsupported by the device");
+ self->priv->load_connection_status_unsupported = TRUE;
+ connection_monitor_stop (self);
+ g_error_free (error);
+ return;
+ }
+
+ /* Report connection or disconnection */
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ mm_obj_dbg (self, "connection status loaded: %s", mm_bearer_connection_status_get_string (status));
+ mm_base_bearer_report_connection_status (self, status);
+}
+
+static gboolean
+connection_monitor_cb (MMBaseBearer *self)
+{
+ /* If the implementation knows how to load connection status, run it */
+ if (self->priv->status == MM_BEARER_STATUS_CONNECTED)
+ MM_BASE_BEARER_GET_CLASS (self)->load_connection_status (
+ self,
+ (GAsyncReadyCallback)load_connection_status_ready,
+ NULL);
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+initial_connection_monitor_cb (MMBaseBearer *self)
+{
+ if (self->priv->status == MM_BEARER_STATUS_CONNECTED)
+ MM_BASE_BEARER_GET_CLASS (self)->load_connection_status (
+ self,
+ (GAsyncReadyCallback)load_connection_status_ready,
+ NULL);
+
+ /* Add new monitor timeout at a higher rate */
+ self->priv->connection_monitor_id = g_timeout_add_seconds (BEARER_CONNECTION_MONITOR_TIMEOUT,
+ (GSourceFunc) connection_monitor_cb,
+ self);
+
+ /* Remove the initial connection monitor timeout as we added a new one */
+ return G_SOURCE_REMOVE;
+}
+
+static void
+connection_monitor_start (MMBaseBearer *self)
+{
+ /* If not implemented, don't schedule anything */
+ if (!MM_BASE_BEARER_GET_CLASS (self)->load_connection_status ||
+ !MM_BASE_BEARER_GET_CLASS (self)->load_connection_status_finish)
+ return;
+
+ if (self->priv->load_connection_status_unsupported)
+ return;
+
+ /* Schedule initial check */
+ g_assert (!self->priv->connection_monitor_id);
+ self->priv->connection_monitor_id = g_timeout_add_seconds (BEARER_CONNECTION_MONITOR_INITIAL_TIMEOUT,
+ (GSourceFunc) initial_connection_monitor_cb,
+ self);
+}
+
+/*****************************************************************************/
+
+static void
+bearer_update_connection_error (MMBaseBearer *self,
+ const GError *connection_error)
+{
+ g_autoptr(GVariant) tuple = NULL;
+
+ if (connection_error) {
+ /* Never overwrite a connection error if it's already set */
+ tuple = mm_gdbus_bearer_dup_connection_error (MM_GDBUS_BEARER (self));
+ if (tuple)
+ return;
+
+ /*
+ * Limit the type of errors we can expose in the interface;
+ * e.g. we don't want QMI or MBIM specific errors reported.
+ *
+ * G_IO_ERROR_CANCELLED is an exception, because we map it to
+ * MM_CORE_ERROR_CANCELLED implicitly when building the DBus error name.
+ */
+ if ((connection_error->domain != MM_CORE_ERROR) &&
+ (connection_error->domain != MM_MOBILE_EQUIPMENT_ERROR) &&
+ (connection_error->domain != MM_CONNECTION_ERROR) &&
+ (connection_error->domain != MM_SERIAL_ERROR) &&
+ (connection_error->domain != MM_CDMA_ACTIVATION_ERROR) &&
+ (!g_error_matches (connection_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
+ g_autoptr(GError) default_connection_error = NULL;
+
+#if defined WITH_QMI
+ if (connection_error->domain == QMI_CORE_ERROR)
+ mm_obj_dbg (self, "cannot set QMI core error as connection error: %s", connection_error->message);
+ else if (connection_error->domain == QMI_PROTOCOL_ERROR)
+ mm_obj_dbg (self, "cannot set QMI protocol error as connection error: %s", connection_error->message);
+ else
+#endif
+#if defined WITH_MBIM
+ if (connection_error->domain == MBIM_CORE_ERROR)
+ mm_obj_dbg (self, "cannot set MBIM core error as connection error: %s", connection_error->message);
+ else if (connection_error->domain == MBIM_PROTOCOL_ERROR)
+ mm_obj_dbg (self, "cannot set MBIM protocol error as connection error: %s", connection_error->message);
+ else if (connection_error->domain == MBIM_STATUS_ERROR)
+ mm_obj_dbg (self, "cannot set MBIM status error as connection error: %s", connection_error->message);
+ else
+#endif
+ mm_obj_dbg (self, "cannot set unhandled domain error as connection error: %s", connection_error->message);
+
+ default_connection_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "%s", connection_error->message);
+ tuple = mm_common_error_to_tuple (default_connection_error);
+ } else
+ tuple = mm_common_error_to_tuple (connection_error);
+ }
+ mm_gdbus_bearer_set_connection_error (MM_GDBUS_BEARER (self), tuple);
+}
+
+/*****************************************************************************/
+
+static void
+bearer_update_interface_stats (MMBaseBearer *self)
+{
+ mm_gdbus_bearer_set_stats (
+ MM_GDBUS_BEARER (self),
+ mm_bearer_stats_get_dictionary (self->priv->stats));
+}
+
+static void
+bearer_reset_ongoing_interface_stats (MMBaseBearer *self)
+{
+ mm_bearer_stats_set_duration (self->priv->stats, 0);
+ mm_bearer_stats_set_tx_bytes (self->priv->stats, 0);
+ mm_bearer_stats_set_rx_bytes (self->priv->stats, 0);
+ mm_bearer_stats_set_start_date (self->priv->stats, 0);
+ mm_bearer_stats_set_uplink_speed (self->priv->stats, 0);
+ mm_bearer_stats_set_downlink_speed (self->priv->stats, 0);
+ bearer_update_interface_stats (self);
+}
+
+static void
+bearer_set_ongoing_interface_stats (MMBaseBearer *self,
+ guint duration,
+ guint64 rx_bytes,
+ guint64 tx_bytes)
+{
+ guint n_updates = 0;
+
+ /* Make sure we don't reset to 0 these values if we had ever set them
+ * before. Just ignore the update if we're reported 0 */
+
+ if (duration) {
+ gint delta_duration;
+
+ delta_duration = duration - mm_bearer_stats_get_duration (self->priv->stats);
+ if (delta_duration > 0) {
+ mm_bearer_stats_set_duration (self->priv->stats, duration);
+ mm_bearer_stats_set_total_duration (self->priv->stats,
+ mm_bearer_stats_get_total_duration (self->priv->stats) + delta_duration);
+ n_updates++;
+ }
+ }
+
+ if (rx_bytes) {
+ gint64 delta_rx_bytes;
+
+ delta_rx_bytes = rx_bytes - mm_bearer_stats_get_rx_bytes (self->priv->stats);
+ if (delta_rx_bytes > 0) {
+ mm_bearer_stats_set_rx_bytes (self->priv->stats, rx_bytes);
+ mm_bearer_stats_set_total_rx_bytes (self->priv->stats,
+ mm_bearer_stats_get_total_rx_bytes (self->priv->stats) + delta_rx_bytes);
+ n_updates++;
+ }
+ }
+
+ if (tx_bytes) {
+ gint64 delta_tx_bytes;
+
+ delta_tx_bytes = tx_bytes - mm_bearer_stats_get_tx_bytes (self->priv->stats);
+ if (delta_tx_bytes > 0) {
+ mm_bearer_stats_set_tx_bytes (self->priv->stats, tx_bytes);
+ mm_bearer_stats_set_total_tx_bytes (self->priv->stats,
+ mm_bearer_stats_get_total_tx_bytes (self->priv->stats) + delta_tx_bytes);
+ n_updates++;
+ }
+ }
+
+ if (n_updates)
+ bearer_update_interface_stats (self);
+}
+
+static void
+bearer_stats_stop (MMBaseBearer *self)
+{
+ if (self->priv->duration_timer) {
+ bearer_set_ongoing_interface_stats (self,
+ (guint64) g_timer_elapsed (self->priv->duration_timer, NULL),
+ 0,
+ 0);
+ g_timer_destroy (self->priv->duration_timer);
+ self->priv->duration_timer = NULL;
+ }
+
+ if (self->priv->stats_update_id) {
+ g_source_remove (self->priv->stats_update_id);
+ self->priv->stats_update_id = 0;
+ }
+}
+
+static void
+reload_stats_ready (MMBaseBearer *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ guint64 rx_bytes = 0;
+ guint64 tx_bytes = 0;
+
+ if (!MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish (self, &rx_bytes, &tx_bytes, res, &error)) {
+ /* If reloading stats fails, warn about it and don't update anything */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_warn (self, "reloading stats failed: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* If we're being told that reloading stats is unsupported, just ignore
+ * the error and update oly the duration timer. */
+ mm_obj_dbg (self, "reloading stats is unsupported by the device");
+ self->priv->reload_stats_unsupported = TRUE;
+ rx_bytes = 0;
+ tx_bytes = 0;
+ g_error_free (error);
+ }
+
+ /* We only update stats if they were retrieved properly */
+ bearer_set_ongoing_interface_stats (self,
+ (guint32) g_timer_elapsed (self->priv->duration_timer, NULL),
+ rx_bytes,
+ tx_bytes);
+}
+
+static gboolean
+stats_update_cb (MMBaseBearer *self)
+{
+ /* Ignore stats update if we're not connected */
+ if (self->priv->status != MM_BEARER_STATUS_CONNECTED)
+ return G_SOURCE_CONTINUE;
+
+ /* If the implementation knows how to update stat values, run it */
+ if (!self->priv->reload_stats_unsupported &&
+ MM_BASE_BEARER_GET_CLASS (self)->reload_stats &&
+ MM_BASE_BEARER_GET_CLASS (self)->reload_stats_finish) {
+ MM_BASE_BEARER_GET_CLASS (self)->reload_stats (
+ self,
+ (GAsyncReadyCallback)reload_stats_ready,
+ NULL);
+ return G_SOURCE_CONTINUE;
+ }
+
+ /* Otherwise, just update duration and we're done */
+ bearer_set_ongoing_interface_stats (self,
+ (guint32) g_timer_elapsed (self->priv->duration_timer, NULL),
+ 0,
+ 0);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+bearer_stats_start (MMBaseBearer *self,
+ guint64 uplink_speed,
+ guint64 downlink_speed)
+{
+ /* Start duration timer */
+ g_assert (!self->priv->duration_timer);
+ self->priv->duration_timer = g_timer_new ();
+
+ /* Schedule */
+ g_assert (!self->priv->stats_update_id);
+ self->priv->stats_update_id = g_timeout_add_seconds (BEARER_STATS_UPDATE_TIMEOUT,
+ (GSourceFunc) stats_update_cb,
+ self);
+
+ mm_bearer_stats_set_start_date (self->priv->stats, (guint64)(g_get_real_time() / G_USEC_PER_SEC));
+ mm_bearer_stats_set_uplink_speed (self->priv->stats, uplink_speed);
+ mm_bearer_stats_set_downlink_speed (self->priv->stats, downlink_speed);
+ bearer_update_interface_stats (self);
+
+ /* Load initial values */
+ stats_update_cb (self);
+}
+
+/*****************************************************************************/
+
+void
+mm_base_bearer_report_speeds (MMBaseBearer *self,
+ guint64 uplink_speed,
+ guint64 downlink_speed)
+{
+ /* Ignore speeds update if we're not connected */
+ if (self->priv->status != MM_BEARER_STATUS_CONNECTED)
+ return;
+ mm_bearer_stats_set_uplink_speed (self->priv->stats, uplink_speed);
+ mm_bearer_stats_set_downlink_speed (self->priv->stats, downlink_speed);
+ bearer_update_interface_stats (self);
+}
+
+/*****************************************************************************/
+
+static void
bearer_reset_interface_status (MMBaseBearer *self)
{
+ mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+ mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
@@ -145,22 +511,54 @@ bearer_update_status (MMBaseBearer *self,
/* NOTE: we do allow status 'CONNECTED' here; it may happen if we go into
* DISCONNECTING and we cannot disconnect */
+ /* Do nothing if the status is the same */
+ if (self->priv->status == status)
+ return;
+
/* Update the property value */
self->priv->status = status;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
/* Ensure that we don't expose any connection related data in the
* interface when going into disconnected state. */
- if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
+ if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) {
+ g_autoptr(GString) report = NULL;
+
bearer_reset_interface_status (self);
+ /* Cleanup flag to ignore disconnection reports */
+ self->priv->ignore_disconnection_reports = FALSE;
+ /* Stop statistics */
+ bearer_stats_stop (self);
+ /* Stop connection monitoring */
+ connection_monitor_stop (self);
+
+ /* Build and log report */
+ report = g_string_new (NULL);
+ g_string_append_printf (report,
+ "connection #%u finished: duration %us",
+ mm_bearer_stats_get_attempts (self->priv->stats),
+ mm_bearer_stats_get_duration (self->priv->stats));
+ if (!self->priv->reload_stats_unsupported)
+ g_string_append_printf (report,
+ ", tx: %" G_GUINT64_FORMAT " bytes, rx: %" G_GUINT64_FORMAT " bytes",
+ mm_bearer_stats_get_tx_bytes (self->priv->stats),
+ mm_bearer_stats_get_rx_bytes (self->priv->stats));
+ mm_obj_info (self, "%s", report->str);
+ }
}
static void
-bearer_update_status_connected (MMBaseBearer *self,
- const gchar *interface,
+bearer_update_status_connected (MMBaseBearer *self,
+ const gchar *interface,
+ gboolean multiplexed,
+ gint profile_id,
MMBearerIpConfig *ipv4_config,
- MMBearerIpConfig *ipv6_config)
+ MMBearerIpConfig *ipv6_config,
+ guint64 uplink_speed,
+ guint64 downlink_speed)
{
+ mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), profile_id);
+ mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), multiplexed);
mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), TRUE);
mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), interface);
@@ -171,9 +569,25 @@ bearer_update_status_connected (MMBaseBearer *self,
MM_GDBUS_BEARER (self),
mm_bearer_ip_config_get_dictionary (ipv6_config));
+ /* If PPP is involved in the requested IP config, we must ignore
+ * all disconnection reports found via CGACT? polling or CGEV URCs.
+ * In this case, upper layers should always explicitly disconnect
+ * the bearer when ownership of the TTY is given back to MM. */
+ if ((ipv4_config && mm_bearer_ip_config_get_method (ipv4_config) == MM_BEARER_IP_METHOD_PPP) ||
+ (ipv6_config && mm_bearer_ip_config_get_method (ipv6_config) == MM_BEARER_IP_METHOD_PPP)) {
+ mm_obj_dbg (self, "PPP is required for connection, will ignore disconnection reports");
+ self->priv->ignore_disconnection_reports = TRUE;
+ }
+
/* Update the property value */
self->priv->status = MM_BEARER_STATUS_CONNECTED;
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_STATUS]);
+
+ /* Start statistics */
+ bearer_stats_start (self, uplink_speed, downlink_speed);
+
+ /* Start connection monitor, if supported */
+ connection_monitor_start (self);
}
/*****************************************************************************/
@@ -198,9 +612,9 @@ deferred_3gpp_unregistration_cb (MMBaseBearer *self)
g_warn_if_fail (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_UNREGISTERED);
self->priv->deferred_3gpp_unregistration_id = 0;
- mm_dbg ("Forcing bearer disconnection, not registered in 3GPP network");
+ mm_obj_dbg (self, "forcing bearer disconnection, not registered in 3GPP network");
mm_base_bearer_disconnect_force (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -221,15 +635,25 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_UNREGISTERED;
break;
case MM_MODEM_3GPP_REGISTRATION_STATE_HOME:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED:
case MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS:
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
break;
case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY:
+ case MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED:
if (mm_bearer_properties_get_allow_roaming (mm_base_bearer_peek_config (self)))
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
else
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_ROAMING;
break;
+ case MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY:
+ self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY;
+ break;
+ default:
+ g_assert_not_reached ();
}
/* If no reason to disconnect, or if it's a mixed CDMA+LTE modem without a CDMA reason,
@@ -243,7 +667,15 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
/* Modem is roaming and roaming not allowed, report right away */
if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_ROAMING) {
- mm_dbg ("Bearer not allowed to connect, registered in roaming 3GPP network");
+ mm_obj_dbg (self, "bearer not allowed to connect, registered in roaming 3GPP network");
+ reset_deferred_unregistration (self);
+ mm_base_bearer_disconnect_force (self);
+ return;
+ }
+
+ /* Modem is registered under emergency services only? */
+ if (self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_EMERGENCY_ONLY) {
+ mm_obj_dbg (self, "bearer not allowed to connect, emergency services only");
reset_deferred_unregistration (self);
mm_base_bearer_disconnect_force (self);
return;
@@ -257,13 +689,13 @@ modem_3gpp_registration_state_changed (MMIfaceModem3gpp *modem,
/* If the bearer is not connected, report right away */
if (self->priv->status != MM_BEARER_STATUS_CONNECTED) {
- mm_dbg ("Bearer not allowed to connect, not registered in 3GPP network");
+ mm_obj_dbg (self, "bearer not allowed to connect, not registered in 3GPP network");
mm_base_bearer_disconnect_force (self);
return;
}
/* Otherwise, setup the new timeout */
- mm_dbg ("Connected bearer not registered in 3GPP network");
+ mm_obj_dbg (self, "connected bearer not registered in 3GPP network");
self->priv->deferred_3gpp_unregistration_id =
g_timeout_add_seconds (BEARER_DEFERRED_UNREGISTRATION_TIMEOUT,
(GSourceFunc) deferred_3gpp_unregistration_cb,
@@ -280,9 +712,9 @@ deferred_cdma_unregistration_cb (MMBaseBearer *self)
g_warn_if_fail (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_UNREGISTERED);
self->priv->deferred_cdma_unregistration_id = 0;
- mm_dbg ("Forcing bearer disconnection, not registered in CDMA network");
+ mm_obj_dbg (self, "forcing bearer disconnection, not registered in CDMA network");
mm_base_bearer_disconnect_force (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -322,7 +754,7 @@ modem_cdma_registration_state_changed (MMIfaceModemCdma *modem,
/* Modem is roaming and roaming not allowed, report right away */
if (self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_ROAMING) {
- mm_dbg ("Bearer not allowed to connect, registered in roaming CDMA network");
+ mm_obj_dbg (self, "bearer not allowed to connect, registered in roaming CDMA network");
reset_deferred_unregistration (self);
mm_base_bearer_disconnect_force (self);
return;
@@ -336,13 +768,13 @@ modem_cdma_registration_state_changed (MMIfaceModemCdma *modem,
/* If the bearer is not connected, report right away */
if (self->priv->status != MM_BEARER_STATUS_CONNECTED) {
- mm_dbg ("Bearer not allowed to connect, not registered in CDMA network");
+ mm_obj_dbg (self, "bearer not allowed to connect, not registered in CDMA network");
mm_base_bearer_disconnect_force (self);
return;
}
/* Otherwise, setup the new timeout */
- mm_dbg ("Connected bearer not registered in CDMA network");
+ mm_obj_dbg (self, "connected bearer not registered in CDMA network");
self->priv->deferred_cdma_unregistration_id =
g_timeout_add_seconds (BEARER_DEFERRED_UNREGISTRATION_TIMEOUT,
(GSourceFunc) deferred_cdma_unregistration_cb,
@@ -421,7 +853,7 @@ mm_base_bearer_connect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -431,14 +863,11 @@ disconnect_after_cancel_ready (MMBaseBearer *self,
GError *error = NULL;
if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
- mm_warn ("Error disconnecting bearer '%s': '%s'. "
- "Will assume disconnected anyway.",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "error disconnecting: %s; will assume disconnected anyway", error->message);
g_error_free (error);
}
else
- mm_dbg ("Disconnected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnected bearer '%s'", self->priv->path);
/* Report disconnection to the bearer object using class method
* mm_bearer_report_connection_status. This gives subclass implementations a
@@ -451,7 +880,7 @@ disconnect_after_cancel_ready (MMBaseBearer *self,
static void
connect_ready (MMBaseBearer *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
gboolean launch_disconnect = FALSE;
@@ -460,44 +889,51 @@ connect_ready (MMBaseBearer *self,
/* NOTE: connect() implementations *MUST* handle cancellations themselves */
result = MM_BASE_BEARER_GET_CLASS (self)->connect_finish (self, res, &error);
if (!result) {
- mm_dbg ("Couldn't connect bearer '%s': '%s'",
- self->priv->path,
- error->message);
- if (g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED)) {
+ mm_obj_warn (self, "connection attempt #%u failed: %s",
+ mm_bearer_stats_get_attempts (self->priv->stats),
+ error->message);
+
+ /* Update failed attempts */
+ mm_bearer_stats_set_failed_attempts (self->priv->stats,
+ mm_bearer_stats_get_failed_attempts (self->priv->stats) + 1);
+ bearer_update_interface_stats (self);
+
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
/* Will launch disconnection */
launch_disconnect = TRUE;
- } else
+ } else {
+ /* Update reported connection error before the status update */
+ bearer_update_connection_error (self, error);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
-
- g_simple_async_result_take_error (simple, error);
+ }
}
/* Handle cancellations detected after successful connection */
else if (g_cancellable_is_cancelled (self->priv->connect_cancellable)) {
- mm_dbg ("Connected bearer '%s', but need to disconnect", self->priv->path);
+ mm_obj_dbg (self, "connected, but need to disconnect");
mm_bearer_connect_result_unref (result);
- g_simple_async_result_set_error (
- simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Bearer got connected, but had to disconnect after cancellation request");
- launch_disconnect = TRUE;
+ error = g_error_new (G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "Bearer got connected, but had to disconnect after cancellation request");
+ launch_disconnect = TRUE;
}
else {
- mm_dbg ("Connected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "connected");
/* Update bearer and interface status */
bearer_update_status_connected (
self,
mm_port_get_device (mm_bearer_connect_result_peek_data (result)),
+ mm_bearer_connect_result_get_multiplexed (result),
+ mm_bearer_connect_result_get_profile_id (result),
mm_bearer_connect_result_peek_ipv4_config (result),
- mm_bearer_connect_result_peek_ipv6_config (result));
+ mm_bearer_connect_result_peek_ipv6_config (result),
+ mm_bearer_connect_result_get_uplink_speed (result),
+ mm_bearer_connect_result_get_downlink_speed (result));
mm_bearer_connect_result_unref (result);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
}
if (launch_disconnect) {
+ /* Update reported connection error before the status update */
+ bearer_update_connection_error (self, error);
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTING);
MM_BASE_BEARER_GET_CLASS (self)->disconnect (
self,
@@ -506,8 +942,13 @@ connect_ready (MMBaseBearer *self,
}
g_clear_object (&self->priv->connect_cancellable);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
void
@@ -515,17 +956,28 @@ mm_base_bearer_connect (MMBaseBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->connect != NULL);
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->connect_finish != NULL);
+ if (!MM_BASE_BEARER_GET_CLASS (self)->connect) {
+ g_assert (!MM_BASE_BEARER_GET_CLASS (self)->connect_finish);
+ g_task_report_new_error (
+ self,
+ callback,
+ user_data,
+ mm_base_bearer_connect,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Bearer doesn't allow explicit connection requests");
+ return;
+ }
/* If already connecting, return error, don't allow a second request. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Bearer already being connected");
@@ -535,10 +987,11 @@ mm_base_bearer_connect (MMBaseBearer *self,
/* If currently disconnecting, return error, previous operation should
* finish before allowing to connect again. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Bearer currently being disconnected");
@@ -548,10 +1001,11 @@ mm_base_bearer_connect (MMBaseBearer *self,
/* Check 3GPP roaming allowance, *only* roaming related here */
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self->priv->modem)) &&
self->priv->reason_3gpp == CONNECTION_FORBIDDEN_REASON_ROAMING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"Not allowed to connect bearer in 3GPP network: '%s'",
@@ -562,10 +1016,11 @@ mm_base_bearer_connect (MMBaseBearer *self,
/* Check CDMA roaming allowance, *only* roaming related here */
if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self->priv->modem)) &&
self->priv->reason_cdma == CONNECTION_FORBIDDEN_REASON_ROAMING) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_base_bearer_connect,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"Not allowed to connect bearer in CDMA network: '%s'",
@@ -573,28 +1028,32 @@ mm_base_bearer_connect (MMBaseBearer *self,
return;
}
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_bearer_connect);
+ task = g_task_new (self, NULL, callback, user_data);
/* If already connected, done */
if (self->priv->status == MM_BEARER_STATUS_CONNECTED) {
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
+ /* Update total attempts */
+ mm_bearer_stats_set_attempts (self->priv->stats,
+ mm_bearer_stats_get_attempts (self->priv->stats) + 1);
+ bearer_reset_ongoing_interface_stats (self);
+
+ /* Clear previous connection error, if any */
+ bearer_update_connection_error (self, NULL);
+
/* Connecting! */
- mm_dbg ("Connecting bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "connecting...");
self->priv->connect_cancellable = g_cancellable_new ();
bearer_update_status (self, MM_BEARER_STATUS_CONNECTING);
MM_BASE_BEARER_GET_CLASS (self)->connect (
self,
self->priv->connect_cancellable,
(GAsyncReadyCallback)connect_ready,
- result);
+ task);
}
typedef struct {
@@ -658,6 +1117,8 @@ handle_connect (MMBaseBearer *self,
MM_BASE_BEARER_MODEM, &ctx->modem,
NULL);
+ mm_obj_dbg (self, "user request to connect");
+
mm_base_modem_authorize (ctx->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
@@ -674,52 +1135,49 @@ mm_base_bearer_disconnect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disconnect_ready (MMBaseBearer *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
- mm_dbg ("Couldn't disconnect bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "couldn't disconnect: %s", error->message);
bearer_update_status (self, MM_BEARER_STATUS_CONNECTED);
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
}
else {
- mm_dbg ("Disconnected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnected");
bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
}
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
status_changed_complete_disconnect (MMBaseBearer *self,
GParamSpec *pspec,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
/* We may get other states here before DISCONNECTED, like DISCONNECTING or
* even CONNECTED. */
if (self->priv->status != MM_BEARER_STATUS_DISCONNECTED)
return;
- mm_dbg ("Disconnected bearer '%s' after cancelling previous connect request",
- self->priv->path);
+ mm_obj_dbg (self, "disconnected after cancelling previous connect request");
g_signal_handler_disconnect (self,
self->priv->disconnect_signal_handler);
self->priv->disconnect_signal_handler = 0;
/* Note: interface state is updated when the DISCONNECTED state is set */
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -727,37 +1185,40 @@ mm_base_bearer_disconnect (MMBaseBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *simple;
+ GTask *task;
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->disconnect != NULL);
- g_assert (MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish != NULL);
+ task = g_task_new (self, NULL, callback, user_data);
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_bearer_disconnect);
+ if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect) {
+ g_assert (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish);
+ g_task_return_new_error (
+ task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Bearer doesn't allow explicit disconnection requests");
+ g_object_unref (task);
+ return;
+ }
/* If already disconnected, done */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTED) {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* If already disconnecting, return error, don't allow a second request. */
if (self->priv->status == MM_BEARER_STATUS_DISCONNECTING) {
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_IN_PROGRESS,
"Bearer already being disconnected");
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ g_object_unref (task);
return;
}
- mm_dbg ("Disconnecting bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnecting...");
/* If currently connecting, try to cancel that operation, and wait to get
* disconnected. */
@@ -774,7 +1235,7 @@ mm_base_bearer_disconnect (MMBaseBearer *self,
g_signal_connect (self,
"notify::" MM_BASE_BEARER_STATUS,
(GCallback)status_changed_complete_disconnect,
- simple); /* takes ownership */
+ task); /* takes ownership */
return;
}
@@ -784,7 +1245,7 @@ mm_base_bearer_disconnect (MMBaseBearer *self,
MM_BASE_BEARER_GET_CLASS (self)->disconnect (
self,
(GAsyncReadyCallback)disconnect_ready,
- simple); /* takes ownership */
+ task); /* takes ownership */
}
typedef struct {
@@ -848,6 +1309,8 @@ handle_disconnect (MMBaseBearer *self,
MM_BASE_BEARER_MODEM, &ctx->modem,
NULL);
+ mm_obj_dbg (self, "user request to disconnect");
+
mm_base_modem_authorize (ctx->modem,
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
@@ -877,9 +1340,7 @@ base_bearer_dbus_export (MMBaseBearer *self)
self->priv->connection,
self->priv->path,
&error)) {
- mm_warn ("couldn't export BEARER at '%s': '%s'",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "couldn't export to bus: %s", error->message);
g_error_free (error);
}
}
@@ -892,7 +1353,7 @@ base_bearer_dbus_unexport (MMBaseBearer *self)
path = g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self));
/* Only unexport if currently exported */
if (path) {
- mm_dbg ("Removing from DBus bearer at '%s'", path);
+ mm_obj_dbg (self, "removing from bus");
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
}
}
@@ -925,10 +1386,10 @@ mm_base_bearer_get_config (MMBaseBearer *self)
NULL);
}
-MMBearerIpFamily
-mm_base_bearer_get_default_ip_family (MMBaseBearer *self)
+gint
+mm_base_bearer_get_profile_id (MMBaseBearer *self)
{
- return self->priv->default_ip_family;
+ return mm_gdbus_bearer_get_profile_id (MM_GDBUS_BEARER (self));
}
/*****************************************************************************/
@@ -940,14 +1401,11 @@ disconnect_force_ready (MMBaseBearer *self,
GError *error = NULL;
if (!MM_BASE_BEARER_GET_CLASS (self)->disconnect_finish (self, res, &error)) {
- mm_warn ("Error disconnecting bearer '%s': '%s'. "
- "Will assume disconnected anyway.",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "error disconnecting: %s; will assume disconnected anyway", error->message);
g_error_free (error);
}
else
- mm_dbg ("Disconnected bearer '%s'", self->priv->path);
+ mm_obj_dbg (self, "disconnected");
/* Report disconnection to the bearer object using class method
* mm_bearer_report_connection_status. This gives subclass implementations a
@@ -964,7 +1422,12 @@ mm_base_bearer_disconnect_force (MMBaseBearer *self)
self->priv->status == MM_BEARER_STATUS_DISCONNECTED)
return;
- mm_dbg ("Forcing disconnection of bearer '%s'", self->priv->path);
+ if (self->priv->ignore_disconnection_reports) {
+ mm_obj_dbg (self, "disconnection should be forced but it's explicitly ignored");
+ return;
+ }
+
+ mm_obj_dbg (self, "forcing disconnection");
/* If currently connecting, try to cancel that operation. */
if (self->priv->status == MM_BEARER_STATUS_CONNECTING) {
@@ -983,28 +1446,215 @@ mm_base_bearer_disconnect_force (MMBaseBearer *self)
/*****************************************************************************/
static void
-report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status)
+report_connection_status (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- /* 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).
+ /* The only status expected at this point is DISCONNECTED or CONNECTED,
+ * although here we just process the DISCONNECTED one.
*/
- g_assert (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED || 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);
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ bearer_update_connection_error (self, connection_error);
+ bearer_update_status (self, MM_BEARER_STATUS_DISCONNECTED);
+ }
}
+/*
+ * This method is used exclusively in two different scenarios:
+ * a) to report disconnections detected by ModemManager itself (e.g. based on
+ * CGACT polling or CGEV URCs), applicable to bearers using both NET and
+ * PPP data ports.
+ * b) to report failed or successful connection attempts by plugins using NET
+ * data ports that rely on vendor-specific URCs (e.g. Icera, MBM, Option
+ * HSO).
+ *
+ * The method is also subclass-able because plugins may require specific
+ * cleanup operations to be done when a bearer is reported as disconnected.
+ * (e.g. the QMI or MBIM implementations require removing signal handlers).
+ *
+ * For all the scenarios involving a) the plugins are required to call the
+ * parent report_connection_status() implementation to report the
+ * DISCONNECTED state. For scenarios involving b) the parent reporting is not
+ * expected at all. In other words, the parent report_connection_status()
+ * is exclusively used in processing disconnections detected by ModemManager
+ * itself.
+ *
+ * If the bearer has been connected and it has required PPP method, we will
+ * ignore all disconnection reports because we cannot disconnect a PPP-based
+ * bearer before the upper layers have stopped using the TTY. In this case,
+ * we must wait for upper layers to detect the disconnection themselves (e.g.
+ * pppd should detect it) and disconnect the bearer through DBus.
+ */
void
-mm_base_bearer_report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status)
+mm_base_bearer_report_connection_status_detailed (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status);
+ /* Reporting disconnection? */
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED || status == MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED) {
+ if (self->priv->ignore_disconnection_reports) {
+ mm_obj_dbg (self, "ignoring disconnection report");
+ return;
+ }
+
+ /* Setup a generic default error if none explicitly given when reporting
+ * bearer disconnections. */
+ if (!connection_error) {
+ g_autoptr(GError) default_connection_error = NULL;
+
+ default_connection_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, self);
+ return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status, default_connection_error);
+ }
+ }
+
+ return MM_BASE_BEARER_GET_CLASS (self)->report_connection_status (self, status, connection_error);
+}
+
+/*****************************************************************************/
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef struct _SyncingContext SyncingContext;
+static void interface_syncing_step (GTask *task);
+
+typedef enum {
+ SYNCING_STEP_FIRST,
+ SYNCING_STEP_REFRESH_CONNECTION,
+ SYNCING_STEP_LAST
+} SyncingStep;
+
+struct _SyncingContext {
+ SyncingStep step;
+ MMBearerStatus status;
+};
+
+gboolean
+mm_base_bearer_sync_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
+reload_connection_status_ready (MMBaseBearer *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ MMBearerConnectionStatus reloaded_status;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ /* The only update we're really interested in is the connected->disconnected
+ * one, because any other would be extremely strange and it's probably not
+ * worth trying to support those; e.g. a disconnected->connected change here
+ * would be impossible to be handled correctly. We'll also ignore intermediate
+ * states (connecting/disconnecting), as we can rely on the reports of the final
+ * state at some point soon.
+ *
+ * So, just handle DISCONNECTED at this point.
+ */
+ reloaded_status = MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status_finish (self, res, &error);
+ if (reloaded_status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ mm_obj_warn (self, "reloading connection status failed: %s", error->message);
+ else if ((ctx->status == MM_BEARER_STATUS_CONNECTED) &&
+ (reloaded_status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)) {
+ mm_obj_dbg (self, "disconnection detected during status synchronization");
+ mm_base_bearer_report_connection_status (self, reloaded_status);
+ }
+
+ /* Go on to the next step */
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+interface_syncing_step (GTask *task)
+{
+ MMBaseBearer *self;
+ SyncingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SYNCING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_REFRESH_CONNECTION:
+ /*
+ * AT+PPP based connections should not be synced.
+ * When a AT+PPP connection bearer is connected, the 'ignore_disconnection_reports' flag is set.
+ */
+ if (!self->priv->ignore_disconnection_reports) {
+ if (!MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status)
+ mm_obj_warn (self, "unable to reload connection status, method not implemented");
+ else {
+ mm_obj_dbg (self, "refreshing connection status");
+ MM_BASE_BEARER_GET_CLASS (self)->reload_connection_status (self,
+ (GAsyncReadyCallback) reload_connection_status_ready,
+ task);
+ return;
+ }
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_base_bearer_sync (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncingContext *ctx;
+ GTask *task;
+
+ /* Create SyncingContext and store the original bearer status */
+ ctx = g_new0 (SyncingContext, 1);
+ ctx->step = SYNCING_STEP_FIRST;
+ ctx->status = self->priv->status;
+
+ /* Create sync steps task and execute it */
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
+ interface_syncing_step (task);
+}
+
+#endif
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMBaseBearer *self;
+
+ self = MM_BASE_BEARER (_self);
+ return g_strdup_printf ("bearer%u", self->priv->dbus_id);
+}
+
+/*****************************************************************************/
+
+static void
set_property (GObject *object,
guint prop_id,
const GValue *value,
@@ -1036,6 +1686,8 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
if (self->priv->modem) {
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
/* Bind the modem's connection (which is set when it is exported,
* and unset when unexported) to the BEARER's connection */
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
@@ -1069,9 +1721,6 @@ set_property (GObject *object,
g_variant_unref (dictionary);
break;
}
- case PROP_DEFAULT_IP_FAMILY:
- self->priv->default_ip_family = g_value_get_flags (value);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1102,9 +1751,6 @@ get_property (GObject *object,
case PROP_CONFIG:
g_value_set_object (value, self->priv->config);
break;
- case PROP_DEFAULT_IP_FAMILY:
- g_value_set_flags (value, self->priv->default_ip_family);
- break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1114,25 +1760,35 @@ get_property (GObject *object,
static void
mm_base_bearer_init (MMBaseBearer *self)
{
+ static guint id = 0;
+
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BASE_BEARER,
MMBaseBearerPrivate);
+
+ /* Each bearer is given a unique id to build its own DBus path */
+ self->priv->dbus_id = id++;
+
self->priv->status = MM_BEARER_STATUS_DISCONNECTED;
self->priv->reason_3gpp = CONNECTION_FORBIDDEN_REASON_NONE;
self->priv->reason_cdma = CONNECTION_FORBIDDEN_REASON_NONE;
- self->priv->default_ip_family = MM_BEARER_IP_FAMILY_IPV4;
+ self->priv->stats = mm_bearer_stats_new ();
/* Set defaults */
- mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
- mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
- mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
- mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL);
- mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), BEARER_IP_TIMEOUT_DEFAULT);
- mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self),
- mm_bearer_ip_config_get_dictionary (NULL));
- mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self),
- mm_bearer_ip_config_get_dictionary (NULL));
+ mm_gdbus_bearer_set_interface (MM_GDBUS_BEARER (self), NULL);
+ mm_gdbus_bearer_set_multiplexed (MM_GDBUS_BEARER (self), FALSE);
+ mm_gdbus_bearer_set_profile_id (MM_GDBUS_BEARER (self), MM_3GPP_PROFILE_ID_UNKNOWN);
+ mm_gdbus_bearer_set_connected (MM_GDBUS_BEARER (self), FALSE);
+ mm_gdbus_bearer_set_suspended (MM_GDBUS_BEARER (self), FALSE);
+ mm_gdbus_bearer_set_properties (MM_GDBUS_BEARER (self), NULL);
+ mm_gdbus_bearer_set_ip_timeout (MM_GDBUS_BEARER (self), BEARER_IP_TIMEOUT_DEFAULT);
+ mm_gdbus_bearer_set_bearer_type (MM_GDBUS_BEARER (self), MM_BEARER_TYPE_DEFAULT);
+ mm_gdbus_bearer_set_ip4_config (MM_GDBUS_BEARER (self),
+ mm_bearer_ip_config_get_dictionary (NULL));
+ mm_gdbus_bearer_set_ip6_config (MM_GDBUS_BEARER (self),
+ mm_bearer_ip_config_get_dictionary (NULL));
+ bearer_update_interface_stats (self);
}
static void
@@ -1150,6 +1806,10 @@ dispose (GObject *object)
{
MMBaseBearer *self = MM_BASE_BEARER (object);
+ connection_monitor_stop (self);
+ bearer_stats_stop (self);
+ g_clear_object (&self->priv->stats);
+
if (self->priv->connection) {
base_bearer_dbus_unexport (self);
g_clear_object (&self->priv->connection);
@@ -1165,6 +1825,12 @@ dispose (GObject *object)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_bearer_class_init (MMBaseBearerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1219,25 +1885,20 @@ mm_base_bearer_class_init (MMBaseBearerClass *klass)
MM_TYPE_BEARER_PROPERTIES,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_CONFIG, properties[PROP_CONFIG]);
-
- properties[PROP_DEFAULT_IP_FAMILY] =
- g_param_spec_flags (MM_BASE_BEARER_DEFAULT_IP_FAMILY,
- "Bearer default IP family",
- "IP family to use for this bearer when no IP family is specified",
- MM_TYPE_BEARER_IP_FAMILY,
- MM_BEARER_IP_FAMILY_IPV4,
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_DEFAULT_IP_FAMILY, properties[PROP_DEFAULT_IP_FAMILY]);
}
/*****************************************************************************/
/* Helpers to implement connect() */
struct _MMBearerConnectResult {
- volatile gint ref_count;
- MMPort *data;
+ volatile gint ref_count;
+ MMPort *data;
MMBearerIpConfig *ipv4_config;
MMBearerIpConfig *ipv6_config;
+ gboolean multiplexed;
+ gint profile_id;
+ guint64 uplink_speed;
+ guint64 downlink_speed;
};
MMBearerConnectResult *
@@ -1279,8 +1940,60 @@ mm_bearer_connect_result_peek_ipv6_config (MMBearerConnectResult *result)
return result->ipv6_config;
}
+void
+mm_bearer_connect_result_set_multiplexed (MMBearerConnectResult *result,
+ gboolean multiplexed)
+{
+ result->multiplexed = multiplexed;
+}
+
+gboolean
+mm_bearer_connect_result_get_multiplexed (MMBearerConnectResult *result)
+{
+ return result->multiplexed;
+}
+
+void
+mm_bearer_connect_result_set_profile_id (MMBearerConnectResult *result,
+ gint profile_id)
+{
+ result->profile_id = profile_id;
+}
+
+gint
+mm_bearer_connect_result_get_profile_id (MMBearerConnectResult *result)
+{
+ return result->profile_id;
+}
+
+void
+mm_bearer_connect_result_set_uplink_speed (MMBearerConnectResult *result,
+ guint64 speed)
+{
+ result->uplink_speed = speed;
+}
+
+guint64
+mm_bearer_connect_result_get_uplink_speed (MMBearerConnectResult *result)
+{
+ return result->uplink_speed;
+}
+
+void
+mm_bearer_connect_result_set_downlink_speed (MMBearerConnectResult *result,
+ guint64 speed)
+{
+ result->downlink_speed = speed;
+}
+
+guint64
+mm_bearer_connect_result_get_downlink_speed (MMBearerConnectResult *result)
+{
+ return result->downlink_speed;
+}
+
MMBearerConnectResult *
-mm_bearer_connect_result_new (MMPort *data,
+mm_bearer_connect_result_new (MMPort *data,
MMBearerIpConfig *ipv4_config,
MMBearerIpConfig *ipv6_config)
{
@@ -1296,5 +2009,7 @@ mm_bearer_connect_result_new (MMPort *data,
result->ipv4_config = g_object_ref (ipv4_config);
if (ipv6_config)
result->ipv6_config = g_object_ref (ipv6_config);
+ result->multiplexed = FALSE; /* default */
+ result->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN;
return result;
}
diff --git a/src/mm-base-bearer.h b/src/mm-base-bearer.h
index 2c196f67..7432c04a 100644
--- a/src/mm-base-bearer.h
+++ b/src/mm-base-bearer.h
@@ -10,10 +10,10 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Author: Aleksander Morgado <aleksander@lanedo.com>
- *
+
* Copyright (C) 2011 Google, Inc.
- * Copyright (C) 2011 - 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2015 Azimut Electronics
+ * Copyright (C) 2011 - 2015 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_BASE_BEARER_H
@@ -37,10 +37,42 @@ MMBearerConnectResult *mm_bearer_connect_result_new (MMPort *data,
MMBearerIpConfig *ipv6_config);
void mm_bearer_connect_result_unref (MMBearerConnectResult *result);
MMBearerConnectResult *mm_bearer_connect_result_ref (MMBearerConnectResult *result);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerConnectResult, mm_bearer_connect_result_unref)
+
MMPort *mm_bearer_connect_result_peek_data (MMBearerConnectResult *result);
MMBearerIpConfig *mm_bearer_connect_result_peek_ipv4_config (MMBearerConnectResult *result);
MMBearerIpConfig *mm_bearer_connect_result_peek_ipv6_config (MMBearerConnectResult *result);
+/* by default, if none specified, multiplexed=FALSE */
+void mm_bearer_connect_result_set_multiplexed (MMBearerConnectResult *result,
+ gboolean multiplexed);
+gboolean mm_bearer_connect_result_get_multiplexed (MMBearerConnectResult *result);
+
+/* profile id, if known */
+void mm_bearer_connect_result_set_profile_id (MMBearerConnectResult *result,
+ gint profile_id);
+gint mm_bearer_connect_result_get_profile_id (MMBearerConnectResult *result);
+
+/* speed, for stats */
+void mm_bearer_connect_result_set_uplink_speed (MMBearerConnectResult *result,
+ guint64 speed);
+guint64 mm_bearer_connect_result_get_uplink_speed (MMBearerConnectResult *result);
+void mm_bearer_connect_result_set_downlink_speed (MMBearerConnectResult *result,
+ guint64 speed);
+guint64 mm_bearer_connect_result_get_downlink_speed (MMBearerConnectResult *result);
+
+/*****************************************************************************/
+
+/* Default timeout values to be used in the steps of a connection or
+ * disconnection attempt that may take long to complete. Note that the actual
+ * connection attempt from the user may have a different timeout, but we don't
+ * really fully care about that, it's a problem to consider in the user side.
+ * In the daemon itself, what we want and require is to be in sync with the
+ * state of the modem. */
+#define MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT 180
+#define MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT 120
+
/*****************************************************************************/
#define MM_TYPE_BASE_BEARER (mm_base_bearer_get_type ())
@@ -54,12 +86,11 @@ typedef struct _MMBaseBearer MMBaseBearer;
typedef struct _MMBaseBearerClass MMBaseBearerClass;
typedef struct _MMBaseBearerPrivate MMBaseBearerPrivate;
-#define MM_BASE_BEARER_PATH "bearer-path"
-#define MM_BASE_BEARER_CONNECTION "bearer-connection"
-#define MM_BASE_BEARER_MODEM "bearer-modem"
-#define MM_BASE_BEARER_STATUS "bearer-status"
-#define MM_BASE_BEARER_CONFIG "bearer-config"
-#define MM_BASE_BEARER_DEFAULT_IP_FAMILY "bearer-deafult-ip-family"
+#define MM_BASE_BEARER_PATH "bearer-path"
+#define MM_BASE_BEARER_CONNECTION "bearer-connection"
+#define MM_BASE_BEARER_MODEM "bearer-modem"
+#define MM_BASE_BEARER_STATUS "bearer-status"
+#define MM_BASE_BEARER_CONFIG "bearer-config"
typedef enum { /*< underscore_name=mm_bearer_status >*/
MM_BEARER_STATUS_DISCONNECTED,
@@ -101,21 +132,71 @@ struct _MMBaseBearerClass {
GAsyncResult *res,
GError **error);
+ /* Monitor connection status:
+ *
+ * Only CONNECTED or DISCONNECTED should be reported here; this method
+ * is used to poll for connection status once the connection has been
+ * established.
+ *
+ * This method will return MM_CORE_ERROR_UNSUPPORTED if the polling
+ * is not required (i.e. if we can safely rely on async indications
+ * sent by the modem).
+ */
+ void (* load_connection_status) (MMBaseBearer *bearer,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMBearerConnectionStatus (* load_connection_status_finish) (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GError **error);
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+ /* Reload connection status:
+ *
+ * This method should return the exact connection status of the bearer, and
+ * the check must always be performed (if supported). This method should not
+ * return MM_CORE_ERROR_UNSUPPORTED as a way to skip the operation, as in
+ * this case the connection monitoring is required during the quick
+ * suspend/resume synchronization.
+ *
+ * It is up to each protocol/plugin whether providing the same method here
+ * and in load_connection_status() makes sense.
+ */
+ void (* reload_connection_status) (MMBaseBearer *bearer,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMBearerConnectionStatus (* reload_connection_status_finish) (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
+
+ /* Reload statistics */
+ void (* reload_stats) (MMBaseBearer *bearer,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* reload_stats_finish) (MMBaseBearer *bearer,
+ guint64 *bytes_rx,
+ guint64 *bytes_tx,
+ GAsyncResult *res,
+ GError **error);
+
/* Report connection status of this bearer */
- void (* report_connection_status) (MMBaseBearer *bearer,
- MMBearerConnectionStatus status);
+ void (* report_connection_status) (MMBaseBearer *bearer,
+ MMBearerConnectionStatus status,
+ const GError *connection_error);
};
GType mm_base_bearer_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseBearer, g_object_unref)
void mm_base_bearer_export (MMBaseBearer *self);
-const gchar *mm_base_bearer_get_path (MMBaseBearer *self);
-MMBearerStatus mm_base_bearer_get_status (MMBaseBearer *self);
-MMBearerProperties *mm_base_bearer_peek_config (MMBaseBearer *self);
-MMBearerProperties *mm_base_bearer_get_config (MMBaseBearer *self);
-MMBearerIpFamily mm_base_bearer_get_default_ip_family (MMBaseBearer *self);
-
+const gchar *mm_base_bearer_get_path (MMBaseBearer *self);
+MMBearerStatus mm_base_bearer_get_status (MMBaseBearer *self);
+MMBearerProperties *mm_base_bearer_peek_config (MMBaseBearer *self);
+MMBearerProperties *mm_base_bearer_get_config (MMBaseBearer *self);
+gint mm_base_bearer_get_profile_id (MMBaseBearer *self);
void mm_base_bearer_connect (MMBaseBearer *self,
GAsyncReadyCallback callback,
@@ -133,7 +214,27 @@ gboolean mm_base_bearer_disconnect_finish (MMBaseBearer *self,
void mm_base_bearer_disconnect_force (MMBaseBearer *self);
-void mm_base_bearer_report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status);
+void mm_base_bearer_report_connection_status_detailed (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error);
+
+/* When unknown, just pass NULL */
+#define mm_base_bearer_report_connection_status(self, status) mm_base_bearer_report_connection_status_detailed (self, status, NULL)
+
+void mm_base_bearer_report_speeds (MMBaseBearer *self,
+ guint64 uplink_speed,
+ guint64 downlink_speed);
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+/* Sync Broadband Bearer (async) */
+void mm_base_bearer_sync (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_base_bearer_sync_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
#endif /* MM_BASE_BEARER_H */
diff --git a/src/mm-base-call.c b/src/mm-base-call.c
new file mode 100644
index 00000000..149cfbd4
--- /dev/null
+++ b/src/mm-base-call.c
@@ -0,0 +1,1581 @@
+/* -*- 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) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ * Copyright (C) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-base-call.h"
+#include "mm-broadband-modem.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-base-modem-at.h"
+#include "mm-base-modem.h"
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
+#include "mm-error-helpers.h"
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBaseCall, mm_base_call, MM_GDBUS_TYPE_CALL_SKELETON, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ PROP_CONNECTION,
+ PROP_MODEM,
+ PROP_SKIP_INCOMING_TIMEOUT,
+ PROP_SUPPORTS_DIALING_TO_RINGING,
+ PROP_SUPPORTS_RINGING_TO_ACTIVE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMBaseCallPrivate {
+ /* The connection to the system bus */
+ GDBusConnection *connection;
+ guint dbus_id;
+
+ /* The modem which owns this call */
+ MMBaseModem *modem;
+ /* The path where the call object is exported */
+ gchar *path;
+ /* Features */
+ gboolean skip_incoming_timeout;
+ gboolean supports_dialing_to_ringing;
+ gboolean supports_ringing_to_active;
+
+ guint incoming_timeout;
+
+ /* The port used for audio while call is ongoing, if known */
+ MMPort *audio_port;
+
+ /* Ongoing call index */
+ guint index;
+
+ /* Start cancellable, used when the call state transition to
+ * 'terminated' is coming asynchronously (e.g. via in-call state
+ * update notifications) */
+ GCancellable *start_cancellable;
+};
+
+/*****************************************************************************/
+/* Incoming calls are reported via RING URCs. If the caller stops the call
+ * attempt before it has been answered, the only thing we would see is that the
+ * URCs are no longer received. So, we will start a timeout whenever a new RING
+ * URC is received, and we refresh the timeout any time a new URC arrives. If
+ * the timeout is expired (meaning no URCs were received in the last N seconds)
+ * then we assume the call attempt is finished and we transition to TERMINATED.
+ */
+
+#define INCOMING_TIMEOUT_SECS 10
+
+static gboolean
+incoming_timeout_cb (MMBaseCall *self)
+{
+ self->priv->incoming_timeout = 0;
+ mm_obj_info (self, "incoming call timed out: no response");
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED);
+ return G_SOURCE_REMOVE;
+}
+
+void
+mm_base_call_incoming_refresh (MMBaseCall *self)
+{
+ if (self->priv->skip_incoming_timeout)
+ return;
+
+ if (self->priv->incoming_timeout)
+ g_source_remove (self->priv->incoming_timeout);
+ self->priv->incoming_timeout = g_timeout_add_seconds (INCOMING_TIMEOUT_SECS, (GSourceFunc)incoming_timeout_cb, self);
+}
+
+/*****************************************************************************/
+/* Update audio settings */
+
+void
+mm_base_call_change_audio_settings (MMBaseCall *self,
+ MMPort *audio_port,
+ MMCallAudioFormat *audio_format)
+{
+ if (!audio_port && self->priv->audio_port && mm_port_get_connected (self->priv->audio_port))
+ mm_port_set_connected (self->priv->audio_port, FALSE);
+ g_clear_object (&self->priv->audio_port);
+
+ if (audio_port) {
+ self->priv->audio_port = g_object_ref (audio_port);
+ mm_port_set_connected (self->priv->audio_port, TRUE);
+ }
+
+ mm_gdbus_call_set_audio_port (MM_GDBUS_CALL (self), audio_port ? mm_port_get_device (audio_port) : NULL);
+ mm_gdbus_call_set_audio_format (MM_GDBUS_CALL (self), mm_call_audio_format_get_dictionary (audio_format));
+}
+
+/*****************************************************************************/
+/* Start call (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+} HandleStartContext;
+
+static void
+handle_start_context_free (HandleStartContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static void
+handle_start_ready (MMBaseCall *self,
+ GAsyncResult *res,
+ HandleStartContext *ctx)
+{
+ GError *error = NULL;
+
+ g_clear_object (&ctx->self->priv->start_cancellable);
+
+ if (!MM_BASE_CALL_GET_CLASS (self)->start_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't start call: %s", error->message);
+
+ /* When cancelled via the start cancellable, it's because we got an early in-call error
+ * before the call attempt was reported as started. */
+ if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+ g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_CANCELLED)) {
+ g_clear_error (&error);
+ error = mm_connection_error_for_code (MM_CONNECTION_ERROR_NO_DIALTONE, self);
+ }
+
+ /* Convert errors into call state updates */
+ if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_DIALTONE))
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR);
+ else if (g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_BUSY) ||
+ g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_ANSWER) ||
+ g_error_matches (error, MM_CONNECTION_ERROR, MM_CONNECTION_ERROR_NO_CARRIER))
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_REFUSED_OR_BUSY);
+ else
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN);
+
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_start_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (self, "call is started");
+
+ /* If dialing to ringing supported, leave it dialing */
+ if (!ctx->self->priv->supports_dialing_to_ringing) {
+ /* If ringing to active supported, set it ringing */
+ if (ctx->self->priv->supports_ringing_to_active)
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_RINGING_OUT, MM_CALL_STATE_REASON_OUTGOING_STARTED);
+ else
+ /* Otherwise, active right away */
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_OUTGOING_STARTED);
+ }
+ mm_gdbus_call_complete_start (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ handle_start_context_free (ctx);
+}
+
+static void
+handle_start_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleStartContext *ctx)
+{
+ MMCallState state;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_start_context_free (ctx);
+ return;
+ }
+
+ /* We can only start call created by the user */
+ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+
+ if (state != MM_CALL_STATE_UNKNOWN) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "This call was not in unknown state, cannot start it");
+ handle_start_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (ctx->self, "user request to start call");
+
+ /* Disallow non-emergency calls when in emergency-only state */
+ if (!mm_iface_modem_voice_authorize_outgoing_call (MM_IFACE_MODEM_VOICE (modem), ctx->self, &error)) {
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_start_context_free (ctx);
+ return;
+ }
+
+ /* Check if we do support doing it */
+ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->start ||
+ !MM_BASE_CALL_GET_CLASS (ctx->self)->start_finish) {
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN);
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Starting call is not supported by this modem");
+ handle_start_context_free (ctx);
+ return;
+ }
+
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_DIALING, MM_CALL_STATE_REASON_OUTGOING_STARTED);
+
+ /* Setup start cancellable to get notified of termination asynchronously */
+ g_assert (!ctx->self->priv->start_cancellable);
+ ctx->self->priv->start_cancellable = g_cancellable_new ();
+
+ MM_BASE_CALL_GET_CLASS (ctx->self)->start (ctx->self,
+ ctx->self->priv->start_cancellable,
+ (GAsyncReadyCallback)handle_start_ready,
+ ctx);
+}
+
+static gboolean
+handle_start (MMBaseCall *self,
+ GDBusMethodInvocation *invocation)
+{
+ HandleStartContext *ctx;
+
+ ctx = g_new0 (HandleStartContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_start_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Accept call (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+} HandleAcceptContext;
+
+static void
+handle_accept_context_free (HandleAcceptContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static void
+handle_accept_ready (MMBaseCall *self,
+ GAsyncResult *res,
+ HandleAcceptContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_BASE_CALL_GET_CLASS (self)->accept_finish (self, res, &error)) {
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_accept_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (self, "call is accepted");
+
+ if (ctx->self->priv->incoming_timeout) {
+ g_source_remove (ctx->self->priv->incoming_timeout);
+ ctx->self->priv->incoming_timeout = 0;
+ }
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED);
+ mm_gdbus_call_complete_accept (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ handle_accept_context_free (ctx);
+}
+
+static void
+handle_accept_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleAcceptContext *ctx)
+{
+ MMCallState state;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_accept_context_free (ctx);
+ return;
+ }
+
+ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+
+ /* We can only accept incoming call in ringing state */
+ if (state != MM_CALL_STATE_RINGING_IN) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "This call was not ringing, cannot accept");
+ handle_accept_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (ctx->self, "user request to accept call");
+
+ /* Check if we do support doing it */
+ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->accept ||
+ !MM_BASE_CALL_GET_CLASS (ctx->self)->accept_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Accepting call is not supported by this modem");
+ handle_accept_context_free (ctx);
+ return;
+ }
+
+ MM_BASE_CALL_GET_CLASS (ctx->self)->accept (ctx->self,
+ (GAsyncReadyCallback)handle_accept_ready,
+ ctx);
+}
+
+static gboolean
+handle_accept (MMBaseCall *self,
+ GDBusMethodInvocation *invocation)
+{
+ HandleAcceptContext *ctx;
+
+ ctx = g_new0 (HandleAcceptContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_accept_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Deflect call (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+ gchar *number;
+} HandleDeflectContext;
+
+static void
+handle_deflect_context_free (HandleDeflectContext *ctx)
+{
+ g_free (ctx->number);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleDeflectContext, ctx);
+}
+
+static void
+handle_deflect_ready (MMBaseCall *self,
+ GAsyncResult *res,
+ HandleDeflectContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_BASE_CALL_GET_CLASS (self)->deflect_finish (self, res, &error)) {
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (self, "call is deflected to '%s'", ctx->number);
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_DEFLECTED);
+ mm_gdbus_call_complete_deflect (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ handle_deflect_context_free (ctx);
+}
+
+static void
+handle_deflect_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleDeflectContext *ctx)
+{
+ MMCallState state;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+
+ /* We can only deflect incoming call in ringing or waiting state */
+ if (state != MM_CALL_STATE_RINGING_IN && state != MM_CALL_STATE_WAITING) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "This call was not ringing/waiting, cannot deflect");
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (ctx->self, "user request to deflect call");
+
+ /* Check if we do support doing it */
+ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->deflect ||
+ !MM_BASE_CALL_GET_CLASS (ctx->self)->deflect_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Deflecting call is not supported by this modem");
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ MM_BASE_CALL_GET_CLASS (ctx->self)->deflect (ctx->self,
+ ctx->number,
+ (GAsyncReadyCallback)handle_deflect_ready,
+ ctx);
+}
+
+static gboolean
+handle_deflect (MMBaseCall *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *number)
+{
+ HandleDeflectContext *ctx;
+
+ ctx = g_slice_new0 (HandleDeflectContext);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->number = g_strdup (number);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_deflect_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Join multiparty call (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+} HandleJoinMultipartyContext;
+
+static void
+handle_join_multiparty_context_free (HandleJoinMultipartyContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static void
+modem_voice_join_multiparty_ready (MMIfaceModemVoice *modem,
+ GAsyncResult *res,
+ HandleJoinMultipartyContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_iface_modem_voice_join_multiparty_finish (modem, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_call_complete_join_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ handle_join_multiparty_context_free (ctx);
+}
+
+static void
+handle_join_multiparty_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleJoinMultipartyContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_join_multiparty_context_free (ctx);
+ return;
+ }
+
+ /* This action is provided in the Call API, but implemented in the Modem.Voice interface
+ * logic, because the action affects not only one call object, but all call objects that
+ * are part of the multiparty call. */
+ mm_iface_modem_voice_join_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem),
+ ctx->self,
+ (GAsyncReadyCallback)modem_voice_join_multiparty_ready,
+ ctx);
+}
+
+static gboolean
+handle_join_multiparty (MMBaseCall *self,
+ GDBusMethodInvocation *invocation)
+{
+ HandleJoinMultipartyContext *ctx;
+
+ ctx = g_new0 (HandleJoinMultipartyContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_join_multiparty_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Leave multiparty call (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+} HandleLeaveMultipartyContext;
+
+static void
+handle_leave_multiparty_context_free (HandleLeaveMultipartyContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static void
+modem_voice_leave_multiparty_ready (MMIfaceModemVoice *modem,
+ GAsyncResult *res,
+ HandleLeaveMultipartyContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_iface_modem_voice_leave_multiparty_finish (modem, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_call_complete_leave_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+
+ handle_leave_multiparty_context_free (ctx);
+}
+
+static void
+handle_leave_multiparty_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleLeaveMultipartyContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_leave_multiparty_context_free (ctx);
+ return;
+ }
+
+ /* This action is provided in the Call API, but implemented in the Modem.Voice interface
+ * logic, because the action affects not only one call object, but all call objects that
+ * are part of the multiparty call. */
+ mm_iface_modem_voice_leave_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem),
+ ctx->self,
+ (GAsyncReadyCallback)modem_voice_leave_multiparty_ready,
+ ctx);
+}
+
+static gboolean
+handle_leave_multiparty (MMBaseCall *self,
+ GDBusMethodInvocation *invocation)
+{
+ HandleLeaveMultipartyContext *ctx;
+
+ ctx = g_new0 (HandleLeaveMultipartyContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_leave_multiparty_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Hangup call (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+} HandleHangupContext;
+
+static void
+handle_hangup_context_free (HandleHangupContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx);
+}
+
+static void
+handle_hangup_ready (MMBaseCall *self,
+ GAsyncResult *res,
+ HandleHangupContext *ctx)
+{
+ GError *error = NULL;
+
+ /* we set it as terminated even if we got an error reported */
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED);
+
+ if (!MM_BASE_CALL_GET_CLASS (self)->hangup_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* note: timeouts are already removed when setting state as TERMINATED */
+ mm_gdbus_call_complete_hangup (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ }
+
+ handle_hangup_context_free (ctx);
+}
+
+static void
+handle_hangup_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleHangupContext *ctx)
+{
+ MMCallState state;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hangup_context_free (ctx);
+ return;
+ }
+
+ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+
+ /* We can only hangup call in a valid state */
+ if (state == MM_CALL_STATE_TERMINATED || state == MM_CALL_STATE_UNKNOWN) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "This call was not active, cannot hangup");
+ handle_hangup_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (ctx->self, "user request to hangup call");
+
+ /* Check if we do support doing it */
+ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->hangup ||
+ !MM_BASE_CALL_GET_CLASS (ctx->self)->hangup_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Hanging up call is not supported by this modem");
+ handle_hangup_context_free (ctx);
+ return;
+ }
+
+ MM_BASE_CALL_GET_CLASS (ctx->self)->hangup (ctx->self,
+ (GAsyncReadyCallback)handle_hangup_ready,
+ ctx);
+}
+
+static gboolean
+handle_hangup (MMBaseCall *self,
+ GDBusMethodInvocation *invocation)
+{
+ HandleHangupContext *ctx;
+
+ ctx = g_new0 (HandleHangupContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_hangup_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Send dtmf (DBus call handling) */
+
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+ gchar *dtmf;
+} HandleSendDtmfContext;
+
+static void
+handle_send_dtmf_context_free (HandleSendDtmfContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_free (ctx->dtmf);
+ g_free (ctx);
+}
+
+static void
+handle_send_dtmf_ready (MMBaseCall *self,
+ GAsyncResult *res,
+ HandleSendDtmfContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_BASE_CALL_GET_CLASS (self)->send_dtmf_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ } else {
+ mm_gdbus_call_complete_send_dtmf (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ }
+
+ handle_send_dtmf_context_free (ctx);
+}
+
+static void
+handle_send_dtmf_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleSendDtmfContext *ctx)
+{
+ MMCallState state;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_send_dtmf_context_free (ctx);
+ return;
+ }
+
+ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+
+ /* Check if we do support doing it */
+ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf ||
+ !MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Sending dtmf is not supported by this modem");
+ handle_send_dtmf_context_free (ctx);
+ return;
+ }
+
+ /* We can only send_dtmf when call is in ACTIVE state */
+ if (state != MM_CALL_STATE_ACTIVE ){
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "This call was not active, cannot send dtmf");
+ handle_send_dtmf_context_free (ctx);
+ return;
+ }
+
+ MM_BASE_CALL_GET_CLASS (ctx->self)->send_dtmf (ctx->self, ctx->dtmf,
+ (GAsyncReadyCallback)handle_send_dtmf_ready,
+ ctx);
+}
+
+static gboolean
+handle_send_dtmf (MMBaseCall *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *dtmf)
+{
+ HandleSendDtmfContext *ctx;
+
+ ctx = g_new0 (HandleSendDtmfContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+
+ ctx->dtmf = g_strdup (dtmf);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_send_dtmf_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+void
+mm_base_call_export (MMBaseCall *self)
+{
+ gchar *path;
+
+ path = g_strdup_printf (MM_DBUS_CALL_PREFIX "/%d", self->priv->dbus_id);
+ g_object_set (self,
+ MM_BASE_CALL_PATH, path,
+ NULL);
+ g_free (path);
+}
+
+void
+mm_base_call_unexport (MMBaseCall *self)
+{
+ g_object_set (self,
+ MM_BASE_CALL_PATH, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+call_dbus_export (MMBaseCall *self)
+{
+ GError *error = NULL;
+
+ /* Handle method invocations */
+ g_object_connect (self,
+ "signal::handle-start", G_CALLBACK (handle_start), NULL,
+ "signal::handle-accept", G_CALLBACK (handle_accept), NULL,
+ "signal::handle-deflect", G_CALLBACK (handle_deflect), NULL,
+ "signal::handle-join-multiparty", G_CALLBACK (handle_join_multiparty), NULL,
+ "signal::handle-leave-multiparty", G_CALLBACK (handle_leave_multiparty), NULL,
+ "signal::handle-hangup", G_CALLBACK (handle_hangup), NULL,
+ "signal::handle-send-dtmf", G_CALLBACK (handle_send_dtmf), NULL,
+ NULL);
+
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
+ self->priv->connection,
+ self->priv->path,
+ &error)) {
+ mm_obj_warn (self, "couldn't export call: %s", error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+call_dbus_unexport (MMBaseCall *self)
+{
+ /* Only unexport if currently exported */
+ if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self)))
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
+}
+
+/*****************************************************************************/
+
+const gchar *
+mm_base_call_get_path (MMBaseCall *self)
+{
+ return self->priv->path;
+}
+
+const gchar *
+mm_base_call_get_number (MMBaseCall *self)
+{
+ return mm_gdbus_call_get_number (MM_GDBUS_CALL (self));
+}
+
+void
+mm_base_call_set_number (MMBaseCall *self,
+ const gchar *number)
+{
+ return mm_gdbus_call_set_number (MM_GDBUS_CALL (self), number);
+}
+
+MMCallDirection
+mm_base_call_get_direction (MMBaseCall *self)
+{
+ return (MMCallDirection) mm_gdbus_call_get_direction (MM_GDBUS_CALL (self));
+}
+
+MMCallState
+mm_base_call_get_state (MMBaseCall *self)
+{
+ return (MMCallState) mm_gdbus_call_get_state (MM_GDBUS_CALL (self));
+}
+
+gboolean
+mm_base_call_get_multiparty (MMBaseCall *self)
+{
+ return mm_gdbus_call_get_multiparty (MM_GDBUS_CALL (self));
+}
+
+void
+mm_base_call_set_multiparty (MMBaseCall *self,
+ gboolean multiparty)
+{
+ return mm_gdbus_call_set_multiparty (MM_GDBUS_CALL (self), multiparty);
+}
+
+/*****************************************************************************/
+/* Current call index, only applicable while the call is ongoing
+ * See 3GPP TS 22.030 [27], subclause 6.5.5.1.
+ */
+
+guint
+mm_base_call_get_index (MMBaseCall *self)
+{
+ return self->priv->index;
+}
+
+void
+mm_base_call_set_index (MMBaseCall *self,
+ guint index)
+{
+ self->priv->index = index;
+}
+
+/*****************************************************************************/
+
+void
+mm_base_call_change_state (MMBaseCall *self,
+ MMCallState new_state,
+ MMCallStateReason reason)
+{
+ MMCallState old_state;
+
+ old_state = mm_gdbus_call_get_state (MM_GDBUS_CALL (self));
+
+ if (old_state == new_state)
+ return;
+
+ mm_obj_info (self, "call state changed: %s -> %s (%s)",
+ mm_call_state_get_string (old_state),
+ mm_call_state_get_string (new_state),
+ mm_call_state_reason_get_string (reason));
+
+ /* Setup/cleanup unsolicited events based on state transitions to/from ACTIVE */
+ if (new_state == MM_CALL_STATE_TERMINATED) {
+ /* reset index */
+ self->priv->index = 0;
+ /* cleanup incoming timeout, if any */
+ if (self->priv->incoming_timeout) {
+ g_source_remove (self->priv->incoming_timeout);
+ self->priv->incoming_timeout = 0;
+ }
+ /* cancel start if ongoing */
+ g_cancellable_cancel (self->priv->start_cancellable);
+ }
+
+ mm_gdbus_call_set_state (MM_GDBUS_CALL (self), new_state);
+ mm_gdbus_call_set_state_reason (MM_GDBUS_CALL (self), reason);
+ mm_gdbus_call_emit_state_changed (MM_GDBUS_CALL (self), old_state, new_state, reason);
+}
+
+/*****************************************************************************/
+
+void
+mm_base_call_received_dtmf (MMBaseCall *self,
+ const gchar *dtmf)
+{
+ mm_gdbus_call_emit_dtmf_received (MM_GDBUS_CALL (self), dtmf);
+}
+
+/*****************************************************************************/
+/* Start the CALL */
+
+static gboolean
+call_start_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+call_start_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response = NULL;
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+
+ /* check response for error */
+ if (response && response[0])
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't start the call: Unhandled response '%s'", response);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+call_start (MMBaseCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GTask *task;
+ gchar *cmd;
+ MMPortSerialAt *port;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self->priv->modem), &error);
+ if (!port) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ cmd = g_strdup_printf ("ATD%s;", mm_gdbus_call_get_number (MM_GDBUS_CALL (self)));
+ mm_base_modem_at_command_full (self->priv->modem,
+ port,
+ cmd,
+ 90,
+ FALSE, /* no cached */
+ FALSE, /* no raw */
+ cancellable,
+ (GAsyncReadyCallback)call_start_ready,
+ task);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+/* Accept the call */
+
+static gboolean
+call_accept_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+call_accept_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+
+ /* check response for error */
+ if (response && response[0])
+ g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't accept the call: Unhandled response '%s'", response);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+call_accept (MMBaseCall *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (self->priv->modem,
+ "ATA",
+ 2,
+ FALSE,
+ (GAsyncReadyCallback)call_accept_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Deflect the call */
+
+static gboolean
+call_deflect_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+call_deflect_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+call_deflect (MMBaseCall *self,
+ const gchar *number,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *cmd;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ cmd = g_strdup_printf ("+CTFR=%s", number);
+ mm_base_modem_at_command (self->priv->modem,
+ cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)call_deflect_ready,
+ task);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+/* Hangup the call */
+
+static gboolean
+call_hangup_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+chup_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+chup_fallback (GTask *task)
+{
+ MMBaseCall *self;
+
+ self = g_task_get_source_object (task);
+ mm_base_modem_at_command (self->priv->modem,
+ "+CHUP",
+ 2,
+ FALSE,
+ (GAsyncReadyCallback)chup_ready,
+ task);
+}
+
+static void
+chld_hangup_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseCall *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't hangup single call with call id '%u': %s",
+ self->priv->index, error->message);
+ g_error_free (error);
+ chup_fallback (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+call_hangup (MMBaseCall *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Try to hangup the single call id */
+ if (self->priv->index) {
+ gchar *cmd;
+
+ cmd = g_strdup_printf ("+CHLD=1%u", self->priv->index);
+ mm_base_modem_at_command (self->priv->modem,
+ cmd,
+ 2,
+ FALSE,
+ (GAsyncReadyCallback)chld_hangup_ready,
+ task);
+ g_free (cmd);
+ return;
+ }
+
+ /* otherwise terminate all */
+ chup_fallback (task);
+}
+
+/*****************************************************************************/
+/* Send DTMF tone to call */
+
+static gboolean
+call_send_dtmf_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+call_send_dtmf_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseCall *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_dbg (self, "couldn't send dtmf: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+call_send_dtmf (MMBaseCall *self,
+ const gchar *dtmf,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *cmd;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ cmd = g_strdup_printf ("AT+VTS=%c", dtmf[0]);
+ mm_base_modem_at_command (self->priv->modem,
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)call_send_dtmf_ready,
+ task);
+
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMBaseCall *self;
+
+ self = MM_BASE_CALL (_self);
+ return g_strdup_printf ("call%u", self->priv->dbus_id);
+}
+
+/*****************************************************************************/
+
+MMBaseCall *
+mm_base_call_new (MMBaseModem *modem,
+ MMCallDirection direction,
+ const gchar *number,
+ gboolean skip_incoming_timeout,
+ gboolean supports_dialing_to_ringing,
+ gboolean supports_ringing_to_active)
+{
+ return MM_BASE_CALL (g_object_new (MM_TYPE_BASE_CALL,
+ MM_BASE_CALL_MODEM, modem,
+ "direction", direction,
+ "number", number,
+ MM_BASE_CALL_SKIP_INCOMING_TIMEOUT, skip_incoming_timeout,
+ MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING, supports_dialing_to_ringing,
+ MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE, supports_ringing_to_active,
+ NULL));
+}
+
+/*****************************************************************************/
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMBaseCall *self = MM_BASE_CALL (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_free (self->priv->path);
+ self->priv->path = g_value_dup_string (value);
+
+ /* Export when we get a DBus connection AND we have a path */
+ if (!self->priv->path)
+ call_dbus_unexport (self);
+ else if (self->priv->connection)
+ call_dbus_export (self);
+ break;
+ case PROP_CONNECTION:
+ g_clear_object (&self->priv->connection);
+ self->priv->connection = g_value_dup_object (value);
+
+ /* Export when we get a DBus connection AND we have a path */
+ if (!self->priv->connection)
+ call_dbus_unexport (self);
+ else if (self->priv->path)
+ call_dbus_export (self);
+ break;
+ case PROP_MODEM:
+ g_clear_object (&self->priv->modem);
+ self->priv->modem = g_value_dup_object (value);
+ if (self->priv->modem) {
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
+ /* Bind the modem's connection (which is set when it is exported,
+ * and unset when unexported) to the call's connection */
+ g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
+ self, MM_BASE_CALL_CONNECTION,
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ }
+ break;
+ case PROP_SKIP_INCOMING_TIMEOUT:
+ self->priv->skip_incoming_timeout = g_value_get_boolean (value);
+ break;
+ case PROP_SUPPORTS_DIALING_TO_RINGING:
+ self->priv->supports_dialing_to_ringing = g_value_get_boolean (value);
+ break;
+ case PROP_SUPPORTS_RINGING_TO_ACTIVE:
+ self->priv->supports_ringing_to_active = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMBaseCall *self = MM_BASE_CALL (object);
+
+ switch (prop_id) {
+ case PROP_PATH:
+ g_value_set_string (value, self->priv->path);
+ break;
+ case PROP_CONNECTION:
+ g_value_set_object (value, self->priv->connection);
+ break;
+ case PROP_MODEM:
+ g_value_set_object (value, self->priv->modem);
+ break;
+ case PROP_SKIP_INCOMING_TIMEOUT:
+ g_value_set_boolean (value, self->priv->skip_incoming_timeout);
+ break;
+ case PROP_SUPPORTS_DIALING_TO_RINGING:
+ g_value_set_boolean (value, self->priv->supports_dialing_to_ringing);
+ break;
+ case PROP_SUPPORTS_RINGING_TO_ACTIVE:
+ g_value_set_boolean (value, self->priv->supports_ringing_to_active);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mm_base_call_init (MMBaseCall *self)
+{
+ static guint id = 0;
+
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_CALL, MMBaseCallPrivate);
+
+ /* Each call is given a unique id to build its own DBus path */
+ self->priv->dbus_id = id++;
+}
+
+static void
+finalize (GObject *object)
+{
+ MMBaseCall *self = MM_BASE_CALL (object);
+
+ g_assert (!self->priv->start_cancellable);
+ g_free (self->priv->path);
+
+ G_OBJECT_CLASS (mm_base_call_parent_class)->finalize (object);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMBaseCall *self = MM_BASE_CALL (object);
+
+ g_clear_object (&self->priv->audio_port);
+
+ if (self->priv->incoming_timeout) {
+ g_source_remove (self->priv->incoming_timeout);
+ self->priv->incoming_timeout = 0;
+ }
+
+ if (self->priv->connection) {
+ /* If we arrived here with a valid connection, make sure we unexport
+ * the object */
+ call_dbus_unexport (self);
+ g_clear_object (&self->priv->connection);
+ }
+
+ g_clear_object (&self->priv->modem);
+
+ G_OBJECT_CLASS (mm_base_call_parent_class)->dispose (object);
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
+mm_base_call_class_init (MMBaseCallClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBaseCallPrivate));
+
+ /* Virtual methods */
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->finalize = finalize;
+ object_class->dispose = dispose;
+
+ klass->start = call_start;
+ klass->start_finish = call_start_finish;
+ klass->accept = call_accept;
+ klass->accept_finish = call_accept_finish;
+ klass->deflect = call_deflect;
+ klass->deflect_finish = call_deflect_finish;
+ klass->hangup = call_hangup;
+ klass->hangup_finish = call_hangup_finish;
+ klass->send_dtmf = call_send_dtmf;
+ klass->send_dtmf_finish = call_send_dtmf_finish;
+
+ properties[PROP_CONNECTION] =
+ g_param_spec_object (MM_BASE_CALL_CONNECTION,
+ "Connection",
+ "GDBus connection to the system bus.",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]);
+
+ properties[PROP_PATH] =
+ g_param_spec_string (MM_BASE_CALL_PATH,
+ "Path",
+ "DBus path of the call",
+ NULL,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]);
+
+ properties[PROP_MODEM] =
+ g_param_spec_object (MM_BASE_CALL_MODEM,
+ "Modem",
+ "The Modem which owns this call",
+ MM_TYPE_BASE_MODEM,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
+
+ properties[PROP_SKIP_INCOMING_TIMEOUT] =
+ g_param_spec_boolean (MM_BASE_CALL_SKIP_INCOMING_TIMEOUT,
+ "Skip incoming timeout",
+ "There is no need to setup a timeout for incoming calls",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_SKIP_INCOMING_TIMEOUT, properties[PROP_SKIP_INCOMING_TIMEOUT]);
+
+ properties[PROP_SUPPORTS_DIALING_TO_RINGING] =
+ g_param_spec_boolean (MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING,
+ "Dialing to ringing",
+ "Whether the call implementation reports dialing to ringing state updates",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_SUPPORTS_DIALING_TO_RINGING, properties[PROP_SUPPORTS_DIALING_TO_RINGING]);
+
+ properties[PROP_SUPPORTS_RINGING_TO_ACTIVE] =
+ g_param_spec_boolean (MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE,
+ "Ringing to active",
+ "Whether the call implementation reports ringing to active state updates",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_SUPPORTS_RINGING_TO_ACTIVE, properties[PROP_SUPPORTS_RINGING_TO_ACTIVE]);
+}
diff --git a/src/mm-base-call.h b/src/mm-base-call.h
new file mode 100644
index 00000000..1b262027
--- /dev/null
+++ b/src/mm-base-call.h
@@ -0,0 +1,140 @@
+/* -*- 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) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#ifndef MM_BASE_CALL_H
+#define MM_BASE_CALL_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-base-modem.h"
+#include "mm-call-audio-format.h"
+
+#define MM_TYPE_BASE_CALL (mm_base_call_get_type ())
+#define MM_BASE_CALL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_CALL, MMBaseCall))
+#define MM_BASE_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_CALL, MMBaseCallClass))
+#define MM_IS_BASE_CALL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_CALL))
+#define MM_IS_BASE_CALL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_CALL))
+#define MM_BASE_CALL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_CALL, MMBaseCallClass))
+
+typedef struct _MMBaseCall MMBaseCall;
+typedef struct _MMBaseCallClass MMBaseCallClass;
+typedef struct _MMBaseCallPrivate MMBaseCallPrivate;
+
+#define MM_BASE_CALL_PATH "call-path"
+#define MM_BASE_CALL_CONNECTION "call-connection"
+#define MM_BASE_CALL_MODEM "call-modem"
+#define MM_BASE_CALL_SKIP_INCOMING_TIMEOUT "call-skip-incoming-timeout"
+#define MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING "call-supports-dialing-to-ringing"
+#define MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE "call-supports-ringing-to-active"
+
+struct _MMBaseCall {
+ MmGdbusCallSkeleton parent;
+ MMBaseCallPrivate *priv;
+};
+
+struct _MMBaseCallClass {
+ MmGdbusCallSkeletonClass parent;
+
+ /* Start the call */
+ void (* start) (MMBaseCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* start_finish) (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Accept the call */
+ void (* accept) (MMBaseCall *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* accept_finish) (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Deflect the call */
+ void (* deflect) (MMBaseCall *self,
+ const gchar *number,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* deflect_finish) (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Hangup the call */
+ void (* hangup) (MMBaseCall *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* hangup_finish) (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Send a DTMF tone */
+ void (* send_dtmf) (MMBaseCall *self,
+ const gchar *dtmf,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* send_dtmf_finish) (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error);
+};
+
+GType mm_base_call_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseCall, g_object_unref)
+
+/* This one can be overriden by plugins */
+MMBaseCall *mm_base_call_new (MMBaseModem *modem,
+ MMCallDirection direction,
+ const gchar *number,
+ gboolean skip_incoming_timeout,
+ gboolean supports_dialing_to_ringing,
+ gboolean supports_ringing_to_active);
+
+void mm_base_call_export (MMBaseCall *self);
+void mm_base_call_unexport (MMBaseCall *self);
+
+const gchar *mm_base_call_get_path (MMBaseCall *self);
+const gchar *mm_base_call_get_number (MMBaseCall *self);
+MMCallDirection mm_base_call_get_direction (MMBaseCall *self);
+MMCallState mm_base_call_get_state (MMBaseCall *self);
+guint mm_base_call_get_index (MMBaseCall *self);
+gboolean mm_base_call_get_multiparty (MMBaseCall *self);
+
+void mm_base_call_set_number (MMBaseCall *self,
+ const gchar *number);
+void mm_base_call_set_index (MMBaseCall *self,
+ guint index);
+void mm_base_call_set_multiparty (MMBaseCall *self,
+ gboolean multiparty);
+
+void mm_base_call_change_state (MMBaseCall *self,
+ MMCallState new_state,
+ MMCallStateReason reason);
+
+void mm_base_call_change_audio_settings (MMBaseCall *self,
+ MMPort *audio_port,
+ MMCallAudioFormat *audio_format);
+
+void mm_base_call_received_dtmf (MMBaseCall *self,
+ const gchar *dtmf);
+
+void mm_base_call_incoming_refresh (MMBaseCall *self);
+
+#endif /* MM_BASE_CALL_H */
diff --git a/src/mm-base-manager.c b/src/mm-base-manager.c
index 3900af5b..46f1ad43 100644
--- a/src/mm-base-manager.c
+++ b/src/mm-base-manager.c
@@ -12,40 +12,63 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Aleksander Morgado <aleksander@gnu.org>
- * Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2011 - 2012 Google, Inc
+ * Copyright (C) 2016 Velocloud, Inc.
+ * Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
+#include <config.h>
+
#include <string.h>
#include <ctype.h>
#include <gmodule.h>
-#include <gudev/gudev.h>
+
+#if defined WITH_QMI
+# include <libqmi-glib.h>
+#endif
+#if defined WITH_QRTR
+# include "mm-kernel-device-qrtr.h"
+# include "mm-qrtr-bus-watcher.h"
+#endif
+#if defined WITH_UDEV
+# include "mm-kernel-device-udev.h"
+#endif
+#include "mm-kernel-device-generic.h"
#include <ModemManager.h>
+#include <ModemManager-tags.h>
+
#include <mm-errors-types.h>
#include <mm-gdbus-manager.h>
#include <mm-gdbus-test.h>
+#include "mm-context.h"
#include "mm-base-manager.h"
+#include "mm-daemon-enums-types.h"
#include "mm-device.h"
#include "mm-plugin-manager.h"
-#include "mm-auth.h"
+#include "mm-auth-provider.h"
#include "mm-plugin.h"
-#include "mm-log.h"
+#include "mm-filter.h"
+#include "mm-log-object.h"
+#include "mm-base-modem.h"
-static void initable_iface_init (GInitableIface *iface);
+static void initable_iface_init (GInitableIface *iface);
+static void log_object_iface_init (MMLogObjectInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMBaseManager, mm_base_manager, MM_GDBUS_TYPE_ORG_FREEDESKTOP_MODEM_MANAGER1_SKELETON, 0,
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
- initable_iface_init));
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
PROP_CONNECTION,
PROP_AUTO_SCAN,
+ PROP_FILTER_POLICY,
PROP_ENABLE_TEST,
PROP_PLUGIN_DIR,
+ PROP_INITIAL_KERNEL_EVENTS,
LAST_PROP
};
@@ -54,24 +77,39 @@ struct _MMBaseManagerPrivate {
GDBusConnection *connection;
/* Whether auto-scanning is enabled */
gboolean auto_scan;
+ /* Filter policy (mask of enabled rules) */
+ MMFilterRule filter_policy;
/* Whether the test interface is enabled */
gboolean enable_test;
/* Path to look for plugins */
gchar *plugin_dir;
- /* The UDev client */
- GUdevClient *udev;
+ /* Path to the list of initial kernel events */
+ gchar *initial_kernel_events;
/* The authorization provider */
MMAuthProvider *authp;
GCancellable *authp_cancellable;
/* The Plugin Manager object */
MMPluginManager *plugin_manager;
+ /* The port/device filter */
+ MMFilter *filter;
/* The container of devices being prepared */
GHashTable *devices;
/* The Object Manager server */
GDBusObjectManagerServer *object_manager;
+ /* The map of inhibited devices */
+ GHashTable *inhibited_devices;
/* The Test interface support */
MmGdbusTest *test_skeleton;
+
+#if defined WITH_UDEV
+ /* The UDev client */
+ GUdevClient *udev;
+#endif
+#if defined WITH_QRTR
+ /* The Qrtr Bus Watcher */
+ MMQrtrBusWatcher *qrtr_bus_watcher;
+#endif
};
/*****************************************************************************/
@@ -94,8 +132,8 @@ find_device_by_modem (MMBaseManager *manager,
}
static MMDevice *
-find_device_by_port (MMBaseManager *manager,
- GUdevDevice *port)
+find_device_by_port (MMBaseManager *manager,
+ MMKernelDevice *port)
{
GHashTableIter iter;
gpointer key, value;
@@ -111,18 +149,28 @@ find_device_by_port (MMBaseManager *manager,
}
static MMDevice *
-find_device_by_sysfs_path (MMBaseManager *self,
- const gchar *sysfs_path)
+find_device_by_port_name (MMBaseManager *manager,
+ const gchar *subsystem,
+ const gchar *name)
{
- return g_hash_table_lookup (self->priv->devices,
- sysfs_path);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_hash_table_iter_init (&iter, manager->priv->devices);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMDevice *candidate = MM_DEVICE (value);
+
+ if (mm_device_owns_port_name (candidate, subsystem, name))
+ return candidate;
+ }
+ return NULL;
}
static MMDevice *
-find_device_by_udev_device (MMBaseManager *manager,
- GUdevDevice *udev_device)
+find_device_by_physdev_uid (MMBaseManager *self,
+ const gchar *physdev_uid)
{
- return find_device_by_sysfs_path (manager, g_udev_device_get_sysfs_path (udev_device));
+ return g_hash_table_lookup (self->priv->devices, physdev_uid);
}
/*****************************************************************************/
@@ -141,123 +189,116 @@ find_device_support_context_free (FindDeviceSupportContext *ctx)
}
static void
-find_device_support_ready (MMPluginManager *plugin_manager,
- GAsyncResult *result,
- FindDeviceSupportContext *ctx)
+device_support_check_ready (MMPluginManager *plugin_manager,
+ GAsyncResult *res,
+ FindDeviceSupportContext *ctx)
{
- GError *error = NULL;
+ GError *error = NULL;
+ MMPlugin *plugin;
+
+ /* If the device support check fails, either with an error, or afterwards
+ * when trying to create a modem object, we must remove the MMDevice from
+ * the tracking table of devices, so that a manual scan request afterwards
+ * re-scans all ports. */
- if (!mm_plugin_manager_find_device_support_finish (plugin_manager, result, &error)) {
- mm_warn ("Couldn't find support for device at '%s': %s",
- mm_device_get_path (ctx->device),
- error->message);
+ /* Receive plugin result from the plugin manager */
+ plugin = mm_plugin_manager_device_support_check_finish (plugin_manager, res, &error);
+ if (!plugin) {
+ mm_obj_info (ctx->self, "couldn't check support for device '%s': %s",
+ mm_device_get_uid (ctx->device), error->message);
g_error_free (error);
- } else if (!mm_device_create_modem (ctx->device, ctx->self->priv->object_manager, &error)) {
- mm_warn ("Couldn't create modem for device at '%s': %s",
- mm_device_get_path (ctx->device),
- error->message);
+ g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (ctx->device));
+ find_device_support_context_free (ctx);
+ return;
+ }
+
+ /* Set the plugin as the one expected in the device */
+ mm_device_set_plugin (ctx->device, G_OBJECT (plugin));
+ g_object_unref (plugin);
+
+ if (!mm_device_create_modem (ctx->device, &error)) {
+ mm_obj_warn (ctx->self, "couldn't create modem for device '%s': %s",
+ mm_device_get_uid (ctx->device), error->message);
g_error_free (error);
- } else {
- mm_info ("Modem for device at '%s' successfully created",
- mm_device_get_path (ctx->device));
+ g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (ctx->device));
+ find_device_support_context_free (ctx);
+ return;
}
+ /* Modem now created */
+ mm_obj_info (ctx->self, "modem for device '%s' successfully created",
+ mm_device_get_uid (ctx->device));
find_device_support_context_free (ctx);
}
-static GUdevDevice *
-find_physical_device (GUdevDevice *child)
+static gboolean is_device_inhibited (MMBaseManager *self,
+ const gchar *physdev_uid);
+static void device_inhibited_track_port (MMBaseManager *self,
+ const gchar *physdev_uid,
+ MMKernelDevice *port,
+ gboolean manual_scan);
+static void device_inhibited_untrack_port (MMBaseManager *self,
+ const gchar *subsystem,
+ const gchar *name);
+
+static void
+device_removed (MMBaseManager *self,
+ const gchar *subsystem,
+ const gchar *name)
{
- GUdevDevice *iter, *old = NULL;
- GUdevDevice *physdev = NULL;
- const char *subsys, *type, *name;
- guint32 i = 0;
- gboolean is_usb = FALSE, is_pci = FALSE, is_pcmcia = FALSE, is_platform = FALSE;
- gboolean is_pnp = FALSE;
-
- g_return_val_if_fail (child != NULL, NULL);
-
- /* Bluetooth rfcomm devices are "virtual" and don't necessarily have
- * parents at all.
- */
- name = g_udev_device_get_name (child);
- if (name && strncmp (name, "rfcomm", 6) == 0)
- return g_object_ref (child);
-
- iter = g_object_ref (child);
- while (iter && i++ < 8) {
- subsys = g_udev_device_get_subsystem (iter);
- if (subsys) {
- if (is_usb || g_str_has_prefix (subsys, "usb")) {
- is_usb = TRUE;
- type = g_udev_device_get_devtype (iter);
- if (type && !strcmp (type, "usb_device")) {
- physdev = iter;
- break;
- }
- } else if (is_pcmcia || !strcmp (subsys, "pcmcia")) {
- GUdevDevice *pcmcia_parent;
- const char *tmp_subsys;
-
- is_pcmcia = TRUE;
-
- /* If the parent of this PCMCIA device is no longer part of
- * the PCMCIA subsystem, we want to stop since we're looking
- * for the base PCMCIA device, not the PCMCIA controller which
- * is usually PCI or some other bus type.
- */
- pcmcia_parent = g_udev_device_get_parent (iter);
- if (pcmcia_parent) {
- tmp_subsys = g_udev_device_get_subsystem (pcmcia_parent);
- if (tmp_subsys && strcmp (tmp_subsys, "pcmcia"))
- physdev = iter;
- g_object_unref (pcmcia_parent);
- if (physdev)
- break;
- }
- } else if (is_platform || !strcmp (subsys, "platform")) {
- /* Take the first platform parent as the physical device */
- is_platform = TRUE;
- physdev = iter;
- break;
- } else if (is_pci || !strcmp (subsys, "pci")) {
- is_pci = TRUE;
- physdev = iter;
- break;
- } else if (is_pnp || !strcmp (subsys, "pnp")) {
- is_pnp = TRUE;
- physdev = iter;
- break;
- }
- }
+ g_autoptr(MMDevice) device = NULL;
- old = iter;
- iter = g_udev_device_get_parent (old);
- g_object_unref (old);
+ g_assert (subsystem);
+ g_assert (name);
+ device = find_device_by_port_name (self, subsystem, name);
+ if (!device) {
+ /* If the device was inhibited and the port is gone, untrack it.
+ * This is only needed for ports that were tracked out of device objects.
+ * In this case we don't rely on the physdev uid, as API-reported
+ * remove kernel events may not include uid. */
+ device_inhibited_untrack_port (self, subsystem, name);
+ return;
}
- return physdev;
+ /* The callbacks triggered when the port is released or device support is
+ * cancelled may end up unreffing the device or removing it from the HT, and
+ * so in order to make sure the reference is still valid when we call
+ * support_check_cancel() and g_hash_table_remove(), we hold a full reference
+ * ourselves. */
+ g_object_ref (device);
+
+ mm_obj_info (self, "port %s released by device '%s'", name, mm_device_get_uid (device));
+ mm_device_release_port_name (device, subsystem, name);
+
+ /* If port probe list gets empty, remove the device object iself */
+ if (!mm_device_peek_port_probe_list (device)) {
+ mm_obj_dbg (self, "removing empty device '%s'", mm_device_get_uid (device));
+ if (mm_plugin_manager_device_support_check_cancel (self->priv->plugin_manager, device))
+ mm_obj_dbg (self, "device support check has been cancelled");
+
+ /* The device may have already been removed from the tracking HT, we
+ * just try to remove it and if it fails, we ignore it */
+ mm_device_remove_modem (device);
+ g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
+ }
}
static void
-device_added (MMBaseManager *manager,
- GUdevDevice *port,
- gboolean hotplugged,
- gboolean manual_scan)
+device_added (MMBaseManager *self,
+ MMKernelDevice *port,
+ gboolean hotplugged,
+ gboolean manual_scan)
{
- MMDevice *device;
- const char *subsys, *name, *physdev_path, *physdev_subsys;
- gboolean is_candidate;
- GUdevDevice *physdev = NULL;
+ MMDevice *device;
+ const gchar *physdev_uid;
+ const gchar *name;
g_return_if_fail (port != NULL);
- subsys = g_udev_device_get_subsystem (port);
- name = g_udev_device_get_name (port);
+ name = mm_kernel_device_get_name (port);
- /* ignore VTs */
- if (strncmp (name, "tty", 3) == 0 && isdigit (name[3]))
- return;
+ mm_obj_dbg (self, "adding port %s at sysfs path: %s",
+ name, mm_kernel_device_get_sysfs_path (port));
/* Ignore devices that aren't completely configured by udev yet. If
* ModemManager is started in parallel with udev, explicitly requesting
@@ -265,173 +306,197 @@ device_added (MMBaseManager *manager,
* applied (a bug in udev/gudev). Since we often need those rules to match
* the device to a specific ModemManager driver, we need to ensure that all
* rules have been processed before handling a device.
- */
- is_candidate = g_udev_device_get_property_as_boolean (port, "ID_MM_CANDIDATE");
- if (!is_candidate)
+ *
+ * This udev tag applies to each port in a device. In other words, the flag
+ * may be set in some ports, but not in others */
+ if (!mm_kernel_device_get_property_as_boolean (port, ID_MM_CANDIDATE)) {
+ /* This could mean that device changed, losing its candidate
+ * flags (such as Bluetooth RFCOMM devices upon disconnect.
+ * Try to forget it. */
+ device_removed (self, mm_kernel_device_get_subsystem (port), name);
+ mm_obj_dbg (self, "port %s not candidate", name);
return;
-
- if (find_device_by_port (manager, port))
- return;
-
- /* Find the port's physical device's sysfs path. This is the kernel device
- * that "owns" all the ports of the device, like the USB device or the PCI
- * device the provides each tty or network port.
- */
- physdev = find_physical_device (port);
- if (!physdev) {
- /* Warn about it, but filter out some common ports that we know don't have
- * anything to do with mobile broadband.
- */
- if ( strcmp (name, "console")
- && strcmp (name, "ptmx")
- && strcmp (name, "lo")
- && strcmp (name, "tty")
- && !strstr (name, "virbr"))
- mm_dbg ("(%s/%s): could not get port's parent device", subsys, name);
-
- goto out;
- }
-
- /* Is the device blacklisted? */
- if (g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_IGNORE")) {
- mm_dbg ("(%s/%s): port's parent device is blacklisted", subsys, name);
- goto out;
}
- /* Is the device in the manual-only greylist? If so, return if this is an
- * automatic scan. */
- if (!manual_scan && g_udev_device_get_property_as_boolean (physdev, "ID_MM_DEVICE_MANUAL_SCAN_ONLY")) {
- mm_dbg ("(%s/%s): port probed only in manual scan", subsys, name);
- goto out;
+ /* Get the port's physical device's uid. All ports of the same physical
+ * device will share the same uid. */
+ physdev_uid = mm_kernel_device_get_physdev_uid (port);
+ g_assert (physdev_uid);
+
+ /* If the device is inhibited, do nothing else */
+ if (is_device_inhibited (self, physdev_uid)) {
+ /* Note: we will not report as hotplugged an inhibited device port
+ * because we don't know what was done with the port out of our
+ * context. */
+ device_inhibited_track_port (self, physdev_uid, port, manual_scan);
+ return;
}
- /* If the physdev is a 'platform' or 'pnp' device that's not whitelisted, ignore it */
- physdev_subsys = g_udev_device_get_subsystem (physdev);
- if ( physdev_subsys
- && ( g_str_equal (physdev_subsys, "platform")
- || g_str_equal (physdev_subsys, "pnp"))
- && !g_udev_device_get_property_as_boolean (physdev, "ID_MM_PLATFORM_DRIVER_PROBE")) {
- mm_dbg ("(%s/%s): port's parent platform driver is not whitelisted", subsys, name);
- goto out;
- }
+ /* Run port filter */
+ if (!mm_filter_port (self->priv->filter, port, manual_scan))
+ return;
- physdev_path = g_udev_device_get_sysfs_path (physdev);
- if (!physdev_path) {
- mm_dbg ("(%s/%s): could not get port's parent device sysfs path", subsys, name);
- goto out;
+ /* If already added, ignore new event */
+ if (find_device_by_port (self, port)) {
+ mm_obj_dbg (self, "port %s already added", name);
+ return;
}
/* See if we already created an object to handle ports in this device */
- device = find_device_by_sysfs_path (manager, physdev_path);
+ device = find_device_by_physdev_uid (self, physdev_uid);
if (!device) {
FindDeviceSupportContext *ctx;
+ mm_obj_dbg (self, "port %s is first in device %s", name, physdev_uid);
+
/* Keep the device listed in the Manager */
- device = mm_device_new (physdev, hotplugged);
- g_hash_table_insert (manager->priv->devices,
- g_strdup (physdev_path),
+ device = mm_device_new (physdev_uid, hotplugged, FALSE, self->priv->object_manager);
+ g_hash_table_insert (self->priv->devices,
+ g_strdup (physdev_uid),
device);
/* Launch device support check */
ctx = g_slice_new (FindDeviceSupportContext);
- ctx->self = g_object_ref (manager);
+ ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
- mm_plugin_manager_find_device_support (
- manager->priv->plugin_manager,
+ mm_plugin_manager_device_support_check (
+ self->priv->plugin_manager,
device,
- (GAsyncReadyCallback)find_device_support_ready,
+ (GAsyncReadyCallback) device_support_check_ready,
ctx);
- }
+ } else
+ mm_obj_dbg (self, "additional port %s in device %s", name, physdev_uid);
/* Grab the port in the existing device. */
mm_device_grab_port (device, port);
+}
-out:
- if (physdev)
- g_object_unref (physdev);
+#if defined WITH_QRTR
+
+static void
+handle_qrtr_device_added (MMBaseManager *self,
+ guint node_id,
+ MMQrtrBusWatcher *bus_watcher)
+{
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+ QrtrNode *node;
+
+ node = mm_qrtr_bus_watcher_peek_node (bus_watcher, node_id);
+
+ kernel_device = mm_kernel_device_qrtr_new (node);
+
+ device_added (self, kernel_device, TRUE, FALSE);
}
static void
-device_removed (MMBaseManager *self,
- GUdevDevice *udev_device)
+handle_qrtr_device_removed (MMBaseManager *self,
+ guint node_id)
{
- MMDevice *device;
- const gchar *subsys;
+ g_autofree gchar *qrtr_device_name = NULL;
+
+ qrtr_device_name = mm_kernel_device_qrtr_helper_build_name (node_id);
+ device_removed (self, MM_KERNEL_DEVICE_QRTR_SUBSYSTEM, qrtr_device_name);
+}
+#endif
+
+static gboolean
+handle_kernel_event (MMBaseManager *self,
+ MMKernelEventProperties *properties,
+ GError **error)
+{
+ const gchar *action;
+ const gchar *subsystem;
const gchar *name;
+ const gchar *uid;
- g_return_if_fail (udev_device != NULL);
-
- subsys = g_udev_device_get_subsystem (udev_device);
- name = g_udev_device_get_name (udev_device);
-
- if (!g_str_has_prefix (subsys, "usb") ||
- (name && g_str_has_prefix (name, "cdc-wdm"))) {
- /* Handle tty/net/wdm port removal */
- device = find_device_by_port (self, udev_device);
- if (device) {
- mm_info ("(%s/%s): released by modem %s",
- subsys,
- name,
- g_udev_device_get_sysfs_path (mm_device_peek_udev_device (device)));
- mm_device_release_port (device, udev_device);
-
- /* If port probe list gets empty, remove the device object iself */
- if (!mm_device_peek_port_probe_list (device)) {
- mm_dbg ("Removing empty device '%s'", mm_device_get_path (device));
- mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, mm_device_get_path (device));
- }
- }
+ action = mm_kernel_event_properties_get_action (properties);
+ if (!action) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'action'");
+ return FALSE;
+ }
+ if (g_strcmp0 (action, "add") != 0 && g_strcmp0 (action, "remove") != 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'action' parameter given: '%s' (expected 'add' or 'remove')", action);
+ return FALSE;
+ }
- return;
+ subsystem = mm_kernel_event_properties_get_subsystem (properties);
+ if (!subsystem) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'subsystem'");
+ return FALSE;
}
- /* This case is designed to handle the case where, at least with kernel 2.6.31, unplugging
- * an in-use ttyACMx device results in udev generating remove events for the usb, but the
- * ttyACMx device (subsystem tty) is not removed, since it was in-use. So if we have not
- * found a modem for the port (above), we're going to look here to see if we have a modem
- * associated with the newly removed device. If so, we'll remove the modem, since the
- * device has been removed. That way, if the device is reinserted later, we'll go through
- * the process of exporting it.
- */
- device = find_device_by_udev_device (self, udev_device);
- if (device) {
- mm_dbg ("Removing device '%s'", mm_device_get_path (device));
- mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, mm_device_get_path (device));
- return;
+ if (!g_strv_contains (mm_plugin_manager_get_subsystems (self->priv->plugin_manager), subsystem)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid 'subsystem' parameter given: '%s'", subsystem);
+ return FALSE;
}
- /* Maybe a plugin is checking whether or not the port is supported.
- * TODO: Cancel every possible supports check in this port. */
+ name = mm_kernel_event_properties_get_name (properties);
+ if (!name) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Missing mandatory parameter 'name'");
+ return FALSE;
+ }
+
+ uid = mm_kernel_event_properties_get_uid (properties);
+
+ mm_obj_dbg (self, "kernel event reported:");
+ mm_obj_dbg (self, " action: %s", action);
+ mm_obj_dbg (self, " subsystem: %s", subsystem);
+ mm_obj_dbg (self, " name: %s", name);
+ mm_obj_dbg (self, " uid: %s", uid ? uid : "n/a");
+
+ if (g_strcmp0 (action, "add") == 0) {
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev ())
+ kernel_device = mm_kernel_device_udev_new_from_properties (self->priv->udev, properties, error);
+ else
+#endif
+ kernel_device = mm_kernel_device_generic_new (properties, error);
+ if (!kernel_device)
+ return FALSE;
+
+ device_added (self, kernel_device, TRUE, TRUE);
+ return TRUE;
+ }
+
+ if (g_strcmp0 (action, "remove") == 0) {
+ device_removed (self, subsystem, name);
+ return TRUE;
+ }
+
+ g_assert_not_reached ();
}
+#if defined WITH_UDEV
+
static void
-handle_uevent (GUdevClient *client,
- const char *action,
- GUdevDevice *device,
- gpointer user_data)
+handle_uevent (MMBaseManager *self,
+ const gchar *action,
+ GUdevDevice *device)
{
- MMBaseManager *self = MM_BASE_MANAGER (user_data);
- const gchar *subsys;
+ const gchar *subsystem;
const gchar *name;
- g_return_if_fail (action != NULL);
-
- /* A bit paranoid */
- subsys = g_udev_device_get_subsystem (device);
- g_return_if_fail (subsys != NULL);
- g_return_if_fail (g_str_equal (subsys, "tty") || g_str_equal (subsys, "net") || g_str_has_prefix (subsys, "usb"));
-
- /* We only care about tty/net and usb/cdc-wdm devices when adding modem ports,
- * but for remove, also handle usb parent device remove events
- */
- name = g_udev_device_get_name (device);
- if ( (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change"))
- && (!g_str_has_prefix (subsys, "usb") || (name && g_str_has_prefix (name, "cdc-wdm"))))
- device_added (self, device, TRUE, FALSE);
- else if (g_str_equal (action, "remove"))
- device_removed (self, device);
+ subsystem = g_udev_device_get_subsystem (device);
+ name = g_udev_device_get_name (device);
+
+ /* Valid udev devices must have subsystem and name set; if they don't have
+ * both things, we silently ignore them. */
+ if (!subsystem || !name)
+ return;
+
+ if (g_str_equal (action, "add") || g_str_equal (action, "move") || g_str_equal (action, "change")) {
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+
+ kernel_device = mm_kernel_device_udev_new (self->priv->udev, device);
+ device_added (self, kernel_device, TRUE, FALSE);
+ return;
+ }
+
+ if (g_str_equal (action, "remove")) {
+ device_removed (self, subsystem, name);
+ return;
+ }
}
typedef struct {
@@ -443,11 +508,25 @@ typedef struct {
static gboolean
start_device_added_idle (StartDeviceAdded *ctx)
{
- device_added (ctx->self, ctx->device, FALSE, ctx->manual_scan);
+ const gchar *subsystem;
+ const gchar *name;
+
+ subsystem = g_udev_device_get_subsystem (ctx->device);
+ name = g_udev_device_get_name (ctx->device);
+
+ /* Valid udev devices must have subsystem and name set; if they don't have
+ * both things, we silently ignore them. */
+ if (subsystem && name) {
+ g_autoptr(MMKernelDevice) kernel_device = NULL;
+
+ kernel_device = mm_kernel_device_udev_new (ctx->self->priv->udev, ctx->device);
+ device_added (ctx->self, kernel_device, FALSE, ctx->manual_scan);
+ }
+
g_object_unref (ctx->self);
g_object_unref (ctx->device);
g_slice_free (StartDeviceAdded, ctx);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -464,58 +543,96 @@ start_device_added (MMBaseManager *self,
g_idle_add ((GSourceFunc)start_device_added_idle, ctx);
}
-void
-mm_base_manager_start (MMBaseManager *manager,
- gboolean manual_scan)
+static void
+process_scan (MMBaseManager *self,
+ gboolean manual_scan)
{
- GList *devices, *iter;
+ const gchar **subsystems;
+ guint i;
+
+ subsystems = mm_plugin_manager_get_subsystems (self->priv->plugin_manager);
+ for (i = 0; subsystems[i]; i++) {
+ GList *devices;
+ GList *iter;
+
+ devices = g_udev_client_query_by_subsystem (self->priv->udev, subsystems[i]);
+ for (iter = devices; iter; iter = g_list_next (iter))
+ start_device_added (self, G_UDEV_DEVICE (iter->data), manual_scan);
+ g_list_free_full (devices, g_object_unref);
+ }
+}
- g_return_if_fail (manager != NULL);
- g_return_if_fail (MM_IS_BASE_MANAGER (manager));
+#endif
- if (!manager->priv->auto_scan && !manual_scan)
- return;
+static void
+process_initial_kernel_events (MMBaseManager *self)
+{
+ gchar *contents = NULL;
+ gchar *line;
+ GError *error = NULL;
- mm_dbg ("Starting %s device scan...", manual_scan ? "manual" : "automatic");
+ if (!self->priv->initial_kernel_events)
+ return;
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "tty");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
+ if (!g_file_get_contents (self->priv->initial_kernel_events, &contents, NULL, &error)) {
+ mm_obj_warn (self, "couldn't load initial kernel events: %s", error->message);
+ g_error_free (error);
+ return;
}
- g_list_free (devices);
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "net");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
- }
- g_list_free (devices);
+ line = contents;
+ while (line) {
+ gchar *next;
+
+ next = strchr (line, '\n');
+ if (next) {
+ *next = '\0';
+ next++;
+ }
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "usb");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- const gchar *name;
+ /* ignore empty lines */
+ if (line[0] != '\0') {
+ MMKernelEventProperties *properties;
+
+ properties = mm_kernel_event_properties_new_from_string (line, &error);
+ if (!properties) {
+ mm_obj_warn (self, "couldn't parse line '%s' as initial kernel event %s", line, error->message);
+ g_clear_error (&error);
+ } else if (!handle_kernel_event (self, properties, &error)) {
+ mm_obj_warn (self, "couldn't process line '%s' as initial kernel event %s", line, error->message);
+ g_clear_error (&error);
+ } else
+ mm_obj_dbg (self, "processed initial kernel event:' %s'", line);
+ g_clear_object (&properties);
+ }
- name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data));
- if (name && g_str_has_prefix (name, "cdc-wdm"))
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
+ line = next;
}
- g_list_free (devices);
- /* Newer kernels report 'usbmisc' subsystem */
- devices = g_udev_client_query_by_subsystem (manager->priv->udev, "usbmisc");
- for (iter = devices; iter; iter = g_list_next (iter)) {
- const gchar *name;
+ g_free (contents);
+}
- name = g_udev_device_get_name (G_UDEV_DEVICE (iter->data));
- if (name && g_str_has_prefix (name, "cdc-wdm"))
- start_device_added (manager, G_UDEV_DEVICE (iter->data), manual_scan);
- g_object_unref (G_OBJECT (iter->data));
+void
+mm_base_manager_start (MMBaseManager *self,
+ gboolean manual_scan)
+{
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_BASE_MANAGER (self));
+
+ if (!self->priv->auto_scan && !manual_scan) {
+ /* If we have a list of initial kernel events, process it now */
+ process_initial_kernel_events (self);
+ return;
}
- g_list_free (devices);
- mm_dbg ("Finished device scan...");
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev ()) {
+ mm_obj_dbg (self, "starting %s device scan...", manual_scan ? "manual" : "automatic");
+ process_scan (self, manual_scan);
+ mm_obj_dbg (self, "finished device scan...");
+ } else
+#endif
+ mm_obj_dbg (self, "unsupported %s device scan...", manual_scan ? "manual" : "automatic");
}
/*****************************************************************************/
@@ -532,8 +649,9 @@ remove_disable_ready (MMBaseModem *modem,
device = find_device_by_modem (self, modem);
if (device) {
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, device);
+ g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
}
}
@@ -549,8 +667,23 @@ foreach_disable (gpointer key,
mm_base_modem_disable (modem, (GAsyncReadyCallback)remove_disable_ready, self);
}
+static gboolean
+foreach_remove (gpointer key,
+ MMDevice *device,
+ MMBaseManager *self)
+{
+ MMBaseModem *modem;
+
+ modem = mm_device_peek_modem (device);
+ if (modem)
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
+ mm_device_remove_modem (device);
+ return TRUE;
+}
+
void
-mm_base_manager_shutdown (MMBaseManager *self)
+mm_base_manager_shutdown (MMBaseManager *self,
+ gboolean disable)
{
g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_BASE_MANAGER (self));
@@ -558,12 +691,18 @@ mm_base_manager_shutdown (MMBaseManager *self)
/* Cancel all ongoing auth requests */
g_cancellable_cancel (self->priv->authp_cancellable);
- g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
+ if (disable) {
+ g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, self);
+
+ /* Disabling may take a few iterations of the mainloop, so the caller
+ * has to iterate the mainloop until all devices have been disabled and
+ * removed.
+ */
+ return;
+ }
- /* Disabling may take a few iterations of the mainloop, so the caller
- * has to iterate the mainloop until all devices have been disabled and
- * removed.
- */
+ /* Otherwise, just remove directly */
+ g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self);
}
guint32
@@ -586,6 +725,50 @@ mm_base_manager_num_modems (MMBaseManager *self)
}
/*****************************************************************************/
+/* Quick resume synchronization */
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+static void
+base_modem_sync_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ g_autoptr(GError) error = NULL;
+
+ mm_base_modem_sync_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "synchronization failed: %s", error->message);
+ return;
+ }
+ mm_obj_info (self, "synchronization finished");
+}
+
+void
+mm_base_manager_sync (MMBaseManager *self)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_BASE_MANAGER (self));
+
+ /* Refresh each device */
+ g_hash_table_iter_init (&iter, self->priv->devices);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMBaseModem *modem;
+
+ modem = mm_device_peek_modem (MM_DEVICE (value));
+
+ /* We just want to start the synchronization, we don't need the result */
+ if (modem)
+ mm_base_modem_sync (modem, (GAsyncReadyCallback)base_modem_sync_ready, NULL);
+ }
+}
+
+#endif
+
+/*****************************************************************************/
/* Set logging */
typedef struct {
@@ -604,18 +787,18 @@ set_logging_context_free (SetLoggingContext *ctx)
}
static void
-set_logging_auth_ready (MMAuthProvider *authp,
- GAsyncResult *res,
+set_logging_auth_ready (MMAuthProvider *authp,
+ GAsyncResult *res,
SetLoggingContext *ctx)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else if (!mm_log_set_level(ctx->level, &error))
+ else if (!mm_log_set_level (ctx->level, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else {
- mm_info ("logging: level '%s'", ctx->level);
+ mm_obj_info (ctx->self, "logging: level '%s'", ctx->level);
mm_gdbus_org_freedesktop_modem_manager1_complete_set_logging (
MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
ctx->invocation);
@@ -632,7 +815,7 @@ handle_set_logging (MmGdbusOrgFreedesktopModemManager1 *manager,
SetLoggingContext *ctx;
ctx = g_new0 (SetLoggingContext, 1);
- ctx->self = g_object_ref (manager);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
ctx->invocation = g_object_ref (invocation);
ctx->level = g_strdup (level);
@@ -671,11 +854,18 @@ scan_devices_auth_ready (MMAuthProvider *authp,
if (!mm_auth_provider_authorize_finish (authp, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else {
- /* Otherwise relaunch device scan */
- mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
- mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
- MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
- ctx->invocation);
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev ()) {
+ /* Otherwise relaunch device scan */
+ mm_base_manager_start (MM_BASE_MANAGER (ctx->self), TRUE);
+ mm_gdbus_org_freedesktop_modem_manager1_complete_scan_devices (
+ MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
+ ctx->invocation);
+ } else
+#endif
+ g_dbus_method_invocation_return_error_literal (
+ ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot request manual scan of devices: unsupported");
}
scan_devices_context_free (ctx);
@@ -688,7 +878,7 @@ handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager,
ScanDevicesContext *ctx;
ctx = g_new (ScanDevicesContext, 1);
- ctx->self = g_object_ref (manager);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
ctx->invocation = g_object_ref (invocation);
mm_auth_provider_authorize (ctx->self->priv->authp,
@@ -701,6 +891,410 @@ handle_scan_devices (MmGdbusOrgFreedesktopModemManager1 *manager,
}
/*****************************************************************************/
+
+typedef struct {
+ MMBaseManager *self;
+ GDBusMethodInvocation *invocation;
+ GVariant *dictionary;
+} ReportKernelEventContext;
+
+static void
+report_kernel_event_context_free (ReportKernelEventContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_variant_unref (ctx->dictionary);
+ g_slice_free (ReportKernelEventContext, ctx);
+}
+
+static void
+report_kernel_event_auth_ready (MMAuthProvider *authp,
+ GAsyncResult *res,
+ ReportKernelEventContext *ctx)
+{
+ GError *error = NULL;
+ MMKernelEventProperties *properties = NULL;
+
+ if (!mm_auth_provider_authorize_finish (authp, res, &error))
+ goto out;
+
+#if defined WITH_UDEV
+ if (!mm_context_get_test_no_udev () && ctx->self->priv->auto_scan) {
+ error = g_error_new_literal (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot report kernel event: "
+ "udev monitoring already in place");
+ goto out;
+ }
+#endif
+
+ properties = mm_kernel_event_properties_new_from_dictionary (ctx->dictionary, &error);
+ if (!properties)
+ goto out;
+
+ handle_kernel_event (ctx->self, properties, &error);
+
+out:
+ if (error) {
+ mm_obj_warn (ctx->self, "couldn't handle kernel event: %s", error->message);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ } else
+ mm_gdbus_org_freedesktop_modem_manager1_complete_report_kernel_event (
+ MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
+ ctx->invocation);
+
+ if (properties)
+ g_object_unref (properties);
+ report_kernel_event_context_free (ctx);
+}
+
+static gboolean
+handle_report_kernel_event (MmGdbusOrgFreedesktopModemManager1 *manager,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary)
+{
+ ReportKernelEventContext *ctx;
+
+ ctx = g_slice_new0 (ReportKernelEventContext);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
+ ctx->invocation = g_object_ref (invocation);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_auth_provider_authorize (ctx->self->priv->authp,
+ invocation,
+ MM_AUTHORIZATION_MANAGER_CONTROL,
+ ctx->self->priv->authp_cancellable,
+ (GAsyncReadyCallback)report_kernel_event_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Inhibit or uninhibit device */
+
+typedef struct {
+ MMKernelDevice *kernel_port;
+ gboolean manual_scan;
+} InhibitedDevicePortInfo;
+
+static void
+inhibited_device_port_info_free (InhibitedDevicePortInfo *port_info)
+{
+ g_object_unref (port_info->kernel_port);
+ g_slice_free (InhibitedDevicePortInfo, port_info);
+}
+
+typedef struct {
+ gchar *sender;
+ guint name_lost_id;
+ GList *port_infos;
+} InhibitedDeviceInfo;
+
+static void
+inhibited_device_info_free (InhibitedDeviceInfo *info)
+{
+ g_list_free_full (info->port_infos, (GDestroyNotify)inhibited_device_port_info_free);
+ g_bus_unwatch_name (info->name_lost_id);
+ g_free (info->sender);
+ g_slice_free (InhibitedDeviceInfo, info);
+}
+
+static InhibitedDeviceInfo *
+find_inhibited_device_info_by_physdev_uid (MMBaseManager *self,
+ const gchar *physdev_uid)
+{
+ return (physdev_uid ? g_hash_table_lookup (self->priv->inhibited_devices, physdev_uid) : NULL);
+}
+
+static gboolean
+is_device_inhibited (MMBaseManager *self,
+ const gchar *physdev_uid)
+{
+ return !!find_inhibited_device_info_by_physdev_uid (self, physdev_uid);
+}
+
+static void
+device_inhibited_untrack_port (MMBaseManager *self,
+ const gchar *subsystem,
+ const gchar *name)
+{
+ GHashTableIter iter;
+ gchar *uid;
+ InhibitedDeviceInfo *info;
+
+ g_hash_table_iter_init (&iter, self->priv->inhibited_devices);
+ while (g_hash_table_iter_next (&iter, (gpointer)&uid, (gpointer)&info)) {
+ GList *l;
+
+ for (l = info->port_infos; l; l = g_list_next (l)) {
+ InhibitedDevicePortInfo *port_info;
+
+ port_info = (InhibitedDevicePortInfo *)(l->data);
+
+ if ((g_strcmp0 (subsystem, mm_kernel_device_get_subsystem (port_info->kernel_port)) == 0) &&
+ (g_strcmp0 (name, mm_kernel_device_get_name (port_info->kernel_port)) == 0)) {
+ mm_obj_dbg (self, "released port %s while inhibited", name);
+ inhibited_device_port_info_free (port_info);
+ info->port_infos = g_list_delete_link (info->port_infos, l);
+ return;
+ }
+ }
+ }
+}
+
+static void
+device_inhibited_track_port (MMBaseManager *self,
+ const gchar *physdev_uid,
+ MMKernelDevice *kernel_port,
+ gboolean manual_scan)
+{
+ InhibitedDevicePortInfo *port_info;
+ InhibitedDeviceInfo *info;
+ GList *l;
+
+ info = find_inhibited_device_info_by_physdev_uid (self, physdev_uid);
+ g_assert (info);
+
+ for (l = info->port_infos; l; l = g_list_next (l)) {
+ /* If device is already tracked, just overwrite the manual scan info */
+ port_info = (InhibitedDevicePortInfo *)(l->data);
+ if (mm_kernel_device_cmp (port_info->kernel_port, kernel_port)) {
+ port_info->manual_scan = manual_scan;
+ return;
+ }
+ }
+
+ mm_obj_dbg (self, "added port %s while inhibited", mm_kernel_device_get_name (kernel_port));
+
+ port_info = g_slice_new0 (InhibitedDevicePortInfo);
+ port_info->kernel_port = g_object_ref (kernel_port);
+ port_info->manual_scan = manual_scan;
+ info->port_infos = g_list_append (info->port_infos, port_info);
+}
+
+typedef struct {
+ MMBaseManager *self;
+ gchar *uid;
+} InhibitSenderLostContext;
+
+static void
+inhibit_sender_lost_context_free (InhibitSenderLostContext *lost_ctx)
+{
+ g_free (lost_ctx->uid);
+ g_slice_free (InhibitSenderLostContext, lost_ctx);
+}
+
+static void
+remove_device_inhibition (MMBaseManager *self,
+ const gchar *uid)
+{
+ InhibitedDeviceInfo *info;
+ MMDevice *device;
+ GList *port_infos;
+
+ info = find_inhibited_device_info_by_physdev_uid (self, uid);
+ g_assert (info);
+
+ device = find_device_by_physdev_uid (self, uid);
+ port_infos = info->port_infos;
+ info->port_infos = NULL;
+ g_hash_table_remove (self->priv->inhibited_devices, uid);
+
+ if (port_infos) {
+ GList *l;
+
+ /* Note that a device can only be inhibited if it had an existing
+ * modem exposed in the bus. And so, inhibition can only be placed
+ * AFTER all port probes have finished for a given device. This means
+ * that we either have a device tracked, or we have a list of port
+ * infos. Both at the same time should never happen. */
+ g_assert (!device);
+
+ /* Report as added all port infos that we had tracked while the
+ * device was inhibited. We can only report the added port after
+ * having removed the entry from the inhibited devices tracking
+ * table. */
+ for (l = port_infos; l; l = g_list_next (l)) {
+ InhibitedDevicePortInfo *port_info;
+
+ port_info = (InhibitedDevicePortInfo *)(l->data);
+ device_added (self, port_info->kernel_port, FALSE, port_info->manual_scan);
+ }
+ g_list_free_full (port_infos, (GDestroyNotify)inhibited_device_port_info_free);
+ }
+ /* The device may be totally gone from the system while we were
+ * keeping the inhibition, so do not error out if not found. */
+ else if (device) {
+ GError *error = NULL;
+
+ /* Uninhibit device, which will create and expose the modem object */
+ if (!mm_device_uninhibit (device, &error)) {
+ mm_obj_warn (self, "couldn't uninhibit device: %s", error->message);
+ g_error_free (error);
+ }
+ }
+}
+
+static void
+inhibit_sender_lost (GDBusConnection *connection,
+ const gchar *sender_name,
+ InhibitSenderLostContext *lost_ctx)
+{
+ mm_obj_info (lost_ctx->self, "device inhibition teardown for uid '%s' (owner disappeared from bus)", lost_ctx->uid);
+ remove_device_inhibition (lost_ctx->self, lost_ctx->uid);
+}
+
+typedef struct {
+ MMBaseManager *self;
+ GDBusMethodInvocation *invocation;
+ gchar *uid;
+ gboolean inhibit;
+} InhibitDeviceContext;
+
+static void
+inhibit_device_context_free (InhibitDeviceContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_free (ctx->uid);
+ g_slice_free (InhibitDeviceContext, ctx);
+}
+
+static void
+device_inhibit_ready (MMDevice *device,
+ GAsyncResult *res,
+ InhibitDeviceContext *ctx)
+{
+ InhibitSenderLostContext *lost_ctx;
+ InhibitedDeviceInfo *info;
+ GError *error = NULL;
+
+ if (!mm_device_inhibit_finish (device, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ inhibit_device_context_free (ctx);
+ return;
+ }
+
+ info = g_slice_new0 (InhibitedDeviceInfo);
+ info->sender = g_strdup (g_dbus_method_invocation_get_sender (ctx->invocation));
+
+ /* This context will exist as long as the sender name watcher exists,
+ * i.e. as long as the associated InhibitDeviceInfo exists. We don't need
+ * an extra reference of self here because these contexts are stored within
+ * self, and therefore bound to its lifetime. */
+ lost_ctx = g_slice_new0 (InhibitSenderLostContext);
+ lost_ctx->self = ctx->self;
+ lost_ctx->uid = g_strdup (ctx->uid);
+ info->name_lost_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (ctx->invocation),
+ info->sender,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL,
+ (GBusNameVanishedCallback)inhibit_sender_lost,
+ lost_ctx,
+ (GDestroyNotify)inhibit_sender_lost_context_free);
+
+ g_hash_table_insert (ctx->self->priv->inhibited_devices, g_strdup (ctx->uid), info);
+
+ mm_obj_info (ctx->self, "device inhibition setup for uid '%s'", ctx->uid);
+
+ mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device (
+ MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
+ ctx->invocation);
+ inhibit_device_context_free (ctx);
+}
+
+static void
+base_manager_inhibit_device (InhibitDeviceContext *ctx)
+{
+ MMDevice *device;
+
+ device = find_device_by_physdev_uid (ctx->self, ctx->uid);
+ if (!device) {
+ g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "No device found with uid '%s'", ctx->uid);
+ inhibit_device_context_free (ctx);
+ return;
+ }
+
+ if (mm_device_get_inhibited (device)) {
+ g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "Device '%s' is already inhibited", ctx->uid);
+ inhibit_device_context_free (ctx);
+ return;
+ }
+
+ mm_device_inhibit (device,
+ (GAsyncReadyCallback) device_inhibit_ready,
+ ctx);
+}
+
+static void
+base_manager_uninhibit_device (InhibitDeviceContext *ctx)
+{
+ InhibitedDeviceInfo *info;
+ const gchar *sender;
+
+ /* Validate uninhibit request */
+ sender = g_dbus_method_invocation_get_sender (ctx->invocation);
+ info = find_inhibited_device_info_by_physdev_uid (ctx->self, ctx->uid);
+ if (!info || (g_strcmp0 (info->sender, sender) != 0)) {
+ g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "No inhibition found for uid '%s'", ctx->uid);
+ inhibit_device_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (ctx->self, "device inhibition teardown for uid '%s'", ctx->uid);
+ remove_device_inhibition (ctx->self, ctx->uid);
+
+ mm_gdbus_org_freedesktop_modem_manager1_complete_inhibit_device (
+ MM_GDBUS_ORG_FREEDESKTOP_MODEM_MANAGER1 (ctx->self),
+ ctx->invocation);
+ inhibit_device_context_free (ctx);
+}
+
+static void
+inhibit_device_auth_ready (MMAuthProvider *authp,
+ GAsyncResult *res,
+ InhibitDeviceContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_auth_provider_authorize_finish (authp, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ inhibit_device_context_free (ctx);
+ return;
+ }
+
+ if (ctx->inhibit)
+ base_manager_inhibit_device (ctx);
+ else
+ base_manager_uninhibit_device (ctx);
+}
+
+static gboolean
+handle_inhibit_device (MmGdbusOrgFreedesktopModemManager1 *manager,
+ GDBusMethodInvocation *invocation,
+ const gchar *uid,
+ gboolean inhibit)
+{
+ InhibitDeviceContext *ctx;
+
+ ctx = g_slice_new0 (InhibitDeviceContext);
+ ctx->self = MM_BASE_MANAGER (g_object_ref (manager));
+ ctx->invocation = g_object_ref (invocation);
+ ctx->uid = g_strdup (uid);
+ ctx->inhibit = inhibit;
+
+ mm_auth_provider_authorize (ctx->self->priv->authp,
+ invocation,
+ MM_AUTHORIZATION_MANAGER_CONTROL,
+ ctx->self->priv->authp_cancellable,
+ (GAsyncReadyCallback)inhibit_device_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
/* Test profile setup */
static gboolean
@@ -713,17 +1307,15 @@ handle_set_profile (MmGdbusTest *skeleton,
{
MMPlugin *plugin;
MMDevice *device;
- gchar *physdev;
+ gchar *physdev_uid;
GError *error = NULL;
- mm_info ("Test profile set to: '%s'", id);
+ mm_obj_info (self, "test profile set to: '%s'", id);
/* Create device and keep it listed in the Manager */
- physdev = g_strdup_printf ("/virtual/%s", id);
- device = mm_device_virtual_new (physdev, TRUE);
- g_hash_table_insert (self->priv->devices,
- g_strdup (physdev),
- device);
+ physdev_uid = g_strdup_printf ("/virtual/%s", id);
+ device = mm_device_new (physdev_uid, TRUE, TRUE, self->priv->object_manager);
+ g_hash_table_insert (self->priv->devices, physdev_uid, device);
/* Grab virtual ports */
mm_device_virtual_grab_ports (device, (const gchar **)ports);
@@ -735,29 +1327,29 @@ handle_set_profile (MmGdbusTest *skeleton,
MM_CORE_ERROR_NOT_FOUND,
"Requested plugin '%s' not found",
plugin_name);
- mm_warn ("Couldn't set plugin for virtual device at '%s': %s",
- mm_device_get_path (device),
- error->message);
+ mm_obj_warn (self, "couldn't set plugin for virtual device '%s': %s",
+ mm_device_get_uid (device),
+ error->message);
goto out;
}
mm_device_set_plugin (device, G_OBJECT (plugin));
/* Create modem */
- if (!mm_device_create_modem (device, self->priv->object_manager, &error)) {
- mm_warn ("Couldn't create modem for virtual device at '%s': %s",
- mm_device_get_path (device),
- error->message);
+ if (!mm_device_create_modem (device, &error)) {
+ mm_obj_warn (self, "couldn't create modem for virtual device '%s': %s",
+ mm_device_get_uid (device),
+ error->message);
goto out;
}
- mm_info ("Modem for virtual device at '%s' successfully created",
- mm_device_get_path (device));
+ mm_obj_info (self, "modem for virtual device '%s' successfully created",
+ mm_device_get_uid (device));
out:
if (error) {
mm_device_remove_modem (device);
- g_hash_table_remove (self->priv->devices, mm_device_get_path (device));
+ g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
g_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error);
} else
@@ -768,65 +1360,85 @@ out:
/*****************************************************************************/
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("base-manager");
+}
+
+/*****************************************************************************/
+
MMBaseManager *
-mm_base_manager_new (GDBusConnection *connection,
- const gchar *plugin_dir,
- gboolean auto_scan,
- gboolean enable_test,
- GError **error)
+mm_base_manager_new (GDBusConnection *connection,
+ const gchar *plugin_dir,
+ gboolean auto_scan,
+ MMFilterRule filter_policy,
+ const gchar *initial_kernel_events,
+ gboolean enable_test,
+ GError **error)
{
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
return g_initable_new (MM_TYPE_BASE_MANAGER,
NULL, /* cancellable */
error,
- MM_BASE_MANAGER_CONNECTION, connection,
- MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir,
- MM_BASE_MANAGER_AUTO_SCAN, auto_scan,
- MM_BASE_MANAGER_ENABLE_TEST, enable_test,
+ MM_BASE_MANAGER_CONNECTION, connection,
+ MM_BASE_MANAGER_PLUGIN_DIR, plugin_dir,
+ MM_BASE_MANAGER_AUTO_SCAN, auto_scan,
+ MM_BASE_MANAGER_FILTER_POLICY, filter_policy,
+ MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS, initial_kernel_events,
+ MM_BASE_MANAGER_ENABLE_TEST, enable_test,
+ "version", MM_DIST_VERSION,
NULL);
}
static void
-set_property (GObject *object,
- guint prop_id,
+set_property (GObject *object,
+ guint prop_id,
const GValue *value,
- GParamSpec *pspec)
+ GParamSpec *pspec)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (object);
switch (prop_id) {
case PROP_CONNECTION: {
gboolean had_connection = FALSE;
- if (priv->connection) {
+ if (self->priv->connection) {
had_connection = TRUE;
- g_object_unref (priv->connection);
+ g_object_unref (self->priv->connection);
}
- priv->connection = g_value_dup_object (value);
+ self->priv->connection = g_value_dup_object (value);
/* Propagate connection loss to subobjects */
- if (had_connection && !priv->connection) {
- if (priv->object_manager) {
- mm_dbg ("Stopping connection in object manager server");
- g_dbus_object_manager_server_set_connection (priv->object_manager, NULL);
+ if (had_connection && !self->priv->connection) {
+ if (self->priv->object_manager) {
+ mm_obj_dbg (self, "stopping connection in object manager server");
+ g_dbus_object_manager_server_set_connection (self->priv->object_manager, NULL);
}
- if (priv->test_skeleton &&
- g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton))) {
- mm_dbg ("Stopping connection in test skeleton");
- g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton));
+ if (self->priv->test_skeleton &&
+ g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton))) {
+ mm_obj_dbg (self, "stopping connection in test skeleton");
+ g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton));
}
}
break;
}
case PROP_AUTO_SCAN:
- priv->auto_scan = g_value_get_boolean (value);
+ self->priv->auto_scan = g_value_get_boolean (value);
+ break;
+ case PROP_FILTER_POLICY:
+ self->priv->filter_policy = g_value_get_flags (value);
break;
case PROP_ENABLE_TEST:
- priv->enable_test = g_value_get_boolean (value);
+ self->priv->enable_test = g_value_get_boolean (value);
break;
case PROP_PLUGIN_DIR:
- g_free (priv->plugin_dir);
- priv->plugin_dir = g_value_dup_string (value);
+ g_free (self->priv->plugin_dir);
+ self->priv->plugin_dir = g_value_dup_string (value);
+ break;
+ case PROP_INITIAL_KERNEL_EVENTS:
+ g_free (self->priv->initial_kernel_events);
+ self->priv->initial_kernel_events = g_value_dup_string (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -835,25 +1447,31 @@ set_property (GObject *object,
}
static void
-get_property (GObject *object,
- guint prop_id,
- GValue *value,
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
GParamSpec *pspec)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (object);
switch (prop_id) {
case PROP_CONNECTION:
- g_value_set_object (value, priv->connection);
+ g_value_set_object (value, self->priv->connection);
break;
case PROP_AUTO_SCAN:
- g_value_set_boolean (value, priv->auto_scan);
+ g_value_set_boolean (value, self->priv->auto_scan);
+ break;
+ case PROP_FILTER_POLICY:
+ g_value_set_flags (value, self->priv->filter_policy);
break;
case PROP_ENABLE_TEST:
- g_value_set_boolean (value, priv->enable_test);
+ g_value_set_boolean (value, self->priv->enable_test);
break;
case PROP_PLUGIN_DIR:
- g_value_set_string (value, priv->plugin_dir);
+ g_value_set_string (value, self->priv->plugin_dir);
+ break;
+ case PROP_INITIAL_KERNEL_EVENTS:
+ g_value_set_string (value, self->priv->initial_kernel_events);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -862,82 +1480,100 @@ get_property (GObject *object,
}
static void
-mm_base_manager_init (MMBaseManager *manager)
+mm_base_manager_init (MMBaseManager *self)
{
- MMBaseManagerPrivate *priv;
- const gchar *subsys[5] = { "tty", "net", "usb", "usbmisc", NULL };
-
/* Setup private data */
- manager->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
- MM_TYPE_BASE_MANAGER,
- MMBaseManagerPrivate);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_MANAGER, MMBaseManagerPrivate);
/* Setup authorization provider */
- priv->authp = mm_auth_get_provider ();
- priv->authp_cancellable = g_cancellable_new ();
+ self->priv->authp = mm_auth_provider_get ();
+ self->priv->authp_cancellable = g_cancellable_new ();
/* Setup internal lists of device objects */
- priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ self->priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- /* Setup UDev client */
- priv->udev = g_udev_client_new (subsys);
+ /* Setup internal list of inhibited devices */
+ self->priv->inhibited_devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)inhibited_device_info_free);
/* By default, enable autoscan */
- priv->auto_scan = TRUE;
+ self->priv->auto_scan = TRUE;
/* By default, no test interface */
- priv->enable_test = FALSE;
+ self->priv->enable_test = FALSE;
/* Setup Object Manager Server */
- priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH);
+ self->priv->object_manager = g_dbus_object_manager_server_new (MM_DBUS_PATH);
/* Enable processing of input DBus messages */
- g_signal_connect (manager,
- "handle-set-logging",
- G_CALLBACK (handle_set_logging),
- NULL);
- g_signal_connect (manager,
- "handle-scan-devices",
- G_CALLBACK (handle_scan_devices),
+ g_object_connect (self,
+ "signal::handle-set-logging", G_CALLBACK (handle_set_logging), NULL,
+ "signal::handle-scan-devices", G_CALLBACK (handle_scan_devices), NULL,
+ "signal::handle-report-kernel-event", G_CALLBACK (handle_report_kernel_event), NULL,
+ "signal::handle-inhibit-device", G_CALLBACK (handle_inhibit_device), NULL,
NULL);
}
static gboolean
-initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
+initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (initable)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (initable);
- /* If autoscan enabled, list for udev events */
- if (priv->auto_scan)
- g_signal_connect (priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
+ /* Create filter */
+ self->priv->filter = mm_filter_new (self->priv->filter_policy, error);
+ if (!self->priv->filter)
+ return FALSE;
/* Create plugin manager */
- priv->plugin_manager = mm_plugin_manager_new (priv->plugin_dir, error);
- if (!priv->plugin_manager)
+ self->priv->plugin_manager = mm_plugin_manager_new (self->priv->plugin_dir, self->priv->filter, error);
+ if (!self->priv->plugin_manager)
return FALSE;
+#if defined WITH_UDEV
+ /* Create udev client based on the subsystems requested by the plugins */
+ self->priv->udev = g_udev_client_new (mm_plugin_manager_get_subsystems (self->priv->plugin_manager));
+
+ /* If autoscan enabled, list for udev events */
+ if (!mm_context_get_test_no_udev () && self->priv->auto_scan)
+ g_signal_connect_swapped (self->priv->udev, "uevent", G_CALLBACK (handle_uevent), initable);
+#endif
+
+#if defined WITH_QRTR
+ if (!mm_context_get_test_no_qrtr ()) {
+ /* Create and setup the QrtrBusWatcher */
+ self->priv->qrtr_bus_watcher = mm_qrtr_bus_watcher_new ();
+ mm_qrtr_bus_watcher_start (self->priv->qrtr_bus_watcher, NULL, NULL);
+ /* If autoscan enabled, list for QrtrBusWatcher events */
+ if (self->priv->auto_scan) {
+ g_object_connect (self->priv->qrtr_bus_watcher,
+ "swapped-signal::" MM_QRTR_BUS_WATCHER_DEVICE_ADDED, G_CALLBACK (handle_qrtr_device_added), self,
+ "swapped-signal::" MM_QRTR_BUS_WATCHER_DEVICE_REMOVED, G_CALLBACK (handle_qrtr_device_removed), self,
+ NULL);
+ }
+ }
+#endif
+
/* Export the manager interface */
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (initable),
- priv->connection,
+ self->priv->connection,
MM_DBUS_PATH,
error))
return FALSE;
/* Export the Object Manager interface */
- g_dbus_object_manager_server_set_connection (priv->object_manager,
- priv->connection);
+ g_dbus_object_manager_server_set_connection (self->priv->object_manager,
+ self->priv->connection);
/* Setup the Test skeleton and export the interface */
- if (priv->enable_test) {
- priv->test_skeleton = mm_gdbus_test_skeleton_new ();
- g_signal_connect (priv->test_skeleton,
+ if (self->priv->enable_test) {
+ self->priv->test_skeleton = mm_gdbus_test_skeleton_new ();
+ g_signal_connect (self->priv->test_skeleton,
"handle-set-profile",
G_CALLBACK (handle_set_profile),
initable);
- if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->test_skeleton),
- priv->connection,
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->priv->test_skeleton),
+ self->priv->connection,
MM_DBUS_PATH,
error))
return FALSE;
@@ -950,32 +1586,43 @@ initable_init (GInitable *initable,
static void
finalize (GObject *object)
{
- MMBaseManagerPrivate *priv = MM_BASE_MANAGER (object)->priv;
+ MMBaseManager *self = MM_BASE_MANAGER (object);
- g_free (priv->plugin_dir);
+ g_free (self->priv->initial_kernel_events);
+ g_free (self->priv->plugin_dir);
- g_hash_table_destroy (priv->devices);
+ g_hash_table_destroy (self->priv->inhibited_devices);
+ g_hash_table_destroy (self->priv->devices);
- if (priv->udev)
- g_object_unref (priv->udev);
+#if defined WITH_UDEV
+ if (self->priv->udev)
+ g_object_unref (self->priv->udev);
+#endif
- if (priv->plugin_manager)
- g_object_unref (priv->plugin_manager);
+#if defined WITH_QRTR
+ if (self->priv->qrtr_bus_watcher)
+ g_object_unref (self->priv->qrtr_bus_watcher);
+#endif
- if (priv->object_manager)
- g_object_unref (priv->object_manager);
+ if (self->priv->filter)
+ g_object_unref (self->priv->filter);
- if (priv->test_skeleton)
- g_object_unref (priv->test_skeleton);
+ if (self->priv->plugin_manager)
+ g_object_unref (self->priv->plugin_manager);
- if (priv->connection)
- g_object_unref (priv->connection);
+ if (self->priv->object_manager)
+ g_object_unref (self->priv->object_manager);
- if (priv->authp)
- g_object_unref (priv->authp);
+ if (self->priv->test_skeleton)
+ g_object_unref (self->priv->test_skeleton);
- if (priv->authp_cancellable)
- g_object_unref (priv->authp_cancellable);
+ if (self->priv->connection)
+ g_object_unref (self->priv->connection);
+
+ /* note: authp is a singleton, we don't keep a full reference */
+
+ if (self->priv->authp_cancellable)
+ g_object_unref (self->priv->authp_cancellable);
G_OBJECT_CLASS (mm_base_manager_parent_class)->finalize (object);
}
@@ -987,6 +1634,12 @@ initable_iface_init (GInitableIface *iface)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_manager_class_init (MMBaseManagerClass *manager_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
@@ -1016,6 +1669,15 @@ mm_base_manager_class_init (MMBaseManagerClass *manager_class)
TRUE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_FILTER_POLICY,
+ g_param_spec_flags (MM_BASE_MANAGER_FILTER_POLICY,
+ "Filter policy",
+ "Mask of rules enabled in the filter",
+ MM_TYPE_FILTER_RULE,
+ MM_FILTER_RULE_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
g_object_class_install_property
(object_class, PROP_ENABLE_TEST,
g_param_spec_boolean (MM_BASE_MANAGER_ENABLE_TEST,
@@ -1031,4 +1693,12 @@ mm_base_manager_class_init (MMBaseManagerClass *manager_class)
"Where to look for plugins",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_INITIAL_KERNEL_EVENTS,
+ g_param_spec_string (MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS,
+ "Initial kernel events",
+ "Path to a file with the list of initial kernel events",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
diff --git a/src/mm-base-manager.h b/src/mm-base-manager.h
index 0e4e97bd..be51d0c0 100644
--- a/src/mm-base-manager.h
+++ b/src/mm-base-manager.h
@@ -21,6 +21,7 @@
#include <glib-object.h>
#include <gio/gio.h>
+#include "mm-filter.h"
#include "mm-gdbus-manager.h"
#define MM_TYPE_BASE_MANAGER (mm_base_manager_get_type ())
@@ -30,10 +31,12 @@
#define MM_IS_BASE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_BASE_MANAGER))
#define MM_BASE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_MANAGER, MMBaseManagerClass))
-#define MM_BASE_MANAGER_CONNECTION "connection" /* Construct-only */
-#define MM_BASE_MANAGER_AUTO_SCAN "auto-scan" /* Construct-only */
-#define MM_BASE_MANAGER_ENABLE_TEST "enable-test" /* Construct-only */
-#define MM_BASE_MANAGER_PLUGIN_DIR "plugin-dir" /* Construct-only */
+#define MM_BASE_MANAGER_CONNECTION "connection" /* Construct-only */
+#define MM_BASE_MANAGER_AUTO_SCAN "auto-scan" /* Construct-only */
+#define MM_BASE_MANAGER_FILTER_POLICY "filter-policy" /* Construct-only */
+#define MM_BASE_MANAGER_ENABLE_TEST "enable-test" /* Construct-only */
+#define MM_BASE_MANAGER_PLUGIN_DIR "plugin-dir" /* Construct-only */
+#define MM_BASE_MANAGER_INITIAL_KERNEL_EVENTS "initial-kernel-events" /* Construct-only */
typedef struct _MMBaseManagerPrivate MMBaseManagerPrivate;
@@ -47,17 +50,25 @@ typedef struct {
} MMBaseManagerClass;
GType mm_base_manager_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseManager, g_object_unref)
-MMBaseManager *mm_base_manager_new (GDBusConnection *bus,
- const gchar *plugin_dir,
- gboolean auto_scan,
- gboolean enable_test,
- GError **error);
+MMBaseManager *mm_base_manager_new (GDBusConnection *bus,
+ const gchar *plugin_dir,
+ gboolean auto_scan,
+ MMFilterRule filter_policy,
+ const gchar *initial_kernel_events,
+ gboolean enable_test,
+ GError **error);
void mm_base_manager_start (MMBaseManager *manager,
gboolean manual_scan);
-void mm_base_manager_shutdown (MMBaseManager *manager);
+void mm_base_manager_shutdown (MMBaseManager *manager,
+ gboolean disable);
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+void mm_base_manager_sync (MMBaseManager *manager);
+#endif
guint32 mm_base_manager_num_modems (MMBaseManager *manager);
diff --git a/src/mm-base-modem-at.c b/src/mm-base-modem-at.c
index 77661e5b..d1352e08 100644
--- a/src/mm-base-modem-at.c
+++ b/src/mm-base-modem-at.c
@@ -30,7 +30,7 @@ abort_async_if_port_unusable (MMBaseModem *self,
GError *error = NULL;
gboolean init_sequence_enabled = FALSE;
- /* If no port given, probably the port dissapeared */
+ /* If no port given, probably the port disappeared */
if (!port) {
g_simple_async_report_error_in_idle (
G_OBJECT (self),
@@ -90,18 +90,18 @@ modem_cancellable_cancelled (GCancellable *modem_cancellable,
/* AT sequence handling */
typedef struct {
- MMBaseModem *self;
- MMPortSerialAt *port;
- GCancellable *cancellable;
- gulong cancelled_id;
- GCancellable *modem_cancellable;
- GCancellable *user_cancellable;
+ MMBaseModem *self;
+ MMPortSerialAt *port;
+ GCancellable *cancellable;
+ gulong cancelled_id;
+ GCancellable *modem_cancellable;
+ GCancellable *user_cancellable;
const MMBaseModemAtCommand *current;
const MMBaseModemAtCommand *sequence;
- GSimpleAsyncResult *simple;
- gpointer response_processor_context;
- GDestroyNotify response_processor_context_free;
- GVariant *result;
+ GSimpleAsyncResult *simple;
+ gpointer response_processor_context;
+ GDestroyNotify response_processor_context_free;
+ GVariant *result;
} AtSequenceContext;
static void
@@ -131,15 +131,14 @@ at_sequence_context_free (AtSequenceContext *ctx)
}
GVariant *
-mm_base_modem_at_sequence_full_finish (MMBaseModem *self,
- GAsyncResult *res,
- gpointer *response_processor_context,
- GError **error)
+mm_base_modem_at_sequence_full_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ gpointer *response_processor_context,
+ GError **error)
{
AtSequenceContext *ctx;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res),
- error))
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
ctx = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
@@ -153,65 +152,67 @@ mm_base_modem_at_sequence_full_finish (MMBaseModem *self,
}
static void
-at_sequence_parse_response (MMPortSerialAt *port,
- GAsyncResult *res,
+at_sequence_parse_response (MMPortSerialAt *port,
+ GAsyncResult *res,
AtSequenceContext *ctx)
{
- GVariant *result = NULL;
- GError *result_error = NULL;
- gboolean continue_sequence;
- GSimpleAsyncResult *simple;
- const gchar *response;
- GError *error = NULL;
+ MMBaseModemAtResponseProcessorResult processor_result;
+ GVariant *result = NULL;
+ GError *result_error = NULL;
+ GSimpleAsyncResult *simple;
+ const gchar *response;
+ GError *error = NULL;
response = mm_port_serial_at_command_finish (port, res, &error);
/* Cancelled? */
if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "AT sequence was cancelled");
- if (error)
- g_error_free (error);
+ g_simple_async_result_set_error (ctx->simple, G_IO_ERROR, G_IO_ERROR_CANCELLED, "AT sequence was cancelled");
+ g_clear_error (&error);
g_simple_async_result_complete (ctx->simple);
at_sequence_context_free (ctx);
return;
}
if (!ctx->current->response_processor)
- /* No need to process response, go on to next command */
- continue_sequence = TRUE;
+ processor_result = MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
else {
const MMBaseModemAtCommand *next = ctx->current + 1;
/* Response processor will tell us if we need to keep on the sequence */
- continue_sequence = !ctx->current->response_processor (
- ctx->self,
- ctx->response_processor_context,
- ctx->current->command,
- response,
- next->command ? FALSE : TRUE, /* Last command in sequence? */
- error,
- &result,
- &result_error);
- /* Were we told to abort the sequence? */
- if (result_error) {
- g_assert (result == NULL);
- g_simple_async_result_take_error (ctx->simple, result_error);
- g_simple_async_result_complete (ctx->simple);
- at_sequence_context_free (ctx);
- if (error)
- g_error_free (error);
- return;
+ processor_result = ctx->current->response_processor (ctx->self,
+ ctx->response_processor_context,
+ ctx->current->command,
+ response,
+ next->command ? FALSE : TRUE, /* Last command in sequence? */
+ error,
+ &result,
+ &result_error);
+ switch (processor_result) {
+ case MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE:
+ g_assert (!result && !result_error);
+ break;
+ case MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS:
+ g_assert (!result_error); /* result is optional */
+ break;
+ case MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE:
+ /* On failure, complete with error right away */
+ g_assert (!result && result_error); /* result is optional */
+ g_simple_async_result_take_error (ctx->simple, result_error);
+ g_simple_async_result_complete (ctx->simple);
+ at_sequence_context_free (ctx);
+ if (error)
+ g_error_free (error);
+ return;
+ default:
+ g_assert_not_reached ();
}
}
if (error)
g_error_free (error);
- if (continue_sequence) {
- g_assert (result == NULL);
+ if (processor_result == MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE) {
ctx->current++;
if (ctx->current->command) {
/* Schedule the next command in the probing group */
@@ -226,7 +227,6 @@ at_sequence_parse_response (MMPortSerialAt *port,
ctx);
return;
}
-
/* On last command, end. */
}
@@ -252,14 +252,14 @@ at_sequence_parse_response (MMPortSerialAt *port,
}
void
-mm_base_modem_at_sequence_full (MMBaseModem *self,
- MMPortSerialAt *port,
+mm_base_modem_at_sequence_full (MMBaseModem *self,
+ MMPortSerialAt *port,
const MMBaseModemAtCommand *sequence,
- gpointer response_processor_context,
- GDestroyNotify response_processor_context_free,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ gpointer response_processor_context,
+ GDestroyNotify response_processor_context_free,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
AtSequenceContext *ctx;
@@ -301,7 +301,7 @@ mm_base_modem_at_sequence_full (MMBaseModem *self,
ctx->current->command,
ctx->current->timeout,
FALSE,
- FALSE,
+ ctx->current->allow_cached,
ctx->cancellable,
(GAsyncReadyCallback)at_sequence_parse_response,
ctx);
@@ -356,76 +356,113 @@ mm_base_modem_at_sequence (MMBaseModem *self,
/*****************************************************************************/
/* Response processor helpers */
-gboolean
-mm_base_modem_response_processor_string (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+MMBaseModemAtResponseProcessorResult
+mm_base_modem_response_processor_string (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
if (error) {
+ *result = NULL;
*result_error = g_error_copy (error);
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
}
*result = g_variant_new_string (response);
- return TRUE;
+ *result_error = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
-gboolean
-mm_base_modem_response_processor_no_result (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+MMBaseModemAtResponseProcessorResult
+mm_base_modem_response_processor_no_result (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
if (error) {
+ *result = NULL;
*result_error = g_error_copy (error);
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
}
*result = NULL;
- return TRUE;
+ *result_error = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
-gboolean
-mm_base_modem_response_processor_no_result_continue (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+MMBaseModemAtResponseProcessorResult
+mm_base_modem_response_processor_no_result_continue (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
- if (error)
+ *result = NULL;
+
+ if (error) {
*result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
- /* Return FALSE so that we keep on with the next steps in the sequence */
- return FALSE;
+ *result_error = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
-gboolean
-mm_base_modem_response_processor_continue_on_error (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+MMBaseModemAtResponseProcessorResult
+mm_base_modem_response_processor_continue_on_error (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
- if (error)
- return FALSE;
-
*result = NULL;
- return TRUE;
+ *result_error = NULL;
+
+ return (error ?
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE :
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS);
+}
+
+MMBaseModemAtResponseProcessorResult
+mm_base_modem_response_processor_string_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
+{
+ if (error) {
+ *result = NULL;
+
+ /* Ignore AT errors (ie, ERROR or CMx ERROR) */
+ if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command) {
+
+ *result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
+
+ *result_error = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
+
+ *result = g_variant_new_string (response);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
/*****************************************************************************/
@@ -483,9 +520,7 @@ at_command_ready (MMPortSerialAt *port,
/* Cancelled? */
if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
+ g_simple_async_result_set_error (ctx->result, G_IO_ERROR, G_IO_ERROR_CANCELLED,
"AT command was cancelled");
if (error)
g_error_free (error);
@@ -620,3 +655,9 @@ mm_base_modem_at_command_raw (MMBaseModem *self,
{
_at_command (self, command, timeout, allow_cached, TRUE, callback, user_data);
}
+
+void
+mm_base_modem_at_command_alloc_clear (MMBaseModemAtCommandAlloc *command)
+{
+ g_free (command->command);
+}
diff --git a/src/mm-base-modem-at.h b/src/mm-base-modem-at.h
index e155c908..770c0e4b 100644
--- a/src/mm-base-modem-at.h
+++ b/src/mm-base-modem-at.h
@@ -21,19 +21,25 @@
#include "mm-base-modem.h"
#include "mm-port-serial-at.h"
+typedef enum {
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE,
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS,
+ MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE,
+} MMBaseModemAtResponseProcessorResult;
+
/*
* The expected result depends on the specific operation, so the GVariant
* created by the response processor needs to match the one expected in
* finish().
*
- * TRUE must be returned when the operation is to be considered successful,
+ * SUCCESS must be returned when the operation is to be considered successful,
* and a result may be given.
*
- * FALSE must be returned when:
- * - A GError is propagated into result_error, which will be treated as a
- * critical error and therefore the operation will be aborted.
- * - When no result_error is given, to instruct the operation to go on with
- * the next scheduled command.
+ * FAILURE must be returned when a GError is propagated into result_error,
+ * which will be treated as a critical error and therefore the operation will be aborted.
+ *
+ * CONTINUE must be returned when neither result nor result_error are given and
+ * the operation should go on with the next scheduled command.
*
* This setup, therefore allows:
* - Running a single command and processing its result.
@@ -42,19 +48,19 @@
* - Running a set of N commands out of M (N<M), where the global result is
* obtained without having executed all configured commands.
*/
-typedef gboolean (* MMBaseModemAtResponseProcessor) (MMBaseModem *self,
- gpointer response_processor_context,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error);
-
-/* Struct to configure AT command operations */
+typedef MMBaseModemAtResponseProcessorResult (* MMBaseModemAtResponseProcessor) (MMBaseModem *self,
+ gpointer response_processor_context,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
+
+/* Struct to configure AT command operations (constant) */
typedef struct {
/* The AT command */
- gchar *command;
+ const gchar *command;
/* Timeout of the command, in seconds */
guint timeout;
/* Flag to allow cached replies */
@@ -93,42 +99,82 @@ GVariant *mm_base_modem_at_sequence_full_finish (MMBaseModem *self,
/* Common helper response processors */
-/* Every string received as response, will be set as result */
-gboolean mm_base_modem_response_processor_string (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error);
-/* Just abort if error without result set, otherwise finish sequence */
-gboolean mm_base_modem_response_processor_no_result (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error);
-/* Just abort if error without result set, otherwise continue sequence */
-gboolean mm_base_modem_response_processor_no_result_continue (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error);
-/* If error, continue sequence, otherwise finish it */
-gboolean mm_base_modem_response_processor_continue_on_error (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error);
+/*
+ * Response processor for commands that are treated as MANDATORY, where a
+ * failure in the command triggers a failure in the sequence. If successful,
+ * provides the output result as a STRING.
+ */
+MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_string (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
+
+/*
+ * Response processor for commands that are treated as MANDATORY, where a
+ * failure in the command triggers a failure in the sequence. If successful,
+ * provides the output result as a BOOLEAN.
+ */
+MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_no_result (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
+
+/*
+ * Response processor for commands that are treated as MANDATORY, where a
+ * failure in the command triggers a failure in the sequence. If successful,
+ * it will run the next command in the sequence.
+ *
+ * E.g. used when we provide a list of commands and we want to run all of
+ * them successfully, and fail the sequence if one of them fails.
+ */
+MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_no_result_continue (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
+
+/*
+ * Response processor for commands that are treated as OPTIONAL, where a
+ * failure in the command doesn't trigger a failure in the sequence. If
+ * successful, it finishes the sequence.
+ *
+ * E.g. used when we provide a list of commands and we want to stop
+ * as soon as one of them doesn't fail.
+ */
+MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_continue_on_error (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
+
+/*
+ * Response processor for commands that are partialy treated as OPTIONAL, where
+ * a failure in the command triggers a failure in the sequence only for non-AT
+ * generic errors. If successful, it finishes the sequence with the response of
+ * the command which didn't fail.
+ */
+MMBaseModemAtResponseProcessorResult mm_base_modem_response_processor_string_ignore_at_errors (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error);
/* Generic AT command handling, using the best AT port available and without
* explicit cancellations. */
@@ -164,4 +210,24 @@ const gchar *mm_base_modem_at_command_full_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error);
+/******************************************************************************/
+/* Support for MMBaseModemAtCommand with heap allocated contents */
+
+/* Exactly same format as MMBaseModemAtCommand, just without
+ * a constant command string. */
+typedef struct {
+ gchar *command;
+ guint timeout;
+ gboolean allow_cached;
+ MMBaseModemAtResponseProcessor response_processor;
+} MMBaseModemAtCommandAlloc;
+
+G_STATIC_ASSERT (sizeof (MMBaseModemAtCommandAlloc) == sizeof (MMBaseModemAtCommand));
+G_STATIC_ASSERT (G_STRUCT_OFFSET (MMBaseModemAtCommandAlloc, command) == G_STRUCT_OFFSET (MMBaseModemAtCommand, command));
+G_STATIC_ASSERT (G_STRUCT_OFFSET (MMBaseModemAtCommandAlloc, timeout) == G_STRUCT_OFFSET (MMBaseModemAtCommand, timeout));
+G_STATIC_ASSERT (G_STRUCT_OFFSET (MMBaseModemAtCommandAlloc, allow_cached) == G_STRUCT_OFFSET (MMBaseModemAtCommand, allow_cached));
+G_STATIC_ASSERT (G_STRUCT_OFFSET (MMBaseModemAtCommandAlloc, response_processor) == G_STRUCT_OFFSET (MMBaseModemAtCommand, response_processor));
+
+void mm_base_modem_at_command_alloc_clear (MMBaseModemAtCommandAlloc *command);
+
#endif /* MM_BASE_MODEM_AT_H */
diff --git a/src/mm-base-modem.c b/src/mm-base-modem.c
index 84ecf8d2..9c9a2a61 100644
--- a/src/mm-base-modem.c
+++ b/src/mm-base-modem.c
@@ -21,21 +21,32 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
-#include <gudev/gudev.h>
#include <ModemManager.h>
+#include <ModemManager-tags.h>
+
#include <mm-errors-types.h>
#include <mm-gdbus-modem.h>
#include "mm-context.h"
#include "mm-base-modem.h"
+#if defined WITH_QRTR
+#include "mm-kernel-device-qrtr.h"
+#endif
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-port-enums-types.h"
#include "mm-serial-parsers.h"
#include "mm-modem-helpers.h"
-G_DEFINE_ABSTRACT_TYPE (MMBaseModem, mm_base_modem, MM_GDBUS_TYPE_OBJECT_SKELETON);
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MMBaseModem, mm_base_modem, MM_GDBUS_TYPE_OBJECT_SKELETON,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+/* If we get 10 consecutive timeouts in a serial port, we consider the modem
+ * invalid and we request re-probing. */
+#define DEFAULT_MAX_TIMEOUTS 10
enum {
PROP_0,
@@ -47,14 +58,25 @@ enum {
PROP_VENDOR_ID,
PROP_PRODUCT_ID,
PROP_CONNECTION,
+ PROP_REPROBE,
+ PROP_DATA_NET_SUPPORTED,
+ PROP_DATA_TTY_SUPPORTED,
PROP_LAST
};
+enum {
+ SIGNAL_LINK_PORT_GRABBED,
+ SIGNAL_LINK_PORT_RELEASED,
+ SIGNAL_LAST
+};
+
static GParamSpec *properties[PROP_LAST];
+static guint signals[SIGNAL_LAST];
struct _MMBaseModemPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
+ guint dbus_id;
/* Modem-wide cancellable. If it ever gets cancelled, no further operations
* should be done by the modem. */
@@ -70,6 +92,7 @@ struct _MMBaseModemPrivate {
gboolean hotplugged;
gboolean valid;
+ gboolean reprobe;
guint max_timeouts;
@@ -81,13 +104,23 @@ struct _MMBaseModemPrivate {
MMPortSerialAt *primary;
MMPortSerialAt *secondary;
MMPortSerialQcdm *qcdm;
- GList *data;
+
+ GList *data;
+ gboolean data_net_supported;
+ gboolean data_tty_supported;
/* GPS-enabled modems will have an AT port for control, and a raw serial
* port to receive all GPS traces */
MMPortSerialAt *gps_control;
MMPortSerialGps *gps;
+ /* Some audio-capable devices will have a port for audio specifically */
+ MMPortSerial *audio;
+
+ /* Support for parallel enable/disable operations */
+ GList *enable_tasks;
+ GList *disable_tasks;
+
#if defined WITH_QMI
/* QMI ports */
GList *qmi;
@@ -97,358 +130,664 @@ struct _MMBaseModemPrivate {
/* MBIM ports */
GList *mbim;
#endif
+
+ /* Additional port links grabbed after having
+ * organized ports */
+ GHashTable *link_ports;
};
-static gchar *
-get_hash_key (const gchar *subsys,
- const gchar *name)
+guint
+mm_base_modem_get_dbus_id (MMBaseModem *self)
{
- return g_strdup_printf ("%s%s", subsys, name);
+ return self->priv->dbus_id;
}
-MMPort *
-mm_base_modem_get_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name)
+/******************************************************************************/
+
+static void
+serial_port_timed_out_cb (MMPortSerial *port,
+ guint n_consecutive_timeouts,
+ MMBaseModem *self)
+{
+ /* If reached the maximum number of timeouts, invalidate modem */
+ if (n_consecutive_timeouts >= self->priv->max_timeouts) {
+ mm_obj_err (self, "port %s timed out %u consecutive times, marking modem as invalid",
+ mm_port_get_device (MM_PORT (port)),
+ n_consecutive_timeouts);
+ g_cancellable_cancel (self->priv->cancellable);
+ return;
+ }
+
+ if (n_consecutive_timeouts > 1)
+ mm_obj_warn (self, "port %s timed out %u consecutive times",
+ mm_port_get_device (MM_PORT (port)),
+ n_consecutive_timeouts);
+}
+
+static MMPort *
+base_modem_create_ignored_port (MMBaseModem *self,
+ const gchar *name)
{
- MMPort *port;
- gchar *key;
+ return MM_PORT (g_object_new (MM_TYPE_PORT,
+ MM_PORT_DEVICE, name,
+ MM_PORT_TYPE, MM_PORT_TYPE_IGNORED,
+ NULL));
+}
- g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
- g_return_val_if_fail (name != NULL, NULL);
- g_return_val_if_fail (subsys != NULL, NULL);
+static MMPort *
+base_modem_create_net_port (MMBaseModem *self,
+ const gchar *name)
+{
+ return MM_PORT (mm_port_net_new (name));
+}
- /* Only 'net' or 'tty' should be given */
- g_return_val_if_fail (g_str_equal (subsys, "net") ||
- g_str_equal (subsys, "tty"),
- NULL);
+static MMPort *
+base_modem_create_tty_port (MMBaseModem *self,
+ const gchar *name,
+ MMKernelDevice *kernel_device,
+ MMPortType ptype)
+{
+ MMPort *port = NULL;
+ const gchar *flow_control_tag;
- key = get_hash_key (subsys, name);
- port = g_hash_table_lookup (self->priv->ports, key);
- g_free (key);
+ if (ptype == MM_PORT_TYPE_QCDM)
+ port = MM_PORT (mm_port_serial_qcdm_new (name, MM_PORT_SUBSYS_TTY));
+ else if (ptype == MM_PORT_TYPE_GPS)
+ port = MM_PORT (mm_port_serial_gps_new (name));
+ else if (ptype == MM_PORT_TYPE_AUDIO)
+ port = MM_PORT (mm_port_serial_new (name, ptype));
+ else if (ptype == MM_PORT_TYPE_AT)
+ port = MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_TTY));
+
+ if (!port)
+ return NULL;
+
+ /* Enable port timeout checks if requested to do so */
+ if (self->priv->max_timeouts > 0)
+ g_signal_connect (port,
+ "timed-out",
+ G_CALLBACK (serial_port_timed_out_cb),
+ self);
+
+ /* Optional user-provided baudrate */
+ if (mm_kernel_device_has_property (kernel_device, ID_MM_TTY_BAUDRATE))
+ g_object_set (port,
+ MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (kernel_device, ID_MM_TTY_BAUDRATE),
+ NULL);
+
+ /* Optional user-provided flow control */
+ flow_control_tag = mm_kernel_device_get_property (kernel_device, ID_MM_TTY_FLOW_CONTROL);
+ if (flow_control_tag) {
+ MMFlowControl flow_control;
+ g_autoptr(GError) inner_error = NULL;
+
+ flow_control = mm_flow_control_from_string (flow_control_tag, &inner_error);
+ if (flow_control != MM_FLOW_CONTROL_UNKNOWN)
+ g_object_set (port,
+ MM_PORT_SERIAL_FLOW_CONTROL, flow_control,
+ NULL);
+ else
+ mm_obj_warn (self, "unsupported flow control settings in port %s: %s",
+ name, inner_error->message);
+ }
return port;
}
-gboolean
-mm_base_modem_owns_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name)
+static MMPort *
+base_modem_create_usbmisc_port (MMBaseModem *self,
+ const gchar *name,
+ MMPortType ptype)
{
- return !!mm_base_modem_get_port (self, subsys, name);
+#if defined WITH_QMI
+ if (ptype == MM_PORT_TYPE_QMI)
+ return MM_PORT (mm_port_qmi_new (name, MM_PORT_SUBSYS_USBMISC));
+#endif
+#if defined WITH_MBIM
+ if (ptype == MM_PORT_TYPE_MBIM)
+ return MM_PORT (mm_port_mbim_new (name, MM_PORT_SUBSYS_USBMISC));
+#endif
+ if (ptype == MM_PORT_TYPE_AT)
+ return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_USBMISC));
+ return NULL;
}
-static void
-serial_port_timed_out_cb (MMPortSerial *port,
- guint n_consecutive_timeouts,
- gpointer user_data)
+static MMPort *
+base_modem_create_rpmsg_port (MMBaseModem *self,
+ const gchar *name,
+ MMPortType ptype)
{
- MMBaseModem *self = (MM_BASE_MODEM (user_data));
+#if defined WITH_QMI
+ if (ptype == MM_PORT_TYPE_QMI)
+ return MM_PORT (mm_port_qmi_new (name, MM_PORT_SUBSYS_RPMSG));
+#endif
+ if (ptype == MM_PORT_TYPE_AT)
+ return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_RPMSG));
+ return NULL;
+}
- if (self->priv->max_timeouts > 0 &&
- n_consecutive_timeouts >= self->priv->max_timeouts) {
- mm_warn ("(%s/%s) port timed out %u times, marking modem '%s' as disabled",
- mm_port_type_get_string (mm_port_get_port_type (MM_PORT (port))),
- mm_port_get_device (MM_PORT (port)),
- n_consecutive_timeouts,
- g_dbus_object_get_object_path (G_DBUS_OBJECT (self)));
+#if defined WITH_QRTR
+static MMPort *
+base_modem_create_qrtr_port (MMBaseModem *self,
+ const gchar *name,
+ MMKernelDevice *kernel_device,
+ MMPortType ptype)
+{
+ if (ptype == MM_PORT_TYPE_QMI) {
+ g_autoptr(QrtrNode) node = NULL;
- /* Only set action to invalidate modem if not already done */
- g_cancellable_cancel (self->priv->cancellable);
+ g_assert (MM_IS_KERNEL_DEVICE_QRTR (kernel_device));
+ node = mm_kernel_device_qrtr_get_node (MM_KERNEL_DEVICE_QRTR (kernel_device));
+ return MM_PORT (mm_port_qmi_new_from_node (name, node));
}
+ return NULL;
}
+#endif
-gboolean
-mm_base_modem_grab_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name,
- const gchar *parent_path,
- MMPortType ptype,
- MMPortSerialAtFlag at_pflags,
- GError **error)
+static MMPort *
+base_modem_create_wwan_port (MMBaseModem *self,
+ const gchar *name,
+ MMPortType ptype)
{
- MMPort *port;
- gchar *key;
+#if defined WITH_QMI
+ if (ptype == MM_PORT_TYPE_QMI)
+ return MM_PORT (mm_port_qmi_new (name, MM_PORT_SUBSYS_WWAN));
+#endif
- g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE);
- g_return_val_if_fail (subsys != NULL, FALSE);
- g_return_val_if_fail (name != NULL, FALSE);
-
- /* Only allow 'tty', 'net' and 'cdc-wdm' ports */
- if (!g_str_equal (subsys, "net") &&
- !g_str_equal (subsys, "tty") &&
- !(g_str_has_prefix (subsys, "usb") && g_str_has_prefix (name, "cdc-wdm")) &&
- !g_str_equal (subsys, "virtual")) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot add port '%s/%s', unhandled subsystem",
- subsys,
- name);
- return FALSE;
- }
+#if defined WITH_MBIM
+ if (ptype == MM_PORT_TYPE_MBIM)
+ return MM_PORT (mm_port_mbim_new (name, MM_PORT_SUBSYS_WWAN));
+#endif
+
+ if (ptype == MM_PORT_TYPE_QCDM)
+ return MM_PORT (mm_port_serial_qcdm_new (name, MM_PORT_SUBSYS_WWAN));
+
+ if (ptype == MM_PORT_TYPE_AT)
+ return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_WWAN));
+
+ return NULL;
+}
+
+static MMPort *
+base_modem_create_virtual_port (MMBaseModem *self,
+ const gchar *name)
+{
+ return MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_UNIX));
+}
+
+static MMPort *
+base_modem_internal_grab_port (MMBaseModem *self,
+ MMKernelDevice *kernel_device,
+ gboolean link_port,
+ MMPortType ptype,
+ MMPortSerialAtFlag at_pflags,
+ GError **error)
+{
+ MMPort *port;
+ const gchar *subsys;
+ const gchar *name;
+ g_autofree gchar *key = NULL;
+
+ subsys = mm_kernel_device_get_subsystem (kernel_device);
+ name = mm_kernel_device_get_name (kernel_device);
/* Check whether we already have it stored */
- key = get_hash_key (subsys, name);
+ key = g_strdup_printf ("%s%s", subsys, name);
port = g_hash_table_lookup (self->priv->ports, key);
if (port) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot add port '%s/%s', already exists",
- subsys,
- name);
- g_free (key);
- return FALSE;
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot add port '%s/%s', already exists", subsys, name);
+ return NULL;
}
/* Explicitly ignored ports, grab them but explicitly flag them as ignored
* right away, all the same way (i.e. regardless of subsystem). */
- if (ptype == MM_PORT_TYPE_IGNORED) {
- port = MM_PORT (g_object_new (MM_TYPE_PORT,
- MM_PORT_DEVICE, name,
- MM_PORT_TYPE, MM_PORT_TYPE_IGNORED,
- NULL));
- }
- /* Serial ports... */
- else if (g_str_equal (subsys, "tty")) {
- if (ptype == MM_PORT_TYPE_QCDM)
- /* QCDM port */
- port = MM_PORT (mm_port_serial_qcdm_new (name));
- else if (ptype == MM_PORT_TYPE_AT) {
- /* AT port */
- port = MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_TTY));
-
- /* Set common response parser */
- mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
- /* Store flags already */
- mm_port_serial_at_set_flags (MM_PORT_SERIAL_AT (port), at_pflags);
- } else if (ptype == MM_PORT_TYPE_GPS) {
- /* Raw GPS port */
- port = MM_PORT (mm_port_serial_gps_new (name));
- } else {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot add port '%s/%s', unhandled serial type",
- subsys,
- name);
- g_free (key);
- return FALSE;
- }
+ if (ptype == MM_PORT_TYPE_IGNORED)
+ port = base_modem_create_ignored_port (self, name);
+ else if (g_str_equal (subsys, "net"))
+ port = base_modem_create_net_port (self, name);
+ else if (g_str_equal (subsys, "tty"))
+ port = base_modem_create_tty_port (self, name, kernel_device, ptype);
+ else if (g_str_equal (subsys, "usbmisc"))
+ port = base_modem_create_usbmisc_port (self, name, ptype);
+ else if (g_str_equal (subsys, "rpmsg"))
+ port = base_modem_create_rpmsg_port (self, name, ptype);
+#if defined WITH_QRTR
+ else if (g_str_equal (subsys, "qrtr"))
+ port = base_modem_create_qrtr_port (self, name, kernel_device, ptype);
+#endif
+ else if (g_str_equal (subsys, "virtual"))
+ port = base_modem_create_virtual_port (self, name);
+ else if (g_str_equal (subsys, "wwan"))
+ port = base_modem_create_wwan_port (self, name, ptype);
- /* For serial ports, enable port timeout checks */
- g_signal_connect (port,
- "timed-out",
- G_CALLBACK (serial_port_timed_out_cb),
- self);
- }
- /* Net ports... */
- else if (g_str_equal (subsys, "net")) {
- port = MM_PORT (g_object_new (MM_TYPE_PORT,
- MM_PORT_DEVICE, name,
- MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET,
- MM_PORT_TYPE, MM_PORT_TYPE_NET,
- NULL));
+ if (!port) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot add port '%s/%s', unhandled port type", subsys, name);
+ return NULL;
}
- /* cdc-wdm ports... */
- else if (g_str_has_prefix (subsys, "usb") &&
- g_str_has_prefix (name, "cdc-wdm")) {
-#if defined WITH_QMI
- if (ptype == MM_PORT_TYPE_QMI)
- port = MM_PORT (mm_port_qmi_new (name));
-#endif
-#if defined WITH_MBIM
- if (!port && ptype == MM_PORT_TYPE_MBIM)
- port = MM_PORT (mm_port_mbim_new (name));
-#endif
- /* Non-serial AT port */
- if (!port && ptype == MM_PORT_TYPE_AT) {
- port = MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_USB));
-
- /* Set common response parser */
- mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
- mm_serial_parser_v1_parse,
- mm_serial_parser_v1_new (),
- mm_serial_parser_v1_destroy);
- /* Store flags already */
- mm_port_serial_at_set_flags (MM_PORT_SERIAL_AT (port), at_pflags);
- }
+ /* Store kernel device */
+ g_object_set (port, MM_PORT_KERNEL_DEVICE, kernel_device, NULL);
- if (!port) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot add port '%s/%s', unsupported",
- subsys,
- name);
- g_free (key);
- return FALSE;
- }
- }
- /* Virtual ports... */
- else if (g_str_equal (subsys, "virtual")) {
- port = MM_PORT (mm_port_serial_at_new (name, MM_PORT_SUBSYS_UNIX));
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (port), mm_log_object_get_id (MM_LOG_OBJECT (self)));
- /* Set common response parser */
+ /* Common setup for all AT ports from all subsystems */
+ if (MM_IS_PORT_SERIAL_AT (port)) {
mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_new (),
mm_serial_parser_v1_destroy);
- /* Store flags already */
+ /* Prefer plugin-provided flags to the generic ones */
+ if (at_pflags == MM_PORT_SERIAL_AT_FLAG_NONE) {
+ if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PRIMARY)) {
+ mm_obj_dbg (port, "AT port flagged as primary");
+ at_pflags = MM_PORT_SERIAL_AT_FLAG_PRIMARY;
+ } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_SECONDARY)) {
+ mm_obj_dbg (port, "AT port flagged as secondary");
+ at_pflags = MM_PORT_SERIAL_AT_FLAG_SECONDARY;
+ } else if (mm_kernel_device_get_property_as_boolean (kernel_device, ID_MM_PORT_TYPE_AT_PPP)) {
+ mm_obj_dbg (port, "AT port flagged as PPP");
+ at_pflags = MM_PORT_SERIAL_AT_FLAG_PPP;
+ }
+ }
+ /* The plugin may specify NONE_NO_GENERIC to avoid the generic
+ * port type hints from being applied. */
+ if (at_pflags == MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC)
+ at_pflags = MM_PORT_SERIAL_AT_FLAG_NONE;
+
mm_port_serial_at_set_flags (MM_PORT_SERIAL_AT (port), at_pflags);
}
- else
- /* We already filter out before all non-tty, non-net, non-cdc-wdm ports */
- g_assert_not_reached();
-
- mm_dbg ("(%s) type '%s' claimed by %s",
- name,
- mm_port_type_get_string (ptype),
- mm_base_modem_get_device (self));
/* Add it to the tracking HT.
* Note: 'key' and 'port' now owned by the HT. */
- g_hash_table_insert (self->priv->ports, key, port);
+ if (link_port)
+ g_hash_table_insert (self->priv->link_ports, g_steal_pointer (&key), port);
+ else
+ g_hash_table_insert (self->priv->ports, g_steal_pointer (&key), port);
+ return port;
+}
- /* Store parent path */
- g_object_set (port,
- MM_PORT_PARENT_PATH, parent_path,
- NULL);
+gboolean
+mm_base_modem_grab_port (MMBaseModem *self,
+ MMKernelDevice *kernel_device,
+ MMPortType ptype,
+ MMPortSerialAtFlag at_pflags,
+ GError **error)
+{
+ if (!base_modem_internal_grab_port (self, kernel_device, FALSE, ptype, at_pflags, error))
+ return FALSE;
+ mm_obj_dbg (self, "port '%s/%s' grabbed",
+ mm_kernel_device_get_subsystem (kernel_device),
+ mm_kernel_device_get_name (kernel_device));
return TRUE;
}
-void
-mm_base_modem_release_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name)
+/******************************************************************************/
+
+gboolean
+mm_base_modem_grab_link_port (MMBaseModem *self,
+ MMKernelDevice *kernel_device,
+ GError **error)
{
- gchar *key;
- MMPort *port;
- GList *l;
+ const gchar *subsystem;
+ const gchar *name;
+ MMPort *port;
- g_return_if_fail (MM_IS_BASE_MODEM (self));
- g_return_if_fail (name != NULL);
- g_return_if_fail (subsys != NULL);
+ /* To simplify things, we only support NET link ports at this point */
+ subsystem = mm_kernel_device_get_subsystem (kernel_device);
+ name = mm_kernel_device_get_name (kernel_device);
- if (!g_str_equal (subsys, "tty") &&
- !g_str_equal (subsys, "net") &&
- !(g_str_has_prefix (subsys, "usb") && g_str_has_prefix (name, "cdc-wdm")) &&
- !g_str_equal (subsys, "virtual"))
- return;
+ if (!g_str_equal (subsystem, "net")) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot add port '%s/%s', unexpected link port subsystem", subsystem, name);
+ return FALSE;
+ }
- key = get_hash_key (subsys, name);
+ /* all the newly added link ports will NOT be 'organized'; i.e. they won't
+ * be available as 'data ports' in the modem, but they can be looked up
+ * by name */
+ port = base_modem_internal_grab_port (self,
+ kernel_device,
+ TRUE,
+ MM_PORT_TYPE_NET,
+ MM_PORT_SERIAL_AT_FLAG_NONE,
+ error);
+ if (!port)
+ return FALSE;
- /* Find the port */
- port = g_hash_table_lookup (self->priv->ports, key);
+
+ mm_obj_dbg (self, "link port '%s/%s' grabbed", subsystem, name);
+ g_signal_emit (self, signals[SIGNAL_LINK_PORT_GRABBED], 0, port);
+ return TRUE;
+}
+
+gboolean
+mm_base_modem_release_link_port (MMBaseModem *self,
+ const gchar *subsystem,
+ const gchar *name,
+ GError **error)
+{
+ g_autofree gchar *key = NULL;
+ MMPort *port;
+
+ key = g_strdup_printf ("%s%s", subsystem, name);
+ port = g_hash_table_lookup (self->priv->link_ports, key);
if (!port) {
- mm_warn ("(%s/%s): cannot release port, not found",
- subsys, name);
- g_free (key);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot release link port '%s/%s', not grabbed", subsystem, name);
+ return FALSE;
+ }
+
+ /* make sure the port object is valid during the port release signal */
+ g_object_ref (port);
+ g_hash_table_remove (self->priv->link_ports, key);
+ mm_obj_dbg (self, "link port '%s/%s' released", subsystem, name);
+ g_signal_emit (self, signals[SIGNAL_LINK_PORT_RELEASED], 0, port);
+ g_object_unref (port);
+ return TRUE;
+}
+
+/******************************************************************************/
+
+typedef struct {
+ gchar *name;
+ gulong link_port_grabbed_id;
+ guint timeout_id;
+} WaitLinkPortContext;
+
+static void
+wait_link_port_context_free (WaitLinkPortContext *ctx)
+{
+ g_assert (!ctx->link_port_grabbed_id);
+ g_assert (!ctx->timeout_id);
+ g_free (ctx->name);
+ g_slice_free (WaitLinkPortContext, ctx);
+}
+
+MMPort *
+mm_base_modem_wait_link_port_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static gboolean
+wait_link_port_timeout_cb (GTask *task)
+{
+ WaitLinkPortContext *ctx;
+ MMBaseModem *self;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->timeout_id = 0;
+ g_signal_handler_disconnect (self, ctx->link_port_grabbed_id);
+ ctx->link_port_grabbed_id = 0;
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Timed out waiting for link port 'net/%s'",
+ ctx->name);
+ g_object_unref (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+wait_link_port_grabbed_cb (MMBaseModem *self,
+ MMPort *link_port,
+ GTask *task)
+{
+ WaitLinkPortContext *ctx;
+ MMPortSubsys link_port_subsystem;
+ const gchar *link_port_name;
+
+ ctx = g_task_get_task_data (task);
+
+ link_port_subsystem = mm_port_get_subsys (link_port);
+ link_port_name = mm_port_get_device (link_port);
+
+ if (link_port_subsystem != MM_PORT_SUBSYS_NET) {
+ mm_obj_warn (self, "unexpected link port subsystem grabbed: %s/%s",
+ mm_port_subsys_get_string (link_port_subsystem),
+ link_port_name);
return;
}
- if (port == (MMPort *)self->priv->primary) {
- /* Cancel modem-wide cancellable; no further actions can be done
- * without a primary port. */
- g_cancellable_cancel (self->priv->cancellable);
+ if (g_strcmp0 (link_port_name, ctx->name) != 0)
+ return;
+
+ /* we got it! */
+
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+ g_signal_handler_disconnect (self, ctx->link_port_grabbed_id);
+ ctx->link_port_grabbed_id = 0;
- g_clear_object (&self->priv->primary);
+ g_task_return_pointer (task, g_object_ref (link_port), g_object_unref);
+ g_object_unref (task);
+}
+
+void
+mm_base_modem_wait_link_port (MMBaseModem *self,
+ const gchar *subsystem,
+ const gchar *name,
+ guint timeout_ms,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ WaitLinkPortContext *ctx;
+ GTask *task;
+ g_autofree gchar *key = NULL;
+ MMPort *port;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!g_str_equal (subsystem, "net")) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot wait for port '%s/%s', unexpected link port subsystem", subsystem, name);
+ g_object_unref (task);
+ return;
}
- l = g_list_find (self->priv->data, port);
- if (l) {
- g_object_unref (l->data);
- self->priv->data = g_list_delete_link (self->priv->data, l);
+ key = g_strdup_printf ("%s%s", subsystem, name);
+ port = g_hash_table_lookup (self->priv->link_ports, key);
+ if (port) {
+ mm_obj_dbg (self, "no need to wait for port '%s/%s': already grabbed", subsystem, name);
+ g_task_return_pointer (task, g_object_ref (port), g_object_unref);
+ g_object_unref (task);
+ return;
}
- if (port == (MMPort *)self->priv->secondary)
- g_clear_object (&self->priv->secondary);
+ ctx = g_slice_new0 (WaitLinkPortContext);
+ ctx->name = g_strdup (name);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)wait_link_port_context_free);
- if (port == (MMPort *)self->priv->qcdm)
- g_clear_object (&self->priv->qcdm);
+ /* task ownership shared between timeout and signal handler */
+ ctx->timeout_id = g_timeout_add (timeout_ms,
+ (GSourceFunc) wait_link_port_timeout_cb,
+ task);
+ ctx->link_port_grabbed_id = g_signal_connect (self,
+ MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED,
+ G_CALLBACK (wait_link_port_grabbed_cb),
+ task);
- if (port == (MMPort *)self->priv->gps_control)
- g_clear_object (&self->priv->gps_control);
+ mm_obj_dbg (self, "waiting for port '%s/%s'...", subsystem, name);
+}
- if (port == (MMPort *)self->priv->gps)
- g_clear_object (&self->priv->gps);
+/******************************************************************************/
-#if defined WITH_QMI
- l = g_list_find (self->priv->qmi, port);
- if (l) {
- g_object_unref (l->data);
- self->priv->qmi = g_list_delete_link (self->priv->qmi, l);
- }
-#endif
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
-#if defined WITH_MBIM
- l = g_list_find (self->priv->mbim, port);
- if (l) {
- g_object_unref (l->data);
- self->priv->mbim = g_list_delete_link (self->priv->mbim, l);
+gboolean
+mm_base_modem_sync_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sync_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_BASE_MODEM_GET_CLASS (self)->sync_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_base_modem_sync (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!MM_BASE_MODEM_GET_CLASS (self)->sync ||
+ !MM_BASE_MODEM_GET_CLASS (self)->sync_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Suspend/resume quick synchronization unsupported");
+ g_object_unref (task);
+ return;
}
-#endif
- /* Remove it from the tracking HT */
- mm_dbg ("(%s/%s) type %s released from %s",
- subsys,
- name,
- mm_port_type_get_string (mm_port_get_port_type (port)),
- mm_port_get_device (port));
- g_hash_table_remove (self->priv->ports, key);
- g_free (key);
+ MM_BASE_MODEM_GET_CLASS (self)->sync (self,
+ (GAsyncReadyCallback) sync_ready,
+ task);
}
+#endif /* WITH_SYSTEMD_SUSPEND_RESUME */
+
+/******************************************************************************/
+
gboolean
-mm_base_modem_disable_finish (MMBaseModem *self,
- GAsyncResult *res,
- GError **error)
+mm_base_modem_disable_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_ready (MMBaseModem *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ GList *l;
+ GList *disable_tasks;
+
+ g_assert (self->priv->disable_tasks);
+ disable_tasks = self->priv->disable_tasks;
+ self->priv->disable_tasks = NULL;
+
+ MM_BASE_MODEM_GET_CLASS (self)->disable_finish (self, res, &error);
+ for (l = disable_tasks; l; l = g_list_next (l)) {
+ if (error)
+ g_task_return_error (G_TASK (l->data), g_error_copy (error));
+ else
+ g_task_return_boolean (G_TASK (l->data), TRUE);
+ }
+ g_clear_error (&error);
+
+ g_list_free_full (disable_tasks, g_object_unref);
}
void
-mm_base_modem_disable (MMBaseModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_base_modem_disable (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
+ gboolean run_disable;
+
g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->disable_finish != NULL);
+ /* If the list of disable tasks is empty, we need to run */
+ run_disable = !self->priv->disable_tasks;
+
+ /* Store task */
+ task = g_task_new (self, self->priv->cancellable, callback, user_data);
+ self->priv->disable_tasks = g_list_append (self->priv->disable_tasks, task);
+
+ if (!run_disable)
+ return;
+
MM_BASE_MODEM_GET_CLASS (self)->disable (
self,
self->priv->cancellable,
- callback,
- user_data);
+ (GAsyncReadyCallback) disable_ready,
+ NULL);
}
gboolean
-mm_base_modem_enable_finish (MMBaseModem *self,
- GAsyncResult *res,
- GError **error)
+mm_base_modem_enable_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+enable_ready (MMBaseModem *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ GList *l;
+ GList *enable_tasks;
+
+ g_assert (self->priv->enable_tasks);
+ enable_tasks = self->priv->enable_tasks;
+ self->priv->enable_tasks = NULL;
+
+ MM_BASE_MODEM_GET_CLASS (self)->enable_finish (self, res, &error);
+ for (l = enable_tasks; l; l = g_list_next (l)) {
+ if (error)
+ g_task_return_error (G_TASK (l->data), g_error_copy (error));
+ else
+ g_task_return_boolean (G_TASK (l->data), TRUE);
+ }
+ g_clear_error (&error);
+
+ g_list_free_full (enable_tasks, g_object_unref);
}
void
-mm_base_modem_enable (MMBaseModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_base_modem_enable (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ GTask *task;
+ gboolean run_enable;
+
g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable != NULL);
g_assert (MM_BASE_MODEM_GET_CLASS (self)->enable_finish != NULL);
+ /* If the list of enable tasks is empty, we need to run */
+ run_enable = !self->priv->enable_tasks;
+
+ /* Store task */
+ task = g_task_new (self, self->priv->cancellable, callback, user_data);
+ self->priv->enable_tasks = g_list_append (self->priv->enable_tasks, task);
+
+ if (!run_enable)
+ return;
+
MM_BASE_MODEM_GET_CLASS (self)->enable (
self,
self->priv->cancellable,
- callback,
- user_data);
+ (GAsyncReadyCallback) enable_ready,
+ NULL);
}
gboolean
@@ -506,6 +845,23 @@ mm_base_modem_set_valid (MMBaseModem *self,
}
}
+void
+mm_base_modem_set_reprobe (MMBaseModem *self,
+ gboolean reprobe)
+{
+ g_return_if_fail (MM_IS_BASE_MODEM (self));
+
+ self->priv->reprobe = reprobe;
+}
+
+gboolean
+mm_base_modem_get_reprobe (MMBaseModem *self)
+{
+ g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE);
+
+ return self->priv->reprobe;
+}
+
gboolean
mm_base_modem_get_valid (MMBaseModem *self)
{
@@ -610,154 +966,22 @@ mm_base_modem_peek_port_gps (MMBaseModem *self)
return self->priv->gps;
}
-#if defined WITH_QMI
-
-MMPortQmi *
-mm_base_modem_get_port_qmi (MMBaseModem *self)
-{
- g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
-
- /* First QMI port in the list is the primary one always */
- return (self->priv->qmi ? ((MMPortQmi *)g_object_ref (self->priv->qmi->data)) : NULL);
-}
-
-MMPortQmi *
-mm_base_modem_peek_port_qmi (MMBaseModem *self)
-{
- g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
-
- /* First QMI port in the list is the primary one always */
- return (self->priv->qmi ? (MMPortQmi *)self->priv->qmi->data : NULL);
-}
-
-MMPortQmi *
-mm_base_modem_get_port_qmi_for_data (MMBaseModem *self,
- MMPort *data,
- GError **error)
-{
- MMPortQmi *qmi;
-
- qmi = mm_base_modem_peek_port_qmi_for_data (self, data, error);
- return (qmi ? (MMPortQmi *)g_object_ref (qmi) : NULL);
-}
-
-MMPortQmi *
-mm_base_modem_peek_port_qmi_for_data (MMBaseModem *self,
- MMPort *data,
- GError **error)
-{
- GList *cdc_wdm_qmi_ports, *l;
- const gchar *net_port_parent_path;
-
- g_warn_if_fail (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET);
- net_port_parent_path = mm_port_get_parent_path (data);
- if (!net_port_parent_path) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "No parent path for 'net/%s'",
- mm_port_get_device (data));
- return NULL;
- }
-
- /* Find the CDC-WDM port on the same USB interface as the given net port */
- cdc_wdm_qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
- MM_PORT_SUBSYS_USB,
- MM_PORT_TYPE_QMI,
- NULL);
- for (l = cdc_wdm_qmi_ports; l; l = g_list_next (l)) {
- const gchar *wdm_port_parent_path;
-
- g_assert (MM_IS_PORT_QMI (l->data));
- wdm_port_parent_path = mm_port_get_parent_path (MM_PORT (l->data));
- if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path))
- return MM_PORT_QMI (l->data);
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "Couldn't find associated QMI port for 'net/%s'",
- mm_port_get_device (data));
- return NULL;
-}
-
-#endif /* WITH_QMI */
-
-#if defined WITH_MBIM
-
-MMPortMbim *
-mm_base_modem_get_port_mbim (MMBaseModem *self)
+MMPortSerial *
+mm_base_modem_get_port_audio (MMBaseModem *self)
{
g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
- /* First MBIM port in the list is the primary one always */
- return (self->priv->mbim ? ((MMPortMbim *)g_object_ref (self->priv->mbim->data)) : NULL);
+ return (self->priv->audio ? g_object_ref (self->priv->audio) : NULL);
}
-MMPortMbim *
-mm_base_modem_peek_port_mbim (MMBaseModem *self)
+MMPortSerial *
+mm_base_modem_peek_port_audio (MMBaseModem *self)
{
g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
- /* First MBIM port in the list is the primary one always */
- return (self->priv->mbim ? (MMPortMbim *)self->priv->mbim->data : NULL);
+ return self->priv->audio;
}
-MMPortMbim *
-mm_base_modem_get_port_mbim_for_data (MMBaseModem *self,
- MMPort *data,
- GError **error)
-{
- MMPortMbim *mbim;
-
- mbim = mm_base_modem_peek_port_mbim_for_data (self, data, error);
- return (mbim ? (MMPortMbim *)g_object_ref (mbim) : NULL);
-}
-
-MMPortMbim *
-mm_base_modem_peek_port_mbim_for_data (MMBaseModem *self,
- MMPort *data,
- GError **error)
-{
- GList *cdc_wdm_mbim_ports, *l;
- const gchar *net_port_parent_path;
-
- g_warn_if_fail (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET);
- net_port_parent_path = mm_port_get_parent_path (data);
- if (!net_port_parent_path) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "No parent path for 'net/%s'",
- mm_port_get_device (data));
- return NULL;
- }
-
- /* Find the CDC-WDM port on the same USB interface as the given net port */
- cdc_wdm_mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
- MM_PORT_SUBSYS_USB,
- MM_PORT_TYPE_MBIM,
- NULL);
- for (l = cdc_wdm_mbim_ports; l; l = g_list_next (l)) {
- const gchar *wdm_port_parent_path;
-
- g_assert (MM_IS_PORT_MBIM (l->data));
- wdm_port_parent_path = mm_port_get_parent_path (MM_PORT (l->data));
- if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path))
- return MM_PORT_MBIM (l->data);
- }
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "Couldn't find associated MBIM port for 'net/%s'",
- mm_port_get_device (data));
- return NULL;
-}
-
-#endif /* WITH_MBIM */
-
MMPort *
mm_base_modem_get_best_data_port (MMBaseModem *self,
MMPortType type)
@@ -791,13 +1015,9 @@ mm_base_modem_peek_best_data_port (MMBaseModem *self,
GList *
mm_base_modem_get_data_ports (MMBaseModem *self)
{
- GList *copy;
-
g_return_val_if_fail (MM_IS_BASE_MODEM (self), NULL);
- copy = g_list_copy (self->priv->data);
- g_list_foreach (copy, (GFunc)g_object_ref, NULL);
- return copy;
+ return g_list_copy_deep (self->priv->data, (GCopyFunc)g_object_ref, NULL);
}
GList *
@@ -858,66 +1078,89 @@ mm_base_modem_has_at_port (MMBaseModem *self)
return FALSE;
}
+static gint
+port_info_cmp (const MMModemPortInfo *a,
+ const MMModemPortInfo *b)
+{
+ /* default to alphabetical sorting on the port name */
+ return g_strcmp0 (a->name, b->name);
+}
+
MMModemPortInfo *
mm_base_modem_get_port_infos (MMBaseModem *self,
- guint *n_port_infos)
+ guint *n_port_infos)
{
- GHashTableIter iter;
- MMModemPortInfo *port_infos;
- MMPort *port;
- guint i;
+ GHashTableIter iter;
+ GArray *port_infos;
+ MMPort *port;
*n_port_infos = g_hash_table_size (self->priv->ports);
- port_infos = g_new (MMModemPortInfo, *n_port_infos);
+ port_infos = g_array_sized_new (FALSE, FALSE, sizeof (MMModemPortInfo), *n_port_infos);
g_hash_table_iter_init (&iter, self->priv->ports);
- i = 0;
while (g_hash_table_iter_next (&iter, NULL, (gpointer)&port)) {
- port_infos[i].name = g_strdup (mm_port_get_device (port));
+ MMModemPortInfo port_info;
+
+ port_info.name = g_strdup (mm_port_get_device (port));
switch (mm_port_get_port_type (port)) {
case MM_PORT_TYPE_NET:
- port_infos[i].type = MM_MODEM_PORT_TYPE_NET;
+ port_info.type = MM_MODEM_PORT_TYPE_NET;
break;
case MM_PORT_TYPE_AT:
- port_infos[i].type = MM_MODEM_PORT_TYPE_AT;
+ port_info.type = MM_MODEM_PORT_TYPE_AT;
break;
case MM_PORT_TYPE_QCDM:
- port_infos[i].type = MM_MODEM_PORT_TYPE_QCDM;
+ port_info.type = MM_MODEM_PORT_TYPE_QCDM;
break;
case MM_PORT_TYPE_GPS:
- port_infos[i].type = MM_MODEM_PORT_TYPE_GPS;
+ port_info.type = MM_MODEM_PORT_TYPE_GPS;
+ break;
+ case MM_PORT_TYPE_AUDIO:
+ port_info.type = MM_MODEM_PORT_TYPE_AUDIO;
break;
case MM_PORT_TYPE_QMI:
- port_infos[i].type = MM_MODEM_PORT_TYPE_QMI;
+ port_info.type = MM_MODEM_PORT_TYPE_QMI;
break;
case MM_PORT_TYPE_MBIM:
- port_infos[i].type = MM_MODEM_PORT_TYPE_MBIM;
+ port_info.type = MM_MODEM_PORT_TYPE_MBIM;
break;
- case MM_PORT_TYPE_UNKNOWN:
case MM_PORT_TYPE_IGNORED:
+ port_info.type = MM_MODEM_PORT_TYPE_IGNORED;
+ break;
+ case MM_PORT_TYPE_UNKNOWN:
default:
- port_infos[i].type = MM_MODEM_PORT_TYPE_UNKNOWN;
+ port_info.type = MM_MODEM_PORT_TYPE_UNKNOWN;
break;
}
- i++;
+ g_array_append_val (port_infos, port_info);
}
- return port_infos;
+ g_assert (*n_port_infos == port_infos->len);
+ g_array_sort (port_infos, (GCompareFunc) port_info_cmp);
+ return (MMModemPortInfo *) g_array_free (port_infos, FALSE);
+}
+
+static gint
+port_cmp (MMPort *a,
+ MMPort *b)
+{
+ /* default to alphabetical sorting on the port name */
+ return g_strcmp0 (mm_port_get_device (a), mm_port_get_device (b));
}
GList *
-mm_base_modem_find_ports (MMBaseModem *self,
- MMPortSubsys subsys,
- MMPortType type,
- const gchar *name)
+mm_base_modem_find_ports (MMBaseModem *self,
+ MMPortSubsys subsys,
+ MMPortType type)
{
- GList *out = NULL;
- GHashTableIter iter;
- gpointer value;
- gpointer key;
+ GList *out = NULL;
+ GHashTableIter iter;
+ gpointer value;
+ gpointer key;
+
+ if (!self->priv->ports)
+ return NULL;
- /* We'll iterate the ht of ports, looking for any port which is matches
- * the compare function */
g_hash_table_iter_init (&iter, self->priv->ports);
while (g_hash_table_iter_next (&iter, &key, &value)) {
MMPort *port = MM_PORT (value);
@@ -928,13 +1171,55 @@ mm_base_modem_find_ports (MMBaseModem *self,
if (type != MM_PORT_TYPE_UNKNOWN && mm_port_get_port_type (port) != type)
continue;
- if (name != NULL && !g_str_equal (mm_port_get_device (port), name))
- continue;
-
out = g_list_append (out, g_object_ref (port));
}
- return out;
+ return g_list_sort (out, (GCompareFunc) port_cmp);
+}
+
+static MMPort *
+peek_port_in_ht (GHashTable *ht,
+ const gchar *name)
+{
+ GHashTableIter iter;
+ gpointer value;
+ gpointer key;
+
+ if (!ht)
+ return NULL;
+
+ g_hash_table_iter_init (&iter, ht);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ MMPort *port = MM_PORT (value);
+
+ if (g_str_equal (mm_port_get_device (port), name))
+ return port;
+ }
+
+ return NULL;
+}
+
+MMPort *
+mm_base_modem_peek_port (MMBaseModem *self,
+ const gchar *name)
+{
+ MMPort *found;
+
+ found = peek_port_in_ht (self->priv->ports, name);
+ if (!found)
+ found = peek_port_in_ht (self->priv->link_ports, name);
+
+ return found;
+}
+
+MMPort *
+mm_base_modem_get_port (MMBaseModem *self,
+ const gchar *name)
+{
+ MMPort *port;
+
+ port = mm_base_modem_peek_port (self, name);
+ return (port ? g_object_ref (port) : NULL);
}
static void
@@ -944,7 +1229,7 @@ initialize_ready (MMBaseModem *self,
GError *error = NULL;
if (mm_base_modem_initialize_finish (self, res, &error)) {
- mm_dbg ("modem properly initialized");
+ mm_obj_dbg (self, "modem initialized");
mm_base_modem_set_valid (self, TRUE);
return;
}
@@ -954,8 +1239,7 @@ initialize_ready (MMBaseModem *self,
/* Even with initialization errors, we do set the state to valid, so
* that the modem gets exported and the failure notified to the user.
*/
- mm_dbg ("Couldn't finish initialization in the current state: '%s'",
- error->message);
+ mm_obj_dbg (self, "couldn't finish initialization in the current state: '%s'", error->message);
g_error_free (error);
mm_base_modem_set_valid (self, TRUE);
return;
@@ -963,7 +1247,7 @@ initialize_ready (MMBaseModem *self,
/* Really fatal, we cannot even export the failed modem (e.g. error before
* even trying to enable the Modem interface */
- mm_warn ("couldn't initialize the modem: '%s'", error->message);
+ mm_obj_warn (self, "couldn't initialize: '%s'", error->message);
g_error_free (error);
}
@@ -971,11 +1255,10 @@ static inline void
log_port (MMBaseModem *self, MMPort *port, const char *desc)
{
if (port) {
- mm_dbg ("(%s) %s/%s %s",
- self->priv->device,
- mm_port_subsys_get_string (mm_port_get_subsys (port)),
- mm_port_get_device (port),
- desc);
+ mm_obj_dbg (self, "%s/%s %s",
+ mm_port_subsys_get_string (mm_port_get_subsys (port)),
+ mm_port_get_device (port),
+ desc);
}
}
@@ -993,17 +1276,19 @@ mm_base_modem_organize_ports (MMBaseModem *self,
MMPortSerialQcdm *qcdm = NULL;
MMPortSerialAt *gps_control = NULL;
MMPortSerialGps *gps = NULL;
- MMPort *data_primary = NULL;
- GList *data = NULL;
+ MMPortSerial *audio = NULL;
+ MMPortSerialAt *data_at_primary = NULL;
+ GList *l;
+ /* These lists don't keep full references, so they should be
+ * g_list_free()-ed on error exits */
+ g_autoptr(GList) data_at = NULL;
+ g_autoptr(GList) data_net = NULL;
#if defined WITH_QMI
- MMPort *qmi_primary = NULL;
- GList *qmi = NULL;
+ g_autoptr(GList) qmi = NULL;
#endif
#if defined WITH_MBIM
- MMPort *mbim_primary = NULL;
- GList *mbim = NULL;
+ g_autoptr(GList) mbim = NULL;
#endif
- GList *l;
g_return_val_if_fail (MM_IS_BASE_MODEM (self), FALSE);
@@ -1032,10 +1317,10 @@ mm_base_modem_organize_ports (MMBaseModem *self,
}
if (flags & MM_PORT_SERIAL_AT_FLAG_PPP) {
- if (!data_primary)
- data_primary = candidate;
+ if (!data_at_primary)
+ data_at_primary = MM_PORT_SERIAL_AT (candidate);
else
- data = g_list_append (data, candidate);
+ data_at = g_list_append (data_at, candidate);
}
/* Explicitly flagged secondary ports trump NONE ports for secondary */
@@ -1065,16 +1350,7 @@ mm_base_modem_organize_ports (MMBaseModem *self,
break;
case MM_PORT_TYPE_NET:
- if (!data_primary)
- data_primary = candidate;
- else if (MM_IS_PORT_SERIAL_AT (data_primary)) {
- /* Net device (if any) is the preferred data port */
- data = g_list_append (data, data_primary);
- data_primary = candidate;
- }
- else
- /* All non-primary net ports get added to the list of data ports */
- data = g_list_append (data, candidate);
+ data_net = g_list_append (data_net, candidate);
break;
case MM_PORT_TYPE_GPS:
@@ -1083,26 +1359,32 @@ mm_base_modem_organize_ports (MMBaseModem *self,
gps = MM_PORT_SERIAL_GPS (candidate);
break;
+ case MM_PORT_TYPE_AUDIO:
+ g_assert (MM_IS_PORT_SERIAL (candidate));
+ if (!audio)
+ audio = MM_PORT_SERIAL (candidate);
+ break;
+
#if defined WITH_QMI
case MM_PORT_TYPE_QMI:
- if (!qmi_primary)
- qmi_primary = candidate;
- else
- /* All non-primary QMI ports get added to the list of QMI ports */
- qmi = g_list_append (qmi, candidate);
+ qmi = g_list_append (qmi, candidate);
break;
#endif
#if defined WITH_MBIM
case MM_PORT_TYPE_MBIM:
- if (!mbim_primary)
- mbim_primary = candidate;
- else
- /* All non-primary MBIM ports get added to the list of MBIM ports */
- mbim = g_list_append (mbim, candidate);
+ mbim = g_list_append (mbim, candidate);
break;
#endif
+ case MM_PORT_TYPE_UNKNOWN:
+ case MM_PORT_TYPE_IGNORED:
+#if !defined WITH_MBIM
+ case MM_PORT_TYPE_MBIM:
+#endif
+#if !defined WITH_QMI
+ case MM_PORT_TYPE_QMI:
+#endif
default:
/* Ignore port */
break;
@@ -1116,20 +1398,20 @@ mm_base_modem_organize_ports (MMBaseModem *self,
secondary = NULL;
}
/* Fallback to a data port if no primary or secondary */
- else if (data_primary && MM_IS_PORT_SERIAL_AT (data_primary)) {
- primary = MM_PORT_SERIAL_AT (data_primary);
- data_primary = NULL;
+ else if (data_at_primary) {
+ primary = data_at_primary;
+ data_at_primary = NULL;
}
else {
gboolean allow_modem_without_at_port = FALSE;
#if defined WITH_QMI
- if (qmi_primary)
+ if (qmi)
allow_modem_without_at_port = TRUE;
#endif
#if defined WITH_MBIM
- if (mbim_primary)
+ if (mbim)
allow_modem_without_at_port = TRUE;
#endif
@@ -1151,7 +1433,7 @@ mm_base_modem_organize_ports (MMBaseModem *self,
#if defined WITH_QMI
/* On QMI-based modems, we need to have at least a net port */
- if (qmi_primary && !data_primary) {
+ if (qmi && !data_net) {
g_set_error_literal (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -1162,7 +1444,7 @@ mm_base_modem_organize_ports (MMBaseModem *self,
#if defined WITH_MBIM
/* On MBIM-based modems, we need to have at least a net port */
- if (mbim_primary && !data_primary) {
+ if (mbim && !data_net) {
g_set_error_literal (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -1172,72 +1454,119 @@ mm_base_modem_organize_ports (MMBaseModem *self,
#endif
/* Data port defaults to primary AT port */
- if (!data_primary)
- data_primary = MM_PORT (primary);
- g_assert (data_primary);
+ if (primary && !data_at_primary)
+ data_at_primary = primary;
/* Reset flags on all ports; clear data port first since it might also
* be the primary or secondary port.
*/
- if (MM_IS_PORT_SERIAL_AT (data_primary))
- mm_port_serial_at_set_flags (MM_PORT_SERIAL_AT (data_primary), MM_PORT_SERIAL_AT_FLAG_NONE);
-
+ if (data_at_primary)
+ mm_port_serial_at_set_flags (data_at_primary, MM_PORT_SERIAL_AT_FLAG_NONE);
if (primary)
mm_port_serial_at_set_flags (primary, MM_PORT_SERIAL_AT_FLAG_PRIMARY);
if (secondary)
mm_port_serial_at_set_flags (secondary, MM_PORT_SERIAL_AT_FLAG_SECONDARY);
-
- if (MM_IS_PORT_SERIAL_AT (data_primary)) {
- flags = mm_port_serial_at_get_flags (MM_PORT_SERIAL_AT (data_primary));
- mm_port_serial_at_set_flags (MM_PORT_SERIAL_AT (data_primary), flags | MM_PORT_SERIAL_AT_FLAG_PPP);
+ if (data_at_primary) {
+ flags = mm_port_serial_at_get_flags (data_at_primary);
+ mm_port_serial_at_set_flags (data_at_primary, flags | MM_PORT_SERIAL_AT_FLAG_PPP);
}
- log_port (self, MM_PORT (primary), "at (primary)");
- log_port (self, MM_PORT (secondary), "at (secondary)");
- log_port (self, MM_PORT (data_primary), "data (primary)");
- for (l = data; l; l = g_list_next (l))
- log_port (self, MM_PORT (l->data), "data (secondary)");
- log_port (self, MM_PORT (qcdm), "qcdm");
- log_port (self, MM_PORT (gps_control), "gps (control)");
- log_port (self, MM_PORT (gps), "gps (nmea)");
+ /* sort ports by name */
+#if defined WITH_QMI
+ qmi = g_list_sort (qmi, (GCompareFunc) port_cmp);
+#endif
+#if defined WITH_MBIM
+ mbim = g_list_sort (mbim, (GCompareFunc) port_cmp);
+#endif
+ data_net = g_list_sort (data_net, (GCompareFunc) port_cmp);
+ data_at = g_list_sort (data_at, (GCompareFunc) port_cmp);
+
+ log_port (self, MM_PORT (primary), "at (primary)");
+ log_port (self, MM_PORT (secondary), "at (secondary)");
+ log_port (self, MM_PORT (data_at_primary), "at (data primary)");
+ for (l = data_at; l; l = g_list_next (l))
+ log_port (self, MM_PORT (l->data), "at (data secondary)");
+ for (l = data_net; l; l = g_list_next (l))
+ log_port (self, MM_PORT (l->data), "net (data)");
+ log_port (self, MM_PORT (qcdm), "qcdm");
+ log_port (self, MM_PORT (gps_control), "gps (control)");
+ log_port (self, MM_PORT (gps), "gps (nmea)");
+ log_port (self, MM_PORT (audio), "audio");
#if defined WITH_QMI
- log_port (self, MM_PORT (qmi_primary), "qmi (primary)");
for (l = qmi; l; l = g_list_next (l))
- log_port (self, MM_PORT (l->data), "qmi (secondary)");
+ log_port (self, MM_PORT (l->data), "qmi");
#endif
#if defined WITH_MBIM
- log_port (self, MM_PORT (mbim_primary), "mbim (primary)");
for (l = mbim; l; l = g_list_next (l))
- log_port (self, MM_PORT (l->data), "mbim (secondary)");
+ log_port (self, MM_PORT (l->data), "mbim");
#endif
/* We keep new refs to the objects here */
+
self->priv->primary = (primary ? g_object_ref (primary) : NULL);
self->priv->secondary = (secondary ? g_object_ref (secondary) : NULL);
self->priv->qcdm = (qcdm ? g_object_ref (qcdm) : NULL);
self->priv->gps_control = (gps_control ? g_object_ref (gps_control) : NULL);
self->priv->gps = (gps ? g_object_ref (gps) : NULL);
- /* Build the final list of data ports, primary port first */
- self->priv->data = g_list_append (self->priv->data, g_object_ref (data_primary));
- g_list_foreach (data, (GFunc)g_object_ref, NULL);
- self->priv->data = g_list_concat (self->priv->data, data);
+ /* Append net ports to the final list of data ports, but only if the modem
+ * supports them */
+ if (data_net) {
+ if (self->priv->data_net_supported) {
+ g_list_foreach (data_net, (GFunc)g_object_ref, NULL);
+ self->priv->data = g_list_concat (self->priv->data, g_steal_pointer (&data_net));
+ } else
+ mm_obj_dbg (self, "net ports available but ignored");
+ }
+
+ /* Append tty ports to the final list of data ports, but only if the modem
+ * supports them */
+ if (data_at_primary || data_at) {
+ if (self->priv->data_tty_supported) {
+ if (data_at_primary)
+ self->priv->data = g_list_append (self->priv->data, g_object_ref (data_at_primary));
+ if (data_at) {
+ g_list_foreach (data_at, (GFunc)g_object_ref, NULL);
+ self->priv->data = g_list_concat (self->priv->data, g_steal_pointer (&data_at));
+ }
+ } else
+ mm_obj_dbg (self, "at data ports available but ignored");
+ }
+
+ /* Fail if we haven't added any single data port; this is probably a plugin
+ * misconfiguration */
+ if (!self->priv->data) {
+ g_set_error_literal (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to find a data port in the modem");
+ return FALSE;
+ }
#if defined WITH_QMI
- /* Build the final list of QMI ports, primary port first */
- if (qmi_primary) {
- self->priv->qmi = g_list_append (self->priv->qmi, g_object_ref (qmi_primary));
+ if (qmi) {
+ /* The first item in the data list must be a net port, because
+ * QMI modems only expect net ports */
+ g_assert (MM_IS_PORT_NET (self->priv->data->data));
+ /* let the MMPortQmi know which net driver is being used, taken
+ * from the first item in the net port list */
+ g_list_foreach (qmi,
+ (GFunc)mm_port_qmi_set_net_driver,
+ (gpointer) mm_kernel_device_get_driver (
+ mm_port_peek_kernel_device (
+ MM_PORT (self->priv->data->data))));
+ g_list_foreach (qmi,
+ (GFunc)mm_port_qmi_set_net_sysfs_path,
+ (gpointer) mm_kernel_device_get_sysfs_path (
+ mm_port_peek_kernel_device (
+ MM_PORT (self->priv->data->data))));
g_list_foreach (qmi, (GFunc)g_object_ref, NULL);
- self->priv->qmi = g_list_concat (self->priv->qmi, qmi);
+ self->priv->qmi = g_steal_pointer (&qmi);
}
#endif
#if defined WITH_MBIM
- /* Build the final list of MBIM ports, primary port first */
- if (mbim_primary) {
- self->priv->mbim = g_list_append (self->priv->mbim, g_object_ref (mbim_primary));
+ if (mbim) {
g_list_foreach (mbim, (GFunc)g_object_ref, NULL);
- self->priv->mbim = g_list_concat (self->priv->mbim, mbim);
+ self->priv->mbim = g_steal_pointer (&mbim);
}
#endif
@@ -1257,23 +1586,22 @@ mm_base_modem_authorize_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
authorize_ready (MMAuthProvider *authp,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!mm_auth_provider_authorize_finish (authp, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
void
@@ -1283,18 +1611,14 @@ mm_base_modem_authorize (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_modem_authorize);
+ task = g_task_new (self, self->priv->authp_cancellable, callback, user_data);
/* When running in the session bus for tests, default to always allow */
if (mm_context_get_test_session ()) {
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -1303,7 +1627,7 @@ mm_base_modem_authorize (MMBaseModem *self,
authorization,
self->priv->authp_cancellable,
(GAsyncReadyCallback)authorize_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -1350,6 +1674,28 @@ mm_base_modem_get_product_id (MMBaseModem *self)
/*****************************************************************************/
+static void
+after_sim_switch_disable_ready (MMBaseModem *self,
+ GAsyncResult *res)
+{
+ g_autoptr(GError) error = NULL;
+
+ mm_base_modem_disable_finish (self, res, &error);
+ if (error)
+ mm_obj_err (self, "failed to disable after SIM switch event: %s", error->message);
+ else
+ mm_base_modem_set_valid (self, FALSE);
+}
+
+void
+mm_base_modem_process_sim_event (MMBaseModem *self)
+{
+ mm_base_modem_set_reprobe (self, TRUE);
+ mm_base_modem_disable (self, (GAsyncReadyCallback) after_sim_switch_disable_ready, NULL);
+}
+
+/*****************************************************************************/
+
static gboolean
base_modem_invalid_idle (MMBaseModem *self)
{
@@ -1357,7 +1703,7 @@ base_modem_invalid_idle (MMBaseModem *self)
* cancelled */
mm_base_modem_set_valid (self, FALSE);
g_object_unref (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -1372,15 +1718,91 @@ base_modem_cancelled (GCancellable *cancellable,
/*****************************************************************************/
static void
+setup_ports_table (GHashTable **ht)
+{
+ g_assert (ht && !*ht);
+ *ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+}
+
+static void
+cleanup_modem_port (MMBaseModem *self,
+ MMPort *port)
+{
+ mm_obj_dbg (self, "cleaning up port '%s/%s'...",
+ mm_port_subsys_get_string (mm_port_get_subsys (MM_PORT (port))),
+ mm_port_get_device (MM_PORT (port)));
+
+ /* Cleanup for serial ports */
+ if (MM_IS_PORT_SERIAL (port)) {
+ g_signal_handlers_disconnect_by_func (port, serial_port_timed_out_cb, self);
+ return;
+ }
+
+#if defined WITH_MBIM
+ /* We need to close the MBIM port cleanly when disposing the modem object */
+ if (MM_IS_PORT_MBIM (port)) {
+ mm_port_mbim_close (MM_PORT_MBIM (port), NULL, NULL);
+ return;
+ }
+#endif
+
+#if defined WITH_QMI
+ /* We need to close the QMI port cleanly when disposing the modem object,
+ * otherwise the allocated CIDs will be kept allocated, and if we end up
+ * allocating too many newer allocations will fail with client-ids-exhausted
+ * errors. */
+ if (MM_IS_PORT_QMI (port)) {
+ mm_port_qmi_close (MM_PORT_QMI (port), NULL, NULL);
+ return;
+ }
+#endif
+}
+
+static void
+teardown_ports_table (MMBaseModem *self,
+ GHashTable **ht)
+{
+ GHashTableIter iter;
+ gpointer value;
+ gpointer key;
+
+ if (!*ht)
+ return;
+
+ g_hash_table_iter_init (&iter, *ht);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ cleanup_modem_port (self, MM_PORT (value));
+ g_hash_table_destroy (g_steal_pointer (ht));
+}
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMBaseModem *self;
+
+ self = MM_BASE_MODEM (_self);
+ return g_strdup_printf ("modem%u", self->priv->dbus_id);
+}
+
+/*****************************************************************************/
+
+static void
mm_base_modem_init (MMBaseModem *self)
{
+ static guint id = 0;
+
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BASE_MODEM,
MMBaseModemPrivate);
+ /* Each modem is given a unique id to build its own DBus path */
+ self->priv->dbus_id = id++;
+
/* Setup authorization provider */
- self->priv->authp = mm_auth_get_provider ();
+ self->priv->authp = mm_auth_provider_get ();
self->priv->authp_cancellable = g_cancellable_new ();
/* Setup modem-wide cancellable */
@@ -1391,10 +1813,10 @@ mm_base_modem_init (MMBaseModem *self)
self,
NULL);
- self->priv->ports = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- g_object_unref);
+ self->priv->max_timeouts = DEFAULT_MAX_TIMEOUTS;
+
+ setup_ports_table (&self->priv->ports);
+ setup_ports_table (&self->priv->link_ports);
}
static void
@@ -1409,6 +1831,9 @@ set_property (GObject *object,
case PROP_VALID:
mm_base_modem_set_valid (self, g_value_get_boolean (value));
break;
+ case PROP_REPROBE:
+ mm_base_modem_set_reprobe (self, g_value_get_boolean (value));
+ break;
case PROP_MAX_TIMEOUTS:
self->priv->max_timeouts = g_value_get_uint (value);
break;
@@ -1434,6 +1859,12 @@ set_property (GObject *object,
g_clear_object (&self->priv->connection);
self->priv->connection = g_value_dup_object (value);
break;
+ case PROP_DATA_NET_SUPPORTED:
+ self->priv->data_net_supported = g_value_get_boolean (value);
+ break;
+ case PROP_DATA_TTY_SUPPORTED:
+ self->priv->data_tty_supported = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1452,6 +1883,9 @@ get_property (GObject *object,
case PROP_VALID:
g_value_set_boolean (value, self->priv->valid);
break;
+ case PROP_REPROBE:
+ g_value_set_boolean (value, self->priv->reprobe);
+ break;
case PROP_MAX_TIMEOUTS:
g_value_set_uint (value, self->priv->max_timeouts);
break;
@@ -1473,6 +1907,12 @@ get_property (GObject *object,
case PROP_CONNECTION:
g_value_set_object (value, self->priv->connection);
break;
+ case PROP_DATA_NET_SUPPORTED:
+ g_value_set_boolean (value, self->priv->data_net_supported);
+ break;
+ case PROP_DATA_TTY_SUPPORTED:
+ g_value_set_boolean (value, self->priv->data_tty_supported);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1488,9 +1928,10 @@ finalize (GObject *object)
* mm_auth_provider_cancel_for_owner (self->priv->authp, object);
*/
- mm_dbg ("Modem (%s) '%s' completely disposed",
- self->priv->plugin,
- self->priv->device);
+ g_assert (!self->priv->enable_tasks);
+ g_assert (!self->priv->disable_tasks);
+
+ mm_obj_dbg (self, "completely disposed");
g_free (self->priv->device);
g_strfreev (self->priv->drivers);
@@ -1507,7 +1948,8 @@ dispose (GObject *object)
/* Cancel all ongoing auth requests */
g_cancellable_cancel (self->priv->authp_cancellable);
g_clear_object (&self->priv->authp_cancellable);
- g_clear_object (&self->priv->authp);
+
+ /* note: authp is a singleton, we don't keep a full reference */
/* Ensure we cancel any ongoing operation, but before
* disconnect our own signal handler, or we'll end up with
@@ -1519,31 +1961,20 @@ dispose (GObject *object)
g_clear_object (&self->priv->primary);
g_clear_object (&self->priv->secondary);
- g_list_free_full (self->priv->data, g_object_unref);
- self->priv->data = NULL;
+ g_list_free_full (g_steal_pointer (&self->priv->data), g_object_unref);
g_clear_object (&self->priv->qcdm);
g_clear_object (&self->priv->gps_control);
g_clear_object (&self->priv->gps);
+ g_clear_object (&self->priv->audio);
#if defined WITH_QMI
- /* We need to close the QMI port cleanly when disposing the modem object,
- * otherwise the allocated CIDs will be kept allocated, and if we end up
- * allocating too many newer allocations will fail with client-ids-exhausted
- * errors. */
- g_list_foreach (self->priv->qmi, (GFunc)mm_port_qmi_close, NULL);
- g_list_free_full (self->priv->qmi, g_object_unref);
- self->priv->qmi = NULL;
+ g_list_free_full (g_steal_pointer (&self->priv->qmi), g_object_unref);
#endif
#if defined WITH_MBIM
- /* We need to close the MBIM port cleanly when disposing the modem object */
- g_list_foreach (self->priv->mbim, (GFunc)mm_port_mbim_close, NULL);
- g_list_free_full (self->priv->mbim, g_object_unref);
- self->priv->mbim = NULL;
+ g_list_free_full (g_steal_pointer (&self->priv->mbim), g_object_unref);
#endif
- if (self->priv->ports) {
- g_hash_table_destroy (self->priv->ports);
- self->priv->ports = NULL;
- }
+ teardown_ports_table (self, &self->priv->link_ports);
+ teardown_ports_table (self, &self->priv->ports);
g_clear_object (&self->priv->connection);
@@ -1551,6 +1982,12 @@ dispose (GObject *object)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_modem_class_init (MMBaseModemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1568,7 +2005,7 @@ mm_base_modem_class_init (MMBaseModemClass *klass)
"Max timeouts",
"Maximum number of consecutive timed out commands sent to "
"the modem before disabling it. If 0, this feature is disabled.",
- 0, G_MAXUINT, 0,
+ 0, G_MAXUINT, DEFAULT_MAX_TIMEOUTS,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MAX_TIMEOUTS, properties[PROP_MAX_TIMEOUTS]);
@@ -1627,4 +2064,46 @@ mm_base_modem_class_init (MMBaseModemClass *klass)
G_TYPE_DBUS_CONNECTION,
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_CONNECTION, properties[PROP_CONNECTION]);
+
+ properties[PROP_REPROBE] =
+ g_param_spec_boolean (MM_BASE_MODEM_REPROBE,
+ "Reprobe",
+ "Whether the modem needs to be reprobed or not.",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_REPROBE, properties[PROP_REPROBE]);
+
+ properties[PROP_DATA_NET_SUPPORTED] =
+ g_param_spec_boolean (MM_BASE_MODEM_DATA_NET_SUPPORTED,
+ "Data NET supported",
+ "Whether the modem supports connection via a NET port.",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_DATA_NET_SUPPORTED, properties[PROP_DATA_NET_SUPPORTED]);
+
+ properties[PROP_DATA_TTY_SUPPORTED] =
+ g_param_spec_boolean (MM_BASE_MODEM_DATA_TTY_SUPPORTED,
+ "Data TTY supported",
+ "Whether the modem supports connection via a TTY port.",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_DATA_TTY_SUPPORTED, properties[PROP_DATA_TTY_SUPPORTED]);
+
+ signals[SIGNAL_LINK_PORT_GRABBED] =
+ g_signal_new (MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMBaseModemClass, link_port_grabbed),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, MM_TYPE_PORT);
+
+ signals[SIGNAL_LINK_PORT_RELEASED] =
+ g_signal_new (MM_BASE_MODEM_SIGNAL_LINK_PORT_RELEASED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMBaseModemClass, link_port_released),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, MM_TYPE_PORT);
}
diff --git a/src/mm-base-modem.h b/src/mm-base-modem.h
index c5f3788e..61e9e59a 100644
--- a/src/mm-base-modem.h
+++ b/src/mm-base-modem.h
@@ -28,8 +28,10 @@
#include <mm-gdbus-modem.h>
-#include "mm-auth.h"
+#include "mm-auth-provider.h"
+#include "mm-kernel-device.h"
#include "mm-port.h"
+#include "mm-port-net.h"
#include "mm-port-serial-at.h"
#include "mm-port-serial-qcdm.h"
#include "mm-port-serial-gps.h"
@@ -53,14 +55,20 @@ typedef struct _MMBaseModem MMBaseModem;
typedef struct _MMBaseModemClass MMBaseModemClass;
typedef struct _MMBaseModemPrivate MMBaseModemPrivate;
-#define MM_BASE_MODEM_CONNECTION "base-modem-connection"
-#define MM_BASE_MODEM_MAX_TIMEOUTS "base-modem-max-timeouts"
-#define MM_BASE_MODEM_VALID "base-modem-valid"
-#define MM_BASE_MODEM_DEVICE "base-modem-device"
-#define MM_BASE_MODEM_DRIVERS "base-modem-drivers"
-#define MM_BASE_MODEM_PLUGIN "base-modem-plugin"
-#define MM_BASE_MODEM_VENDOR_ID "base-modem-vendor-id"
-#define MM_BASE_MODEM_PRODUCT_ID "base-modem-product-id"
+#define MM_BASE_MODEM_CONNECTION "base-modem-connection"
+#define MM_BASE_MODEM_MAX_TIMEOUTS "base-modem-max-timeouts"
+#define MM_BASE_MODEM_VALID "base-modem-valid"
+#define MM_BASE_MODEM_DEVICE "base-modem-device"
+#define MM_BASE_MODEM_DRIVERS "base-modem-drivers"
+#define MM_BASE_MODEM_PLUGIN "base-modem-plugin"
+#define MM_BASE_MODEM_VENDOR_ID "base-modem-vendor-id"
+#define MM_BASE_MODEM_PRODUCT_ID "base-modem-product-id"
+#define MM_BASE_MODEM_REPROBE "base-modem-reprobe"
+#define MM_BASE_MODEM_DATA_NET_SUPPORTED "base-modem-data-net-supported"
+#define MM_BASE_MODEM_DATA_TTY_SUPPORTED "base-modem-data-tty-supported"
+
+#define MM_BASE_MODEM_SIGNAL_LINK_PORT_GRABBED "base-modem-link-port-grabbed"
+#define MM_BASE_MODEM_SIGNAL_LINK_PORT_RELEASED "base-modem-link-port-released"
struct _MMBaseModem {
MmGdbusObjectSkeleton parent;
@@ -99,26 +107,53 @@ struct _MMBaseModemClass {
gboolean (*disable_finish) (MMBaseModem *self,
GAsyncResult *res,
GError **error);
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ /* Modem synchronization.
+ * When resuming in quick suspend/resume mode,
+ * this method triggers a synchronization of all modem interfaces */
+ void (* sync) (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* sync_finish) (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error);
+#endif
+
+ /* signals */
+ void (* link_port_grabbed) (MMBaseModem *self,
+ MMPort *link_port);
+ void (* link_port_released) (MMBaseModem *self,
+ MMPort *link_port);
};
GType mm_base_modem_get_type (void);
-
-gboolean mm_base_modem_grab_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name,
- const gchar *parent_path,
- MMPortType ptype,
- MMPortSerialAtFlag at_pflags,
- GError **error);
-void mm_base_modem_release_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name);
-MMPort *mm_base_modem_get_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name);
-gboolean mm_base_modem_owns_port (MMBaseModem *self,
- const gchar *subsys,
- const gchar *name);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseModem, g_object_unref)
+
+guint mm_base_modem_get_dbus_id (MMBaseModem *self);
+
+gboolean mm_base_modem_grab_port (MMBaseModem *self,
+ MMKernelDevice *kernel_device,
+ MMPortType ptype,
+ MMPortSerialAtFlag at_pflags,
+ GError **error);
+gboolean mm_base_modem_grab_link_port (MMBaseModem *self,
+ MMKernelDevice *kernel_device,
+ GError **error);
+gboolean mm_base_modem_release_link_port (MMBaseModem *self,
+ const gchar *subsystem,
+ const gchar *name,
+ GError **error);
+
+void mm_base_modem_wait_link_port (MMBaseModem *self,
+ const gchar *subsystem,
+ const gchar *name,
+ guint timeout_ms,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMPort *mm_base_modem_wait_link_port_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error);
gboolean mm_base_modem_has_at_port (MMBaseModem *self);
@@ -130,14 +165,7 @@ MMPortSerialAt *mm_base_modem_peek_port_secondary (MMBaseModem *self);
MMPortSerialQcdm *mm_base_modem_peek_port_qcdm (MMBaseModem *self);
MMPortSerialAt *mm_base_modem_peek_port_gps_control (MMBaseModem *self);
MMPortSerialGps *mm_base_modem_peek_port_gps (MMBaseModem *self);
-#if defined WITH_QMI
-MMPortQmi *mm_base_modem_peek_port_qmi (MMBaseModem *self);
-MMPortQmi *mm_base_modem_peek_port_qmi_for_data (MMBaseModem *self, MMPort *data, GError **error);
-#endif
-#if defined WITH_MBIM
-MMPortMbim *mm_base_modem_peek_port_mbim (MMBaseModem *self);
-MMPortMbim *mm_base_modem_peek_port_mbim_for_data (MMBaseModem *self, MMPort *data, GError **error);
-#endif
+MMPortSerial *mm_base_modem_peek_port_audio (MMBaseModem *self);
MMPortSerialAt *mm_base_modem_peek_best_at_port (MMBaseModem *self, GError **error);
MMPort *mm_base_modem_peek_best_data_port (MMBaseModem *self, MMPortType type);
GList *mm_base_modem_peek_data_ports (MMBaseModem *self);
@@ -147,14 +175,7 @@ MMPortSerialAt *mm_base_modem_get_port_secondary (MMBaseModem *self);
MMPortSerialQcdm *mm_base_modem_get_port_qcdm (MMBaseModem *self);
MMPortSerialAt *mm_base_modem_get_port_gps_control (MMBaseModem *self);
MMPortSerialGps *mm_base_modem_get_port_gps (MMBaseModem *self);
-#if defined WITH_QMI
-MMPortQmi *mm_base_modem_get_port_qmi (MMBaseModem *self);
-MMPortQmi *mm_base_modem_get_port_qmi_for_data (MMBaseModem *self, MMPort *data, GError **error);
-#endif
-#if defined WITH_MBIM
-MMPortMbim *mm_base_modem_get_port_mbim (MMBaseModem *self);
-MMPortMbim *mm_base_modem_get_port_mbim_for_data (MMBaseModem *self, MMPort *data, GError **error);
-#endif
+MMPortSerial *mm_base_modem_get_port_audio (MMBaseModem *self);
MMPortSerialAt *mm_base_modem_get_best_at_port (MMBaseModem *self, GError **error);
MMPort *mm_base_modem_get_best_data_port (MMBaseModem *self, MMPortType type);
GList *mm_base_modem_get_data_ports (MMBaseModem *self);
@@ -162,10 +183,13 @@ GList *mm_base_modem_get_data_ports (MMBaseModem *self);
MMModemPortInfo *mm_base_modem_get_port_infos (MMBaseModem *self,
guint *n_port_infos);
-GList *mm_base_modem_find_ports (MMBaseModem *self,
- MMPortSubsys subsys,
- MMPortType type,
- const gchar *name);
+GList *mm_base_modem_find_ports (MMBaseModem *self,
+ MMPortSubsys subsys,
+ MMPortType type);
+MMPort *mm_base_modem_peek_port (MMBaseModem *self,
+ const gchar *name);
+MMPort *mm_base_modem_get_port (MMBaseModem *self,
+ const gchar *name);
void mm_base_modem_set_hotplugged (MMBaseModem *self,
gboolean hotplugged);
@@ -175,6 +199,10 @@ void mm_base_modem_set_valid (MMBaseModem *self,
gboolean valid);
gboolean mm_base_modem_get_valid (MMBaseModem *self);
+void mm_base_modem_set_reprobe (MMBaseModem *self,
+ gboolean reprobe);
+gboolean mm_base_modem_get_reprobe (MMBaseModem *self);
+
const gchar *mm_base_modem_get_device (MMBaseModem *self);
const gchar **mm_base_modem_get_drivers (MMBaseModem *self);
const gchar *mm_base_modem_get_plugin (MMBaseModem *self);
@@ -215,4 +243,17 @@ gboolean mm_base_modem_disable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error);
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+void mm_base_modem_sync (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_base_modem_sync_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
+
+void mm_base_modem_process_sim_event (MMBaseModem *self);
+
#endif /* MM_BASE_MODEM_H */
diff --git a/src/mm-base-sim.c b/src/mm-base-sim.c
index 104e7f8d..59af3a88 100644
--- a/src/mm-base-sim.c
+++ b/src/mm-base-sim.c
@@ -30,20 +30,22 @@
#include "mm-base-sim.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
static void async_initable_iface_init (GAsyncInitableIface *iface);
+static void log_object_iface_init (MMLogObjectInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMBaseSim, mm_base_sim, MM_GDBUS_TYPE_SIM_SKELETON, 0,
- G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
- async_initable_iface_init))
+ G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_iface_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
PROP_PATH,
PROP_CONNECTION,
PROP_MODEM,
+ PROP_SLOT_NUMBER,
PROP_LAST
};
@@ -57,10 +59,16 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBaseSimPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
+ guint dbus_id;
+
/* The modem which owns this SIM */
MMBaseModem *modem;
/* The path where the SIM object is exported */
gchar *path;
+
+ /* The SIM slot number, which will be 0 always if the system
+ * doesn't support multiple SIMS. */
+ guint slot_number;
};
static guint signals[SIGNAL_LAST] = { 0 };
@@ -70,10 +78,9 @@ static guint signals[SIGNAL_LAST] = { 0 };
void
mm_base_sim_export (MMBaseSim *self)
{
- static guint id = 0;
gchar *path;
- path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%d", id++);
+ path = g_strdup_printf (MM_DBUS_SIM_PREFIX "/%d", self->priv->dbus_id);
g_object_set (self,
MM_BASE_SIM_PATH, path,
NULL);
@@ -81,6 +88,21 @@ mm_base_sim_export (MMBaseSim *self)
}
/*****************************************************************************/
+/* Reprobe when a puk lock is discovered after pin1_retries are exhausted */
+
+static void
+reprobe_if_puk_discovered (MMBaseSim *self,
+ GError *error)
+{
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK)) {
+ mm_obj_dbg (self, "Discovered PUK lock, discarding old modem...");
+ mm_base_modem_process_sim_event (self->priv->modem);
+ }
+}
+
+/*****************************************************************************/
/* CHANGE PIN (Generic implementation) */
static gboolean
@@ -88,23 +110,23 @@ change_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
change_pin_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -114,23 +136,20 @@ change_pin (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- change_pin);
+ task = g_task_new (self, NULL, callback, user_data);
command = g_strdup_printf ("+CPWD=\"SC\",\"%s\",\"%s\"",
old_pin,
new_pin);
- mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
+ mm_base_modem_at_command (self->priv->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)change_pin_ready,
- result);
+ task);
g_free (command);
}
@@ -158,17 +177,18 @@ handle_change_pin_context_free (HandleChangePinContext *ctx)
}
static void
-after_change_update_lock_info_ready (MMIfaceModem *self,
+after_change_update_lock_info_ready (MMIfaceModem *modem,
GAsyncResult *res,
HandleChangePinContext *ctx)
{
/* We just want to ensure that we tried to update the unlock
* retries, no big issue if it failed */
- mm_iface_modem_update_lock_info_finish (self, res, NULL);
+ mm_iface_modem_update_lock_info_finish (modem, res, NULL);
if (ctx->save_error) {
- g_dbus_method_invocation_take_error (ctx->invocation, ctx->save_error);
- ctx->save_error = NULL;
+ g_dbus_method_invocation_return_gerror (ctx->invocation, ctx->save_error);
+ reprobe_if_puk_discovered (ctx->self, ctx->save_error);
+ g_clear_error (&ctx->save_error);
} else {
mm_gdbus_sim_complete_change_pin (MM_GDBUS_SIM (ctx->self), ctx->invocation);
}
@@ -222,11 +242,21 @@ handle_change_pin_auth_ready (MMBaseModem *modem,
return;
}
+ if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot change PIN: "
+ "SIM not currently active");
+ handle_change_pin_context_free (ctx);
+ return;
+ }
+
MM_BASE_SIM_GET_CLASS (ctx->self)->change_pin (ctx->self,
- ctx->old_pin,
- ctx->new_pin,
- (GAsyncReadyCallback)handle_change_pin_ready,
- ctx);
+ ctx->old_pin,
+ ctx->new_pin,
+ (GAsyncReadyCallback)handle_change_pin_ready,
+ ctx);
}
static gboolean
@@ -260,23 +290,23 @@ enable_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enable_pin_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -286,23 +316,20 @@ enable_pin (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enable_pin);
+ task = g_task_new (self, NULL, callback, user_data);
command = g_strdup_printf ("+CLCK=\"SC\",%d,\"%s\"",
enabled ? 1 : 0,
pin);
- mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
+ mm_base_modem_at_command (self->priv->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)enable_pin_ready,
- result);
+ task);
g_free (command);
}
@@ -338,8 +365,9 @@ after_enable_update_lock_info_ready (MMIfaceModem *modem,
mm_iface_modem_update_lock_info_finish (modem, res, NULL);
if (ctx->save_error) {
- g_dbus_method_invocation_take_error (ctx->invocation, ctx->save_error);
- ctx->save_error = NULL;
+ g_dbus_method_invocation_return_gerror (ctx->invocation, ctx->save_error);
+ reprobe_if_puk_discovered (ctx->self, ctx->save_error);
+ g_clear_error (&ctx->save_error);
} else {
/* Signal about the new lock state */
g_signal_emit (ctx->self, signals[SIGNAL_PIN_LOCK_ENABLED], 0, ctx->enabled);
@@ -395,11 +423,21 @@ handle_enable_pin_auth_ready (MMBaseModem *modem,
return;
}
+ if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot enable/disable PIN: "
+ "SIM not currently active");
+ handle_enable_pin_context_free (ctx);
+ return;
+ }
+
MM_BASE_SIM_GET_CLASS (ctx->self)->enable_pin (ctx->self,
- ctx->pin,
- ctx->enabled,
- (GAsyncReadyCallback)handle_enable_pin_ready,
- ctx);
+ ctx->pin,
+ ctx->enabled,
+ (GAsyncReadyCallback)handle_enable_pin_ready,
+ ctx);
}
static gboolean
@@ -432,23 +470,23 @@ common_send_pin_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
send_pin_puk_ready (MMBaseModem *modem,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
@@ -458,24 +496,21 @@ common_send_pin_puk (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gchar *command;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_send_pin_puk);
+ task = g_task_new (self, NULL, callback, user_data);
command = (puk ?
g_strdup_printf ("+CPIN=\"%s\",\"%s\"", puk, pin) :
g_strdup_printf ("+CPIN=\"%s\"", pin));
- mm_base_modem_at_command (MM_BASE_MODEM (self->priv->modem),
+ mm_base_modem_at_command (self->priv->modem,
command,
3,
FALSE,
(GAsyncReadyCallback)send_pin_puk_ready,
- result);
+ task);
g_free (command);
}
@@ -501,27 +536,6 @@ send_pin (MMBaseSim *self,
/*****************************************************************************/
/* SEND PIN/PUK (common logic) */
-typedef struct {
- MMBaseSim *self;
- GSimpleAsyncResult *result;
- GError *save_error;
- gulong wait_for_unlock_id;
-} SendPinPukContext;
-
-static void
-send_pin_puk_context_complete_and_free (SendPinPukContext *ctx)
-{
- if (ctx->wait_for_unlock_id)
- g_signal_handler_disconnect (ctx->self->priv->modem,
- ctx->wait_for_unlock_id);
- if (ctx->save_error)
- g_error_free (ctx->save_error);
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static GError *
error_for_unlock_check (MMModemLock lock)
{
@@ -555,66 +569,66 @@ error_for_unlock_check (MMModemLock lock)
}
gboolean
-mm_base_sim_send_pin_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+mm_base_sim_send_pin_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
gboolean
-mm_base_sim_send_puk_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+mm_base_sim_send_puk_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
update_lock_info_ready (MMIfaceModem *modem,
GAsyncResult *res,
- SendPinPukContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MMModemLock lock;
+ GError *error = NULL;
+ MMModemLock lock;
lock = mm_iface_modem_update_lock_info_finish (modem, res, &error);
- /* Even if we may be SIM-PIN2/PUK2 locked, we don't consider this an error
- * in the PIN/PUK sending */
- if (lock != MM_MODEM_LOCK_NONE &&
- lock != MM_MODEM_LOCK_SIM_PIN2 &&
- lock != MM_MODEM_LOCK_SIM_PUK2) {
+ /* Consider it only an error if SIM-PIN/PUK is locked or lock is unknown */
+ if (lock == MM_MODEM_LOCK_UNKNOWN ||
+ lock == MM_MODEM_LOCK_SIM_PIN ||
+ lock == MM_MODEM_LOCK_SIM_PUK) {
+ const GError *saved_error;
+
/* Device is locked. Now:
+ * - If we got an error during update_lock_info, report it. The sim might have been blocked.
* - If we got an error in the original send-pin action, report it.
- * - If we got an error in the pin-check action, report it.
* - Otherwise, build our own error from the lock code.
*/
- if (ctx->save_error) {
- g_simple_async_result_take_error (ctx->result, ctx->save_error);
- ctx->save_error = NULL;
- g_clear_error (&error);
- } else if (error)
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_take_error (ctx->result,
- error_for_unlock_check (lock));
- send_pin_puk_context_complete_and_free (ctx);
- return;
- }
+ if (!error) {
+ saved_error = g_task_get_task_data (task);
+ if (saved_error)
+ error = g_error_copy (saved_error);
+ else
+ error = error_for_unlock_check (lock);
+ }
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- send_pin_puk_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
-send_pin_ready (MMBaseSim *self,
+send_pin_ready (MMBaseSim *self,
GAsyncResult *res,
- SendPinPukContext *ctx)
+ GTask *task)
{
- MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
+ GError *error = NULL;
+ MMModemLock known_lock = MM_MODEM_LOCK_UNKNOWN;
- if (!MM_BASE_SIM_GET_CLASS (self)->send_pin_finish (self, res, &ctx->save_error)) {
- if (g_error_matches (ctx->save_error,
+ if (!MM_BASE_SIM_GET_CLASS (self)->send_pin_finish (self, res, &error)) {
+ g_task_set_task_data (task, error, (GDestroyNotify)g_error_free);
+ if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK))
known_lock = MM_MODEM_LOCK_SIM_PUK;
@@ -625,91 +639,142 @@ send_pin_ready (MMBaseSim *self,
MM_IFACE_MODEM (self->priv->modem),
known_lock,
(GAsyncReadyCallback)update_lock_info_ready,
- ctx);
+ task);
}
static void
-send_puk_ready (MMBaseSim *self,
+send_puk_ready (MMBaseSim *self,
GAsyncResult *res,
- SendPinPukContext *ctx)
+ GTask *task)
{
- MM_BASE_SIM_GET_CLASS (self)->send_puk_finish (self, res, &ctx->save_error);
+ GError *error = NULL;
+
+ if (!MM_BASE_SIM_GET_CLASS (self)->send_puk_finish (self, res, &error))
+ g_task_set_task_data (task, error, (GDestroyNotify)g_error_free);
/* Once pin/puk has been sent, recheck lock */
mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self->priv->modem),
MM_MODEM_LOCK_UNKNOWN, /* ask */
(GAsyncReadyCallback)update_lock_info_ready,
- ctx);
+ task);
}
void
-mm_base_sim_send_pin (MMBaseSim *self,
- const gchar *pin,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_base_sim_send_pin (MMBaseSim *self,
+ const gchar *pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- SendPinPukContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
/* If sending PIN is not implemented, report an error */
if (!MM_BASE_SIM_GET_CLASS (self)->send_pin ||
!MM_BASE_SIM_GET_CLASS (self)->send_pin_finish) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot send PIN: "
- "operation not supported");
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot send PIN: operation not supported");
+ g_object_unref (task);
return;
}
- ctx = g_new0 (SendPinPukContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_sim_send_pin);
+ /* Only allow sending SIM-PIN if really SIM-PIN locked */
+ if (mm_iface_modem_get_unlock_required (MM_IFACE_MODEM (self->priv->modem)) != MM_MODEM_LOCK_SIM_PIN) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Cannot send PIN: device is not SIM-PIN locked");
+ g_object_unref (task);
+ return;
+ }
MM_BASE_SIM_GET_CLASS (self)->send_pin (self,
- pin,
- (GAsyncReadyCallback)send_pin_ready,
- ctx);
+ pin,
+ (GAsyncReadyCallback)send_pin_ready,
+ task);
}
void
-mm_base_sim_send_puk (MMBaseSim *self,
- const gchar *puk,
- const gchar *new_pin,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_base_sim_send_puk (MMBaseSim *self,
+ const gchar *puk,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- SendPinPukContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
/* If sending PIN is not implemented, report an error */
if (!MM_BASE_SIM_GET_CLASS (self)->send_puk ||
!MM_BASE_SIM_GET_CLASS (self)->send_puk_finish) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot send PUK: "
- "operation not supported");
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot send PUK: operation not supported");
+ g_object_unref (task);
return;
}
- ctx = g_new0 (SendPinPukContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_base_sim_send_puk);
+ /* Only allow sending SIM-PUK if really SIM-PUK locked */
+ if (mm_iface_modem_get_unlock_required (MM_IFACE_MODEM (self->priv->modem)) != MM_MODEM_LOCK_SIM_PUK) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Cannot send PUK: device is not SIM-PUK locked");
+ g_object_unref (task);
+ return;
+ }
MM_BASE_SIM_GET_CLASS (self)->send_puk (self,
- puk,
- new_pin,
- (GAsyncReadyCallback)send_puk_ready,
- ctx);
+ puk,
+ new_pin,
+ (GAsyncReadyCallback)send_puk_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* LOAD SIM IDENTIFIER */
+
+gchar *
+mm_base_sim_load_sim_identifier_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_sim_identifier_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ gchar *simid;
+ GError *error = NULL;
+
+ simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &error);
+ if (!simid)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, simid, g_free);
+ g_object_unref (task);
+}
+
+void
+mm_base_sim_load_sim_identifier (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier ||
+ !MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "not implemented");
+ g_object_unref (task);
+ return;
+ }
+
+ MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier (
+ self,
+ (GAsyncReadyCallback)load_sim_identifier_ready,
+ task);
}
/*****************************************************************************/
@@ -737,9 +802,11 @@ handle_send_pin_ready (MMBaseSim *self,
{
GError *error = NULL;
- if (!mm_base_sim_send_pin_finish (self, res, &error))
- g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ if (!mm_base_sim_send_pin_finish (self, res, &error)) {
+ g_dbus_method_invocation_return_gerror (ctx->invocation, error);
+ reprobe_if_puk_discovered (self, error);
+ g_clear_error (&error);
+ } else
mm_gdbus_sim_complete_send_pin (MM_GDBUS_SIM (self), ctx->invocation);
handle_send_pin_context_free (ctx);
@@ -758,6 +825,16 @@ handle_send_pin_auth_ready (MMBaseModem *modem,
return;
}
+ if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot send PIN: "
+ "SIM not currently active");
+ handle_send_pin_context_free (ctx);
+ return;
+ }
+
mm_base_sim_send_pin (ctx->self,
ctx->pin,
(GAsyncReadyCallback)handle_send_pin_ready,
@@ -810,12 +887,27 @@ handle_send_puk_ready (MMBaseSim *self,
HandleSendPukContext *ctx)
{
GError *error = NULL;
-
- if (!mm_base_sim_send_puk_finish (self, res, &error))
+ gboolean sim_error = FALSE;
+
+ if (!mm_base_sim_send_puk_finish (self, res, &error)) {
+ sim_error = 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_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ } else
mm_gdbus_sim_complete_send_puk (MM_GDBUS_SIM (self), ctx->invocation);
+ if (sim_error) {
+ mm_obj_info (self, "Received critical sim error. SIM might be permanently blocked. Reprobing...");
+ mm_base_modem_process_sim_event (self->priv->modem);
+ }
+
handle_send_puk_context_free (ctx);
}
@@ -832,6 +924,16 @@ handle_send_puk_auth_ready (MMBaseModem *modem,
return;
}
+ if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot send PUK: "
+ "SIM not currently active");
+ handle_send_puk_context_free (ctx);
+ return;
+ }
+
mm_base_sim_send_puk (ctx->self,
ctx->puk,
ctx->new_pin,
@@ -862,6 +964,568 @@ handle_send_puk (MMBaseSim *self,
}
/*****************************************************************************/
+/* Check if preferred networks is supported.
+ *
+ * Modems like the Intel-based EM7345 fail very badly when CPOL? is run, even
+ * completely blocking the AT port after that. We need to avoid running any
+ * CPOL related command in these modules.
+ */
+
+static gboolean
+check_preferred_networks_disabled (MMBaseSim *self)
+{
+ MMPort *primary;
+
+ primary = MM_PORT (mm_base_modem_peek_port_primary (self->priv->modem));
+ return (primary ?
+ mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (primary),
+ "ID_MM_PREFERRED_NETWORKS_CPOL_DISABLED") :
+ FALSE);
+}
+
+/*****************************************************************************/
+/* SET PREFERRED NETWORKS (Generic implementation) */
+
+/* Setting preferred network list with AT+CPOL is a complicated procedure with
+ * the following steps:
+ * 1. Using AT+CPOL=? to get SIM capacity; the capacity is checked to ensure
+ * that the list is not too large for the SIM card.
+ * 2. Reading existing preferred networks from SIM with AT+CPOL?.
+ * 3. Clearing existing networks with a series of AT+CPOL=<index> commands.
+ * 4. Setting the new list by invoking AT+CPOL for each network.
+ *
+ * There are some complications with AT+CPOL handling which makes the work more
+ * difficult for us. It seems that modems can only handle a certain exact number
+ * of access technology identifiers - and this cannot be certainly known in
+ * advance.
+ *
+ * If AT+CPOL? in step 2 returns anything, we can deduce the number of supported
+ * identifiers there. But if there were no networks configured earlier, we must
+ * start with a default based on modem capacity and work our way down from there
+ * if the AT+CPOL command fails.
+ */
+
+static void set_preferred_networks_set_next (MMBaseSim *self,
+ GTask *task);
+static void set_preferred_networks_clear_next (MMBaseSim *self,
+ GTask *task);
+
+typedef struct {
+ GList *set_list;
+ /* AT+CPOL indices that must be cleared before setting the networks. */
+ GArray *clear_index;
+ /* Number of access technology identifiers we will set. */
+ guint act_count;
+ /* If TRUE, we know that act_count is something the modem can handle */
+ gboolean act_count_verified;
+ /* Index of preferred network currently being set (0 = first) */
+ guint current_write_index;
+ /* Operation error code */
+ GError *error;
+} SetPreferredNetworksContext;
+
+static void
+set_preferred_network_context_free (SetPreferredNetworksContext *ctx)
+{
+ g_list_free_full (ctx->set_list, (GDestroyNotify) mm_sim_preferred_network_free);
+ g_clear_error (&ctx->error);
+ g_array_free (ctx->clear_index, TRUE);
+ g_slice_free (SetPreferredNetworksContext, ctx);
+}
+
+static gboolean
+set_preferred_networks_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parse_old_preferred_networks (const gchar *response,
+ SetPreferredNetworksContext *ctx)
+{
+ gchar **entries;
+ gchar **iter;
+
+ entries = g_strsplit_set (response, "\r\n", -1);
+ for (iter = entries; iter && *iter; iter++) {
+ guint index;
+ guint act_count = 0;
+
+ g_strstrip (*iter);
+ if (strlen (*iter) == 0)
+ continue;
+
+ if (mm_sim_parse_cpol_query_response (*iter,
+ &index,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ &act_count,
+ NULL) && index > 0) {
+ /* Remember how many access technologies the modem/SIM can take */
+ if (!ctx->act_count_verified || act_count > ctx->act_count) {
+ ctx->act_count = act_count;
+ ctx->act_count_verified = TRUE;
+ }
+
+ /* Store the index to be cleared */
+ g_array_append_val (ctx->clear_index, index);
+ }
+ }
+ g_strfreev (entries);
+}
+
+/* This function is called only in error case, after reloading the network list from SIM. */
+static void
+set_preferred_networks_reload_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GList *preferred_nets_list;
+ SetPreferredNetworksContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load list of preferred networks: %s", error->message);
+ g_error_free (error);
+ }
+
+ mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
+ mm_sim_preferred_network_list_get_variant (preferred_nets_list));
+ g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
+
+ /* Return the original error stored in our context */
+ g_task_return_error (task, g_steal_pointer (&ctx->error));
+ g_object_unref (task);
+}
+
+
+static gboolean
+set_preferred_networks_retry_command (MMBaseSim *self,
+ SetPreferredNetworksContext *ctx)
+{
+ /* If we haven't yet determined the number of access technology parameters supported by
+ * the modem, try reducing the count if possible and retry with the reduced count.
+ */
+ if (!ctx->act_count_verified && ctx->act_count > 0) {
+ ctx->act_count--;
+ mm_obj_dbg (self, "retrying operation with %u access technologies", ctx->act_count);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+set_preferred_network_reload_and_return_error (MMBaseSim *self,
+ GTask *task,
+ GError *error)
+{
+ SetPreferredNetworksContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* Reload the complete list from SIM card to ensure that the PreferredNetworks
+ * property matches with whatever is actually on the SIM.
+ */
+ if (MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks &&
+ MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) {
+ ctx->error = error;
+ MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks (
+ self,
+ (GAsyncReadyCallback)set_preferred_networks_reload_ready,
+ task);
+ } else {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ }
+}
+
+static void
+set_preferred_networks_set_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ SetPreferredNetworksContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ /* The command may fail; check if we can retry */
+ if (error) {
+ if (!set_preferred_networks_retry_command (self, ctx)) {
+ /* Retrying not possible, failing... */
+ mm_obj_warn (self, "failed to set preferred networks: '%s'", error->message);
+ set_preferred_network_reload_and_return_error (self, task, error);
+ return;
+ }
+ /* Retrying possible */
+ g_clear_error (&error);
+ } else {
+ /* Last set operation was successful, so we know for sure how many access technologies
+ * the modem can take.
+ */
+ ctx->act_count_verified = TRUE;
+ ctx->current_write_index++;
+ }
+
+ set_preferred_networks_set_next (self, task);
+}
+
+static gboolean
+set_preferred_networks_check_support (MMBaseSim *self,
+ SetPreferredNetworksContext *ctx,
+ MMSimPreferredNetwork *network,
+ GError **error)
+{
+ MMModemAccessTechnology requested_act;
+ MMModemAccessTechnology supported_act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ requested_act = mm_sim_preferred_network_get_access_technology (network);
+ if (ctx->act_count >= 1)
+ supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
+ if (ctx->act_count >= 2)
+ supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
+ if (ctx->act_count >= 3)
+ supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ if (ctx->act_count >= 4)
+ supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ if (ctx->act_count >= 5)
+ supported_act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+
+ if (requested_act & ~supported_act) {
+ g_autofree gchar *act_string = NULL;
+
+ act_string = mm_modem_access_technology_build_string_from_mask (requested_act & ~supported_act);
+ mm_obj_warn (self, "cannot set preferred net '%s'; access technology '%s' not supported by modem/SIM",
+ mm_sim_preferred_network_get_operator_code (network), act_string);
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Access technology unsupported by modem or SIM");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Set next preferred network in queue */
+static void
+set_preferred_networks_set_next (MMBaseSim *self,
+ GTask *task)
+{
+ SetPreferredNetworksContext *ctx;
+ g_autofree gchar *command = NULL;
+ MMSimPreferredNetwork *current_network;
+ const gchar *operator_code;
+ MMModemAccessTechnology act;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ current_network = (MMSimPreferredNetwork *) g_list_nth_data (ctx->set_list, ctx->current_write_index);
+ if (current_network == NULL) {
+ /* No more networks to set; we are done. */
+ mm_obj_dbg (self, "setting preferred networks completed.");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!set_preferred_networks_check_support (self, ctx, current_network, &error)) {
+ set_preferred_network_reload_and_return_error (self, task, error);
+ return;
+ }
+
+ operator_code = mm_sim_preferred_network_get_operator_code (current_network);
+ act = mm_sim_preferred_network_get_access_technology (current_network);
+
+ /* Assemble the command to set the network */
+ command = g_strdup_printf ("+CPOL=%u,2,\"%s\"%s%s%s%s%s", ctx->current_write_index + 1, operator_code,
+ ctx->act_count == 0 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_GSM) ? ",1" : ",0"),
+ ctx->act_count <= 1 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT) ? ",1" : ",0"),
+ ctx->act_count <= 2 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS) ? ",1" : ",0"),
+ ctx->act_count <= 3 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_LTE) ? ",1" : ",0"),
+ ctx->act_count <= 4 ? "" : ((act & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) ? ",1" : ",0"));
+ mm_base_modem_at_command (
+ self->priv->modem,
+ command,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_preferred_networks_set_ready,
+ task);
+}
+
+static void
+set_preferred_networks_clear_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't clear preferred network entry: '%s'", error->message);
+ set_preferred_network_reload_and_return_error (self, task, error);
+ return;
+ }
+
+ set_preferred_networks_clear_next (self, task);
+}
+
+/* Clear one of the networks in the clear list */
+static void
+set_preferred_networks_clear_next (MMBaseSim *self,
+ GTask *task)
+{
+ SetPreferredNetworksContext *ctx;
+ g_autofree gchar *command = NULL;
+ guint current_clear_index;
+
+ ctx = g_task_get_task_data (task);
+
+ /* Clear from last index to first, since some modems (e.g. u-blox) may shift up items
+ * following the cleared ones.
+ */
+ if (ctx->clear_index->len > 0) {
+ current_clear_index = g_array_index (ctx->clear_index, guint, ctx->clear_index->len - 1);
+ g_array_remove_index (ctx->clear_index, ctx->clear_index->len - 1);
+ command = g_strdup_printf ("+CPOL=%u", current_clear_index);
+ mm_base_modem_at_command (
+ self->priv->modem,
+ command,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_preferred_networks_clear_ready,
+ task);
+ return;
+ }
+ /* All clear operations done; start setting new networks */
+ set_preferred_networks_set_next (self, task);
+}
+
+static void
+set_preferred_networks_load_existing_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ GError *error = NULL;
+ SetPreferredNetworksContext *ctx;
+ const gchar *response;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load existing preferred network list: '%s'", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ parse_old_preferred_networks (response, ctx);
+ set_preferred_networks_clear_next (self, task);
+}
+
+static void
+set_preferred_networks_query_sim_capacity_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ GError *error = NULL;
+ SetPreferredNetworksContext *ctx;
+ const gchar *response;
+ guint max_index;
+ guint networks_count;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't query preferred network list capacity: '%s'", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!mm_sim_parse_cpol_test_response (response, NULL, &max_index, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Compare the number of networks to maximum index returned by AT+CPOL=? */
+ networks_count = g_list_length (ctx->set_list);
+ if (networks_count > max_index) {
+ mm_obj_warn (self, "can't set %u preferred networks; SIM capacity: %u", networks_count, max_index);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY,
+ "Too many networks; SIM capacity %u", max_index);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "setting %u preferred networks, SIM capacity: %u", networks_count, max_index);
+ mm_base_modem_at_command (
+ self->priv->modem,
+ "+CPOL?",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)set_preferred_networks_load_existing_ready,
+ task);
+}
+
+static void
+set_preferred_networks (MMBaseSim *self,
+ GList *preferred_network_list,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ SetPreferredNetworksContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (check_preferred_networks_disabled (self)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "setting preferred networks is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "set preferred networks: loading existing networks...");
+
+ ctx = g_slice_new0 (SetPreferredNetworksContext);
+ ctx->set_list = mm_sim_preferred_network_list_copy (preferred_network_list);
+ ctx->clear_index = g_array_new (FALSE, TRUE, sizeof (guint));
+ if (mm_iface_modem_is_5g (MM_IFACE_MODEM (self->priv->modem)))
+ ctx->act_count = 5;
+ else if (mm_iface_modem_is_4g (MM_IFACE_MODEM (self->priv->modem)))
+ ctx->act_count = 4;
+ else if (mm_iface_modem_is_3g (MM_IFACE_MODEM (self->priv->modem)))
+ ctx->act_count = 3;
+ else if (mm_iface_modem_is_2g (MM_IFACE_MODEM (self->priv->modem)))
+ ctx->act_count = 2;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) set_preferred_network_context_free);
+
+ /* Query SIM capacity first to find out how many preferred networks it can take */
+ mm_base_modem_at_command (
+ self->priv->modem,
+ "+CPOL=?",
+ 20,
+ FALSE, /* Do not cache, the response depends on SIM card properties */
+ (GAsyncReadyCallback)set_preferred_networks_query_sim_capacity_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* SET PREFERRED NETWORKS (DBus call handling) */
+
+typedef struct {
+ MMBaseSim *self;
+ GDBusMethodInvocation *invocation;
+ GVariant *networks;
+} HandleSetPreferredNetworksContext;
+
+static void
+handle_set_preferred_networks_context_free (HandleSetPreferredNetworksContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_variant_unref (ctx->networks);
+ g_free (ctx);
+}
+
+static void
+handle_set_preferred_networks_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ HandleSetPreferredNetworksContext *ctx)
+{
+ GError *error = NULL;
+
+ MM_BASE_SIM_GET_CLASS (self)->set_preferred_networks_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't set preferred networks: %s", error->message);
+ g_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error));
+ } else {
+ mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self), ctx->networks);
+ mm_gdbus_sim_complete_set_preferred_networks (MM_GDBUS_SIM (self), ctx->invocation);
+ }
+
+ handle_set_preferred_networks_context_free (ctx);
+}
+
+static void
+handle_set_preferred_networks_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleSetPreferredNetworksContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_preferred_networks_context_free (ctx);
+ return;
+ }
+
+ if (!mm_gdbus_sim_get_active (MM_GDBUS_SIM (ctx->self))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set preferred networks: "
+ "SIM not currently active");
+ handle_set_preferred_networks_context_free (ctx);
+ return;
+ }
+
+ if (!MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks ||
+ !MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set preferred networks: "
+ "not implemented");
+ handle_set_preferred_networks_context_free (ctx);
+ return;
+ }
+
+ MM_BASE_SIM_GET_CLASS (ctx->self)->set_preferred_networks (
+ ctx->self,
+ mm_sim_preferred_network_list_new_from_variant (ctx->networks),
+ (GAsyncReadyCallback)handle_set_preferred_networks_ready,
+ ctx);
+}
+
+static gboolean
+handle_set_preferred_networks (MMBaseSim *self,
+ GDBusMethodInvocation *invocation,
+ GVariant *networks_variant)
+{
+ HandleSetPreferredNetworksContext *ctx;
+
+ ctx = g_new0 (HandleSetPreferredNetworksContext, 1);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->networks = g_variant_ref (networks_variant);
+
+ mm_base_modem_authorize (self->priv->modem,
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_preferred_networks_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
static void
sim_dbus_export (MMBaseSim *self)
@@ -885,14 +1549,16 @@ sim_dbus_export (MMBaseSim *self)
"handle-send-puk",
G_CALLBACK (handle_send_puk),
NULL);
+ g_signal_connect (self,
+ "handle-set-preferred-networks",
+ G_CALLBACK (handle_set_preferred_networks),
+ NULL);
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
self->priv->connection,
self->priv->path,
&error)) {
- mm_warn ("couldn't export SIM at '%s': '%s'",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "couldn't export SIM: %s", error->message);
g_error_free (error);
}
}
@@ -913,6 +1579,34 @@ mm_base_sim_get_path (MMBaseSim *self)
return self->priv->path;
}
+guint
+mm_base_sim_get_slot_number (MMBaseSim *self)
+{
+ return self->priv->slot_number;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_base_sim_is_emergency_number (MMBaseSim *self,
+ const gchar *number)
+{
+ const gchar *const *emergency_numbers;
+ guint i;
+
+ emergency_numbers = mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self));
+
+ if (!emergency_numbers)
+ return FALSE;
+
+ for (i = 0; emergency_numbers[i]; i++) {
+ if (g_strcmp0 (number, emergency_numbers[i]) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
/*****************************************************************************/
#undef STR_REPLY_READY_FN
@@ -920,60 +1614,291 @@ mm_base_sim_get_path (MMBaseSim *self)
static void \
NAME##_command_ready (MMBaseModem *modem, \
GAsyncResult *res, \
- GSimpleAsyncResult *operation_result) \
+ GTask *task) \
{ \
GError *error = NULL; \
const gchar *response; \
\
response = mm_base_modem_at_command_finish (modem, res, &error); \
if (error) \
- g_simple_async_result_take_error (operation_result, error); \
+ g_task_return_error (task, error); \
else \
- g_simple_async_result_set_op_res_gpointer (operation_result, \
- (gpointer)response, \
- NULL); \
+ g_task_return_pointer (task, g_strdup (response), g_free); \
\
- g_simple_async_result_complete (operation_result); \
- g_object_unref (operation_result); \
+ g_object_unref (task); \
}
/*****************************************************************************/
-/* SIM IDENTIFIER */
+/* Emergency numbers */
-static gchar *
-parse_iccid (const gchar *response,
- GError **error)
+static GStrv
+parse_emergency_numbers (const gchar *response,
+ GError **error)
{
- gchar buf[21];
- const gchar *str;
- gint sw1;
- gint sw2;
- gboolean success = FALSE;
+ guint sw1 = 0;
+ guint sw2 = 0;
+ gchar *hex = 0;
+ GStrv ret;
+
+ if (!mm_3gpp_parse_crsm_response (response, &sw1, &sw2, &hex, error))
+ return NULL;
- memset (buf, 0, sizeof (buf));
- str = mm_strip_tag (response, "+CRSM:");
- if (sscanf (str, "%d,%d,\"%20c\"", &sw1, &sw2, (char *) &buf) == 3)
- success = TRUE;
- else {
- /* May not include quotes... */
- if (sscanf (str, "%d,%d,%20c", &sw1, &sw2, (char *) &buf) == 3)
- success = TRUE;
+ if ((sw1 == 0x90 && sw2 == 0x00) ||
+ (sw1 == 0x91) ||
+ (sw1 == 0x92) ||
+ (sw1 == 0x9f)) {
+ ret = mm_3gpp_parse_emergency_numbers (hex, error);
+ g_free (hex);
+ return ret;
}
- if (!success) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse the CRSM response");
+ g_free (hex);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
+ sw1, sw2);
+ return NULL;
+}
+
+static GStrv
+load_emergency_numbers_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gchar *result;
+ GStrv emergency_numbers;
+ guint i;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return NULL;
+
+ emergency_numbers = parse_emergency_numbers (result, error);
+ g_free (result);
+
+ if (!emergency_numbers)
+ return NULL;
+
+ for (i = 0; emergency_numbers[i]; i++)
+ mm_obj_dbg (self, "loaded emergency number: %s", emergency_numbers[i]);
+
+ return emergency_numbers;
+}
+
+STR_REPLY_READY_FN (load_emergency_numbers)
+
+static void
+load_emergency_numbers (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_obj_dbg (self, "loading emergency numbers...");
+
+ /* READ BINARY of EF_ECC (Emergency Call Codes) ETSI TS 51.011 section 10.3.27 */
+ mm_base_modem_at_command (
+ self->priv->modem,
+ "+CRSM=176,28599,0,0,15",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_emergency_numbers_command_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Preferred networks */
+
+static GList *
+parse_preferred_networks (const gchar *response,
+ GError **error)
+{
+ gchar **entries;
+ gchar **iter;
+ GList *result = NULL;
+
+ entries = g_strsplit_set (response, "\r\n", -1);
+ for (iter = entries; iter && *iter; iter++) {
+ gchar *operator_code = NULL;
+ gboolean gsm_act;
+ gboolean gsm_compact_act;
+ gboolean utran_act;
+ gboolean eutran_act;
+ gboolean ngran_act;
+ MMSimPreferredNetwork *preferred_network = NULL;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ g_strstrip (*iter);
+ if (strlen (*iter) == 0)
+ continue;
+
+ if (mm_sim_parse_cpol_query_response (*iter,
+ NULL,
+ &operator_code,
+ &gsm_act,
+ &gsm_compact_act,
+ &utran_act,
+ &eutran_act,
+ &ngran_act,
+ NULL,
+ error)) {
+ preferred_network = mm_sim_preferred_network_new ();
+ mm_sim_preferred_network_set_operator_code (preferred_network, operator_code);
+ if (gsm_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
+ if (gsm_compact_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
+ if (utran_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ if (eutran_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ if (ngran_act)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+ mm_sim_preferred_network_set_access_technology (preferred_network, act);
+ result = g_list_append (result, preferred_network);
+ } else
+ break;
+ g_free (operator_code);
}
+ g_strfreev (entries);
+
+ return result;
+}
+
+static GList *
+load_preferred_networks_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gchar *result;
+ GList *preferred_network_list;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return NULL;
+
+ preferred_network_list = parse_preferred_networks (result, error);
+ mm_obj_dbg (self, "loaded %u preferred networks", g_list_length (preferred_network_list));
+
+ g_free (result);
+
+ return preferred_network_list;
+}
+
+STR_REPLY_READY_FN (load_preferred_networks)
+
+static void
+load_preferred_networks_set_format_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ /* Ignore error */
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_dbg (self, "setting preferred network list format failed: '%s'", error->message);
+ g_error_free (error);
+ }
+
+ mm_obj_dbg (self, "loading preferred networks...");
+
+ mm_base_modem_at_command (
+ modem,
+ "+CPOL?",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_preferred_networks_command_ready,
+ task);
+}
+
+static void
+load_preferred_networks_cpls_command_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBaseSim *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ /* AT+CPLS may not be supported so we ignore any error and proceed even if it fails */
+ mm_base_modem_at_command_finish (modem, res, &error);
+ if (error) {
+ mm_obj_dbg (self, "selecting user-defined preferred network list failed: '%s'", error->message);
+ g_error_free (error);
+ }
+
+ mm_obj_dbg (self, "setting preferred networks format...");
+
+ /* Request numeric MCCMNC format */
+ mm_base_modem_at_command (
+ modem,
+ "+CPOL=,2",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_preferred_networks_set_format_ready,
+ task);
+}
+
+static void
+load_preferred_networks (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (check_preferred_networks_disabled (self)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "setting preferred networks is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Invoke AT+CPLS=0 first to make sure the correct (user-defined) preferred network list is selected */
+ mm_obj_dbg (self, "selecting user-defined preferred network list...");
+
+ mm_base_modem_at_command (
+ self->priv->modem,
+ "+CPLS=0",
+ 20,
+ FALSE,
+ (GAsyncReadyCallback)load_preferred_networks_cpls_command_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* ICCID */
+
+static gchar *
+parse_iccid (const gchar *response,
+ GError **error)
+{
+ guint sw1 = 0;
+ guint sw2 = 0;
+ gchar *hex = 0;
+ gchar *ret;
+
+ if (!mm_3gpp_parse_crsm_response (response,
+ &sw1,
+ &sw2,
+ &hex,
+ error))
+ return NULL;
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
- return mm_3gpp_parse_iccid (buf, error);
+ ret = mm_3gpp_parse_iccid (hex, error);
+ g_free (hex);
+ return ret;
} else {
+ g_free (hex);
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -988,18 +1913,19 @@ load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- const gchar *result;
+ gchar *result;
gchar *sim_identifier;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return NULL;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
sim_identifier = parse_iccid (result, error);
+ g_free (result);
if (!sim_identifier)
return NULL;
- mm_dbg ("loaded SIM identifier: %s", sim_identifier);
+ mm_obj_dbg (self, "loaded SIM identifier: %s", sim_identifier);
return sim_identifier;
}
@@ -1010,19 +1936,16 @@ load_sim_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading SIM identifier...");
+ mm_obj_dbg (self, "loading SIM identifier...");
/* READ BINARY of EFiccid (ICC Identification) ETSI TS 102.221 section 13.2 */
mm_base_modem_at_command (
- MM_BASE_MODEM (self->priv->modem),
+ self->priv->modem,
"+CRSM=176,12258,0,0,10",
20,
FALSE,
(GAsyncReadyCallback)load_sim_identifier_command_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_sim_identifier));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1058,18 +1981,19 @@ load_imsi_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- const gchar *result;
+ gchar *result;
gchar *imsi;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return NULL;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
imsi = parse_imsi (result, error);
+ g_free (result);
if (!imsi)
return NULL;
- mm_dbg ("loaded IMSI: %s", imsi);
+ mm_obj_dbg (self, "loaded IMSI: %s", imsi);
return imsi;
}
@@ -1080,18 +2004,15 @@ load_imsi (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading IMSI...");
+ mm_obj_dbg (self, "loading IMSI...");
mm_base_modem_at_command (
- MM_BASE_MODEM (self->priv->modem),
+ self->priv->modem,
"+CIMI",
3,
FALSE,
(GAsyncReadyCallback)load_imsi_command_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_imsi));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1101,78 +2022,49 @@ static guint
parse_mnc_length (const gchar *response,
GError **error)
{
- gint sw1;
- gint sw2;
- gboolean success = FALSE;
- gchar hex[51];
-
- memset (hex, 0, sizeof (hex));
- if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
- success = TRUE;
- else {
- /* May not include quotes... */
- if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
- success = TRUE;
- }
+ guint sw1 = 0;
+ guint sw2 = 0;
+ g_autofree gchar *hex = NULL;
- if (!success) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse the CRSM response");
+ if (!mm_3gpp_parse_crsm_response (response,
+ &sw1,
+ &sw2,
+ &hex,
+ error))
return 0;
- }
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
- gsize buflen = 0;
- guint32 mnc_len;
- gchar *bin;
-
- /* Make sure the buffer is only hex characters */
- while (buflen < sizeof (hex) && hex[buflen]) {
- if (!isxdigit (hex[buflen])) {
- hex[buflen] = 0x0;
- break;
- }
- buflen++;
- }
+ gsize buflen = 0;
+ guint32 mnc_len;
+ g_autofree guint8 *bin = NULL;
/* Convert hex string to binary */
- bin = mm_utils_hexstr2bin (hex, &buflen);
- if (!bin || buflen < 4) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM returned malformed response '%s'",
- hex);
- g_free (bin);
+ bin = mm_utils_hexstr2bin (hex, -1, &buflen, error);
+ if (!bin) {
+ g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
+ return 0;
+ }
+ if (buflen < 4) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned malformed response '%s': too short", hex);
return 0;
}
/* MNC length is byte 4 of this SIM file */
- mnc_len = bin[3] & 0xFF;
- if (mnc_len == 2 || mnc_len == 3) {
- g_free (bin);
+ mnc_len = bin[3];
+ if (mnc_len == 2 || mnc_len == 3)
return mnc_len;
- }
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM returned invalid MNC length %d (should be either 2 or 3)",
- mnc_len);
- g_free (bin);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned invalid MNC length %d (should be either 2 or 3)", mnc_len);
return 0;
}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
- sw1, sw2);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2);
return 0;
}
@@ -1183,12 +2075,19 @@ load_operator_identifier_finish (MMBaseSim *self,
{
GError *inner_error = NULL;
const gchar *imsi;
- const gchar *result;
+ gchar *result;
guint mnc_length;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return NULL;
+
+ mnc_length = parse_mnc_length (result, &inner_error);
+ g_free (result);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return NULL;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ }
imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self));
if (!imsi) {
@@ -1199,12 +2098,6 @@ load_operator_identifier_finish (MMBaseSim *self,
return NULL;
}
- mnc_length = parse_mnc_length (result, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return NULL;
- }
-
/* Build Operator ID */
return g_strndup (imsi, 3 + mnc_length);
}
@@ -1216,19 +2109,16 @@ load_operator_identifier (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading Operator ID...");
+ mm_obj_dbg (self, "loading operator ID...");
/* READ BINARY of EFad (Administrative Data) ETSI 51.011 section 10.3.18 */
mm_base_modem_at_command (
- MM_BASE_MODEM (self->priv->modem),
+ self->priv->modem,
"+CRSM=176,28589,0,0,4",
10,
FALSE,
(GAsyncReadyCallback)load_operator_identifier_command_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_operator_identifier));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1238,71 +2128,50 @@ static gchar *
parse_spn (const gchar *response,
GError **error)
{
- gint sw1;
- gint sw2;
- gboolean success = FALSE;
- gchar hex[51];
-
- memset (hex, 0, sizeof (hex));
- if (sscanf (response, "+CRSM:%d,%d,\"%50c\"", &sw1, &sw2, (char *) &hex) == 3)
- success = TRUE;
- else {
- /* May not include quotes... */
- if (sscanf (response, "+CRSM:%d,%d,%50c", &sw1, &sw2, (char *) &hex) == 3)
- success = TRUE;
- }
+ guint sw1 = 0;
+ guint sw2 = 0;
+ g_autofree gchar *hex = NULL;
- if (!success) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse the CRSM response");
+ if (!mm_3gpp_parse_crsm_response (response,
+ &sw1,
+ &sw2,
+ &hex,
+ error))
return NULL;
- }
if ((sw1 == 0x90 && sw2 == 0x00) ||
(sw1 == 0x91) ||
(sw1 == 0x92) ||
(sw1 == 0x9f)) {
- gsize buflen = 0;
- gchar *bin;
- gchar *utf8;
-
- /* Make sure the buffer is only hex characters */
- while (buflen < sizeof (hex) && hex[buflen]) {
- if (!isxdigit (hex[buflen])) {
- hex[buflen] = 0x0;
- break;
- }
- buflen++;
- }
+ g_autoptr(GByteArray) bin_array = NULL;
+ g_autofree guint8 *bin = NULL;
+ gsize binlen = 0;
/* Convert hex string to binary */
- bin = mm_utils_hexstr2bin (hex, &buflen);
+ bin = mm_utils_hexstr2bin (hex, -1, &binlen, error);
if (!bin) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM returned malformed response '%s'",
- hex);
+ g_prefix_error (error, "SIM returned malformed response '%s': ", hex);
return NULL;
}
/* Remove the FF filler at the end */
- while (buflen > 1 && bin[buflen - 1] == (char)0xff)
- buflen--;
+ while (binlen > 1 && bin[binlen - 1] == 0xff)
+ binlen--;
+ if (binlen <= 1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM returned empty response '%s'", hex);
+ return NULL;
+ }
+ /* Setup as bytearray.
+ * First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
+ bin_array = g_byte_array_sized_new (binlen - 1);
+ g_byte_array_append (bin_array, bin + 1, binlen - 1);
- /* First byte is metadata; remainder is GSM-7 unpacked into octets; convert to UTF8 */
- utf8 = (gchar *)mm_charset_gsm_unpacked_to_utf8 ((guint8 *)bin + 1, buflen - 1);
- g_free (bin);
- return utf8;
+ return mm_modem_charset_bytearray_to_utf8 (bin_array, MM_MODEM_CHARSET_GSM, FALSE, error);
}
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "SIM failed to handle CRSM request (sw1 %d sw2 %d)",
- sw1, sw2);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM failed to handle CRSM request (sw1 %d sw2 %d)", sw1, sw2);
return NULL;
}
@@ -1311,13 +2180,16 @@ load_operator_name_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- const gchar *result;
+ gchar *result;
+ gchar *spn;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return NULL;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- return parse_spn (result, error);
+ spn = parse_spn (result, error);
+ g_free (result);
+ return spn;
}
STR_REPLY_READY_FN (load_operator_name)
@@ -1327,53 +2199,72 @@ load_operator_name (MMBaseSim *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading Operator Name...");
+ mm_obj_dbg (self, "loading operator name...");
/* READ BINARY of EFspn (Service Provider Name) ETSI 51.011 section 10.3.11 */
mm_base_modem_at_command (
- MM_BASE_MODEM (self->priv->modem),
+ self->priv->modem,
"+CRSM=176,28486,0,0,17",
10,
FALSE,
(GAsyncReadyCallback)load_operator_name_command_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_operator_name));
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+
+MMBaseSim *
+mm_base_sim_new_initialized (MMBaseModem *modem,
+ guint slot_number,
+ gboolean active,
+ const gchar *sim_identifier,
+ const gchar *imsi,
+ const gchar *eid,
+ const gchar *operator_identifier,
+ const gchar *operator_name,
+ const GStrv emergency_numbers)
+{
+ MMBaseSim *sim;
+
+ sim = MM_BASE_SIM (g_object_new (MM_TYPE_BASE_SIM,
+ MM_BASE_SIM_MODEM, modem,
+ MM_BASE_SIM_SLOT_NUMBER, slot_number,
+ "active", active,
+ "sim-identifier", sim_identifier,
+ "imsi", imsi,
+ "eid", eid,
+ "operator-identifier", operator_identifier,
+ "operator-name", operator_name,
+ "emergency-numbers", emergency_numbers,
+ NULL));
+
+ mm_base_sim_export (sim);
+ return sim;
}
/*****************************************************************************/
typedef struct _InitAsyncContext InitAsyncContext;
-static void interface_initialization_step (InitAsyncContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_WAIT_READY,
INITIALIZATION_STEP_SIM_IDENTIFIER,
INITIALIZATION_STEP_IMSI,
+ INITIALIZATION_STEP_EID,
INITIALIZATION_STEP_OPERATOR_ID,
INITIALIZATION_STEP_OPERATOR_NAME,
+ INITIALIZATION_STEP_EMERGENCY_NUMBERS,
+ INITIALIZATION_STEP_PREFERRED_NETWORKS,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitAsyncContext {
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- MMBaseSim *self;
InitializationStep step;
guint sim_identifier_tries;
};
-static void
-init_async_context_free (InitAsyncContext *ctx)
-{
- g_object_unref (ctx->self);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_free (ctx);
-}
-
MMBaseSim *
mm_base_sim_new_finish (GAsyncResult *res,
GError **error)
@@ -1399,18 +2290,20 @@ initable_init_finish (GAsyncInitable *initable,
GAsyncResult *result,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
-load_sim_identifier_ready (MMBaseSim *self,
- GAsyncResult *res,
- InitAsyncContext *ctx)
+init_load_sim_identifier_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ InitAsyncContext *ctx;
GError *error = NULL;
gchar *simid;
- simid = MM_BASE_SIM_GET_CLASS (ctx->self)->load_sim_identifier_finish (self, res, &error);
+ ctx = g_task_get_task_data (task);
+ simid = MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish (self, res, &error);
if (!simid) {
/* TODO: make the retries gobi-specific? */
@@ -1420,12 +2313,11 @@ load_sim_identifier_ready (MMBaseSim *self,
*/
if (++ctx->sim_identifier_tries < 2) {
g_clear_error (&error);
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
return;
}
- mm_warn ("couldn't load SIM identifier: '%s'",
- error ? error->message : "unknown error");
+ mm_obj_warn (self, "couldn't load SIM identifier: %s", error ? error->message : "unknown error");
g_clear_error (&error);
}
@@ -1434,127 +2326,256 @@ load_sim_identifier_ready (MMBaseSim *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
+}
+
+static void
+init_load_emergency_numbers_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitAsyncContext *ctx;
+ GError *error = NULL;
+ GStrv str_list;
+
+ str_list = MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load list of emergency numbers: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (str_list) {
+ mm_gdbus_sim_set_emergency_numbers (MM_GDBUS_SIM (self), (const gchar *const *) str_list);
+ g_strfreev (str_list);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+init_load_preferred_networks_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitAsyncContext *ctx;
+ GError *error = NULL;
+ GList *preferred_nets_list;
+
+ preferred_nets_list = MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load list of preferred networks: %s", error->message);
+ g_error_free (error);
+ }
+
+ mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
+ mm_sim_preferred_network_list_get_variant (preferred_nets_list));
+ g_list_free_full (preferred_nets_list, (GDestroyNotify) mm_sim_preferred_network_free);
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
}
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
static void \
- load_##NAME##_ready (MMBaseSim *self, \
- GAsyncResult *res, \
- InitAsyncContext *ctx) \
+ init_load_##NAME##_ready (MMBaseSim *self, \
+ GAsyncResult *res, \
+ GTask *task) \
{ \
+ InitAsyncContext *ctx; \
GError *error = NULL; \
gchar *val; \
\
- val = MM_BASE_SIM_GET_CLASS (ctx->self)->load_##NAME##_finish (self, res, &error); \
+ val = MM_BASE_SIM_GET_CLASS (self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_sim_set_##NAME (MM_GDBUS_SIM (self), val); \
g_free (val); \
\
if (error) { \
- mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
+ mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
+ ctx = g_task_get_task_data (task); \
ctx->step++; \
- interface_initialization_step (ctx); \
+ interface_initialization_step (task); \
}
STR_REPLY_READY_FN (imsi, "IMSI")
-STR_REPLY_READY_FN (operator_identifier, "Operator identifier")
-STR_REPLY_READY_FN (operator_name, "Operator name")
+STR_REPLY_READY_FN (eid, "EID")
+STR_REPLY_READY_FN (operator_identifier, "operator identifier")
+STR_REPLY_READY_FN (operator_name, "operator name")
+
+static void
+init_wait_sim_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitAsyncContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't wait for SIM to be ready: %s", error->message);
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
static void
-interface_initialization_step (InitAsyncContext *ctx)
+interface_initialization_step (GTask *task)
{
- 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);
+ MMBaseSim *self;
+ InitAsyncContext *ctx;
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
+
+ case INITIALIZATION_STEP_WAIT_READY:
+ if (MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready &&
+ MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->wait_sim_ready (
+ self,
+ (GAsyncReadyCallback)init_wait_sim_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_SIM_IDENTIFIER:
/* SIM ID is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
- if (mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (ctx->self)) == NULL &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_sim_identifier &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_sim_identifier_finish) {
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_sim_identifier (
- ctx->self,
- (GAsyncReadyCallback)load_sim_identifier_ready,
- ctx);
+ if (mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (self)) == NULL &&
+ MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier &&
+ MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_sim_identifier (
+ self,
+ (GAsyncReadyCallback)init_load_sim_identifier_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_IMSI:
/* IMSI is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
- if (mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (ctx->self)) == NULL &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_imsi &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_imsi_finish) {
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_imsi (
- ctx->self,
- (GAsyncReadyCallback)load_imsi_ready,
- ctx);
+ if (mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (self)) == NULL &&
+ MM_BASE_SIM_GET_CLASS (self)->load_imsi &&
+ MM_BASE_SIM_GET_CLASS (self)->load_imsi_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_imsi (
+ self,
+ (GAsyncReadyCallback)init_load_imsi_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
+
+ case INITIALIZATION_STEP_EID:
+ /* EID is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_eid (MM_GDBUS_SIM (self)) == NULL &&
+ MM_BASE_SIM_GET_CLASS (self)->load_eid &&
+ MM_BASE_SIM_GET_CLASS (self)->load_eid_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_eid (
+ self,
+ (GAsyncReadyCallback)init_load_eid_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_OPERATOR_ID:
/* Operator ID is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
- if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (ctx->self)) == NULL &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_operator_identifier &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_operator_identifier_finish) {
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_operator_identifier (
- ctx->self,
- (GAsyncReadyCallback)load_operator_identifier_ready,
- ctx);
+ if (mm_gdbus_sim_get_operator_identifier (MM_GDBUS_SIM (self)) == NULL &&
+ MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier &&
+ MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_operator_identifier (
+ self,
+ (GAsyncReadyCallback)init_load_operator_identifier_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_OPERATOR_NAME:
/* Operator Name is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
- if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (ctx->self)) == NULL &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_operator_name &&
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_operator_name_finish) {
- MM_BASE_SIM_GET_CLASS (ctx->self)->load_operator_name (
- ctx->self,
- (GAsyncReadyCallback)load_operator_name_ready,
- ctx);
+ if (mm_gdbus_sim_get_operator_name (MM_GDBUS_SIM (self)) == NULL &&
+ MM_BASE_SIM_GET_CLASS (self)->load_operator_name &&
+ MM_BASE_SIM_GET_CLASS (self)->load_operator_name_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_operator_name (
+ self,
+ (GAsyncReadyCallback)init_load_operator_name_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
+
+ case INITIALIZATION_STEP_EMERGENCY_NUMBERS:
+ /* Emergency Numbers are meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_sim_get_emergency_numbers (MM_GDBUS_SIM (self)) == NULL &&
+ MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers &&
+ MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_emergency_numbers (
+ self,
+ (GAsyncReadyCallback)init_load_emergency_numbers_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INITIALIZATION_STEP_PREFERRED_NETWORKS:
+ if (MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks &&
+ MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks_finish) {
+ MM_BASE_SIM_GET_CLASS (self)->load_preferred_networks (
+ self,
+ (GAsyncReadyCallback)init_load_preferred_networks_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- g_simple_async_result_complete_in_idle (ctx->result);
- init_async_context_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
- }
+ default:
+ break;
+ }
g_assert_not_reached ();
}
@@ -1566,21 +2587,20 @@ common_init_async (GAsyncInitable *initable,
gpointer user_data)
{
+ MMBaseSim *self;
InitAsyncContext *ctx;
+ GTask *task;
+
+ self = MM_BASE_SIM (initable);
ctx = g_new (InitAsyncContext, 1);
- ctx->self = g_object_ref (initable);
- ctx->result = g_simple_async_result_new (G_OBJECT (initable),
- callback,
- user_data,
- common_init_async);
- ctx->cancellable = (cancellable ?
- g_object_ref (cancellable) :
- NULL);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->sim_identifier_tries = 0;
- interface_initialization_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ interface_initialization_step (task);
}
static void
@@ -1592,6 +2612,7 @@ initable_init_async (GAsyncInitable *initable,
{
mm_gdbus_sim_set_sim_identifier (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_imsi (MM_GDBUS_SIM (initable), NULL);
+ mm_gdbus_sim_set_eid (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_operator_identifier (MM_GDBUS_SIM (initable), NULL);
mm_gdbus_sim_set_operator_name (MM_GDBUS_SIM (initable), NULL);
@@ -1610,6 +2631,7 @@ mm_base_sim_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
@@ -1633,6 +2655,19 @@ mm_base_sim_initialize (MMBaseSim *self,
user_data);
}
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMBaseSim *self;
+
+ self = MM_BASE_SIM (_self);
+ return g_strdup_printf ("sim%u", self->priv->dbus_id);
+}
+
+/*****************************************************************************/
+
static void
set_property (GObject *object,
guint prop_id,
@@ -1665,6 +2700,8 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
if (self->priv->modem) {
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
/* Bind the modem's connection (which is set when it is exported,
* and unset when unexported) to the SIM's connection */
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
@@ -1672,6 +2709,9 @@ set_property (GObject *object,
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
}
break;
+ case PROP_SLOT_NUMBER:
+ self->priv->slot_number = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1696,6 +2736,9 @@ get_property (GObject *object,
case PROP_MODEM:
g_value_set_object (value, self->priv->modem);
break;
+ case PROP_SLOT_NUMBER:
+ g_value_set_uint (value, self->priv->slot_number);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -1705,8 +2748,13 @@ get_property (GObject *object,
static void
mm_base_sim_init (MMBaseSim *self)
{
+ static guint id = 0;
+
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_SIM, MMBaseSimPrivate);
+
+ /* Each SIM is given a unique id to build its own DBus path */
+ self->priv->dbus_id = id++;
}
static void
@@ -1744,6 +2792,12 @@ async_initable_iface_init (GAsyncInitableIface *iface)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_sim_class_init (MMBaseSimClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1764,6 +2818,12 @@ mm_base_sim_class_init (MMBaseSimClass *klass)
klass->load_operator_identifier_finish = load_operator_identifier_finish;
klass->load_operator_name = load_operator_name;
klass->load_operator_name_finish = load_operator_name_finish;
+ klass->load_emergency_numbers = load_emergency_numbers;
+ klass->load_emergency_numbers_finish = load_emergency_numbers_finish;
+ klass->load_preferred_networks = load_preferred_networks;
+ klass->load_preferred_networks_finish = load_preferred_networks_finish;
+ klass->set_preferred_networks = set_preferred_networks;
+ klass->set_preferred_networks_finish = set_preferred_networks_finish;
klass->send_pin = send_pin;
klass->send_pin_finish = common_send_pin_puk_finish;
klass->send_puk = send_puk;
@@ -1797,6 +2857,14 @@ mm_base_sim_class_init (MMBaseSimClass *klass)
G_PARAM_READWRITE);
g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
+ properties[PROP_SLOT_NUMBER] =
+ g_param_spec_uint (MM_BASE_SIM_SLOT_NUMBER,
+ "Slot number",
+ "The slot number where the SIM is inserted",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_SLOT_NUMBER, properties[PROP_SLOT_NUMBER]);
+
/* Signals */
signals[SIGNAL_PIN_LOCK_ENABLED] =
g_signal_new (MM_BASE_SIM_PIN_LOCK_ENABLED,
diff --git a/src/mm-base-sim.h b/src/mm-base-sim.h
index 19039942..c6f9dfe0 100644
--- a/src/mm-base-sim.h
+++ b/src/mm-base-sim.h
@@ -36,9 +36,10 @@ typedef struct _MMBaseSimClass MMBaseSimClass;
typedef struct _MMBaseSimPrivate MMBaseSimPrivate;
/* Properties */
-#define MM_BASE_SIM_PATH "sim-path"
-#define MM_BASE_SIM_CONNECTION "sim-connection"
-#define MM_BASE_SIM_MODEM "sim-modem"
+#define MM_BASE_SIM_PATH "sim-path"
+#define MM_BASE_SIM_CONNECTION "sim-connection"
+#define MM_BASE_SIM_MODEM "sim-modem"
+#define MM_BASE_SIM_SLOT_NUMBER "sim-slot-number"
/* Signals */
#define MM_BASE_SIM_PIN_LOCK_ENABLED "sim-pin-lock-enabled"
@@ -51,6 +52,14 @@ struct _MMBaseSim {
struct _MMBaseSimClass {
MmGdbusSimSkeletonClass parent;
+ /* Wait SIM ready (async) */
+ void (* wait_sim_ready) (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* wait_sim_ready_finish) (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Load SIM identifier (async) */
void (* load_sim_identifier) (MMBaseSim *self,
GAsyncReadyCallback callback,
@@ -67,6 +76,14 @@ struct _MMBaseSimClass {
GAsyncResult *res,
GError **error);
+ /* Load EID (async) */
+ void (* load_eid) (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gchar * (* load_eid_finish) (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Load operator identifier (async) */
void (* load_operator_identifier) (MMBaseSim *self,
GAsyncReadyCallback callback,
@@ -83,6 +100,14 @@ struct _MMBaseSimClass {
GAsyncResult *res,
GError **error);
+ /* Load emergency numbers (async) */
+ void (* load_emergency_numbers) (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GStrv (* load_emergency_numbers_finish) (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Change PIN (async) */
void (* change_pin) (MMBaseSim *self,
const gchar *old_pin,
@@ -125,44 +150,84 @@ struct _MMBaseSimClass {
/* Signals */
void (* pin_lock_enabled) (MMBaseSim *self,
gboolean enabled);
+
+ /* Load preferred networks (async) */
+ void (* load_preferred_networks) (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ GList * (* load_preferred_networks_finish) (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Set preferred networks (async) */
+ void (* set_preferred_networks) (MMBaseSim *self,
+ GList *preferred_network_list,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* set_preferred_networks_finish) (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
};
GType mm_base_sim_get_type (void);
-
-void mm_base_sim_new (MMBaseModem *modem,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMBaseSim *mm_base_sim_new_finish (GAsyncResult *res,
- GError **error);
-
-void mm_base_sim_initialize (MMBaseSim *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_base_sim_initialize_finish (MMBaseSim *self,
- GAsyncResult *result,
- GError **error);
-
-void mm_base_sim_send_pin (MMBaseSim *self,
- const gchar *pin,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_base_sim_send_pin_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error);
-
-void mm_base_sim_send_puk (MMBaseSim *self,
- const gchar *puk,
- const gchar *new_pin,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_base_sim_send_puk_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error);
-
-void mm_base_sim_export (MMBaseSim *self);
-
-const gchar *mm_base_sim_get_path (MMBaseSim *sim);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseSim, g_object_unref)
+
+void mm_base_sim_new (MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseSim *mm_base_sim_new_finish (GAsyncResult *res,
+ GError **error);
+
+void mm_base_sim_initialize (MMBaseSim *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_base_sim_initialize_finish (MMBaseSim *self,
+ GAsyncResult *result,
+ GError **error);
+
+MMBaseSim *mm_base_sim_new_initialized (MMBaseModem *modem,
+ guint slot_number,
+ gboolean active,
+ const gchar *sim_identifier,
+ const gchar *imsi,
+ const gchar *eid,
+ const gchar *operator_identifier,
+ const gchar *operator_name,
+ const GStrv emergency_numbers);
+
+void mm_base_sim_send_pin (MMBaseSim *self,
+ const gchar *pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_base_sim_send_pin_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_base_sim_send_puk (MMBaseSim *self,
+ const gchar *puk,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_base_sim_send_puk_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_base_sim_export (MMBaseSim *self);
+
+const gchar *mm_base_sim_get_path (MMBaseSim *sim);
+
+guint mm_base_sim_get_slot_number (MMBaseSim *self);
+
+void mm_base_sim_load_sim_identifier (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *mm_base_sim_load_sim_identifier_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_base_sim_is_emergency_number (MMBaseSim *self,
+ const gchar *number);
#endif /* MM_BASE_SIM_H */
diff --git a/src/mm-base-sms.c b/src/mm-base-sms.c
index bc38a1c5..1c291daf 100644
--- a/src/mm-base-sms.c
+++ b/src/mm-base-sms.c
@@ -33,10 +33,13 @@
#include "mm-sms-part-3gpp.h"
#include "mm-base-modem-at.h"
#include "mm-base-modem.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
-G_DEFINE_TYPE (MMBaseSms, mm_base_sms, MM_GDBUS_TYPE_SMS_SKELETON)
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMBaseSms, mm_base_sms, MM_GDBUS_TYPE_SMS_SKELETON, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
@@ -54,6 +57,8 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBaseSmsPrivate {
/* The connection to the system bus */
GDBusConnection *connection;
+ guint dbus_id;
+
/* The modem which owns this SMS */
MMBaseModem *modem;
/* The path where the SMS object is exported */
@@ -124,7 +129,7 @@ generate_3gpp_submit_pdus (MMBaseSms *self,
g_assert (!(text != NULL && data != NULL));
if (text) {
- split_text = mm_sms_part_3gpp_util_split_text (text, &encoding);
+ split_text = mm_sms_part_3gpp_util_split_text (text, &encoding, self);
if (!split_text) {
g_set_error (error,
MM_CORE_ERROR,
@@ -149,8 +154,7 @@ generate_3gpp_submit_pdus (MMBaseSms *self,
if (split_text)
g_strfreev (split_text);
else if (split_data) {
- guint i = 0;
-
+ i = 0;
while (split_data[i])
g_byte_array_unref (split_data[i++]);
g_free (split_data);
@@ -174,14 +178,14 @@ generate_3gpp_submit_pdus (MMBaseSms *self,
if (!split_text[i])
break;
part_text = split_text[i];
- mm_dbg (" Processing chunk '%u' of text with '%u' bytes",
- i, (guint) strlen (part_text));
+ mm_obj_dbg (self, " processing chunk '%u' of text with '%u' bytes",
+ i, (guint) strlen (part_text));
} else if (split_data) {
if (!split_data[i])
break;
part_data = split_data[i];
- mm_dbg (" Processing chunk '%u' of data with '%u' bytes",
- i, part_data->len);
+ mm_obj_dbg (self, " processing chunk '%u' of data with '%u' bytes",
+ i, part_data->len);
} else
g_assert_not_reached ();
@@ -201,11 +205,9 @@ generate_3gpp_submit_pdus (MMBaseSms *self,
mm_sms_part_set_concat_reference (part, 0); /* We don't set a concat reference here */
mm_sms_part_set_concat_sequence (part, i + 1);
mm_sms_part_set_concat_max (part, n_parts);
-
- mm_dbg ("Created SMS part '%u' for multipart SMS ('%u' parts expected)",
- i + 1, n_parts);
+ mm_obj_dbg (self, "created SMS part '%u' for multipart SMS ('%u' parts expected)", i + 1, n_parts);
} else {
- mm_dbg ("Created SMS part for singlepart SMS");
+ mm_obj_dbg (self, "created SMS part for singlepart SMS");
}
/* Add to the list of parts */
@@ -215,10 +217,8 @@ generate_3gpp_submit_pdus (MMBaseSms *self,
}
/* Free array (not contents, which were taken for the part) */
- if (split_text)
- g_free (split_text);
- if (split_data)
- g_free (split_data);
+ g_free (split_text);
+ g_free (split_data);
/* Set additional multipart specific properties */
if (n_parts > 1) {
@@ -273,14 +273,14 @@ generate_cdma_submit_pdus (MMBaseSms *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_obj_dbg (self, "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");
+ mm_obj_dbg (self, "created SMS part for CDMA SMS");
/* Add to the list of parts */
self->priv->parts = g_list_append (self->priv->parts, part);
@@ -653,10 +653,9 @@ handle_send (MMBaseSms *self,
void
mm_base_sms_export (MMBaseSms *self)
{
- static guint id = 0;
gchar *path;
- path = g_strdup_printf (MM_DBUS_SMS_PREFIX "/%d", id++);
+ path = g_strdup_printf (MM_DBUS_SMS_PREFIX "/%d", self->priv->dbus_id);
g_object_set (self,
MM_BASE_SMS_PATH, path,
NULL);
@@ -692,9 +691,7 @@ sms_dbus_export (MMBaseSms *self)
self->priv->connection,
self->priv->path,
&error)) {
- mm_warn ("couldn't export SMS at '%s': '%s'",
- self->priv->path,
- error->message);
+ mm_obj_warn (self, "couldn't export SMS: %s", error->message);
g_error_free (error);
}
}
@@ -774,12 +771,13 @@ mm_base_sms_get_parts (MMBaseSms *self)
/*****************************************************************************/
static gboolean
-sms_get_store_or_send_command (MMSmsPart *part,
- gboolean text_or_pdu, /* TRUE for PDU */
- gboolean store_or_send, /* TRUE for send */
- gchar **out_cmd,
- gchar **out_msg_data,
- GError **error)
+sms_get_store_or_send_command (MMBaseSms *self,
+ MMSmsPart *part,
+ gboolean text_or_pdu, /* TRUE for PDU */
+ gboolean store_or_send, /* TRUE for send */
+ gchar **out_cmd,
+ gchar **out_msg_data,
+ GError **error)
{
g_assert (out_cmd != NULL);
g_assert (out_msg_data != NULL);
@@ -798,7 +796,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_3gpp_get_submit_pdu (part, &pdulen, &msgstart, error);
+ pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, self, error);
if (!pdu)
/* 'error' should already be set */
return FALSE;
@@ -830,9 +828,7 @@ sms_get_store_or_send_command (MMSmsPart *part,
/* Store the SMS */
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
- GSimpleAsyncResult *result;
MMSmsStorage storage;
gboolean need_unlock;
gboolean use_pdu_mode;
@@ -841,15 +837,12 @@ typedef struct {
} SmsStoreContext;
static void
-sms_store_context_complete_and_free (SmsStoreContext *ctx)
+sms_store_context_free (SmsStoreContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
/* Unlock mem2 storage if we had the lock */
if (ctx->need_unlock)
mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx->msg_data);
g_free (ctx);
}
@@ -859,16 +852,17 @@ sms_store_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void sms_store_next_part (SmsStoreContext *ctx);
+static void sms_store_next_part (GTask *task);
static void
store_msg_data_ready (MMBaseModem *modem,
GAsyncResult *res,
- SmsStoreContext *ctx)
+ GTask *task)
{
+ SmsStoreContext *ctx;
const gchar *response;
GError *error = NULL;
gint rv;
@@ -876,81 +870,92 @@ store_msg_data_ready (MMBaseModem *modem,
response = mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Read the new part index from the reply */
rv = sscanf (response, "+CMGW: %d", &idx);
if (rv != 1 || idx < 0) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't read index of already stored part: "
- "%d fields parsed",
- rv);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't read index of already stored part: "
+ "%d fields parsed",
+ rv);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Set the index in the part we hold */
mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, (guint)idx);
ctx->current = g_list_next (ctx->current);
- sms_store_next_part (ctx);
+ sms_store_next_part (task);
}
static void
store_ready (MMBaseModem *modem,
GAsyncResult *res,
- SmsStoreContext *ctx)
+ GTask *task)
{
- const gchar *response;
+ SmsStoreContext *ctx;
GError *error = NULL;
- response = mm_base_modem_at_command_finish (modem, res, &error);
+ mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* Send the actual message data */
+ ctx = g_task_get_task_data (task);
+
+ /* Send the actual message data.
+ * We send the data as 'raw' data because we do NOT want it to
+ * be treated as an AT command (i.e. we don't want it prefixed
+ * with AT+ and suffixed with <CR><LF>), plus, we want it to be
+ * sent right away (not queued after other AT commands). */
mm_base_modem_at_command_raw (ctx->modem,
ctx->msg_data,
10,
FALSE,
(GAsyncReadyCallback)store_msg_data_ready,
- ctx);
+ task);
}
static void
-sms_store_next_part (SmsStoreContext *ctx)
+sms_store_next_part (GTask *task)
{
+ MMBaseSms *self;
+ SmsStoreContext *ctx;
gchar *cmd;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (!ctx->current) {
/* Done we are */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- if (ctx->msg_data) {
- g_free (ctx->msg_data);
- ctx->msg_data = NULL;
- }
+ g_clear_pointer (&ctx->msg_data, g_free);
- if (!sms_get_store_or_send_command ((MMSmsPart *)ctx->current->data,
+ if (!sms_get_store_or_send_command (self,
+ (MMSmsPart *)ctx->current->data,
ctx->use_pdu_mode,
FALSE,
&cmd,
&ctx->msg_data,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -962,30 +967,35 @@ sms_store_next_part (SmsStoreContext *ctx)
10,
FALSE,
(GAsyncReadyCallback)store_ready,
- ctx);
+ task);
g_free (cmd);
}
static void
store_lock_sms_storages_ready (MMBroadbandModem *modem,
GAsyncResult *res,
- SmsStoreContext *ctx)
+ GTask *task)
{
+ MMBaseSms *self;
+ SmsStoreContext *ctx;
GError *error = NULL;
if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* We are now locked. Whatever result we have here, we need to make sure
* we unlock the storages before finishing. */
ctx->need_unlock = TRUE;
/* Go on to store the parts */
- ctx->current = ctx->self->priv->parts;
- sms_store_next_part (ctx);
+ ctx->current = self->priv->parts;
+ sms_store_next_part (task);
}
static void
@@ -995,14 +1005,10 @@ sms_store (MMBaseSms *self,
gpointer user_data)
{
SmsStoreContext *ctx;
+ GTask *task;
/* Setup the context */
ctx = g_new0 (SmsStoreContext, 1);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_store);
- ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (self->priv->modem);
ctx->storage = storage;
@@ -1011,6 +1017,9 @@ sms_store (MMBaseSms *self,
MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode,
NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free);
+
/* First, lock storage to use */
g_assert (MM_IS_BROADBAND_MODEM (self->priv->modem));
mm_broadband_modem_lock_sms_storages (
@@ -1018,16 +1027,14 @@ sms_store (MMBaseSms *self,
MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */
ctx->storage,
(GAsyncReadyCallback)store_lock_sms_storages_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* Send the SMS */
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
- GSimpleAsyncResult *result;
gboolean need_unlock;
gboolean from_storage;
gboolean use_pdu_mode;
@@ -1036,15 +1043,12 @@ typedef struct {
} SmsSendContext;
static void
-sms_send_context_complete_and_free (SmsSendContext *ctx)
+sms_send_context_free (SmsSendContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
/* Unlock mem2 storage if we had the lock */
if (ctx->need_unlock)
mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), FALSE, TRUE);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx->msg_data);
g_free (ctx);
}
@@ -1054,10 +1058,10 @@ sms_send_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void sms_send_next_part (SmsSendContext *ctx);
+static void sms_send_next_part (GTask *task);
static gint
read_message_reference_from_reply (const gchar *response,
@@ -1087,86 +1091,100 @@ read_message_reference_from_reply (const gchar *response,
static void
send_generic_msg_data_ready (MMBaseModem *modem,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ SmsSendContext *ctx;
GError *error = NULL;
const gchar *response;
gint message_reference;
response = mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
message_reference = read_message_reference_from_reply (response, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data,
(guint)message_reference);
ctx->current = g_list_next (ctx->current);
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
static void
send_generic_ready (MMBaseModem *modem,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ SmsSendContext *ctx;
GError *error = NULL;
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* Send the actual message data */
+ ctx = g_task_get_task_data (task);
+
+ /* Send the actual message data.
+ * We send the data as 'raw' data because we do NOT want it to
+ * be treated as an AT command (i.e. we don't want it prefixed
+ * with AT+ and suffixed with <CR><LF>), plus, we want it to be
+ * sent right away (not queued after other AT commands). */
mm_base_modem_at_command_raw (ctx->modem,
ctx->msg_data,
- 10,
+ MM_BASE_SMS_DEFAULT_SEND_TIMEOUT,
FALSE,
(GAsyncReadyCallback)send_generic_msg_data_ready,
- ctx);
+ task);
}
static void
send_from_storage_ready (MMBaseModem *modem,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ MMBaseSms *self;
+ SmsSendContext *ctx;
GError *error = NULL;
const gchar *response;
gint message_reference;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("Couldn't send SMS from storage: '%s'; trying generic send...",
- error->message);
+ mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message);
g_error_free (error);
ctx->from_storage = FALSE;
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
return;
}
message_reference = read_message_reference_from_reply (response, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1174,19 +1192,24 @@ send_from_storage_ready (MMBaseModem *modem,
(guint)message_reference);
ctx->current = g_list_next (ctx->current);
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
static void
-sms_send_next_part (SmsSendContext *ctx)
+sms_send_next_part (GTask *task)
{
+ MMBaseSms *self;
+ SmsSendContext *ctx;
GError *error = NULL;
gchar *cmd;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (!ctx->current) {
/* Done we are */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -1196,63 +1219,68 @@ sms_send_next_part (SmsSendContext *ctx)
mm_sms_part_get_index ((MMSmsPart *)ctx->current->data));
mm_base_modem_at_command (ctx->modem,
cmd,
- 30,
+ MM_BASE_SMS_DEFAULT_SEND_TIMEOUT,
FALSE,
(GAsyncReadyCallback)send_from_storage_ready,
- ctx);
+ task);
g_free (cmd);
return;
}
/* Generic send */
- if (ctx->msg_data) {
- g_free (ctx->msg_data);
- ctx->msg_data = NULL;
- }
+ g_clear_pointer (&ctx->msg_data, g_free);
- if (!sms_get_store_or_send_command ((MMSmsPart *)ctx->current->data,
+ if (!sms_get_store_or_send_command (self,
+ (MMSmsPart *)ctx->current->data,
ctx->use_pdu_mode,
TRUE,
&cmd,
&ctx->msg_data,
&error)) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_assert (cmd != NULL);
g_assert (ctx->msg_data != NULL);
+
+ /* no network involved in this initial AT command, so lower timeout */
mm_base_modem_at_command (ctx->modem,
cmd,
- 30,
+ 10,
FALSE,
(GAsyncReadyCallback)send_generic_ready,
- ctx);
+ task);
g_free (cmd);
}
static void
send_lock_sms_storages_ready (MMBroadbandModem *modem,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ MMBaseSms *self;
+ SmsSendContext *ctx;
GError *error = NULL;
if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* We are now locked. Whatever result we have here, we need to make sure
* we unlock the storages before finishing. */
ctx->need_unlock = TRUE;
/* Go on to send the parts */
- ctx->current = ctx->self->priv->parts;
- sms_send_next_part (ctx);
+ ctx->current = self->priv->parts;
+ sms_send_next_part (task);
}
static void
@@ -1261,16 +1289,15 @@ sms_send (MMBaseSms *self,
gpointer user_data)
{
SmsSendContext *ctx;
+ GTask *task;
/* Setup the context */
ctx = g_new0 (SmsSendContext, 1);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_send);
- ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (self->priv->modem);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free);
+
/* If the SMS is STORED, try to send from storage */
ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN);
if (ctx->from_storage) {
@@ -1281,7 +1308,7 @@ sms_send (MMBaseSms *self,
MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */
mm_base_sms_get_storage (self),
(GAsyncReadyCallback)send_lock_sms_storages_ready,
- ctx);
+ task);
return;
}
@@ -1290,30 +1317,25 @@ sms_send (MMBaseSms *self,
MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode,
NULL);
ctx->current = self->priv->parts;
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
/*****************************************************************************/
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
- GSimpleAsyncResult *result;
gboolean need_unlock;
GList *current;
guint n_failed;
} SmsDeletePartsContext;
static void
-sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx)
+sms_delete_parts_context_free (SmsDeletePartsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
/* Unlock mem1 storage if we had the lock */
if (ctx->need_unlock)
mm_broadband_modem_unlock_sms_storages (MM_BROADBAND_MODEM (ctx->modem), TRUE, FALSE);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -1322,24 +1344,29 @@ sms_delete_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void delete_next_part (SmsDeletePartsContext *ctx);
+static void delete_next_part (GTask *task);
static void
delete_part_ready (MMBaseModem *modem,
GAsyncResult *res,
- SmsDeletePartsContext *ctx)
+ GTask *task)
{
+ MMBaseSms *self;
+ SmsDeletePartsContext *ctx;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_finish (modem, res, &error);
if (error) {
ctx->n_failed++;
- mm_dbg ("Couldn't delete SMS part with index %u: '%s'",
- mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
- error->message);
+ mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s",
+ mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
+ error->message);
g_error_free (error);
}
@@ -1347,14 +1374,17 @@ delete_part_ready (MMBaseModem *modem,
mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX);
ctx->current = g_list_next (ctx->current);
- delete_next_part (ctx);
+ delete_next_part (task);
}
static void
-delete_next_part (SmsDeletePartsContext *ctx)
+delete_next_part (GTask *task)
{
+ SmsDeletePartsContext *ctx;
gchar *cmd;
+ ctx = g_task_get_task_data (task);
+
/* Skip non-stored parts */
while (ctx->current &&
mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX)
@@ -1363,15 +1393,15 @@ delete_next_part (SmsDeletePartsContext *ctx)
/* If all removed, we're done */
if (!ctx->current) {
if (ctx->n_failed > 0)
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't delete %u parts from this SMS",
- ctx->n_failed);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't delete %u parts from this SMS",
+ ctx->n_failed);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
- sms_delete_parts_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
@@ -1382,30 +1412,35 @@ delete_next_part (SmsDeletePartsContext *ctx)
10,
FALSE,
(GAsyncReadyCallback)delete_part_ready,
- ctx);
+ task);
g_free (cmd);
}
static void
delete_lock_sms_storages_ready (MMBroadbandModem *modem,
GAsyncResult *res,
- SmsDeletePartsContext *ctx)
+ GTask *task)
{
+ MMBaseSms *self;
+ SmsDeletePartsContext *ctx;
GError *error = NULL;
if (!mm_broadband_modem_lock_sms_storages_finish (modem, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_delete_parts_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* We are now locked. Whatever result we have here, we need to make sure
* we unlock the storages before finishing. */
ctx->need_unlock = TRUE;
/* Go on deleting parts */
- ctx->current = ctx->self->priv->parts;
- delete_next_part (ctx);
+ ctx->current = self->priv->parts;
+ delete_next_part (task);
}
static void
@@ -1414,19 +1449,18 @@ sms_delete (MMBaseSms *self,
gpointer user_data)
{
SmsDeletePartsContext *ctx;
+ GTask *task;
ctx = g_new0 (SmsDeletePartsContext, 1);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_delete);
- ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (self->priv->modem);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free);
+
if (mm_base_sms_get_storage (self) == MM_SMS_STORAGE_UNKNOWN) {
- mm_dbg ("Not removing parts from non-stored SMS");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_delete_parts_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "not removing parts from non-stored SMS");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -1436,7 +1470,7 @@ sms_delete (MMBaseSms *self,
mm_base_sms_get_storage (self),
MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */
(GAsyncReadyCallback)delete_lock_sms_storages_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -1458,13 +1492,13 @@ mm_base_sms_delete_finish (MMBaseSms *self,
return deleted;
}
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
mm_base_sms_delete (MMBaseSms *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
if (MM_BASE_SMS_GET_CLASS (self)->delete &&
MM_BASE_SMS_GET_CLASS (self)->delete_finish) {
@@ -1472,26 +1506,56 @@ mm_base_sms_delete (MMBaseSms *self,
return;
}
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Deleting SMS is not supported by this modem");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ mm_base_sms_delete,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Deleting SMS is not supported by this modem");
}
/*****************************************************************************/
+static void
+initialize_sms (MMBaseSms *self)
+{
+ MMSmsPart *part;
+ guint validity_relative;
+
+ /* Some of the fields of the SMS object may be initialized as soon as we have
+ * one part already available, even if it's not exactly the first one */
+ g_assert (self->priv->parts);
+ part = (MMSmsPart *)(self->priv->parts->data);
+
+ /* Prepare for validity tuple */
+ validity_relative = mm_sms_part_get_validity_relative (part);
+
+ g_object_set (self,
+ "pdu-type", mm_sms_part_get_pdu_type (part),
+ "smsc", mm_sms_part_get_smsc (part),
+ "class", mm_sms_part_get_class (part),
+ "teleservice-id", mm_sms_part_get_cdma_teleservice_id (part),
+ "service-category", mm_sms_part_get_cdma_service_category (part),
+ "number", mm_sms_part_get_number (part),
+ "validity", (validity_relative ?
+ g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (validity_relative)) :
+ g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))),
+ "timestamp", mm_sms_part_get_timestamp (part),
+ "discharge-timestamp", mm_sms_part_get_discharge_timestamp (part),
+ "delivery-state", mm_sms_part_get_delivery_state (part),
+ NULL);
+}
+
static gboolean
-assemble_sms (MMBaseSms *self,
- GError **error)
+assemble_sms (MMBaseSms *self,
+ GError **error)
{
- GList *l;
- guint idx;
+ GList *l;
+ guint idx;
MMSmsPart **sorted_parts;
- GString *fulltext;
+ GString *fulltext;
GByteArray *fulldata;
- guint validity_relative;
sorted_parts = g_new0 (MMSmsPart *, self->priv->max_parts);
@@ -1516,12 +1580,12 @@ assemble_sms (MMBaseSms *self,
idx = mm_sms_part_get_concat_sequence ((MMSmsPart *)l->data);
if (idx < 1 || idx > self->priv->max_parts) {
- mm_warn ("Invalid part index (%u) found, ignoring", idx);
+ mm_obj_warn (self, "invalid part index (%u) found, ignoring", idx);
continue;
}
if (sorted_parts[idx - 1]) {
- mm_warn ("Duplicate part index (%u) found, ignoring", idx);
+ mm_obj_warn (self, "duplicate part index (%u) found, ignoring", idx);
continue;
}
@@ -1579,30 +1643,15 @@ assemble_sms (MMBaseSms *self,
/* If we got all parts, we also have the first one always */
g_assert (sorted_parts[0] != NULL);
- /* Prepare for validity tuple */
- validity_relative = mm_sms_part_get_validity_relative (sorted_parts[0]);
-
/* If we got everything, assemble the text! */
g_object_set (self,
- "pdu-type", mm_sms_part_get_pdu_type (sorted_parts[0]),
- "text", fulltext->str,
- "data", g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
- fulldata->data,
- fulldata->len * sizeof (guint8),
- TRUE,
- (GDestroyNotify) g_byte_array_unref,
- 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)) :
- g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))),
- "timestamp", mm_sms_part_get_timestamp (sorted_parts[0]),
- "discharge-timestamp", mm_sms_part_get_discharge_timestamp (sorted_parts[0]),
- "delivery-state", mm_sms_part_get_delivery_state (sorted_parts[0]),
+ "text", fulltext->str,
+ "data", g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+ fulldata->data,
+ fulldata->len * sizeof (guint8),
+ TRUE,
+ (GDestroyNotify) g_byte_array_unref,
+ g_byte_array_ref (fulldata)),
/* delivery report request and message reference taken always from the last part */
"message-reference", mm_sms_part_get_message_reference (sorted_parts[self->priv->max_parts - 1]),
"delivery-report-request", mm_sms_part_get_delivery_report_request (sorted_parts[self->priv->max_parts - 1]),
@@ -1674,6 +1723,10 @@ mm_base_sms_multipart_take_part (MMBaseSms *self,
part,
(GCompareFunc)cmp_sms_part_sequence);
+ /* If this is the first part we take, initialize common SMS fields */
+ if (g_list_length (self->priv->parts) == 1)
+ initialize_sms (self);
+
/* We only populate contents when the multipart SMS is complete */
if (mm_base_sms_multipart_is_complete (self)) {
GError *inner_error = NULL;
@@ -1681,8 +1734,7 @@ mm_base_sms_multipart_take_part (MMBaseSms *self,
if (!assemble_sms (self, &inner_error)) {
/* We DO NOT propagate the error. The part was properly taken
* so ownership passed to the MMBaseSms object. */
- mm_warn ("Couldn't assemble SMS: '%s'",
- inner_error->message);
+ mm_obj_warn (self, "couldn't assemble SMS: %s", inner_error->message);
g_error_free (inner_error);
} else {
/* Completed AND assembled
@@ -1724,6 +1776,9 @@ mm_base_sms_singlepart_new (MMBaseModem *modem,
/* Keep the single part in the list */
self->priv->parts = g_list_prepend (self->priv->parts, part);
+ /* Initialize common SMS fields */
+ initialize_sms (self);
+
if (!assemble_sms (self, error)) {
/* Note: we need to remove the part from the list, as we really didn't
* take it, and therefore the caller is responsible for freeing it. */
@@ -1782,9 +1837,9 @@ mm_base_sms_multipart_new (MMBaseModem *modem,
}
MMBaseSms *
-mm_base_sms_new_from_properties (MMBaseModem *modem,
- MMSmsProperties *properties,
- GError **error)
+mm_base_sms_new_from_properties (MMBaseModem *modem,
+ MMSmsProperties *props,
+ GError **error)
{
MMBaseSms *self;
const gchar *text;
@@ -1792,17 +1847,17 @@ mm_base_sms_new_from_properties (MMBaseModem *modem,
g_assert (MM_IS_IFACE_MODEM_MESSAGING (modem));
- text = mm_sms_properties_get_text (properties);
- data = mm_sms_properties_peek_data_bytearray (properties);
+ text = mm_sms_properties_get_text (props);
+ data = mm_sms_properties_peek_data_bytearray (props);
/* Don't create SMS from properties if either (text|data) or number is missing */
- if (!mm_sms_properties_get_number (properties) ||
+ if (!mm_sms_properties_get_number (props) ||
(!text && !data)) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_INVALID_ARGS,
"Cannot create SMS: mandatory parameter '%s' is missing",
- (!mm_sms_properties_get_number (properties)?
+ (!mm_sms_properties_get_number (props)?
"number" : "text' or 'data"));
return NULL;
}
@@ -1821,8 +1876,8 @@ mm_base_sms_new_from_properties (MMBaseModem *modem,
g_object_set (self,
"state", MM_SMS_STATE_UNKNOWN,
"storage", MM_SMS_STORAGE_UNKNOWN,
- "number", mm_sms_properties_get_number (properties),
- "pdu-type", (mm_sms_properties_get_teleservice_id (properties) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN ?
+ "number", mm_sms_properties_get_number (props),
+ "pdu-type", (mm_sms_properties_get_teleservice_id (props) == MM_SMS_CDMA_TELESERVICE_ID_UNKNOWN ?
MM_SMS_PDU_TYPE_SUBMIT :
MM_SMS_PDU_TYPE_CDMA_SUBMIT),
"text", text,
@@ -1834,14 +1889,14 @@ mm_base_sms_new_from_properties (MMBaseModem *modem,
(GDestroyNotify) g_byte_array_unref,
g_byte_array_ref (data)) :
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),
+ "smsc", mm_sms_properties_get_smsc (props),
+ "class", mm_sms_properties_get_class (props),
+ "teleservice-id", mm_sms_properties_get_teleservice_id (props),
+ "service-category", mm_sms_properties_get_service_category (props),
+ "delivery-report-request", mm_sms_properties_get_delivery_report_request (props),
"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))) :
+ "validity", (mm_sms_properties_get_validity_type (props) == MM_SMS_VALIDITY_TYPE_RELATIVE ?
+ g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_RELATIVE, g_variant_new_uint32 (mm_sms_properties_get_validity_relative (props))) :
g_variant_new ("(uv)", MM_SMS_VALIDITY_TYPE_UNKNOWN, g_variant_new_boolean (FALSE))),
NULL);
@@ -1853,6 +1908,17 @@ mm_base_sms_new_from_properties (MMBaseModem *modem,
/*****************************************************************************/
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMBaseSms *self;
+
+ self = MM_BASE_SMS (_self);
+ return g_strdup_printf ("sms%u", self->priv->dbus_id);
+}
+
+/*****************************************************************************/
+
static void
set_property (GObject *object,
guint prop_id,
@@ -1886,6 +1952,8 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
if (self->priv->modem) {
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
/* Bind the modem's connection (which is set when it is exported,
* and unset when unexported) to the SMS's connection */
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
@@ -1944,10 +2012,15 @@ get_property (GObject *object,
static void
mm_base_sms_init (MMBaseSms *self)
{
+ static guint id = 0;
+
/* Initialize private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_SMS, MMBaseSmsPrivate);
/* Defaults */
self->priv->max_parts = 1;
+
+ /* Each SMS is given a unique id to build its own DBus path */
+ self->priv->dbus_id = id++;
}
static void
@@ -1979,6 +2052,12 @@ dispose (GObject *object)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_base_sms_class_init (MMBaseSmsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
diff --git a/src/mm-base-sms.h b/src/mm-base-sms.h
index 0d4b5bc5..be8d559c 100644
--- a/src/mm-base-sms.h
+++ b/src/mm-base-sms.h
@@ -27,6 +27,14 @@
#include "mm-sms-part.h"
#include "mm-base-modem.h"
+/*****************************************************************************/
+
+/* Default timeout value to be used when sending a SMS, long enough so that the
+ * operation succeeds or fails under low signal conditions. */
+#define MM_BASE_SMS_DEFAULT_SEND_TIMEOUT (5 * 60)
+
+/*****************************************************************************/
+
#define MM_TYPE_BASE_SMS (mm_base_sms_get_type ())
#define MM_BASE_SMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_SMS, MMBaseSms))
#define MM_BASE_SMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_SMS, MMBaseSmsClass))
@@ -80,8 +88,9 @@ struct _MMBaseSmsClass {
};
GType mm_base_sms_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseSms, g_object_unref)
-/* This one can be overriden by plugins */
+/* This one can be overridden by plugins */
MMBaseSms *mm_base_sms_new (MMBaseModem *modem);
MMBaseSms *mm_base_sms_new_from_properties (MMBaseModem *modem,
MMSmsProperties *properties,
diff --git a/src/mm-bearer-list.c b/src/mm-bearer-list.c
index 14477419..24fdac0a 100644
--- a/src/mm-bearer-list.c
+++ b/src/mm-bearer-list.c
@@ -34,8 +34,8 @@ G_DEFINE_TYPE (MMBearerList, mm_bearer_list, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_NUM_BEARERS,
- PROP_MAX_BEARERS,
PROP_MAX_ACTIVE_BEARERS,
+ PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS,
PROP_LAST
};
@@ -44,36 +44,23 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBearerListPrivate {
/* List of bearers */
GList *bearers;
- /* Max number of bearers */
- guint max_bearers;
/* Max number of active bearers */
guint max_active_bearers;
+ guint max_active_multiplexed_bearers;
};
/*****************************************************************************/
guint
-mm_bearer_list_get_max (MMBearerList *self)
-{
- return self->priv->max_bearers;
-}
-
-guint
mm_bearer_list_get_max_active (MMBearerList *self)
{
return self->priv->max_active_bearers;
}
guint
-mm_bearer_list_get_count (MMBearerList *self)
-{
- return g_list_length (self->priv->bearers);
-}
-
-guint
-mm_bearer_list_get_count_active (MMBearerList *self)
+mm_bearer_list_get_max_active_multiplexed (MMBearerList *self)
{
- return 0; /* TODO */
+ return self->priv->max_active_multiplexed_bearers;
}
gboolean
@@ -81,16 +68,6 @@ mm_bearer_list_add_bearer (MMBearerList *self,
MMBaseBearer *bearer,
GError **error)
{
- /* Just in case, ensure we don't go off limits */
- if (g_list_length (self->priv->bearers) == self->priv->max_bearers) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_TOO_MANY,
- "Cannot add new bearer: already reached maximum (%u)",
- self->priv->max_bearers);
- return FALSE;
- }
-
/* Keep our own reference */
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]);
@@ -105,15 +82,6 @@ mm_bearer_list_delete_bearer (MMBearerList *self,
{
GList *l;
- if (!g_str_has_prefix (path, MM_DBUS_BEARER_PREFIX)) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Cannot delete bearer: invalid path '%s'",
- path);
- return FALSE;
- }
-
for (l = self->priv->bearers; l; l = g_list_next (l)) {
if (g_str_equal (path, mm_base_bearer_get_path (MM_BASE_BEARER (l->data)))) {
g_object_unref (l->data);
@@ -131,17 +99,6 @@ mm_bearer_list_delete_bearer (MMBearerList *self,
return FALSE;
}
-void
-mm_bearer_list_delete_all_bearers (MMBearerList *self)
-{
- if (!self->priv->bearers)
- return;
-
- 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
mm_bearer_list_get_paths (MMBearerList *self)
{
@@ -167,13 +124,47 @@ mm_bearer_list_foreach (MMBearerList *self,
}
MMBaseBearer *
-mm_bearer_list_find (MMBearerList *self,
- MMBearerProperties *properties)
+mm_bearer_list_find_by_properties (MMBearerList *self,
+ MMBearerProperties *props)
{
GList *l;
for (l = self->priv->bearers; l; l = g_list_next (l)) {
- if (mm_bearer_properties_cmp (mm_base_bearer_peek_config (MM_BASE_BEARER (l->data)), properties))
+ /* always strict matching when comparing these bearer properties, as they're all
+ * built in the same place */
+ if (mm_bearer_properties_cmp (mm_base_bearer_peek_config (MM_BASE_BEARER (l->data)),
+ props,
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NONE))
+ return g_object_ref (l->data);
+ }
+
+ return NULL;
+}
+
+MMBaseBearer *
+mm_bearer_list_find_by_path (MMBearerList *self,
+ const gchar *path)
+{
+ GList *l;
+
+ for (l = self->priv->bearers; l; l = g_list_next (l)) {
+ if (g_str_equal (path, mm_base_bearer_get_path (MM_BASE_BEARER (l->data))))
+ return g_object_ref (l->data);
+ }
+
+ return NULL;
+}
+
+MMBaseBearer *
+mm_bearer_list_find_by_profile_id (MMBearerList *self,
+ gint profile_id)
+{
+ GList *l;
+
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ for (l = self->priv->bearers; l; l = g_list_next (l)) {
+ if (mm_base_bearer_get_profile_id (MM_BASE_BEARER (l->data)) == profile_id)
return g_object_ref (l->data);
}
@@ -183,19 +174,16 @@ mm_bearer_list_find (MMBearerList *self,
/*****************************************************************************/
typedef struct {
- GSimpleAsyncResult *result;
GList *pending;
MMBaseBearer *current;
} DisconnectAllContext;
static void
-disconnect_all_context_complete_and_free (DisconnectAllContext *ctx)
+disconnect_all_context_free (DisconnectAllContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
if (ctx->current)
g_object_unref (ctx->current);
- g_list_free_full (ctx->pending, (GDestroyNotify) g_object_unref);
+ g_list_free_full (ctx->pending, g_object_unref);
g_free (ctx);
}
@@ -204,37 +192,40 @@ mm_bearer_list_disconnect_all_bearers_finish (MMBearerList *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void disconnect_next_bearer (DisconnectAllContext *ctx);
+static void disconnect_next_bearer (GTask *task);
static void
disconnect_ready (MMBaseBearer *bearer,
GAsyncResult *res,
- DisconnectAllContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!mm_base_bearer_disconnect_finish (bearer, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_all_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- disconnect_next_bearer (ctx);
+ disconnect_next_bearer (task);
}
static void
-disconnect_next_bearer (DisconnectAllContext *ctx)
+disconnect_next_bearer (GTask *task)
{
+ DisconnectAllContext *ctx;
+
+ ctx = g_task_get_task_data (task);
if (ctx->current)
g_clear_object (&ctx->current);
/* No more bearers? all done! */
if (!ctx->pending) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_all_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -243,7 +234,7 @@ disconnect_next_bearer (DisconnectAllContext *ctx)
mm_base_bearer_disconnect (ctx->current,
(GAsyncReadyCallback)disconnect_ready,
- ctx);
+ task);
}
void
@@ -252,33 +243,118 @@ mm_bearer_list_disconnect_all_bearers (MMBearerList *self,
gpointer user_data)
{
DisconnectAllContext *ctx;
+ GTask *task;
ctx = g_new0 (DisconnectAllContext, 1);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_bearer_list_disconnect_all_bearers);
/* Get a copy of the list */
- ctx->pending = g_list_copy (self->priv->bearers);
- g_list_foreach (ctx->pending, (GFunc) g_object_ref, NULL);
+ ctx->pending = g_list_copy_deep (self->priv->bearers,
+ (GCopyFunc)g_object_ref,
+ NULL);
- disconnect_next_bearer (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task,
+ ctx,
+ (GDestroyNotify)disconnect_all_context_free);
+
+ disconnect_next_bearer (task);
}
/*****************************************************************************/
-MMBearerList *
-mm_bearer_list_new (guint max_bearers,
- guint max_active_bearers)
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef struct {
+ GList *pending;
+ MMBaseBearer *current;
+} SyncAllContext;
+
+static void
+sync_all_context_free (SyncAllContext *ctx)
+{
+ if (ctx->current)
+ g_object_unref (ctx->current);
+ g_list_free_full (ctx->pending, g_object_unref);
+ g_free (ctx);
+}
+
+gboolean
+mm_bearer_list_sync_all_bearers_finish (MMBearerList *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void sync_next_bearer (GTask *task);
+
+static void
+sync_ready (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_base_bearer_sync_finish (bearer, res, &error))
+ mm_obj_warn (bearer, "failed synchronizing state: %s", error->message);
+
+ sync_next_bearer (task);
+}
+
+static void
+sync_next_bearer (GTask *task)
{
- mm_dbg ("Creating bearer list (max: %u, max active: %u)",
- max_bearers,
- max_active_bearers);
+ SyncAllContext *ctx;
+ ctx = g_task_get_task_data (task);
+ if (ctx->current)
+ g_clear_object (&ctx->current);
+
+ /* No more bearers? all done! */
+ if (!ctx->pending) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->current = MM_BASE_BEARER (ctx->pending->data);
+ ctx->pending = g_list_delete_link (ctx->pending, ctx->pending);
+
+ mm_base_bearer_sync (ctx->current, (GAsyncReadyCallback)sync_ready, task);
+}
+
+void
+mm_bearer_list_sync_all_bearers (MMBearerList *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncAllContext *ctx;
+ GTask *task;
+
+ ctx = g_new0 (SyncAllContext, 1);
+
+ /* Get a copy of the list */
+ ctx->pending = g_list_copy_deep (self->priv->bearers,
+ (GCopyFunc)g_object_ref,
+ NULL);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sync_all_context_free);
+
+ sync_next_bearer (task);
+}
+
+#endif
+
+/*****************************************************************************/
+
+MMBearerList *
+mm_bearer_list_new (guint max_active_bearers,
+ guint max_active_multiplexed_bearers)
+{
/* Create the object */
return g_object_new (MM_TYPE_BEARER_LIST,
- MM_BEARER_LIST_MAX_BEARERS, max_bearers,
MM_BEARER_LIST_MAX_ACTIVE_BEARERS, max_active_bearers,
+ MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS, max_active_multiplexed_bearers,
NULL);
}
@@ -294,12 +370,12 @@ set_property (GObject *object,
case PROP_NUM_BEARERS:
g_assert_not_reached ();
break;
- case PROP_MAX_BEARERS:
- self->priv->max_bearers = g_value_get_uint (value);
- break;
case PROP_MAX_ACTIVE_BEARERS:
self->priv->max_active_bearers = g_value_get_uint (value);
break;
+ case PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS:
+ self->priv->max_active_multiplexed_bearers = g_value_get_uint (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -318,12 +394,12 @@ get_property (GObject *object,
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;
case PROP_MAX_ACTIVE_BEARERS:
g_value_set_uint (value, self->priv->max_active_bearers);
break;
+ case PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS:
+ g_value_set_uint (value, self->priv->max_active_multiplexed_bearers);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -344,7 +420,10 @@ dispose (GObject *object)
{
MMBearerList *self = MM_BEARER_LIST (object);
- mm_bearer_list_delete_all_bearers (self);
+ if (self->priv->bearers) {
+ g_list_free_full (self->priv->bearers, g_object_unref);
+ self->priv->bearers = NULL;
+ }
G_OBJECT_CLASS (mm_bearer_list_parent_class)->dispose (object);
}
@@ -371,24 +450,23 @@ mm_bearer_list_class_init (MMBearerListClass *klass)
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",
- "Maximum number of bearers the list can handle",
- 1,
- G_MAXUINT,
- 1,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
- g_object_class_install_property (object_class, PROP_MAX_BEARERS, properties[PROP_MAX_BEARERS]);
-
properties[PROP_MAX_ACTIVE_BEARERS] =
g_param_spec_uint (MM_BEARER_LIST_MAX_ACTIVE_BEARERS,
"Max active bearers",
"Maximum number of active bearers the list can handle",
- 1,
+ 0,
G_MAXUINT,
- 1,
+ 0,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_MAX_ACTIVE_BEARERS, properties[PROP_MAX_ACTIVE_BEARERS]);
+ properties[PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS] =
+ g_param_spec_uint (MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS,
+ "Max active multiplexed bearers",
+ "Maximum number of active multiplexed bearers the list can handle",
+ 0,
+ G_MAXUINT,
+ 0,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS, properties[PROP_MAX_ACTIVE_MULTIPLEXED_BEARERS]);
}
diff --git a/src/mm-bearer-list.h b/src/mm-bearer-list.h
index a9698958..71a394ba 100644
--- a/src/mm-bearer-list.h
+++ b/src/mm-bearer-list.h
@@ -30,9 +30,9 @@
#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"
+#define MM_BEARER_LIST_NUM_BEARERS "num-bearers"
+#define MM_BEARER_LIST_MAX_ACTIVE_BEARERS "max-active-bearers"
+#define MM_BEARER_LIST_MAX_ACTIVE_MULTIPLEXED_BEARERS "max-active-multiplexed-bearers"
typedef struct _MMBearerList MMBearerList;
typedef struct _MMBearerListClass MMBearerListClass;
@@ -48,16 +48,14 @@ struct _MMBearerListClass {
};
GType mm_bearer_list_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerList, g_object_unref)
-MMBearerList *mm_bearer_list_new (guint max_bearers,
- guint max_active_bearers);
+MMBearerList *mm_bearer_list_new (guint max_active_bearers,
+ guint max_active_multiplexed_bearers);
-GStrv mm_bearer_list_get_paths (MMBearerList *self);
-
-guint mm_bearer_list_get_count (MMBearerList *self);
-guint mm_bearer_list_get_count_active (MMBearerList *self);
-guint mm_bearer_list_get_max (MMBearerList *self);
-guint mm_bearer_list_get_max_active (MMBearerList *self);
+GStrv mm_bearer_list_get_paths (MMBearerList *self);
+guint mm_bearer_list_get_max_active (MMBearerList *self);
+guint mm_bearer_list_get_max_active_multiplexed (MMBearerList *self);
gboolean mm_bearer_list_add_bearer (MMBearerList *self,
MMBaseBearer *bearer,
@@ -65,7 +63,6 @@ gboolean mm_bearer_list_add_bearer (MMBearerList *self,
gboolean mm_bearer_list_delete_bearer (MMBearerList *self,
const gchar *path,
GError **error);
-void mm_bearer_list_delete_all_bearers (MMBearerList *self);
typedef void (*MMBearerListForeachFunc) (MMBaseBearer *bearer,
gpointer user_data);
@@ -73,8 +70,12 @@ void mm_bearer_list_foreach (MMBearerList *self,
MMBearerListForeachFunc func,
gpointer user_data);
-MMBaseBearer *mm_bearer_list_find (MMBearerList *self,
- MMBearerProperties *properties);
+MMBaseBearer *mm_bearer_list_find_by_properties (MMBearerList *self,
+ MMBearerProperties *properties);
+MMBaseBearer *mm_bearer_list_find_by_path (MMBearerList *self,
+ const gchar *path);
+MMBaseBearer *mm_bearer_list_find_by_profile_id (MMBearerList *self,
+ gint profile_id);
void mm_bearer_list_disconnect_all_bearers (MMBearerList *self,
GAsyncReadyCallback callback,
@@ -83,4 +84,15 @@ gboolean mm_bearer_list_disconnect_all_bearers_finish (MMBearerList *self,
GAsyncResult *res,
GError **error);
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+void mm_bearer_list_sync_all_bearers (MMBearerList *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_bearer_list_sync_all_bearers_finish (MMBearerList *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
+
#endif /* MM_BEARER_LIST_H */
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c
index 358b261a..36848ee3 100644
--- a/src/mm-bearer-mbim.c
+++ b/src/mm-bearer-mbim.c
@@ -27,60 +27,54 @@
#include <libmm-glib.h>
#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-modem-helpers-mbim.h"
#include "mm-port-enums-types.h"
#include "mm-bearer-mbim.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
+#include "mm-context.h"
G_DEFINE_TYPE (MMBearerMbim, mm_bearer_mbim, MM_TYPE_BASE_BEARER)
-enum {
- PROP_0,
- PROP_SESSION_ID,
- PROP_LAST
-};
-
-static GParamSpec *properties[PROP_LAST];
-
struct _MMBearerMbimPrivate {
- /* The session ID for this bearer */
- guint32 session_id;
-
- MMPort *data;
+ MMPortMbim *mbim;
+ MMPort *data;
+ MMPort *link;
+ guint32 session_id;
};
/*****************************************************************************/
static gboolean
-peek_ports (gpointer self,
- MbimDevice **o_device,
- MMPort **o_data,
- GAsyncReadyCallback callback,
- gpointer user_data)
+peek_ports (gpointer self,
+ MMPortMbim **o_mbim,
+ MMPort **o_data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- MMBaseModem *modem = NULL;
+ g_autoptr(MMBaseModem) modem = NULL;
g_object_get (G_OBJECT (self),
MM_BASE_BEARER_MODEM, &modem,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
- if (o_device) {
+ if (o_mbim) {
MMPortMbim *port;
- port = mm_base_modem_peek_port_mbim (modem);
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem));
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek MBIM port");
- g_object_unref (modem);
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ peek_ports,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek MBIM port");
return FALSE;
}
- *o_device = mm_port_mbim_peek_device (port);
+ *o_mbim = port;
}
if (o_data) {
@@ -89,100 +83,218 @@ peek_ports (gpointer self,
/* Grab a data port */
port = mm_base_modem_peek_best_data_port (modem, MM_PORT_TYPE_NET);
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
- g_object_unref (modem);
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ peek_ports,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
return FALSE;
}
*o_data = port;
}
- g_object_unref (modem);
return TRUE;
}
/*****************************************************************************/
+/* Stats */
+
+typedef struct {
+ guint64 rx_bytes;
+ guint64 tx_bytes;
+} ReloadStatsResult;
+
+static gboolean
+reload_stats_finish (MMBaseBearer *bearer,
+ guint64 *rx_bytes,
+ guint64 *tx_bytes,
+ GAsyncResult *res,
+ GError **error)
+{
+ ReloadStatsResult *stats;
+
+ stats = g_task_propagate_pointer (G_TASK (res), error);
+ if (!stats)
+ return FALSE;
+
+ if (rx_bytes)
+ *rx_bytes = stats->rx_bytes;
+ if (tx_bytes)
+ *tx_bytes = stats->tx_bytes;
+
+ g_free (stats);
+ return TRUE;
+}
+
+static void
+packet_statistics_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ guint64 in_octets = 0;
+ guint64 out_octets = 0;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_packet_statistics_response_parse (
+ response,
+ NULL, /* in_discards */
+ NULL, /* in_errors */
+ &in_octets, /* in_octets */
+ NULL, /* in_packets */
+ &out_octets, /* out_octets */
+ NULL, /* out_packets */
+ NULL, /* out_errors */
+ NULL, /* out_discards */
+ &error)) {
+ /* Store results */
+ ReloadStatsResult *stats;
+
+ stats = g_new (ReloadStatsResult, 1);
+ stats->rx_bytes = in_octets;
+ stats->tx_bytes = out_octets;
+ g_task_return_pointer (task, stats, g_free);
+ } else if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED)) {
+ g_clear_error (&error);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "operation not allowed");
+ } else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static void
+reload_stats (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMPortMbim *mbim;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_ports (self, &mbim, NULL, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ message = (mbim_message_packet_statistics_query_new (NULL));
+ mbim_device_command (mm_port_mbim_peek_device (mbim),
+ message,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)packet_statistics_query_ready,
+ task);
+}
+
+/*****************************************************************************/
/* Connect */
+#define WAIT_LINK_PORT_TIMEOUT_MS 2500
+
typedef enum {
CONNECT_STEP_FIRST,
+ CONNECT_STEP_LOAD_PROFILE_SETTINGS,
CONNECT_STEP_PACKET_SERVICE,
- CONNECT_STEP_PROVISIONED_CONTEXTS,
+ CONNECT_STEP_SETUP_LINK,
+ CONNECT_STEP_SETUP_LINK_MASTER_UP,
+ CONNECT_STEP_CHECK_DISCONNECTED,
+ CONNECT_STEP_ENSURE_DISCONNECTED,
CONNECT_STEP_CONNECT,
CONNECT_STEP_IP_CONFIGURATION,
CONNECT_STEP_LAST
} ConnectStep;
typedef struct {
- MMBearerMbim *self;
- MbimDevice *device;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- MMBearerProperties *properties;
- ConnectStep step;
- MMPort *data;
- MbimContextIpType ip_type;
+ MMPortMbim *mbim;
+ MMBroadbandModemMbim *modem;
+ ConnectStep step;
+ MMPort *data;
MMBearerConnectResult *connect_result;
+ guint64 uplink_speed;
+ guint64 downlink_speed;
+ /* settings to use */
+ gint profile_id;
+ gchar *apn;
+ MbimContextType context_type;
+ gchar *user;
+ gchar *password;
+ MbimAuthProtocol auth;
+ MbimContextIpType requested_ip_type;
+ MbimContextIpType activated_ip_type;
+ /* multiplex support */
+ guint session_id;
+ gchar *link_prefix_hint;
+ gchar *link_name;
+ MMPort *link;
} ConnectContext;
static void
-connect_context_complete_and_free (ConnectContext *ctx)
+connect_context_free (ConnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->connect_result)
- 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);
+ if (ctx->link_name) {
+ mm_port_mbim_cleanup_link (ctx->mbim, ctx->link_name, NULL, NULL);
+ g_free (ctx->link_name);
+ }
+ g_clear_object (&ctx->link);
+ g_free (ctx->link_prefix_hint);
+
+ g_free (ctx->apn);
+ g_free (ctx->user);
+ g_free (ctx->password);
+
+ g_clear_pointer (&ctx->connect_result, (GDestroyNotify)mm_bearer_connect_result_unref);
+
+ g_clear_object (&ctx->data);
+ g_object_unref (ctx->mbim);
+ g_object_unref (ctx->modem);
g_slice_free (ConnectContext, ctx);
}
static MMBearerConnectResult *
-connect_finish (MMBaseBearer *self,
- GAsyncResult *res,
- GError **error)
+connect_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void connect_context_step (ConnectContext *ctx);
+static void connect_context_step (GTask *task);
static void
-ip_configuration_query_ready (MbimDevice *device,
+ip_configuration_query_ready (MbimDevice *device,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MbimMessage *response;
- MbimIPConfigurationAvailableFlag ipv4configurationavailable;
- MbimIPConfigurationAvailableFlag ipv6configurationavailable;
- guint32 ipv4addresscount;
- MbimIPv4Element **ipv4address;
- guint32 ipv6addresscount;
- MbimIPv6Element **ipv6address;
- const MbimIPv4 *ipv4gateway;
- const MbimIPv6 *ipv6gateway;
- guint32 ipv4dnsservercount;
- MbimIPv4 *ipv4dnsserver;
- guint32 ipv6dnsservercount;
- MbimIPv6 *ipv6dnsserver;
- guint32 ipv4mtu;
- guint32 ipv6mtu;
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ MbimIPConfigurationAvailableFlag ipv4configurationavailable;
+ MbimIPConfigurationAvailableFlag ipv6configurationavailable;
+ guint32 ipv4addresscount;
+ g_autoptr(MbimIPv4ElementArray) ipv4address = NULL;
+ guint32 ipv6addresscount;
+ g_autoptr(MbimIPv6ElementArray) ipv6address = NULL;
+ const MbimIPv4 *ipv4gateway;
+ const MbimIPv6 *ipv6gateway;
+ guint32 ipv4dnsservercount;
+ g_autofree MbimIPv4 *ipv4dnsserver = NULL;
+ guint32 ipv6dnsservercount;
+ g_autofree MbimIPv6 *ipv6dnsserver = NULL;
+ guint32 ipv4mtu;
+ guint32 ipv6mtu;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_ip_configuration_response_parse (
response,
NULL, /* sessionid */
@@ -201,118 +313,125 @@ ip_configuration_query_ready (MbimDevice *device,
&ipv4mtu,
&ipv6mtu,
&error)) {
- gchar *str;
- GInetAddress *addr;
- MMBearerIpConfig *ipv4_config;
- MMBearerIpConfig *ipv6_config;
+ g_autofree gchar *ipv4configurationavailable_str = NULL;
+ g_autofree gchar *ipv6configurationavailable_str = NULL;
+ g_autoptr(MMBearerIpConfig) ipv4_config = NULL;
+ g_autoptr(MMBearerIpConfig) ipv6_config = NULL;
/* IPv4 info */
- str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv4configurationavailable);
- mm_dbg ("IPv4 configuration available: '%s'", str);
- g_free (str);
+ ipv4configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv4configurationavailable);
+ mm_obj_dbg (self, "IPv4 configuration available: '%s'", ipv4configurationavailable_str);
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) {
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv4addresscount) {
guint i;
- mm_dbg (" IP addresses (%u)", ipv4addresscount);
+ mm_obj_dbg (self, " IP addresses (%u)", ipv4addresscount);
for (i = 0; i < ipv4addresscount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[i]->ipv4_address, G_SOCKET_FAMILY_IPV4);
str = g_inet_address_to_string (addr);
- mm_dbg (" IP [%u]: '%s/%u'", i, str, ipv4address[i]->on_link_prefix_length);
- g_free (str);
- g_object_unref (addr);
+ mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv4address[i]->on_link_prefix_length);
}
}
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv4gateway) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
str = g_inet_address_to_string (addr);
- mm_dbg (" Gateway: '%s'", str);
- g_free (str);
- g_object_unref (addr);
+ mm_obj_dbg (self, " gateway: '%s'", str);
}
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv4dnsservercount) {
guint i;
- mm_dbg (" DNS addresses (%u)", ipv4dnsservercount);
+ mm_obj_dbg (self, " DNS addresses (%u)", ipv4dnsservercount);
for (i = 0; i < ipv4dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4);
if (!g_inet_address_get_is_any (addr)) {
+ g_autofree gchar *str = NULL;
+
str = g_inet_address_to_string (addr);
- mm_dbg (" DNS [%u]: '%s'", i, str);
- g_free (str);
+ mm_obj_dbg (self, " DNS [%u]: '%s'", i, str);
}
- g_object_unref (addr);
}
}
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) {
- mm_dbg (" MTU: '%u'", ipv4mtu);
- }
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv4mtu)
+ mm_obj_dbg (self, " MTU: '%u'", ipv4mtu);
/* IPv6 info */
- str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv6configurationavailable);
- mm_dbg ("IPv6 configuration available: '%s'", str);
- g_free (str);
+ ipv6configurationavailable_str = mbim_ip_configuration_available_flag_build_string_from_mask (ipv6configurationavailable);
+ mm_obj_dbg (self, "IPv6 configuration available: '%s'", ipv6configurationavailable_str);
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) {
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && ipv6addresscount) {
guint i;
- mm_dbg (" IP addresses (%u)", ipv6addresscount);
+ mm_obj_dbg (self, " IP addresses (%u)", ipv6addresscount);
for (i = 0; i < ipv6addresscount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[i]->ipv6_address, G_SOCKET_FAMILY_IPV6);
str = g_inet_address_to_string (addr);
- mm_dbg (" IP [%u]: '%s/%u'", i, str, ipv6address[i]->on_link_prefix_length);
- g_free (str);
- g_object_unref (addr);
+ mm_obj_dbg (self, " IP [%u]: '%s/%u'", i, str, ipv6address[i]->on_link_prefix_length);
}
}
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) && ipv6gateway) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
str = g_inet_address_to_string (addr);
- mm_dbg (" Gateway: '%s'", str);
- g_free (str);
- g_object_unref (addr);
+ mm_obj_dbg (self, " gateway: '%s'", str);
}
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) {
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && ipv6dnsservercount) {
guint i;
- mm_dbg (" DNS addresses (%u)", ipv6dnsservercount);
+ mm_obj_dbg (self, " DNS addresses (%u)", ipv6dnsservercount);
for (i = 0; i < ipv6dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6);
if (!g_inet_address_get_is_any (addr)) {
+ g_autofree gchar *str = NULL;
+
str = g_inet_address_to_string (addr);
- mm_dbg (" DNS [%u]: '%s'", i, str);
- g_free (str);
+ mm_obj_dbg (self, " DNS [%u]: '%s'", i, str);
}
- g_object_unref (addr);
}
}
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) {
- mm_dbg (" MTU: '%u'", ipv6mtu);
- }
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU) && ipv6mtu)
+ mm_obj_dbg (self, " MTU: '%u'", ipv6mtu);
/* Build connection results */
/* Build IPv4 config */
- if (ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ gboolean address_set = FALSE;
+
ipv4_config = mm_bearer_ip_config_new ();
- /* We assume that if we have IP and DNS, we can setup static */
- if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS &&
- ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS &&
- ipv4addresscount > 0 &&
- ipv4dnsservercount > 0) {
- gchar **strarr;
- guint i, n;
+ /* We assume that if we have an IP we can use static configuration.
+ * Not all modems or providers will return DNS servers or even a
+ * gateway, and not all modems support DHCP either. The IP management
+ * daemon/script just has to deal with this...
+ */
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv4addresscount > 0)) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_STATIC);
@@ -320,599 +439,882 @@ ip_configuration_query_ready (MbimDevice *device,
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4address[0]->ipv4_address, G_SOCKET_FAMILY_IPV4);
str = g_inet_address_to_string (addr);
mm_bearer_ip_config_set_address (ipv4_config, str);
- g_free (str);
- g_object_unref (addr);
+ address_set = TRUE;
/* Netmask */
mm_bearer_ip_config_set_prefix (ipv4_config, ipv4address[0]->on_link_prefix_length);
/* Gateway */
if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
- addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
- str = g_inet_address_to_string (addr);
- mm_bearer_ip_config_set_gateway (ipv4_config, str);
- g_free (str);
- g_object_unref (addr);
+ g_autoptr(GInetAddress) gw_addr = NULL;
+ g_autofree gchar *gw_str = NULL;
+
+ gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv4gateway, G_SOCKET_FAMILY_IPV4);
+ gw_str = g_inet_address_to_string (gw_addr);
+ mm_bearer_ip_config_set_gateway (ipv4_config, gw_str);
}
+ } else
+ mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+
+ /* DNS */
+ if ((ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv4dnsservercount > 0)) {
+ g_auto(GStrv) strarr = NULL;
+ guint i;
+ guint n;
- /* DNS */
strarr = g_new0 (gchar *, ipv4dnsservercount + 1);
for (i = 0, n = 0; i < ipv4dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv4dnsserver[i], G_SOCKET_FAMILY_IPV4);
if (!g_inet_address_get_is_any (addr))
strarr[n++] = g_inet_address_to_string (addr);
- g_object_unref (addr);
}
mm_bearer_ip_config_set_dns (ipv4_config, (const gchar **)strarr);
- g_strfreev (strarr);
- } else
- mm_bearer_ip_config_set_method (ipv4_config, MM_BEARER_IP_METHOD_DHCP);
+ }
/* MTU */
if (ipv4configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
mm_bearer_ip_config_set_mtu (ipv4_config, ipv4mtu);
- } else
- ipv4_config = NULL;
+
+ /* We requested IPv4, but it wasn't reported as activated. If there is no IP address
+ * provided by the modem, we assume the IPv4 bearer wasn't truly activated */
+ if (!address_set &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ mm_obj_dbg (self, "IPv4 requested but no IPv4 activated and no IPv4 address set: ignoring");
+ g_clear_object (&ipv4_config);
+ }
+ }
/* Build IPv6 config */
- if (ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
- ctx->ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
- ipv6_config = mm_bearer_ip_config_new ();
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4V6 ||
+ ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ gboolean address_set = FALSE;
+ gboolean gateway_set = FALSE;
+ gboolean dns_set = FALSE;
- /* We assume that if we have IP and DNS, we can setup static */
- if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS &&
- ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS &&
- ipv6addresscount > 0 &&
- ipv6dnsservercount > 0) {
- gchar **strarr;
- guint i, n;
+ ipv6_config = mm_bearer_ip_config_new ();
- mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_STATIC);
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_ADDRESS) && (ipv6addresscount > 0)) {
+ g_autoptr(GInetAddress) addr = NULL;
+ g_autofree gchar *str = NULL;
/* IP address, pick the first one */
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6address[0]->ipv6_address, G_SOCKET_FAMILY_IPV6);
str = g_inet_address_to_string (addr);
mm_bearer_ip_config_set_address (ipv6_config, str);
- g_free (str);
+ address_set = TRUE;
/* If the address is a link-local one, then SLAAC or DHCP must be used
- * to get the real prefix and address. Change the method to DHCP to
- * indicate this to clients.
+ * to get the real prefix and address.
+ * FIXME: maybe the modem reported non-LL address in ipv6address[1] ?
*/
if (g_inet_address_get_is_link_local (addr))
- mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
-
- g_object_unref (addr);
+ address_set = FALSE;
/* Netmask */
mm_bearer_ip_config_set_prefix (ipv6_config, ipv6address[0]->on_link_prefix_length);
/* Gateway */
if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_GATEWAY) {
- addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
- str = g_inet_address_to_string (addr);
- mm_bearer_ip_config_set_gateway (ipv6_config, str);
- g_free (str);
- g_object_unref (addr);
+ g_autoptr(GInetAddress) gw_addr = NULL;
+ g_autofree gchar *gw_str = NULL;
+
+ gw_addr = g_inet_address_new_from_bytes ((guint8 *)ipv6gateway, G_SOCKET_FAMILY_IPV6);
+ gw_str = g_inet_address_to_string (gw_addr);
+ mm_bearer_ip_config_set_gateway (ipv6_config, gw_str);
+ gateway_set = TRUE;
}
+ }
+
+ if ((ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_DNS) && (ipv6dnsservercount > 0)) {
+ g_auto(GStrv) strarr = NULL;
+ guint i;
+ guint n;
/* DNS */
strarr = g_new0 (gchar *, ipv6dnsservercount + 1);
for (i = 0, n = 0; i < ipv6dnsservercount; i++) {
+ g_autoptr(GInetAddress) addr = NULL;
+
addr = g_inet_address_new_from_bytes ((guint8 *)&ipv6dnsserver[i], G_SOCKET_FAMILY_IPV6);
if (!g_inet_address_get_is_any (addr))
strarr[n++] = g_inet_address_to_string (addr);
- g_object_unref (addr);
}
mm_bearer_ip_config_set_dns (ipv6_config, (const gchar **)strarr);
- g_strfreev (strarr);
- } else
- mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+ dns_set = TRUE;
+ }
/* MTU */
if (ipv6configurationavailable & MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_MTU)
mm_bearer_ip_config_set_mtu (ipv6_config, ipv6mtu);
- } else
- ipv6_config = NULL;
+
+ /* Only use the static method if all basic properties are available,
+ * otherwise use DHCP to indicate the missing ones should be
+ * retrieved from SLAAC or DHCPv6.
+ */
+ if (address_set && gateway_set && dns_set)
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_STATIC);
+ else
+ mm_bearer_ip_config_set_method (ipv6_config, MM_BEARER_IP_METHOD_DHCP);
+
+ /* We requested IPv6, but it wasn't reported as activated. If there is no IPv6 address
+ * provided by the modem, we assume the IPv6 bearer wasn't truly activated */
+ if (!address_set &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4V6 &&
+ ctx->activated_ip_type != MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6) {
+ mm_obj_dbg (self, "IPv6 requested but no IPv6 activated and no IPv6 address set: ignoring");
+ g_clear_object (&ipv6_config);
+ }
+ }
/* Store result */
- ctx->connect_result = mm_bearer_connect_result_new (ctx->data,
+ ctx->connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data,
ipv4_config,
ipv6_config);
+ mm_bearer_connect_result_set_multiplexed (ctx->connect_result, !!ctx->link);
- 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 (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
+ mm_bearer_connect_result_set_profile_id (ctx->connect_result, ctx->profile_id);
- if (response)
- mbim_message_unref (response);
+ mm_bearer_connect_result_set_uplink_speed (ctx->connect_result, ctx->uplink_speed);
+ mm_bearer_connect_result_set_downlink_speed (ctx->connect_result, ctx->downlink_speed);
+ }
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
}
static void
-connect_set_ready (MbimDevice *device,
+connect_set_ready (MbimDevice *device,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MbimMessage *response;
- guint32 session_id;
- MbimActivationState activation_state;
- MbimContextIpType ip_type;
- guint32 nw_error;
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ guint32 session_id;
+ MbimActivationState activation_state;
+ guint32 nw_error;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
- (mbim_message_command_done_get_result (response, &error) ||
+ (mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
error->code == MBIM_STATUS_ERROR_FAILURE)) {
- GError *inner_error = NULL;
+ g_autoptr(GError) inner_error = NULL;
if (mbim_message_connect_response_parse (
response,
&session_id,
&activation_state,
NULL, /* voice_call_state */
- &ip_type,
+ &ctx->activated_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)",
+ /* Report the IP type we asked for and the one returned by the modem */
+ mm_obj_dbg (self, "session ID '%u': %s (requested IP type: %s, activated IP type: %s, nw error: %s)",
session_id,
mbim_activation_state_get_string (activation_state),
- mbim_context_ip_type_get_string (ip_type));
+ mbim_context_ip_type_get_string (ctx->requested_ip_type),
+ mbim_context_ip_type_get_string (ctx->activated_ip_type),
+ mbim_nw_error_get_string (nw_error));
+ /* If the response reports an ACTIVATED state, we're good even if
+ * there is a nw_error set (e.g. asking for IPv4v6 may return a
+ * 'pdp-type-ipv4-only-allowed' nw_error). */
+ if (activation_state != MBIM_ACTIVATION_STATE_ACTIVATED &&
+ activation_state != MBIM_ACTIVATION_STATE_ACTIVATING) {
+ g_clear_error (&error);
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error, self);
}
} else {
/* Prefer the error from the result to the parsing error */
if (!error)
- error = inner_error;
- else
- g_error_free (inner_error);
+ error = g_steal_pointer (&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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
}
static void
-provisioned_contexts_query_ready (MbimDevice *device,
- GAsyncResult *res,
- ConnectContext *ctx)
+ensure_disconnected_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
- MbimMessage *response;
- guint32 provisioned_contexts_count;
- MbimProvisionedContextElement **provisioned_contexts;
+ ConnectContext *ctx;
+ g_autoptr(MbimMessage) response = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ /* Ignore all errors, just go on */
+ response = mbim_device_command_finish (device, res, NULL);
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+check_disconnected_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ guint32 session_id;
+ MbimActivationState activation_state;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_provisioned_contexts_response_parse (
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_connect_response_parse (
response,
- &provisioned_contexts_count,
- &provisioned_contexts,
+ &session_id,
+ &activation_state,
+ NULL, /* voice_call_state */
+ NULL, /* ip_type */
+ NULL, /* context_type */
+ NULL, /* nw_error */
&error)) {
- guint32 i;
-
- mm_dbg ("Provisioned contexts found (%u):", provisioned_contexts_count);
- for (i = 0; i < provisioned_contexts_count; i++) {
- MbimProvisionedContextElement *el = provisioned_contexts[i];
- gchar *uuid_str;
-
- uuid_str = mbim_uuid_get_printable (&el->context_type);
- mm_dbg ("[%u] context type: %s", el->context_id, mbim_context_type_get_string (mbim_uuid_to_context_type (&el->context_type)));
- mm_dbg (" uuid: %s", uuid_str);
- mm_dbg (" access string: %s", el->access_string ? el->access_string : "");
- mm_dbg (" username: %s", el->user_name ? el->user_name : "");
- mm_dbg (" password: %s", el->password ? el->password : "");
- mm_dbg (" compression: %s", mbim_compression_get_string (el->compression));
- mm_dbg (" auth: %s", mbim_auth_protocol_get_string (el->auth_protocol));
- g_free (uuid_str);
- }
+ mm_obj_dbg (self, "session ID '%u': %s", session_id, mbim_activation_state_get_string (activation_state));
+ } else
+ activation_state = MBIM_ACTIVATION_STATE_UNKNOWN;
+
+ /* Some modem (e.g. Huawei ME936) reports MBIM_ACTIVATION_STATE_UNKNOWN
+ * when being queried for the activation state before an IP session has
+ * been activated once. Here we expect a modem would at least tell the
+ * truth when the session has been activated, so we proceed to deactivate
+ * the session only the modem indicates the session has been activated or
+ * is being activated.
+ */
+ if (activation_state == MBIM_ACTIVATION_STATE_ACTIVATED || activation_state == MBIM_ACTIVATION_STATE_ACTIVATING)
+ ctx->step = CONNECT_STEP_ENSURE_DISCONNECTED;
+ else
+ ctx->step = CONNECT_STEP_CONNECT;
+
+ connect_context_step (task);
+}
- mbim_provisioned_context_element_array_free (provisioned_contexts);
- } else {
- mm_dbg ("Error listing provisioned contexts: %s", error->message);
- g_error_free (error);
+static void
+master_interface_up_ready (MMPortNet *link,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_net_link_setup_finish (link, res, &error)) {
+ g_prefix_error (&error, "Couldn't bring master interface up: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- if (response)
- mbim_message_unref (response);
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+wait_link_port_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->link = mm_base_modem_wait_link_port_finish (modem, res, &error);
+ if (!ctx->link) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
+}
+
+static void
+setup_link_ready (MMPortMbim *mbim,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MMBaseModem) modem = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->link_name = mm_port_mbim_setup_link_finish (mbim, res, &ctx->session_id, &error);
+ if (!ctx->link_name) {
+ g_prefix_error (&error, "failed to create net link for device: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* From now on link_name will be set, and we'll use that to know
+ * whether we should cleanup the link upon a connection failure */
+ mm_obj_info (self, "net link %s created (session id %u)", ctx->link_name, ctx->session_id);
+
+ /* Wait for the data port with the given interface name, which will be
+ * added asynchronously */
+ g_object_get (self,
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+ g_assert (modem);
+
+ mm_base_modem_wait_link_port (modem,
+ "net",
+ ctx->link_name,
+ WAIT_LINK_PORT_TIMEOUT_MS,
+ (GAsyncReadyCallback) wait_link_port_ready,
+ task);
}
static void
packet_service_set_ready (MbimDevice *device,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MbimMessage *response;
- guint32 nw_error;
- MbimPacketServiceState packet_service_state;
- MbimDataClass highest_available_data_class;
- guint64 uplink_speed;
- guint64 downlink_speed;
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ guint32 nw_error;
+ MbimPacketServiceState packet_service_state;
+ MbimDataClass data_class;
+ guint64 uplink_speed = 0;
+ guint64 downlink_speed = 0;
+ MbimFrequencyRange frequency_range = MBIM_FREQUENCY_RANGE_UNKNOWN;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
- (mbim_message_command_done_get_result (response, &error) ||
+ (mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
error->code == MBIM_STATUS_ERROR_FAILURE)) {
- GError *inner_error = NULL;
+ g_autoptr(GError) inner_error = NULL;
- if (mbim_message_packet_service_response_parse (
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ mbim_message_ms_basic_connect_v2_packet_service_response_parse (
response,
&nw_error,
&packet_service_state,
- &highest_available_data_class,
+ &data_class,
&uplink_speed,
&downlink_speed,
- &inner_error)) {
+ &frequency_range,
+ &inner_error);
+ } else {
+ mbim_message_packet_service_response_parse (
+ response,
+ &nw_error,
+ &packet_service_state,
+ &data_class,
+ &uplink_speed,
+ &downlink_speed,
+ &inner_error);
+ }
+
+ if (!inner_error) {
if (nw_error) {
- if (error)
- g_error_free (error);
- error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
+ g_clear_error (&error);
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error, self);
} 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);
+ g_autofree gchar *data_class_str = NULL;
+ g_autofree gchar *frequency_range_str = NULL;
+
+ data_class_str = mbim_data_class_build_string_from_mask (data_class);
+ frequency_range_str = mbim_frequency_range_build_string_from_mask (frequency_range);
+ mm_obj_dbg (self, "packet service update:");
+ mm_obj_dbg (self, " state: '%s'", mbim_packet_service_state_get_string (packet_service_state));
+ mm_obj_dbg (self, " data class: '%s'", data_class_str);
+ mm_obj_dbg (self, " uplink: '%" G_GUINT64_FORMAT "' bps", uplink_speed);
+ mm_obj_dbg (self, " downlink: '%" G_GUINT64_FORMAT "' bps", downlink_speed);
+ mm_obj_dbg (self, " frequency range: '%s'", frequency_range_str);
}
} else {
/* Prefer the error from the result to the parsing error */
if (!error)
- error = inner_error;
- else
- g_error_free (inner_error);
+ error = g_steal_pointer (&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. */
if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NO_DEVICE_SUPPORT)) {
- mm_dbg ("Device doesn't support packet service attach");
+ mm_obj_dbg (self, "device doesn't support packet service attach");
g_error_free (error);
} else {
/* All other errors are fatal */
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
}
+ /* store speeds to include in the connection result later on */
+ ctx->uplink_speed = uplink_speed;
+ ctx->downlink_speed = downlink_speed;
+
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
+}
+
+static gboolean
+load_settings_from_profile (MMBearerMbim *self,
+ ConnectContext *ctx,
+ MM3gppProfile *profile,
+ MMBearerApnType default_apn_type,
+ GError **error)
+{
+ MMBearerAllowedAuth bearer_auth;
+ MMBearerApnType apn_type;
+ GError *inner_error = NULL;
+
+ /* APN settings */
+ ctx->apn = g_strdup (mm_3gpp_profile_get_apn (profile));
+ apn_type = mm_3gpp_profile_get_apn_type (profile);
+ if (apn_type == MM_BEARER_APN_TYPE_NONE) {
+ if (default_apn_type == MM_BEARER_APN_TYPE_NONE) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "APN type in profile is not initialized");
+ return FALSE;
+ }
+ apn_type = default_apn_type;
+ }
+ ctx->context_type = mm_bearer_apn_type_to_mbim_context_type (apn_type, self, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ /* Auth settings */
+ ctx->user = g_strdup (mm_3gpp_profile_get_user (profile));
+ ctx->password = g_strdup (mm_3gpp_profile_get_password (profile));
+ if (!ctx->user && !ctx->password) {
+ ctx->auth = MBIM_AUTH_PROTOCOL_NONE;
+ } else {
+ bearer_auth = mm_3gpp_profile_get_allowed_auth (profile);
+ ctx->auth = mm_bearer_allowed_auth_to_mbim_auth_protocol (bearer_auth, self, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ }
+
+ /* This IP type reading is applicable only when the profile comes
+ * from the input bearer properties, as there is no IP type stored
+ * in the device profiles. Therefore, only read it if it hasn't been
+ * read yet */
+ if (ctx->requested_ip_type == MBIM_CONTEXT_IP_TYPE_DEFAULT) {
+ MMBearerIpFamily ip_type;
+
+ ip_type = mm_3gpp_profile_get_ip_type (profile);
+ mm_3gpp_normalize_ip_family (&ip_type);
+ ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
}
static void
-connect_context_step (ConnectContext *ctx)
+get_profile_ready (MMIfaceModem3gppProfileManager *modem,
+ GAsyncResult *res,
+ GTask *task)
{
- MbimMessage *message;
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error);
+ if (!profile) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!load_settings_from_profile (self, ctx, profile, MM_BEARER_APN_TYPE_NONE, &error)) {
+ g_prefix_error (&error, "Couldn't load settings from profile: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+connect_context_step (GTask *task)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ g_autoptr(MbimMessage) message = NULL;
/* If cancelled, complete */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Connection setup operation has been cancelled");
- connect_context_complete_and_free (ctx);
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case CONNECT_STEP_FIRST:
- /* Fall down */
ctx->step++;
-
- case CONNECT_STEP_PACKET_SERVICE: {
- GError *error = NULL;
-
- mm_dbg ("Activating packet service...");
- message = (mbim_message_packet_service_set_new (
- MBIM_PACKET_SERVICE_ACTION_ATTACH,
- &error));
- if (!message) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ /* Fall through */
+
+ case CONNECT_STEP_LOAD_PROFILE_SETTINGS:
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "loading connection settings from profile '%d'...", ctx->profile_id);
+ mm_iface_modem_3gpp_profile_manager_get_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->modem),
+ ctx->profile_id,
+ (GAsyncReadyCallback)get_profile_ready,
+ task);
return;
}
+ ctx->step++;
+ /* Fall through */
- mbim_device_command (ctx->device,
+ case CONNECT_STEP_PACKET_SERVICE:
+ mm_obj_dbg (self, "activating packet service...");
+ message = mbim_message_packet_service_set_new (MBIM_PACKET_SERVICE_ACTION_ATTACH, NULL);
+ mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
30,
NULL,
(GAsyncReadyCallback)packet_service_set_ready,
- ctx);
- mbim_message_unref (message);
+ task);
return;
- }
- case CONNECT_STEP_PROVISIONED_CONTEXTS:
- mm_dbg ("Listing provisioned contexts...");
- message = mbim_message_provisioned_contexts_query_new (NULL);
- mbim_device_command (ctx->device,
+ case CONNECT_STEP_SETUP_LINK:
+ /* if a link prefix hint is available, it's because we should be doing
+ * multiplexing */
+ if (ctx->link_prefix_hint) {
+ mm_obj_dbg (self, "setting up new multiplexed link...");
+ mm_port_mbim_setup_link (ctx->mbim,
+ ctx->data,
+ ctx->link_prefix_hint,
+ (GAsyncReadyCallback) setup_link_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_SETUP_LINK_MASTER_UP:
+ /* if the connection is done through a new link, we need to ifup the master interface */
+ if (ctx->link) {
+ mm_obj_dbg (self, "bringing master interface %s up...", mm_port_get_device (ctx->data));
+ mm_port_net_link_setup (MM_PORT_NET (ctx->data),
+ TRUE,
+ 0, /* ignore */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) master_interface_up_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_CHECK_DISCONNECTED:
+ mm_obj_dbg (self, "checking if session %u is disconnected...", ctx->session_id);
+ message = mbim_message_connect_query_new (
+ ctx->session_id,
+ MBIM_ACTIVATION_STATE_UNKNOWN,
+ MBIM_VOICE_CALL_STATE_NONE,
+ MBIM_CONTEXT_IP_TYPE_DEFAULT,
+ mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+ 0,
+ NULL);
+ mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
10,
NULL,
- (GAsyncReadyCallback)provisioned_contexts_query_ready,
- ctx);
- mbim_message_unref (message);
+ (GAsyncReadyCallback)check_disconnected_ready,
+ task);
return;
- case CONNECT_STEP_CONNECT: {
- const gchar *apn;
- const gchar *user;
- const gchar *password;
- MbimAuthProtocol auth;
- MbimContextIpType ip_type;
- MMBearerIpFamily ip_family;
- GError *error = NULL;
-
- /* Setup parameters to use */
-
- apn = mm_bearer_properties_get_apn (ctx->properties);
- user = mm_bearer_properties_get_user (ctx->properties);
- password = mm_bearer_properties_get_password (ctx->properties);
-
- if (!user && !password) {
- auth = MBIM_AUTH_PROTOCOL_NONE;
- } else {
- MMBearerAllowedAuth bearer_auth;
- bearer_auth = mm_bearer_properties_get_allowed_auth (ctx->properties);
- if (bearer_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
- mm_dbg ("Using default (PAP) authentication method");
- auth = MBIM_AUTH_PROTOCOL_PAP;
- } else if (bearer_auth & MM_BEARER_ALLOWED_AUTH_PAP) {
- auth = MBIM_AUTH_PROTOCOL_PAP;
- } else if (bearer_auth & MM_BEARER_ALLOWED_AUTH_CHAP) {
- auth = MBIM_AUTH_PROTOCOL_CHAP;
- } else if (bearer_auth & MM_BEARER_ALLOWED_AUTH_MSCHAPV2) {
- auth = MBIM_AUTH_PROTOCOL_MSCHAPV2;
- } else if (bearer_auth & MM_BEARER_ALLOWED_AUTH_NONE) {
- auth = MBIM_AUTH_PROTOCOL_NONE;
- } else {
- gchar *str;
-
- str = mm_bearer_allowed_auth_build_string_from_mask (bearer_auth);
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot use any of the specified authentication methods (%s)",
- str);
- g_free (str);
- connect_context_complete_and_free (ctx);
- return;
- }
- }
-
- ip_family = mm_bearer_properties_get_ip_type (ctx->properties);
- if (ip_family == MM_BEARER_IP_FAMILY_NONE ||
- ip_family == MM_BEARER_IP_FAMILY_ANY) {
- gchar * str;
-
- ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (ctx->self));
- str = mm_bearer_ip_family_build_string_from_mask (ip_family);
- mm_dbg ("No specific IP family requested, defaulting to %s", str);
- g_free (str);
- }
-
- if (ip_family == MM_BEARER_IP_FAMILY_IPV4)
- ip_type = MBIM_CONTEXT_IP_TYPE_IPV4;
- else if (ip_family == MM_BEARER_IP_FAMILY_IPV6)
- ip_type = MBIM_CONTEXT_IP_TYPE_IPV6;
- else if (ip_family == MM_BEARER_IP_FAMILY_IPV4V6)
- ip_type = MBIM_CONTEXT_IP_TYPE_IPV4V6;
- else if (ip_family == (MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6))
- ip_type = MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6;
- else if (ip_family == MM_BEARER_IP_FAMILY_NONE ||
- ip_family == MM_BEARER_IP_FAMILY_ANY)
- /* A valid default IP family should have been specified */
- g_assert_not_reached ();
- else {
- gchar * str;
-
- str = mm_bearer_ip_family_build_string_from_mask (ip_family);
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Unsupported IP type configuration: '%s'",
- str);
- g_free (str);
- connect_context_complete_and_free (ctx);
- return;
- }
-
- mm_dbg ("Launching connection with APN '%s'...", apn);
- message = (mbim_message_connect_set_new (
- ctx->self->priv->session_id,
- MBIM_ACTIVATION_COMMAND_ACTIVATE,
- apn ? apn : "",
- user ? user : "",
- password ? password : "",
- MBIM_COMPRESSION_NONE,
- auth,
- ip_type,
- mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
- &error));
- if (!message) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
- return;
- }
+ case CONNECT_STEP_ENSURE_DISCONNECTED:
+ mm_obj_dbg (self, "ensuring session %u is disconnected...", ctx->session_id);
+ message = mbim_message_connect_set_new (
+ ctx->session_id,
+ MBIM_ACTIVATION_COMMAND_DEACTIVATE,
+ "",
+ "",
+ "",
+ MBIM_COMPRESSION_NONE,
+ MBIM_AUTH_PROTOCOL_NONE,
+ MBIM_CONTEXT_IP_TYPE_DEFAULT,
+ mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+ NULL);
+ mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
+ message,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
+ NULL,
+ (GAsyncReadyCallback)ensure_disconnected_ready,
+ task);
+ return;
- mbim_device_command (ctx->device,
+ case CONNECT_STEP_CONNECT:
+ mm_obj_dbg (self, "launching %s connection in session %u...",
+ mbim_context_ip_type_get_string (ctx->requested_ip_type), ctx->session_id);
+ message = mbim_message_connect_set_new (
+ ctx->session_id,
+ MBIM_ACTIVATION_COMMAND_ACTIVATE,
+ ctx->apn ? ctx->apn : "",
+ ctx->user ? ctx->user : "",
+ ctx->password ? ctx->password : "",
+ MBIM_COMPRESSION_NONE,
+ ctx->auth,
+ ctx->requested_ip_type,
+ mbim_uuid_from_context_type (ctx->context_type),
+ NULL);
+ mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
- 60,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
NULL,
(GAsyncReadyCallback)connect_set_ready,
- ctx);
- mbim_message_unref (message);
+ task);
return;
- }
-
- case CONNECT_STEP_IP_CONFIGURATION: {
- GError *error = NULL;
-
- mm_dbg ("Querying IP configuration...");
- message = (mbim_message_ip_configuration_query_new (
- ctx->self->priv->session_id,
- MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv4configurationavailable */
- MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv6configurationavailable */
- 0, /* ipv4addresscount */
- NULL, /* ipv4address */
- 0, /* ipv6addresscount */
- NULL, /* ipv6address */
- NULL, /* ipv4gateway */
- NULL, /* ipv6gateway */
- 0, /* ipv4dnsservercount */
- NULL, /* ipv4dnsserver */
- 0, /* ipv6dnsservercount */
- NULL, /* ipv6dnsserver */
- 0, /* ipv4mtu */
- 0, /* ipv6mtu */
- &error));
- if (!message) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
- return;
- }
- mbim_device_command (ctx->device,
+ case CONNECT_STEP_IP_CONFIGURATION:
+ mm_obj_dbg (self, "querying IP configuration...");
+ message = mbim_message_ip_configuration_query_new (
+ ctx->session_id,
+ MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv4configurationavailable */
+ MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv6configurationavailable */
+ 0, /* ipv4addresscount */
+ NULL, /* ipv4address */
+ 0, /* ipv6addresscount */
+ NULL, /* ipv6address */
+ NULL, /* ipv4gateway */
+ NULL, /* ipv6gateway */
+ 0, /* ipv4dnsservercount */
+ NULL, /* ipv4dnsserver */
+ 0, /* ipv6dnsservercount */
+ NULL, /* ipv6dnsserver */
+ 0, /* ipv4mtu */
+ 0, /* ipv6mtu */
+ NULL);
+ mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
60,
NULL,
(GAsyncReadyCallback)ip_configuration_query_ready,
- ctx);
- mbim_message_unref (message);
+ task);
return;
- }
case CONNECT_STEP_LAST:
/* Port is connected; update the state */
- mm_port_set_connected (MM_PORT (ctx->data), TRUE);
+ mm_port_set_connected (ctx->link ? ctx->link : ctx->data, TRUE);
+
+ /* Keep connection related data */
+ g_assert (self->priv->mbim == NULL);
+ self->priv->mbim = g_object_ref (ctx->mbim);
+ g_assert (self->priv->data == NULL);
+ self->priv->data = ctx->data ? g_object_ref (ctx->data) : NULL;
+ g_assert (self->priv->link == NULL);
+ self->priv->link = ctx->link ? g_object_ref (ctx->link) : NULL;
+ g_assert (!self->priv->session_id);
+ self->priv->session_id = ctx->session_id;
- /* Keep the data port */
- g_assert (ctx->self->priv->data == NULL);
- ctx->self->priv->data = g_object_ref (ctx->data);
+ /* reset the link name to avoid cleaning up the link on context free */
+ g_clear_pointer (&ctx->link_name, g_free);
/* Set operation result */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
+ g_task_return_pointer (
+ task,
mm_bearer_connect_result_ref (ctx->connect_result),
(GDestroyNotify)mm_bearer_connect_result_unref);
- connect_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
}
-static void
-_connect (MMBaseBearer *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+static gboolean
+load_settings_from_bearer (MMBearerMbim *self,
+ ConnectContext *ctx,
+ MMBearerProperties *properties,
+ GError **error)
{
- ConnectContext *ctx;
- MMPort *data;
- MbimDevice *device;
- MMBaseModem *modem = NULL;
- const gchar *apn;
+ MMBearerMultiplexSupport multiplex;
+ gboolean multiplex_supported = TRUE;
+ const gchar *data_port_driver;
+
+ data_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (ctx->data));
+ if (!g_strcmp0 (data_port_driver, "mhi_net"))
+ multiplex_supported = FALSE;
+
+ if (mm_kernel_device_get_wwandev_sysfs_path (mm_port_peek_kernel_device (ctx->data)))
+ multiplex_supported = FALSE;
+
+ /* If no multiplex setting given by the user, assume none */
+ multiplex = mm_bearer_properties_get_multiplex (properties);
+ if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) {
+ if (mm_context_get_test_multiplex_requested ())
+ multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED;
+ else
+ multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE;
+ }
- if (!peek_ports (self, &device, &data, callback, user_data))
- return;
+ if (multiplex_supported &&
+ (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED ||
+ multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED)) {
+ /* the link prefix hint given must be modem-specific */
+ ctx->link_prefix_hint = g_strdup_printf ("mbimmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (ctx->modem)));
+ }
- g_object_get (self,
- MM_BASE_BEARER_MODEM, &modem,
- NULL);
- g_assert (modem);
+ if (!multiplex_supported && multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Multiplexing required but not supported by %s", data_port_driver);
+ return FALSE;
+ }
- /* Check whether we have an APN */
- apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
-
- /* Is this a 3GPP only modem and no APN was given? If so, error */
- if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !apn) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "3GPP connection logic requires APN setting");
- g_object_unref (modem);
- return;
+ /* If profile id is given, we'll load all settings from the stored profile,
+ * so ignore any other setting received in the bearer properties */
+ ctx->profile_id = mm_bearer_properties_get_profile_id (properties);
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ MMBearerIpFamily ip_type;
+ GError *inner_error = NULL;
+
+ /* If we're loading settings from a profile, still read the ip-type
+ * from the user input, as that is not stored in the profile */
+ ip_type = mm_bearer_properties_get_ip_type (properties);
+ mm_3gpp_normalize_ip_family (&ip_type);
+ ctx->requested_ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_type, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* Use the implicit profile settings in the bearer properties.
+ * If not loading from a stored profile, initialize the APN type to 'internet'
+ * (TYPE_DEFAULT) by default, which is what we've done until now. */
+ if (!load_settings_from_profile (self,
+ ctx,
+ mm_bearer_properties_peek_3gpp_profile (properties),
+ MM_BEARER_APN_TYPE_DEFAULT,
+ error))
+ return FALSE;
+
+ /* Is this a 3GPP only modem and no APN or profile id was given? If so, error */
+ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (ctx->modem)) && !ctx->apn) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "3GPP connection logic requires APN or profile id setting");
+ return FALSE;
}
- g_object_unref (modem);
+ return TRUE;
+}
- mm_dbg ("Launching connection with data port (%s/%s)",
- mm_port_subsys_get_string (mm_port_get_subsys (data)),
- mm_port_get_device (data));
+static void
+_connect (MMBaseBearer *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ConnectContext *ctx;
+ MMPort *data;
+ MMPortMbim *mbim;
+ GTask *task;
+ GError *error = NULL;
+ g_autoptr(MMBaseModem) modem = NULL;
+ g_autoptr(MMBearerProperties) properties = NULL;
+
+ if (!peek_ports (self, &mbim, &data, callback, user_data))
+ return;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ g_object_get (self,
+ MM_BASE_BEARER_MODEM, &modem,
+ MM_BASE_BEARER_CONFIG, &properties,
+ NULL);
+ g_assert (modem);
ctx = g_slice_new0 (ConnectContext);
- ctx->self = g_object_ref (self);
- ctx->device = g_object_ref (device);;
+ ctx->modem = MM_BROADBAND_MODEM_MBIM (g_object_ref (modem));
+ ctx->mbim = g_object_ref (mbim);
ctx->data = g_object_ref (data);
- ctx->cancellable = g_object_ref (cancellable);
ctx->step = CONNECT_STEP_FIRST;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- _connect);
+ ctx->requested_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+ ctx->activated_ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
+
+ if (!load_settings_from_bearer (MM_BEARER_MBIM (self), ctx, properties, &error)) {
+ g_prefix_error (&error, "Invalid bearer properties: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- g_object_get (self,
- MM_BASE_BEARER_CONFIG, &ctx->properties,
- NULL);
+ mm_obj_dbg (self, "launching %sconnection with data port (%s/%s)",
+ ctx->link_prefix_hint ? "multiplexed " : "",
+ mm_port_subsys_get_string (mm_port_get_subsys (data)),
+ mm_port_get_device (data));
/* Run! */
- connect_context_step (ctx);
+ connect_context_step (task);
}
/*****************************************************************************/
@@ -925,193 +1327,218 @@ typedef enum {
} DisconnectStep;
typedef struct {
- MMBearerMbim *self;
- MbimDevice *device;
- GSimpleAsyncResult *result;
- MMPort *data;
- DisconnectStep step;
+ MMPortMbim *mbim;
+ guint session_id;
+ DisconnectStep step;
} DisconnectContext;
static void
-disconnect_context_complete_and_free (DisconnectContext *ctx)
+disconnect_context_free (DisconnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->data);
- g_object_unref (ctx->self);
+ g_object_unref (ctx->mbim);
g_slice_free (DisconnectContext, ctx);
}
static gboolean
-disconnect_finish (MMBaseBearer *self,
- GAsyncResult *res,
- GError **error)
+disconnect_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+reset_bearer_connection (MMBearerMbim *self)
+{
+ if (self->priv->data) {
+ mm_port_set_connected (self->priv->data, FALSE);
+ g_clear_object (&self->priv->data);
+ }
+
+ if (self->priv->link) {
+ g_assert (self->priv->mbim);
+ /* Link is disconnected; update the state */
+ mm_port_set_connected (self->priv->link, FALSE);
+ mm_port_mbim_cleanup_link (self->priv->mbim,
+ mm_port_get_device (self->priv->link),
+ NULL,
+ NULL);
+ g_clear_object (&self->priv->link);
+ }
+ self->priv->session_id = 0;
+ g_clear_object (&self->priv->mbim);
}
-static void disconnect_context_step (DisconnectContext *ctx);
+static void disconnect_context_step (GTask *task);
static void
-disconnect_set_ready (MbimDevice *device,
+disconnect_set_ready (MbimDevice *device,
GAsyncResult *res,
- DisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MbimMessage *response;
- guint32 session_id;
- MbimActivationState activation_state;
- guint32 nw_error;
+ MMBearerMbim *self;
+ DisconnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ guint32 session_id;
+ MbimActivationState activation_state;
+ guint32 nw_error;
+ g_autoptr(GError) inner_error = NULL;
+ gboolean result = FALSE;
+ gboolean parsed_result = FALSE;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
- if (response) {
- GError *inner_error = NULL;
- gboolean result = FALSE, parsed_result = FALSE;
-
- result = mbim_message_command_done_get_result (response, &error);
- /* Parse the response only for the cases we need to */
- if (result ||
- g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE) ||
- g_error_matches (error, MBIM_STATUS_ERROR,
- MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED)) {
- parsed_result = mbim_message_connect_response_parse (
- response,
- &session_id,
- &activation_state,
- NULL, /* voice_call_state */
- NULL, /* ip_type */
- NULL, /* context_type */
- &nw_error,
- &inner_error);
- }
+ if (!response)
+ goto out;
+
+ result = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
+
+ /* Parse the response only for the cases we need to */
+ if (result ||
+ g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE) ||
+ g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED)) {
+ parsed_result = mbim_message_connect_response_parse (
+ response,
+ &session_id,
+ &activation_state,
+ NULL, /* voice_call_state */
+ NULL, /* ip_type */
+ NULL, /* context_type */
+ &nw_error,
+ &inner_error);
+ }
- /* Now handle different response / error cases */
- if (result && parsed_result) {
- mm_dbg ("Session ID '%u': %s",
- session_id,
- mbim_activation_state_get_string (activation_state));
- } else if (g_error_matches (error,
- MBIM_STATUS_ERROR,
- MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED)) {
- mm_dbg ("Session ID '%u' already disconnected.", session_id);
- g_clear_error (&error);
- g_clear_error (&inner_error);
- } else if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE)) {
- if (nw_error) {
- g_error_free (error);
- error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
- }
- } /* else: For other errors, give precedence to error over nw_error */
+ /* Now handle different response / error cases */
- /* Give precedence to original error over parsing error */
- if (!error && inner_error)
- error = g_error_copy (inner_error);
+ if (result && parsed_result) {
+ g_assert (!error);
+ g_assert (!inner_error);
+ mm_obj_dbg (self, "session ID '%u': %s", session_id, mbim_activation_state_get_string (activation_state));
+ /* success */
+ goto out;
+ }
+
+ if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED)) {
+ if (parsed_result)
+ mm_obj_dbg (self, "context not activated: session ID '%u' already disconnected", session_id);
+ else
+ mm_obj_dbg (self, "context not activated: already disconnected");
+
+ g_clear_error (&error);
g_clear_error (&inner_error);
+ /* success */
+ goto out;
}
- if (response)
- mbim_message_unref (response);
+ if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE) && parsed_result && nw_error != 0) {
+ g_assert (!inner_error);
+ g_error_free (error);
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error, self);
+ /* error out with nw_error error */
+ goto out;
+ }
+ /* Give precedence to original error over parsing error */
+ if (!error && inner_error)
+ error = g_steal_pointer (&inner_error);
+
+out:
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Keep on */
ctx->step++;
- disconnect_context_step (ctx);
+ disconnect_context_step (task);
}
static void
-disconnect_context_step (DisconnectContext *ctx)
+disconnect_context_step (GTask *task)
{
+ MMBearerMbim *self;
+ DisconnectContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISCONNECT_STEP_FIRST:
- /* Fall down */
ctx->step++;
+ /* Fall through */
case DISCONNECT_STEP_DISCONNECT: {
- MbimMessage *message;
- GError *error = NULL;
-
- message = (mbim_message_connect_set_new (
- ctx->self->priv->session_id,
- MBIM_ACTIVATION_COMMAND_DEACTIVATE,
- "",
- "",
- "",
- MBIM_COMPRESSION_NONE,
- MBIM_AUTH_PROTOCOL_NONE,
- MBIM_CONTEXT_IP_TYPE_DEFAULT,
- mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
- &error));
- if (!message) {
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_context_complete_and_free (ctx);
- return;
- }
-
- mbim_device_command (ctx->device,
+ g_autoptr(MbimMessage) message = NULL;
+
+ message = mbim_message_connect_set_new (
+ ctx->session_id,
+ MBIM_ACTIVATION_COMMAND_DEACTIVATE,
+ "",
+ "",
+ "",
+ MBIM_COMPRESSION_NONE,
+ MBIM_AUTH_PROTOCOL_NONE,
+ MBIM_CONTEXT_IP_TYPE_DEFAULT,
+ mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+ NULL);
+ mbim_device_command (mm_port_mbim_peek_device (ctx->mbim),
message,
- 30,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
NULL,
(GAsyncReadyCallback)disconnect_set_ready,
- ctx);
- mbim_message_unref (message);
+ task);
return;
}
case DISCONNECT_STEP_LAST:
/* Port is disconnected; update the state */
- mm_port_set_connected (ctx->self->priv->data, FALSE);
- g_clear_object (&ctx->self->priv->data);
+ reset_bearer_connection (self);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
}
static void
-disconnect (MMBaseBearer *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+disconnect (MMBaseBearer *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- MMBearerMbim *self = MM_BEARER_MBIM (_self);
- MbimDevice *device;
+ MMBearerMbim *self = MM_BEARER_MBIM (_self);
DisconnectContext *ctx;
+ GTask *task;
- if (!self->priv->data) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't disconnect MBIM bearer: this bearer is not connected");
- return;
- }
+ task = g_task_new (self, NULL, callback, user_data);
- if (!peek_ports (self, &device, NULL, callback, user_data))
+ if ((!self->priv->data && !self->priv->link) ||
+ !self->priv->mbim) {
+ mm_obj_dbg (self, "no need to disconnect: MBIM bearer is already disconnected");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+ }
- mm_dbg ("Launching disconnection on data port (%s/%s)",
- mm_port_subsys_get_string (mm_port_get_subsys (self->priv->data)),
- mm_port_get_device (self->priv->data));
+ mm_obj_dbg (self, "launching disconnection on data port (%s/%s)",
+ mm_port_subsys_get_string (mm_port_get_subsys (self->priv->data)),
+ mm_port_get_device (self->priv->data));
ctx = g_slice_new0 (DisconnectContext);
- ctx->self = g_object_ref (self);
- ctx->device = g_object_ref (device);
- ctx->data = g_object_ref (self->priv->data);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect);
+ ctx->mbim = g_object_ref (self->priv->mbim);
+ ctx->session_id = self->priv->session_id;
ctx->step = DISCONNECT_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free);
+
/* Run! */
- disconnect_context_step (ctx);
+ disconnect_context_step (task);
}
/*****************************************************************************/
@@ -1126,72 +1553,157 @@ mm_bearer_mbim_get_session_id (MMBearerMbim *self)
/*****************************************************************************/
-MMBaseBearer *
-mm_bearer_mbim_new (MMBroadbandModemMbim *modem,
- MMBearerProperties *config,
- guint32 session_id)
+static void
+report_connection_status (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- MMBaseBearer *bearer;
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
+ /* Cleanup all connection related data */
+ reset_bearer_connection (MM_BEARER_MBIM (self));
- /* The Mbim bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer)
- * and that means that the object is not async-initable, so we just use
- * g_object_new() here */
- bearer = g_object_new (MM_TYPE_BEARER_MBIM,
- MM_BASE_BEARER_MODEM, modem,
- MM_BASE_BEARER_CONFIG, config,
- MM_BEARER_MBIM_SESSION_ID, (guint)session_id,
- NULL);
+ /* Chain up parent's report_connection_status() */
+ MM_BASE_BEARER_CLASS (mm_bearer_mbim_parent_class)->report_connection_status (self, status, connection_error);
+}
- /* Only export valid bearers */
- mm_base_bearer_export (bearer);
+/*****************************************************************************/
- return bearer;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+static MMBearerConnectionStatus
+reload_connection_status_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gint val;
+
+ val = g_task_propagate_int (G_TASK (res), error);
+ if (val < 0)
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+
+ return (MMBearerConnectionStatus) val;
}
static void
-set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
+reload_connection_status_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
{
- MMBearerMbim *self = MM_BEARER_MBIM (object);
+ MMBearerMbim *self;
+ guint32 session_id;
+ MbimActivationState activation_state;
+ MMBearerConnectionStatus bearer_connection_status = MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_connect_response_parse (
+ response,
+ &session_id,
+ &activation_state,
+ NULL, /* voice_call_state */
+ NULL, /* ip_type */
+ NULL, /* context_type */
+ NULL, /* nw_error */
+ &error)) {
+ g_prefix_error (&error, "Cannot load session ID '%u' status: ",
+ mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self)));
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "session ID '%u': %s", session_id, mbim_activation_state_get_string (activation_state));
- switch (prop_id) {
- case PROP_SESSION_ID:
- self->priv->session_id = g_value_get_uint (value);
+ switch (activation_state) {
+ case MBIM_ACTIVATION_STATE_ACTIVATED:
+ case MBIM_ACTIVATION_STATE_ACTIVATING:
+ /* for the purposes of the sync operation, it's fine to map ACTIVATING
+ * to CONNECTED, as we're really going to ignore that state in the actual
+ * processing of the logic. */
+ bearer_connection_status = MM_BEARER_CONNECTION_STATUS_CONNECTED;
break;
+ case MBIM_ACTIVATION_STATE_DEACTIVATING:
+ bearer_connection_status = MM_BEARER_CONNECTION_STATUS_DISCONNECTING;
+ break;
+ case MBIM_ACTIVATION_STATE_DEACTIVATED:
+ bearer_connection_status = MM_BEARER_CONNECTION_STATUS_DISCONNECTED;
+ break;
+ case MBIM_ACTIVATION_STATE_UNKNOWN:
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
+
+ if (bearer_connection_status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot load session ID '%u' status",
+ mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self)));
+ else
+ g_task_return_int (task, bearer_connection_status);
+ g_object_unref (task);
}
static void
-get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+reload_connection_status (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- MMBearerMbim *self = MM_BEARER_MBIM (object);
+ MMPortMbim *mbim;
+ GTask *task = NULL;
+ g_autoptr(MbimMessage) message = NULL;
- switch (prop_id) {
- case PROP_SESSION_ID:
- g_value_set_uint (value, self->priv->session_id);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ if (!peek_ports (self, &mbim, NULL, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ message = mbim_message_connect_query_new (mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (self)),
+ MBIM_ACTIVATION_STATE_UNKNOWN,
+ MBIM_VOICE_CALL_STATE_NONE,
+ MBIM_CONTEXT_IP_TYPE_DEFAULT,
+ mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_INTERNET),
+ 0,
+ NULL);
+ mbim_device_command (mm_port_mbim_peek_device (mbim),
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)reload_connection_status_ready,
+ task);
}
+#endif /* WITH_SYSTEMD_SUSPEND_RESUME */
+
+/*****************************************************************************/
+
+MMBaseBearer *
+mm_bearer_mbim_new (MMBroadbandModemMbim *modem,
+ MMBearerProperties *config)
+{
+ MMBaseBearer *bearer;
+
+ /* The Mbim bearer inherits from MMBaseBearer (so it's not a MMBroadbandBearer)
+ * and that means that the object is not async-initable, so we just use
+ * g_object_new() here */
+ bearer = g_object_new (MM_TYPE_BEARER_MBIM,
+ MM_BASE_BEARER_MODEM, modem,
+ MM_BASE_BEARER_CONFIG, config,
+ NULL);
+
+ /* Only export valid bearers */
+ mm_base_bearer_export (bearer);
+
+ return bearer;
+}
static void
mm_bearer_mbim_init (MMBearerMbim *self)
{
/* Initialize private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_BEARER_MBIM,
- MMBearerMbimPrivate);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_MBIM, MMBearerMbimPrivate);
}
static void
@@ -1199,7 +1711,7 @@ dispose (GObject *object)
{
MMBearerMbim *self = MM_BEARER_MBIM (object);
- g_clear_object (&self->priv->data);
+ reset_bearer_connection (self);
G_OBJECT_CLASS (mm_bearer_mbim_parent_class)->dispose (object);
}
@@ -1214,21 +1726,18 @@ mm_bearer_mbim_class_init (MMBearerMbimClass *klass)
/* Virtual methods */
object_class->dispose = dispose;
- object_class->get_property = get_property;
- object_class->set_property = set_property;
base_bearer_class->connect = _connect;
base_bearer_class->connect_finish = connect_finish;
base_bearer_class->disconnect = disconnect;
base_bearer_class->disconnect_finish = disconnect_finish;
-
- properties[PROP_SESSION_ID] =
- g_param_spec_uint (MM_BEARER_MBIM_SESSION_ID,
- "Session ID",
- "Session ID to use with this bearer",
- 0,
- 255,
- 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
- g_object_class_install_property (object_class, PROP_SESSION_ID, properties[PROP_SESSION_ID]);
+ base_bearer_class->report_connection_status = report_connection_status;
+ base_bearer_class->reload_stats = reload_stats;
+ base_bearer_class->reload_stats_finish = reload_stats_finish;
+ base_bearer_class->load_connection_status = NULL;
+ base_bearer_class->load_connection_status_finish = NULL;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = reload_connection_status;
+ base_bearer_class->reload_connection_status_finish = reload_connection_status_finish;
+#endif
}
diff --git a/src/mm-bearer-mbim.h b/src/mm-bearer-mbim.h
index 67dea09a..3da599a6 100644
--- a/src/mm-bearer-mbim.h
+++ b/src/mm-bearer-mbim.h
@@ -48,12 +48,12 @@ struct _MMBearerMbimClass {
};
GType mm_bearer_mbim_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerMbim, g_object_unref)
/* MBIM bearer creation implementation.
* NOTE it is *not* a broadband bearer, so not async-initable */
MMBaseBearer *mm_bearer_mbim_new (MMBroadbandModemMbim *modem,
- MMBearerProperties *config,
- guint32 session_id);
+ MMBearerProperties *config);
guint32 mm_bearer_mbim_get_session_id (MMBearerMbim *self);
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 95ab4f79..e1252ac2 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2015 Azimut Electronics
*/
#include <config.h>
@@ -27,48 +28,459 @@
#include <libmm-glib.h>
#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-bearer-qmi.h"
#include "mm-modem-helpers-qmi.h"
#include "mm-port-enums-types.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
+#include "mm-context.h"
G_DEFINE_TYPE (MMBearerQmi, mm_bearer_qmi, MM_TYPE_BASE_BEARER)
#define GLOBAL_PACKET_DATA_HANDLE 0xFFFFFFFF
-enum {
- PROP_0,
- PROP_FORCE_DHCP,
- PROP_LAST
-};
-
-static GParamSpec *properties[PROP_LAST];
-
struct _MMBearerQmiPrivate {
+ /* Cancellables available during a connection attempt */
+ GCancellable *ongoing_connect_user_cancellable;
+ GCancellable *ongoing_connect_network_cancellable;
+
/* State kept while connected */
+ MMPortQmi *qmi;
+ gboolean explicit_qmi_open;
+
QmiClientWds *client_ipv4;
+ guint packet_service_status_ipv4_indication_id;
+ guint event_report_ipv4_indication_id;
+
QmiClientWds *client_ipv6;
+ guint packet_service_status_ipv6_indication_id;
+ guint event_report_ipv6_indication_id;
+
MMPort *data;
+ MMPort *link;
+ guint mux_id;
guint32 packet_data_handle_ipv4;
guint32 packet_data_handle_ipv6;
- gboolean force_dhcp;
};
/*****************************************************************************/
+/* Stats */
+
+typedef enum {
+ RELOAD_STATS_CONTEXT_STEP_FIRST,
+ RELOAD_STATS_CONTEXT_STEP_IPV4,
+ RELOAD_STATS_CONTEXT_STEP_IPV6,
+ RELOAD_STATS_CONTEXT_STEP_LAST,
+} ReloadStatsContextStep;
+
+typedef struct {
+ guint64 rx_bytes;
+ guint64 tx_bytes;
+} ReloadStatsResult;
+
+typedef struct {
+ QmiMessageWdsGetPacketStatisticsInput *input;
+ ReloadStatsContextStep step;
+ ReloadStatsResult stats;
+} ReloadStatsContext;
+
+static gboolean
+reload_stats_finish (MMBaseBearer *bearer,
+ guint64 *rx_bytes,
+ guint64 *tx_bytes,
+ GAsyncResult *res,
+ GError **error)
+{
+ ReloadStatsResult *stats;
+
+ stats = g_task_propagate_pointer (G_TASK (res), error);
+ if (!stats)
+ return FALSE;
+
+ if (rx_bytes)
+ *rx_bytes = stats->rx_bytes;
+ if (tx_bytes)
+ *tx_bytes = stats->tx_bytes;
+
+ g_free (stats);
+ return TRUE;
+}
+
+static void
+reload_stats_context_free (ReloadStatsContext *ctx)
+{
+ qmi_message_wds_get_packet_statistics_input_unref (ctx->input);
+ g_slice_free (ReloadStatsContext, ctx);
+}
+
+static void reload_stats_context_step (GTask *task);
+
+static void
+get_packet_statistics_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ReloadStatsContext *ctx;
+ GError *error = NULL;
+ QmiMessageWdsGetPacketStatisticsOutput *output;
+ guint64 tx_bytes_ok = 0;
+ guint64 rx_bytes_ok = 0;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_wds_get_packet_statistics_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_wds_get_packet_statistics_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get packet statistics: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_wds_get_packet_statistics_output_unref (output);
+ return;
+ }
+
+ qmi_message_wds_get_packet_statistics_output_get_tx_bytes_ok (output, &tx_bytes_ok, NULL);
+ qmi_message_wds_get_packet_statistics_output_get_rx_bytes_ok (output, &rx_bytes_ok, NULL);
+ ctx->stats.rx_bytes += rx_bytes_ok;
+ ctx->stats.tx_bytes += tx_bytes_ok;
+
+ qmi_message_wds_get_packet_statistics_output_unref (output);
+
+ /* Go on */
+ ctx->step++;
+ reload_stats_context_step (task);
+}
+
+static void
+reload_stats_context_step (GTask *task)
+{
+ MMBearerQmi *self;
+ ReloadStatsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case RELOAD_STATS_CONTEXT_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+ case RELOAD_STATS_CONTEXT_STEP_IPV4:
+ if (self->priv->client_ipv4) {
+ qmi_client_wds_get_packet_statistics (QMI_CLIENT_WDS (self->priv->client_ipv4),
+ ctx->input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_packet_statistics_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+ case RELOAD_STATS_CONTEXT_STEP_IPV6:
+ if (self->priv->client_ipv6) {
+ qmi_client_wds_get_packet_statistics (QMI_CLIENT_WDS (self->priv->client_ipv6),
+ ctx->input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_packet_statistics_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+ case RELOAD_STATS_CONTEXT_STEP_LAST:
+ g_task_return_pointer (task,
+ g_memdup (&ctx->stats, sizeof (ctx->stats)),
+ g_free);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+reload_stats (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ReloadStatsContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (ReloadStatsContext);
+ ctx->input = qmi_message_wds_get_packet_statistics_input_new ();
+ qmi_message_wds_get_packet_statistics_input_set_mask (
+ ctx->input,
+ (QMI_WDS_PACKET_STATISTICS_MASK_FLAG_TX_BYTES_OK |
+ QMI_WDS_PACKET_STATISTICS_MASK_FLAG_RX_BYTES_OK),
+ NULL);
+ ctx->step = RELOAD_STATS_CONTEXT_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)reload_stats_context_free);
+
+ reload_stats_context_step (task);
+}
+
+/*****************************************************************************/
+/* Connection status check */
+
+typedef enum {
+ CONNECTION_STATUS_CONTEXT_STEP_FIRST,
+ CONNECTION_STATUS_CONTEXT_STEP_IPV4,
+ CONNECTION_STATUS_CONTEXT_STEP_IPV6,
+ CONNECTION_STATUS_CONTEXT_STEP_LAST,
+} ConnectionStatusContextStep;
+
+typedef struct {
+ ConnectionStatusContextStep step;
+} ConnectionStatusContext;
+
+static MMBearerConnectionStatus
+reload_connection_status_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gint val;
+
+ val = g_task_propagate_int (G_TASK (res), error);
+ if (val < 0)
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+
+ return (MMBearerConnectionStatus) val;
+}
+
+static void connection_status_context_step (GTask *task);
+
+static void
+get_packet_service_status_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ QmiMessageWdsGetPacketServiceStatusOutput *output;
+ QmiWdsConnectionStatus status = QMI_WDS_CONNECTION_STATUS_UNKNOWN;
+ ConnectionStatusContext *ctx;
+
+ output = qmi_client_wds_get_packet_service_status_finish (client, res, &error);
+ if (!output)
+ goto out;
+
+ if (!qmi_message_wds_get_packet_service_status_output_get_result (output, &error))
+ goto out;
+
+ qmi_message_wds_get_packet_service_status_output_get_connection_status (
+ output,
+ &status,
+ NULL);
+
+ out:
+ if (output)
+ qmi_message_wds_get_packet_service_status_output_unref (output);
+
+ /* An error checking status is reported right away */
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Report disconnection right away */
+ if (status != QMI_WDS_CONNECTION_STATUS_CONNECTED) {
+ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ g_object_unref (task);
+ return;
+ }
+
+ /* we're reported as connected, go on to next check if any */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ connection_status_context_step (task);
+}
+
+static void
+connection_status_context_step (GTask *task)
+{
+ MMBearerQmi *self;
+ ConnectionStatusContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case CONNECTION_STATUS_CONTEXT_STEP_FIRST:
+ /* If no clients ready on start, assume disconnected */
+ if (!self->priv->client_ipv4 && !self->priv->client_ipv6) {
+ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECTION_STATUS_CONTEXT_STEP_IPV4:
+ if (self->priv->client_ipv4) {
+ qmi_client_wds_get_packet_service_status (self->priv->client_ipv4,
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_packet_service_status_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECTION_STATUS_CONTEXT_STEP_IPV6:
+ if (self->priv->client_ipv6) {
+ qmi_client_wds_get_packet_service_status (self->priv->client_ipv6,
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_packet_service_status_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECTION_STATUS_CONTEXT_STEP_LAST:
+ /* All available clients are connected */
+ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+reload_connection_status (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ ConnectionStatusContext *ctx;
+
+ ctx = g_new (ConnectionStatusContext, 1);
+ ctx->step = CONNECTION_STATUS_CONTEXT_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ connection_status_context_step (task);
+}
+
+/*****************************************************************************/
+/* Connection status polling */
+
+static MMBearerConnectionStatus
+load_connection_status_finish (MMBaseBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gint val;
+
+ val = g_task_propagate_int (G_TASK (res), error);
+ if (val < 0)
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+
+ return (MMBearerConnectionStatus) val;
+}
+
+static void
+reload_connection_status_ready (MMBaseBearer *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerConnectionStatus status;
+ GError *error = NULL;
+
+ status = reload_connection_status_finish (self, res, &error);
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, MM_BEARER_CONNECTION_STATUS_CONNECTED);
+ g_object_unref (task);
+}
+
+static void
+load_connection_status (MMBaseBearer *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBearerQmi *self = MM_BEARER_QMI (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Connection status polling is an optional feature that must be
+ * enabled explicitly via udev tags. If not set, out as unsupported.
+ * Note that when connected via a muxed link, the udev tag should be
+ * checked on the master interface (lower device) */
+ if ((self->priv->data &&
+ !mm_kernel_device_get_global_property_as_boolean (mm_port_peek_kernel_device (self->priv->data),
+ "ID_MM_QMI_CONNECTION_STATUS_POLLING_ENABLE")) ||
+ (self->priv->link &&
+ !mm_kernel_device_get_global_property_as_boolean (mm_kernel_device_peek_lower_device (mm_port_peek_kernel_device (self->priv->link)),
+ "ID_MM_QMI_CONNECTION_STATUS_POLLING_ENABLE"))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Connection status polling not required");
+ g_object_unref (task);
+ return;
+ }
+
+ reload_connection_status (_self, (GAsyncReadyCallback)reload_connection_status_ready, task);
+}
+
+/*****************************************************************************/
/* Connect */
+#define WAIT_LINK_PORT_TIMEOUT_MS 2500
+
+static void common_setup_cleanup_packet_service_status_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ gboolean enable,
+ guint *indication_id);
+
+static void setup_event_report_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+static void cleanup_event_report_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ guint *indication_id);
+
typedef enum {
CONNECT_STEP_FIRST,
+ CONNECT_STEP_LOAD_PROFILE_SETTINGS,
CONNECT_STEP_OPEN_QMI_PORT,
+ CONNECT_STEP_SETUP_DATA_FORMAT,
+ CONNECT_STEP_SETUP_LINK,
+ CONNECT_STEP_SETUP_LINK_MASTER_UP,
+ CONNECT_STEP_IP_METHOD,
CONNECT_STEP_IPV4,
CONNECT_STEP_WDS_CLIENT_IPV4,
+ CONNECT_STEP_BIND_DATA_PORT_IPV4,
CONNECT_STEP_IP_FAMILY_IPV4,
+ CONNECT_STEP_ENABLE_INDICATIONS_IPV4,
CONNECT_STEP_START_NETWORK_IPV4,
CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4,
CONNECT_STEP_IPV6,
CONNECT_STEP_WDS_CLIENT_IPV6,
+ CONNECT_STEP_BIND_DATA_PORT_IPV6,
CONNECT_STEP_IP_FAMILY_IPV6,
+ CONNECT_STEP_ENABLE_INDICATIONS_IPV6,
CONNECT_STEP_START_NETWORK_IPV6,
CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6,
CONNECT_STEP_LAST
@@ -76,51 +488,117 @@ typedef enum {
typedef struct {
MMBearerQmi *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- ConnectStep step;
- MMPort *data;
- MMPortQmi *qmi;
- gchar *user;
- gchar *password;
- gchar *apn;
- QmiWdsAuthentication auth;
- gboolean no_ip_family_preference;
- gboolean default_ip_family_set;
-
- gboolean ipv4;
- gboolean running_ipv4;
- QmiClientWds *client_ipv4;
- guint32 packet_data_handle_ipv4;
+ MMBaseModem *modem;
+ ConnectStep step;
+ MMPort *data;
+ MMPortQmi *qmi;
+ QmiSioPort sio_port;
+
+ gint profile_id;
+ MMBearerIpMethod ip_method;
+ gboolean explicit_qmi_open;
+ gchar *user;
+ gchar *password;
+ gchar *apn;
+ QmiWdsAuthentication auth;
+ gboolean no_ip_family_preference;
+
+ MMBearerMultiplexSupport multiplex;
+ QmiWdaDataAggregationProtocol dap;
+ guint mux_id;
+ gchar *link_prefix_hint;
+ gchar *link_name;
+ MMPort *link;
+
+ gboolean ipv4;
+ gboolean running_ipv4;
+ QmiClientWds *client_ipv4;
+ guint packet_service_status_ipv4_indication_id;
+ guint event_report_ipv4_indication_id;
+ guint32 packet_data_handle_ipv4;
MMBearerIpConfig *ipv4_config;
- GError *error_ipv4;
-
- gboolean ipv6;
- gboolean running_ipv6;
- QmiClientWds *client_ipv6;
- guint32 packet_data_handle_ipv6;
+ GError *error_ipv4;
+
+ gboolean ipv6;
+ gboolean running_ipv6;
+ QmiClientWds *client_ipv6;
+ guint packet_service_status_ipv6_indication_id;
+ guint event_report_ipv6_indication_id;
+ guint32 packet_data_handle_ipv6;
MMBearerIpConfig *ipv6_config;
- GError *error_ipv6;
+ GError *error_ipv6;
} ConnectContext;
static void
-connect_context_complete_and_free (ConnectContext *ctx)
+connect_context_free (ConnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_free (ctx->apn);
g_free (ctx->user);
g_free (ctx->password);
+
+ if (ctx->client_ipv4) {
+ if (ctx->packet_service_status_ipv4_indication_id) {
+ common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ FALSE,
+ &ctx->packet_service_status_ipv4_indication_id);
+ }
+ if (ctx->event_report_ipv4_indication_id) {
+ cleanup_event_report_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ &ctx->event_report_ipv4_indication_id);
+ }
+ if (ctx->packet_data_handle_ipv4) {
+ g_autoptr(QmiMessageWdsStopNetworkInput) input = NULL;
+
+ input = qmi_message_wds_stop_network_input_new ();
+ qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv4, NULL);
+ qmi_client_wds_stop_network (ctx->client_ipv4, input, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, NULL, NULL);
+ }
+ g_clear_object (&ctx->client_ipv4);
+ }
+
+ if (ctx->client_ipv6) {
+ if (ctx->packet_service_status_ipv6_indication_id) {
+ common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ FALSE,
+ &ctx->packet_service_status_ipv6_indication_id);
+ }
+ if (ctx->event_report_ipv6_indication_id) {
+ cleanup_event_report_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ &ctx->event_report_ipv6_indication_id);
+ }
+ if (ctx->packet_data_handle_ipv6) {
+ g_autoptr(QmiMessageWdsStopNetworkInput) input = NULL;
+
+ input = qmi_message_wds_stop_network_input_new ();
+ qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv6, NULL);
+ qmi_client_wds_stop_network (ctx->client_ipv6, input, MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT, NULL, NULL, NULL);
+ }
+ g_clear_object (&ctx->client_ipv6);
+ }
+
+ if (ctx->link_name) {
+ mm_port_qmi_cleanup_link (ctx->qmi, ctx->link_name, ctx->mux_id, NULL, NULL);
+ g_free (ctx->link_name);
+ }
+ g_clear_object (&ctx->link);
+ g_free (ctx->link_prefix_hint);
+
+ if (ctx->explicit_qmi_open)
+ mm_port_qmi_close (ctx->qmi, NULL, NULL);
+
g_clear_error (&ctx->error_ipv4);
g_clear_error (&ctx->error_ipv6);
- g_clear_object (&ctx->client_ipv4);
- g_clear_object (&ctx->client_ipv6);
g_clear_object (&ctx->ipv4_config);
g_clear_object (&ctx->ipv6_config);
- g_object_unref (ctx->data);
- g_object_unref (ctx->qmi);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->self);
+
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->qmi);
+ g_clear_object (&ctx->modem);
+ g_clear_object (&ctx->self);
g_slice_free (ConnectContext, ctx);
}
@@ -129,132 +607,31 @@ connect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void connect_context_step (ConnectContext *ctx);
-
static void
-start_network_ready (QmiClientWds *client,
- GAsyncResult *res,
- ConnectContext *ctx)
+complete_connect (GTask *task,
+ MMBearerConnectResult *result,
+ GError *error)
{
- GError *error = NULL;
- QmiMessageWdsStartNetworkOutput *output;
-
- g_assert (ctx->running_ipv4 || ctx->running_ipv6);
- g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
-
- output = qmi_client_wds_start_network_finish (client, res, &error);
- if (output &&
- !qmi_message_wds_start_network_output_get_result (output, &error)) {
- /* No-effect errors should be ignored. The modem will keep the
- * connection active as long as there is a WDS client which requested
- * to start the network. If ModemManager crashed while a connection was
- * active, we would be leaving an unreleased WDS client around and the
- * modem would just keep connected. */
- if (g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- g_error_free (error);
- error = NULL;
- if (ctx->running_ipv4)
- ctx->packet_data_handle_ipv4 = GLOBAL_PACKET_DATA_HANDLE;
- else
- ctx->packet_data_handle_ipv6 = GLOBAL_PACKET_DATA_HANDLE;
-
- /* Fall down to a successful connection */
- } else {
- mm_info ("error: couldn't start network: %s", error->message);
- if (g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_CALL_FAILED)) {
- QmiWdsCallEndReason cer;
- QmiWdsVerboseCallEndReasonType verbose_cer_type;
- gint16 verbose_cer_reason;
-
- if (qmi_message_wds_start_network_output_get_call_end_reason (
- output,
- &cer,
- NULL))
- mm_info ("call end reason (%u): '%s'",
- cer,
- qmi_wds_call_end_reason_get_string (cer));
+ ConnectContext *ctx;
- if (qmi_message_wds_start_network_output_get_verbose_call_end_reason (
- output,
- &verbose_cer_type,
- &verbose_cer_reason,
- NULL))
- mm_info ("verbose call end reason (%u,%d): [%s] %s",
- verbose_cer_type,
- verbose_cer_reason,
- qmi_wds_verbose_call_end_reason_type_get_string (verbose_cer_type),
- qmi_wds_verbose_call_end_reason_get_string (verbose_cer_type, verbose_cer_reason));
- }
- }
- }
+ g_assert (result || error);
+ g_assert (!(result && error));
- if (error) {
- if (ctx->running_ipv4)
- ctx->error_ipv4 = error;
- else
- ctx->error_ipv6 = error;
- } else {
- if (ctx->running_ipv4)
- qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle_ipv4, NULL);
- else
- qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle_ipv6, NULL);
- }
+ ctx = g_task_get_task_data (task);
+ g_clear_object (&ctx->self->priv->ongoing_connect_user_cancellable);
+ g_clear_object (&ctx->self->priv->ongoing_connect_network_cancellable);
- if (output)
- qmi_message_wds_start_network_output_unref (output);
-
- /* Keep on */
- ctx->step++;
- connect_context_step (ctx);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, result, (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
}
-static QmiMessageWdsStartNetworkInput *
-build_start_network_input (ConnectContext *ctx)
-{
- QmiMessageWdsStartNetworkInput *input;
-
- g_assert (ctx->running_ipv4 || ctx->running_ipv6);
- g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
-
- input = qmi_message_wds_start_network_input_new ();
-
- if (ctx->apn && ctx->apn[0])
- qmi_message_wds_start_network_input_set_apn (input, ctx->apn, NULL);
-
- if (ctx->auth != QMI_WDS_AUTHENTICATION_NONE) {
- qmi_message_wds_start_network_input_set_authentication_preference (input, ctx->auth, NULL);
-
- if (ctx->user)
- qmi_message_wds_start_network_input_set_username (input, ctx->user, NULL);
- if (ctx->password)
- qmi_message_wds_start_network_input_set_password (input, ctx->password, NULL);
- }
-
- /* Only add the IP family preference TLV if explicitly requested a given
- * family. This TLV may be newer than the Start Network command itself, so
- * we'll just allow the case where none is specified. Also, don't add this
- * TLV if we already set a default IP family preference with "WDS Set IP
- * Family" */
- if (!ctx->no_ip_family_preference &&
- !ctx->default_ip_family_set) {
- qmi_message_wds_start_network_input_set_ip_family_preference (
- input,
- (ctx->running_ipv6 ? QMI_WDS_IP_FAMILY_IPV6 : QMI_WDS_IP_FAMILY_IPV4),
- NULL);
- }
-
- return input;
-}
+static void connect_context_step (GTask *task);
static void
qmi_inet4_ntop (guint32 address, char *buf, const gsize buflen)
@@ -271,6 +648,7 @@ qmi_inet4_ntop (guint32 address, char *buf, const gsize buflen)
static MMBearerIpConfig *
get_ipv4_config (MMBearerQmi *self,
+ MMBearerIpMethod ip_method,
QmiMessageWdsGetCurrentSettingsOutput *output,
guint32 mtu)
{
@@ -285,7 +663,7 @@ get_ipv4_config (MMBearerQmi *self,
/* IPv4 subnet mask */
if (!qmi_message_wds_get_current_settings_output_get_ipv4_gateway_subnet_mask (output, &addr, &error)) {
- mm_dbg ("Failed to read IPv4 netmask (%s)", error->message);
+ mm_obj_warn (self, "failed to read IPv4 netmask: %s", error->message);
g_clear_error (&error);
return NULL;
}
@@ -294,62 +672,58 @@ get_ipv4_config (MMBearerQmi *self,
/* IPv4 address */
if (!qmi_message_wds_get_current_settings_output_get_ipv4_address (output, &addr, &error)) {
- mm_dbg ("IPv4 family but no IPv4 address (%s)", error->message);
+ mm_obj_warn (self, "IPv4 family but no IPv4 address: %s", error->message);
g_clear_error (&error);
return NULL;
}
- mm_dbg ("QMI IPv4 Settings:");
+ mm_obj_info (self, "QMI IPv4 Settings:");
config = mm_bearer_ip_config_new ();
- if (self->priv->force_dhcp)
- mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
- else {
- mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+ mm_bearer_ip_config_set_method (config, ip_method);
- /* IPv4 address */
- qmi_inet4_ntop (addr, buf, sizeof (buf));
- mm_bearer_ip_config_set_address (config, buf);
- mm_bearer_ip_config_set_prefix (config, prefix);
- mm_dbg (" Address: %s/%d", buf, prefix);
-
- /* IPv4 gateway address */
- if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error)) {
- qmi_inet4_ntop (addr, buf, sizeof (buf));
- mm_bearer_ip_config_set_gateway (config, buf);
- mm_dbg (" Gateway: %s", buf);
- } else {
- mm_dbg (" Gateway: failed (%s)", error->message);
- g_clear_error (&error);
- }
+ /* IPv4 address */
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ mm_bearer_ip_config_set_address (config, buf);
+ mm_bearer_ip_config_set_prefix (config, prefix);
+ mm_obj_info (self, " address: %s/%d", buf, prefix);
- /* IPv4 DNS #1 */
- if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error)) {
- qmi_inet4_ntop (addr, buf, sizeof (buf));
- dns[dns_idx++] = buf;
- mm_dbg (" DNS #1: %s", buf);
- } else {
- mm_dbg (" DNS #1: failed (%s)", error->message);
- g_clear_error (&error);
- }
+ /* IPv4 gateway address */
+ if (qmi_message_wds_get_current_settings_output_get_ipv4_gateway_address (output, &addr, &error)) {
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ mm_bearer_ip_config_set_gateway (config, buf);
+ mm_obj_info (self, " gateway: %s", buf);
+ } else {
+ mm_obj_info (self, " gateway: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
- /* IPv6 DNS #2 */
- if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error)) {
- qmi_inet4_ntop (addr, buf2, sizeof (buf2));
- dns[dns_idx++] = buf2;
- mm_dbg (" DNS #2: %s", buf2);
- } else {
- mm_dbg (" DNS #2: failed (%s)", error->message);
- g_clear_error (&error);
- }
+ /* IPv4 DNS #1 */
+ if (qmi_message_wds_get_current_settings_output_get_primary_ipv4_dns_address (output, &addr, &error)) {
+ qmi_inet4_ntop (addr, buf, sizeof (buf));
+ dns[dns_idx++] = buf;
+ mm_obj_info (self, " DNS #1: %s", buf);
+ } else {
+ mm_obj_info (self, " DNS #1: failed (%s)", error->message);
+ g_clear_error (&error);
+ }
- if (dns_idx > 0)
- mm_bearer_ip_config_set_dns (config, (const gchar **) &dns);
+ /* IPv4 DNS #2 */
+ if (qmi_message_wds_get_current_settings_output_get_secondary_ipv4_dns_address (output, &addr, &error)) {
+ qmi_inet4_ntop (addr, buf2, sizeof (buf2));
+ dns[dns_idx++] = buf2;
+ mm_obj_info (self, " DNS #2: %s", buf2);
+ } else {
+ mm_obj_info (self, " DNS #2: failed (%s)", error->message);
+ g_clear_error (&error);
}
+ if (dns_idx > 0)
+ mm_bearer_ip_config_set_dns (config, (const gchar **) &dns);
+
if (mtu) {
mm_bearer_ip_config_set_mtu (config, mtu);
- mm_dbg (" MTU: %d", mtu);
+ mm_obj_info (self, " MTU: %d", mtu);
}
return config;
@@ -376,6 +750,7 @@ qmi_inet6_ntop (GArray *array, char *buf, const gsize buflen)
static MMBearerIpConfig *
get_ipv6_config (MMBearerQmi *self,
+ MMBearerIpMethod ip_method,
QmiMessageWdsGetCurrentSettingsOutput *output,
guint32 mtu)
{
@@ -390,36 +765,30 @@ get_ipv6_config (MMBearerQmi *self,
/* If the message has an IPv6 address, create an IPv6 bearer config */
if (!qmi_message_wds_get_current_settings_output_get_ipv6_address (output, &array, &prefix, &error)) {
- mm_dbg ("IPv6 family but no IPv6 address (%s)", error->message);
+ mm_obj_warn (self, "IPv6 family but no IPv6 address: %s", error->message);
g_clear_error (&error);
return NULL;
}
- mm_dbg ("QMI IPv6 Settings:");
+ mm_obj_info (self, "QMI IPv6 Settings:");
config = mm_bearer_ip_config_new ();
- if (self->priv->force_dhcp)
- /* Force DHCP, but still set the static IPv6 config details if we get them */
- mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_DHCP);
- else
- mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_STATIC);
+ mm_bearer_ip_config_set_method (config, ip_method);
/* IPv6 address */
qmi_inet6_ntop (array, buf, sizeof (buf));
- g_array_unref (array);
mm_bearer_ip_config_set_address (config, buf);
mm_bearer_ip_config_set_prefix (config, prefix);
- mm_dbg (" Address: %s/%d", buf, prefix);
+ mm_obj_info (self, " address: %s/%d", buf, prefix);
/* IPv6 gateway address */
if (qmi_message_wds_get_current_settings_output_get_ipv6_gateway_address (output, &array, &prefix, &error)) {
qmi_inet6_ntop (array, buf, sizeof (buf));
mm_bearer_ip_config_set_gateway (config, buf);
- mm_dbg (" Gateway: %s", buf);
- g_array_unref (array);
+ mm_obj_info (self, " gateway: %s/%d", buf, prefix);
} else {
- mm_dbg (" Gateway: failed (%s)", error->message);
+ mm_obj_info (self, " gateway: failed (%s)", error->message);
g_clear_error (&error);
}
@@ -427,10 +796,9 @@ get_ipv6_config (MMBearerQmi *self,
if (qmi_message_wds_get_current_settings_output_get_ipv6_primary_dns_address (output, &array, &error)) {
qmi_inet6_ntop (array, buf, sizeof (buf));
dns[dns_idx++] = buf;
- mm_dbg (" DNS #1: %s", buf);
- g_array_unref (array);
+ mm_obj_info (self, " DNS #1: %s", buf);
} else {
- mm_dbg (" DNS #1: failed (%s)", error->message);
+ mm_obj_info (self, " DNS #1: failed (%s)", error->message);
g_clear_error (&error);
}
@@ -438,10 +806,9 @@ get_ipv6_config (MMBearerQmi *self,
if (qmi_message_wds_get_current_settings_output_get_ipv6_secondary_dns_address (output, &array, &error)) {
qmi_inet6_ntop (array, buf2, sizeof (buf2));
dns[dns_idx++] = buf2;
- mm_dbg (" DNS #2: %s", buf2);
- g_array_unref (array);
+ mm_obj_info (self, " DNS #2: %s", buf2);
} else {
- mm_dbg (" DNS #2: failed (%s)", error->message);
+ mm_obj_info (self, " DNS #2: failed (%s)", error->message);
g_clear_error (&error);
}
@@ -450,7 +817,7 @@ get_ipv6_config (MMBearerQmi *self,
if (mtu) {
mm_bearer_ip_config_set_mtu (config, mtu);
- mm_dbg (" MTU: %d", mtu);
+ mm_obj_info (self, " MTU: %d", mtu);
}
return config;
@@ -459,43 +826,66 @@ get_ipv6_config (MMBearerQmi *self,
static void
get_current_settings_ready (QmiClientWds *client,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ MMBearerQmi *self;
+ ConnectContext *ctx;
GError *error = NULL;
QmiMessageWdsGetCurrentSettingsOutput *output;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
g_assert (ctx->running_ipv4 || ctx->running_ipv6);
output = qmi_client_wds_get_current_settings_finish (client, res, &error);
- if (!output ||
- !qmi_message_wds_get_current_settings_output_get_result (output, &error)) {
- /* Never treat this as a hard connection error; not all devices support
- * "WDS Get Current Settings" */
- mm_info ("error: couldn't get current settings: %s", error->message);
+ if (!output || !qmi_message_wds_get_current_settings_output_get_result (output, &error)) {
+ MMBearerIpConfig *config;
+
+ /* When we're using static IP address, the current settings are mandatory */
+ if (ctx->ip_method == MM_BEARER_IP_METHOD_STATIC) {
+ mm_obj_warn (self, "failed to retrieve mandatory IP settings: %s", error->message);
+ if (output)
+ qmi_message_wds_get_current_settings_output_unref (output);
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* Otherwise, just go on as we're asking for DHCP */
+ mm_obj_dbg (self, "couldn't get current settings: %s", error->message);
g_error_free (error);
+
+ config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (config, ctx->ip_method);
+
+ if (ctx->running_ipv4)
+ ctx->ipv4_config = config;
+ else if (ctx->running_ipv6)
+ ctx->ipv6_config = config;
+ else
+ g_assert_not_reached ();
} else {
QmiWdsIpFamily ip_family = QMI_WDS_IP_FAMILY_UNSPECIFIED;
guint32 mtu = 0;
GArray *array;
if (!qmi_message_wds_get_current_settings_output_get_ip_family (output, &ip_family, &error)) {
- mm_dbg (" IP Family: failed (%s); assuming IPv4", error->message);
+ mm_obj_dbg (self, " IP Family: failed (%s); assuming IPv4", error->message);
g_clear_error (&error);
ip_family = QMI_WDS_IP_FAMILY_IPV4;
}
- mm_dbg (" IP Family: %s",
+ mm_obj_dbg (self, " IP Family: %s",
(ip_family == QMI_WDS_IP_FAMILY_IPV4) ? "IPv4" :
(ip_family == QMI_WDS_IP_FAMILY_IPV6) ? "IPv6" : "unknown");
if (!qmi_message_wds_get_current_settings_output_get_mtu (output, &mtu, &error)) {
- mm_dbg (" MTU: failed (%s)", error->message);
+ mm_obj_dbg (self, " MTU: failed (%s)", error->message);
g_clear_error (&error);
}
if (ip_family == QMI_WDS_IP_FAMILY_IPV4)
- ctx->ipv4_config = get_ipv4_config (ctx->self, output, mtu);
+ ctx->ipv4_config = get_ipv4_config (ctx->self, ctx->ip_method, output, mtu);
else if (ip_family == QMI_WDS_IP_FAMILY_IPV6)
- ctx->ipv6_config = get_ipv6_config (ctx->self, output, mtu);
+ ctx->ipv6_config = get_ipv6_config (ctx->self, ctx->ip_method, output, mtu);
/* Domain names */
if (qmi_message_wds_get_current_settings_output_get_domain_name_list (output, &array, &error)) {
@@ -507,10 +897,10 @@ get_current_settings_ready (QmiClientWds *client,
g_string_append (s, ", ");
g_string_append (s, g_array_index (array, const char *, i));
}
- mm_dbg (" Domains: %s", s->str);
+ mm_obj_dbg (self, " domains: %s", s->str);
g_string_free (s, TRUE);
} else {
- mm_dbg (" Domains: failed (%s)", error ? error->message : "unknown");
+ mm_obj_dbg (self, " domains: failed (%s)", error ? error->message : "unknown");
g_clear_error (&error);
}
}
@@ -520,15 +910,17 @@ get_current_settings_ready (QmiClientWds *client,
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
}
static void
-get_current_settings (ConnectContext *ctx, QmiClientWds *client)
+get_current_settings (GTask *task, QmiClientWds *client)
{
+ ConnectContext *ctx;
QmiMessageWdsGetCurrentSettingsInput *input;
QmiWdsGetCurrentSettingsRequestedSettings requested;
+ ctx = g_task_get_task_data (task);
g_assert (ctx->running_ipv4 || ctx->running_ipv6);
requested = QMI_WDS_GET_CURRENT_SETTINGS_REQUESTED_SETTINGS_DNS_ADDRESS |
@@ -544,20 +936,398 @@ get_current_settings (ConnectContext *ctx, QmiClientWds *client)
qmi_client_wds_get_current_settings (client,
input,
10,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)get_current_settings_ready,
- ctx);
+ task);
qmi_message_wds_get_current_settings_input_unref (input);
}
+static GError *
+mobile_equipment_error_from_start_network_output (MMBearerQmi *self,
+ QmiMessageWdsStartNetworkOutput *output)
+{
+ QmiWdsCallEndReason cer;
+ QmiWdsVerboseCallEndReasonType verbose_cer_type;
+ gint16 verbose_cer_reason;
+
+ if (qmi_message_wds_start_network_output_get_verbose_call_end_reason (
+ output,
+ &verbose_cer_type,
+ &verbose_cer_reason,
+ NULL)) {
+ const gchar *verbose_cer_type_str;
+ const gchar *verbose_cer_reason_str;
+
+ verbose_cer_type_str = qmi_wds_verbose_call_end_reason_type_get_string (verbose_cer_type);
+ verbose_cer_reason_str = qmi_wds_verbose_call_end_reason_get_string (verbose_cer_type, verbose_cer_reason);
+ mm_obj_info (self, "verbose call end reason (%u,%d): [%s] %s",
+ verbose_cer_type,
+ verbose_cer_reason,
+ verbose_cer_type_str,
+ verbose_cer_reason_str);
+
+ /* If we have a 3GPP verbose call end reason, we try to build an error
+ * with the exact error code and message */
+ if (verbose_cer_type == QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_3GPP)
+ return qmi_mobile_equipment_error_from_verbose_call_end_reason_3gpp ((QmiWdsVerboseCallEndReason3gpp)verbose_cer_reason, self);
+
+ return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Call failed: %s error: %s", verbose_cer_type_str, verbose_cer_reason_str);
+ }
+
+ if (qmi_message_wds_start_network_output_get_call_end_reason (
+ output,
+ &cer,
+ NULL)) {
+ const gchar *cer_str;
+
+ cer_str = qmi_wds_call_end_reason_get_string (cer);
+ mm_obj_info (self, "call end reason (%u): %s", cer, cer_str);
+
+ return g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Call failed: %s", cer_str);
+ }
+
+ return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed");
+}
+
+static void
+start_network_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerQmi *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ QmiMessageWdsStartNetworkOutput *output;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->running_ipv4 || ctx->running_ipv6);
+ g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
+
+ output = qmi_client_wds_start_network_finish (client, res, &error);
+ if (output && !qmi_message_wds_start_network_output_get_result (output, &error)) {
+ /* No-effect errors should be ignored. The modem will keep the
+ * connection active as long as there is a WDS client which requested
+ * to start the network. If ModemManager crashed while a connection was
+ * active, we would be leaving an unreleased WDS client around and the
+ * modem would just keep connected. */
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_clear_error (&error);
+ if (ctx->running_ipv4)
+ ctx->packet_data_handle_ipv4 = GLOBAL_PACKET_DATA_HANDLE;
+ else
+ ctx->packet_data_handle_ipv6 = GLOBAL_PACKET_DATA_HANDLE;
+ /* Fall down to a successful connection */
+ } else {
+ mm_obj_info (self, "couldn't start network: %s", error->message);
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_CALL_FAILED)) {
+ g_clear_error (&error);
+ error = mobile_equipment_error_from_start_network_output (self, output);
+ }
+ }
+ }
+
+ if (error) {
+ if (ctx->running_ipv4)
+ ctx->error_ipv4 = error;
+ else
+ ctx->error_ipv6 = error;
+ } else {
+ if (ctx->running_ipv4)
+ qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle_ipv4, NULL);
+ else
+ qmi_message_wds_start_network_output_get_packet_data_handle (output, &ctx->packet_data_handle_ipv6, NULL);
+ }
+
+ if (output)
+ qmi_message_wds_start_network_output_unref (output);
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static QmiMessageWdsStartNetworkInput *
+build_start_network_input (ConnectContext *ctx)
+{
+ QmiMessageWdsStartNetworkInput *input;
+
+ g_assert (ctx->running_ipv4 || ctx->running_ipv6);
+ g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
+
+ input = qmi_message_wds_start_network_input_new ();
+
+ /* When requesting to connect through a profile, add the profile-id setting */
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_assert (ctx->profile_id <= (gint)G_MAXUINT8);
+ qmi_message_wds_start_network_input_set_profile_index_3gpp (input, (guint8)ctx->profile_id, NULL);
+ } else {
+ /* If user gives empty string as APN, we also skip setting it in the
+ * request. */
+ if (ctx->apn && ctx->apn[0])
+ qmi_message_wds_start_network_input_set_apn (input, ctx->apn, NULL);
+
+ /* Auth info */
+ qmi_message_wds_start_network_input_set_authentication_preference (input, ctx->auth, NULL);
+ if (ctx->auth != QMI_WDS_AUTHENTICATION_NONE) {
+ if (ctx->user)
+ qmi_message_wds_start_network_input_set_username (input, ctx->user, NULL);
+ if (ctx->user)
+ qmi_message_wds_start_network_input_set_password (input, ctx->password, NULL);
+ }
+ }
+
+ /* Only add the IP family preference TLV if explicitly requested a given
+ * family. This TLV may be newer than the Start Network command itself, so
+ * we'll just allow the case where none is specified. */
+ if (!ctx->no_ip_family_preference) {
+ qmi_message_wds_start_network_input_set_ip_family_preference (
+ input,
+ (ctx->running_ipv6 ? QMI_WDS_IP_FAMILY_IPV6 : QMI_WDS_IP_FAMILY_IPV4),
+ NULL);
+ }
+
+ return input;
+}
+
+static void
+packet_service_status_indication_cb (QmiClientWds *client,
+ QmiIndicationWdsPacketServiceStatusOutput *output,
+ MMBearerQmi *self)
+{
+ QmiWdsConnectionStatus connection_status;
+ MMBearerStatus bearer_status;
+
+ if (!qmi_indication_wds_packet_service_status_output_get_connection_status (
+ output,
+ &connection_status,
+ NULL,
+ NULL))
+ return;
+
+ bearer_status = mm_base_bearer_get_status (MM_BASE_BEARER (self));
+ if (connection_status == QMI_WDS_CONNECTION_STATUS_DISCONNECTED &&
+ bearer_status != MM_BEARER_STATUS_DISCONNECTED &&
+ bearer_status != MM_BEARER_STATUS_DISCONNECTING) {
+ QmiWdsCallEndReason cer;
+ QmiWdsVerboseCallEndReasonType verbose_cer_type;
+ gint16 verbose_cer_reason;
+ g_autoptr(GError) connection_error = NULL;
+
+ if (qmi_indication_wds_packet_service_status_output_get_verbose_call_end_reason (
+ output,
+ &verbose_cer_type,
+ &verbose_cer_reason,
+ NULL)) {
+ const gchar *verbose_cer_type_str;
+ const gchar *verbose_cer_reason_str;
+
+ verbose_cer_type_str = qmi_wds_verbose_call_end_reason_type_get_string (verbose_cer_type);
+ verbose_cer_reason_str = qmi_wds_verbose_call_end_reason_get_string (verbose_cer_type, verbose_cer_reason);
+ mm_obj_info (self, "verbose call end reason (%u,%d): [%s] %s",
+ verbose_cer_type,
+ verbose_cer_reason,
+ verbose_cer_type_str,
+ verbose_cer_reason_str);
+
+ /* If we have a 3GPP verbose call end reason, we try to build an error
+ * with the exact error code and message */
+ if (verbose_cer_type == QMI_WDS_VERBOSE_CALL_END_REASON_TYPE_3GPP)
+ connection_error = qmi_mobile_equipment_error_from_verbose_call_end_reason_3gpp ((QmiWdsVerboseCallEndReason3gpp)verbose_cer_reason, self);
+ else
+ connection_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Call failed: %s error: %s", verbose_cer_type_str, verbose_cer_reason_str);
+ } else if (qmi_indication_wds_packet_service_status_output_get_call_end_reason (
+ output,
+ &cer,
+ NULL)) {
+ const gchar *cer_str;
+
+ cer_str = qmi_wds_call_end_reason_get_string (cer);
+ mm_obj_info (self, "call end reason (%u): %s", cer, cer_str);
+
+ connection_error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Call failed: %s", cer_str);
+ } else
+ connection_error = g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "Call failed");
+
+ mm_base_bearer_report_connection_status_detailed (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_DISCONNECTED, connection_error);
+ }
+}
+
+static void
+common_setup_cleanup_packet_service_status_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ gboolean enable,
+ guint *indication_id)
+{
+ if (!client)
+ return;
+
+ /* Connect/Disconnect "Packet Service Status" indications */
+ if (enable) {
+ g_assert (*indication_id == 0);
+ *indication_id =
+ g_signal_connect (client,
+ "packet-service-status",
+ G_CALLBACK (packet_service_status_indication_cb),
+ self);
+ } else if (*indication_id != 0) {
+ g_signal_handler_disconnect (client, *indication_id);
+ *indication_id = 0;
+ }
+}
+
+static void
+event_report_indication_cb (QmiClientWds *client,
+ QmiIndicationWdsEventReportOutput *output,
+ MMBearerQmi *self)
+{
+ mm_obj_dbg (self, "got QMI WDS event report");
+}
+
+static guint
+connect_enable_indications_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ MMBearerQmi *self,
+ GError **error)
+{
+ QmiMessageWdsSetEventReportOutput *output;
+
+ /* Don't care about the result */
+ output = qmi_client_wds_set_event_report_finish (client, res, error);
+ if (!output || !qmi_message_wds_set_event_report_output_get_result (output, error)) {
+ if (output)
+ qmi_message_wds_set_event_report_output_unref (output);
+ return 0;
+ }
+ qmi_message_wds_set_event_report_output_unref (output);
+
+ return g_signal_connect (client,
+ "event-report",
+ G_CALLBACK (event_report_indication_cb),
+ self);
+}
+
+static void
+connect_enable_indications_ipv4_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->event_report_ipv4_indication_id == 0);
+
+ ctx->event_report_ipv4_indication_id =
+ connect_enable_indications_ready (client, res, ctx->self, &ctx->error_ipv4);
+
+ if (!ctx->event_report_ipv4_indication_id)
+ ctx->step = CONNECT_STEP_LAST;
+ else
+ ctx->step++;
+
+ connect_context_step (task);
+}
+
+static void
+connect_enable_indications_ipv6_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->event_report_ipv6_indication_id == 0);
+
+ ctx->event_report_ipv6_indication_id =
+ connect_enable_indications_ready (client, res, ctx->self, &ctx->error_ipv6);
+
+ if (!ctx->event_report_ipv6_indication_id)
+ ctx->step = CONNECT_STEP_LAST;
+ else
+ ctx->step++;
+
+ connect_context_step (task);
+}
+
+static QmiMessageWdsSetEventReportInput *
+event_report_input_new (gboolean enable)
+{
+ QmiMessageWdsSetEventReportInput *input;
+
+ input = qmi_message_wds_set_event_report_input_new ();
+ qmi_message_wds_set_event_report_input_set_extended_data_bearer_technology (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_limited_data_system_status (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_uplink_flow_control (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_data_systems (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_evdo_pm_change (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_preferred_data_system (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_data_call_status (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_current_data_bearer_technology (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_mip_status (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_dormancy_status (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_data_bearer_technology (input, enable, NULL);
+ qmi_message_wds_set_event_report_input_set_channel_rate (input, enable, NULL);
+
+ return input;
+}
+
+static void
+setup_event_report_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiMessageWdsSetEventReportInput *input = event_report_input_new (TRUE);
+
+ qmi_client_wds_set_event_report (client,
+ input,
+ 5,
+ cancellable,
+ callback,
+ user_data);
+ qmi_message_wds_set_event_report_input_unref (input);
+}
+
+static void
+cleanup_event_report_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ guint *indication_id)
+{
+ QmiMessageWdsSetEventReportInput *input;
+
+ g_assert (*indication_id != 0);
+ g_signal_handler_disconnect (client, *indication_id);
+ *indication_id = 0;
+
+ input = event_report_input_new (FALSE);
+ qmi_client_wds_set_event_report (client,
+ input,
+ 5,
+ NULL,
+ NULL,
+ NULL);
+ qmi_message_wds_set_event_report_input_unref (input);
+}
+
static void
set_ip_family_ready (QmiClientWds *client,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ MMBearerQmi *self;
+ ConnectContext *ctx;
GError *error = NULL;
QmiMessageWdsSetIpFamilyOutput *output;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
g_assert (ctx->running_ipv4 || ctx->running_ipv6);
g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
@@ -568,308 +1338,714 @@ set_ip_family_ready (QmiClientWds *client,
}
if (error) {
- /* Ensure we add the IP family preference TLV */
- mm_dbg ("Couldn't set IP family preference: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't set IP family preference: %s", error->message);
g_error_free (error);
- ctx->default_ip_family_set = FALSE;
- } else {
- /* No need to add IP family preference */
- ctx->default_ip_family_set = TRUE;
}
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
+}
+
+static void
+bind_data_port_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(QmiMessageWdsBindDataPortOutput) output = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->running_ipv4 || ctx->running_ipv6);
+ g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
+
+ output = qmi_client_wds_bind_data_port_finish (client, res, &error);
+ if (!output || !qmi_message_wds_bind_data_port_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't bind data port: ");
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+bind_mux_data_port_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(QmiMessageWdsBindMuxDataPortOutput) output = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->running_ipv4 || ctx->running_ipv6);
+ g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
+
+ output = qmi_client_wds_bind_mux_data_port_finish (client, res, &error);
+ if (!output || !qmi_message_wds_bind_mux_data_port_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't bind mux data port: ");
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
}
static void
qmi_port_allocate_client_ready (MMPortQmi *qmi,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ ConnectContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
g_assert (ctx->running_ipv4 || ctx->running_ipv6);
g_assert (!(ctx->running_ipv4 && ctx->running_ipv6));
if (!mm_port_qmi_allocate_client_finish (qmi, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_prefix_error (&error, "Couldn't allocate %s client in QMI port %s: ",
+ ctx->running_ipv4 ? "IPv4" : "IPv6",
+ mm_port_get_device (MM_PORT (qmi)));
+ complete_connect (task, NULL, error);
return;
}
if (ctx->running_ipv4)
- ctx->client_ipv4 = QMI_CLIENT_WDS (mm_port_qmi_get_client (qmi,
- QMI_SERVICE_WDS,
- MM_PORT_QMI_FLAG_WDS_IPV4));
+ ctx->client_ipv4 = QMI_CLIENT_WDS (mm_port_qmi_get_client (
+ qmi,
+ QMI_SERVICE_WDS,
+ MM_PORT_QMI_FLAG_WITH_MUX_ID (MM_PORT_QMI_FLAG_WDS_IPV4, ctx->mux_id)));
else
- ctx->client_ipv6 = QMI_CLIENT_WDS (mm_port_qmi_get_client (qmi,
- QMI_SERVICE_WDS,
- MM_PORT_QMI_FLAG_WDS_IPV6));
+ ctx->client_ipv6 = QMI_CLIENT_WDS (mm_port_qmi_get_client (
+ qmi,
+ QMI_SERVICE_WDS,
+ MM_PORT_QMI_FLAG_WITH_MUX_ID (MM_PORT_QMI_FLAG_WDS_IPV6, ctx->mux_id)));
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+master_interface_up_ready (MMPortNet *link,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_net_link_setup_finish (link, res, &error)) {
+ g_prefix_error (&error, "Couldn't bring master interface up: ");
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+wait_link_port_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->link = mm_base_modem_wait_link_port_finish (modem, res, &error);
+ if (!ctx->link) {
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+setup_link_ready (MMPortQmi *qmi,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MMBaseModem) modem = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->link_name = mm_port_qmi_setup_link_finish (qmi, res, &ctx->mux_id, &error);
+ if (!ctx->link_name) {
+ g_prefix_error (&error, "failed to create net link for device: ");
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* From now on link_name will be set, and we'll use that to know
+ * whether we should cleanup the link upon a connection failure */
+ mm_obj_info (ctx->self, "net link %s created (mux id %u)", ctx->link_name, ctx->mux_id);
+
+ /* Wait for the data port with the given interface name, which will be
+ * added asynchronously */
+ g_object_get (ctx->self,
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+ g_assert (modem);
+
+ mm_base_modem_wait_link_port (modem,
+ "net",
+ ctx->link_name,
+ WAIT_LINK_PORT_TIMEOUT_MS,
+ (GAsyncReadyCallback) wait_link_port_ready,
+ task);
+}
+
+static void
+setup_data_format_ready (MMPortQmi *qmi,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_qmi_setup_data_format_finish (qmi, res, &error)) {
+ /* a failure here could indicate no support for WDA Set Data Format,
+ * if so, just go on with the plain CTL based support (and data aggregation
+ * protocol disabled) */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ complete_connect (task, NULL, g_steal_pointer (&error));
+ return;
+ }
+ ctx->dap = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+ } else
+ ctx->dap = mm_port_qmi_get_data_aggregation_protocol (ctx->qmi);
+
+ if ((ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) &&
+ (ctx->dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED)) {
+ complete_connect (task, NULL,
+ g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot enable multiplex support"));
+ return;
+ }
+
+ if ((ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_NONE) &&
+ (ctx->dap != QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED)) {
+ complete_connect (task, NULL,
+ g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot disable multiplex support"));
+ return;
+ }
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
}
static void
qmi_port_open_ready (MMPortQmi *qmi,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
+ ConnectContext *ctx;
GError *error = NULL;
if (!mm_port_qmi_open_finish (qmi, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_prefix_error (&error, "Couldn't open QMI port %s: ",
+ mm_port_get_device (MM_PORT (qmi)));
+ complete_connect (task, NULL, error);
return;
}
+ ctx = g_task_get_task_data (task);
+ ctx->explicit_qmi_open = TRUE;
+
/* Keep on */
ctx->step++;
- connect_context_step (ctx);
+ connect_context_step (task);
+}
+
+static gboolean
+load_ip_type_settings_from_profile (ConnectContext *ctx,
+ MM3gppProfile *profile,
+ GError **error)
+{
+ MMBearerIpFamily ip_family;
+
+ ip_family = mm_3gpp_profile_get_ip_type (profile);
+ if (mm_3gpp_normalize_ip_family (&ip_family))
+ ctx->no_ip_family_preference = TRUE;
+ if (ip_family & MM_BEARER_IP_FAMILY_IPV4)
+ ctx->ipv4 = TRUE;
+ if (ip_family & MM_BEARER_IP_FAMILY_IPV6)
+ ctx->ipv6 = TRUE;
+ if (ip_family & MM_BEARER_IP_FAMILY_IPV4V6) {
+ ctx->ipv4 = TRUE;
+ ctx->ipv6 = TRUE;
+ }
+ if (!ctx->ipv4 && !ctx->ipv6) {
+ g_autofree gchar *str = NULL;
+
+ str = mm_bearer_ip_family_build_string_from_mask (ip_family);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported IP type requested: '%s'", str);
+ return FALSE;
+ }
+
+ return TRUE;
}
static void
-connect_context_step (ConnectContext *ctx)
+get_profile_ready (MMIfaceModem3gppProfileManager *modem,
+ GAsyncResult *res,
+ GTask *task)
{
- /* If cancelled, complete */
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Connection setup operation has been cancelled");
- connect_context_complete_and_free (ctx);
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error);
+ if (!profile) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- switch (ctx->step) {
- case CONNECT_STEP_FIRST:
+ if (!load_ip_type_settings_from_profile (ctx, profile, &error)) {
+ g_prefix_error (&error, "Couldn't load ip type settings from profile: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- g_assert (ctx->ipv4 || ctx->ipv6);
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+connect_context_step (GTask *task)
+{
+ MMBearerQmi *self;
+ ConnectContext *ctx;
+
+ self = g_task_get_source_object (task);
+
+ g_assert (self->priv->ongoing_connect_user_cancellable);
+ if (g_cancellable_is_cancelled (self->priv->ongoing_connect_user_cancellable)) {
+ complete_connect (task,
+ NULL,
+ g_error_new (G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ "operation cancelled"));
+ return;
+ }
+
+ g_assert (self->priv->ongoing_connect_network_cancellable);
+ if (g_cancellable_is_cancelled (self->priv->ongoing_connect_network_cancellable)) {
+ complete_connect (task,
+ NULL,
+ g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_ABORTED,
+ "aborted by the network"));
+ return;
+ }
- /* Fall down */
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case CONNECT_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_LOAD_PROFILE_SETTINGS:
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "loading connection settings from profile '%d'...", ctx->profile_id);
+ mm_iface_modem_3gpp_profile_manager_get_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->modem),
+ ctx->profile_id,
+ (GAsyncReadyCallback)get_profile_ready,
+ task);
+ return;
+ }
ctx->step++;
+ /* fall through */
case CONNECT_STEP_OPEN_QMI_PORT:
+ g_assert (ctx->ipv4 || ctx->ipv6);
+ /* If we're explicitly opening the port (e.g. using a different cdc-wdm
+ * port because the primary one is already connected by a different
+ * bearer), then make sure we also close it if anything goes wrong and
+ * during disconnect */
if (!mm_port_qmi_is_open (ctx->qmi)) {
mm_port_qmi_open (ctx->qmi,
TRUE,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)qmi_port_open_ready,
- ctx);
+ task);
return;
}
- /* If already open, just fall down */
ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_SETUP_DATA_FORMAT: {
+ MMPortQmiSetupDataFormatAction action;
+
+ switch (ctx->multiplex) {
+ case MM_BEARER_MULTIPLEX_SUPPORT_NONE:
+ action = MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT;
+ break;
+ case MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED:
+ case MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED:
+ action = MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX;
+ break;
+ case MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ mm_port_qmi_setup_data_format (ctx->qmi,
+ ctx->data,
+ action,
+ (GAsyncReadyCallback) setup_data_format_ready,
+ task);
+ return;
+ }
+
+ case CONNECT_STEP_SETUP_LINK:
+ /* if muxing has been enabled in the port, we need to create a new link
+ * interface. */
+ if (MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (ctx->dap)) {
+ mm_port_qmi_setup_link (ctx->qmi,
+ ctx->data,
+ ctx->link_prefix_hint,
+ (GAsyncReadyCallback) setup_link_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_SETUP_LINK_MASTER_UP:
+ /* if the connection is done through a new link, we need to ifup the master interface */
+ if (ctx->link) {
+ mm_obj_dbg (self, "bringing master interface %s up...", mm_port_get_device (ctx->data));
+ mm_port_net_link_setup (MM_PORT_NET (ctx->data),
+ TRUE,
+ 0, /* ignore */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) master_interface_up_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_IP_METHOD:
+ /* Once the QMI port is open, we decide the IP method we're going
+ * to request. If the LLP is raw-ip, we force Static IP, because not
+ * all DHCP clients support the raw-ip interfaces; otherwise default
+ * to DHCP as always. */
+ if (mm_port_qmi_get_link_layer_protocol (ctx->qmi) == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP)
+ ctx->ip_method = MM_BEARER_IP_METHOD_STATIC;
+ else
+ ctx->ip_method = MM_BEARER_IP_METHOD_DHCP;
+
+ mm_obj_dbg (self, "defaulting to use %s IP method", mm_bearer_ip_method_get_string (ctx->ip_method));
+ ctx->step++;
+ /* fall through */
case CONNECT_STEP_IPV4:
/* If no IPv4 setup needed, jump to IPv6 */
if (!ctx->ipv4) {
ctx->step = CONNECT_STEP_IPV6;
- connect_context_step (ctx);
+ connect_context_step (task);
return;
}
/* Start IPv4 setup */
- mm_dbg ("Running IPv4 connection setup");
+ mm_obj_dbg (self, "running IPv4 connection setup");
ctx->running_ipv4 = TRUE;
ctx->running_ipv6 = FALSE;
- /* Just fall down */
ctx->step++;
+ /* fall through */
case CONNECT_STEP_WDS_CLIENT_IPV4: {
QmiClient *client;
client = mm_port_qmi_get_client (ctx->qmi,
QMI_SERVICE_WDS,
- MM_PORT_QMI_FLAG_WDS_IPV4);
+ MM_PORT_QMI_FLAG_WITH_MUX_ID (MM_PORT_QMI_FLAG_WDS_IPV4, ctx->mux_id));
if (!client) {
- mm_dbg ("Allocating IPv4-specific WDS client");
+ mm_obj_dbg (self, "allocating IPv4-specific WDS client (mux id %u)", ctx->mux_id);
mm_port_qmi_allocate_client (ctx->qmi,
QMI_SERVICE_WDS,
- MM_PORT_QMI_FLAG_WDS_IPV4,
- ctx->cancellable,
+ MM_PORT_QMI_FLAG_WITH_MUX_ID (MM_PORT_QMI_FLAG_WDS_IPV4, ctx->mux_id),
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)qmi_port_allocate_client_ready,
- ctx);
+ task);
return;
}
ctx->client_ipv4 = QMI_CLIENT_WDS (client);
- /* Just fall down */
ctx->step++;
- }
+ } /* fall through */
+
+ case CONNECT_STEP_BIND_DATA_PORT_IPV4:
+ /* If SIO port given, bind client to it */
+ if (ctx->sio_port != QMI_SIO_PORT_NONE) {
+ g_autoptr(QmiMessageWdsBindDataPortInput) input = NULL;
+
+ mm_obj_dbg (self, "binding to data port: %s", qmi_sio_port_get_string (ctx->sio_port));
+ input = qmi_message_wds_bind_data_port_input_new ();
+ qmi_message_wds_bind_data_port_input_set_data_port (input, ctx->sio_port, NULL);
+ qmi_client_wds_bind_data_port (ctx->client_ipv4,
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)bind_data_port_ready,
+ task);
+ return;
+ }
+
+ /* If mux id given, bind mux data port */
+ if (ctx->mux_id != QMI_DEVICE_MUX_ID_UNBOUND) {
+ g_autoptr(QmiMessageWdsBindMuxDataPortInput) input = NULL;
+
+ mm_obj_dbg (self, "binding to mux id %d", ctx->mux_id);
+ input = qmi_message_wds_bind_mux_data_port_input_new ();
+ qmi_message_wds_bind_mux_data_port_input_set_endpoint_info (
+ input,
+ mm_port_qmi_get_endpoint_type (ctx->qmi),
+ mm_port_qmi_get_endpoint_interface_number (ctx->qmi),
+ NULL);
+ qmi_message_wds_bind_mux_data_port_input_set_mux_id (input, ctx->mux_id, NULL);
+
+ qmi_client_wds_bind_mux_data_port (ctx->client_ipv4,
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)bind_mux_data_port_ready,
+ task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall through */
case CONNECT_STEP_IP_FAMILY_IPV4:
/* If client is new enough, select IP family */
- if (!ctx->no_ip_family_preference &&
- qmi_client_check_version (QMI_CLIENT (ctx->client_ipv4), 1, 9)) {
+ if (!ctx->no_ip_family_preference) {
QmiMessageWdsSetIpFamilyInput *input;
- mm_dbg ("Setting default IP family to: IPv4");
+ mm_obj_dbg (self, "setting default IP family to: IPv4");
input = qmi_message_wds_set_ip_family_input_new ();
qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV4, NULL);
qmi_client_wds_set_ip_family (ctx->client_ipv4,
input,
10,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)set_ip_family_ready,
- ctx);
+ task);
qmi_message_wds_set_ip_family_input_unref (input);
return;
}
- ctx->default_ip_family_set = FALSE;
-
- /* Just fall down */
ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_ENABLE_INDICATIONS_IPV4:
+ common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ TRUE,
+ &ctx->packet_service_status_ipv4_indication_id);
+ setup_event_report_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) connect_enable_indications_ipv4_ready,
+ task);
+ return;
case CONNECT_STEP_START_NETWORK_IPV4: {
QmiMessageWdsStartNetworkInput *input;
- mm_dbg ("Starting IPv4 connection...");
+ mm_obj_dbg (self, "starting IPv4 connection...");
input = build_start_network_input (ctx);
qmi_client_wds_start_network (ctx->client_ipv4,
input,
- 45,
- ctx->cancellable,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)start_network_ready,
- ctx);
+ task);
qmi_message_wds_start_network_input_unref (input);
return;
}
- case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4: {
+ case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4:
/* Retrieve and print IP configuration */
if (ctx->packet_data_handle_ipv4) {
- mm_dbg ("Getting IPv4 configuration...");
- get_current_settings (ctx, ctx->client_ipv4);
+ mm_obj_dbg (self, "getting IPv4 configuration...");
+ get_current_settings (task, ctx->client_ipv4);
return;
}
- /* Fall through */
ctx->step++;
- }
+ /* fall through */
case CONNECT_STEP_IPV6:
/* If no IPv6 setup needed, jump to last */
if (!ctx->ipv6) {
ctx->step = CONNECT_STEP_LAST;
- connect_context_step (ctx);
+ connect_context_step (task);
return;
}
/* Start IPv6 setup */
- mm_dbg ("Running IPv6 connection setup");
+ mm_obj_dbg (self, "running IPv6 connection setup");
ctx->running_ipv4 = FALSE;
ctx->running_ipv6 = TRUE;
- /* Just fall down */
ctx->step++;
+ /* fall through */
case CONNECT_STEP_WDS_CLIENT_IPV6: {
QmiClient *client;
client = mm_port_qmi_get_client (ctx->qmi,
QMI_SERVICE_WDS,
- MM_PORT_QMI_FLAG_WDS_IPV6);
+ MM_PORT_QMI_FLAG_WITH_MUX_ID (MM_PORT_QMI_FLAG_WDS_IPV6, ctx->mux_id));
if (!client) {
- mm_dbg ("Allocating IPv6-specific WDS client");
+ mm_obj_dbg (self, "allocating IPv6-specific WDS client (mux id %u)", ctx->mux_id);
mm_port_qmi_allocate_client (ctx->qmi,
QMI_SERVICE_WDS,
- MM_PORT_QMI_FLAG_WDS_IPV6,
- ctx->cancellable,
+ MM_PORT_QMI_FLAG_WITH_MUX_ID (MM_PORT_QMI_FLAG_WDS_IPV6, ctx->mux_id),
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)qmi_port_allocate_client_ready,
- ctx);
+ task);
return;
}
ctx->client_ipv6 = QMI_CLIENT_WDS (client);
- /* Just fall down */
ctx->step++;
- }
+ } /* fall through */
+
+ case CONNECT_STEP_BIND_DATA_PORT_IPV6:
+ /* If SIO port given, bind client to it */
+ if (ctx->sio_port != QMI_SIO_PORT_NONE) {
+ g_autoptr(QmiMessageWdsBindDataPortInput) input = NULL;
+
+ mm_obj_dbg (self, "binding to data port: %s", qmi_sio_port_get_string (ctx->sio_port));
+ input = qmi_message_wds_bind_data_port_input_new ();
+ qmi_message_wds_bind_data_port_input_set_data_port (input, ctx->sio_port, NULL);
+ qmi_client_wds_bind_data_port (ctx->client_ipv6,
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)bind_data_port_ready,
+ task);
+ return;
+ }
- case CONNECT_STEP_IP_FAMILY_IPV6:
+ /* If mux id given, bind mux data port */
+ if (ctx->mux_id != QMI_DEVICE_MUX_ID_UNBOUND) {
+ g_autoptr(QmiMessageWdsBindMuxDataPortInput) input = NULL;
+
+ mm_obj_dbg (self, "binding to mux id %d", ctx->mux_id);
+ input = qmi_message_wds_bind_mux_data_port_input_new ();
+ qmi_message_wds_bind_mux_data_port_input_set_endpoint_info (
+ input,
+ mm_port_qmi_get_endpoint_type (ctx->qmi),
+ mm_port_qmi_get_endpoint_interface_number (ctx->qmi),
+ NULL);
+ qmi_message_wds_bind_mux_data_port_input_set_mux_id (input, ctx->mux_id, NULL);
+
+ qmi_client_wds_bind_mux_data_port (ctx->client_ipv6,
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)bind_mux_data_port_ready,
+ task);
+ return;
+ }
- g_assert (ctx->no_ip_family_preference == FALSE);
+ ctx->step++;
+ /* fall through */
- /* If client is new enough, select IP family */
- if (qmi_client_check_version (QMI_CLIENT (ctx->client_ipv6), 1, 9)) {
- QmiMessageWdsSetIpFamilyInput *input;
+ case CONNECT_STEP_IP_FAMILY_IPV6: {
+ QmiMessageWdsSetIpFamilyInput *input;
- mm_dbg ("Setting default IP family to: IPv6");
- input = qmi_message_wds_set_ip_family_input_new ();
- qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV6, NULL);
- qmi_client_wds_set_ip_family (ctx->client_ipv6,
- input,
- 10,
- ctx->cancellable,
- (GAsyncReadyCallback)set_ip_family_ready,
- ctx);
- qmi_message_wds_set_ip_family_input_unref (input);
- return;
- }
+ g_assert (ctx->no_ip_family_preference == FALSE);
- ctx->default_ip_family_set = FALSE;
+ mm_obj_dbg (self, "setting default IP family to: IPv6");
+ input = qmi_message_wds_set_ip_family_input_new ();
+ qmi_message_wds_set_ip_family_input_set_preference (input, QMI_WDS_IP_FAMILY_IPV6, NULL);
+ qmi_client_wds_set_ip_family (ctx->client_ipv6,
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)set_ip_family_ready,
+ task);
+ qmi_message_wds_set_ip_family_input_unref (input);
+ return;
+ }
- /* Just fall down */
- ctx->step++;
+ case CONNECT_STEP_ENABLE_INDICATIONS_IPV6:
+ common_setup_cleanup_packet_service_status_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ TRUE,
+ &ctx->packet_service_status_ipv6_indication_id);
+ setup_event_report_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) connect_enable_indications_ipv6_ready,
+ task);
+ return;
case CONNECT_STEP_START_NETWORK_IPV6: {
QmiMessageWdsStartNetworkInput *input;
- mm_dbg ("Starting IPv6 connection...");
+ mm_obj_dbg (self, "starting IPv6 connection...");
input = build_start_network_input (ctx);
qmi_client_wds_start_network (ctx->client_ipv6,
input,
- 45,
- ctx->cancellable,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)start_network_ready,
- ctx);
+ task);
qmi_message_wds_start_network_input_unref (input);
return;
}
- case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6: {
+ case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6:
/* Retrieve and print IP configuration */
if (ctx->packet_data_handle_ipv6) {
- mm_dbg ("Getting IPv6 configuration...");
- get_current_settings (ctx, ctx->client_ipv6);
+ mm_obj_dbg (self, "getting IPv6 configuration...");
+ get_current_settings (task, ctx->client_ipv6);
return;
}
- /* Fall through */
ctx->step++;
- }
+ /* fall through */
- case CONNECT_STEP_LAST:
- /* If one of IPv4 or IPv6 succeeds, we're connected */
- if (ctx->packet_data_handle_ipv4 || ctx->packet_data_handle_ipv6) {
- /* Port is connected; update the state */
- mm_port_set_connected (MM_PORT (ctx->data), TRUE);
-
- /* Keep connection related data */
- g_assert (ctx->self->priv->data == NULL);
- ctx->self->priv->data = g_object_ref (ctx->data);
-
- g_assert (ctx->self->priv->packet_data_handle_ipv4 == 0);
- g_assert (ctx->self->priv->client_ipv4 == NULL);
- if (ctx->packet_data_handle_ipv4) {
- ctx->self->priv->packet_data_handle_ipv4 = ctx->packet_data_handle_ipv4;
- ctx->self->priv->client_ipv4 = g_object_ref (ctx->client_ipv4);
- }
-
- g_assert (ctx->self->priv->packet_data_handle_ipv6 == 0);
- g_assert (ctx->self->priv->client_ipv6 == NULL);
- if (ctx->packet_data_handle_ipv6) {
- ctx->self->priv->packet_data_handle_ipv6 = ctx->packet_data_handle_ipv6;
- ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6);
- }
+ case CONNECT_STEP_LAST: {
+ MMBearerConnectResult *connect_result;
- /* Set operation result */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, ctx->ipv6_config),
- (GDestroyNotify)mm_bearer_connect_result_unref);
- } else {
+ /* If one of IPv4 or IPv6 succeeds, we're connected */
+ if (!ctx->packet_data_handle_ipv4 && !ctx->packet_data_handle_ipv6) {
GError *error;
/* No connection, set error. If both set, IPv4 error preferred */
@@ -881,186 +2057,249 @@ connect_context_step (ConnectContext *ctx)
ctx->error_ipv6 = NULL;
}
- g_simple_async_result_take_error (ctx->result, error);
+ complete_connect (task, NULL, error);
+ return;
+ }
+
+ /* Port is connected; update the state */
+ mm_port_set_connected (ctx->link ? ctx->link : ctx->data, TRUE);
+
+ /* Keep connection related data */
+
+ g_assert (ctx->self->priv->qmi == NULL);
+ ctx->self->priv->qmi = g_object_ref (ctx->qmi);
+ if (ctx->explicit_qmi_open) {
+ ctx->self->priv->explicit_qmi_open = TRUE;
+ ctx->explicit_qmi_open = FALSE;
+ }
+
+ g_assert (ctx->self->priv->data == NULL);
+ ctx->self->priv->data = ctx->data ? g_object_ref (ctx->data) : NULL;
+ g_assert (ctx->self->priv->link == NULL);
+ ctx->self->priv->link = ctx->link ? g_object_ref (ctx->link) : NULL;
+ g_assert (ctx->self->priv->mux_id == QMI_DEVICE_MUX_ID_UNBOUND);
+ ctx->self->priv->mux_id = ctx->mux_id;
+
+ /* reset the link name to avoid cleaning up the link on context free */
+ g_clear_pointer (&ctx->link_name, g_free);
+
+ g_assert (ctx->self->priv->packet_data_handle_ipv4 == 0);
+ g_assert (ctx->self->priv->client_ipv4 == NULL);
+ if (ctx->packet_data_handle_ipv4) {
+ ctx->self->priv->packet_data_handle_ipv4 = ctx->packet_data_handle_ipv4;
+ ctx->packet_data_handle_ipv4 = 0;
+ ctx->self->priv->packet_service_status_ipv4_indication_id = ctx->packet_service_status_ipv4_indication_id;
+ ctx->packet_service_status_ipv4_indication_id = 0;
+ ctx->self->priv->event_report_ipv4_indication_id = ctx->event_report_ipv4_indication_id;
+ ctx->event_report_ipv4_indication_id = 0;
+ ctx->self->priv->client_ipv4 = g_object_ref (ctx->client_ipv4);
}
- connect_context_complete_and_free (ctx);
+ g_assert (ctx->self->priv->packet_data_handle_ipv6 == 0);
+ g_assert (ctx->self->priv->client_ipv6 == NULL);
+ if (ctx->packet_data_handle_ipv6) {
+ ctx->self->priv->packet_data_handle_ipv6 = ctx->packet_data_handle_ipv6;
+ ctx->packet_data_handle_ipv6 = 0;
+ ctx->self->priv->packet_service_status_ipv6_indication_id = ctx->packet_service_status_ipv6_indication_id;
+ ctx->packet_service_status_ipv6_indication_id = 0;
+ ctx->self->priv->event_report_ipv6_indication_id = ctx->event_report_ipv6_indication_id;
+ ctx->event_report_ipv6_indication_id = 0;
+ ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6);
+ }
+
+ connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data,
+ ctx->ipv4_config,
+ ctx->ipv6_config);
+ mm_bearer_connect_result_set_multiplexed (connect_result, !!ctx->link);
+
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
+ mm_bearer_connect_result_set_profile_id (connect_result, ctx->profile_id);
+
+ complete_connect (task, connect_result, NULL);
return;
}
+
+ default:
+ g_assert_not_reached ();
+ }
}
static void
-_connect (MMBaseBearer *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+cancel_operation_cancellable (GCancellable *cancellable,
+ GCancellable *operation_cancellable)
{
- MMBearerProperties *properties = NULL;
- ConnectContext *ctx;
- MMBaseModem *modem = NULL;
- MMPort *data;
- MMPortQmi *qmi;
- GError *error = NULL;
- const gchar *apn;
-
- g_object_get (self,
- MM_BASE_BEARER_MODEM, &modem,
- NULL);
- g_assert (modem);
+ g_cancellable_cancel (operation_cancellable);
+}
- /* Grab a data port */
- data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
- if (!data) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No valid data port found to launch connection");
- g_object_unref (modem);
- return;
+static gboolean
+load_settings_from_bearer (MMBearerQmi *self,
+ MMBaseModem *modem,
+ ConnectContext *ctx,
+ MMBearerProperties *properties,
+ GError **error)
+{
+ MMBearerAllowedAuth bearer_auth;
+ GError *inner_error = NULL;
+ const gchar *str;
+ const gchar *data_port_driver;
+
+ data_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (ctx->data));
+
+ /* If no multiplex setting given by the user, assume none; unless in IPA */
+ ctx->multiplex = mm_bearer_properties_get_multiplex (properties);
+ if (ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN) {
+ if (mm_context_get_test_multiplex_requested ())
+ ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED;
+ else if (!g_strcmp0 (data_port_driver, "ipa"))
+ ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED;
+ else
+ ctx->multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE;
}
- /* Each data port has a single QMI port associated */
- qmi = mm_base_modem_get_port_qmi_for_data (modem, data, &error);
- if (!qmi) {
- g_simple_async_report_take_gerror_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- error);
- g_object_unref (data);
- g_object_unref (modem);
- return;
+ /* The link prefix hint given must be modem-specific */
+ if (ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED ||
+ ctx->multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
+ ctx->link_prefix_hint = g_strdup_printf ("qmapmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (modem)));
}
- /* Check whether we have an APN */
- apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ /* If profile id is given, we'll launch the connection specifying the profile id in use
+ * exclusively, so we ignore any additional user provided setting */
+ ctx->profile_id = mm_bearer_properties_get_profile_id (properties);
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ /* Is this a 3GPP2 only modem and profile id was given? If so, error, as we don't support
+ * 3GPP2 profiles in ModemManager */
+ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem))) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "3GPP2 doesn't support profile id setting");
+ return FALSE;
+ }
+ /* All done now, we'll need to load IP type settings later on once
+ * we load the real profile to use */
+ return TRUE;
+ }
+ /* APN settings */
+ ctx->apn = g_strdup (mm_bearer_properties_get_apn (properties));
/* Is this a 3GPP only modem and no APN was given? If so, error */
- if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !apn) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "3GPP connection logic requires APN setting");
- g_object_unref (modem);
- return;
+ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !ctx->apn) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "3GPP connection logic requires APN setting");
+ return FALSE;
}
-
/* Is this a 3GPP2 only modem and APN was given? If so, error */
- if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && apn) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "3GPP2 doesn't support APN setting");
- g_object_unref (modem);
- return;
+ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && ctx->apn) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "3GPP2 doesn't support APN setting");
+ return FALSE;
}
- g_object_unref (modem);
+ /* IP type settings */
+ if (!load_ip_type_settings_from_profile (ctx, mm_bearer_properties_peek_3gpp_profile (properties), error))
+ return FALSE;
- mm_dbg ("Launching connection with QMI port (%s/%s) and data port (%s/%s)",
- mm_port_subsys_get_string (mm_port_get_subsys (MM_PORT (qmi))),
- mm_port_get_device (MM_PORT (qmi)),
- mm_port_subsys_get_string (mm_port_get_subsys (data)),
- mm_port_get_device (data));
+ /* Auth settings; in we treat user/password empty strings as no strings */
+ str = mm_bearer_properties_get_user (properties);
+ if (str && str[0])
+ ctx->user = g_strdup (str);
+ str = mm_bearer_properties_get_password (properties);
+ if (str && str[0])
+ ctx->password = g_strdup (str);
- ctx = g_slice_new0 (ConnectContext);
- ctx->self = g_object_ref (self);
- ctx->qmi = qmi;
- ctx->data = data;
- ctx->cancellable = g_object_ref (cancellable);
- ctx->step = CONNECT_STEP_FIRST;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- connect);
+ if (!ctx->user && !ctx->password)
+ ctx->auth = QMI_WDS_AUTHENTICATION_NONE;
+ else {
+ bearer_auth = mm_bearer_properties_get_allowed_auth (properties);
+ ctx->auth = mm_bearer_allowed_auth_to_qmi_authentication (bearer_auth, self, &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+_connect (MMBaseBearer *_self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBearerQmi *self = MM_BEARER_QMI (_self);
+ ConnectContext *ctx;
+ GError *error = NULL;
+ GTask *task;
+ g_autoptr(GCancellable) operation_cancellable = NULL;
+ g_autoptr(MMBaseModem) modem = NULL;
+ g_autoptr(MMBearerProperties) properties = NULL;
+
+ operation_cancellable = g_cancellable_new ();
+ task = g_task_new (self, operation_cancellable, callback, user_data);
+ g_task_set_check_cancellable (task, FALSE);
g_object_get (self,
+ MM_BASE_BEARER_MODEM, &modem,
MM_BASE_BEARER_CONFIG, &properties,
NULL);
+ g_assert (modem);
- if (properties) {
- MMBearerAllowedAuth auth;
- MMBearerIpFamily ip_family;
-
- ctx->apn = g_strdup (mm_bearer_properties_get_apn (properties));
- ctx->user = g_strdup (mm_bearer_properties_get_user (properties));
- ctx->password = g_strdup (mm_bearer_properties_get_password (properties));
-
- ip_family = mm_bearer_properties_get_ip_type (properties);
- if (ip_family == MM_BEARER_IP_FAMILY_NONE ||
- ip_family == MM_BEARER_IP_FAMILY_ANY) {
- gchar *ip_family_str;
-
- ip_family = mm_base_bearer_get_default_ip_family (self);
- ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);
- mm_dbg ("No specific IP family requested, defaulting to %s",
- ip_family_str);
- ctx->no_ip_family_preference = TRUE;
- g_free (ip_family_str);
- }
+ ctx = g_slice_new0 (ConnectContext);
+ ctx->self = g_object_ref (self);
+ ctx->modem = g_object_ref (modem);
+ ctx->mux_id = QMI_DEVICE_MUX_ID_UNBOUND;
+ ctx->sio_port = QMI_SIO_PORT_NONE;
+ ctx->step = CONNECT_STEP_FIRST;
+ ctx->ip_method = MM_BEARER_IP_METHOD_UNKNOWN;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
- if (ip_family & MM_BEARER_IP_FAMILY_IPV4)
- ctx->ipv4 = TRUE;
- if (ip_family & MM_BEARER_IP_FAMILY_IPV6)
- ctx->ipv6 = TRUE;
- if (ip_family & MM_BEARER_IP_FAMILY_IPV4V6) {
- ctx->ipv4 = TRUE;
- ctx->ipv6 = TRUE;
- }
+ /* Grab a data port */
+ ctx->data = mm_base_modem_get_best_data_port (modem, MM_PORT_TYPE_NET);
+ if (!ctx->data) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "No valid data port found to launch connection");
+ g_object_unref (task);
+ return;
+ }
- if (!ctx->ipv4 && !ctx->ipv6) {
- gchar *str;
-
- str = mm_bearer_ip_family_build_string_from_mask (ip_family);
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Unsupported IP type requested: '%s'",
- str);
- g_free (str);
- connect_context_complete_and_free (ctx);
- return;
- }
+ /* Each data port has a single QMI port associated */
+ ctx->qmi = mm_broadband_modem_qmi_get_port_qmi_for_data (MM_BROADBAND_MODEM_QMI (modem), ctx->data, &ctx->sio_port, &error);
+ if (!ctx->qmi) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ ctx->dap = mm_port_qmi_get_data_aggregation_protocol (ctx->qmi);
- auth = mm_bearer_properties_get_allowed_auth (properties);
- g_object_unref (properties);
-
- if (auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
- mm_dbg ("Using default (PAP) authentication method");
- ctx->auth = QMI_WDS_AUTHENTICATION_PAP;
- } else if (auth & (MM_BEARER_ALLOWED_AUTH_PAP |
- MM_BEARER_ALLOWED_AUTH_CHAP |
- MM_BEARER_ALLOWED_AUTH_NONE)) {
- /* Only PAP and/or CHAP or NONE are supported */
- ctx->auth = mm_bearer_allowed_auth_to_qmi_authentication (auth);
- } else {
- gchar *str;
-
- str = mm_bearer_allowed_auth_build_string_from_mask (auth);
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot use any of the specified authentication methods (%s)",
- str);
- g_free (str);
- connect_context_complete_and_free (ctx);
- return;
- }
+ /* load all settings from bearer */
+ if (!load_settings_from_bearer (self, modem, ctx, properties, &error)) {
+ g_prefix_error (&error, "Invalid bearer properties: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
+ /* setup network cancellable */
+ g_assert (!self->priv->ongoing_connect_network_cancellable);
+ self->priv->ongoing_connect_network_cancellable = g_cancellable_new ();
+ g_cancellable_connect (self->priv->ongoing_connect_network_cancellable,
+ G_CALLBACK (cancel_operation_cancellable),
+ g_object_ref (operation_cancellable),
+ g_object_unref);
+
+ /* setup user cancellable */
+ g_assert (!self->priv->ongoing_connect_user_cancellable);
+ self->priv->ongoing_connect_user_cancellable = g_object_ref (cancellable);
+ g_cancellable_connect (self->priv->ongoing_connect_user_cancellable,
+ G_CALLBACK (cancel_operation_cancellable),
+ g_object_ref (operation_cancellable),
+ g_object_unref);
+
/* Run! */
- connect_context_step (ctx);
+ mm_obj_dbg (self, "launching connection with QMI port (%s) and data port (%s) (multiplex %s)",
+ mm_port_get_device (MM_PORT (ctx->qmi)),
+ mm_port_get_device (ctx->data),
+ mm_bearer_multiplex_support_get_string (ctx->multiplex));
+ connect_context_step (task);
}
/*****************************************************************************/
@@ -1074,9 +2313,6 @@ typedef enum {
} DisconnectStep;
typedef struct {
- MMBearerQmi *self;
- GSimpleAsyncResult *result;
- MMPort *data;
DisconnectStep step;
gboolean running_ipv4;
@@ -1091,10 +2327,8 @@ typedef struct {
} DisconnectContext;
static void
-disconnect_context_complete_and_free (DisconnectContext *ctx)
+disconnect_context_free (DisconnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
if (ctx->error_ipv4)
g_error_free (ctx->error_ipv4);
if (ctx->error_ipv6)
@@ -1103,8 +2337,6 @@ disconnect_context_complete_and_free (DisconnectContext *ctx)
g_object_unref (ctx->client_ipv4);
if (ctx->client_ipv6)
g_object_unref (ctx->client_ipv6);
- g_object_unref (ctx->data);
- g_object_unref (ctx->self);
g_slice_free (DisconnectContext, ctx);
}
@@ -1113,7 +2345,7 @@ disconnect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -1122,35 +2354,82 @@ reset_bearer_connection (MMBearerQmi *self,
gboolean reset_ipv6)
{
if (reset_ipv4) {
+ if (self->priv->client_ipv4) {
+ if (self->priv->packet_service_status_ipv4_indication_id)
+ common_setup_cleanup_packet_service_status_unsolicited_events (self,
+ self->priv->client_ipv4,
+ FALSE,
+ &self->priv->packet_service_status_ipv4_indication_id);
+ if (self->priv->event_report_ipv4_indication_id)
+ cleanup_event_report_unsolicited_events (self,
+ self->priv->client_ipv4,
+ &self->priv->event_report_ipv4_indication_id);
+ }
self->priv->packet_data_handle_ipv4 = 0;
g_clear_object (&self->priv->client_ipv4);
}
if (reset_ipv6) {
+ if (self->priv->client_ipv6) {
+ if (self->priv->packet_service_status_ipv6_indication_id)
+ common_setup_cleanup_packet_service_status_unsolicited_events (self,
+ self->priv->client_ipv6,
+ FALSE,
+ &self->priv->packet_service_status_ipv6_indication_id);
+ if (self->priv->event_report_ipv6_indication_id)
+ cleanup_event_report_unsolicited_events (self,
+ self->priv->client_ipv6,
+ &self->priv->event_report_ipv6_indication_id);
+ }
self->priv->packet_data_handle_ipv6 = 0;
g_clear_object (&self->priv->client_ipv6);
}
- if (!self->priv->packet_data_handle_ipv4 &&
- !self->priv->packet_data_handle_ipv6) {
+ if (!self->priv->packet_data_handle_ipv4 && !self->priv->packet_data_handle_ipv6) {
if (self->priv->data) {
/* Port is disconnected; update the state */
mm_port_set_connected (self->priv->data, FALSE);
g_clear_object (&self->priv->data);
}
+ if (self->priv->link) {
+ g_assert (self->priv->qmi);
+ /* Link is disconnected; update the state */
+ mm_port_set_connected (self->priv->link, FALSE);
+ mm_port_qmi_cleanup_link (self->priv->qmi,
+ mm_port_get_device (self->priv->link),
+ self->priv->mux_id,
+ NULL,
+ NULL);
+ g_clear_object (&self->priv->link);
+ }
+ self->priv->mux_id = QMI_DEVICE_MUX_ID_UNBOUND;
+
+ /* Close port if we had it explicitly open for this connection */
+ if (self->priv->qmi) {
+ if (self->priv->explicit_qmi_open) {
+ self->priv->explicit_qmi_open = FALSE;
+ mm_port_qmi_close (self->priv->qmi, NULL, NULL);
+ }
+ g_clear_object (&self->priv->qmi);
+ }
}
}
-static void disconnect_context_step (DisconnectContext *ctx);
+static void disconnect_context_step (GTask *task);
static void
stop_network_ready (QmiClientWds *client,
GAsyncResult *res,
- DisconnectContext *ctx)
+ GTask *task)
{
+ MMBearerQmi *self;
+ DisconnectContext *ctx;
GError *error = NULL;
QmiMessageWdsStopNetworkOutput *output;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_wds_stop_network_finish (client, res, &error);
if (output &&
!qmi_message_wds_stop_network_output_get_result (output, &error)) {
@@ -1170,7 +2449,7 @@ stop_network_ready (QmiClientWds *client,
ctx->error_ipv6 = error;
} else {
/* Clear internal status */
- reset_bearer_connection (ctx->self,
+ reset_bearer_connection (self,
ctx->running_ipv4,
ctx->running_ipv6);
}
@@ -1180,21 +2459,36 @@ stop_network_ready (QmiClientWds *client,
/* Keep on */
ctx->step++;
- disconnect_context_step (ctx);
+ disconnect_context_step (task);
}
static void
-disconnect_context_step (DisconnectContext *ctx)
+disconnect_context_step (GTask *task)
{
+ MMBearerQmi *self;
+ DisconnectContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISCONNECT_STEP_FIRST:
- /* Fall down */
ctx->step++;
+ /* fall through */
case DISCONNECT_STEP_STOP_NETWORK_IPV4:
if (ctx->packet_data_handle_ipv4) {
QmiMessageWdsStopNetworkInput *input;
+ common_setup_cleanup_packet_service_status_unsolicited_events (self,
+ ctx->client_ipv4,
+ FALSE,
+ &self->priv->packet_service_status_ipv4_indication_id);
+ if (self->priv->event_report_ipv4_indication_id)
+ cleanup_event_report_unsolicited_events (self,
+ ctx->client_ipv4,
+ &self->priv->event_report_ipv4_indication_id);
+
input = qmi_message_wds_stop_network_input_new ();
qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv4, NULL);
@@ -1202,20 +2496,30 @@ disconnect_context_step (DisconnectContext *ctx)
ctx->running_ipv6 = FALSE;
qmi_client_wds_stop_network (ctx->client_ipv4,
input,
- 30,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
NULL,
(GAsyncReadyCallback)stop_network_ready,
- ctx);
+ task);
+ qmi_message_wds_stop_network_input_unref (input);
return;
}
- /* Fall down */
ctx->step++;
+ /* fall through */
case DISCONNECT_STEP_STOP_NETWORK_IPV6:
if (ctx->packet_data_handle_ipv6) {
QmiMessageWdsStopNetworkInput *input;
+ common_setup_cleanup_packet_service_status_unsolicited_events (self,
+ ctx->client_ipv6,
+ FALSE,
+ &self->priv->packet_service_status_ipv6_indication_id);
+ if (self->priv->event_report_ipv6_indication_id)
+ cleanup_event_report_unsolicited_events (self,
+ ctx->client_ipv6,
+ &self->priv->event_report_ipv6_indication_id);
+
input = qmi_message_wds_stop_network_input_new ();
qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv6, NULL);
@@ -1223,19 +2527,20 @@ disconnect_context_step (DisconnectContext *ctx)
ctx->running_ipv6 = TRUE;
qmi_client_wds_stop_network (ctx->client_ipv6,
input,
- 30,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
NULL,
(GAsyncReadyCallback)stop_network_ready,
- ctx);
+ task);
+ qmi_message_wds_stop_network_input_unref (input);
return;
}
- /* Fall down */
ctx->step++;
+ /* fall through */
case DISCONNECT_STEP_LAST:
if (!ctx->error_ipv4 && !ctx->error_ipv6)
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
else {
GError *error;
@@ -1248,11 +2553,14 @@ disconnect_context_step (DisconnectContext *ctx)
ctx->error_ipv6 = NULL;
}
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
}
- disconnect_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
}
@@ -1263,57 +2571,58 @@ disconnect (MMBaseBearer *_self,
{
MMBearerQmi *self = MM_BEARER_QMI (_self);
DisconnectContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
if ((!self->priv->packet_data_handle_ipv4 && !self->priv->packet_data_handle_ipv6) ||
(!self->priv->client_ipv4 && !self->priv->client_ipv6) ||
- !self->priv->data) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't disconnect QMI bearer: this bearer is not connected");
+ (!self->priv->data && !self->priv->link) ||
+ !self->priv->qmi) {
+ mm_obj_dbg (self, "no need to disconnect: QMI bearer is already disconnected");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
ctx = g_slice_new0 (DisconnectContext);
- ctx->self = g_object_ref (self);
- ctx->data = g_object_ref (self->priv->data);
ctx->client_ipv4 = self->priv->client_ipv4 ? g_object_ref (self->priv->client_ipv4) : NULL;
ctx->packet_data_handle_ipv4 = self->priv->packet_data_handle_ipv4;
ctx->client_ipv6 = self->priv->client_ipv6 ? g_object_ref (self->priv->client_ipv6) : NULL;
ctx->packet_data_handle_ipv6 = self->priv->packet_data_handle_ipv6;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect);
ctx->step = DISCONNECT_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free);
+
/* Run! */
- disconnect_context_step (ctx);
+ disconnect_context_step (task);
}
/*****************************************************************************/
static void
-report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status)
+report_connection_status (MMBaseBearer *_self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
- if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
+ MMBearerQmi *self = MM_BEARER_QMI (_self);
+
+ if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED) {
+ /* Cancel any ongoing connection attempt */
+ g_cancellable_cancel (self->priv->ongoing_connect_network_cancellable);
/* Cleanup all connection related data */
- reset_bearer_connection (MM_BEARER_QMI (self), TRUE, TRUE);
+ reset_bearer_connection (self, TRUE, TRUE);
+ }
/* Chain up parent's report_connection_status() */
- MM_BASE_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_connection_status (self, status);
+ MM_BASE_BEARER_CLASS (mm_bearer_qmi_parent_class)->report_connection_status (_self, status, connection_error);
}
/*****************************************************************************/
MMBaseBearer *
mm_bearer_qmi_new (MMBroadbandModemQmi *modem,
- MMBearerProperties *config,
- gboolean force_dhcp)
+ MMBearerProperties *config)
{
MMBaseBearer *bearer;
@@ -1323,7 +2632,6 @@ mm_bearer_qmi_new (MMBroadbandModemQmi *modem,
bearer = g_object_new (MM_TYPE_BEARER_QMI,
MM_BASE_BEARER_MODEM, modem,
MM_BASE_BEARER_CONFIG, config,
- MM_BEARER_QMI_FORCE_DHCP, force_dhcp,
NULL);
/* Only export valid bearers */
@@ -1336,45 +2644,8 @@ static void
mm_bearer_qmi_init (MMBearerQmi *self)
{
/* Initialize private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_BEARER_QMI,
- MMBearerQmiPrivate);
-}
-
-static void
-set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- MMBearerQmi *self = MM_BEARER_QMI (object);
-
- switch (prop_id) {
- case PROP_FORCE_DHCP:
- self->priv->force_dhcp = g_value_get_boolean (value);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-static void
-get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- MMBearerQmi *self = MM_BEARER_QMI (object);
-
- switch (prop_id) {
- case PROP_FORCE_DHCP:
- g_value_set_boolean (value, self->priv->force_dhcp);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BEARER_QMI, MMBearerQmiPrivate);
+ self->priv->mux_id = QMI_DEVICE_MUX_ID_UNBOUND;
}
static void
@@ -1382,9 +2653,9 @@ dispose (GObject *object)
{
MMBearerQmi *self = MM_BEARER_QMI (object);
- g_clear_object (&self->priv->data);
- g_clear_object (&self->priv->client_ipv4);
- g_clear_object (&self->priv->client_ipv6);
+ g_assert (!self->priv->ongoing_connect_user_cancellable);
+ g_assert (!self->priv->ongoing_connect_network_cancellable);
+ reset_bearer_connection (self, TRUE, TRUE);
G_OBJECT_CLASS (mm_bearer_qmi_parent_class)->dispose (object);
}
@@ -1399,21 +2670,18 @@ mm_bearer_qmi_class_init (MMBearerQmiClass *klass)
/* Virtual methods */
object_class->dispose = dispose;
- object_class->get_property = get_property;
- object_class->set_property = set_property;
base_bearer_class->connect = _connect;
base_bearer_class->connect_finish = connect_finish;
base_bearer_class->disconnect = disconnect;
base_bearer_class->disconnect_finish = disconnect_finish;
base_bearer_class->report_connection_status = report_connection_status;
-
- /* Properties */
- properties[PROP_FORCE_DHCP] =
- g_param_spec_boolean (MM_BEARER_QMI_FORCE_DHCP,
- "Force DHCP",
- "Never setup static IP config, always DHCP.",
- FALSE,
- G_PARAM_READWRITE);
- g_object_class_install_property (object_class, PROP_FORCE_DHCP, properties[PROP_FORCE_DHCP]);
+ base_bearer_class->reload_stats = reload_stats;
+ base_bearer_class->reload_stats_finish = reload_stats_finish;
+ base_bearer_class->load_connection_status = load_connection_status;
+ base_bearer_class->load_connection_status_finish = load_connection_status_finish;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = reload_connection_status;
+ base_bearer_class->reload_connection_status_finish = reload_connection_status_finish;
+#endif
}
diff --git a/src/mm-bearer-qmi.h b/src/mm-bearer-qmi.h
index b17f1d4b..d75773f5 100644
--- a/src/mm-bearer-qmi.h
+++ b/src/mm-bearer-qmi.h
@@ -38,8 +38,6 @@ typedef struct _MMBearerQmi MMBearerQmi;
typedef struct _MMBearerQmiClass MMBearerQmiClass;
typedef struct _MMBearerQmiPrivate MMBearerQmiPrivate;
-#define MM_BEARER_QMI_FORCE_DHCP "bearer-qmi-force-dhcp"
-
struct _MMBearerQmi {
MMBaseBearer parent;
MMBearerQmiPrivate *priv;
@@ -50,11 +48,11 @@ struct _MMBearerQmiClass {
};
GType mm_bearer_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBearerQmi, g_object_unref)
/* QMI bearer creation implementation.
* NOTE it is *not* a broadband bearer, so not async-initable */
MMBaseBearer *mm_bearer_qmi_new (MMBroadbandModemQmi *modem,
- MMBearerProperties *config,
- gboolean force_dhcp);
+ MMBearerProperties *config);
#endif /* MM_BEARER_QMI_H */
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c
index 98714ebc..c6234c12 100644
--- a/src/mm-broadband-bearer.c
+++ b/src/mm-broadband-bearer.c
@@ -30,11 +30,13 @@
#include "mm-broadband-bearer.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-iface-modem-cdma.h"
#include "mm-base-modem-at.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-port-enums-types.h"
+#include "mm-helper-enums-types.h"
static void async_initable_iface_init (GAsyncInitableIface *iface);
@@ -48,6 +50,14 @@ typedef enum {
CONNECTION_TYPE_CDMA,
} ConnectionType;
+enum {
+ PROP_0,
+ PROP_FLOW_CONTROL,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
struct _MMBroadbandBearerPrivate {
/*-- Common stuff --*/
/* Data port used when modem is connected */
@@ -55,37 +65,26 @@ struct _MMBroadbandBearerPrivate {
/* Current connection type */
ConnectionType connection_type;
+ /* PPP specific */
+ MMFlowControl flow_control;
+
/*-- 3GPP specific --*/
/* CID of the PDP context */
- guint cid;
+ gint profile_id;
};
/*****************************************************************************/
-
-guint
-mm_broadband_bearer_get_3gpp_cid (MMBroadbandBearer *self)
-{
- return self->priv->cid;
-}
-
-/*****************************************************************************/
/* Detailed connect context, used in both CDMA and 3GPP sequences */
typedef struct {
- MMBroadbandBearer *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPortSerialAt *secondary;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
MMPort *data;
gboolean close_data_on_exit;
/* 3GPP-specific */
- guint cid;
- guint max_cid;
- gboolean use_existing_cid;
MMBearerIpFamily ip_family;
} DetailedConnectContext;
@@ -94,18 +93,12 @@ detailed_connect_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-detailed_connect_context_complete_and_free (DetailedConnectContext *ctx)
+detailed_connect_context_free (DetailedConnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->primary);
if (ctx->secondary)
g_object_unref (ctx->secondary);
@@ -114,74 +107,26 @@ detailed_connect_context_complete_and_free (DetailedConnectContext *ctx)
mm_port_serial_close (MM_PORT_SERIAL (ctx->data));
g_object_unref (ctx->data);
}
- g_object_unref (ctx->self);
g_object_unref (ctx->modem);
g_slice_free (DetailedConnectContext, ctx);
}
-static gboolean
-detailed_connect_context_set_error_if_cancelled (DetailedConnectContext *ctx,
- GError **error)
-{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Connection setup operation has been cancelled");
- return TRUE;
-}
-
-static gboolean
-detailed_connect_context_complete_and_free_if_cancelled (DetailedConnectContext *ctx)
-{
- GError *error = NULL;
-
- if (!detailed_connect_context_set_error_if_cancelled (ctx, &error))
- return FALSE;
-
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
- return TRUE;
-}
-
static DetailedConnectContext *
detailed_connect_context_new (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
- MMPortSerialAt *secondary,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ MMPortSerialAt *secondary)
{
DetailedConnectContext *ctx;
ctx = g_slice_new0 (DetailedConnectContext);
- ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->secondary = (secondary ? g_object_ref (secondary) : NULL);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- detailed_connect_context_new);
ctx->ip_family = mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
- if (ctx->ip_family == MM_BEARER_IP_FAMILY_NONE ||
- ctx->ip_family == MM_BEARER_IP_FAMILY_ANY) {
- gchar *default_family;
-
- ctx->ip_family = mm_base_bearer_get_default_ip_family (MM_BASE_BEARER (self));
- default_family = mm_bearer_ip_family_build_string_from_mask (ctx->ip_family);
- mm_dbg ("No specific IP family requested, defaulting to %s", default_family);
- g_free (default_family);
- }
+ mm_3gpp_normalize_ip_family (&ctx->ip_family);
- /* NOTE:
- * We don't currently support cancelling AT commands, so we'll just check
- * whether the operation is to be cancelled at each step. */
- ctx->cancellable = g_object_ref (cancellable);
return ctx;
}
@@ -189,8 +134,9 @@ detailed_connect_context_new (MMBroadbandBearer *self,
/* Generic implementations (both 3GPP and CDMA) are always AT-port based */
static MMPortSerialAt *
-common_get_at_data_port (MMBaseModem *modem,
- GError **error)
+common_get_at_data_port (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ GError **error)
{
MMPort *data;
@@ -211,7 +157,7 @@ common_get_at_data_port (MMBaseModem *modem,
return NULL;
}
- mm_dbg ("Connection through a plain serial AT port (%s)", mm_port_get_device (data));
+ mm_obj_dbg (self, "connection through a plain serial AT port: %s", mm_port_get_device (data));
return MM_PORT_SERIAL_AT (g_object_ref (data));
}
@@ -228,25 +174,47 @@ common_get_at_data_port (MMBaseModem *modem,
*/
static void
-dial_cdma_ready (MMBaseModem *modem,
+dial_cdma_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MMBearerIpConfig *config;
+ MMBroadbandBearer *self;
+ DetailedConnectContext *ctx;
+ GError *error = NULL;
+ MMBearerIpConfig *config;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
/* DO NOT check for cancellable here. If we got here without errors, the
* bearer is really connected and therefore we need to reflect that in
* the state machine. */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- mm_warn ("Couldn't connect: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't connect: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* else... Yuhu! */
+ /* Configure flow control to use while connected */
+ if (self->priv->flow_control != MM_FLOW_CONTROL_NONE) {
+ gchar *flow_control_str;
+
+ flow_control_str = mm_flow_control_build_string_from_mask (self->priv->flow_control);
+ mm_obj_dbg (self, "setting flow control in %s: %s", mm_port_get_device (ctx->data), flow_control_str);
+ g_free (flow_control_str);
+
+ if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->data), self->priv->flow_control, &error)) {
+ mm_obj_warn (self, "couldn't set flow control settings in %s: %s", mm_port_get_device (ctx->data), error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ /* The ATD command has succeeded, and therefore the TTY is in data mode now.
+ * Instead of waiting for setting the port as connected later in
+ * connect_succeeded(), we do it right away so that we stop our polling. */
+ mm_port_set_connected (ctx->data, TRUE);
/* Keep port open during connection */
ctx->close_data_on_exit = FALSE;
@@ -257,70 +225,62 @@ dial_cdma_ready (MMBaseModem *modem,
mm_bearer_ip_config_set_method (config, MM_BEARER_IP_METHOD_PPP);
/* Assume only IPv4 is given */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
+ g_task_return_pointer (
+ task,
mm_bearer_connect_result_new (ctx->data, config, NULL),
(GDestroyNotify)mm_bearer_connect_result_unref);
- detailed_connect_context_complete_and_free (ctx);
+ g_object_unref (task);
g_object_unref (config);
}
static void
-cdma_connect_context_dial (DetailedConnectContext *ctx)
+cdma_connect_context_dial (GTask *task)
{
- gchar *command;
- const gchar *number;
-
- number = mm_bearer_properties_get_number (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ DetailedConnectContext *ctx;
- /* If a number was given when creating the bearer, use that one.
- * Otherwise, use the default one, #777
- */
- if (number)
- command = g_strconcat ("DT", number, NULL);
- else
- command = g_strdup ("DT#777");
+ ctx = g_task_get_task_data (task);
mm_base_modem_at_command_full (ctx->modem,
MM_PORT_SERIAL_AT (ctx->data),
- command,
- 90,
+ "DT#777",
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE,
NULL,
(GAsyncReadyCallback)dial_cdma_ready,
- ctx);
- g_free (command);
+ task);
}
static void
set_rm_protocol_ready (MMBaseModem *self,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
GError *error = NULL;
/* If cancelled, complete */
- if (detailed_connect_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
mm_base_modem_at_command_full_finish (self, res, &error);
if (error) {
- mm_warn ("Couldn't set RM protocol: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't set RM protocol: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Nothing else needed, go on with dialing */
- cdma_connect_context_dial (ctx);
+ cdma_connect_context_dial (task);
}
static void
current_rm_protocol_ready (MMBaseModem *self,
GAsyncResult *res,
- DetailedConnectContext *ctx)
+ GTask *task)
{
const gchar *result;
GError *error = NULL;
@@ -328,14 +288,16 @@ current_rm_protocol_ready (MMBaseModem *self,
MMModemCdmaRmProtocol current_rm;
/* If cancelled, complete */
- if (detailed_connect_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
result = mm_base_modem_at_command_full_finish (self, res, &error);
if (error) {
- mm_warn ("Couldn't query current RM protocol: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't query current RM protocol: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -343,31 +305,30 @@ current_rm_protocol_ready (MMBaseModem *self,
current_index = (guint) atoi (result);
current_rm = mm_cdma_get_rm_protocol_from_index (current_index, &error);
if (error) {
- mm_warn ("Couldn't parse RM protocol reply (%s): '%s'",
- result,
- error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't parse RM protocol reply (%s): %s", result, error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (current_rm != mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self)))) {
+ DetailedConnectContext *ctx;
guint new_index;
gchar *command;
- mm_dbg ("Setting requested RM protocol...");
+ mm_obj_dbg (self, "setting requested RM protocol...");
new_index = (mm_cdma_get_index_from_rm_protocol (
mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self))),
&error));
if (error) {
- mm_warn ("Cannot set RM protocol: '%s'",
- error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ mm_obj_warn (self, "cannot set RM protocol: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
command = g_strdup_printf ("+CRM=%u", new_index);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
@@ -377,13 +338,13 @@ current_rm_protocol_ready (MMBaseModem *self,
FALSE,
NULL,
(GAsyncReadyCallback)set_rm_protocol_ready,
- ctx);
+ task);
g_free (command);
return;
}
/* Nothing else needed, go on with dialing */
- cdma_connect_context_dial (ctx);
+ cdma_connect_context_dial (task);
}
static void
@@ -396,24 +357,22 @@ connect_cdma (MMBroadbandBearer *self,
gpointer user_data)
{
DetailedConnectContext *ctx;
+ GTask *task;
GError *error = NULL;
g_assert (primary != NULL);
- ctx = detailed_connect_context_new (self,
- modem,
- primary,
- NULL,
- cancellable,
- callback,
- user_data);
+ ctx = detailed_connect_context_new (self, modem, primary, NULL);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free);
/* Grab dial port. This gets a reference to the dial port and OPENs it.
* If we fail, we'll need to close it ourselves. */
- ctx->data = (MMPort *)common_get_at_data_port (ctx->modem, &error);
+ ctx->data = (MMPort *)common_get_at_data_port (self, ctx->modem, &error);
if (!ctx->data) {
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
ctx->close_data_on_exit = TRUE;
@@ -422,7 +381,7 @@ connect_cdma (MMBroadbandBearer *self,
mm_base_bearer_peek_config (MM_BASE_BEARER (self))) !=
MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
/* Need to query current RM protocol */
- mm_dbg ("Querying current RM protocol set...");
+ mm_obj_dbg (self, "querying current RM protocol set...");
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
"+CRM?",
@@ -431,121 +390,95 @@ connect_cdma (MMBroadbandBearer *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)current_rm_protocol_ready,
- ctx);
+ task);
return;
}
/* Nothing else needed, go on with dialing */
- cdma_connect_context_dial (ctx);
+ cdma_connect_context_dial (task);
}
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
- MMBroadbandBearer *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
GError *saved_error;
-
MMPortSerialAt *dial_port;
gboolean close_dial_port_on_exit;
} Dial3gppContext;
static void
-dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+dial_3gpp_context_free (Dial3gppContext *ctx)
{
if (ctx->saved_error)
g_error_free (ctx->saved_error);
if (ctx->dial_port)
g_object_unref (ctx->dial_port);
- g_object_unref (ctx->cancellable);
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (Dial3gppContext, ctx);
}
-static gboolean
-dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx,
- GError **error)
-{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Dial operation has been cancelled");
- return TRUE;
-}
-
-static gboolean
-dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx)
-{
- GError *error = NULL;
-
- if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error))
- return FALSE;
-
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
- return TRUE;
-}
-
static MMPort *
dial_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
extended_error_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ Dial3gppContext *ctx;
const gchar *result;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
/* Close the dialling port as we got an error */
mm_port_serial_close (MM_PORT_SERIAL (ctx->dial_port));
/* If cancelled, complete */
- if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
result = mm_base_modem_at_command_full_finish (modem, res, NULL);
if (result &&
g_str_has_prefix (result, "+CEER: ") &&
strlen (result) > 7) {
- g_simple_async_result_set_error (ctx->result,
- ctx->saved_error->domain,
- ctx->saved_error->code,
- "%s", &result[7]);
+ error = g_error_new (ctx->saved_error->domain,
+ ctx->saved_error->code,
+ "%s",
+ &result[7]);
g_error_free (ctx->saved_error);
} else
- g_simple_async_result_take_error (ctx->result,
- ctx->saved_error);
+ g_propagate_error (&error, ctx->saved_error);
ctx->saved_error = NULL;
/* Done with errors */
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
static void
atd_ready (MMBaseModem *modem,
GAsyncResult *res,
- Dial3gppContext *ctx)
+ GTask *task)
{
+ MMBroadbandBearer *self;
+ Dial3gppContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* DO NOT check for cancellable here. If we got here without errors, the
* bearer is really connected and therefore we need to reflect that in
* the state machine. */
@@ -561,14 +494,34 @@ atd_ready (MMBaseModem *modem,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)extended_error_ready,
- ctx);
+ task);
return;
}
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_object_ref (ctx->dial_port),
- (GDestroyNotify)g_object_unref);
- dial_3gpp_context_complete_and_free (ctx);
+ /* Configure flow control to use while connected */
+ if (self->priv->flow_control != MM_FLOW_CONTROL_NONE) {
+ gchar *flow_control_str;
+ GError *error = NULL;
+
+ flow_control_str = mm_flow_control_build_string_from_mask (self->priv->flow_control);
+ mm_obj_dbg (self, "setting flow control in %s: %s", mm_port_get_device (MM_PORT (ctx->dial_port)), flow_control_str);
+ g_free (flow_control_str);
+
+ if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->dial_port), self->priv->flow_control, &error)) {
+ mm_obj_warn (self, "couldn't set flow control settings in %s: %s", mm_port_get_device (MM_PORT (ctx->dial_port)), error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ /* The ATD command has succeeded, and therefore the TTY is in data mode now.
+ * Instead of waiting for setting the port as connected later in
+ * connect_succeeded(), we do it right away so that we stop our polling. */
+ mm_port_set_connected (MM_PORT (ctx->dial_port), TRUE);
+
+ g_task_return_pointer (task,
+ g_object_ref (ctx->dial_port),
+ g_object_unref);
+ g_object_unref (task);
}
static void
@@ -582,26 +535,24 @@ dial_3gpp (MMBroadbandBearer *self,
{
gchar *command;
Dial3gppContext *ctx;
+ GTask *task;
GError *error = NULL;
g_assert (primary != NULL);
ctx = g_slice_new0 (Dial3gppContext);
- ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- dial_3gpp);
- ctx->cancellable = g_object_ref (cancellable);
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)dial_3gpp_context_free);
/* Grab dial port. This gets a reference to the dial port and OPENs it.
* If we fail, we'll need to close it ourselves. */
- ctx->dial_port = common_get_at_data_port (ctx->modem, &error);
+ ctx->dial_port = common_get_at_data_port (self, ctx->modem, &error);
if (!ctx->dial_port) {
- g_simple_async_result_take_error (ctx->result, error);
- dial_3gpp_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -610,16 +561,119 @@ dial_3gpp (MMBroadbandBearer *self,
mm_base_modem_at_command_full (ctx->modem,
ctx->dial_port,
command,
- 60,
+ MM_BASE_BEARER_DEFAULT_CONNECTION_TIMEOUT,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)atd_ready,
- ctx);
+ task);
g_free (command);
}
/*****************************************************************************/
+/* 3GPP cid selection (sub-step of the 3GPP Connection sequence) */
+
+typedef struct {
+ MMBaseModem *modem;
+ GCancellable *cancellable;
+ gint profile_id;
+} SelectProfile3gppContext;
+
+static void
+select_profile_3gpp_context_free (SelectProfile3gppContext *ctx)
+{
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->cancellable);
+ g_slice_free (SelectProfile3gppContext, ctx);
+}
+
+static gint
+select_profile_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gint profile_id;
+
+ /* returns -1 on failure */
+ profile_id = (gint) g_task_propagate_int (G_TASK (res), error);
+ return (profile_id < 0) ? MM_3GPP_PROFILE_ID_UNKNOWN : profile_id;
+}
+
+static void
+select_profile_3gpp_set_profile_ready (MMIfaceModem3gppProfileManager *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ profile = mm_iface_modem_3gpp_profile_manager_set_profile_finish (modem, res, &error);
+ if (!profile)
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, mm_3gpp_profile_get_profile_id (profile));
+ g_object_unref (task);
+}
+
+static void
+select_profile_3gpp_get_profile_ready (MMIfaceModem3gppProfileManager *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SelectProfile3gppContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (modem, res, &error);
+ if (!profile)
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, ctx->profile_id);
+ g_object_unref (task);
+}
+
+static void
+select_profile_3gpp (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ SelectProfile3gppContext *ctx;
+ MMBearerProperties *bearer_properties;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ ctx = g_slice_new0 (SelectProfile3gppContext);
+ ctx->modem = g_object_ref (modem);
+ ctx->cancellable = g_object_ref (cancellable);
+
+ bearer_properties = mm_base_bearer_peek_config (MM_BASE_BEARER (self));
+ ctx->profile_id = mm_bearer_properties_get_profile_id (bearer_properties);
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify)select_profile_3gpp_context_free);
+
+ if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_iface_modem_3gpp_profile_manager_set_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (modem),
+ mm_bearer_properties_peek_3gpp_profile (bearer_properties),
+ FALSE, /* not strict! */
+ (GAsyncReadyCallback)select_profile_3gpp_set_profile_ready,
+ task);
+ return;
+ }
+
+ mm_iface_modem_3gpp_profile_manager_get_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (modem),
+ ctx->profile_id,
+ (GAsyncReadyCallback)select_profile_3gpp_get_profile_ready,
+ task);
+}
+
+/*****************************************************************************/
/* 3GPP CONNECT
*
* 3GPP connection procedure of a bearer involves several steps:
@@ -635,21 +689,23 @@ dial_3gpp (MMBroadbandBearer *self,
*/
static void
-get_ip_config_3gpp_ready (MMBroadbandModem *modem,
- GAsyncResult *res,
- DetailedConnectContext *ctx)
-{
- MMBearerIpConfig *ipv4_config = NULL;
- MMBearerIpConfig *ipv6_config = NULL;
- GError *error = NULL;
-
- if (!MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp_finish (ctx->self,
- res,
- &ipv4_config,
- &ipv6_config,
- &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+get_ip_config_3gpp_ready (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DetailedConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MMBearerConnectResult) result = NULL;
+ g_autoptr(MMBearerIpConfig) ipv4_config = NULL;
+ g_autoptr(MMBearerIpConfig) ipv6_config = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp_finish (self, res,
+ &ipv4_config, &ipv6_config,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -657,34 +713,33 @@ get_ip_config_3gpp_ready (MMBroadbandModem *modem,
if (MM_IS_PORT_SERIAL_AT (ctx->data))
ctx->close_data_on_exit = FALSE;
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config),
- (GDestroyNotify)mm_bearer_connect_result_unref);
- detailed_connect_context_complete_and_free (ctx);
+ result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config);
+ mm_bearer_connect_result_set_profile_id (result, self->priv->profile_id);
- if (ipv4_config)
- g_object_unref (ipv4_config);
- if (ipv6_config)
- g_object_unref (ipv6_config);
+ g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
}
static void
-dial_3gpp_ready (MMBroadbandModem *modem,
- GAsyncResult *res,
- DetailedConnectContext *ctx)
+dial_3gpp_ready (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GTask *task)
{
- MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN;
- MMBearerIpConfig *ipv4_config = NULL;
- MMBearerIpConfig *ipv6_config = NULL;
- GError *error = NULL;
+ DetailedConnectContext *ctx;
+ MMBearerIpMethod ip_method = MM_BEARER_IP_METHOD_UNKNOWN;
+ GError *error = NULL;
+ g_autoptr(MMBearerConnectResult) result = NULL;
+ g_autoptr(MMBearerIpConfig) ipv4_config = NULL;
+ g_autoptr(MMBearerIpConfig) ipv6_config = NULL;
+
+ ctx = g_task_get_task_data (task);
- ctx->data = MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->dial_3gpp_finish (ctx->self, res, &error);
+ ctx->data = MM_BROADBAND_BEARER_GET_CLASS (self)->dial_3gpp_finish (self, res, &error);
if (!ctx->data) {
/* Clear CID when it failed to connect. */
- ctx->self->priv->cid = 0;
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN;
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -693,19 +748,19 @@ dial_3gpp_ready (MMBroadbandModem *modem,
if (MM_IS_PORT_SERIAL_AT (ctx->data))
ctx->close_data_on_exit = TRUE;
- if (MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp &&
- MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp_finish) {
+ if (MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp &&
+ MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp_finish) {
/* Launch specific IP config retrieval */
- MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->get_ip_config_3gpp (
- ctx->self,
+ MM_BROADBAND_BEARER_GET_CLASS (self)->get_ip_config_3gpp (
+ self,
MM_BROADBAND_MODEM (ctx->modem),
ctx->primary,
ctx->secondary,
ctx->data,
- ctx->cid,
+ (guint)self->priv->profile_id,
ctx->ip_family,
(GAsyncReadyCallback)get_ip_config_3gpp_ready,
- ctx);
+ task);
return;
}
@@ -733,408 +788,140 @@ dial_3gpp_ready (MMBroadbandModem *modem,
}
g_assert (ipv4_config || ipv6_config);
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config),
- (GDestroyNotify)mm_bearer_connect_result_unref);
- detailed_connect_context_complete_and_free (ctx);
-
- g_clear_object (&ipv4_config);
- g_clear_object (&ipv6_config);
+ result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config);
+ mm_bearer_connect_result_set_profile_id (result, self->priv->profile_id);
+ g_task_return_pointer (task, g_steal_pointer (&result), (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
}
static void
-start_3gpp_dial (DetailedConnectContext *ctx)
+select_profile_3gpp_ready (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GTask *task)
{
- /* Keep CID around after initializing the PDP context in order to
- * handle corresponding unsolicited PDP activation responses. */
- ctx->self->priv->cid = ctx->cid;
- MM_BROADBAND_BEARER_GET_CLASS (ctx->self)->dial_3gpp (ctx->self,
- ctx->modem,
- ctx->primary,
- ctx->cid,
- ctx->cancellable,
- (GAsyncReadyCallback)dial_3gpp_ready,
- ctx);
-}
+ DetailedConnectContext *ctx;
+ GError *error = NULL;
-static void
-initialize_pdp_context_ready (MMBaseModem *modem,
- GAsyncResult *res,
- DetailedConnectContext *ctx)
-{
- GError *error = NULL;
+ ctx = g_task_get_task_data (task);
- /* If cancelled, complete */
- if (detailed_connect_context_complete_and_free_if_cancelled (ctx))
- return;
-
- mm_base_modem_at_command_full_finish (modem, res, &error);
- if (error) {
- mm_warn ("Couldn't initialize PDP context with our APN: '%s'",
- error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
+ /* Keep CID around after initializing the PDP context in order to
+ * handle corresponding unsolicited PDP activation responses. */
+ self->priv->profile_id = select_profile_3gpp_finish (self, res, &error);
+ if (self->priv->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- start_3gpp_dial (ctx);
+ MM_BROADBAND_BEARER_GET_CLASS (self)->dial_3gpp (self,
+ ctx->modem,
+ ctx->primary,
+ (guint) self->priv->profile_id,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) dial_3gpp_ready,
+ task);
}
static void
-find_cid_ready (MMBaseModem *modem,
- GAsyncResult *res,
- DetailedConnectContext *ctx)
-{
- GVariant *result;
- gchar *apn, *command;
- GError *error = NULL;
- const gchar *pdp_type;
-
- result = mm_base_modem_at_sequence_full_finish (modem, res, NULL, &error);
- if (!result) {
- mm_warn ("Couldn't find best CID to use: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- detailed_connect_context_complete_and_free (ctx);
- return;
- }
-
- /* If cancelled, complete. Normally, we would get the cancellation error
- * already when finishing the sequence, but we may still get cancelled
- * between last command result parsing in the sequence and the ready(). */
- if (detailed_connect_context_complete_and_free_if_cancelled (ctx))
- return;
-
- pdp_type = mm_3gpp_get_pdp_type_from_ip_family (ctx->ip_family);
- if (!pdp_type) {
- gchar * str;
-
- str = mm_bearer_ip_family_build_string_from_mask (ctx->ip_family);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Unsupported IP type requested: '%s'",
- str);
- g_free (str);
- detailed_connect_context_complete_and_free (ctx);
- return;
- }
- ctx->cid = g_variant_get_uint32 (result);
-
- /* If there's already a PDP context defined, just use it */
- if (ctx->use_existing_cid) {
- start_3gpp_dial (ctx);
- return;
- }
-
- /* Otherwise, initialize a new PDP context with our APN */
- apn = mm_port_serial_at_quote_string (mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))));
- command = g_strdup_printf ("+CGDCONT=%u,\"%s\",%s",
- ctx->cid,
- pdp_type,
- apn);
- g_free (apn);
- mm_base_modem_at_command_full (ctx->modem,
- ctx->primary,
- command,
- 3,
- FALSE,
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)initialize_pdp_context_ready,
- ctx);
- g_free (command);
-}
-
-static gboolean
-parse_cid_range (MMBaseModem *modem,
- DetailedConnectContext *ctx,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
-{
- GError *inner_error = NULL;
- GList *formats, *l;
- guint cid;
-
- /* If cancelled, set result error */
- if (detailed_connect_context_set_error_if_cancelled (ctx, result_error))
- return FALSE;
-
- if (error) {
- mm_dbg ("Unexpected +CGDCONT error: '%s'", error->message);
- mm_dbg ("Defaulting to CID=1");
- *result = g_variant_new_uint32 (1);
- return TRUE;
- }
-
- formats = mm_3gpp_parse_cgdcont_test_response (response, &inner_error);
- if (inner_error) {
- mm_dbg ("Error parsing +CGDCONT test response: '%s'", inner_error->message);
- mm_dbg ("Defaulting to CID=1");
- g_error_free (inner_error);
- *result = g_variant_new_uint32 (1);
- return TRUE;
- }
-
- cid = 0;
- for (l = formats; l; l = g_list_next (l)) {
- MM3gppPdpContextFormat *format = l->data;
-
- /* Found exact PDP type? */
- if (format->pdp_type == ctx->ip_family) {
- if (ctx->max_cid < format->max_cid)
- cid = ctx->max_cid + 1;
- else
- cid = ctx->max_cid;
- break;
- }
- }
-
- mm_3gpp_pdp_context_format_list_free (formats);
-
- if (cid == 0) {
- mm_dbg ("Defaulting to CID=1");
- cid = 1;
- } else
- mm_dbg ("Using CID %u", cid);
-
- *result = g_variant_new_uint32 (cid);
- return TRUE;
-}
-
-static gboolean
-parse_pdp_list (MMBaseModem *modem,
- DetailedConnectContext *ctx,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+connect_3gpp (MMBroadbandBearer *self,
+ MMBroadbandModem *modem,
+ MMPortSerialAt *primary,
+ MMPortSerialAt *secondary,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GError *inner_error = NULL;
- GList *pdp_list;
- GList *l;
- guint cid;
-
- /* If cancelled, set result error */
- if (detailed_connect_context_set_error_if_cancelled (ctx, result_error))
- return FALSE;
-
- ctx->max_cid = 0;
-
- /* Some Android phones don't support querying existing PDP contexts,
- * but will accept setting the APN. So if CGDCONT? isn't supported,
- * just ignore that error and hope for the best. (bgo #637327)
- */
- if (g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) {
- mm_dbg ("Querying PDP context list is unsupported");
- return FALSE;
- }
-
- if (error) {
- mm_dbg ("Unexpected +CGDCONT? error: '%s'", error->message);
- return FALSE;
- }
-
- pdp_list = mm_3gpp_parse_cgdcont_read_response (response, &inner_error);
- if (!pdp_list) {
- if (inner_error) {
- mm_dbg ("%s", inner_error->message);
- g_error_free (inner_error);
- } else {
- /* No predefined PDP contexts found */
- mm_dbg ("No PDP contexts found");
- }
- return FALSE;
- }
-
- cid = 0;
-
- /* Show all found PDP contexts in debug log */
- mm_dbg ("Found '%u' PDP contexts", g_list_length (pdp_list));
- for (l = pdp_list; l; l = g_list_next (l)) {
- MM3gppPdpContext *pdp = l->data;
- gchar *ip_family_str;
-
- ip_family_str = mm_bearer_ip_family_build_string_from_mask (pdp->pdp_type);
- mm_dbg (" PDP context [cid=%u] [type='%s'] [apn='%s']",
- pdp->cid,
- ip_family_str,
- pdp->apn ? pdp->apn : "");
- g_free (ip_family_str);
- }
-
- /* Look for the exact PDP context we want */
- for (l = pdp_list; l; l = g_list_next (l)) {
- MM3gppPdpContext *pdp = l->data;
-
- if (pdp->pdp_type == ctx->ip_family) {
- /* PDP with no APN set? we may use that one if not exact match found */
- if (!pdp->apn || !pdp->apn[0]) {
- mm_dbg ("Found PDP context with CID %u and no APN",
- pdp->cid);
- cid = pdp->cid;
- } else {
- const gchar *apn;
-
- apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
- if (apn && g_str_equal (pdp->apn, apn)) {
- gchar *ip_family_str;
-
- /* Found a PDP context with the same CID and PDP type, we'll use it. */
- ip_family_str = mm_bearer_ip_family_build_string_from_mask (pdp->pdp_type);
- mm_dbg ("Found PDP context with CID %u and PDP type %s for APN '%s'",
- pdp->cid, ip_family_str, pdp->apn);
- cid = pdp->cid;
- ctx->use_existing_cid = TRUE;
- g_free (ip_family_str);
- /* In this case, stop searching */
- break;
- }
- }
- }
-
- if (ctx->max_cid < pdp->cid)
- ctx->max_cid = pdp->cid;
- }
- mm_3gpp_pdp_context_list_free (pdp_list);
-
- if (cid > 0) {
- *result = g_variant_new_uint32 (cid);
- return TRUE;
- }
+ DetailedConnectContext *ctx;
+ GTask *task;
- return FALSE;
-}
+ g_assert (primary != NULL);
-static const MMBaseModemAtCommand find_cid_sequence[] = {
- { "+CGDCONT?", 3, FALSE, (MMBaseModemAtResponseProcessor)parse_pdp_list },
- { "+CGDCONT=?", 3, TRUE, (MMBaseModemAtResponseProcessor)parse_cid_range },
- { NULL }
-};
+ /* Clear CID on every connection attempt */
+ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN;
-static void
-connect_3gpp (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
- MMPortSerialAt *primary,
- MMPortSerialAt *secondary,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- DetailedConnectContext *ctx;
+ ctx = detailed_connect_context_new (self, modem, primary, secondary);
- g_assert (primary != NULL);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_connect_context_free);
- ctx = detailed_connect_context_new (self,
- modem,
- primary,
- secondary,
- cancellable,
- callback,
- user_data);
-
- mm_dbg ("Looking for best CID...");
- mm_base_modem_at_sequence_full (ctx->modem,
- ctx->primary,
- find_cid_sequence,
- ctx, /* also passed as response processor context */
- NULL, /* response_processor_context_free */
- NULL, /* cancellable */
- (GAsyncReadyCallback)find_cid_ready,
- ctx);
+ select_profile_3gpp (self,
+ ctx->modem,
+ cancellable,
+ (GAsyncReadyCallback)select_profile_3gpp_ready,
+ task);
}
/*****************************************************************************/
/* CONNECT */
-typedef struct {
- MMBroadbandBearer *self;
- GSimpleAsyncResult *result;
-} ConnectContext;
-
-static void
-connect_context_complete_and_free (ConnectContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (ConnectContext, ctx);
-}
-
static MMBearerConnectResult *
connect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return mm_bearer_connect_result_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-connect_succeeded (ConnectContext *ctx,
+connect_succeeded (GTask *task,
ConnectionType connection_type,
MMBearerConnectResult *result)
{
+ MMBroadbandBearer *self;
+
+ self = g_task_get_source_object (task);
+
/* Keep connected port and type of connection */
- ctx->self->priv->port = g_object_ref (mm_bearer_connect_result_peek_data (result));
- ctx->self->priv->connection_type = connection_type;
+ self->priv->port = g_object_ref (mm_bearer_connect_result_peek_data (result));
+ self->priv->connection_type = connection_type;
- /* Port is connected; update the state */
- mm_port_set_connected (ctx->self->priv->port, TRUE);
+ /* Port is connected; update the state. For ATD based connections, the port
+ * may already be set as connected, but no big deal. */
+ mm_port_set_connected (self->priv->port, TRUE);
/* Set operation result */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- result,
- (GDestroyNotify)mm_bearer_connect_result_unref);
- connect_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ result,
+ (GDestroyNotify)mm_bearer_connect_result_unref);
+ g_object_unref (task);
}
static void
connect_cdma_ready (MMBroadbandBearer *self,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
MMBearerConnectResult *result;
GError *error = NULL;
result = MM_BROADBAND_BEARER_GET_CLASS (self)->connect_cdma_finish (self, res, &error);
if (!result) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* take result */
- connect_succeeded (ctx, CONNECTION_TYPE_CDMA, result);
+ connect_succeeded (task, CONNECTION_TYPE_CDMA, result);
}
static void
connect_3gpp_ready (MMBroadbandBearer *self,
GAsyncResult *res,
- ConnectContext *ctx)
+ GTask *task)
{
MMBearerConnectResult *result;
GError *error = NULL;
result = MM_BROADBAND_BEARER_GET_CLASS (self)->connect_3gpp_finish (self, res, &error);
if (!result) {
- g_simple_async_result_take_error (ctx->result, error);
- connect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* take result */
- connect_succeeded (ctx, CONNECTION_TYPE_3GPP, result);
+ connect_succeeded (task, CONNECTION_TYPE_3GPP, result);
}
static void
@@ -1143,20 +930,20 @@ connect (MMBaseBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MMBaseModem *modem = NULL;
- MMPortSerialAt *primary;
- ConnectContext *ctx;
- const gchar *apn;
+ MMPortSerialAt *primary;
+ const gchar *apn;
+ gint profile_id;
+ MMBearerMultiplexSupport multiplex;
+ GTask *task;
+ g_autoptr(MMBaseModem) modem = NULL;
+
+ task = g_task_new (self, cancellable, callback, user_data);
/* Don't try to connect if already connected */
if (MM_BROADBAND_BEARER (self)->priv->port) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CONNECTED,
- "Couldn't connect: this bearer is already connected");
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED,
+ "Couldn't connect: this bearer is already connected");
+ g_object_unref (task);
return;
}
@@ -1169,89 +956,79 @@ connect (MMBaseBearer *self,
/* We will launch the ATD call in the primary port... */
primary = mm_base_modem_peek_port_primary (modem);
if (!primary) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CONNECTED,
- "Couldn't connect: couldn't get primary port");
- g_object_unref (modem);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED,
+ "Couldn't connect: couldn't get primary port");
+ g_object_unref (task);
return;
}
/* ...only if not already connected */
if (mm_port_get_connected (MM_PORT (primary))) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CONNECTED,
- "Couldn't connect: primary AT port is already connected");
- g_object_unref (modem);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED,
+ "Couldn't connect: primary AT port is already connected");
+ g_object_unref (task);
return;
}
/* Default bearer connection logic
*
* 1) 3GPP-only modem:
- * 1a) If no APN given, error.
- * 1b) If APN given, run 3GPP connection logic.
+ * 1a) If no profile id or APN given, error.
+ * 1b) If APN or profile id given, run 3GPP connection logic.
* 1c) If APN given, but empty (""), run 3GPP connection logic and try
* to use default subscription APN.
*
* 2) 3GPP2-only modem:
- * 2a) If no APN given, run CDMA connection logic.
- * 2b) If APN given, error.
+ * 2a) If no APN or profile id given, run CDMA connection logic.
+ * 2b) If APN or profile id given, error.
*
* 3) 3GPP and 3GPP2 modem:
- * 3a) If no APN given, run CDMA connection logic.
+ * 3a) If no profile id or APN given, run CDMA connection logic.
* 3b) If APN given, run 3GPP connection logic.
- * 1c) If APN given, but empty (""), run 3GPP connection logic and try
+ * 3c) If APN given, but empty (""), run 3GPP connection logic and try
* to use default subscription APN.
*/
/* Check whether we have an APN */
apn = mm_bearer_properties_get_apn (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
-
- /* Is this a 3GPP only modem and no APN was given? If so, error */
- if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) && !apn) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "3GPP connection logic requires APN setting");
- g_object_unref (modem);
+ profile_id = mm_bearer_properties_get_profile_id (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+
+ /* Is this a 3GPP only modem and no APN or profile id was given? If so, error */
+ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (modem)) &&
+ !apn && (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "3GPP connection logic requires APN or profile id setting");
+ g_object_unref (task);
return;
}
- /* Is this a 3GPP2 only modem and APN was given? If so, error */
- if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) && apn) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "3GPP2 doesn't support APN setting");
- g_object_unref (modem);
+ /* Is this a 3GPP2 only modem and APN or profile id was given? If so, error */
+ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (modem)) &&
+ (apn || (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "3GPP2 doesn't support APN or profile id setting");
+ g_object_unref (task);
return;
}
- /* In this context, we only keep the stuff we'll need later */
- ctx = g_slice_new0 (ConnectContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- connect);
+ /* If no multiplex setting given by the user, assume none (which is the only
+ * supported mode anyway) */
+ multiplex = mm_bearer_properties_get_multiplex (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
+ if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN)
+ multiplex = MM_BEARER_MULTIPLEX_SUPPORT_NONE;
+
+ /* The generic broadband bearer doesn't support multiplexing */
+ if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Multiplexing required but not supported");
+ g_object_unref (task);
+ return;
+ }
/* If the modem has 3GPP capabilities and an APN, launch 3GPP-based connection */
- if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) && apn) {
- mm_dbg ("Launching 3GPP connection attempt with APN '%s'", apn);
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (modem)) &&
+ (apn || (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN))) {
+ mm_obj_dbg (self, "launching 3GPP connection attempt");
MM_BROADBAND_BEARER_GET_CLASS (self)->connect_3gpp (
MM_BROADBAND_BEARER (self),
MM_BROADBAND_MODEM (modem),
@@ -1259,14 +1036,14 @@ connect (MMBaseBearer *self,
mm_base_modem_peek_port_secondary (modem),
cancellable,
(GAsyncReadyCallback) connect_3gpp_ready,
- ctx);
- g_object_unref (modem);
+ task);
return;
}
/* Otherwise, launch CDMA-specific connection. */
- if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) && !apn) {
- mm_dbg ("Launching 3GPP2 connection attempt");
+ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (modem)) &&
+ !apn && (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)) {
+ mm_obj_dbg (self, "launching 3GPP2 connection attempt");
MM_BROADBAND_BEARER_GET_CLASS (self)->connect_cdma (
MM_BROADBAND_BEARER (self),
MM_BROADBAND_MODEM (modem),
@@ -1274,8 +1051,7 @@ connect (MMBaseBearer *self,
mm_base_modem_peek_port_secondary (modem),
cancellable,
(GAsyncReadyCallback) connect_cdma_ready,
- ctx);
- g_object_unref (modem);
+ task);
return;
}
@@ -1286,12 +1062,10 @@ connect (MMBaseBearer *self,
/* Detailed disconnect context, used in both CDMA and 3GPP sequences */
typedef struct {
- MMBroadbandBearer *self;
MMBaseModem *modem;
MMPortSerialAt *primary;
MMPortSerialAt *secondary;
MMPort *data;
- GSimpleAsyncResult *result;
/* 3GPP-specific */
gchar *cgact_command;
@@ -1303,46 +1077,35 @@ detailed_disconnect_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-detailed_disconnect_context_complete_and_free (DetailedDisconnectContext *ctx)
+detailed_disconnect_context_free (DetailedDisconnectContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->cgact_command)
- g_free (ctx->cgact_command);
- g_object_unref (ctx->result);
+ g_free (ctx->cgact_command);
g_object_unref (ctx->data);
g_object_unref (ctx->primary);
if (ctx->secondary)
g_object_unref (ctx->secondary);
- g_object_unref (ctx->self);
g_object_unref (ctx->modem);
g_free (ctx);
}
static DetailedDisconnectContext *
-detailed_disconnect_context_new (MMBroadbandBearer *self,
- MMBroadbandModem *modem,
+detailed_disconnect_context_new (MMBroadbandModem *modem,
MMPortSerialAt *primary,
MMPortSerialAt *secondary,
- MMPort *data,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ MMPort *data)
{
DetailedDisconnectContext *ctx;
ctx = g_new0 (DetailedDisconnectContext, 1);
- ctx->self = g_object_ref (self);
ctx->modem = MM_BASE_MODEM (g_object_ref (modem));
ctx->primary = g_object_ref (primary);
ctx->secondary = (secondary ? g_object_ref (secondary) : NULL);
ctx->data = g_object_ref (data);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- detailed_disconnect_context_new);
+
return ctx;
}
@@ -1352,9 +1115,12 @@ detailed_disconnect_context_new (MMBroadbandBearer *self,
static void
data_flash_cdma_ready (MMPortSerial *data,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearer *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
mm_port_serial_flash_finish (data, res, &error);
@@ -1376,40 +1142,50 @@ data_flash_cdma_ready (MMPortSerial *data,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_FLASH_FAILED)) {
/* Fatal */
- g_simple_async_result_take_error (ctx->result, error);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("Port flashing failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "port flashing failed (not fatal): %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- detailed_disconnect_context_complete_and_free (ctx);
+ /* Run init port sequence in the data port */
+ mm_port_serial_at_run_init_sequence (MM_PORT_SERIAL_AT (data));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
data_reopen_cdma_ready (MMPortSerial *data,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearer *self;
+ DetailedDisconnectContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL);
if (!mm_port_serial_reopen_finish (data, res, &error)) {
/* Fatal */
- g_simple_async_result_take_error (ctx->result, error);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Just flash the data port */
- mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
+ mm_obj_dbg (self, "flashing data port %s...", mm_port_get_device (MM_PORT (ctx->data)));
mm_port_serial_flash (MM_PORT_SERIAL (ctx->data),
1000,
TRUE,
(GAsyncReadyCallback)data_flash_cdma_ready,
- ctx);
+ task);
}
static void
@@ -1422,56 +1198,65 @@ disconnect_cdma (MMBroadbandBearer *self,
gpointer user_data)
{
DetailedDisconnectContext *ctx;
+ GTask *task;
g_assert (primary != NULL);
/* Generic CDMA plays only with SERIAL data ports */
g_assert (MM_IS_PORT_SERIAL (data));
- ctx = detailed_disconnect_context_new (self,
- modem,
- primary,
- secondary,
- data,
- callback,
- user_data);
+ ctx = detailed_disconnect_context_new (modem, primary, secondary, data);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free);
+
+ /* We don't want to run init sequence right away during the reopen, as we're
+ * going to flash afterwards. */
+ g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL);
/* Fully reopen the port before flashing */
- mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
+ mm_obj_dbg (self, "reopening data port %s...", mm_port_get_device (MM_PORT (ctx->data)));
mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data),
1000,
(GAsyncReadyCallback)data_reopen_cdma_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* 3GPP disconnect */
static void
-cgact_data_ready (MMBaseModem *modem,
+cgact_data_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
+ MMBroadbandBearer *self;
+ GError *error = NULL;
- GError *error = NULL;
+ self = g_task_get_source_object (task);
/* Ignore errors for now */
mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
- mm_dbg ("PDP context deactivation failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "PDP context deactivation failed (not fatal): %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
data_flash_3gpp_ready (MMPortSerial *data,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearer *self;
+ DetailedDisconnectContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
mm_port_serial_flash_finish (data, res, &error);
@@ -1493,87 +1278,120 @@ data_flash_3gpp_ready (MMPortSerial *data,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_FLASH_FAILED)) {
/* Fatal */
- g_simple_async_result_take_error (ctx->result, error);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("Port flashing failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "port flashing failed (not fatal): %s", error->message);
g_error_free (error);
}
+ /* Run init port sequence in the data port */
+ mm_port_serial_at_run_init_sequence (MM_PORT_SERIAL_AT (data));
+
/* Don't bother doing the CGACT again if it was already done on the
* primary or secondary port */
if (ctx->cgact_sent) {
- mm_dbg ("PDP disconnection already sent");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- detailed_disconnect_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "PDP disconnection already sent");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- /* Last resort, try to send CGACT in the data port itself */
- mm_dbg ("Sending PDP context deactivation in data port...");
+ /* Send another CGACT on the primary port (also the data port when the modem
+ * only has one serial port) if the previous one failed. Some modems, like
+ * the Huawei E173 (fw 11.126.15.00.445) stop responding on their primary
+ * port when the CGACT is sent on the separte data port.
+ */
+ if (MM_PORT_SERIAL (ctx->primary) == data)
+ mm_obj_dbg (self, "sending PDP context deactivation in primary/data port...");
+ else
+ mm_obj_dbg (self, "sending PDP context deactivation in primary port again...");
+
mm_base_modem_at_command_full (ctx->modem,
- MM_PORT_SERIAL_AT (data),
+ ctx->primary,
ctx->cgact_command,
10,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)cgact_data_ready,
- ctx);
+ task);
}
static void
data_reopen_3gpp_ready (MMPortSerial *data,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearer *self;
+ DetailedDisconnectContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_object_set (data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE, NULL);
if (!mm_port_serial_reopen_finish (data, res, &error)) {
/* Fatal */
- g_simple_async_result_take_error (ctx->result, error);
- detailed_disconnect_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Just flash the data port */
- mm_dbg ("Flashing data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
+ mm_obj_dbg (self, "flashing data port %s...", mm_port_get_device (MM_PORT (ctx->data)));
mm_port_serial_flash (MM_PORT_SERIAL (ctx->data),
1000,
TRUE,
(GAsyncReadyCallback)data_flash_3gpp_ready,
- ctx);
+ task);
}
static void
-data_reopen_3gpp (DetailedDisconnectContext *ctx)
+data_reopen_3gpp (GTask *task)
{
+ MMBroadbandBearer *self;
+ DetailedDisconnectContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* We don't want to run init sequence right away during the reopen, as we're
+ * going to flash afterwards. */
+ g_object_set (ctx->data, MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE, NULL);
+
/* Fully reopen the port before flashing */
- mm_dbg ("Reopening data port (%s)...", mm_port_get_device (MM_PORT (ctx->data)));
+ mm_obj_dbg (self, "reopening data port %s...", mm_port_get_device (MM_PORT (ctx->data)));
mm_port_serial_reopen (MM_PORT_SERIAL (ctx->data),
1000,
(GAsyncReadyCallback)data_reopen_3gpp_ready,
- ctx);
+ task);
}
static void
-cgact_ready (MMBaseModem *modem,
+cgact_ready (MMBaseModem *modem,
GAsyncResult *res,
- DetailedDisconnectContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandBearer *self;
+ DetailedDisconnectContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
mm_base_modem_at_command_full_finish (modem, res, &error);
if (!error)
ctx->cgact_sent = TRUE;
else {
- mm_dbg ("PDP context deactivation failed (not fatal): %s", error->message);
+ mm_obj_dbg (self, "PDP context deactivation failed (not fatal): %s", error->message);
g_error_free (error);
}
- data_reopen_3gpp (ctx);
+ data_reopen_3gpp (task);
}
static void
@@ -1587,38 +1405,36 @@ disconnect_3gpp (MMBroadbandBearer *self,
gpointer user_data)
{
DetailedDisconnectContext *ctx;
+ GTask *task;
g_assert (primary != NULL);
/* Generic 3GPP plays only with SERIAL data ports */
g_assert (MM_IS_PORT_SERIAL (data));
- ctx = detailed_disconnect_context_new (self,
- modem,
- primary,
- secondary,
- data,
- callback,
- user_data);
+ ctx = detailed_disconnect_context_new (modem, primary, secondary, data);
/* If no specific CID was used, disable all PDP contexts */
ctx->cgact_command = (cid > 0 ?
g_strdup_printf ("+CGACT=0,%d", cid) :
g_strdup_printf ("+CGACT=0"));
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_disconnect_context_free);
+
/* If the primary port is NOT connected (doesn't have to be the data port),
* we'll send CGACT there */
if (!mm_port_get_connected (MM_PORT (ctx->primary))) {
- mm_dbg ("Sending PDP context deactivation in primary port...");
+ mm_obj_dbg (self, "sending PDP context deactivation in primary port...");
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
ctx->cgact_command,
- 10,
+ 45,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)cgact_ready,
- ctx);
+ task);
return;
}
@@ -1628,48 +1444,32 @@ disconnect_3gpp (MMBroadbandBearer *self,
* driver doesn't support it).
*/
if (ctx->secondary) {
- mm_dbg ("Sending PDP context deactivation in secondary port...");
+ mm_obj_dbg (self, "sending PDP context deactivation in secondary port...");
mm_base_modem_at_command_full (ctx->modem,
ctx->secondary,
ctx->cgact_command,
- 10,
+ 45,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)cgact_ready,
- ctx);
+ task);
return;
}
/* If no secondary port, go on to reopen & flash the data/primary port */
- data_reopen_3gpp (ctx);
+ data_reopen_3gpp (task);
}
/*****************************************************************************/
/* DISCONNECT */
-typedef struct {
- MMBroadbandBearer *self;
- GSimpleAsyncResult *result;
- MMPort *data;
-} DisconnectContext;
-
-static void
-disconnect_context_complete_and_free (DisconnectContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->data);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
disconnect_finish (MMBaseBearer *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -1689,56 +1489,46 @@ reset_bearer_connection (MMBroadbandBearer *self)
}
static void
-disconnect_succeeded (DisconnectContext *ctx)
-{
- /* Cleanup all connection related data */
- reset_bearer_connection (ctx->self);
-
- /* Set operation result */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disconnect_context_complete_and_free (ctx);
-}
-
-static void
-disconnect_failed (DisconnectContext *ctx,
- GError *error)
-{
- g_simple_async_result_take_error (ctx->result, error);
- disconnect_context_complete_and_free (ctx);
-}
-
-static void
disconnect_cdma_ready (MMBroadbandBearer *self,
GAsyncResult *res,
- DisconnectContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_cdma_finish (self,
res,
&error))
- disconnect_failed (ctx, error);
- else
- disconnect_succeeded (ctx);
+ g_task_return_error (task, error);
+ else {
+ /* Cleanup all connection related data */
+ reset_bearer_connection (self);
+
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
}
static void
disconnect_3gpp_ready (MMBroadbandBearer *self,
GAsyncResult *res,
- DisconnectContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp_finish (self,
res,
&error))
- disconnect_failed (ctx, error);
+ g_task_return_error (task, error);
else {
/* Clear CID if we got any set */
- if (ctx->self->priv->cid)
- ctx->self->priv->cid = 0;
- disconnect_succeeded (ctx);
+ self->priv->profile_id = MM_3GPP_PROFILE_ID_UNKNOWN;
+
+ /* Cleanup all connection related data */
+ reset_bearer_connection (self);
+
+ g_task_return_boolean (task, TRUE);
}
+ g_object_unref (task);
}
static void
@@ -1748,16 +1538,14 @@ disconnect (MMBaseBearer *self,
{
MMPortSerialAt *primary;
MMBaseModem *modem = NULL;
- DisconnectContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
if (!MM_BROADBAND_BEARER (self)->priv->port) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't disconnect: this bearer is not connected");
+ mm_obj_dbg (self, "no need to disconnect: bearer is already disconnected");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -1769,26 +1557,15 @@ disconnect (MMBaseBearer *self,
/* We need the primary port to disconnect... */
primary = mm_base_modem_peek_port_primary (modem);
if (!primary) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't disconnect: couldn't get primary port");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't disconnect: couldn't get primary port");
+ g_object_unref (task);
g_object_unref (modem);
return;
}
- /* In this context, we only keep the stuff we'll need later */
- ctx = g_new0 (DisconnectContext, 1);
- ctx->self = g_object_ref (self);
- ctx->data = g_object_ref (MM_BROADBAND_BEARER (self)->priv->port);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disconnect);
-
switch (MM_BROADBAND_BEARER (self)->priv->connection_type) {
case CONNECTION_TYPE_3GPP:
MM_BROADBAND_BEARER_GET_CLASS (self)->disconnect_3gpp (
@@ -1797,9 +1574,9 @@ disconnect (MMBaseBearer *self,
primary,
mm_base_modem_peek_port_secondary (modem),
MM_BROADBAND_BEARER (self)->priv->port,
- MM_BROADBAND_BEARER (self)->priv->cid,
+ (guint)MM_BROADBAND_BEARER (self)->priv->profile_id,
(GAsyncReadyCallback) disconnect_3gpp_ready,
- ctx);
+ task);
break;
case CONNECTION_TYPE_CDMA:
@@ -1810,10 +1587,11 @@ disconnect (MMBaseBearer *self,
mm_base_modem_peek_port_secondary (modem),
MM_BROADBAND_BEARER (self)->priv->port,
(GAsyncReadyCallback) disconnect_cdma_ready,
- ctx);
+ task);
break;
case CONNECTION_TYPE_NONE:
+ default:
g_assert_not_reached ();
}
@@ -1821,10 +1599,132 @@ disconnect (MMBaseBearer *self,
}
/*****************************************************************************/
+/* Connection status monitoring */
+
+static MMBearerConnectionStatus
+load_connection_status_finish (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+ }
+ return (MMBearerConnectionStatus)value;
+}
+
+static void
+cgact_periodic_query_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandBearer *self;
+ const gchar *response;
+ GError *error = NULL;
+ GList *pdp_active_list = NULL;
+ GList *l;
+ MMBearerConnectionStatus status = MM_BEARER_CONNECTION_STATUS_UNKNOWN;
+
+ self = MM_BROADBAND_BEARER (g_task_get_source_object (task));
+
+ response = mm_base_modem_at_command_finish (modem, res, &error);
+ if (response)
+ pdp_active_list = mm_3gpp_parse_cgact_read_response (response, &error);
+
+ if (error) {
+ g_assert (!pdp_active_list);
+ g_prefix_error (&error, "Couldn't check current list of active PDP contexts: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (l = pdp_active_list; l; l = g_list_next (l)) {
+ MM3gppPdpContextActive *pdp_active;
+
+ /* We look for he just assume the first active PDP context found is the one we're
+ * looking for. */
+ pdp_active = (MM3gppPdpContextActive *)(l->data);
+ if (pdp_active->cid == (guint)self->priv->profile_id) {
+ status = (pdp_active->active ? MM_BEARER_CONNECTION_STATUS_CONNECTED : MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ break;
+ }
+ }
+ mm_3gpp_pdp_context_active_list_free (pdp_active_list);
+
+ /* PDP context not found? This shouldn't happen, error out */
+ if (status == MM_BEARER_CONNECTION_STATUS_UNKNOWN)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "PDP context not found in the known contexts list");
+ else
+ g_task_return_int (task, (gssize) status);
+ g_object_unref (task);
+}
static void
-report_connection_status (MMBaseBearer *self,
- MMBearerConnectionStatus status)
+load_connection_status (MMBaseBearer *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMBaseModem *modem = NULL;
+ MMPortSerialAt *port;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_object_get (MM_BASE_BEARER (self),
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+
+ /* No connection status checks on CDMA-only */
+ if (MM_BROADBAND_BEARER (self)->priv->connection_type == CONNECTION_TYPE_CDMA) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't load connection status: unsupported in CDMA");
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* If CID not defined, error out */
+ if (MM_BROADBAND_BEARER (self)->priv->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't load connection status: cid not defined");
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* If no control port available, error out */
+ port = mm_base_modem_peek_best_at_port (modem, NULL);
+ if (!port) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Couldn't load connection status: no control port available");
+ g_object_unref (task);
+ goto out;
+ }
+
+ mm_base_modem_at_command_full (MM_BASE_MODEM (modem),
+ port,
+ "+CGACT?",
+ 3,
+ FALSE, /* allow cached */
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback) cgact_periodic_query_ready,
+ task);
+
+out:
+ g_clear_object (&modem);
+}
+
+/*****************************************************************************/
+
+static void
+report_connection_status (MMBaseBearer *self,
+ MMBearerConnectionStatus status,
+ const GError *connection_error)
{
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED)
/* Cleanup all connection related data */
@@ -1833,13 +1733,14 @@ report_connection_status (MMBaseBearer *self,
/* Chain up parent's report_connection_status() */
MM_BASE_BEARER_CLASS (mm_broadband_bearer_parent_class)->report_connection_status (
self,
- status);
+ status,
+ connection_error);
}
/*****************************************************************************/
typedef struct _InitAsyncContext InitAsyncContext;
-static void interface_initialization_step (InitAsyncContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
@@ -1848,28 +1749,19 @@ typedef enum {
} InitializationStep;
struct _InitAsyncContext {
- MMBroadbandBearer *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MMBaseModem *modem;
InitializationStep step;
MMPortSerialAt *port;
};
static void
-init_async_context_free (InitAsyncContext *ctx,
- gboolean close_port)
+init_async_context_free (InitAsyncContext *ctx)
{
if (ctx->port) {
- if (close_port)
- mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
g_object_unref (ctx->port);
}
- g_object_unref (ctx->self);
g_object_unref (ctx->modem);
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
g_free (ctx);
}
@@ -1898,22 +1790,26 @@ initable_init_finish (GAsyncInitable *initable,
GAsyncResult *result,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_boolean (G_TASK (result), error);
}
static void
crm_range_ready (MMBaseModem *modem,
GAsyncResult *res,
- InitAsyncContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self;
+ InitAsyncContext *ctx;
GError *error = NULL;
const gchar *response;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_full_finish (modem, res, &error);
if (error) {
/* We should possibly take this error as fatal. If we were told to use a
* specific Rm protocol, we must be able to check if it is supported. */
- g_simple_async_result_take_error (ctx->result, error);
} else {
MMModemCdmaRmProtocol min = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
MMModemCdmaRmProtocol max = MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN;
@@ -1923,13 +1819,14 @@ crm_range_ready (MMBaseModem *modem,
&error)) {
MMModemCdmaRmProtocol current;
- current = mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self)));
+ current = mm_bearer_properties_get_rm_protocol (mm_base_bearer_peek_config (MM_BASE_BEARER (self)));
/* Check if value within the range */
if (current >= min &&
current <= max) {
/* Fine, go on with next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
+ return;
}
g_assert (error == NULL);
@@ -1938,29 +1835,33 @@ crm_range_ready (MMBaseModem *modem,
"Requested RM protocol '%s' is not supported",
mm_modem_cdma_rm_protocol_get_string (current));
}
-
/* Failed, set as fatal as well */
- g_simple_async_result_take_error (ctx->result, error);
}
- g_simple_async_result_complete (ctx->result);
- init_async_context_free (ctx, TRUE);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
static void
-interface_initialization_step (InitAsyncContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMBroadbandModem *self;
+ InitAsyncContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_CDMA_RM_PROTOCOL:
/* If a specific RM protocol is given, we need to check whether it is
* supported. */
if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->modem)) &&
mm_bearer_properties_get_rm_protocol (
- mm_base_bearer_peek_config (MM_BASE_BEARER (ctx->self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
+ mm_base_bearer_peek_config (MM_BASE_BEARER (self))) != MM_MODEM_CDMA_RM_PROTOCOL_UNKNOWN) {
mm_base_modem_at_command_full (ctx->modem,
ctx->port,
"+CRM=?",
@@ -1969,19 +1870,21 @@ interface_initialization_step (InitAsyncContext *ctx)
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)crm_range_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* Fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- g_simple_async_result_complete_in_idle (ctx->result);
- init_async_context_free (ctx, TRUE);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
g_assert_not_reached ();
@@ -1995,62 +1898,103 @@ initable_init_async (GAsyncInitable *initable,
gpointer user_data)
{
InitAsyncContext *ctx;
+ GTask *task;
GError *error = NULL;
ctx = g_new0 (InitAsyncContext, 1);
- ctx->self = g_object_ref (initable);
- ctx->result = g_simple_async_result_new (G_OBJECT (initable),
- callback,
- user_data,
- initable_init_async);
- ctx->cancellable = (cancellable ?
- g_object_ref (cancellable) :
- NULL);
-
g_object_get (initable,
MM_BASE_BEARER_MODEM, &ctx->modem,
NULL);
+ task = g_task_new (MM_BROADBAND_BEARER (initable),
+ cancellable,
+ callback,
+ user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)init_async_context_free);
+
ctx->port = mm_base_modem_get_port_primary (ctx->modem);
if (!ctx->port) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get primary port");
- g_simple_async_result_complete_in_idle (ctx->result);
- init_async_context_free (ctx, FALSE);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get primary port");
+ g_object_unref (task);
return;
}
if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- g_simple_async_result_complete_in_idle (ctx->result);
- init_async_context_free (ctx, FALSE);
+ g_clear_object (&ctx->port);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
void
mm_broadband_bearer_new (MMBroadbandModem *modem,
- MMBearerProperties *properties,
+ MMBearerProperties *bearer_properties,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ MMFlowControl flow_control;
+
+ /* Inherit flow control from modem object directly */
+ g_object_get (modem,
+ MM_BROADBAND_MODEM_FLOW_CONTROL, &flow_control,
+ NULL);
+
g_async_initable_new_async (
MM_TYPE_BROADBAND_BEARER,
G_PRIORITY_DEFAULT,
cancellable,
callback,
user_data,
- MM_BASE_BEARER_MODEM, modem,
- MM_BASE_BEARER_CONFIG, properties,
+ MM_BASE_BEARER_MODEM, modem,
+ MM_BASE_BEARER_CONFIG, bearer_properties,
+ MM_BROADBAND_BEARER_FLOW_CONTROL, flow_control,
NULL);
}
static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandBearer *self = MM_BROADBAND_BEARER (object);
+
+ switch (prop_id) {
+ case PROP_FLOW_CONTROL:
+ self->priv->flow_control = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandBearer *self = MM_BROADBAND_BEARER (object);
+
+ switch (prop_id) {
+ case PROP_FLOW_CONTROL:
+ g_value_set_flags (value, self->priv->flow_control);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
mm_broadband_bearer_init (MMBroadbandBearer *self)
{
/* Initialize private data */
@@ -2060,6 +2004,7 @@ mm_broadband_bearer_init (MMBroadbandBearer *self)
/* Set defaults */
self->priv->connection_type = CONNECTION_TYPE_NONE;
+ self->priv->flow_control = MM_FLOW_CONTROL_NONE;
}
static void
@@ -2087,14 +2032,21 @@ mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerPrivate));
- /* Virtual methods */
- object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
base_bearer_class->connect = connect;
base_bearer_class->connect_finish = connect_finish;
base_bearer_class->disconnect = disconnect;
base_bearer_class->disconnect_finish = disconnect_finish;
base_bearer_class->report_connection_status = report_connection_status;
+ base_bearer_class->load_connection_status = load_connection_status;
+ base_bearer_class->load_connection_status_finish = load_connection_status_finish;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_bearer_class->reload_connection_status = load_connection_status;
+ base_bearer_class->reload_connection_status_finish = load_connection_status_finish;
+#endif
klass->connect_3gpp = connect_3gpp;
klass->connect_3gpp_finish = detailed_connect_finish;
@@ -2108,4 +2060,13 @@ mm_broadband_bearer_class_init (MMBroadbandBearerClass *klass)
klass->disconnect_3gpp_finish = detailed_disconnect_finish;
klass->disconnect_cdma = disconnect_cdma;
klass->disconnect_cdma_finish = detailed_disconnect_finish;
+
+ properties[PROP_FLOW_CONTROL] =
+ g_param_spec_flags (MM_BROADBAND_BEARER_FLOW_CONTROL,
+ "Flow control",
+ "Flow control settings to use during connection",
+ MM_TYPE_FLOW_CONTROL,
+ MM_FLOW_CONTROL_NONE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_FLOW_CONTROL, properties[PROP_FLOW_CONTROL]);
}
diff --git a/src/mm-broadband-bearer.h b/src/mm-broadband-bearer.h
index a3e9b165..5d548a1f 100644
--- a/src/mm-broadband-bearer.h
+++ b/src/mm-broadband-bearer.h
@@ -25,6 +25,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-modem-helpers.h"
#include "mm-base-bearer.h"
#include "mm-broadband-modem.h"
@@ -39,6 +40,8 @@ typedef struct _MMBroadbandBearer MMBroadbandBearer;
typedef struct _MMBroadbandBearerClass MMBroadbandBearerClass;
typedef struct _MMBroadbandBearerPrivate MMBroadbandBearerPrivate;
+#define MM_BROADBAND_BEARER_FLOW_CONTROL "broadband-bearer-flow-control"
+
struct _MMBroadbandBearer {
MMBaseBearer parent;
MMBroadbandBearerPrivate *priv;
@@ -47,7 +50,7 @@ struct _MMBroadbandBearer {
struct _MMBroadbandBearerClass {
MMBaseBearerClass parent;
- /* Full 3GPP connection sequence */
+ /* Full 3GPP connection sequence (cid selection, dial, get ip config) */
void (* connect_3gpp) (MMBroadbandBearer *self,
MMBroadbandModem *modem,
MMPortSerialAt *primary,
@@ -59,6 +62,17 @@ struct _MMBroadbandBearerClass {
GAsyncResult *res,
GError **error);
+ /* CID selection sub-part of 3GPP connection */
+ void (* cid_selection_3gpp) (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMPortSerialAt *primary,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ guint (* cid_selection_3gpp_finish) (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Dialing sub-part of 3GPP connection */
void (* dial_3gpp) (MMBroadbandBearer *self,
MMBaseModem *modem,
@@ -127,6 +141,7 @@ struct _MMBroadbandBearerClass {
};
GType mm_broadband_bearer_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandBearer, g_object_unref)
/* Default 3GPP bearer creation implementation */
void mm_broadband_bearer_new (MMBroadbandModem *modem,
@@ -137,6 +152,4 @@ void mm_broadband_bearer_new (MMBroadbandModem *modem,
MMBaseBearer *mm_broadband_bearer_new_finish (GAsyncResult *res,
GError **error);
-guint mm_broadband_bearer_get_3gpp_cid (MMBroadbandBearer *self);
-
#endif /* MM_BROADBAND_BEARER_H */
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index 37dd0a21..f7ed6cdd 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -28,24 +28,56 @@
#include "mm-sms-mbim.h"
#include "ModemManager.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-errors-types.h"
#include "mm-error-helpers.h"
#include "mm-modem-helpers.h"
#include "mm-bearer-list.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
+#include "mm-iface-modem-3gpp-ussd.h"
+#include "mm-iface-modem-location.h"
#include "mm-iface-modem-messaging.h"
+#include "mm-iface-modem-signal.h"
+#include "mm-iface-modem-sar.h"
#include "mm-sms-part-3gpp.h"
-static void iface_modem_init (MMIfaceModem *iface);
-static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
-static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+# include <libqmi-glib.h>
+# include "mm-shared-qmi.h"
+#endif
+
+static void iface_modem_init (MMIfaceModem *iface);
+static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
+static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
+static void iface_modem_location_init (MMIfaceModemLocation *iface);
+static void iface_modem_messaging_init (MMIfaceModemMessaging *iface);
+static void iface_modem_signal_init (MMIfaceModemSignal *iface);
+static void iface_modem_sar_init (MMIfaceModemSar *iface);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+static void shared_qmi_init (MMSharedQmi *iface);
+#endif
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+static MMIfaceModemLocation *iface_modem_location_parent;
+#endif
+static MMIfaceModemSignal *iface_modem_signal_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbim, mm_broadband_modem_mbim, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
- G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init))
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
+ 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_SIGNAL, iface_modem_signal_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SAR, iface_modem_sar_init)
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QMI, shared_qmi_init)
+#endif
+)
typedef enum {
PROCESS_NOTIFICATION_FLAG_NONE = 0,
@@ -55,29 +87,83 @@ typedef enum {
PROCESS_NOTIFICATION_FLAG_CONNECT = 1 << 3,
PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO = 1 << 4,
PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE = 1 << 5,
+ PROCESS_NOTIFICATION_FLAG_PCO = 1 << 6,
+ PROCESS_NOTIFICATION_FLAG_USSD = 1 << 7,
+ PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO = 1 << 8,
+ PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS = 1 << 9,
+ PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS = 1 << 10,
} ProcessNotificationFlag;
+enum {
+ PROP_0,
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ PROP_QMI_UNSUPPORTED,
+#endif
+ PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED,
+ PROP_LAST
+};
+
struct _MMBroadbandModemMbimPrivate {
/* Queried and cached capabilities */
MbimCellularClass caps_cellular_class;
MbimDataClass caps_data_class;
+ gchar *caps_custom_data_class;
MbimSmsCaps caps_sms;
guint caps_max_sessions;
gchar *caps_device_id;
gchar *caps_firmware_info;
+ gchar *caps_hardware_info;
+
+ /* Supported features */
+ gboolean is_profile_management_supported;
+ gboolean is_pco_supported;
+ gboolean is_lte_attach_info_supported;
+ gboolean is_ussd_supported;
+ gboolean is_atds_location_supported;
+ gboolean is_atds_signal_supported;
+ gboolean is_intel_reset_supported;
+ gboolean is_slot_info_status_supported;
+ gboolean is_ms_sar_supported;
/* Process unsolicited notifications */
guint notification_id;
ProcessNotificationFlag setup_flags;
ProcessNotificationFlag enable_flags;
+ GList *pco_list;
+
/* 3GPP registration helpers */
- gchar *current_operator_id;
- gchar *current_operator_name;
+ gchar *current_operator_id;
+ gchar *current_operator_name;
+ gchar *requested_operator_id;
+ MbimDataClass requested_data_class; /* 0 for defaults/auto */
+ GTask *pending_allowed_modes_action;
- /* Access technology updates */
+ /* USSD helpers */
+ GTask *pending_ussd_action;
+
+ /* Access technology and registration updates */
MbimDataClass available_data_classes;
MbimDataClass highest_available_data_class;
+ MbimRegisterState reg_state;
+ MbimPacketServiceState packet_service_state;
+
+ MbimSubscriberReadyState last_ready_state;
+
+ /* For notifying when the mbim-proxy connection is dead */
+ gulong mbim_device_removed_id;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ gboolean qmi_unsupported;
+ /* Flag when QMI-based capability/mode switching is in use */
+ gboolean qmi_capability_and_mode_switching;
+#endif
+
+ gboolean intel_firmware_update_unsupported;
+
+ /* Multi-SIM support */
+ guint32 executor_index;
+ guint active_slot_index;
};
/*****************************************************************************/
@@ -90,14 +176,15 @@ peek_device (gpointer self,
{
MMPortMbim *port;
- port = mm_base_modem_peek_port_mbim (MM_BASE_MODEM (self));
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek MBIM port");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ peek_device,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek MBIM port");
return FALSE;
}
@@ -105,21 +192,190 @@ peek_device (gpointer self,
return TRUE;
}
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static QmiClient *
+shared_qmi_peek_client (MMSharedQmi *self,
+ QmiService service,
+ MMPortQmiFlag flag,
+ GError **error)
+{
+ MMPortMbim *port;
+ QmiClient *client;
+
+ g_assert (flag == MM_PORT_QMI_FLAG_DEFAULT);
+
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
+ if (!port) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek MBIM port");
+ return NULL;
+ }
+
+ if (!mm_port_mbim_supports_qmi (port)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported");
+ return NULL;
+ }
+
+ client = mm_port_mbim_peek_qmi_client (port, service);
+ if (!client)
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek client for service '%s'",
+ qmi_service_get_string (service));
+
+ return client;
+}
+
+#endif
+
+/*****************************************************************************/
+
+MMPortMbim *
+mm_broadband_modem_mbim_get_port_mbim (MMBroadbandModemMbim *self)
+{
+ MMPortMbim *primary_mbim_port;
+
+ g_assert (MM_IS_BROADBAND_MODEM_MBIM (self));
+
+ primary_mbim_port = mm_broadband_modem_mbim_peek_port_mbim (self);
+ return (primary_mbim_port ?
+ MM_PORT_MBIM (g_object_ref (primary_mbim_port)) :
+ NULL);
+}
+
+MMPortMbim *
+mm_broadband_modem_mbim_peek_port_mbim (MMBroadbandModemMbim *self)
+{
+ MMPortMbim *primary_mbim_port = NULL;
+ GList *mbim_ports;
+
+ g_assert (MM_IS_BROADBAND_MODEM_MBIM (self));
+
+ mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_UNKNOWN,
+ MM_PORT_TYPE_MBIM);
+
+ /* First MBIM port in the list is the primary one always */
+ if (mbim_ports)
+ primary_mbim_port = MM_PORT_MBIM (mbim_ports->data);
+
+ g_list_free_full (mbim_ports, g_object_unref);
+
+ return primary_mbim_port;
+}
+
+MMPortMbim *
+mm_broadband_modem_mbim_get_port_mbim_for_data (MMBroadbandModemMbim *self,
+ MMPort *data,
+ GError **error)
+{
+ MMPortMbim *mbim_port;
+
+ g_assert (MM_IS_BROADBAND_MODEM_MBIM (self));
+
+ mbim_port = mm_broadband_modem_mbim_peek_port_mbim_for_data (self, data, error);
+ return (mbim_port ?
+ MM_PORT_MBIM (g_object_ref (mbim_port)) :
+ NULL);
+}
+
+static MMPortMbim *
+peek_port_mbim_for_data (MMBroadbandModemMbim *self,
+ MMPort *data,
+ GError **error)
+{
+ GList *cdc_wdm_mbim_ports;
+ GList *l;
+ const gchar *net_port_parent_path;
+ MMPortMbim *found = NULL;
+ const gchar *net_port_driver;
+
+ g_assert (MM_IS_BROADBAND_MODEM_MBIM (self));
+ g_assert (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET);
+
+ net_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (data));
+ if (g_strcmp0 (net_port_driver, "cdc_mbim") != 0 && g_strcmp0 (net_port_driver, "mhi_net")) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unsupported MBIM kernel driver for 'net/%s': %s",
+ mm_port_get_device (data),
+ net_port_driver);
+ return NULL;
+ }
+
+ if (!g_strcmp0 (net_port_driver, "mhi_net"))
+ return mm_broadband_modem_mbim_peek_port_mbim (self);
+
+ net_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (data));
+ if (!net_port_parent_path) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "No parent path for 'net/%s'",
+ mm_port_get_device (data));
+ return NULL;
+ }
+
+ /* Find the CDC-WDM port on the same USB interface as the given net port */
+ cdc_wdm_mbim_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_USBMISC,
+ MM_PORT_TYPE_MBIM);
+
+ for (l = cdc_wdm_mbim_ports; l && !found; l = g_list_next (l)) {
+ const gchar *wdm_port_parent_path;
+
+ g_assert (MM_IS_PORT_MBIM (l->data));
+ wdm_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (MM_PORT (l->data)));
+ if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path))
+ found = MM_PORT_MBIM (l->data);
+ }
+
+ g_list_free_full (cdc_wdm_mbim_ports, g_object_unref);
+
+ if (!found)
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Couldn't find associated MBIM port for 'net/%s'",
+ mm_port_get_device (data));
+
+ return found;
+}
+
+MMPortMbim *
+mm_broadband_modem_mbim_peek_port_mbim_for_data (MMBroadbandModemMbim *self,
+ MMPort *data,
+ GError **error)
+{
+ g_assert (MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->peek_port_mbim_for_data);
+
+ return MM_BROADBAND_MODEM_MBIM_GET_CLASS (self)->peek_port_mbim_for_data (self, data, error);
+}
+
/*****************************************************************************/
-/* Current Capabilities loading (Modem interface) */
+/* Current capabilities (Modem interface) */
typedef struct {
- MMBroadbandModemMbim *self;
- GSimpleAsyncResult *result;
-} LoadCapabilitiesContext;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ MMModemCapability current_qmi;
+#endif
+ MbimDevice *device;
+ MMModemCapability current_mbim;
+} LoadCurrentCapabilitiesContext;
static void
-load_capabilities_context_complete_and_free (LoadCapabilitiesContext *ctx)
+load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LoadCapabilitiesContext, ctx);
+ g_object_unref (ctx->device);
+ g_free (ctx);
}
static MMModemCapability
@@ -127,97 +383,279 @@ modem_load_current_capabilities_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMModemCapability caps;
- gchar *caps_str;
+ GError *inner_error = NULL;
+ gssize value;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return MM_MODEM_CAPABILITY_NONE;
+ }
+ return (MMModemCapability)value;
+}
+
+static void
+complete_current_capabilities (GTask *task)
+{
+ LoadCurrentCapabilitiesContext *ctx;
+ MMModemCapability result = 0;
+
+ ctx = g_task_get_task_data (task);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ {
+ MMBroadbandModemMbim *self;
+
+ self = g_task_get_source_object (task);
+ /* Warn if the MBIM loaded capabilities isn't a subset of the QMI loaded ones */
+ if (ctx->current_qmi && ctx->current_mbim) {
+ gchar *mbim_caps_str;
+ gchar *qmi_caps_str;
- caps = ((MMModemCapability) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res))));
- caps_str = mm_modem_capability_build_string_from_mask (caps);
- mm_dbg ("loaded modem capabilities: %s", caps_str);
- g_free (caps_str);
- return caps;
+ mbim_caps_str = mm_common_build_capabilities_string ((const MMModemCapability *)&(ctx->current_mbim), 1);
+ qmi_caps_str = mm_common_build_capabilities_string ((const MMModemCapability *)&(ctx->current_qmi), 1);
+
+ if ((ctx->current_mbim & ctx->current_qmi) != ctx->current_mbim)
+ mm_obj_warn (self, "MBIM reported current capabilities (%s) not found in QMI-over-MBIM reported ones (%s)",
+ mbim_caps_str, qmi_caps_str);
+ else
+ mm_obj_dbg (self, "MBIM reported current capabilities (%s) is a subset of the QMI-over-MBIM reported ones (%s)",
+ mbim_caps_str, qmi_caps_str);
+ g_free (mbim_caps_str);
+ g_free (qmi_caps_str);
+
+ result = ctx->current_qmi;
+ self->priv->qmi_capability_and_mode_switching = TRUE;
+ } else if (ctx->current_qmi) {
+ result = ctx->current_qmi;
+ self->priv->qmi_capability_and_mode_switching = TRUE;
+ } else
+ result = ctx->current_mbim;
+
+ /* If current capabilities loading is done via QMI, we can safely assume that all the other
+ * capability and mode related operations are going to be done via QMI as well, so that we
+ * don't mix both logics */
+ if (self->priv->qmi_capability_and_mode_switching)
+ mm_obj_info (self, "QMI-based capability and mode switching support enabled");
+ }
+#else
+ result = ctx->current_mbim;
+#endif
+
+ g_task_return_int (task, (gint) result);
+ g_object_unref (task);
}
static void
device_caps_query_ready (MbimDevice *device,
GAsyncResult *res,
- LoadCapabilitiesContext *ctx)
+ GTask *task)
{
- MMModemCapability mask;
- MbimMessage *response;
- GError *error = NULL;
+ MMBroadbandModemMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+ LoadCurrentCapabilitiesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
- if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_device_caps_response_parse (
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_device_caps_response_parse (
response,
NULL, /* device_type */
- &ctx->self->priv->caps_cellular_class,
+ &self->priv->caps_cellular_class,
NULL, /* voice_class */
NULL, /* sim_class */
- &ctx->self->priv->caps_data_class,
- &ctx->self->priv->caps_sms,
+ &self->priv->caps_data_class,
+ &self->priv->caps_sms,
NULL, /* ctrl_caps */
- &ctx->self->priv->caps_max_sessions,
- NULL, /* custom_data_class */
- &ctx->self->priv->caps_device_id,
- &ctx->self->priv->caps_firmware_info,
- NULL, /* hardware_info */
+ &self->priv->caps_max_sessions,
+ &self->priv->caps_custom_data_class,
+ &self->priv->caps_device_id,
+ &self->priv->caps_firmware_info,
+ &self->priv->caps_hardware_info,
&error)) {
- /* Build mask of modem capabilities */
- mask = 0;
- if (ctx->self->priv->caps_cellular_class & MBIM_CELLULAR_CLASS_GSM)
- mask |= MM_MODEM_CAPABILITY_GSM_UMTS;
- if (ctx->self->priv->caps_cellular_class & MBIM_CELLULAR_CLASS_CDMA)
- mask |= MM_MODEM_CAPABILITY_CDMA_EVDO;
- if (ctx->self->priv->caps_data_class & MBIM_DATA_CLASS_LTE)
- mask |= MM_MODEM_CAPABILITY_LTE;
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (mask),
- NULL);
- } else
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+ ctx->current_mbim = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class,
+ self->priv->caps_data_class,
+ self->priv->caps_custom_data_class);
+ complete_current_capabilities (task);
+
+out:
if (response)
mbim_message_unref (response);
- load_capabilities_context_complete_and_free (ctx);
}
static void
-modem_load_current_capabilities (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_current_capabilities_mbim (GTask *task)
{
- LoadCapabilitiesContext *ctx;
- MbimDevice *device;
- MbimMessage *message;
+ MMBroadbandModemMbim *self;
+ MbimMessage *message;
+ LoadCurrentCapabilitiesContext *ctx;
- if (!peek_device (self, &device, callback, user_data))
- return;
-
- ctx = g_slice_new (LoadCapabilitiesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_current_capabilities);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- mm_dbg ("loading current capabilities...");
+ mm_obj_dbg (self, "loading current capabilities...");
message = mbim_message_device_caps_query_new (NULL);
- mbim_device_command (device,
+ mbim_device_command (ctx->device,
message,
10,
NULL,
(GAsyncReadyCallback)device_caps_query_ready,
- ctx);
+ task);
mbim_message_unref (message);
}
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+qmi_load_current_capabilities_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadCurrentCapabilitiesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->current_qmi = mm_shared_qmi_load_current_capabilities_finish (self, res, &error);
+ if (error) {
+ mm_obj_dbg (self, "couldn't load currrent capabilities using QMI over MBIM: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ load_current_capabilities_mbim (task);
+}
+
+#endif
+
+static void
+modem_load_current_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ LoadCurrentCapabilitiesContext *ctx;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_new0 (LoadCurrentCapabilitiesContext, 1);
+ ctx->device = g_object_ref (device);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) load_current_capabilities_context_free);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ mm_shared_qmi_load_current_capabilities (self,
+ (GAsyncReadyCallback)qmi_load_current_capabilities_ready,
+ task);
+#else
+ load_current_capabilities_mbim (task);
+#endif
+}
+
+/*****************************************************************************/
+/* Supported Capabilities loading (Modem interface) */
+
+static GArray *
+modem_load_supported_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_load_supported_capabilities_finish (self, res, error);
+#endif
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_capabilities_mbim (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MMModemCapability current;
+ GArray *supported = NULL;
+
+ self = g_task_get_source_object (task);
+
+ /* Current capabilities should have been cached already, just assume them */
+ current = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class,
+ self->priv->caps_data_class,
+ self->priv->caps_custom_data_class);
+ if (current != 0) {
+ supported = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
+ g_array_append_val (supported, current);
+ }
+
+ if (!supported)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't load supported capabilities: no previously catched current capabilities");
+ else
+ g_task_return_pointer (task, supported, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+modem_load_supported_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_load_supported_capabilities (self, callback, user_data);
+ return;
+ }
+#endif
+
+ task = g_task_new (self, NULL, callback, user_data);
+ load_supported_capabilities_mbim (task);
+}
+
+/*****************************************************************************/
+/* Capabilities switching (Modem interface) */
+
+static gboolean
+modem_set_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_set_current_capabilities_finish (self, res, error);
+#endif
+ g_assert (error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_set_current_capabilities (MMIfaceModem *self,
+ MMModemCapability capabilities,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_set_current_capabilities (self, capabilities, callback, user_data);
+ return;
+ }
+#endif
+
+ g_task_report_new_error (self, callback, user_data,
+ modem_set_current_capabilities,
+ MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Capability switching is not supported");
+}
+
/*****************************************************************************/
/* Manufacturer loading (Modem interface) */
@@ -226,7 +664,7 @@ modem_load_manufacturer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return g_strdup (mm_base_modem_get_plugin (MM_BASE_MODEM (self)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -234,43 +672,95 @@ modem_load_manufacturer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
+ gchar *manufacturer = NULL;
+ MMPortMbim *port;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_manufacturer);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
+ if (port) {
+ manufacturer = g_strdup (mm_kernel_device_get_physdev_manufacturer (
+ mm_port_peek_kernel_device (MM_PORT (port))));
+ }
+
+ if (!manufacturer)
+ manufacturer = g_strdup (mm_base_modem_get_plugin (MM_BASE_MODEM (self)));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, manufacturer, g_free);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Model loading (Modem interface) */
static gchar *
-modem_load_model_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_load_model_default (MMIfaceModem *self)
{
return g_strdup_printf ("MBIM [%04X:%04X]",
(mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)) & 0xFFFF),
(mm_base_modem_get_product_id (MM_BASE_MODEM (self)) & 0xFFFF));
+}
+static gchar *
+modem_load_model_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
}
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+qmi_load_model_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ gchar *model = NULL;
+ GError *error = NULL;
+
+ model = mm_shared_qmi_load_model_finish (self, res, &error);
+ if (!model) {
+ mm_obj_dbg (self, "couldn't load model using QMI over MBIM: %s", error->message);
+ model = modem_load_model_default (self);
+ g_clear_error (&error);
+ }
+
+ g_task_return_pointer (task, model, g_free);
+ g_object_unref (task);
+}
+
+#endif
+
static void
modem_load_model (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ gchar *model = NULL;
+ GTask *task;
+ MMPortMbim *port;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
+ if (port) {
+ model = g_strdup (mm_kernel_device_get_physdev_product (
+ mm_port_peek_kernel_device (MM_PORT (port))));
+ }
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (!model) {
+ mm_shared_qmi_load_model (self, (GAsyncReadyCallback)qmi_load_model_ready, task);
+ return;
+ }
+#endif
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_model);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (!model)
+ model = modem_load_model_default (self);
+
+ g_task_return_pointer (task, model, g_free);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -281,30 +771,60 @@ modem_load_revision_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (MM_BROADBAND_MODEM_MBIM (self)->priv->caps_firmware_info)
- return g_strdup (MM_BROADBAND_MODEM_MBIM (self)->priv->caps_firmware_info);
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Firmware revision information not given in device capabilities");
- return NULL;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_load_revision (MMIfaceModem *self,
+modem_load_revision (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
- /* Just complete */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_revision);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ if (self->priv->caps_firmware_info)
+ g_task_return_pointer (task,
+ g_strdup (self->priv->caps_firmware_info),
+ g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Firmware revision information not given in device capabilities");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Hardware Revision loading (Modem interface) */
+
+static gchar *
+modem_load_hardware_revision_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+modem_load_hardware_revision (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (self->priv->caps_hardware_info)
+ g_task_return_pointer (task,
+ g_strdup (self->priv->caps_hardware_info),
+ g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Hardware revision information not given in device capabilities");
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -315,30 +835,28 @@ modem_load_equipment_identifier_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (MM_BROADBAND_MODEM_MBIM (self)->priv->caps_device_id)
- return g_strdup (MM_BROADBAND_MODEM_MBIM (self)->priv->caps_device_id);
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Device ID not given in device capabilities");
- return NULL;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_load_equipment_identifier (MMIfaceModem *self,
+modem_load_equipment_identifier (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
- /* Just complete */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_equipment_identifier);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ if (self->priv->caps_device_id)
+ g_task_return_pointer (task,
+ g_strdup (self->priv->caps_device_id),
+ g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Device ID not given in device capabilities");
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -349,13 +867,7 @@ modem_load_device_identifier_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- gchar *device_identifier;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- device_identifier = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- return device_identifier;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -363,97 +875,473 @@ modem_load_device_identifier (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- gchar *device_identifier;
+ gchar *device_identifier;
+ GTask *task;
+ GError *error = NULL;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_device_identifier);
+ task = g_task_new (self, NULL, callback, user_data);
/* Just use dummy ATI/ATI1 replies, all the other internal info should be
* enough for uniqueness */
- device_identifier = mm_broadband_modem_create_device_identifier (MM_BROADBAND_MODEM (self), "", "");
- g_simple_async_result_set_op_res_gpointer (result,
- device_identifier,
- (GDestroyNotify)g_free);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ device_identifier = mm_broadband_modem_create_device_identifier (MM_BROADBAND_MODEM (self), "", "", &error);
+ if (!device_identifier)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, device_identifier, g_free);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Supported modes loading (Modem interface) */
static GArray *
-modem_load_supported_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_load_supported_modes (MMIfaceModem *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_supported_modes_mbim (GTask *task,
+ MbimDevice *device)
+{
+ MMBroadbandModemMbim *self;
+ GArray *all;
+ GArray *combinations;
+ GArray *filtered;
+ MMModemMode mask_all;
+ MMModemModeCombination mode = {
+ .allowed = MM_MODEM_MODE_NONE,
+ .preferred = MM_MODEM_MODE_NONE,
+ };
+
+ self = g_task_get_source_object (task);
+
+ if (self->priv->caps_data_class == 0) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Data class not given in device capabilities");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Build all */
+ mask_all = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class);
+ mode.allowed = mask_all;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ g_array_append_val (all, mode);
+
+ combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination));
+
+ /* When using MBIMEx we can enable the mode switching operation because
+ * we'll be able to know if the modes requested are the ones configured
+ * as preferred after the operation. */
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+#define ADD_MODE_PREFERENCE(MODE_MASK) do { \
+ mode.allowed = MODE_MASK; \
+ mode.preferred = MM_MODEM_MODE_NONE; \
+ g_array_append_val (combinations, mode); \
+ } while (0)
+
+ /* 2G, 3G */
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+
+ /* +4G */
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+
+ /* +5G */
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G | MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G | MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G);
+
+ filtered = mm_filter_supported_modes (all, combinations, self);
+ g_array_unref (combinations);
+ g_array_unref (all);
+#undef ADD_MODE_PREFERENCE
+ } else
+ filtered = all;
+
+ g_task_return_pointer (task, filtered, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+shared_qmi_load_supported_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
GArray *combinations;
- MMModemModeCombination mode;
- GSimpleAsyncResult *result;
- MMModemMode all;
+ GError *error = NULL;
- /* Just complete */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_modes);
+ combinations = mm_shared_qmi_load_supported_modes_finish (self, res, &error);
+ if (!combinations)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+}
+#endif
- if (self->priv->caps_data_class == 0) {
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Data class not given in device capabilities");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
-
- all = 0;
-
- /* 3GPP... */
- if (self->priv->caps_data_class & (MBIM_DATA_CLASS_GPRS |
- MBIM_DATA_CLASS_EDGE))
- all |= MM_MODEM_MODE_2G;
- if (self->priv->caps_data_class & (MBIM_DATA_CLASS_UMTS |
- MBIM_DATA_CLASS_HSDPA |
- MBIM_DATA_CLASS_HSUPA))
- all |= MM_MODEM_MODE_3G;
- if (self->priv->caps_data_class & MBIM_DATA_CLASS_LTE)
- all |= MM_MODEM_MODE_4G;
-
- /* 3GPP2... */
- if (self->priv->caps_data_class & MBIM_DATA_CLASS_1XRTT)
- all |= MM_MODEM_MODE_2G;
- if (self->priv->caps_data_class & (MBIM_DATA_CLASS_1XEVDO |
- MBIM_DATA_CLASS_1XEVDO_REVA |
- MBIM_DATA_CLASS_1XEVDV |
- MBIM_DATA_CLASS_3XRTT |
- MBIM_DATA_CLASS_1XEVDO_REVB))
- all |= MM_MODEM_MODE_3G;
- if (self->priv->caps_data_class & MBIM_DATA_CLASS_UMB)
- all |= MM_MODEM_MODE_4G;
-
- /* Build a mask with all supported modes */
- combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
- mode.allowed = all;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
+static void
+modem_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MbimDevice *device;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_load_supported_modes (self, (GAsyncReadyCallback)shared_qmi_load_supported_modes_ready, task);
+ return;
+ }
+#endif
+
+ load_supported_modes_mbim (task, device);
+}
+
+/*****************************************************************************/
+/* Current modes loading (Modem interface) */
+
+static gboolean
+modem_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ g_autofree MMModemModeCombination *mode = NULL;
+
+ mode = g_task_propagate_pointer (G_TASK (res), error);
+ if (!mode)
+ return FALSE;
+
+ *allowed = mode->allowed;
+ *preferred = mode->preferred;
+ return TRUE;
+}
+
+static void
+register_state_current_modes_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ MMModemModeCombination *mode = NULL;
+ GError *error = NULL;
+ MbimDataClass preferred_data_classes;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_v2_register_state_response_parse (
+ response,
+ NULL, /* nw_error */
+ NULL, /* register_state */
+ NULL, /* register_mode */
+ NULL, /* available_data_classes */
+ NULL, /* current_cellular_class */
+ NULL, /* provider_id */
+ NULL, /* provider_name */
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ &preferred_data_classes,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mode = g_new0 (MMModemModeCombination, 1);
+ mode->allowed = mm_modem_mode_from_mbim_data_class (preferred_data_classes);
+ mode->preferred = MM_MODEM_MODE_NONE;
+ g_task_return_pointer (task, mode, (GDestroyNotify)g_free);
+ g_object_unref (task);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+shared_qmi_load_current_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autofree MMModemModeCombination *mode = NULL;
+ GError *error = NULL;
+
+ mode = g_new0 (MMModemModeCombination, 1);
+ if (!mm_shared_qmi_load_current_modes_finish (self, res, &mode->allowed, &mode->preferred, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, g_steal_pointer (&mode), (GDestroyNotify)g_free);
+ g_object_unref (task);
+}
+
+#endif
+
+static void
+modem_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MbimDevice *device;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_load_current_modes (self, (GAsyncReadyCallback)shared_qmi_load_current_modes_ready, task);
+ return;
+ }
+#endif
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ g_autoptr(MbimMessage) message = NULL;
+
+ message = mbim_message_register_state_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 60,
+ NULL,
+ (GAsyncReadyCallback)register_state_current_modes_query_ready,
+ task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Current mode loading is not supported");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Current modes switching (Modem interface) */
+
+static gboolean
+modem_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+complete_pending_allowed_modes_action (MMBroadbandModemMbim *self,
+ MbimDataClass preferred_data_classes)
+{
+ MbimDataClass requested_data_classes;
+ MMModemMode preferred_modes;
+ MMModemMode requested_modes;
+
+ if (!self->priv->pending_allowed_modes_action)
+ return;
+
+ requested_data_classes = (MbimDataClass) GPOINTER_TO_UINT (g_task_get_task_data (self->priv->pending_allowed_modes_action));
+ requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes);
+ preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes);
+
+ /* only early complete on success, as we don't know if they're going to be
+ * intermediate indications emitted before the preference change is valid */
+ if (requested_modes == preferred_modes) {
+ GTask *task;
+
+ task = g_steal_pointer (&self->priv->pending_allowed_modes_action);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ }
+}
+
+static void
+register_state_current_modes_set_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ MMBroadbandModemMbim *self;
+ GError *error = NULL;
+ MbimDataClass preferred_data_classes;
+ MMModemMode preferred_modes;
+ MbimDataClass requested_data_classes;
+ MMModemMode requested_modes;
+
+ self = g_task_get_source_object (task);
+ requested_data_classes = (MbimDataClass) GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* If the task is still in the private info, it means it wasn't either
+ * cancelled or completed, so we just unref that reference and go on
+ * with out response processing. But if the task is no longer in the
+ * private info (or if there's a different task), then it means we're
+ * either cancelled (by some new incoming user request) or otherwise
+ * successfully completed (if completed via a Register State indication).
+ * In both those cases, just unref the incoming task and go on. */
+ if (self->priv->pending_allowed_modes_action != task) {
+ g_assert (g_task_get_completed (task));
+ g_object_unref (task);
+ return;
+ }
+ g_clear_object (&self->priv->pending_allowed_modes_action);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_v2_register_state_response_parse (
+ response,
+ NULL, /* nw_error */
+ NULL, /* register_state */
+ NULL, /* register_mode */
+ NULL, /* available_data_classes */
+ NULL, /* current_cellular_class */
+ NULL, /* provider_id */
+ NULL, /* provider_name */
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ &preferred_data_classes,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes);
+ preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes);
+
+ if (requested_modes != preferred_modes) {
+ g_autofree gchar *requested_modes_str = NULL;
+ g_autofree gchar *preferred_modes_str = NULL;
+ g_autofree gchar *requested_data_classes_str = NULL;
+ g_autofree gchar *preferred_data_classes_str = NULL;
+
+ requested_modes_str = mm_modem_mode_build_string_from_mask (requested_modes);
+ preferred_modes_str = mm_modem_mode_build_string_from_mask (preferred_modes);
+ requested_data_classes_str = mbim_data_class_build_string_from_mask (requested_data_classes);
+ preferred_data_classes_str = mbim_data_class_build_string_from_mask (preferred_data_classes);
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Current mode update failed: requested %s (%s) but reported preferred is %s (%s)",
+ requested_modes_str, requested_data_classes_str,
+ preferred_modes_str, preferred_data_classes_str);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+shared_qmi_set_current_modes_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
- g_simple_async_result_set_op_res_gpointer (result, combinations, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (!mm_shared_qmi_set_current_modes_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+#endif
+
+static void
+modem_set_current_modes (MMIfaceModem *_self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
+ MbimDevice *device;
+ g_autoptr(GCancellable) cancellable = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ cancellable = g_cancellable_new ();
+ task = g_task_new (self, cancellable, callback, user_data);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (self->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_set_current_modes (MM_IFACE_MODEM (self),
+ allowed,
+ preferred,
+ (GAsyncReadyCallback)shared_qmi_set_current_modes_ready,
+ task);
+ return;
+ }
+#endif
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ g_autoptr(MbimMessage) message = NULL;
+
+ /* Limit ANY to the currently supported modes */
+ if (allowed == MM_MODEM_MODE_ANY)
+ allowed = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class);
+
+ self->priv->requested_data_class = mm_mbim_data_class_from_modem_mode (allowed,
+ mm_iface_modem_is_3gpp (_self),
+ mm_iface_modem_is_cdma (_self));
+
+ /* Store the ongoing allowed modes action, so that we can finish the
+ * operation early via indications, instead of waiting for the modem
+ * to be registered on the requested access technologies */
+ g_task_set_task_data (task, GUINT_TO_POINTER (self->priv->requested_data_class), NULL);
+ if (self->priv->pending_allowed_modes_action) {
+ /* cancel the task and clear this reference; the _set_ready()
+ * will take care of completing the task */
+ g_cancellable_cancel (g_task_get_cancellable (self->priv->pending_allowed_modes_action));
+ g_task_return_error_if_cancelled (self->priv->pending_allowed_modes_action);
+ g_clear_object (&self->priv->pending_allowed_modes_action);
+ }
+ self->priv->pending_allowed_modes_action = g_object_ref (task);
+
+ /* use the last requested operator id to determine whether the
+ * registration should be manual or automatic */
+ message = mbim_message_register_state_set_new (
+ self->priv->requested_operator_id ? self->priv->requested_operator_id : "",
+ self->priv->requested_operator_id ? MBIM_REGISTER_ACTION_MANUAL : MBIM_REGISTER_ACTION_AUTOMATIC,
+ self->priv->requested_data_class,
+ NULL);
+ mbim_device_command (device,
+ message,
+ 60,
+ NULL,
+ (GAsyncReadyCallback)register_state_current_modes_set_ready,
+ task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Current mode switching is not supported");
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -464,11 +1352,15 @@ modem_load_supported_ip_families_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_BEARER_IP_FAMILY_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMBearerIpFamily) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_IP_FAMILY_NONE;
+ }
+ return (MMBearerIpFamily)value;
}
static void
@@ -476,41 +1368,29 @@ modem_load_supported_ip_families (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_ip_families);
+ GTask *task;
+ task = g_task_new (self, NULL, callback, user_data);
/* Assume IPv4 + IPv6 + IPv4v6 supported */
- g_simple_async_result_set_op_res_gpointer (
- result,
- GUINT_TO_POINTER (MM_BEARER_IP_FAMILY_IPV4 |
- MM_BEARER_IP_FAMILY_IPV6 |
- MM_BEARER_IP_FAMILY_IPV4V6),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_int (task,
+ MM_BEARER_IP_FAMILY_IPV4 |
+ MM_BEARER_IP_FAMILY_IPV6 |
+ MM_BEARER_IP_FAMILY_IPV4V6);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Unlock required loading (Modem interface) */
typedef struct {
- MMBroadbandModemMbim *self;
- GSimpleAsyncResult *result;
- guint n_ready_status_checks;
MbimDevice *device;
+ gboolean last_attempt;
} LoadUnlockRequiredContext;
static void
-load_unlock_required_context_complete_and_free (LoadUnlockRequiredContext *ctx)
+load_unlock_required_context_free (LoadUnlockRequiredContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->device);
- g_object_unref (ctx->self);
g_slice_free (LoadUnlockRequiredContext, ctx);
}
@@ -519,16 +1399,21 @@ modem_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;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCK_UNKNOWN;
+ }
+ return (MMModemLock)value;
}
static void
pin_query_ready (MbimDevice *device,
GAsyncResult *res,
- LoadUnlockRequiredContext *ctx)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -537,7 +1422,7 @@ pin_query_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_pin_response_parse (
response,
&pin_type,
@@ -550,39 +1435,42 @@ pin_query_ready (MbimDevice *device,
unlock_required = MM_MODEM_LOCK_NONE;
else
unlock_required = mm_modem_lock_from_mbim_pin_type (pin_type);
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (unlock_required),
- NULL);
+
+ g_task_return_int (task, unlock_required);
}
/* VZ20M reports an error when SIM-PIN is required... */
else if (g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED)) {
g_error_free (error);
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (MBIM_PIN_TYPE_PIN1),
- NULL);
+ g_task_return_int (task, MBIM_PIN_TYPE_PIN1);
}
else
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- load_unlock_required_context_complete_and_free (ctx);
}
-static gboolean wait_for_sim_ready (LoadUnlockRequiredContext *ctx);
+static gboolean wait_for_sim_ready (GTask *task);
static void
unlock_required_subscriber_ready_state_ready (MbimDevice *device,
GAsyncResult *res,
- LoadUnlockRequiredContext *ctx)
+ GTask *task)
{
+ LoadUnlockRequiredContext *ctx;
+ MMBroadbandModemMbim *self;
MbimMessage *response;
GError *error = NULL;
MbimSubscriberReadyState ready_state = MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED;
+ ctx = g_task_get_task_data (task);
+ self = g_task_get_source_object (task);
+
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_subscriber_ready_status_response_parse (
response,
&ready_state,
@@ -603,43 +1491,46 @@ unlock_required_subscriber_ready_state_ready (MbimDevice *device,
* The MC7710 may use this while the SIM is not ready yet. */
break;
case MBIM_SUBSCRIBER_READY_STATE_BAD_SIM:
- error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG);
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, self);
break;
case MBIM_SUBSCRIBER_READY_STATE_FAILURE:
case MBIM_SUBSCRIBER_READY_STATE_NOT_ACTIVATED:
+ case MBIM_SUBSCRIBER_READY_STATE_NO_ESIM_PROFILE:
default:
- error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE);
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE, self);
break;
}
}
+ self->priv->last_ready_state = ready_state;
+
/* Fatal errors are reported right away */
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- load_unlock_required_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
+
/* Need to retry? */
- else if (ready_state == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED ||
- ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) {
- if (--ctx->n_ready_status_checks == 0) {
- /* All retries consumed, issue error */
+ if (ready_state == MBIM_SUBSCRIBER_READY_STATE_NOT_INITIALIZED ||
+ ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) {
+ /* All retries consumed? issue error */
+ if (ctx->last_attempt) {
if (ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED)
- g_simple_async_result_take_error (
- ctx->result,
- mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED));
+ g_task_return_error (task, mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, self));
else
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Error waiting for SIM to get initialized");
- load_unlock_required_context_complete_and_free (ctx);
- } else {
- /* Retry */
- g_timeout_add_seconds (1, (GSourceFunc)wait_for_sim_ready, ctx);
- }
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Error waiting for SIM to get initialized");
+ } else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY,
+ "SIM not ready yet (retry)");
+ g_object_unref (task);
+ goto out;
}
+
/* Initialized but locked? */
- else if (ready_state == MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED) {
+ if (ready_state == MBIM_SUBSCRIBER_READY_STATE_DEVICE_LOCKED ||
+ ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) {
MbimMessage *message;
/* Query which lock is to unlock */
@@ -649,59 +1540,57 @@ unlock_required_subscriber_ready_state_ready (MbimDevice *device,
10,
NULL,
(GAsyncReadyCallback)pin_query_ready,
- ctx);
+ task);
mbim_message_unref (message);
+ goto out;
}
- /* Initialized but locked? */
- else if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (MM_MODEM_LOCK_NONE),
- NULL);
- load_unlock_required_context_complete_and_free (ctx);
- } else
- g_assert_not_reached ();
+ g_assert_not_reached ();
+
+out:
if (response)
mbim_message_unref (response);
}
static gboolean
-wait_for_sim_ready (LoadUnlockRequiredContext *ctx)
+wait_for_sim_ready (GTask *task)
{
+ LoadUnlockRequiredContext *ctx;
MbimMessage *message;
+ ctx = g_task_get_task_data (task);
message = mbim_message_subscriber_ready_status_query_new (NULL);
mbim_device_command (ctx->device,
message,
10,
NULL,
(GAsyncReadyCallback)unlock_required_subscriber_ready_state_ready,
- ctx);
+ task);
mbim_message_unref (message);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
modem_load_unlock_required (MMIfaceModem *self,
+ gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data)
{
LoadUnlockRequiredContext *ctx;
MbimDevice *device;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
ctx = g_slice_new (LoadUnlockRequiredContext);
- ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_unlock_required);
- ctx->n_ready_status_checks = 10;
+ ctx->last_attempt = last_attempt;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_required_context_free);
- wait_for_sim_ready (ctx);
+ wait_for_sim_ready (task);
}
/*****************************************************************************/
@@ -712,16 +1601,13 @@ modem_load_unlock_retries_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return MM_UNLOCK_RETRIES (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
pin_query_unlock_retries_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -730,31 +1616,65 @@ pin_query_unlock_retries_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_pin_response_parse (
response,
&pin_type,
NULL,
&remaining_attempts,
&error)) {
+ MMIfaceModem *self;
+ MMModemLock lock;
MMUnlockRetries *retries;
+ self = g_task_get_source_object (task);
+ lock = mm_modem_lock_from_mbim_pin_type (pin_type);
retries = mm_unlock_retries_new ();
+
+ /* If PIN1 is disabled and we have tried to enable it with a wrong PIN,
+ * the modem would have indicated the number of remaining attempts for
+ * PIN1 (unless PUK1 is engaged) in the response to the failed
+ * MBIM_CID_PIN set operation. Thus, MMSimMbim would have updated
+ * MMIfaceModem's MMUnlockRetries with information about PIN1.
+ *
+ * However, a MBIM_CID_PIN query may be issued (e.g. MMBaseSim calls
+ * mm_iface_modem_update_lock_info()) after the MBIM_CID_PIN set
+ * operation to query the number of remaining attempts for a PIN type.
+ * Unfortunately, we can't specify a particular PIN type in a
+ * MBIM_CID_PIN query. The modem may not reply with information about
+ * PIN1 if PIN1 is disabled. When that happens, we would like to
+ * preserve our knowledge about the number of remaining attempts for
+ * PIN1. Here we thus carry over any existing information on PIN1 from
+ * MMIfaceModem's MMUnlockRetries if the MBIM_CID_PIN query reports
+ * something other than PIN1. */
+ if (lock != MM_MODEM_LOCK_SIM_PIN) {
+ MMUnlockRetries *previous_retries;
+ guint previous_sim_pin_retries;
+
+ previous_retries = mm_iface_modem_get_unlock_retries (self);
+ previous_sim_pin_retries = mm_unlock_retries_get (previous_retries,
+ MM_MODEM_LOCK_SIM_PIN);
+ if (previous_sim_pin_retries != MM_UNLOCK_RETRIES_UNKNOWN) {
+ mm_unlock_retries_set (retries,
+ MM_MODEM_LOCK_SIM_PIN,
+ previous_sim_pin_retries);
+ }
+ g_object_unref (previous_retries);
+ }
+
/* According to the MBIM specification, RemainingAttempts is set to
* 0xffffffff if the device does not support this information. */
- if (remaining_attempts != G_MAXUINT32) {
- mm_unlock_retries_set (retries,
- mm_modem_lock_from_mbim_pin_type (pin_type),
- remaining_attempts);
- }
- g_simple_async_result_set_op_res_gpointer (simple, retries, g_object_unref);
+ if (remaining_attempts != G_MAXUINT32)
+ mm_unlock_retries_set (retries, lock, remaining_attempts);
+
+ g_task_return_pointer (task, retries, g_object_unref);
} else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -762,17 +1682,14 @@ modem_load_unlock_retries (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MbimDevice *device;
MbimMessage *message;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_unlock_retries);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_pin_query_new (NULL);
mbim_device_command (device,
@@ -780,7 +1697,7 @@ modem_load_unlock_retries (MMIfaceModem *self,
10,
NULL,
(GAsyncReadyCallback)pin_query_unlock_retries_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -792,16 +1709,13 @@ modem_load_own_numbers_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
own_numbers_subscriber_ready_state_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -809,7 +1723,7 @@ own_numbers_subscriber_ready_state_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_subscriber_ready_status_response_parse (
response,
NULL, /* ready_state */
@@ -819,14 +1733,14 @@ own_numbers_subscriber_ready_state_ready (MbimDevice *device,
NULL, /* telephone_numbers_count */
&telephone_numbers,
&error)) {
- g_simple_async_result_set_op_res_gpointer (simple, telephone_numbers, NULL);
+ g_task_return_pointer (task, telephone_numbers, (GDestroyNotify)g_strfreev);
} else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -834,17 +1748,14 @@ modem_load_own_numbers (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MbimDevice *device;
MbimMessage *message;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_own_numbers);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_subscriber_ready_status_query_new (NULL);
mbim_device_command (device,
@@ -852,7 +1763,7 @@ modem_load_own_numbers (MMIfaceModem *self,
10,
NULL,
(GAsyncReadyCallback)own_numbers_subscriber_ready_state_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -864,16 +1775,21 @@ modem_load_power_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_POWER_STATE_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemPowerState) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+ }
+ return (MMModemPowerState)value;
}
static void
radio_state_query_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -882,7 +1798,7 @@ radio_state_query_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_radio_state_response_parse (
response,
&hardware_radio_state,
@@ -895,16 +1811,13 @@ radio_state_query_ready (MbimDevice *device,
state = MM_MODEM_POWER_STATE_LOW;
else
state = MM_MODEM_POWER_STATE_ON;
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (state),
- NULL);
+ g_task_return_int (task, state);
} else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -912,17 +1825,14 @@ modem_load_power_state (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MbimDevice *device;
MbimMessage *message;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_power_state);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_radio_state_query_new (NULL);
mbim_device_command (device,
@@ -930,240 +1840,397 @@ modem_load_power_state (MMIfaceModem *self,
10,
NULL,
(GAsyncReadyCallback)radio_state_query_ready,
- result);
+ task);
mbim_message_unref (message);
}
/*****************************************************************************/
-/* Power up/down (Modem interface) */
+/* Power up (Modem interface) */
static gboolean
-common_power_up_down_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+power_up_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-radio_state_set_down_ready (MbimDevice *device,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- MbimMessage *response;
- GError *error = NULL;
-
- response = mbim_device_command_finish (device, res, &error);
- if (response)
- mbim_message_command_done_get_result (response, &error);
-
- if (error)
- g_simple_async_result_take_error (simple, error);
- else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
-
- if (response)
- mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-radio_state_set_up_ready (MbimDevice *device,
+radio_state_set_up_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- MbimMessage *response;
- GError *error = NULL;
- MbimRadioSwitchState hardware_radio_state;
- MbimRadioSwitchState software_radio_state;
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(GError) error = NULL;
+ MbimRadioSwitchState hardware_radio_state;
+ MbimRadioSwitchState software_radio_state;
+
+ self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_radio_state_response_parse (
response,
&hardware_radio_state,
&software_radio_state,
&error)) {
if (hardware_radio_state == MBIM_RADIO_SWITCH_STATE_OFF)
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Cannot power-up: hardware radio switch is OFF");
else if (software_radio_state == MBIM_RADIO_SWITCH_STATE_OFF)
- g_warn_if_reached ();
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot power-up: sotware radio switch is OFF");
}
- if (error)
- g_simple_async_result_take_error (simple, error);
- else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ /* Nice! we're done, quick exit */
+ if (!error) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
- if (response)
+ /* The SDX55 returns "Operation not allowed", but not really sure about other
+ * older devices. The original logic in the MBIM implemetation triggered a retry
+ * for any kind of error, so let's do the same for now. */
+ mm_obj_warn (self, "%s", error->message);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Invalid transition");
+ g_object_unref (task);
+}
+
+static void
+modem_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_ON, NULL);
+ mbim_device_command (device,
+ message,
+ 20,
+ NULL,
+ (GAsyncReadyCallback)radio_state_set_up_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Power down (Modem interface) */
+
+static gboolean
+power_down_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+radio_state_set_down_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response) {
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-common_power_up_down (MMIfaceModem *self,
- gboolean up,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
MbimDevice *device;
MbimMessage *message;
- MbimRadioSwitchState state;
- GAsyncReadyCallback ready_cb;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_power_up_down);
-
- if (up) {
- ready_cb = (GAsyncReadyCallback)radio_state_set_up_ready;
- state = MBIM_RADIO_SWITCH_STATE_ON;
- } else {
- ready_cb = (GAsyncReadyCallback)radio_state_set_down_ready;
- state = MBIM_RADIO_SWITCH_STATE_OFF;
- }
+ task = g_task_new (self, NULL, callback, user_data);
- message = mbim_message_radio_state_set_new (state, NULL);
+ message = mbim_message_radio_state_set_new (MBIM_RADIO_SWITCH_STATE_OFF, NULL);
mbim_device_command (device,
message,
20,
NULL,
- ready_cb,
- result);
+ (GAsyncReadyCallback)radio_state_set_down_ready,
+ task);
mbim_message_unref (message);
}
+/*****************************************************************************/
+/* Signal quality loading (Modem interface) */
+
+static guint
+modem_load_signal_quality_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), error);
+ return value < 0 ? 0 : value;
+}
+
static void
-modem_power_down (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+signal_state_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
{
- common_power_up_down (self, FALSE, callback, user_data);
+ MMBroadbandModemMbim *self;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(MbimRsrpSnrInfoArray) rsrp_snr = NULL;
+ guint32 rsrp_snr_count = 0;
+ guint32 rssi;
+ guint quality;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ if (!mbim_message_ms_basic_connect_v2_signal_state_response_parse (
+ response,
+ &rssi,
+ NULL, /* error_rate */
+ NULL, /* signal_strength_interval */
+ NULL, /* rssi_threshold */
+ NULL, /* error_rate_threshold */
+ &rsrp_snr_count,
+ &rsrp_snr,
+ &error))
+ g_prefix_error (&error, "Failed processing MBIMEx v2.0 signal state response: ");
+ else
+ mm_obj_dbg (self, "proccessed MBIMEx v2.0 signal state response");
+ } else {
+ if (!mbim_message_signal_state_response_parse (
+ response,
+ &rssi,
+ NULL, /* error_rate */
+ NULL, /* signal_strength_interval */
+ NULL, /* rssi_threshold */
+ NULL, /* error_rate_threshold */
+ &error))
+ g_prefix_error (&error, "Failed processing signal state response: ");
+ else
+ mm_obj_dbg (self, "proccessed signal state response");
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else {
+ quality = mm_signal_quality_from_mbim_signal_state (rssi, rsrp_snr, rsrp_snr_count, self);
+ g_task_return_int (task, quality);
+ }
+ g_object_unref (task);
}
static void
-modem_power_up (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_load_signal_quality (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- common_power_up_down (self, TRUE, callback, user_data);
+ MbimDevice *device;
+ MbimMessage *message;
+ GTask *task;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ message = mbim_message_signal_state_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)signal_state_query_ready,
+ task);
+ mbim_message_unref (message);
}
/*****************************************************************************/
-/* Create Bearer (Modem interface) */
+/* Reset */
-static MMBaseBearer *
-modem_create_bearer_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+static gboolean
+modem_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- MMBaseBearer *bearer;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-typedef struct {
- guint32 session_id;
- gboolean found;
-} FindSessionId;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
static void
-bearer_list_session_id_foreach (MMBaseBearer *bearer,
- gpointer user_data)
+shared_qmi_reset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- FindSessionId *ctx = user_data;
+ GError *error = NULL;
- if (!ctx->found &&
- MM_IS_BEARER_MBIM (bearer) &&
- mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (bearer)) == ctx->session_id)
- ctx->found = TRUE;
+ if (!mm_shared_qmi_reset_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
-static gint
-find_next_bearer_session_id (MMBroadbandModemMbim *self)
+static void
+modem_reset_shared_qmi (GTask *task)
{
- MMBearerList *bearer_list;
- guint i;
+ mm_shared_qmi_reset (MM_IFACE_MODEM (g_task_get_source_object (task)),
+ (GAsyncReadyCallback)shared_qmi_reset_ready,
+ task);
+}
- g_object_get (self,
- MM_IFACE_MODEM_BEARER_LIST, &bearer_list,
- NULL);
+#endif
- if (!bearer_list)
- return 0;
+static void
+intel_firmware_update_modem_reboot_set_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MbimMessage *response;
+ GError *error = NULL;
- for (i = 0; i <= 255; i++) {
- FindSessionId ctx;
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* We don't really expect the Intel firmware update service to be
+ * available in QMI modems, but doesn't harm to fallback to the QMI
+ * implementation here */
+ mm_obj_dbg (g_task_get_source_object (task), "couldn't run intel reset: %s", error->message);
+ g_error_free (error);
+ modem_reset_shared_qmi (task);
+#else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+#endif
+ } else {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ }
- ctx.session_id = i;
- ctx.found = FALSE;
+ if (response)
+ mbim_message_unref (response);
+}
- mm_bearer_list_foreach (bearer_list,
- bearer_list_session_id_foreach,
- &ctx);
+static void
+modem_reset (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
+ MbimDevice *device;
+ MbimMessage *message;
- if (!ctx.found) {
- g_object_unref (bearer_list);
- return (gint)i;
- }
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->is_intel_reset_supported) {
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ modem_reset_shared_qmi (task);
+#else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "modem reset operation is not supported");
+ g_object_unref (task);
+#endif
+ return;
}
- /* no valid session id found */
- g_object_unref (bearer_list);
- return -1;
+ /* This message is defined in the Intel Firmware Update service, but it
+ * really is just a standard modem reboot. */
+ message = mbim_message_intel_firmware_update_modem_reboot_set_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)intel_firmware_update_modem_reboot_set_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Create Bearer (Modem interface) */
+
+static MMBaseBearer *
+modem_create_bearer_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_create_bearer (MMIfaceModem *self,
- MMBearerProperties *properties,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_create_bearer (MMIfaceModem *self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
MMBaseBearer *bearer;
- GSimpleAsyncResult *result;
- gint session_id;
+ GTask *task;
- /* Set a new ref to the bearer object as result */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ /* Note: the session id to be used by the bearer will always be 0
+ * for non-multiplexed sessions, bound to the non-VLAN-tagged traffic
+ * managed by the master network interface */
- /* Find a new session ID */
- session_id = find_next_bearer_session_id (MM_BROADBAND_MODEM_MBIM (self));
- if (session_id < 0) {
- g_simple_async_result_set_error (
- result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Not enough session IDs");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_obj_dbg (self, "creating MBIM bearer in MBIM modem");
+ bearer = mm_bearer_mbim_new (MM_BROADBAND_MODEM_MBIM (self), properties);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
+}
- /* We just create a MMBearerMbim */
- mm_dbg ("Creating MBIM bearer in MBIM modem");
- bearer = mm_bearer_mbim_new (MM_BROADBAND_MODEM_MBIM (self),
- properties,
- (guint)session_id);
+/*****************************************************************************/
+/* Create Bearer List (Modem interface) */
- g_simple_async_result_set_op_res_gpointer (result, bearer, g_object_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+static MMBearerList *
+modem_create_bearer_list (MMIfaceModem *self)
+{
+ guint n;
+ guint n_multiplexed;
+
+ /* The maximum number of available/connected modems is guessed from
+ * the size of the data ports list. */
+ n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self)));
+ mm_obj_dbg (self, "allowed up to %u active bearers", n);
+
+ /* The maximum number of multiplexed links is defined by the MBIM protocol */
+ n_multiplexed = (MBIM_DEVICE_SESSION_ID_MAX - MBIM_DEVICE_SESSION_ID_MIN + 1);
+ mm_obj_dbg (self, "allowed up to %u active multiplexed bearers", n_multiplexed);
+
+ /* by default, no multiplexing support */
+ return mm_bearer_list_new (n, n_multiplexed);
}
/*****************************************************************************/
@@ -1190,6 +2257,113 @@ create_sim (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Reset data interfaces during initialization */
+
+typedef struct {
+ GList *ports;
+ MMPort *data;
+ MMPortMbim *mbim;
+} ResetPortsContext;
+
+static void
+reset_ports_context_free (ResetPortsContext *ctx)
+{
+ g_assert (!ctx->data);
+ g_assert (!ctx->mbim);
+ g_list_free_full (ctx->ports, g_object_unref);
+ g_slice_free (ResetPortsContext, ctx);
+}
+
+static gboolean
+reset_ports_finish (MMBroadbandModemMbim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void reset_next_port (GTask *task);
+
+static void
+port_mbim_reset_ready (MMPortMbim *mbim,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ ResetPortsContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_mbim_reset_finish (mbim, res, &error))
+ mm_obj_warn (self, "couldn't reset MBIM port '%s' with data interface '%s': %s",
+ mm_port_get_device (MM_PORT (ctx->mbim)),
+ mm_port_get_device (ctx->data),
+ error->message);
+
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->mbim);
+ reset_next_port (task);
+}
+
+static void
+reset_next_port (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ ResetPortsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!ctx->ports) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* steal full data port reference from list head */
+ ctx->data = ctx->ports->data;
+ ctx->ports = g_list_delete_link (ctx->ports, ctx->ports);
+
+ ctx->mbim = mm_broadband_modem_mbim_get_port_mbim_for_data (self, ctx->data, NULL);
+ if (!ctx->mbim) {
+ mm_obj_dbg (self, "no MBIM port associated to data port '%s': ignoring data interface reset",
+ mm_port_get_device (ctx->data));
+ g_clear_object (&ctx->data);
+ reset_next_port (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "running MBIM port '%s' reset with data interface '%s'",
+ mm_port_get_device (MM_PORT (ctx->mbim)), mm_port_get_device (ctx->data));
+
+ mm_port_mbim_reset (ctx->mbim,
+ ctx->data,
+ (GAsyncReadyCallback) port_mbim_reset_ready,
+ task);
+}
+
+static void
+reset_ports (MMBroadbandModemMbim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ ResetPortsContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (ResetPortsContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)reset_ports_context_free);
+
+ ctx->ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_UNKNOWN,
+ MM_PORT_TYPE_NET);
+
+ reset_next_port (task);
+}
+
+/*****************************************************************************/
/* First enabling step */
static gboolean
@@ -1197,13 +2371,13 @@ enabling_started_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_enabling_started_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
@@ -1213,13 +2387,12 @@ parent_enabling_started_ready (MMBroadbandModem *self,
&error)) {
/* Don't treat this as fatal. Parent enabling may fail if it cannot grab a primary
* AT port, which isn't really an issue in MBIM-based modems */
- mm_dbg ("Couldn't start parent enabling: %s", error->message);
+ mm_obj_dbg (self, "couldn't start parent enabling: %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -1227,54 +2400,45 @@ enabling_started (MMBroadbandModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enabling_started);
+ task = g_task_new (self, NULL, callback, user_data);
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_parent_class)->enabling_started (
self,
(GAsyncReadyCallback)parent_enabling_started_ready,
- result);
+ task);
}
/*****************************************************************************/
/* First initialization step */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMPortMbim *mbim;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ guint qmi_service_index;
+#endif
} InitializationStartedContext;
static void
-initialization_started_context_complete_and_free (InitializationStartedContext *ctx)
+initialization_started_context_free (InitializationStartedContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->mbim)
g_object_unref (ctx->mbim);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (InitializationStartedContext, ctx);
}
static gpointer
-initialization_started_finish (MMBroadbandModem *self,
- GAsyncResult *res,
- GError **error)
+initialization_started_finish (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- /* Just parent's pointer passed here */
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_initialization_started_ready (MMBroadbandModem *self,
- GAsyncResult *res,
- InitializationStartedContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
gpointer parent_ctx;
GError *error = NULL;
@@ -1286,76 +2450,398 @@ parent_initialization_started_ready (MMBroadbandModem *self,
if (error) {
/* Don't treat this as fatal. Parent initialization may fail if it cannot grab a primary
* AT port, which isn't really an issue in MBIM-based modems */
- mm_dbg ("Couldn't start parent initialization: %s", error->message);
+ mm_obj_dbg (self, "couldn't start parent initialization: %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gpointer (ctx->result, parent_ctx, NULL);
- initialization_started_context_complete_and_free (ctx);
+ /* Just parent's pointer passed here */
+ g_task_return_pointer (task, parent_ctx, NULL);
+ g_object_unref (task);
}
static void
-parent_initialization_started (InitializationStartedContext *ctx)
+parent_initialization_started (GTask *task)
{
+ MMBroadbandModem *self;
+
+ self = g_task_get_source_object (task);
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_mbim_parent_class)->initialization_started (
- ctx->self,
+ self,
(GAsyncReadyCallback)parent_initialization_started_ready,
- ctx);
+ task);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static const QmiService qmi_services[] = {
+ QMI_SERVICE_DMS,
+ QMI_SERVICE_NAS,
+ QMI_SERVICE_PDS,
+ QMI_SERVICE_LOC,
+ QMI_SERVICE_PDC,
+};
+
+static void allocate_next_qmi_client (GTask *task);
+
+static void
+mbim_port_allocate_qmi_client_ready (MMPortMbim *mbim,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ InitializationStartedContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_mbim_allocate_qmi_client_finish (mbim, res, &error)) {
+ mm_obj_dbg (self, "couldn't allocate QMI client for service '%s': %s",
+ qmi_service_get_string (qmi_services[ctx->qmi_service_index]),
+ error->message);
+ g_error_free (error);
+ }
+
+ ctx->qmi_service_index++;
+ allocate_next_qmi_client (task);
+}
+
+static void
+allocate_next_qmi_client (GTask *task)
+{
+ InitializationStartedContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->qmi_service_index == G_N_ELEMENTS (qmi_services)) {
+ parent_initialization_started (task);
+ return;
+ }
+
+ /* Otherwise, allocate next client */
+ mm_port_mbim_allocate_qmi_client (ctx->mbim,
+ qmi_services[ctx->qmi_service_index],
+ NULL,
+ (GAsyncReadyCallback)mbim_port_allocate_qmi_client_ready,
+ task);
}
+#endif
+
static void
-mbim_port_open_ready (MMPortMbim *mbim,
+query_device_services_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+ MbimDeviceServiceElement **device_services;
+ guint32 device_services_count;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_device_services_response_parse (
+ response,
+ &device_services_count,
+ NULL, /* max_dss_sessions */
+ &device_services,
+ &error)) {
+ guint32 i;
+
+ for (i = 0; i < device_services_count; i++) {
+ MbimService service;
+ guint32 j;
+
+ service = mbim_uuid_to_service (&device_services[i]->device_service_id);
+
+ if (service == MBIM_SERVICE_BASIC_CONNECT) {
+ for (j = 0; j < device_services[i]->cids_count; j++) {
+ if (device_services[i]->cids[j] == MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS) {
+ mm_obj_dbg (self, "Profile management is supported");
+ self->priv->is_profile_management_supported = TRUE;
+ }
+ }
+ continue;
+ }
+
+ if (service == MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS) {
+ for (j = 0; j < device_services[i]->cids_count; j++) {
+ if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO) {
+ mm_obj_dbg (self, "PCO is supported");
+ self->priv->is_pco_supported = TRUE;
+ } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO) {
+ mm_obj_dbg (self, "LTE attach info is supported");
+ self->priv->is_lte_attach_info_supported = TRUE;
+ } else if (device_services[i]->cids[j] == MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS) {
+ mm_obj_dbg (self, "Slot info status is supported");
+ self->priv->is_slot_info_status_supported = TRUE;
+ }
+ }
+ continue;
+ }
+
+ if (service == MBIM_SERVICE_USSD) {
+ for (j = 0; j < device_services[i]->cids_count; j++) {
+ if (device_services[i]->cids[j] == MBIM_CID_USSD) {
+ mm_obj_dbg (self, "USSD is supported");
+ self->priv->is_ussd_supported = TRUE;
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (service == MBIM_SERVICE_ATDS) {
+ for (j = 0; j < device_services[i]->cids_count; j++) {
+ if (device_services[i]->cids[j] == MBIM_CID_ATDS_LOCATION) {
+ mm_obj_dbg (self, "ATDS location is supported");
+ self->priv->is_atds_location_supported = TRUE;
+ } else if (device_services[i]->cids[j] == MBIM_CID_ATDS_SIGNAL) {
+ mm_obj_dbg (self, "ATDS signal is supported");
+ self->priv->is_atds_signal_supported = TRUE;
+ }
+ }
+ continue;
+ }
+
+ if (service == MBIM_SERVICE_INTEL_FIRMWARE_UPDATE) {
+ if (self->priv->intel_firmware_update_unsupported) {
+ mm_obj_dbg (self, "Intel firmware update service is explicitly ignored");
+ continue;
+ }
+ for (j = 0; j < device_services[i]->cids_count; j++) {
+ if (device_services[i]->cids[j] == MBIM_CID_INTEL_FIRMWARE_UPDATE_MODEM_REBOOT) {
+ mm_obj_dbg (self, "Intel reset is supported");
+ self->priv->is_intel_reset_supported = TRUE;
+ }
+ }
+ continue;
+ }
+
+ if (service == MBIM_SERVICE_MS_SAR) {
+ for (j = 0; j < device_services[i]->cids_count; j++) {
+ if (device_services[i]->cids[j] == MBIM_CID_MS_SAR_CONFIG) {
+ mm_obj_dbg (self, "SAR is supported");
+ self->priv->is_ms_sar_supported = TRUE;
+ }
+ }
+ continue;
+ }
+
+ /* no optional features to check in remaining services */
+ }
+ mbim_device_service_element_array_free (device_services);
+ } else {
+ /* Ignore error */
+ mm_obj_warn (self, "couldn't query device services: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (response)
+ mbim_message_unref (response);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ allocate_next_qmi_client (task);
+#else
+ parent_initialization_started (task);
+#endif
+}
+
+static void
+query_device_services (GTask *task)
+{
+ MMBroadbandModem *self;
+ InitializationStartedContext *ctx;
+ MbimMessage *message;
+ MbimDevice *device;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ device = mm_port_mbim_peek_device (ctx->mbim);
+ g_assert (device);
+
+ mm_obj_dbg (self, "querying device services...");
+
+ message = mbim_message_device_services_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)query_device_services_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+static void
+mbim_device_removed_cb (MbimDevice *device,
+ MMBroadbandModemMbim *self)
+{
+ /* We have to do a full re-probe here because simply reopening the device
+ * and restarting mbim-proxy will leave us without MBIM notifications. */
+ mm_obj_info (self, "connection to mbim-proxy for %s lost, reprobing",
+ mbim_device_get_path_display (device));
+
+ g_signal_handler_disconnect (device,
+ self->priv->mbim_device_removed_id);
+ self->priv->mbim_device_removed_id = 0;
+
+ mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE);
+ mm_base_modem_set_valid (MM_BASE_MODEM (self), FALSE);
+}
+
+static void
+track_mbim_device_removed (MMBroadbandModemMbim *self,
+ MMPortMbim *mbim)
+{
+ MbimDevice *device;
+
+ device = mm_port_mbim_peek_device (mbim);
+ g_assert (device);
+
+ /* Register removal handler so we can handle mbim-proxy crashes */
+ self->priv->mbim_device_removed_id = g_signal_connect (
+ device,
+ MBIM_DEVICE_SIGNAL_REMOVED,
+ G_CALLBACK (mbim_device_removed_cb),
+ self);
+}
+
+static void
+untrack_mbim_device_removed (MMBroadbandModemMbim *self,
+ MMPortMbim *mbim)
+{
+ MbimDevice *device;
+
+ if (self->priv->mbim_device_removed_id == 0)
+ return;
+
+ device = mm_port_mbim_peek_device (mbim);
+ if (!device)
+ return;
+
+ g_signal_handler_disconnect (device, self->priv->mbim_device_removed_id);
+ self->priv->mbim_device_removed_id = 0;
+}
+
+static void
+mbim_port_open_ready (MMPortMbim *mbim,
GAsyncResult *res,
- InitializationStartedContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!mm_port_mbim_open_finish (mbim, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- initialization_started_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* Done we are, launch parent's callback */
- parent_initialization_started (ctx);
+ /* Make sure we know if mbim-proxy dies on us, and then do the parent's
+ * initialization */
+ track_mbim_device_removed (MM_BROADBAND_MODEM_MBIM (g_task_get_source_object (task)), mbim);
+ query_device_services (task);
}
static void
-initialization_started (MMBroadbandModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+initialization_open_port (GTask *task)
{
InitializationStartedContext *ctx;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ MMBroadbandModemMbim *self;
+ gboolean qmi_unsupported = FALSE;
+#endif
+
+ ctx = g_task_get_task_data (task);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ self = g_task_get_source_object (task);
+ g_object_get (self,
+ MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED, &qmi_unsupported,
+ NULL);
+#endif
+
+ /* Now open our MBIM port */
+ mm_port_mbim_open (ctx->mbim,
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ ! qmi_unsupported, /* With QMI over MBIM support if available */
+#endif
+ NULL,
+ (GAsyncReadyCallback)mbim_port_open_ready,
+ task);
+}
+
+static void
+reset_ports_ready (MMBroadbandModemMbim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!reset_ports_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ initialization_open_port (task);
+}
+
+static void
+initialization_reset_ports (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+
+ self = g_task_get_source_object (task);
+
+ /* reseting the data interfaces is really only needed if the device
+ * hasn't been hotplugged */
+ if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (self))) {
+ mm_obj_dbg (self, "not running data interface reset procedure: device is hotplugged");
+ initialization_open_port (task);
+ return;
+ }
+
+ reset_ports (self, (GAsyncReadyCallback)reset_ports_ready, task);
+}
+
+static void
+initialization_started (MMBroadbandModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationStartedContext *ctx;
+ GTask *task;
ctx = g_slice_new0 (InitializationStartedContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- initialization_started);
- ctx->mbim = mm_base_modem_get_port_mbim (MM_BASE_MODEM (self));
+ ctx->mbim = mm_broadband_modem_mbim_get_port_mbim (MM_BROADBAND_MODEM_MBIM (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_started_context_free);
/* This may happen if we unplug the modem unexpectedly */
if (!ctx->mbim) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot initialize: MBIM port went missing");
- initialization_started_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot initialize: MBIM port went missing");
+ g_object_unref (task);
return;
}
if (mm_port_mbim_is_open (ctx->mbim)) {
- /* Nothing to be done, just launch parent's callback */
- parent_initialization_started (ctx);
+ /* Nothing to be done, just connect to a signal and launch parent's
+ * callback */
+ track_mbim_device_removed (MM_BROADBAND_MODEM_MBIM (self), ctx->mbim);
+ query_device_services (task);
return;
}
- /* Now open our MBIM port */
- mm_port_mbim_open (ctx->mbim,
- NULL,
- (GAsyncReadyCallback)mbim_port_open_ready,
- ctx);
+ initialization_reset_ports (task);
}
/*****************************************************************************/
@@ -1366,10 +2852,7 @@ modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -1378,24 +2861,19 @@ modem_3gpp_load_imei (MMIfaceModem3gpp *_self,
gpointer user_data)
{
MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_imei);
+ GTask *task;
+ task = g_task_new (self, NULL, callback, user_data);
if (self->priv->caps_device_id)
- g_simple_async_result_set_op_res_gpointer (result,
- self->priv->caps_device_id,
- NULL);
+ g_task_return_pointer (task,
+ g_strdup (self->priv->caps_device_id),
+ g_free);
else
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Device doesn't report a valid IMEI");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Device doesn't report a valid IMEI");
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -1406,17 +2884,21 @@ modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_3GPP_FACILITY_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return ((MMModem3gppFacility) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_3GPP_FACILITY_NONE;
+ }
+ return (MMModem3gppFacility)value;
}
static void
pin_list_query_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -1431,7 +2913,7 @@ pin_list_query_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_pin_list_response_parse (
response,
&pin_desc_pin1,
@@ -1479,16 +2961,14 @@ pin_list_query_ready (MbimDevice *device,
mask |= MM_MODEM_3GPP_FACILITY_CORP_PERS;
mbim_pin_desc_free (pin_desc_corporate_pin);
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (mask),
- NULL);
+ g_task_return_int (task, mask);
} else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -1496,17 +2976,14 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MbimDevice *device;
MbimMessage *message;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_enabled_facility_locks);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_pin_list_query_new (NULL);
mbim_device_command (device,
@@ -1514,7 +2991,556 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
10,
NULL,
(GAsyncReadyCallback)pin_list_query_ready,
- result);
+ task);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Facility locks disabling (3GPP interface) */
+
+typedef struct _DisableFacilityLockContext DisableFacilityLockContext;
+struct _DisableFacilityLockContext {
+ MbimPinType pin_type;
+};
+
+static gboolean
+modem_3gpp_disable_facility_lock_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_facility_lock_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisableFacilityLockContext *ctx;
+ MbimMessage *response = NULL;
+ guint32 remaining_attempts;
+ MbimPinState pin_state;
+ MbimPinType pin_type;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response,
+ MBIM_MESSAGE_TYPE_COMMAND_DONE,
+ &error)) {
+ g_task_return_error (task, error);
+ } else if (!mbim_message_pin_response_parse (response,
+ &pin_type,
+ &pin_state,
+ &remaining_attempts,
+ &error)) {
+ g_task_return_error (task, error);
+ } else if (pin_type == ctx->pin_type &&
+ pin_state == MBIM_PIN_STATE_LOCKED) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Disabling PIN lock %s failed, remaining attempts: %u",
+ mbim_pin_state_get_string (pin_state),
+ remaining_attempts);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (response)
+ mbim_message_unref (response);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_disable_facility_lock (MMIfaceModem3gpp *self,
+ MMModem3gppFacility facility,
+ guint8 slot,
+ const gchar *key,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisableFacilityLockContext *ctx;
+ MbimMessage *message;
+ MbimPinType pin_type;
+ MbimDevice *device;
+ GTask *task;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Set type of pin lock to disable */
+ pin_type = mbim_pin_type_from_mm_modem_3gpp_facility (facility);
+ if (pin_type == MBIM_PIN_TYPE_UNKNOWN) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Not supported type of facility lock.");
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "Trying to disable %s lock using key: %s",
+ mbim_pin_type_get_string (pin_type), key);
+
+ ctx = g_new0 (DisableFacilityLockContext, 1);
+ ctx->pin_type = pin_type;
+ g_task_set_task_data (task, ctx, g_free);
+
+ message = mbim_message_pin_set_new (pin_type,
+ MBIM_PIN_OPERATION_DISABLE,
+ key,
+ NULL,
+ NULL);
+
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)disable_facility_lock_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer info loading */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static MMBearerProperties *
+common_process_lte_attach_info (MMBroadbandModemMbim *self,
+ guint32 lte_attach_state,
+ guint32 ip_type,
+ const gchar *access_string,
+ const gchar *user_name,
+ const gchar *password,
+ guint32 compression,
+ guint32 auth_protocol,
+ GError **error)
+{
+ MMBearerProperties *properties;
+ MMBearerIpFamily ip_family;
+ MMBearerAllowedAuth auth;
+
+ /* Remove LTE attach bearer info */
+ if (lte_attach_state == MBIM_LTE_ATTACH_STATE_DETACHED) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Not attached to LTE");
+ return NULL;
+ }
+
+ properties = mm_bearer_properties_new ();
+ if (access_string)
+ mm_bearer_properties_set_apn (properties, access_string);
+ if (user_name)
+ mm_bearer_properties_set_user (properties, user_name);
+ if (password)
+ mm_bearer_properties_set_password (properties, password);
+
+ ip_family = mm_bearer_ip_family_from_mbim_context_ip_type (ip_type);
+ if (ip_family != MM_BEARER_IP_FAMILY_NONE)
+ mm_bearer_properties_set_ip_type (properties, ip_family);
+
+ auth = mm_bearer_allowed_auth_from_mbim_auth_protocol (auth_protocol);
+ if (auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN)
+ mm_bearer_properties_set_allowed_auth (properties, auth);
+
+ /* note: we don't expose compression settings */
+
+ return properties;
+}
+
+static void
+lte_attach_info_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ MMBearerProperties *properties;
+ guint32 lte_attach_state;
+ guint32 ip_type;
+ g_autofree gchar *access_string = NULL;
+ g_autofree gchar *user_name = NULL;
+ g_autofree gchar *password = NULL;
+ guint32 compression;
+ guint32 auth_protocol;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_lte_attach_info_response_parse (
+ response,
+ &lte_attach_state,
+ &ip_type,
+ &access_string,
+ &user_name,
+ &password,
+ &compression,
+ &auth_protocol,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ properties = common_process_lte_attach_info (self,
+ lte_attach_state,
+ ip_type,
+ access_string,
+ user_name,
+ password,
+ compression,
+ auth_protocol,
+ &error);
+ g_assert (properties || error);
+ if (properties)
+ g_task_return_pointer (task, properties, g_object_unref);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ MbimMessage *message;
+ GTask *task;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->is_lte_attach_info_supported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "LTE attach status is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ message = mbim_message_ms_basic_connect_extensions_lte_attach_info_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)lte_attach_info_query_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer settings loading */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static MMBearerProperties *
+common_process_lte_attach_configuration (MMBroadbandModemMbim *self,
+ MbimLteAttachConfiguration *config,
+ GError **error)
+{
+ MMBearerProperties *properties;
+ MMBearerIpFamily ip_family = MM_BEARER_IP_FAMILY_NONE;
+ MMBearerAllowedAuth auth;
+
+ properties = mm_bearer_properties_new ();
+ if (config->access_string)
+ mm_bearer_properties_set_apn (properties, config->access_string);
+ if (config->user_name)
+ mm_bearer_properties_set_user (properties, config->user_name);
+ if (config->password)
+ mm_bearer_properties_set_password (properties, config->password);
+
+ ip_family = mm_bearer_ip_family_from_mbim_context_ip_type (config->ip_type);
+ if (ip_family != MM_BEARER_IP_FAMILY_NONE)
+ mm_bearer_properties_set_ip_type (properties, ip_family);
+
+ auth = mm_bearer_allowed_auth_from_mbim_auth_protocol (config->auth_protocol);
+ if (auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN)
+ mm_bearer_properties_set_allowed_auth (properties, auth);
+
+ /* note: we don't expose compression settings or the configuration source details */
+
+ return properties;
+}
+
+static void
+lte_attach_configuration_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+ MMBearerProperties *properties = NULL;
+ guint32 n_configurations = 0;
+ MbimLteAttachConfiguration **configurations = NULL;
+ guint i;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_lte_attach_configuration_response_parse (
+ response,
+ &n_configurations,
+ &configurations,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* We should always receive 3 configurations but the MBIM API doesn't force
+ * that so we'll just assume we don't get always the same fixed number */
+ for (i = 0; i < n_configurations; i++) {
+ /* We only support configuring the HOME settings */
+ if (configurations[i]->roaming != MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_HOME)
+ continue;
+ properties = common_process_lte_attach_configuration (self, configurations[i], &error);
+ break;
+ }
+ mbim_lte_attach_configuration_array_free (configurations);
+
+ if (!properties && !error)
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Couldn't find home network LTE attach settings");
+
+ g_assert (properties || error);
+ if (properties)
+ g_task_return_pointer (task, properties, g_object_unref);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+
+ out:
+ if (response)
+ mbim_message_unref (response);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ MbimMessage *message;
+ GTask *task;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->is_lte_attach_info_supported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "LTE attach status info is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ message = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)lte_attach_configuration_query_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Set initial EPS bearer settings
+ *
+ * The logic to set the EPS bearer settings requires us to first load the current
+ * settings from the module, because we are only going to change the settings
+ * associated to the HOME slot, we will leave untouched the PARTNER and NON-PARTNER
+ * slots.
+ */
+
+static gboolean
+modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_lte_attach_configuration_set_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ if (response)
+ mbim_message_unref (response);
+}
+
+static void
+before_set_lte_attach_configuration_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MbimMessage *request;
+ MbimMessage *response;
+ GError *error = NULL;
+ MMBearerProperties *config;
+ guint32 n_configurations = 0;
+ MbimLteAttachConfiguration **configurations = NULL;
+ guint i;
+
+ self = g_task_get_source_object (task);
+ config = g_task_get_task_data (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_lte_attach_configuration_response_parse (
+ response,
+ &n_configurations,
+ &configurations,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* We should always receive 3 configurations but the MBIM API doesn't force
+ * that so we'll just assume we don't get always the same fixed number */
+ for (i = 0; i < n_configurations; i++) {
+ MMBearerIpFamily ip_family;
+ MMBearerAllowedAuth auth;
+
+ /* We only support configuring the HOME settings */
+ if (configurations[i]->roaming != MBIM_LTE_ATTACH_CONTEXT_ROAMING_CONTROL_HOME)
+ continue;
+
+ ip_family = mm_bearer_properties_get_ip_type (config);
+ if (ip_family == MM_BEARER_IP_FAMILY_NONE || ip_family == MM_BEARER_IP_FAMILY_ANY)
+ configurations[i]->ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+ else {
+ configurations[i]->ip_type = mm_bearer_ip_family_to_mbim_context_ip_type (ip_family, &error);
+ if (error) {
+ configurations[i]->ip_type = MBIM_CONTEXT_IP_TYPE_DEFAULT;
+ mm_obj_warn (self, "unexpected IP type settings requested: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ auth = mm_bearer_properties_get_allowed_auth (config);
+ if (auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN)
+ configurations[i]->auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+ else {
+ configurations[i]->auth_protocol = mm_bearer_allowed_auth_to_mbim_auth_protocol (auth, self, &error);
+ if (error) {
+ configurations[i]->auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+ mm_obj_warn (self, "unexpected auth settings requested: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ g_clear_pointer (&(configurations[i]->access_string), g_free);
+ configurations[i]->access_string = g_strdup (mm_bearer_properties_get_apn (config));
+
+ g_clear_pointer (&(configurations[i]->user_name), g_free);
+ configurations[i]->user_name = g_strdup (mm_bearer_properties_get_user (config));
+
+ g_clear_pointer (&(configurations[i]->password), g_free);
+ configurations[i]->password = g_strdup (mm_bearer_properties_get_password (config));
+
+ configurations[i]->source = MBIM_CONTEXT_SOURCE_USER;
+ configurations[i]->compression = MBIM_COMPRESSION_NONE;
+ break;
+ }
+
+ request = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_set_new (
+ MBIM_LTE_ATTACH_CONTEXT_OPERATION_DEFAULT,
+ n_configurations,
+ (const MbimLteAttachConfiguration *const *)configurations,
+ &error);
+ if (!request) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+ mbim_device_command (device,
+ request,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)set_lte_attach_configuration_set_ready,
+ task);
+ mbim_message_unref (request);
+
+ out:
+ if (configurations)
+ mbim_lte_attach_configuration_array_free (configurations);
+
+ if (response)
+ mbim_message_unref (response);
+}
+
+static void
+modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self,
+ MMBearerProperties *config,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
+ MbimDevice *device;
+ MbimMessage *message;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->is_lte_attach_info_supported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "LTE attach configuration is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_set_task_data (task, g_object_ref (config), g_object_unref);
+
+ message = mbim_message_ms_basic_connect_extensions_lte_attach_configuration_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)before_set_lte_attach_configuration_query_ready,
+ task);
mbim_message_unref (message);
}
@@ -1523,26 +3549,64 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
static void
basic_connect_notification_signal_state (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+ MbimDevice *device,
+ MbimMessage *notification)
{
- guint32 rssi;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(MbimRsrpSnrInfoArray) rsrp_snr = NULL;
+ guint32 rsrp_snr_count = 0;
+ guint32 coded_rssi;
+ guint32 coded_error_rate;
+ guint32 quality;
+ MbimDataClass data_class;
+ g_autoptr(MMSignal) cdma = NULL;
+ g_autoptr(MMSignal) evdo = NULL;
+ g_autoptr(MMSignal) gsm = NULL;
+ g_autoptr(MMSignal) umts = NULL;
+ g_autoptr(MMSignal) lte = NULL;
+ g_autoptr(MMSignal) nr5g = NULL;
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ if (!mbim_message_ms_basic_connect_v2_signal_state_notification_parse (
+ notification,
+ &coded_rssi,
+ &coded_error_rate,
+ NULL, /* signal_strength_interval */
+ NULL, /* rssi_threshold */
+ NULL, /* error_rate_threshold */
+ &rsrp_snr_count,
+ &rsrp_snr,
+ &error)) {
+ mm_obj_warn (self, "failed processing MBIMEx v2.0 signal state indication: %s", error->message);
+ return;
+ }
+ mm_obj_dbg (self, "proccessed MBIMEx v2.0 signal state indication");
+ } else {
+ if (!mbim_message_signal_state_notification_parse (
+ notification,
+ &coded_rssi,
+ &coded_error_rate,
+ NULL, /* signal_strength_interval */
+ NULL, /* rssi_threshold */
+ NULL, /* error_rate_threshold */
+ &error)) {
+ mm_obj_warn (self, "failed processing signal state indication: %s", error->message);
+ return;
+ }
+ mm_obj_dbg (self, "proccessed signal state indication");
+ }
- if (mbim_message_signal_state_notification_parse (
- notification,
- &rssi,
- NULL, /* error_rate */
- NULL, /* signal_strength_interval */
- NULL, /* rssi_threshold */
- NULL, /* error_rate_threshold */
- NULL)) {
- guint32 quality;
+ quality = mm_signal_quality_from_mbim_signal_state (coded_rssi, rsrp_snr, rsrp_snr_count, self);
+ mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
- /* Normalize the quality. 99 means unknown, we default it to 0 */
- quality = CLAMP (rssi == 99 ? 0 : rssi, 0, 31) * 100 / 31;
+ /* Best guess of current data class */
+ data_class = self->priv->highest_available_data_class;
+ if (data_class == 0)
+ data_class = self->priv->available_data_classes;
- mm_dbg ("Signal state indication: %u --> %u%%", rssi, quality);
- mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
- }
+ if (mm_signal_from_mbim_signal_state (data_class, coded_rssi, coded_error_rate, rsrp_snr, rsrp_snr_count,
+ self, &cdma, &evdo, &gsm, &umts, &lte, &nr5g))
+ mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), cdma, evdo, gsm, umts, lte, nr5g);
}
static void
@@ -1559,12 +3623,19 @@ update_access_technologies (MMBroadbandModemMbim *self)
static void
update_registration_info (MMBroadbandModemMbim *self,
- MbimRegisterState state,
- MbimDataClass available_data_classes,
- gchar *operator_id_take,
- gchar *operator_name_take)
+ MbimRegisterState state,
+ MbimDataClass available_data_classes,
+ gchar *operator_id_take,
+ gchar *operator_name_take)
{
MMModem3gppRegistrationState reg_state;
+ MMModem3gppRegistrationState reg_state_cs;
+ MMModem3gppRegistrationState reg_state_ps;
+ MMModem3gppRegistrationState reg_state_eps;
+ MMModem3gppRegistrationState reg_state_5gs;
+ gboolean operator_updated = FALSE;
+
+ self->priv->reg_state = state;
reg_state = mm_modem_3gpp_registration_state_from_mbim_register_state (state);
@@ -1574,6 +3645,7 @@ update_registration_info (MMBroadbandModemMbim *self,
g_str_equal (self->priv->current_operator_id, operator_id_take)) {
g_free (operator_id_take);
} else {
+ operator_updated = TRUE;
g_free (self->priv->current_operator_id);
self->priv->current_operator_id = operator_id_take;
}
@@ -1582,61 +3654,116 @@ update_registration_info (MMBroadbandModemMbim *self,
g_str_equal (self->priv->current_operator_name, operator_name_take)) {
g_free (operator_name_take);
} else {
+ operator_updated = TRUE;
g_free (self->priv->current_operator_name);
self->priv->current_operator_name = operator_name_take;
}
} else {
- if (self->priv->current_operator_id) {
- g_free (self->priv->current_operator_id);
- self->priv->current_operator_id = NULL;
- }
- if (self->priv->current_operator_name) {
- g_free (self->priv->current_operator_name);
- self->priv->current_operator_name = NULL;
- }
+ if (self->priv->current_operator_id || self->priv->current_operator_name)
+ operator_updated = TRUE;
+ g_clear_pointer (&self->priv->current_operator_id, g_free);
+ g_clear_pointer (&self->priv->current_operator_name, g_free);
g_free (operator_id_take);
g_free (operator_name_take);
}
- mm_iface_modem_3gpp_update_ps_registration_state (
- MM_IFACE_MODEM_3GPP (self),
- reg_state);
+ reg_state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
+ reg_state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
+ reg_state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
+ reg_state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
+
+ if (available_data_classes & (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE |
+ MBIM_DATA_CLASS_UMTS | MBIM_DATA_CLASS_HSDPA | MBIM_DATA_CLASS_HSUPA)) {
+ reg_state_cs = reg_state;
+ if (self->priv->packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED)
+ reg_state_ps = reg_state;
+ }
+
+ if (available_data_classes & (MBIM_DATA_CLASS_LTE))
+ reg_state_eps = reg_state;
+
+ if (available_data_classes & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA))
+ reg_state_5gs = reg_state;
+
+ mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_cs);
+ mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_ps);
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_eps);
+ if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_5gs);
self->priv->available_data_classes = available_data_classes;
update_access_technologies (self);
+
+ /* request to reload operator info explicitly, so that the new
+ * operator name and code is propagated to the DBus interface */
+ if (operator_updated)
+ mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL);
}
static void
basic_connect_notification_register_state (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+ MbimDevice *device,
+ MbimMessage *notification)
{
- MbimRegisterState register_state;
- MbimDataClass available_data_classes;
- gchar *provider_id;
- gchar *provider_name;
-
- if (mbim_message_register_state_notification_parse (
- notification,
- NULL, /* nw_error */
- &register_state,
- NULL, /* register_mode */
- &available_data_classes,
- NULL, /* current_cellular_class */
- &provider_id,
- &provider_name,
- NULL, /* roaming_text */
- NULL, /* registration_flag */
- NULL)) {
- update_registration_info (self,
- register_state,
- available_data_classes,
- provider_id,
- provider_name);
+ g_autoptr(GError) error = NULL;
+ MbimRegisterState register_state;
+ MbimDataClass available_data_classes;
+ gchar *provider_id;
+ gchar *provider_name;
+ MbimDataClass preferred_data_classes = 0;
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ if (!mbim_message_ms_basic_connect_v2_register_state_notification_parse (
+ notification,
+ NULL, /* nw_error */
+ &register_state,
+ NULL, /* register_mode */
+ &available_data_classes,
+ NULL, /* current_cellular_class */
+ &provider_id,
+ &provider_name,
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ &preferred_data_classes,
+ &error)) {
+ mm_obj_warn (self, "failed processing MBIMEx v2.0 register state indication: %s", error->message);
+ return;
+ }
+ mm_obj_dbg (self, "proccessed MBIMEx v2.0 register state indication");
+ } else {
+ if (!mbim_message_register_state_notification_parse (
+ notification,
+ NULL, /* nw_error */
+ &register_state,
+ NULL, /* register_mode */
+ &available_data_classes,
+ NULL, /* current_cellular_class */
+ &provider_id,
+ &provider_name,
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ NULL)) {
+ mm_obj_warn (self, "failed processing register state indication: %s", error->message);
+ return;
+ }
+ mm_obj_dbg (self, "proccessed register state indication");
}
+
+ update_registration_info (self,
+ register_state,
+ available_data_classes,
+ provider_id,
+ provider_name);
+
+ if (preferred_data_classes)
+ complete_pending_allowed_modes_action (self, preferred_data_classes);
}
typedef struct {
- guint32 session_id;
+ MMBroadbandModemMbim *self;
+ guint32 session_id;
+ GError *connection_error;
} ReportDisconnectedStatusContext;
static void
@@ -1647,19 +3774,21 @@ bearer_list_report_disconnected_status (MMBaseBearer *bearer,
if (MM_IS_BEARER_MBIM (bearer) &&
mm_bearer_mbim_get_session_id (MM_BEARER_MBIM (bearer)) == ctx->session_id) {
- mm_dbg ("Bearer '%s' was disconnected.", mm_base_bearer_get_path (bearer));
- mm_base_bearer_report_connection_status (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ mm_obj_dbg (ctx->self, "bearer '%s' was disconnected.", mm_base_bearer_get_path (bearer));
+ mm_base_bearer_report_connection_status_detailed (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED, ctx->connection_error);
}
}
static void
basic_connect_notification_connect (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+ MbimDevice *device,
+ MbimMessage *notification)
{
- guint32 session_id;
- MbimActivationState activation_state;
- const MbimUuid *context_type;
- MMBearerList *bearer_list;
+ guint32 session_id;
+ MbimActivationState activation_state;
+ const MbimUuid *context_type;
+ guint32 nw_error;
+ g_autoptr(MMBearerList) bearer_list = NULL;
if (!mbim_message_connect_notification_parse (
notification,
@@ -1668,7 +3797,7 @@ basic_connect_notification_connect (MMBroadbandModemMbim *self,
NULL, /* voice_call_state */
NULL, /* ip_type */
&context_type,
- NULL, /* nw_error */
+ &nw_error,
NULL)) {
return;
}
@@ -1682,21 +3811,26 @@ basic_connect_notification_connect (MMBroadbandModemMbim *self,
if (mbim_uuid_to_context_type (context_type) == MBIM_CONTEXT_TYPE_INTERNET &&
activation_state == MBIM_ACTIVATION_STATE_DEACTIVATED) {
- ReportDisconnectedStatusContext ctx;
+ ReportDisconnectedStatusContext ctx;
+ g_autoptr(GError) connection_error = NULL;
- mm_dbg ("Session ID '%u' was deactivated.", session_id);
- ctx.session_id = session_id;
- mm_bearer_list_foreach (bearer_list,
- (MMBearerListForeachFunc)bearer_list_report_disconnected_status,
- &ctx);
- }
+ connection_error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error, self);
+
+ mm_obj_dbg (self, "session ID '%u' was deactivated: %s", session_id, connection_error->message);
- g_object_unref (bearer_list);
+ ctx.self = self;
+ ctx.session_id = session_id;
+ ctx.connection_error = connection_error;
+ mm_bearer_list_foreach (bearer_list,
+ (MMBearerListForeachFunc)bearer_list_report_disconnected_status,
+ &ctx);
+ }
}
static void
basic_connect_notification_subscriber_ready_status (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+ MbimDevice *device,
+ MbimMessage *notification)
{
MbimSubscriberReadyState ready_state;
gchar **telephone_numbers;
@@ -1716,50 +3850,146 @@ basic_connect_notification_subscriber_ready_status (MMBroadbandModemMbim *self,
if (ready_state == MBIM_SUBSCRIBER_READY_STATE_INITIALIZED)
mm_iface_modem_update_own_numbers (MM_IFACE_MODEM (self), telephone_numbers);
- /* TODO: handle SIM removal using MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED */
+ if ((self->priv->last_ready_state != MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED &&
+ ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED) ||
+ (self->priv->last_ready_state == MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED &&
+ ready_state != MBIM_SUBSCRIBER_READY_STATE_SIM_NOT_INSERTED)) {
+ /* SIM has been removed or reinserted, re-probe to ensure correct interfaces are exposed */
+ mm_obj_dbg (self, "SIM hot swap detected");
+ mm_broadband_modem_sim_hot_swap_detected (MM_BROADBAND_MODEM (self));
+ }
+ self->priv->last_ready_state = ready_state;
g_strfreev (telephone_numbers);
}
+typedef struct {
+ MMBroadbandModemMbim *self;
+ guint64 uplink_speed;
+ guint64 downlink_speed;
+} ReportSpeedsContext;
+
static void
-basic_connect_notification_packet_service (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+bearer_list_report_speeds (MMBaseBearer *bearer,
+ gpointer user_data)
{
- MbimPacketServiceState packet_service_state;
- MbimDataClass highest_available_data_class;
- gchar *str;
+ ReportSpeedsContext *ctx = user_data;
- if (!mbim_message_packet_service_notification_parse (
- notification,
- NULL, /* nw_error */
- &packet_service_state,
- &highest_available_data_class,
- NULL, /* uplink_speed */
- NULL, /* downlink_speed */
- NULL)) {
- return;
+ if (MM_IS_BEARER_MBIM (bearer)) {
+ mm_obj_dbg (ctx->self, "bearer '%s' speeds updated", mm_base_bearer_get_path (bearer));
+ mm_base_bearer_report_speeds (bearer, ctx->uplink_speed, ctx->downlink_speed);
+ }
+}
+
+static void
+basic_connect_notification_packet_service (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
+{
+ guint32 nw_error;
+ MbimPacketServiceState packet_service_state;
+ MbimDataClass data_class;
+ guint64 uplink_speed;
+ guint64 downlink_speed;
+ MbimFrequencyRange frequency_range = MBIM_FREQUENCY_RANGE_UNKNOWN;
+ g_autofree gchar *data_class_str = NULL;
+ g_autofree gchar *frequency_range_str = NULL;
+ const gchar *nw_error_str;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(MMBearerList) bearer_list = NULL;
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ if (!mbim_message_ms_basic_connect_v2_packet_service_notification_parse (
+ notification,
+ &nw_error,
+ &packet_service_state,
+ &data_class, /* current */
+ &uplink_speed,
+ &downlink_speed,
+ &frequency_range,
+ &error)) {
+ mm_obj_warn (self, "failed processing MBIMEx v2.0 packet service indication: %s", error->message);
+ return;
+ }
+ mm_obj_dbg (self, "proccessed MBIMEx v2.0 packet service indication");
+ } else {
+ if (!mbim_message_packet_service_notification_parse (
+ notification,
+ &nw_error,
+ &packet_service_state,
+ &data_class, /* highest_available */
+ &uplink_speed,
+ &downlink_speed,
+ &error)) {
+ mm_obj_warn (self, "failed processing packet service indication: %s", error->message);
+ return;
+ }
+ mm_obj_dbg (self, "proccessed packet service indication");
}
- str = mbim_data_class_build_string_from_mask (highest_available_data_class);
- mm_dbg("Packet service state: '%s', data class: '%s'",
- mbim_packet_service_state_get_string(packet_service_state), str);
- g_free (str);
+ data_class_str = mbim_data_class_build_string_from_mask (data_class);
+ frequency_range_str = mbim_frequency_range_build_string_from_mask (frequency_range);
+ nw_error_str = mbim_nw_error_get_string (nw_error);
+
+ mm_obj_dbg (self, "packet service update:");
+ if (nw_error_str)
+ mm_obj_dbg (self, " nw error: '%s'", nw_error_str);
+ else
+ mm_obj_dbg (self, " nw error: '0x%x'", nw_error);
+ mm_obj_dbg (self, " state: '%s'", mbim_packet_service_state_get_string (packet_service_state));
+ mm_obj_dbg (self, " data class: '%s'", data_class_str);
+ mm_obj_dbg (self, " uplink: '%" G_GUINT64_FORMAT "' bps", uplink_speed);
+ mm_obj_dbg (self, " downlink: '%" G_GUINT64_FORMAT "' bps", downlink_speed);
+ mm_obj_dbg (self, " frequency range: '%s'", frequency_range_str);
if (packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED) {
- self->priv->highest_available_data_class = highest_available_data_class;
+ self->priv->highest_available_data_class = data_class;
} else if (packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED) {
- self->priv->highest_available_data_class = 0;
+ self->priv->highest_available_data_class = 0;
}
-
update_access_technologies (self);
+
+ if (self->priv->packet_service_state != packet_service_state) {
+ self->priv->packet_service_state = packet_service_state;
+ update_registration_info (self,
+ self->priv->reg_state,
+ self->priv->available_data_classes,
+ NULL,
+ NULL);
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_BEARER_LIST, &bearer_list,
+ NULL);
+ if (bearer_list) {
+ ReportSpeedsContext ctx = {
+ .uplink_speed = uplink_speed,
+ .downlink_speed = downlink_speed,
+ };
+
+ mm_bearer_list_foreach (bearer_list,
+ (MMBearerListForeachFunc)bearer_list_report_speeds,
+ &ctx);
+ }
+}
+
+static void
+basic_connect_notification_provisioned_contexts (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
+{
+ /* We don't even attempt to parse the indication, we just need to notify that
+ * something changed to the upper layers */
+ mm_iface_modem_3gpp_profile_manager_updated (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self));
}
static void add_sms_part (MMBroadbandModemMbim *self,
const MbimSmsPduReadRecord *pdu);
static void
-sms_notification_read_sms (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+sms_notification_read_flash_sms (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
{
MbimSmsFormat format;
guint32 messages_count;
@@ -1786,29 +4016,33 @@ sms_notification_read_sms (MMBroadbandModemMbim *self,
static void
basic_connect_notification (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+ MbimDevice *device,
+ MbimMessage *notification)
{
switch (mbim_message_indicate_status_get_cid (notification)) {
case MBIM_CID_BASIC_CONNECT_SIGNAL_STATE:
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY)
- basic_connect_notification_signal_state (self, notification);
+ basic_connect_notification_signal_state (self, device, notification);
break;
case MBIM_CID_BASIC_CONNECT_REGISTER_STATE:
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES)
- basic_connect_notification_register_state (self, notification);
+ basic_connect_notification_register_state (self, device, notification);
break;
case MBIM_CID_BASIC_CONNECT_CONNECT:
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_CONNECT)
- basic_connect_notification_connect (self, notification);
+ basic_connect_notification_connect (self, device, notification);
break;
case MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS:
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO)
- basic_connect_notification_subscriber_ready_status (self, notification);
+ basic_connect_notification_subscriber_ready_status (self, device, notification);
break;
case MBIM_CID_BASIC_CONNECT_PACKET_SERVICE:
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE)
- basic_connect_notification_packet_service (self, notification);
+ basic_connect_notification_packet_service (self, device, notification);
break;
+ case MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS:
+ if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS)
+ basic_connect_notification_provisioned_contexts (self, device, notification);
default:
/* Ignore */
break;
@@ -1827,7 +4061,7 @@ alert_sms_read_query_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_sms_read_response_parse (
response,
NULL,
@@ -1843,7 +4077,7 @@ alert_sms_read_query_ready (MbimDevice *device,
}
if (error) {
- mm_dbg ("Flash message reading failed: %s", error->message);
+ mm_obj_dbg (self, "flash message reading failed: %s", error->message);
g_error_free (error);
}
@@ -1854,21 +4088,21 @@ alert_sms_read_query_ready (MbimDevice *device,
}
static void
-sms_notification_read_alert_sms (MMBroadbandModemMbim *self,
- guint32 index)
+sms_notification_read_stored_sms (MMBroadbandModemMbim *self,
+ guint32 index)
{
MMPortMbim *port;
MbimDevice *device;
MbimMessage *message;
- port = mm_base_modem_peek_port_mbim (MM_BASE_MODEM (self));
+ port = mm_broadband_modem_mbim_peek_port_mbim (self);
if (!port)
return;
device = mm_port_mbim_peek_device (port);
if (!device)
return;
- mm_dbg ("Reading flash SMS at index '%u'", index);
+ mm_obj_dbg (self, "reading new SMS at index '%u'", index);
message = mbim_message_sms_read_query_new (MBIM_SMS_FORMAT_PDU,
MBIM_SMS_FLAG_INDEX,
index,
@@ -1884,28 +4118,29 @@ sms_notification_read_alert_sms (MMBroadbandModemMbim *self,
static void
sms_notification (MMBroadbandModemMbim *self,
- MbimMessage *notification)
+ MbimDevice *device,
+ MbimMessage *notification)
{
switch (mbim_message_indicate_status_get_cid (notification)) {
case MBIM_CID_SMS_READ:
+ /* New flash/alert message? */
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ)
- sms_notification_read_sms (self, notification);
+ sms_notification_read_flash_sms (self, device, notification);
break;
case MBIM_CID_SMS_MESSAGE_STORE_STATUS: {
MbimSmsStatusFlag flag;
guint32 index;
- /* New flash/alert message? */
if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ &&
mbim_message_sms_message_store_status_notification_parse (
notification,
&flag,
&index,
NULL)) {
- mm_dbg ("Received flash message: '%s'", mbim_sms_status_flag_get_string (flag));
+ mm_obj_dbg (self, "received SMS store status update: '%s'", mbim_sms_status_flag_get_string (flag));
if (flag == MBIM_SMS_STATUS_FLAG_NEW_MESSAGE)
- sms_notification_read_alert_sms (self, index);
+ sms_notification_read_stored_sms (self, index);
}
break;
}
@@ -1917,24 +4152,166 @@ sms_notification (MMBroadbandModemMbim *self,
}
static void
-device_notification_cb (MbimDevice *device,
- MbimMessage *notification,
- MMBroadbandModemMbim *self)
+ms_basic_connect_extensions_notification_pco (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
{
- MbimService service;
+ MbimPcoValue *pco_value;
+ GError *error = NULL;
+ gchar *pco_data_hex;
+ MMPco *pco;
- service = mbim_message_indicate_status_get_service (notification);
- mm_dbg ("Received notification (service '%s', command '%s')",
- mbim_service_get_string (service),
- mbim_cid_get_printable (service,
- mbim_message_indicate_status_get_cid (notification)));
-
- switch (service) {
- case MBIM_SERVICE_BASIC_CONNECT:
- basic_connect_notification (self, notification);
+ if (!mbim_message_ms_basic_connect_extensions_pco_notification_parse (
+ notification,
+ &pco_value,
+ &error)) {
+ mm_obj_warn (self, "couldn't parse PCO notification: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ pco_data_hex = mm_utils_bin2hexstr (pco_value->pco_data_buffer,
+ pco_value->pco_data_size);
+ mm_obj_dbg (self, "received PCO: session ID=%u type=%s size=%u data=%s",
+ pco_value->session_id,
+ mbim_pco_type_get_string (pco_value->pco_data_type),
+ pco_value->pco_data_size,
+ pco_data_hex);
+ g_free (pco_data_hex);
+
+ pco = mm_pco_new ();
+ mm_pco_set_session_id (pco, pco_value->session_id);
+ mm_pco_set_complete (pco,
+ pco_value->pco_data_type == MBIM_PCO_TYPE_COMPLETE);
+ mm_pco_set_data (pco,
+ pco_value->pco_data_buffer,
+ pco_value->pco_data_size);
+
+ self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco);
+ mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (self),
+ self->priv->pco_list);
+ g_object_unref (pco);
+ mbim_pco_value_free (pco_value);
+}
+
+static void
+ms_basic_connect_extensions_notification_lte_attach_info (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(MMBearerProperties) properties = NULL;
+ guint32 lte_attach_state;
+ guint32 ip_type;
+ g_autofree gchar *access_string = NULL;
+ g_autofree gchar *user_name = NULL;
+ g_autofree gchar *password = NULL;
+ guint32 compression;
+ guint32 auth_protocol;
+
+ if (!mbim_message_ms_basic_connect_extensions_lte_attach_info_notification_parse (
+ notification,
+ &lte_attach_state,
+ &ip_type,
+ &access_string,
+ &user_name,
+ &password,
+ &compression,
+ &auth_protocol,
+ &error)) {
+ mm_obj_warn (self, "couldn't parse LTE attach status notification: %s", error->message);
+ return;
+ }
+
+ properties = common_process_lte_attach_info (self,
+ lte_attach_state,
+ ip_type,
+ access_string,
+ user_name,
+ password,
+ compression,
+ auth_protocol,
+ NULL);
+ mm_iface_modem_3gpp_update_initial_eps_bearer (MM_IFACE_MODEM_3GPP (self), properties);
+}
+
+static void
+update_sim_from_slot_status (MMBroadbandModemMbim *self,
+ MbimUiccSlotState slot_status,
+ guint slot_index)
+{
+ g_autoptr(MMBaseSim) sim = NULL;
+
+ mm_obj_dbg (self, "Updating sim at slot %d", slot_index + 1);
+
+ /* Not fully ready (NOT_READY) or unusable (ERROR) SIM cards should also be
+ * reported as being available in the non-active slot. */
+ if (slot_status == MBIM_UICC_SLOT_STATE_ACTIVE ||
+ slot_status == MBIM_UICC_SLOT_STATE_ACTIVE_ESIM ||
+ slot_status == MBIM_UICC_SLOT_STATE_ACTIVE_ESIM_NO_PROFILES ||
+ slot_status == MBIM_UICC_SLOT_STATE_NOT_READY ||
+ slot_status == MBIM_UICC_SLOT_STATE_ERROR) {
+ sim = mm_sim_mbim_new_initialized (MM_BASE_MODEM (self),
+ slot_index,
+ FALSE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ }
+
+ mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), slot_index, sim);
+}
+
+static void
+ms_basic_connect_extensions_notification_slot_info_status (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
+{
+ g_autoptr(GError) error = NULL;
+ guint32 slot_index;
+ MbimUiccSlotState slot_state;
+
+ if (!mbim_message_ms_basic_connect_extensions_slot_info_status_notification_parse (
+ notification,
+ &slot_index,
+ &slot_state,
+ &error)) {
+ mm_obj_warn (self, "Couldn't parse slot info status notification: %s", error->message);
+ return;
+ }
+
+
+ if (self->priv->active_slot_index == slot_index + 1) {
+ /* Major SIM event on the active slot, will request reprobing the
+ * modem from scratch. */
+ mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
+ } else {
+ /* Modifies SIM object at the given slot based on the reported state,
+ * when the slot is not the active one. */
+ update_sim_from_slot_status (self, slot_state, slot_index);
+ }
+}
+
+static void
+ms_basic_connect_extensions_notification (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
+{
+ switch (mbim_message_indicate_status_get_cid (notification)) {
+ case MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO:
+ if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO)
+ ms_basic_connect_extensions_notification_pco (self, device, notification);
+ break;
+ case MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO:
+ if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO)
+ ms_basic_connect_extensions_notification_lte_attach_info (self, device, notification);
break;
- case MBIM_SERVICE_SMS:
- sms_notification (self, notification);
+ case MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS:
+ if (self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS)
+ ms_basic_connect_extensions_notification_slot_info_status (self, device, notification);
break;
default:
/* Ignore */
@@ -1942,38 +4319,71 @@ device_notification_cb (MbimDevice *device,
}
}
-static gboolean
-common_setup_cleanup_unsolicited_events_finish (MMBroadbandModemMbim *self,
- GAsyncResult *res,
- GError **error)
+static void
+process_ussd_notification (MMBroadbandModemMbim *self,
+ MbimMessage *notification);
+
+static void
+ussd_notification (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ MbimMessage *notification)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ if (mbim_message_indicate_status_get_cid (notification) != MBIM_CID_USSD) {
+ mm_obj_warn (self, "unexpected USSD notification (cid %u)", mbim_message_indicate_status_get_cid (notification));
+ return;
+ }
+
+ if (!(self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD))
+ return;
+
+ process_ussd_notification (self, notification);
}
static void
-common_setup_cleanup_unsolicited_events (MMBroadbandModemMbim *self,
- gboolean setup,
- GAsyncReadyCallback callback,
- gpointer user_data)
+device_notification_cb (MbimDevice *device,
+ MbimMessage *notification,
+ MMBroadbandModemMbim *self)
{
- MbimDevice *device;
- GSimpleAsyncResult *result;
+ MbimService service;
- if (!peek_device (self, &device, callback, user_data))
- return;
+ service = mbim_message_indicate_status_get_service (notification);
+ mm_obj_dbg (self, "received notification (service '%s', command '%s')",
+ mbim_service_get_string (service),
+ mbim_cid_get_printable (service,
+ mbim_message_indicate_status_get_cid (notification)));
+
+ if (service == MBIM_SERVICE_BASIC_CONNECT)
+ basic_connect_notification (self, device, notification);
+ else if (service == MBIM_SERVICE_MS_BASIC_CONNECT_EXTENSIONS)
+ ms_basic_connect_extensions_notification (self, device, notification);
+ else if (service == MBIM_SERVICE_SMS)
+ sms_notification (self, device, notification);
+ else if (service == MBIM_SERVICE_USSD)
+ ussd_notification (self, device, notification);
+}
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_setup_cleanup_unsolicited_events);
+static void
+common_setup_cleanup_unsolicited_events_sync (MMBroadbandModemMbim *self,
+ MbimDevice *device,
+ gboolean setup)
+{
+ if (!device)
+ return;
- mm_dbg ("Supported notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s)",
- self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no",
- self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no",
- self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no",
- self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no",
- self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no",
- self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no");
+ mm_obj_dbg (self, "supported notifications: signal (%s), registration (%s), sms (%s), "
+ "connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), "
+ "lte attach info (%s), provisioned contexts (%s), slot_info_status (%s)",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS ? "yes" : "no",
+ self->priv->setup_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS ? "yes" : "no");
if (setup) {
/* Don't re-enable it if already there */
@@ -1992,9 +4402,33 @@ common_setup_cleanup_unsolicited_events (MMBroadbandModemMbim *self,
self->priv->notification_id = 0;
}
}
+}
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+static gboolean
+common_setup_cleanup_unsolicited_events_finish (MMBroadbandModemMbim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+common_setup_cleanup_unsolicited_events (MMBroadbandModemMbim *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MbimDevice *device;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ common_setup_cleanup_unsolicited_events_sync (self, device, setup);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -2009,48 +4443,67 @@ common_setup_cleanup_unsolicited_events_3gpp_finish (MMIfaceModem3gpp *self,
}
static void
-cleanup_unsolicited_events_3gpp (MMIfaceModem3gpp *self,
+cleanup_unsolicited_events_3gpp (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT;
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
- common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), FALSE, callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT;
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+ if (self->priv->is_pco_supported)
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PCO;
+ if (self->priv->is_lte_attach_info_supported)
+ self->priv->setup_flags &= PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO;
+ if (self->priv->is_slot_info_status_supported)
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS;
+ common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data);
}
static void
-setup_unsolicited_events_3gpp (MMIfaceModem3gpp *self,
+setup_unsolicited_events_3gpp (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT;
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
- common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), TRUE, callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT;
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+ if (self->priv->is_pco_supported)
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PCO;
+ if (self->priv->is_lte_attach_info_supported)
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO;
+ if (self->priv->is_slot_info_status_supported)
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS;
+ common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data);
}
/*****************************************************************************/
/* Cleanup/Setup unsolicited registration events */
static void
-cleanup_unsolicited_registration_events (MMIfaceModem3gpp *self,
+cleanup_unsolicited_registration_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
- common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), FALSE, callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
+ common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data);
}
static void
-setup_unsolicited_registration_events (MMIfaceModem3gpp *self,
+setup_unsolicited_registration_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
- common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), TRUE, callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
+ common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data);
}
/*****************************************************************************/
@@ -2061,30 +4514,28 @@ common_enable_disable_unsolicited_events_finish (MMBroadbandModemMbim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
subscribe_list_set_ready_cb (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
response = mbim_device_command_finish (device, res, &error);
- if (response)
- mbim_message_command_done_get_result (response, &error);
+ if (response) {
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
+ mbim_message_unref (response);
+ }
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
-
- if (response)
- mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -2094,38 +4545,41 @@ common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self,
{
MbimMessage *request;
MbimDevice *device;
- GSimpleAsyncResult *result;
MbimEventEntry **entries;
guint n_entries = 0;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_enable_disable_unsolicited_events);
-
- mm_dbg ("Enabled notifications: signal (%s), registration (%s), sms (%s), connect (%s), subscriber (%s), packet (%s)",
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no",
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no",
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no",
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no",
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no",
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no");
-
- entries = g_new0 (MbimEventEntry *, 3);
+ mm_obj_dbg (self, "enabled notifications: signal (%s), registration (%s), sms (%s), "
+ "connect (%s), subscriber (%s), packet (%s), pco (%s), ussd (%s), "
+ "lte attach info (%s), provisioned contexts (%s), slot_info_status (%s)",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SMS_READ ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS ? "yes" : "no",
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS ? "yes" : "no");
+
+ entries = g_new0 (MbimEventEntry *, 5);
/* Basic connect service */
if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY ||
self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES ||
self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_CONNECT ||
self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO ||
- self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE) {
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE ||
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS) {
entries[n_entries] = g_new (MbimEventEntry, 1);
memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_BASIC_CONNECT, sizeof (MbimUuid));
entries[n_entries]->cids_count = 0;
- entries[n_entries]->cids = g_new0 (guint32, 5);
+ entries[n_entries]->cids = g_new0 (guint32, 6);
if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY)
entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_SIGNAL_STATE;
if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES)
@@ -2136,6 +4590,25 @@ common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self,
entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_SUBSCRIBER_READY_STATUS;
if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE)
entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_PACKET_SERVICE;
+ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS)
+ entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_BASIC_CONNECT_PROVISIONED_CONTEXTS;
+ n_entries++;
+ }
+
+ /* Basic connect extensions service */
+ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO ||
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO ||
+ self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS) {
+ entries[n_entries] = g_new (MbimEventEntry, 1);
+ memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_MS_BASIC_CONNECT_EXTENSIONS, sizeof (MbimUuid));
+ entries[n_entries]->cids_count = 0;
+ entries[n_entries]->cids = g_new0 (guint32, 3);
+ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_PCO)
+ entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_PCO;
+ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO)
+ entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_LTE_ATTACH_INFO;
+ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS)
+ entries[n_entries]->cids[entries[n_entries]->cids_count++] = MBIM_CID_MS_BASIC_CONNECT_EXTENSIONS_SLOT_INFO_STATUS;
n_entries++;
}
@@ -2150,6 +4623,18 @@ common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self,
n_entries++;
}
+ /* USSD service */
+ if (self->priv->enable_flags & PROCESS_NOTIFICATION_FLAG_USSD) {
+ entries[n_entries] = g_new (MbimEventEntry, 1);
+ memcpy (&(entries[n_entries]->device_service_id), MBIM_UUID_USSD, sizeof (MbimUuid));
+ entries[n_entries]->cids_count = 1;
+ entries[n_entries]->cids = g_new0 (guint32, 1);
+ entries[n_entries]->cids[0] = MBIM_CID_USSD;
+ n_entries++;
+ }
+
+ task = g_task_new (self, NULL, callback, user_data);
+
request = (mbim_message_device_service_subscribe_list_set_new (
n_entries,
(const MbimEventEntry *const *)entries,
@@ -2159,7 +4644,7 @@ common_enable_disable_unsolicited_events (MMBroadbandModemMbim *self,
10,
NULL,
(GAsyncReadyCallback)subscribe_list_set_ready_cb,
- result);
+ task);
mbim_message_unref (request);
mbim_event_entry_array_free (entries);
}
@@ -2176,28 +4661,155 @@ modem_3gpp_common_enable_disable_unsolicited_registration_events_finish (MMIface
}
static void
-modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *self,
+modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *_self,
gboolean cs_supported,
gboolean ps_supported,
gboolean eps_supported,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
- common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
}
static void
-modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *self,
+modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *_self,
gboolean cs_supported,
gboolean ps_supported,
gboolean eps_supported,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
- common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_REGISTRATION_UPDATES;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Setup SIM hot swap */
+
+typedef struct {
+ MbimDevice *device;
+ GError *subscriber_info_error;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ GError *qmi_error;
+#endif
+} SetupSimHotSwapContext;
+
+static void
+setup_sim_hot_swap_context_free (SetupSimHotSwapContext *ctx)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ g_clear_error (&ctx->qmi_error);
+#endif
+ g_clear_error (&ctx->subscriber_info_error);
+ g_clear_object (&ctx->device);
+ g_slice_free (SetupSimHotSwapContext, ctx);
+}
+
+static gboolean
+modem_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sim_hot_swap_complete (GTask *task)
+{
+ SetupSimHotSwapContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* If MBIM based logic worked, success */
+ if (!ctx->subscriber_info_error)
+ g_task_return_boolean (task, TRUE);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* Otherwise, If QMI-over-MBIM based logic worked, success */
+ else if (!ctx->qmi_error)
+ g_task_return_boolean (task, TRUE);
+#endif
+ /* Otherwise, prefer MBIM specific error */
+ else
+ g_task_return_error (task, g_steal_pointer (&ctx->subscriber_info_error));
+ g_object_unref (task);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+qmi_setup_sim_hot_swap_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetupSimHotSwapContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ if (!mm_shared_qmi_setup_sim_hot_swap_finish (self, res, &ctx->qmi_error))
+ mm_obj_dbg (self, "couldn't setup SIM hot swap using QMI over MBIM: %s", ctx->qmi_error->message);
+
+ sim_hot_swap_complete (task);
+}
+
+#endif
+
+static void
+enable_subscriber_info_unsolicited_events_ready (MMBroadbandModemMbim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetupSimHotSwapContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!common_enable_disable_unsolicited_events_finish (self, res, &ctx->subscriber_info_error)) {
+ mm_obj_dbg (self, "failed to enable subscriber info events: %s", ctx->subscriber_info_error->message);
+ /* reset setup flags if enabling failed */
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
+ common_setup_cleanup_unsolicited_events_sync (self, ctx->device, FALSE);
+ }
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ mm_shared_qmi_setup_sim_hot_swap (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)qmi_setup_sim_hot_swap_ready,
+ task);
+#else
+ sim_hot_swap_complete (task);
+#endif
+}
+
+static void
+modem_setup_sim_hot_swap (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ GTask *task;
+ SetupSimHotSwapContext *ctx;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (SetupSimHotSwapContext);
+ ctx->device = g_object_ref (device);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)setup_sim_hot_swap_context_free);
+
+ /* Setup flags synchronously, which never fails */
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
+ common_setup_cleanup_unsolicited_events_sync (self, ctx->device, TRUE);
+
+ /* Enable flags asynchronously, which may fail */
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
+ common_enable_disable_unsolicited_events (self,
+ (GAsyncReadyCallback)enable_subscriber_info_unsolicited_events_ready,
+ task);
}
/*****************************************************************************/
@@ -2212,180 +4824,255 @@ modem_3gpp_common_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *se
}
static void
-modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
+modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT;
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
- common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ gboolean is_sim_hot_swap_configured = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, &is_sim_hot_swap_configured,
+ NULL);
+
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_CONNECT;
+ if (is_sim_hot_swap_configured)
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+ if (self->priv->is_pco_supported)
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PCO;
+ if (self->priv->is_lte_attach_info_supported)
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO;
+ if (self->priv->is_slot_info_status_supported)
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
}
static void
-modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT;
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
- common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SIGNAL_QUALITY;
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_CONNECT;
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SUBSCRIBER_INFO;
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PACKET_SERVICE;
+ if (self->priv->is_pco_supported)
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PCO;
+ if (self->priv->is_lte_attach_info_supported)
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_LTE_ATTACH_INFO;
+ if (self->priv->is_slot_info_status_supported)
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SLOT_INFO_STATUS;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
}
/*****************************************************************************/
/* Load operator name (3GPP interface) */
static gchar *
-modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *_self,
+modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
-
- if (self->priv->current_operator_name)
- return g_strdup (self->priv->current_operator_name);
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Current operator name is still unknown");
- return NULL;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
+modem_3gpp_load_operator_name (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
- /* Just finish the async operation */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_operator_name);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ if (self->priv->current_operator_name)
+ g_task_return_pointer (task,
+ g_strdup (self->priv->current_operator_name),
+ g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Current operator name is still unknown");
+ g_object_unref (task);
}
/*****************************************************************************/
/* Load operator code (3GPP interface) */
static gchar *
-modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *_self,
+modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
-
- if (self->priv->current_operator_id)
- return g_strdup (self->priv->current_operator_id);
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Current operator MCC/MNC is still unknown");
- return NULL;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_3gpp_load_operator_code (MMIfaceModem3gpp *self,
+modem_3gpp_load_operator_code (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
- /* Just finish the async operation */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_operator_code);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ if (self->priv->current_operator_id)
+ g_task_return_pointer (task,
+ g_strdup (self->priv->current_operator_id),
+ g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Current operator MCC/MNC is still unknown");
+ g_object_unref (task);
}
/*****************************************************************************/
/* Registration checks (3GPP interface) */
static gboolean
-modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-register_state_query_ready (MbimDevice *device,
+atds_location_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ MMBroadbandModemMbim *self;
+ GError *error = NULL;
+ guint32 lac;
+ guint32 tac;
+ guint32 cid;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_atds_location_response_parse (response, &lac, &tac, &cid, &error)) {
+ g_task_return_error (task, error);
+ } else {
+ mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+register_state_query_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- MbimMessage *response;
- GError *error = NULL;
- MbimRegisterState register_state;
- MbimDataClass available_data_classes;
- gchar *provider_id;
- gchar *provider_name;
+ g_autoptr(MbimMessage) response = NULL;
+ MMBroadbandModemMbim *self;
+ GError *error = NULL;
+ MbimRegisterState register_state;
+ MbimDataClass available_data_classes;
+ gchar *provider_id;
+ gchar *provider_name;
+
+ self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
- if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_register_state_response_parse (
- response,
- NULL, /* nw_error */
- &register_state,
- NULL, /* register_mode */
- &available_data_classes,
- NULL, /* current_cellular_class */
- &provider_id,
- &provider_name,
- NULL, /* roaming_text */
- NULL, /* registration_flag */
- NULL)) {
- MMBroadbandModemMbim *self;
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- self = MM_BROADBAND_MODEM_MBIM (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
- update_registration_info (self,
- register_state,
- available_data_classes,
- provider_id,
- provider_name);
- g_object_unref (self);
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ if (!mbim_message_ms_basic_connect_v2_register_state_response_parse (
+ response,
+ NULL, /* nw_error */
+ &register_state,
+ NULL, /* register_mode */
+ &available_data_classes,
+ NULL, /* current_cellular_class */
+ &provider_id,
+ &provider_name,
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ NULL, /* preferred_data_classes */
+ &error))
+ g_prefix_error (&error, "Failed processing MBIMEx v2.0 register state response: ");
+ else
+ mm_obj_dbg (self, "proccessed MBIMEx v2.0 register state response");
+ } else {
+ if (!mbim_message_register_state_response_parse (
+ response,
+ NULL, /* nw_error */
+ &register_state,
+ NULL, /* register_mode */
+ &available_data_classes,
+ NULL, /* current_cellular_class */
+ &provider_id,
+ &provider_name,
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ &error))
+ g_prefix_error (&error, "Failed processing register state response: ");
+ else
+ mm_obj_dbg (self, "proccessed register state response");
+ }
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- } else
- g_simple_async_result_take_error (simple, error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- if (response)
- mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ update_registration_info (self,
+ register_state,
+ available_data_classes,
+ provider_id,
+ provider_name);
+
+ if (self->priv->is_atds_location_supported) {
+ g_autoptr(MbimMessage) message = NULL;
+
+ message = mbim_message_atds_location_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)atds_location_query_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
+ gboolean is_cs_supported,
+ gboolean is_ps_supported,
+ gboolean is_eps_supported,
+ gboolean is_5gs_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
- MbimDevice *device;
- MbimMessage *message;
+ g_autoptr(MbimMessage) message = NULL;
+ MbimDevice *device;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_run_registration_checks);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_register_state_query_new (NULL);
mbim_device_command (device,
@@ -2393,8 +5080,7 @@ modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
10,
NULL,
(GAsyncReadyCallback)register_state_query_ready,
- result);
- mbim_message_unref (message);
+ task);
}
/*****************************************************************************/
@@ -2404,86 +5090,116 @@ modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_3gpp_register_in_network_finish (self, res, error);
+#endif
+
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
register_state_set_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- MbimMessage *response;
- GError *error = NULL;
- MbimNwError nw_error;
+ MMBroadbandModemMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
+ /* According to Mobile Broadband Interface Model specification 1.0,
+ * Errata 1, table 10.5.9.8: Status codes for MBIM_CID_REGISTER_STATE,
+ * NwError field of MBIM_REGISTRATION_STATE_INFO structure is valid
+ * if and only if MBIM_SET_REGISTRATION_STATE response status code equals
+ * MBIM_STATUS_FAILURE.
+ * Therefore it only makes sense to parse this value if MBIM_STATUS_FAILURE
+ * result is returned in response, contrary to usual "success" code.
+ * However, some modems do not set this value to 0 when registered,
+ * causing ModemManager to drop to idle state, while modem itself is
+ * registered.
+ * Also NwError "0" is defined in 3GPP TS 24.008 as "Unknown error",
+ * not "No error", making it unsuitable as condition for registration check.
+ */
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
- mbim_message_register_state_response_parse (
- response,
- &nw_error,
- NULL, /* &register_state */
- NULL, /* register_mode */
- NULL, /* available_data_classes */
- NULL, /* current_cellular_class */
- NULL, /* provider_id */
- NULL, /* provider_name */
- NULL, /* roaming_text */
- NULL, /* registration_flag */
- NULL)) {
- if (nw_error)
- error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error);
+ !mbim_message_response_get_result (response,
+ MBIM_MESSAGE_TYPE_COMMAND_DONE,
+ &error) &&
+ g_error_matches (error, MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE)) {
+ MbimNwError nw_error;
+
+ g_clear_error (&error);
+ if (mbim_message_register_state_response_parse (
+ response,
+ &nw_error,
+ NULL, /* &register_state */
+ NULL, /* register_mode */
+ NULL, /* available_data_classes */
+ NULL, /* current_cellular_class */
+ NULL, /* provider_id */
+ NULL, /* provider_name */
+ NULL, /* roaming_text */
+ NULL, /* registration_flag */
+ &error))
+ error = mm_mobile_equipment_error_from_mbim_nw_error (nw_error, self);
}
- if (error)
- g_simple_async_result_take_error (simple, error);
- else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
-
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
+modem_3gpp_register_in_network (MMIfaceModem3gpp *_self,
const gchar *operator_id,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- MbimDevice *device;
- MbimMessage *message;
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* data_class set to 0 in the MBIM register state set message ends up
+ * selecting some "auto" mode that would overwrite whatever capabilities
+ * and modes we had set. So, if we're using QMI-based capability and
+ * mode switching, also use QMI-based network registration. */
+ if (self->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_3gpp_register_in_network (_self, operator_id, cancellable, callback, user_data);
+ return;
+ }
+#endif
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_register_in_network);
+ task = g_task_new (self, NULL, callback, user_data);
+ /* keep track of which operator id is selected */
+ g_clear_pointer (&self->priv->requested_operator_id, g_free);
if (operator_id && operator_id[0])
- message = (mbim_message_register_state_set_new (
- operator_id,
- MBIM_REGISTER_ACTION_MANUAL,
- 0, /* data_class, none preferred */
- NULL));
- else
- message = (mbim_message_register_state_set_new (
- "",
- MBIM_REGISTER_ACTION_AUTOMATIC,
- 0, /* data_class, none preferred */
- NULL));
+ self->priv->requested_operator_id = g_strdup (operator_id);
+
+ message = (mbim_message_register_state_set_new (
+ self->priv->requested_operator_id ? self->priv->requested_operator_id : "",
+ self->priv->requested_operator_id ? MBIM_REGISTER_ACTION_MANUAL : MBIM_REGISTER_ACTION_AUTOMATIC,
+ self->priv->requested_data_class,
+ NULL));
mbim_device_command (device,
message,
60,
NULL,
(GAsyncReadyCallback)register_state_set_ready,
- result);
- mbim_message_unref (message);
+ task);
}
/*****************************************************************************/
@@ -2494,70 +5210,1373 @@ modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+visible_providers_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
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;
+ GError *error = 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) &&
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_visible_providers_response_parse (response,
&n_providers,
&providers,
- error)) {
+ &error)) {
+ GList *info_list;
+
info_list = mm_3gpp_network_info_list_from_mbim_providers ((const MbimProvider *const *)providers,
n_providers);
mbim_provider_array_free (providers);
+
+ g_task_return_pointer (task, info_list, (GDestroyNotify)mm_3gpp_network_info_list_free);
+ } else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+
+ if (response)
+ mbim_message_unref (response);
+}
+
+static void
+modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ MbimMessage *message;
+ GTask *task;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "scanning networks...");
+ message = mbim_message_visible_providers_query_new (MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN, NULL);
+ mbim_device_command (device,
+ message,
+ 300,
+ NULL,
+ (GAsyncReadyCallback)visible_providers_query_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
+
+static gboolean
+modem_signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_signal_check_support_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ gboolean parent_supported;
+
+ parent_supported = iface_modem_signal_parent->check_support_finish (self, res, NULL);
+ g_task_return_boolean (task, parent_supported);
+ g_object_unref (task);
+}
+
+static void
+modem_signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If ATDS signal is supported, we support the Signal interface */
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->is_atds_signal_supported) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
}
- return info_list;
+
+ /* Otherwise, check if the parent CESQ-based implementation works */
+ g_assert (iface_modem_signal_parent->check_support && iface_modem_signal_parent->check_support_finish);
+ iface_modem_signal_parent->check_support (self,
+ (GAsyncReadyCallback)parent_signal_check_support_ready,
+ task);
}
+/*****************************************************************************/
+/* Load extended signal information (Signal interface) */
+
+typedef struct {
+ MMSignal *gsm;
+ MMSignal *umts;
+ MMSignal *lte;
+ MMSignal *nr5g;
+} SignalLoadValuesResult;
+
static void
-visible_providers_query_ready (MbimDevice *device,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+signal_load_values_result_free (SignalLoadValuesResult *result)
+{
+ g_clear_object (&result->gsm);
+ g_clear_object (&result->umts);
+ g_clear_object (&result->lte);
+ g_clear_object (&result->nr5g);
+ g_slice_free (SignalLoadValuesResult, result);
+}
+
+static gboolean
+modem_signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
+{
+ SignalLoadValuesResult *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ if (gsm)
+ *gsm = g_steal_pointer (&result->gsm);
+ if (umts)
+ *umts = g_steal_pointer (&result->umts);
+ if (lte)
+ *lte = g_steal_pointer (&result->lte);
+ if (nr5g)
+ *nr5g = g_steal_pointer (&result->nr5g);
+
+ signal_load_values_result_free (result);
+
+ /* No 3GPP2 support */
+ if (cdma)
+ *cdma = NULL;
+ if (evdo)
+ *evdo = NULL;
+ return TRUE;
+}
+
+static void
+atds_signal_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MbimMessage *response;
+ SignalLoadValuesResult *result;
+ GError *error = NULL;
+ guint32 rssi;
+ guint32 error_rate;
+ guint32 rscp;
+ guint32 ecno;
+ guint32 rsrq;
+ guint32 rsrp;
+ guint32 snr;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_atds_signal_response_parse (response, &rssi, &error_rate, &rscp, &ecno, &rsrq, &rsrp, &snr, &error)) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ result = g_slice_new0 (SignalLoadValuesResult);
+
+ if (rscp <= 96) {
+ result->umts = mm_signal_new ();
+ mm_signal_set_rscp (result->umts, -120.0 + rscp);
+ }
+
+ if (ecno <= 49) {
+ if (!result->umts)
+ result->umts = mm_signal_new ();
+ mm_signal_set_ecio (result->umts, -24.0 + ((gdouble) ecno / 2));
+ }
+
+ if (rsrq <= 34) {
+ result->lte = mm_signal_new ();
+ mm_signal_set_rsrq (result->lte, -19.5 + ((gdouble) rsrq / 2));
+ }
+
+ if (rsrp <= 97) {
+ if (!result->lte)
+ result->lte = mm_signal_new ();
+ mm_signal_set_rsrp (result->lte, -140.0 + rsrp);
+ }
+
+ if (snr <= 35) {
+ if (!result->lte)
+ result->lte = mm_signal_new ();
+ mm_signal_set_snr (result->lte, -5.0 + snr);
+ }
+
+ /* RSSI may be given for all 2G, 3G or 4G so we detect to which one applies */
+ if (rssi <= 31) {
+ gdouble value;
+
+ value = -113.0 + (2 * rssi);
+ if (result->lte)
+ mm_signal_set_rssi (result->lte, value);
+ else if (result->umts)
+ mm_signal_set_rssi (result->umts, value);
+ else {
+ result->gsm = mm_signal_new ();
+ mm_signal_set_rssi (result->gsm, value);
+ }
+ }
+
+ if (!result->gsm && !result->umts && !result->lte) {
+ signal_load_values_result_free (result);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No signal details given");
+ goto out;
+ }
+
+ g_task_return_pointer (task, result, (GDestroyNotify) signal_load_values_result_free);
+
+out:
+ if (response)
+ mbim_message_unref (response);
+ g_object_unref (task);
+}
+
+static void
+parent_signal_load_values_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SignalLoadValuesResult *result;
+ GError *error = NULL;
+
+ result = g_slice_new0 (SignalLoadValuesResult);
+ if (!iface_modem_signal_parent->load_values_finish (self, res,
+ NULL, NULL,
+ &result->gsm, &result->umts, &result->lte, &result->nr5g,
+ &error)) {
+ signal_load_values_result_free (result);
+ g_task_return_error (task, error);
+ } else if (!result->gsm && !result->umts && !result->lte) {
+ signal_load_values_result_free (result);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No signal details given");
+ } else
+ g_task_return_pointer (task, result, (GDestroyNotify) signal_load_values_result_free);
+ g_object_unref (task);
+}
+
+static void
+modem_signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ MbimMessage *message;
+ GTask *task;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->is_atds_signal_supported) {
+ message = mbim_message_atds_signal_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)atds_signal_query_ready,
+ task);
+ mbim_message_unref (message);
+ return;
+ }
+
+ /* Fallback to parent CESQ based implementation */
+ g_assert (iface_modem_signal_parent->load_values && iface_modem_signal_parent->load_values_finish);
+ iface_modem_signal_parent->load_values (self,
+ NULL,
+ (GAsyncReadyCallback)parent_signal_load_values_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup threshold values (Signal interface) */
+
+static gboolean
+modem_signal_setup_thresholds_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+signal_state_set_thresholds_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_signal_state_response_parse (
+ response,
+ NULL, /* rssi */
+ NULL, /* error_rate */
+ NULL, /* signal_strength_interval */
+ NULL, /* rssi_threshold */
+ NULL, /* error_rate_threshold */
+ &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static void
+modem_signal_setup_thresholds (MMIfaceModemSignal *self,
+ guint rssi_threshold,
+ gboolean error_rate_threshold,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(MbimMessage) message = NULL;
+ MbimDevice *device;
+ GTask *task;
+ guint coded_rssi_threshold = 0;
+ guint coded_error_rate_threshold = 0;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* the input RSSI threshold difference is given in dBm, and in the MBIM
+ * protocol we use a linear scale of coded values that correspond to 2dBm
+ * per code point. */
+ if (rssi_threshold) {
+ coded_rssi_threshold = rssi_threshold / 2;
+ if (!coded_rssi_threshold)
+ coded_rssi_threshold = 1; /* minimum value when enabled */
+ }
+
+ /* the input error rate threshold is given as a boolean to enable or
+ * disable, and in the MBIM protocol we have a non-linear scale of
+ * coded values. We just select the minimum coded value, so that we
+ * get all reports, i.e. every time it changes the coded value */
+ if (error_rate_threshold)
+ coded_error_rate_threshold = 1; /* minimum value when enabled */
+
+ message = (mbim_message_signal_state_set_new (
+ 0, /* signal strength interval set to default always */
+ coded_rssi_threshold,
+ coded_error_rate_threshold,
+ NULL));
+
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)signal_state_set_thresholds_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Check support (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_check_support_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_3gpp_profile_manager_check_support (MMIfaceModem3gppProfileManager *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)) && self->priv->is_profile_management_supported)
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_boolean (task, FALSE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Setup/Cleanup unsolicited event handlers (3gppProfileManager interface) */
+
+static gboolean
+common_setup_cleanup_unsolicited_events_3gpp_profile_manager_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_setup_cleanup_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error);
+}
+
+static void
+cleanup_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
+ common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data);
+}
+
+static void
+setup_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
+ common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Enable/Disable unsolicited event handlers (3gppProfileManager interface) */
+
+static gboolean
+common_enable_disable_unsolicited_events_3gpp_profile_manager_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error);
+}
+
+static void
+disable_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
+}
+
+static void
+enable_unsolicited_events_3gpp_profile_manager (MMIfaceModem3gppProfileManager *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_PROVISIONED_CONTEXTS;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Check format (3gppProfileManager interface) */
+
+static gboolean
+modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *new_id,
+ gint *min_profile_id,
+ gint *max_profile_id,
+ GEqualFunc *apn_cmp,
+ MM3gppProfileCmpFlags *profile_cmp_flags,
+ GError **error)
+{
+ if (!g_task_propagate_boolean (G_TASK (res), error)) {
+ g_assert_not_reached ();
+ return FALSE;
+ }
+
+ if (new_id)
+ *new_id = TRUE;
+ if (min_profile_id)
+ *min_profile_id = 1;
+ if (max_profile_id)
+ *max_profile_id = G_MAXINT - 1;
+ /* use default string comparison method */
+ if (apn_cmp)
+ *apn_cmp = NULL;
+ /* we don't support IP type in the profiles */
+ if (profile_cmp_flags)
+ *profile_cmp_flags = MM_3GPP_PROFILE_CMP_FLAGS_NO_IP_TYPE;
+ return TRUE;
+}
+
+static void
+modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self,
+ MMBearerIpFamily ip_type,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* List profiles (3GPP profile management interface) */
+
+typedef struct {
+ GList *profiles;
+} ListProfilesContext;
+
+static void
+list_profiles_context_free (ListProfilesContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->profiles);
+ g_slice_free (ListProfilesContext, ctx);
+}
+
+static gboolean
+modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **out_profiles,
+ GError **error)
+{
+ ListProfilesContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (out_profiles)
+ *out_profiles = g_steal_pointer (&ctx->profiles);
+ return TRUE;
+}
+
+static MM3gppProfile *
+provisioned_context_element_to_3gpp_profile (MbimProvisionedContextElement *element)
+{
+ MM3gppProfile *profile;
+ MMBearerApnType apn_type;
+
+ apn_type = mm_bearer_apn_type_from_mbim_context_type (mbim_uuid_to_context_type (&element->context_type));
+ if (apn_type == MM_BEARER_APN_TYPE_NONE)
+ return NULL;
+
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, element->context_id);
+ mm_3gpp_profile_set_apn (profile, element->access_string);
+ mm_3gpp_profile_set_apn_type (profile, apn_type);
+ mm_3gpp_profile_set_user (profile, element->user_name);
+ mm_3gpp_profile_set_password (profile, element->password);
+ mm_3gpp_profile_set_allowed_auth (profile, (mm_bearer_allowed_auth_from_mbim_auth_protocol (element->auth_protocol)));
+ /* compression unused, and ip-type not provided */
+ return profile;
+}
+
+static void
+profile_manager_provisioned_contexts_query_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ GError *error = NULL;
+ guint32 provisioned_contexts_count = 0;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(MbimProvisionedContextElementArray) provisioned_contexts = NULL;
+
+ ctx = g_slice_new0 (ListProfilesContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response,
+ MBIM_MESSAGE_TYPE_COMMAND_DONE,
+ &error) &&
+ mbim_message_provisioned_contexts_response_parse (response,
+ &provisioned_contexts_count,
+ &provisioned_contexts,
+ &error)) {
+ guint i;
+
+ for (i = 0; i < provisioned_contexts_count; i++) {
+ MM3gppProfile *profile;
+
+ profile = provisioned_context_element_to_3gpp_profile (provisioned_contexts[i]);
+ if (profile)
+ ctx->profiles = g_list_append (ctx->profiles, profile);
+ }
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "querying provisioned contexts...");
+ message = mbim_message_provisioned_contexts_query_new (NULL);
+
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)profile_manager_provisioned_contexts_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Store profile (3GPP profile management interface) */
+
+static gint
+modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return MM_3GPP_PROFILE_ID_UNKNOWN;
+
+ return GPOINTER_TO_INT (g_task_get_task_data (G_TASK (res)));
+}
+
+static void
+profile_manager_provisioned_contexts_set_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ GError *error = NULL;
+ gint profile_id;
+ MMBearerApnType apn_type;
+ MMBearerAllowedAuth allowed_auth;
+ MbimAuthProtocol auth_protocol = MBIM_AUTH_PROTOCOL_NONE;
+ MbimContextType context_type;
+ const MbimUuid *context_type_uuid;
+ const gchar *apn;
+ const gchar *user;
+ const gchar *password;
+ g_autofree gchar *apn_type_str = NULL;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL);
+
+ apn = mm_3gpp_profile_get_apn (profile);
+
+ apn_type = mm_3gpp_profile_get_apn_type (profile);
+ context_type = mm_bearer_apn_type_to_mbim_context_type (apn_type, self, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ context_type_uuid = mbim_uuid_from_context_type (context_type);
+ apn_type_str = mm_bearer_apn_type_build_string_from_mask (apn_type);
+
+ user = mm_3gpp_profile_get_user (profile);
+ password = mm_3gpp_profile_get_password (profile);
+
+ allowed_auth = mm_3gpp_profile_get_allowed_auth (profile);
+ if ((allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) || user || password) {
+ auth_protocol = mm_bearer_allowed_auth_to_mbim_auth_protocol (allowed_auth, self, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ mm_obj_dbg (self, "storing profile '%d': apn '%s', apn type '%s'",
+ profile_id, apn, apn_type_str);
+
+ message = mbim_message_provisioned_contexts_set_new (profile_id,
+ context_type_uuid,
+ apn ? apn : "",
+ user ? user : "",
+ password ? password : "",
+ MBIM_COMPRESSION_NONE,
+ auth_protocol,
+ "", /* provider id */
+ &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)profile_manager_provisioned_contexts_set_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Delete profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+profile_manager_provisioned_contexts_reset_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response && mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ GError *error = NULL;
+ gint profile_id;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ mm_obj_dbg (self, "deleting profile '%d'", profile_id);
+
+ message = mbim_message_provisioned_contexts_set_new (profile_id,
+ mbim_uuid_from_context_type (MBIM_CONTEXT_TYPE_NONE),
+ "", /* access string */
+ "", /* user */
+ "", /* pass */
+ MBIM_COMPRESSION_NONE,
+ MBIM_AUTH_PROTOCOL_NONE,
+ "", /* provider id */
+ &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)profile_manager_provisioned_contexts_reset_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Check if USSD supported (3GPP/USSD interface) */
+
+static gboolean
+modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, MM_BROADBAND_MODEM_MBIM (self)->priv->is_ussd_supported);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* USSD encoding/deconding helpers
+ *
+ * Note: we don't care about subclassing the ussd_encode/decode methods in the
+ * interface, as we're going to use this methods just here.
+ */
+
+static GByteArray *
+ussd_encode (const gchar *command,
+ guint32 *scheme,
+ GError **error)
+{
+ g_autoptr(GByteArray) array = NULL;
+
+ if (mm_charset_can_convert_to (command, MM_MODEM_CHARSET_GSM)) {
+ g_autoptr(GByteArray) gsm = NULL;
+ guint8 *packed;
+ guint32 packed_len = 0;
+
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
+ gsm = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (!gsm) {
+ g_prefix_error (error, "Failed to encode USSD command in GSM7 charset: ");
+ return NULL;
+ }
+ packed = mm_charset_gsm_pack (gsm->data, gsm->len, 0, &packed_len);
+ array = g_byte_array_new_take (packed, packed_len);
+ } else {
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_UCS2;
+ array = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_UCS2, FALSE, error);
+ if (!array) {
+ g_prefix_error (error, "Failed to encode USSD command in UCS2 charset: ");
+ return NULL;
+ }
+ }
+
+ if (array->len > 160) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Failed to encode USSD command: encoded data too long (%u > 160)", array->len);
+ return NULL;
+ }
+
+ return g_steal_pointer (&array);
+}
+
+static gchar *
+ussd_decode (guint32 scheme,
+ GByteArray *data,
+ GError **error)
+{
+ gchar *decoded = NULL;
+
+ if (scheme == MM_MODEM_GSM_USSD_SCHEME_7BIT) {
+ g_autoptr(GByteArray) unpacked_array = NULL;
+ guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+
+ unpacked = mm_charset_gsm_unpack ((const guint8 *)data->data, (data->len * 8) / 7, 0, &unpacked_len);
+ unpacked_array = g_byte_array_new_take (unpacked, unpacked_len);
+
+ decoded = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (!decoded)
+ g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (GSM7 charset): ", scheme);
+ } else if (scheme == MM_MODEM_GSM_USSD_SCHEME_UCS2) {
+ decoded = mm_modem_charset_bytearray_to_utf8 (data, MM_MODEM_CHARSET_UCS2, FALSE, error);
+ if (!decoded)
+ g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (UCS2 charset): ", scheme);
+ } else
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Failed to decode USSD command in unsupported 0x%04x scheme", scheme);
+
+ return decoded;
+}
+
+/*****************************************************************************/
+/* USSD notifications */
+
+static void
+process_ussd_message (MMBroadbandModemMbim *self,
+ MbimUssdResponse ussd_response,
+ MbimUssdSessionState ussd_session_state,
+ guint32 scheme,
+ guint32 data_size,
+ const guint8 *data)
+{
+ GTask *task = NULL;
+ MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE;
+ GByteArray *bytearray;
+ gchar *converted = NULL;
+ GError *error = NULL;
+
+ /* Steal task and balance out received reference */
+ if (self->priv->pending_ussd_action) {
+ task = self->priv->pending_ussd_action;
+ self->priv->pending_ussd_action = NULL;
+ }
+
+ bytearray = g_byte_array_new ();
+ if (data && data_size)
+ bytearray = g_byte_array_append (bytearray, data, data_size);
+
+ switch (ussd_response) {
+ case MBIM_USSD_RESPONSE_NO_ACTION_REQUIRED:
+ /* no further action required */
+ converted = ussd_decode (scheme, bytearray, &error);
+ if (!converted)
+ break;
+
+ /* Response to the user's request? */
+ if (task)
+ break;
+
+ /* Network-initiated USSD-Notify */
+ mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), converted);
+ g_clear_pointer (&converted, g_free);
+ break;
+
+ case MBIM_USSD_RESPONSE_ACTION_REQUIRED:
+ /* further action required */
+ ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE;
+
+ converted = ussd_decode (scheme, bytearray, &error);
+ if (!converted)
+ break;
+ /* Response to the user's request? */
+ if (task)
+ break;
+
+ /* Network-initiated USSD-Request */
+ mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), converted);
+ g_clear_pointer (&converted, g_free);
+ break;
+
+ case MBIM_USSD_RESPONSE_TERMINATED_BY_NETWORK:
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network");
+ break;
+
+ case MBIM_USSD_RESPONSE_OTHER_LOCAL_CLIENT:
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Another ongoing USSD operation is in progress");
+ break;
+
+ case MBIM_USSD_RESPONSE_OPERATION_NOT_SUPPORTED:
+ error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Operation not supported");
+ break;
+
+ case MBIM_USSD_RESPONSE_NETWORK_TIMEOUT:
+ error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "Network timeout");
+ break;
+
+ default:
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown USSD response (%u)", ussd_response);
+ break;
+ }
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state);
+
+ g_byte_array_unref (bytearray);
+
+ /* Complete the pending action */
+ if (task) {
+ if (error)
+ g_task_return_error (task, error);
+ else if (converted)
+ g_task_return_pointer (task, converted, g_free);
+ else
+ g_assert_not_reached ();
+ g_object_unref (task);
+ return;
+ }
+
+ /* If no pending task, just report the error */
+ if (error) {
+ mm_obj_dbg (self, "network reported USSD message: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_assert (!converted);
+}
+
+static void
+process_ussd_notification (MMBroadbandModemMbim *self,
+ MbimMessage *notification)
+{
+ MbimUssdResponse ussd_response;
+ MbimUssdSessionState ussd_session_state;
+ guint32 scheme;
+ guint32 data_size;
+ const guint8 *data;
+
+ if (mbim_message_ussd_notification_parse (notification,
+ &ussd_response,
+ &ussd_session_state,
+ &scheme,
+ &data_size,
+ &data,
+ NULL)) {
+ mm_obj_dbg (self, "received USSD indication: %s, session state: %s, scheme: 0x%x, data size: %u bytes",
+ mbim_ussd_response_get_string (ussd_response),
+ mbim_ussd_session_state_get_string (ussd_session_state),
+ scheme,
+ data_size);
+ process_ussd_message (self, ussd_response, ussd_session_state, scheme, data_size, data);
+ }
+}
+
+/*****************************************************************************/
+/* Setup/Cleanup unsolicited result codes (3GPP/USSD interface) */
+
+static gboolean
+modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+common_setup_flag_ussd_ready (MMBroadbandModemMbim *self,
+ GAsyncResult *res,
+ GTask *task)
{
- MbimMessage *response;
GError *error = NULL;
+ if (!common_setup_cleanup_unsolicited_events_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_setup_cleanup_unsolicited_ussd_events (MMBroadbandModemMbim *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GINT_TO_POINTER (setup), NULL);
+
+ if (setup)
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_USSD;
+ else
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_USSD;
+ common_setup_cleanup_unsolicited_events (self, setup, (GAsyncReadyCallback)common_setup_flag_ussd_ready, task);
+}
+
+static void
+modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_setup_cleanup_unsolicited_ussd_events (MM_BROADBAND_MODEM_MBIM (self), FALSE, callback, user_data);
+}
+
+static void
+modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_setup_cleanup_unsolicited_ussd_events (MM_BROADBAND_MODEM_MBIM (self), TRUE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Enable/Disable URCs (3GPP/USSD interface) */
+
+static gboolean
+modem_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_enable_disable_unsolicited_events_finish (MM_BROADBAND_MODEM_MBIM (self), res, error);
+}
+
+static void
+modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_USSD;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
+}
+
+static void
+modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_USSD;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Send command (3GPP/USSD interface) */
+
+static gchar *
+modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+ussd_send_ready (MbimDevice *device,
+ GAsyncResult *res,
+ MMBroadbandModemMbim *self)
+{
+ MbimMessage *response;
+ GError *error = NULL;
+ MbimUssdResponse ussd_response;
+ MbimUssdSessionState ussd_session_state;
+ guint32 scheme;
+ guint32 data_size;
+ const guint8 *data;
+
+ /* Note: if there is a cached task, it is ALWAYS completed here */
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_ussd_response_parse (response,
+ &ussd_response,
+ &ussd_session_state,
+ &scheme,
+ &data_size,
+ &data,
+ &error)) {
+ mm_obj_dbg (self, "received USSD response: %s, session state: %s, scheme: 0x%x, data size: %u bytes",
+ mbim_ussd_response_get_string (ussd_response),
+ mbim_ussd_session_state_get_string (ussd_session_state),
+ scheme,
+ data_size);
+ process_ussd_message (self, ussd_response, ussd_session_state, scheme, data_size, data);
+ } else {
+ /* Report error in the cached task, if any */
+ if (self->priv->pending_ussd_action) {
+ GTask *task;
+
+ task = self->priv->pending_ussd_action;
+ self->priv->pending_ussd_action = NULL;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ } else {
+ mm_obj_dbg (self, "failed to parse USSD response: %s", error->message);
+ g_clear_error (&error);
+ }
+ }
+
+ if (response)
+ mbim_message_unref (response);
+
+ /* Balance out received reference */
+ g_object_unref (self);
+}
+
+static void
+modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self;
+ MbimDevice *device;
+ GTask *task;
+ MbimUssdAction action;
+ MbimMessage *message;
+ GByteArray *encoded;
+ guint32 scheme = 0;
+ GError *error = NULL;
+
+ self = MM_BROADBAND_MODEM_MBIM (_self);
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Fail if there is an ongoing operation already */
+ if (self->priv->pending_ussd_action) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "there is already an ongoing USSD operation");
+ g_object_unref (task);
+ return;
+ }
+
+ switch (mm_iface_modem_3gpp_ussd_get_state (MM_IFACE_MODEM_3GPP_USSD (self))) {
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE:
+ action = MBIM_USSD_ACTION_INITIATE;
+ break;
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE:
+ action = MBIM_USSD_ACTION_CONTINUE;
+ break;
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN:
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE:
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+
+ encoded = ussd_encode (command, &scheme, &error);
+ if (!encoded) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ message = mbim_message_ussd_set_new (action, scheme, encoded->len, encoded->data, &error);
+ if (!message) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Cache the action, as it may be completed via URCs */
+ self->priv->pending_ussd_action = task;
+ mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE);
+
+ mbim_device_command (device,
+ message,
+ 100,
+ NULL,
+ (GAsyncReadyCallback)ussd_send_ready,
+ g_object_ref (self)); /* Full reference! */
+ mbim_message_unref (message);
+}
+
+/*****************************************************************************/
+/* Cancel USSD (3GPP/USSD interface) */
+
+static gboolean
+modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ussd_cancel_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
response = mbim_device_command_finish (device, res, &error);
if (response)
- g_simple_async_result_set_op_res_gpointer (simple, response, (GDestroyNotify)mbim_message_unref);
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
+
+ /* Complete the pending action, regardless of the operation result */
+ if (self->priv->pending_ussd_action) {
+ GTask *pending_task;
+
+ pending_task = self->priv->pending_ussd_action;
+ self->priv->pending_ussd_action = NULL;
+
+ g_task_return_new_error (pending_task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "USSD session was cancelled");
+ g_object_unref (pending_task);
+ }
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
+ MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+
+ if (error)
+ g_task_return_error (task, error);
else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ if (response)
+ mbim_message_unref (response);
}
static void
-modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
- MbimDevice *device;
- MbimMessage *message;
+ MMBroadbandModemMbim *self;
+ MbimDevice *device;
+ GTask *task;
+ MbimMessage *message;
+ GError *error = NULL;
+ self = MM_BROADBAND_MODEM_MBIM (_self);
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);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("scanning networks...");
- message = mbim_message_visible_providers_query_new (MBIM_VISIBLE_PROVIDERS_ACTION_FULL_SCAN, NULL);
+ message = mbim_message_ussd_set_new (MBIM_USSD_ACTION_CANCEL, 0, 0, NULL, &error);
+ if (!message) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
mbim_device_command (device,
message,
- 120,
+ 10,
NULL,
- (GAsyncReadyCallback)visible_providers_query_ready,
- result);
+ (GAsyncReadyCallback)ussd_cancel_ready,
+ task);
mbim_message_unref (message);
}
@@ -2569,8 +6588,7 @@ messaging_check_support_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- /* no error expected here */
- return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -2579,25 +6597,20 @@ messaging_check_support (MMIfaceModemMessaging *_self,
gpointer user_data)
{
MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
/* We only handle 3GPP messaging (PDU based) currently */
if (self->priv->caps_sms & MBIM_SMS_CAPS_PDU_RECEIVE &&
self->priv->caps_sms & MBIM_SMS_CAPS_PDU_SEND) {
- mm_dbg ("Messaging capabilities supported");
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ mm_obj_dbg (self, "messaging capabilities supported");
+ g_task_return_boolean (task, TRUE);
} else {
- mm_dbg ("Messaging capabilities not supported by this modem");
- g_simple_async_result_set_op_res_gboolean (result, FALSE);
+ mm_obj_dbg (self, "messaging capabilities not supported by this modem");
+ g_task_return_boolean (task, FALSE);
}
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -2626,40 +6639,22 @@ messaging_load_supported_storages (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_load_supported_storages);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Load initial SMS parts */
-typedef struct {
- MMBroadbandModemMbim *self;
- GSimpleAsyncResult *result;
-} LoadInitialSmsPartsContext;
-
-static void
-load_initial_sms_parts_context_complete_and_free (LoadInitialSmsPartsContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LoadInitialSmsPartsContext, ctx);
-}
-
static gboolean
load_initial_sms_parts_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -2672,18 +6667,20 @@ add_sms_part (MMBroadbandModemMbim *self,
part = mm_sms_part_3gpp_new_from_binary_pdu (pdu->message_index,
pdu->pdu_data,
pdu->pdu_data_size,
+ self,
+ FALSE,
&error);
if (part) {
- mm_dbg ("Correctly parsed PDU (%d)", pdu->message_index);
+ mm_obj_dbg (self, "correctly parsed PDU (%d)", pdu->message_index);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
part,
mm_sms_state_from_mbim_message_status (pdu->message_status),
MM_SMS_STORAGE_MT);
} else {
/* Don't treat the error as critical */
- mm_dbg ("Error parsing PDU (%d): %s",
- pdu->message_index,
- error->message);
+ mm_obj_dbg (self, "error parsing PDU (%d): %s",
+ pdu->message_index,
+ error->message);
g_error_free (error);
}
}
@@ -2691,16 +6688,19 @@ add_sms_part (MMBroadbandModemMbim *self,
static void
sms_read_query_ready (MbimDevice *device,
GAsyncResult *res,
- LoadInitialSmsPartsContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemMbim *self;
MbimMessage *response;
GError *error = NULL;
guint32 messages_count;
MbimSmsPduReadRecord **pdu_messages;
+ self = g_task_get_source_object (task);
+
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_sms_read_response_parse (
response,
NULL,
@@ -2711,15 +6711,16 @@ sms_read_query_ready (MbimDevice *device,
guint i;
for (i = 0; i < messages_count; i++)
- add_sms_part (ctx->self, pdu_messages[i]);
+ add_sms_part (self, pdu_messages[i]);
mbim_sms_pdu_read_record_array_free (pdu_messages);
+ g_task_return_boolean (task, TRUE);
} else
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
-
- load_initial_sms_parts_context_complete_and_free (ctx);
}
static void
@@ -2728,23 +6729,18 @@ load_initial_sms_parts (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- LoadInitialSmsPartsContext *ctx;
MbimDevice *device;
MbimMessage *message;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
g_assert (storage == MM_SMS_STORAGE_MT);
- ctx = g_slice_new0 (LoadInitialSmsPartsContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_initial_sms_parts);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("loading SMS parts...");
+ mm_obj_dbg (self, "loading SMS parts...");
message = mbim_message_sms_read_query_new (MBIM_SMS_FORMAT_PDU,
MBIM_SMS_FLAG_ALL,
0, /* message index, unused */
@@ -2754,7 +6750,7 @@ load_initial_sms_parts (MMIfaceModemMessaging *self,
10,
NULL,
(GAsyncReadyCallback)sms_read_query_ready,
- ctx);
+ task);
mbim_message_unref (message);
}
@@ -2770,21 +6766,25 @@ common_setup_cleanup_unsolicited_events_messaging_finish (MMIfaceModemMessaging
}
static void
-cleanup_unsolicited_events_messaging (MMIfaceModemMessaging *self,
+cleanup_unsolicited_events_messaging (MMIfaceModemMessaging *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SMS_READ;
- common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), FALSE, callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags &= ~PROCESS_NOTIFICATION_FLAG_SMS_READ;
+ common_setup_cleanup_unsolicited_events (self, FALSE, callback, user_data);
}
static void
-setup_unsolicited_events_messaging (MMIfaceModemMessaging *self,
+setup_unsolicited_events_messaging (MMIfaceModemMessaging *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SMS_READ;
- common_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), TRUE, callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->setup_flags |= PROCESS_NOTIFICATION_FLAG_SMS_READ;
+ common_setup_cleanup_unsolicited_events (self, TRUE, callback, user_data);
}
/*****************************************************************************/
@@ -2799,21 +6799,25 @@ common_enable_disable_unsolicited_events_messaging_finish (MMIfaceModemMessaging
}
static void
-disable_unsolicited_events_messaging (MMIfaceModemMessaging *self,
+disable_unsolicited_events_messaging (MMIfaceModemMessaging *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SMS_READ;
- common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags &= ~PROCESS_NOTIFICATION_FLAG_SMS_READ;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
}
static void
-enable_unsolicited_events_messaging (MMIfaceModemMessaging *self,
+enable_unsolicited_events_messaging (MMIfaceModemMessaging *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MM_BROADBAND_MODEM_MBIM (self)->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SMS_READ;
- common_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_MBIM (self), callback, user_data);
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+
+ self->priv->enable_flags |= PROCESS_NOTIFICATION_FLAG_SMS_READ;
+ common_enable_disable_unsolicited_events (self, callback, user_data);
}
/*****************************************************************************/
@@ -2826,6 +6830,1021 @@ messaging_create_sms (MMIfaceModemMessaging *self)
}
/*****************************************************************************/
+/* Check support (SAR interface) */
+
+static gboolean
+sar_check_support_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sar_check_support (MMIfaceModemSar *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "SAR capabilities %s", self->priv->is_ms_sar_supported ? "supported" : "not supported");
+ g_task_return_boolean (task, self->priv->is_ms_sar_supported);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+
+static gboolean
+sar_load_state_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ gboolean *out_state,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gboolean result;
+
+ result = g_task_propagate_boolean (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_state)
+ *out_state = result;
+ return TRUE;
+}
+
+static void
+sar_config_query_state_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ MbimSarBackoffState state;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_ms_sar_config_response_parse (
+ response,
+ NULL,
+ &state,
+ NULL,
+ NULL,
+ NULL,
+ &error))
+ g_task_return_boolean (task, state);
+ else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static void
+sar_load_state (MMIfaceModemSar *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ message = mbim_message_ms_sar_config_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)sar_config_query_state_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static gboolean
+sar_load_power_level_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ guint *out_power_level,
+ GError **error)
+{
+ gssize result;
+
+ result = g_task_propagate_int (G_TASK (res), error);
+ if (result < 0)
+ return FALSE;
+
+ *out_power_level = (guint) result;
+ return TRUE;
+}
+
+static void
+sar_config_query_power_level_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ GError *error = NULL;
+ guint32 states_count;
+ g_autoptr(MbimSarConfigStateArray) config_states = NULL;
+ g_autoptr(MbimMessage) response = NULL;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_ms_sar_config_response_parse (
+ response,
+ NULL,
+ NULL,
+ NULL,
+ &states_count,
+ &config_states,
+ &error)) {
+ if (states_count == 0) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Couldn't load config states");
+ } else {
+ if (states_count > 1)
+ mm_obj_dbg (self, "Device reports SAR config states for %u antennas separately, but only considering the first one", states_count);
+ g_task_return_int (task, config_states[0]->backoff_index);
+ }
+ } else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static void
+sar_load_power_level (MMIfaceModemSar *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ message = mbim_message_ms_sar_config_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)sar_config_query_power_level_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static gboolean
+sar_enable_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sar_config_set_enable_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static void
+sar_enable (MMIfaceModemSar *_self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+ g_autofree MbimSarConfigState *config_state = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /*
+ * the value 0xFFFFFFFF means all antennas
+ * the backoff index set to the current index of modem
+ */
+ config_state = g_new (MbimSarConfigState, 1);
+ config_state->antenna_index = 0xFFFFFFFF;
+ config_state->backoff_index = mm_iface_modem_sar_get_power_level (_self);
+
+ message = mbim_message_ms_sar_config_set_new (MBIM_SAR_CONTROL_MODE_OS,
+ enable ? MBIM_SAR_BACKOFF_STATE_ENABLED : MBIM_SAR_BACKOFF_STATE_DISABLED,
+ 1, (const MbimSarConfigState **)&config_state, NULL);
+
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)sar_config_set_enable_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static gboolean
+sar_set_power_level_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sar_config_set_power_level_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+ g_task_return_boolean (task, TRUE);
+ } else
+ g_task_return_error (task, error);
+
+ g_object_unref (task);
+}
+
+static void
+sar_set_power_level (MMIfaceModemSar *_self,
+ guint power_level,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
+ MbimDevice *device;
+ GTask *task;
+ g_autoptr(MbimMessage) message = NULL;
+ g_autofree MbimSarConfigState *config_state = NULL;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ if (!mm_iface_modem_get_sar_state (_self)) {
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ sar_set_power_level,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Couldn't set power level of SAR, because the SAR is disabled");
+ return;
+ }
+
+ /*
+ * the value 0xFFFFFFFF means all antennas
+ * the backoff index set to the input power level
+ */
+ config_state = g_new (MbimSarConfigState, 1);
+ config_state->antenna_index = 0xFFFFFFFF;
+ config_state->backoff_index = power_level;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ message = mbim_message_ms_sar_config_set_new (MBIM_SAR_CONTROL_MODE_OS,
+ MBIM_SAR_BACKOFF_STATE_ENABLED,
+ 1, (const MbimSarConfigState **)&config_state, NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)sar_config_set_power_level_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object);
+
+ switch (prop_id) {
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ case PROP_QMI_UNSUPPORTED:
+ self->priv->qmi_unsupported = g_value_get_boolean (value);
+ break;
+#endif
+ case PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED:
+ self->priv->intel_firmware_update_unsupported = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object);
+
+ switch (prop_id) {
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ case PROP_QMI_UNSUPPORTED:
+ g_value_set_boolean (value, self->priv->qmi_unsupported);
+ break;
+#endif
+ case PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED:
+ g_value_set_boolean (value, self->priv->intel_firmware_update_unsupported);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+/*****************************************************************************/
+/* Create SIMs in all SIM slots */
+
+typedef struct {
+ GPtrArray *sim_slots;
+ guint number_slots;
+ guint query_slot_index; /* range [0,number_slots-1] */
+ guint active_slot_index; /* range [1,number_slots] */
+} LoadSimSlotsContext;
+
+static void
+load_sim_slots_context_free (LoadSimSlotsContext *ctx)
+{
+ g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref);
+ g_slice_free (LoadSimSlotsContext, ctx);
+}
+
+static gboolean
+load_sim_slots_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GPtrArray **sim_slots,
+ guint *primary_sim_slot,
+ GError **error)
+{
+ LoadSimSlotsContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK(res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+
+ if (sim_slots)
+ *sim_slots = g_steal_pointer (&ctx->sim_slots);
+ if (primary_sim_slot)
+ *primary_sim_slot = ctx->active_slot_index;
+ return TRUE;
+}
+
+static void
+query_slot_information_status (MbimDevice *device,
+ guint slot_index,
+ GTask *task);
+
+static void
+query_slot_information_status_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMIfaceModem *self;
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ guint32 slot_index;
+ MbimUiccSlotState slot_state;
+ LoadSimSlotsContext *ctx;
+ MMBaseSim *sim;
+ gboolean sim_active = FALSE;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_slot_info_status_response_parse (
+ response,
+ &slot_index,
+ &slot_state,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* the slot index in MM starts at 1 */
+ if ((slot_index + 1) == ctx->active_slot_index)
+ sim_active = TRUE;
+
+ /* Not fully ready (NOT_READY) or unusable (ERROR) SIM cards should also be
+ * reported as being available. */
+ if (slot_state == MBIM_UICC_SLOT_STATE_ACTIVE ||
+ slot_state == MBIM_UICC_SLOT_STATE_ACTIVE_ESIM ||
+ slot_state == MBIM_UICC_SLOT_STATE_ACTIVE_ESIM_NO_PROFILES ||
+ slot_state == MBIM_UICC_SLOT_STATE_NOT_READY ||
+ slot_state == MBIM_UICC_SLOT_STATE_ERROR) {
+ sim = mm_sim_mbim_new_initialized (MM_BASE_MODEM (self),
+ slot_index,
+ sim_active,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ g_ptr_array_add (ctx->sim_slots, sim);
+ } else
+ g_ptr_array_add (ctx->sim_slots, NULL);
+
+ ctx->query_slot_index++;
+ if (ctx->query_slot_index < ctx->number_slots) {
+ query_slot_information_status (device, ctx->query_slot_index, task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+query_slot_information_status (MbimDevice *device,
+ guint slot_index,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) message = NULL;
+
+ message = mbim_message_ms_basic_connect_extensions_slot_info_status_query_new (slot_index, NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)query_slot_information_status_ready,
+ task);
+}
+
+static void
+query_device_slot_mappings_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) message = NULL;
+ guint32 map_count = 0;
+ g_autoptr(MbimSlotArray) slot_mappings = NULL;
+ LoadSimSlotsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse (
+ response,
+ &map_count,
+ &slot_mappings,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (self->priv->executor_index >= map_count) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "The executor index doesn't have an entry in the map count");
+ g_object_unref (task);
+ return;
+ }
+
+ /* the slot index in MM starts at 1 */
+ ctx->active_slot_index = slot_mappings[self->priv->executor_index]->slot + 1;
+ self->priv->active_slot_index = ctx->active_slot_index;
+
+ query_slot_information_status (device, ctx->query_slot_index, task);
+}
+
+static void
+query_device_slot_mappings (MbimDevice *device,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) message = NULL;
+
+ message = mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)query_device_slot_mappings_ready,
+ task);
+}
+
+static void
+query_device_caps_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ g_autoptr(MbimMessage) message = NULL;
+ guint32 executor_index;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_device_caps_response_parse (
+ response,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &executor_index,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ self->priv->executor_index = executor_index;
+
+ query_device_slot_mappings (device, task);
+}
+
+static void
+query_sys_caps_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(MbimMessage) message = NULL;
+ GError *error = NULL;
+ guint32 number_executors;
+ guint32 number_slots;
+ guint32 concurrency;
+ guint64 modem_id;
+ LoadSimSlotsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_sys_caps_response_parse (
+ response,
+ &number_executors,
+ &number_slots,
+ &concurrency,
+ &modem_id,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (number_slots == 1) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Only one SIM slot is supported");
+ g_object_unref (task);
+ return;
+ }
+ ctx->number_slots = number_slots;
+ ctx->sim_slots = g_ptr_array_new_full (number_slots, NULL);
+
+ if (number_executors == 0) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "There is no executor");
+ g_object_unref (task);
+ return;
+ }
+ /* Given that there is one single executor supported,we assume the executor index to be always 0 */
+ if (number_executors == 1) {
+ self->priv->executor_index = 0;
+ query_device_slot_mappings (device, task);
+ return;
+ }
+ /* Given that more than one executors supported,we first query the current device caps to know which is the current executor index */
+ message = mbim_message_ms_basic_connect_extensions_device_caps_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)query_device_caps_ready,
+ task);
+}
+
+static void
+load_sim_slots_mbim (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MbimDevice *device;
+ MbimMessage *message;
+
+ self = g_task_get_source_object (task);
+
+ if (!peek_device (self, &device, NULL, NULL))
+ return;
+
+ message = mbim_message_ms_basic_connect_extensions_sys_caps_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)query_sys_caps_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+static void
+shared_qmi_load_sim_slots_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GPtrArray) sim_slots = NULL;
+ guint primary_sim_slot = 0;
+ LoadSimSlotsContext *ctx;
+
+ if (!mm_shared_qmi_load_sim_slots_finish (self, res, &sim_slots, &primary_sim_slot, NULL)) {
+ load_sim_slots_mbim (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->sim_slots = g_steal_pointer (&sim_slots);
+ ctx->active_slot_index = primary_sim_slot;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+#endif
+
+static void
+load_sim_slots (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ LoadSimSlotsContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (LoadSimSlotsContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_sim_slots_context_free);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ mm_shared_qmi_load_sim_slots (self, (GAsyncReadyCallback)shared_qmi_load_sim_slots_ready, task);
+#else
+ load_sim_slots_mbim (task);
+#endif
+}
+
+/*****************************************************************************/
+/* Set Primary SIM slot (modem interface) */
+
+static gboolean
+set_primary_sim_slot_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_device_slot_mappings_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(GError) error = NULL;
+ guint32 map_count = 0;
+ g_autoptr(MbimSlotArray) slot_mappings = NULL;
+ guint i;
+ guint slot_number;
+
+ self = g_task_get_source_object (task);
+
+ /* the slot index in MM starts at 1 */
+ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)) - 1;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse (
+ response,
+ &map_count,
+ &slot_mappings,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (i = 0; i < map_count; i++) {
+ if (i == self->priv->executor_index) {
+ if (slot_number != slot_mappings[i]->slot) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "SIM slot switch to '%u' failed", slot_number);
+ } else {
+ self->priv->active_slot_index = slot_number + 1;
+ g_task_return_boolean (task, TRUE);
+ }
+
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Can't find executor index '%u'", self->priv->executor_index);
+ g_object_unref (task);
+ return;
+}
+
+static void
+before_set_query_device_slot_mappings_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(MbimMessage) message = NULL;
+ guint32 map_count = 0;
+ g_autoptr(MbimSlotArray) slot_mappings = NULL;
+ guint i;
+ guint slot_number;
+
+ self = g_task_get_source_object (task);
+
+ /* the slot index in MM starts at 1 */
+ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task)) - 1;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_basic_connect_extensions_device_slot_mappings_response_parse (
+ response,
+ &map_count,
+ &slot_mappings,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (i = 0; i < map_count; i++) {
+ if (slot_number == slot_mappings[i]->slot) {
+ if (i != self->priv->executor_index) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "The sim slot '%u' is already used by executor '%u'", slot_number, i);
+ } else {
+ mm_obj_dbg (self, "The slot is already the requested one");
+ g_task_return_boolean (task, TRUE);
+ }
+
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ for (i = 0; i < map_count; i++) {
+ if (i == self->priv->executor_index)
+ slot_mappings[i]->slot = slot_number;
+ }
+
+ message = mbim_message_ms_basic_connect_extensions_device_slot_mappings_set_new (map_count,
+ (const MbimSlot **)slot_mappings,
+ NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)set_device_slot_mappings_ready,
+ task);
+}
+
+static void
+set_primary_sim_slot_mbim (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MbimDevice *device;
+ MbimMessage *message;
+
+ self = g_task_get_source_object (task);
+
+ if (!peek_device (self, &device, NULL, NULL))
+ return;
+
+ message = mbim_message_ms_basic_connect_extensions_device_slot_mappings_query_new (NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)before_set_query_device_slot_mappings_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+static void
+shared_qmi_set_primary_sim_slot_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ if (!mm_shared_qmi_set_primary_sim_slot_finish (self, res, NULL)) {
+ set_primary_sim_slot_mbim (task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+#endif
+
+static void
+set_primary_sim_slot (MMIfaceModem *self,
+ guint sim_slot,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_task_set_task_data (task, GUINT_TO_POINTER (sim_slot), NULL);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ mm_shared_qmi_set_primary_sim_slot (self, sim_slot, (GAsyncReadyCallback)shared_qmi_set_primary_sim_slot_ready, task);
+#else
+ set_primary_sim_slot_mbim (task);
+#endif
+}
+
+/*****************************************************************************/
+/* Set packet service state (3GPP interface) */
+
+static gboolean
+set_packet_service_state_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+packet_service_set_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ MbimPacketServiceState requested_packet_service_state;
+ MbimPacketServiceState packet_service_state;
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (mbim_device_check_ms_mbimex_version (device, 2, 0)) {
+ mbim_message_ms_basic_connect_v2_packet_service_response_parse (
+ response,
+ NULL, /* nw_error */
+ &packet_service_state,
+ NULL, /* data_class */
+ NULL, /* uplink_speed */
+ NULL, /* downlink_speed */
+ NULL, /* frequency_range */
+ &error);
+ } else {
+ mbim_message_packet_service_response_parse (
+ response,
+ NULL, /* nw_error */
+ &packet_service_state,
+ NULL, /* data_class */
+ NULL, /* uplink_speed */
+ NULL, /* downlink_speed */
+ &error);
+ }
+
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ requested_packet_service_state = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ if (((requested_packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED) &&
+ (packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED || packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHING)) ||
+ ((requested_packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED) &&
+ (packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED || packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHING)))
+ g_task_return_boolean (task, TRUE);
+ else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to request state %s, current state: %s",
+ mbim_packet_service_state_get_string (requested_packet_service_state),
+ mbim_packet_service_state_get_string (packet_service_state));
+ g_object_unref (task);
+}
+
+static void
+set_packet_service_state (MMIfaceModem3gpp *self,
+ MMModem3gppPacketServiceState packet_service_state,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(MbimMessage) message = NULL;
+ MbimDevice *device;
+ GTask *task;
+ MbimPacketServiceAction packet_service_action;
+ MbimPacketServiceState requested_packet_service_state;
+
+ g_assert ((packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) ||
+ (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED));
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ switch (packet_service_state) {
+ case MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED:
+ packet_service_action = MBIM_PACKET_SERVICE_ACTION_ATTACH;
+ requested_packet_service_state = MBIM_PACKET_SERVICE_STATE_ATTACHED;
+ break;
+ case MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED:
+ packet_service_action = MBIM_PACKET_SERVICE_ACTION_DETACH;
+ requested_packet_service_state = MBIM_PACKET_SERVICE_STATE_DETACHED;
+ break;
+ case MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_task_set_task_data (task, GUINT_TO_POINTER (requested_packet_service_state), NULL);
+
+ message = mbim_message_packet_service_set_new (packet_service_action, NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)packet_service_set_ready,
+ task);
+}
+
+
+/*****************************************************************************/
MMBroadbandModemMbim *
mm_broadband_modem_mbim_new (const gchar *device,
@@ -2840,6 +7859,12 @@ mm_broadband_modem_mbim_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* MBIM bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
NULL);
}
@@ -2850,24 +7875,46 @@ mm_broadband_modem_mbim_init (MMBroadbandModemMbim *self)
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_BROADBAND_MODEM_MBIM,
MMBroadbandModemMbimPrivate);
+ self->priv->packet_service_state = MBIM_PACKET_SERVICE_STATE_UNKNOWN;
}
static void
-finalize (GObject *object)
+dispose (GObject *object)
{
+ MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object);
MMPortMbim *mbim;
+
+ /* If any port cleanup is needed, it must be done during dispose(), as
+ * the modem object will be affected by an explciit g_object_run_dispose()
+ * that will remove all port references right away */
+ mbim = mm_broadband_modem_mbim_peek_port_mbim (self);
+ if (mbim) {
+ /* Explicitly remove notification handler */
+ self->priv->setup_flags = PROCESS_NOTIFICATION_FLAG_NONE;
+ common_setup_cleanup_unsolicited_events_sync (self, mm_port_mbim_peek_device (mbim), FALSE);
+ /* Disconnect signal handler for mbim-proxy disappearing, if it exists */
+ untrack_mbim_device_removed (self, mbim);
+ /* If we did open the MBIM port during initialization, close it now */
+ if (mm_port_mbim_is_open (mbim))
+ mm_port_mbim_close (mbim, NULL, NULL);
+ }
+
+ G_OBJECT_CLASS (mm_broadband_modem_mbim_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object);
+ g_free (self->priv->caps_custom_data_class);
g_free (self->priv->caps_device_id);
g_free (self->priv->caps_firmware_info);
+ g_free (self->priv->caps_hardware_info);
g_free (self->priv->current_operator_id);
g_free (self->priv->current_operator_name);
-
- mbim = mm_base_modem_peek_port_mbim (MM_BASE_MODEM (self));
- /* If we did open the MBIM port during initialization, close it now */
- if (mbim && mm_port_mbim_is_open (mbim)) {
- mm_port_mbim_close (mbim, NULL, NULL);
- }
+ g_free (self->priv->requested_operator_id);
+ g_list_free_full (self->priv->pco_list, g_object_unref);
G_OBJECT_CLASS (mm_broadband_modem_mbim_parent_class)->finalize (object);
}
@@ -2876,20 +7923,30 @@ static void
iface_modem_init (MMIfaceModem *iface)
{
/* Initialization steps */
+ iface->load_supported_capabilities = modem_load_supported_capabilities;
+ iface->load_supported_capabilities_finish = modem_load_supported_capabilities_finish;
iface->load_current_capabilities = modem_load_current_capabilities;
iface->load_current_capabilities_finish = modem_load_current_capabilities_finish;
+ iface->set_current_capabilities = modem_set_current_capabilities;
+ iface->set_current_capabilities_finish = modem_set_current_capabilities_finish;
iface->load_manufacturer = modem_load_manufacturer;
iface->load_manufacturer_finish = modem_load_manufacturer_finish;
iface->load_model = modem_load_model;
iface->load_model_finish = modem_load_model_finish;
iface->load_revision = modem_load_revision;
iface->load_revision_finish = modem_load_revision_finish;
+ iface->load_hardware_revision = modem_load_hardware_revision;
+ iface->load_hardware_revision_finish = modem_load_hardware_revision_finish;
iface->load_equipment_identifier = modem_load_equipment_identifier;
iface->load_equipment_identifier_finish = modem_load_equipment_identifier_finish;
iface->load_device_identifier = modem_load_device_identifier;
iface->load_device_identifier_finish = modem_load_device_identifier_finish;
iface->load_supported_modes = modem_load_supported_modes;
iface->load_supported_modes_finish = modem_load_supported_modes_finish;
+ iface->load_current_modes = modem_load_current_modes;
+ iface->load_current_modes_finish = modem_load_current_modes_finish;
+ iface->set_current_modes = modem_set_current_modes;
+ iface->set_current_modes_finish = modem_set_current_modes_finish;
iface->load_unlock_required = modem_load_unlock_required;
iface->load_unlock_required_finish = modem_load_unlock_required_finish;
iface->load_unlock_retries = modem_load_unlock_retries;
@@ -2899,12 +7956,33 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_power_state = modem_load_power_state;
iface->load_power_state_finish = modem_load_power_state_finish;
iface->modem_power_up = modem_power_up;
- iface->modem_power_up_finish = common_power_up_down_finish;
+ iface->modem_power_up_finish = power_up_finish;
iface->modem_power_down = modem_power_down;
- iface->modem_power_down_finish = common_power_up_down_finish;
+ iface->modem_power_down_finish = power_down_finish;
+ iface->reset = modem_reset;
+ iface->reset_finish = modem_reset_finish;
iface->load_supported_ip_families = modem_load_supported_ip_families;
iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ iface->load_carrier_config = mm_shared_qmi_load_carrier_config;
+ iface->load_carrier_config_finish = mm_shared_qmi_load_carrier_config_finish;
+ iface->setup_carrier_config = mm_shared_qmi_setup_carrier_config;
+ iface->setup_carrier_config_finish = mm_shared_qmi_setup_carrier_config_finish;
+ iface->load_supported_bands = mm_shared_qmi_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_qmi_load_supported_bands_finish;
+ iface->load_current_bands = mm_shared_qmi_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish;
+ iface->set_current_bands = mm_shared_qmi_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_qmi_set_current_bands_finish;
+ iface->fcc_unlock = mm_shared_qmi_fcc_unlock;
+ iface->fcc_unlock_finish = mm_shared_qmi_fcc_unlock_finish;
+#endif
+
+ /* Additional actions */
+ iface->load_signal_quality = modem_load_signal_quality;
+ iface->load_signal_quality_finish = modem_load_signal_quality_finish;
+
/* Unneeded things */
iface->modem_after_power_up = NULL;
iface->modem_after_power_up_finish = NULL;
@@ -2914,16 +7992,31 @@ iface_modem_init (MMIfaceModem *iface)
iface->setup_flow_control_finish = NULL;
iface->setup_charset = NULL;
iface->setup_charset_finish = NULL;
- iface->load_signal_quality = NULL;
- iface->load_signal_quality_finish = NULL;
+ iface->load_access_technologies = NULL;
+ iface->load_access_technologies_finish = NULL;
/* Create MBIM-specific SIM */
iface->create_sim = create_sim;
iface->create_sim_finish = create_sim_finish;
+ iface->load_sim_slots = load_sim_slots;
+ iface->load_sim_slots_finish = load_sim_slots_finish;
+ iface->set_primary_sim_slot = set_primary_sim_slot;
+ iface->set_primary_sim_slot_finish = set_primary_sim_slot_finish;
- /* Create MBIM-specific bearer */
+ /* Create MBIM-specific bearer and bearer list */
iface->create_bearer = modem_create_bearer;
iface->create_bearer_finish = modem_create_bearer_finish;
+ iface->create_bearer_list = modem_create_bearer_list;
+
+ /* SIM hot swapping */
+ iface->setup_sim_hot_swap = modem_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = modem_setup_sim_hot_swap_finish;
+
+ /* Other actions */
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ iface->factory_reset = mm_shared_qmi_factory_reset;
+ iface->factory_reset_finish = mm_shared_qmi_factory_reset_finish;
+#endif
}
static void
@@ -2955,12 +8048,112 @@ 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_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer;
+ iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish;
+ iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
+ iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
+ iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
+ iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_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;
iface->register_in_network_finish = modem_3gpp_register_in_network_finish;
iface->scan_networks = modem_3gpp_scan_networks;
iface->scan_networks_finish = modem_3gpp_scan_networks_finish;
+ iface->disable_facility_lock = modem_3gpp_disable_facility_lock;
+ iface->disable_facility_lock_finish = modem_3gpp_disable_facility_lock_finish;
+ iface->set_packet_service_state = set_packet_service_state;
+ iface->set_packet_service_state_finish = set_packet_service_state_finish;
+}
+
+static void
+iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
+{
+ /* Initialization steps */
+ iface->check_support = modem_3gpp_profile_manager_check_support;
+ iface->check_support_finish = modem_3gpp_profile_manager_check_support_finish;
+
+ /* Enabling steps */
+ iface->setup_unsolicited_events = setup_unsolicited_events_3gpp_profile_manager;
+ iface->setup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_3gpp_profile_manager_finish;
+ iface->enable_unsolicited_events = enable_unsolicited_events_3gpp_profile_manager;
+ iface->enable_unsolicited_events_finish = common_enable_disable_unsolicited_events_3gpp_profile_manager_finish;
+
+ /* Disabling steps */
+ iface->cleanup_unsolicited_events = cleanup_unsolicited_events_3gpp_profile_manager;
+ iface->cleanup_unsolicited_events_finish = common_setup_cleanup_unsolicited_events_3gpp_profile_manager_finish;
+ iface->disable_unsolicited_events = disable_unsolicited_events_3gpp_profile_manager;
+ iface->disable_unsolicited_events_finish = common_enable_disable_unsolicited_events_3gpp_profile_manager_finish;
+
+ /* Additional actions */
+ iface->list_profiles = modem_3gpp_profile_manager_list_profiles;
+ iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish;
+ iface->check_format = modem_3gpp_profile_manager_check_format;
+ iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish;
+ iface->check_activated_profile = NULL;
+ iface->check_activated_profile_finish = NULL;
+ iface->deactivate_profile = NULL;
+ iface->deactivate_profile_finish = NULL;
+ iface->store_profile = modem_3gpp_profile_manager_store_profile;
+ iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish;
+ iface->delete_profile = modem_3gpp_profile_manager_delete_profile;
+ iface->delete_profile_finish = modem_3gpp_profile_manager_delete_profile_finish;
+}
+
+static void
+iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface)
+{
+ /* Initialization steps */
+ iface->check_support = modem_3gpp_ussd_check_support;
+ iface->check_support_finish = modem_3gpp_ussd_check_support_finish;
+
+ /* Enabling steps */
+ iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish;
+
+ /* Disabling steps */
+ iface->cleanup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events;
+ iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish;
+
+ /* Additional actions */
+ iface->send = modem_3gpp_ussd_send;
+ iface->send_finish = modem_3gpp_ussd_send_finish;
+ iface->cancel = modem_3gpp_ussd_cancel;
+ iface->cancel_finish = modem_3gpp_ussd_cancel_finish;
+}
+
+static void
+iface_modem_location_init (MMIfaceModemLocation *iface)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ iface_modem_location_parent = g_type_interface_peek_parent (iface);
+
+ iface->load_capabilities = mm_shared_qmi_location_load_capabilities;
+ iface->load_capabilities_finish = mm_shared_qmi_location_load_capabilities_finish;
+ iface->enable_location_gathering = mm_shared_qmi_enable_location_gathering;
+ iface->enable_location_gathering_finish = mm_shared_qmi_enable_location_gathering_finish;
+ iface->disable_location_gathering = mm_shared_qmi_disable_location_gathering;
+ iface->disable_location_gathering_finish = mm_shared_qmi_disable_location_gathering_finish;
+ iface->load_supl_server = mm_shared_qmi_location_load_supl_server;
+ iface->load_supl_server_finish = mm_shared_qmi_location_load_supl_server_finish;
+ iface->set_supl_server = mm_shared_qmi_location_set_supl_server;
+ iface->set_supl_server_finish = mm_shared_qmi_location_set_supl_server_finish;
+ iface->load_supported_assistance_data = mm_shared_qmi_location_load_supported_assistance_data;
+ iface->load_supported_assistance_data_finish = mm_shared_qmi_location_load_supported_assistance_data_finish;
+ iface->inject_assistance_data = mm_shared_qmi_location_inject_assistance_data;
+ iface->inject_assistance_data_finish = mm_shared_qmi_location_inject_assistance_data_finish;
+ iface->load_assistance_data_servers = mm_shared_qmi_location_load_assistance_data_servers;
+ iface->load_assistance_data_servers_finish = mm_shared_qmi_location_load_assistance_data_servers_finish;
+#else
+ iface->load_capabilities = NULL;
+ iface->load_capabilities_finish = NULL;
+ iface->enable_location_gathering = NULL;
+ iface->enable_location_gathering_finish = NULL;
+#endif
}
static void
@@ -2974,6 +8167,8 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)
iface->setup_sms_format_finish = NULL;
iface->set_default_storage = NULL;
iface->set_default_storage_finish = NULL;
+ iface->init_current_storages = NULL;
+ iface->init_current_storages_finish = NULL;
iface->load_initial_sms_parts = load_initial_sms_parts;
iface->load_initial_sms_parts_finish = load_initial_sms_parts_finish;
iface->setup_unsolicited_events = setup_unsolicited_events_messaging;
@@ -2988,6 +8183,51 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)
}
static void
+iface_modem_signal_init (MMIfaceModemSignal *iface)
+{
+ iface_modem_signal_parent = g_type_interface_peek_parent (iface);
+
+ iface->check_support = modem_signal_check_support;
+ iface->check_support_finish = modem_signal_check_support_finish;
+ iface->load_values = modem_signal_load_values;
+ iface->load_values_finish = modem_signal_load_values_finish;
+ iface->setup_thresholds = modem_signal_setup_thresholds;
+ iface->setup_thresholds_finish = modem_signal_setup_thresholds_finish;
+}
+
+static void
+iface_modem_sar_init (MMIfaceModemSar *iface)
+{
+ iface->check_support = sar_check_support;
+ iface->check_support_finish = sar_check_support_finish;
+ iface->load_state = sar_load_state;
+ iface->load_state_finish = sar_load_state_finish;
+ iface->load_power_level = sar_load_power_level;
+ iface->load_power_level_finish = sar_load_power_level_finish;
+ iface->enable = sar_enable;
+ iface->enable_finish = sar_enable_finish;
+ iface->set_power_level = sar_set_power_level;
+ iface->set_power_level_finish = sar_set_power_level_finish;
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedQmi *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+shared_qmi_init (MMSharedQmi *iface)
+{
+ iface->peek_client = shared_qmi_peek_client;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+}
+
+#endif
+
+static void
mm_broadband_modem_mbim_class_init (MMBroadbandModemMbimClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -2995,6 +8235,11 @@ mm_broadband_modem_mbim_class_init (MMBroadbandModemMbimClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandModemMbimPrivate));
+ klass->peek_port_mbim_for_data = peek_port_mbim_for_data;
+
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->dispose = dispose;
object_class->finalize = finalize;
broadband_modem_class->initialization_started = initialization_started;
@@ -3004,4 +8249,20 @@ mm_broadband_modem_mbim_class_init (MMBroadbandModemMbimClass *klass)
/* Do not initialize the MBIM modem through AT commands */
broadband_modem_class->enabling_modem_init = NULL;
broadband_modem_class->enabling_modem_init_finish = NULL;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ g_object_class_install_property (object_class, PROP_QMI_UNSUPPORTED,
+ g_param_spec_boolean (MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED,
+ "QMI over MBIM unsupported",
+ "TRUE when QMI over MBIM should not be considered.",
+ FALSE,
+ G_PARAM_READWRITE));
+#endif
+
+ g_object_class_install_property (object_class, PROP_INTEL_FIRMWARE_UPDATE_UNSUPPORTED,
+ g_param_spec_boolean (MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED,
+ "Intel Firmware Update service unsupported",
+ "TRUE when the Intel Firmware Update service should not be considered.",
+ FALSE,
+ G_PARAM_READWRITE));
}
diff --git a/src/mm-broadband-modem-mbim.h b/src/mm-broadband-modem-mbim.h
index b8cf748c..529b3374 100644
--- a/src/mm-broadband-modem-mbim.h
+++ b/src/mm-broadband-modem-mbim.h
@@ -29,6 +29,9 @@ typedef struct _MMBroadbandModemMbim MMBroadbandModemMbim;
typedef struct _MMBroadbandModemMbimClass MMBroadbandModemMbimClass;
typedef struct _MMBroadbandModemMbimPrivate MMBroadbandModemMbimPrivate;
+#define MM_BROADBAND_MODEM_MBIM_QMI_UNSUPPORTED "broadband-modem-mbim-qmi-unsupported"
+#define MM_BROADBAND_MODEM_MBIM_INTEL_FIRMWARE_UPDATE_UNSUPPORTED "broadband-modem-mbim-intel-firmware-update-unsupported"
+
struct _MMBroadbandModemMbim {
MMBroadbandModem parent;
MMBroadbandModemMbimPrivate *priv;
@@ -36,14 +39,27 @@ struct _MMBroadbandModemMbim {
struct _MMBroadbandModemMbimClass{
MMBroadbandModemClass parent;
+
+ MMPortMbim * (* peek_port_mbim_for_data) (MMBroadbandModemMbim *self,
+ MMPort *data,
+ GError **error);
};
GType mm_broadband_modem_mbim_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandModemMbim, g_object_unref)
-MMBroadbandModemMbim *mm_broadband_modem_mbim_new (const gchar *device,
+MMBroadbandModemMbim *mm_broadband_modem_mbim_new (const gchar *device,
const gchar **drivers,
- const gchar *plugin,
- guint16 vendor_id,
- guint16 product_id);
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+MMPortMbim *mm_broadband_modem_mbim_peek_port_mbim (MMBroadbandModemMbim *self);
+MMPortMbim *mm_broadband_modem_mbim_peek_port_mbim_for_data (MMBroadbandModemMbim *self,
+ MMPort *data,
+ GError **error);
+MMPortMbim *mm_broadband_modem_mbim_get_port_mbim (MMBroadbandModemMbim *self);
+MMPortMbim *mm_broadband_modem_mbim_get_port_mbim_for_data (MMBroadbandModemMbim *self,
+ MMPort *data,
+ GError **error);
#endif /* MM_BROADBAND_MODEM_MBIM_H */
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index f4b6c558..a79cc981 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -32,42 +32,54 @@
#include "mm-modem-helpers-qmi.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-iface-modem-3gpp-ussd.h"
+#include "mm-iface-modem-voice.h"
#include "mm-iface-modem-cdma.h"
#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-shared-qmi.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"
+#include "mm-call-qmi.h"
+#include "mm-call-list.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
+static void iface_modem_voice_init (MMIfaceModemVoice *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 void shared_qmi_init (MMSharedQmi *iface);
+static MMIfaceModemLocation *iface_modem_location_parent;
static MMIfaceModemMessaging *iface_modem_messaging_parent;
-static MMIfaceModemLocation *iface_modem_location_parent;
+static MMIfaceModemVoice *iface_modem_voice_parent;
G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmi, mm_broadband_modem_qmi, MM_TYPE_BROADBAND_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init)
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))
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QMI, shared_qmi_init))
struct _MMBroadbandModemQmiPrivate {
/* Cached device IDs, retrieved by the modem interface when loading device
@@ -76,20 +88,31 @@ struct _MMBroadbandModemQmiPrivate {
gchar *meid;
gchar *esn;
- /* Cached supported radio interfaces; in order to load supported modes */
- GArray *supported_radio_interfaces;
-
/* Cached supported frequency bands; in order to handle ANY */
GArray *supported_bands;
/* 3GPP and CDMA share unsolicited events setup/enable/disable/cleanup */
gboolean unsolicited_events_enabled;
gboolean unsolicited_events_setup;
- guint event_report_indication_id;
+ guint nas_event_report_indication_id;
+ guint wds_event_report_indication_id;
#if defined WITH_NEWEST_QMI_COMMANDS
- guint signal_info_indication_id;
+ guint nas_signal_info_indication_id;
#endif /* WITH_NEWEST_QMI_COMMANDS */
+ /* New devices may not support the legacy DMS UIM commands */
+ gboolean dms_uim_deprecated;
+
+ /* Whether autoconnect disabling needs to be checked up during
+ * the device enabling */
+ gboolean autoconnect_checked;
+
+ /* Index of the WDS profile used as initial EPS bearer */
+ guint16 default_attach_pdn;
+
+ /* Support for the APN type mask in profiles */
+ gboolean apn_type_not_supported;
+
/* 3GPP/CDMA registration helpers */
gchar *current_operator_id;
gchar *current_operator_description;
@@ -99,43 +122,62 @@ struct _MMBroadbandModemQmiPrivate {
#if defined WITH_NEWEST_QMI_COMMANDS
guint system_info_indication_id;
#endif /* WITH_NEWEST_QMI_COMMANDS */
+ guint network_reject_indication_id;
/* CDMA activation helpers */
MMModemCdmaActivationState activation_state;
guint activation_event_report_indication_id;
- gpointer activation_ctx;
+ GTask *activation_task;
/* Messaging helpers */
- gboolean messaging_fallback_at;
+ gboolean messaging_fallback_at_only;
gboolean messaging_unsolicited_events_enabled;
gboolean messaging_unsolicited_events_setup;
guint messaging_event_report_indication_id;
/* Location helpers */
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;
+ /* 3GPP USSD helpers */
+ guint ussd_indication_id;
+ guint ussd_release_indication_id;
+ gboolean ussd_unsolicited_events_enabled;
+ gboolean ussd_unsolicited_events_setup;
+ GTask *pending_ussd_action;
+
+ /* Voice helpers */
+ guint all_call_status_indication_id;
+ gboolean all_call_status_unsolicited_events_enabled;
+ gboolean all_call_status_unsolicited_events_setup;
+ guint supplementary_service_indication_id;
+ gboolean supplementary_service_unsolicited_events_setup;
+
/* Firmware helpers */
+ gboolean firmware_list_preloaded;
GList *firmware_list;
MMFirmwareProperties *current_firmware;
+
+ /* For notifying when the qmi-proxy connection is dead */
+ guint qmi_device_removed_id;
};
/*****************************************************************************/
static QmiClient *
-peek_qmi_client (MMBroadbandModemQmi *self,
- QmiService service,
- GError **error)
+shared_qmi_peek_client (MMSharedQmi *self,
+ QmiService service,
+ MMPortQmiFlag flag,
+ GError **error)
{
MMPortQmi *port;
QmiClient *client;
- port = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
+ port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self));
if (!port) {
g_set_error (error,
MM_CORE_ERROR,
@@ -144,9 +186,7 @@ peek_qmi_client (MMBroadbandModemQmi *self,
return NULL;
}
- client = mm_port_qmi_peek_client (port,
- service,
- MM_PORT_QMI_FLAG_DEFAULT);
+ client = mm_port_qmi_peek_client (port, service, flag);
if (!client)
g_set_error (error,
MM_CORE_ERROR,
@@ -157,774 +197,203 @@ peek_qmi_client (MMBroadbandModemQmi *self,
return client;
}
-static gboolean
-ensure_qmi_client (MMBroadbandModemQmi *self,
- QmiService service,
- QmiClient **o_client,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GError *error = NULL;
- QmiClient *client;
-
- client = peek_qmi_client (self, service, &error);
- if (!client) {
- g_simple_async_report_take_gerror_in_idle (
- G_OBJECT (self),
- callback,
- user_data,
- error);
- return FALSE;
- }
-
- *o_client = client;
- return TRUE;
-}
-
/*****************************************************************************/
-/* Power cycle */
-
-static gboolean
-power_cycle_finish (MMBroadbandModemQmi *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-power_cycle_set_operating_mode_reset_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- QmiMessageDmsSetOperatingModeOutput *output;
- GError *error = NULL;
-
- output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
- if (!output ||
- !qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
- g_simple_async_result_take_error (simple, error);
- } else {
- mm_info ("Modem is being rebooted now");
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
-
- if (output)
- qmi_message_dms_set_operating_mode_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-power_cycle_set_operating_mode_offline_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- QmiMessageDmsSetOperatingModeInput *input;
- QmiMessageDmsSetOperatingModeOutput *output;
- GError *error = NULL;
-
- output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
- if (!output) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- qmi_message_dms_set_operating_mode_output_unref (output);
- return;
- }
-
- qmi_message_dms_set_operating_mode_output_unref (output);
-
- /* Now, go into reset mode. This will fully reboot the modem, and the current
- * modem object should get disposed. */
- input = qmi_message_dms_set_operating_mode_input_new ();
- qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_RESET, NULL);
- qmi_client_dms_set_operating_mode (client,
- input,
- 20,
- NULL,
- (GAsyncReadyCallback)power_cycle_set_operating_mode_reset_ready,
- simple);
- qmi_message_dms_set_operating_mode_input_unref (input);
-}
-static void
-power_cycle (MMBroadbandModemQmi *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+MMPortQmi *
+mm_broadband_modem_qmi_get_port_qmi (MMBroadbandModemQmi *self)
{
- QmiMessageDmsSetOperatingModeInput *input;
- GSimpleAsyncResult *simple;
- QmiClient *client;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
- return;
+ MMPortQmi *primary_qmi_port;
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- power_cycle);
+ g_assert (MM_IS_BROADBAND_MODEM_QMI (self));
- /* Now, go into offline mode */
- input = qmi_message_dms_set_operating_mode_input_new ();
- qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_OFFLINE, NULL);
- qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (client),
- input,
- 20,
- NULL,
- (GAsyncReadyCallback)power_cycle_set_operating_mode_offline_ready,
- simple);
- qmi_message_dms_set_operating_mode_input_unref (input);
+ primary_qmi_port = mm_broadband_modem_qmi_peek_port_qmi (self);
+ return (primary_qmi_port ?
+ MM_PORT_QMI (g_object_ref (primary_qmi_port)) :
+ NULL);
}
-/*****************************************************************************/
-/* Create Bearer (Modem interface) */
-
-static MMBaseBearer *
-modem_create_bearer_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+MMPortQmi *
+mm_broadband_modem_qmi_peek_port_qmi (MMBroadbandModemQmi *self)
{
- MMBaseBearer *bearer;
+ MMPortQmi *primary_qmi_port = NULL;
+ GList *qmi_ports;
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
+ g_assert (MM_IS_BROADBAND_MODEM_QMI (self));
- return g_object_ref (bearer);
-}
+ qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_UNKNOWN,
+ MM_PORT_TYPE_QMI);
-static void
-modem_create_bearer (MMIfaceModem *self,
- MMBearerProperties *properties,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- MMBaseBearer *bearer;
- GSimpleAsyncResult *result;
+ /* First QMI port in the list is the primary one always */
+ if (qmi_ports)
+ primary_qmi_port = MM_PORT_QMI (qmi_ports->data);
- /* Set a new ref to the bearer object as result */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ g_list_free_full (qmi_ports, g_object_unref);
- /* We just create a MMBearerQmi */
- mm_dbg ("Creating QMI bearer in QMI modem");
- bearer = mm_bearer_qmi_new (MM_BROADBAND_MODEM_QMI (self), properties, TRUE);
- g_simple_async_result_set_op_res_gpointer (result, bearer, g_object_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ return primary_qmi_port;
}
-/*****************************************************************************/
-/* Current Capabilities loading (Modem interface) */
-
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *nas_client;
- QmiClientDms *dms_client;
- GSimpleAsyncResult *result;
- gboolean run_get_system_selection_preference;
- gboolean run_get_technology_preference;
-
- MMQmiCapabilitiesContext capabilities_context;
-} LoadCurrentCapabilitiesContext;
-
-static MMModemCapability
-modem_load_current_capabilities_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+MMPortQmi *
+mm_broadband_modem_qmi_get_port_qmi_for_data (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_CAPABILITY_NONE;
+ MMPortQmi *qmi_port;
- return ((MMModemCapability) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res))));
-}
+ g_assert (MM_IS_BROADBAND_MODEM_QMI (self));
-static void
-load_current_capabilities_context_complete_and_free (LoadCurrentCapabilitiesContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->nas_client);
- g_object_unref (ctx->dms_client);
- g_object_unref (ctx->self);
- g_slice_free (LoadCurrentCapabilitiesContext, ctx);
+ qmi_port = mm_broadband_modem_qmi_peek_port_qmi_for_data (self, data, out_sio_port, error);
+ return (qmi_port ?
+ MM_PORT_QMI (g_object_ref (qmi_port)) :
+ NULL);
}
-static void load_current_capabilities_context_step (LoadCurrentCapabilitiesContext *ctx);
-
-static void
-load_current_capabilities_get_capabilities_ready (QmiClientDms *client,
- GAsyncResult *res,
- LoadCurrentCapabilitiesContext *ctx)
+static MMPortQmi *
+peek_port_qmi_for_data (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error)
{
- QmiMessageDmsGetCapabilitiesOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_dms_get_capabilities_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- } else if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get Capabilities: ");
- g_simple_async_result_take_error (ctx->result, error);
- } else {
- guint i;
- GArray *radio_interface_list;
+ GList *cdc_wdm_qmi_ports;
+ GList *l;
+ const gchar *net_port_parent_path;
+ MMPortQmi *found = NULL;
+ const gchar *net_port_driver;
- qmi_message_dms_get_capabilities_output_get_info (
- output,
- NULL, /* info_max_tx_channel_rate */
- NULL, /* info_max_rx_channel_rate */
- NULL, /* info_data_service_capability */
- NULL, /* info_sim_capability */
- &radio_interface_list,
- NULL);
+ g_assert (MM_IS_BROADBAND_MODEM_QMI (self));
+ g_assert (mm_port_get_subsys (data) == MM_PORT_SUBSYS_NET);
- for (i = 0; i < radio_interface_list->len; i++) {
- ctx->capabilities_context.dms_capabilities |=
- mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list,
- QmiDmsRadioInterface,
- i));
- }
+ net_port_driver = mm_kernel_device_get_driver (mm_port_peek_kernel_device (data));
+ if (g_strcmp0 (net_port_driver, "qmi_wwan") != 0 && g_strcmp0 (net_port_driver, "mhi_net")) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unsupported QMI kernel driver for 'net/%s': %s",
+ mm_port_get_device (data),
+ net_port_driver);
+ return NULL;
}
- if (output)
- qmi_message_dms_get_capabilities_output_unref (output);
-
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (mm_modem_capability_from_qmi_capabilities_context (&ctx->capabilities_context)),
- NULL);
- load_current_capabilities_context_complete_and_free (ctx);
-}
+ if (!g_strcmp0 (net_port_driver, "mhi_net"))
+ return mm_broadband_modem_qmi_peek_port_qmi (self);
-static void
-load_current_capabilities_get_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- LoadCurrentCapabilitiesContext *ctx)
-{
- QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_nas_get_technology_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_get_technology_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't get technology preference: %s", error->message);
- g_error_free (error);
- } else {
- qmi_message_nas_get_technology_preference_output_get_active (
- output,
- &ctx->capabilities_context.nas_tp_mask,
- NULL, /* duration */
- NULL);
+ net_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (data));
+ if (!net_port_parent_path) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "No parent path for 'net/%s'",
+ mm_port_get_device (data));
+ return NULL;
}
- if (output)
- qmi_message_nas_get_technology_preference_output_unref (output);
-
- /* Mark as TP already run */
- ctx->run_get_technology_preference = FALSE;
- load_current_capabilities_context_step (ctx);
-}
-
-static void
-load_current_capabilities_get_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- LoadCurrentCapabilitiesContext *ctx)
-{
- QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
+ /* Find the CDC-WDM port on the same USB interface as the given net port */
+ cdc_wdm_qmi_ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_USBMISC,
+ MM_PORT_TYPE_QMI);
+ for (l = cdc_wdm_qmi_ports; l && !found; l = g_list_next (l)) {
+ const gchar *wdm_port_parent_path;
- output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't get system selection preference: %s", error->message);
- g_error_free (error);
- } else {
- qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
- output,
- &ctx->capabilities_context.nas_ssp_mode_preference_mask,
- NULL);
+ g_assert (MM_IS_PORT_QMI (l->data));
+ wdm_port_parent_path = mm_kernel_device_get_interface_sysfs_path (mm_port_peek_kernel_device (MM_PORT (l->data)));
+ if (wdm_port_parent_path && g_str_equal (wdm_port_parent_path, net_port_parent_path))
+ found = MM_PORT_QMI (l->data);
}
- if (output)
- qmi_message_nas_get_system_selection_preference_output_unref (output);
-
- /* Mark as SSP already run */
- ctx->run_get_system_selection_preference = FALSE;
- load_current_capabilities_context_step (ctx);
-}
+ g_list_free_full (cdc_wdm_qmi_ports, g_object_unref);
-static void
-load_current_capabilities_context_step (LoadCurrentCapabilitiesContext *ctx)
-{
- if (ctx->run_get_system_selection_preference) {
- qmi_client_nas_get_system_selection_preference (
- ctx->nas_client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)load_current_capabilities_get_system_selection_preference_ready,
- ctx);
- return;
- }
-
- if (ctx->run_get_technology_preference) {
- qmi_client_nas_get_technology_preference (
- ctx->nas_client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)load_current_capabilities_get_technology_preference_ready,
- ctx);
- return;
- }
+ if (!found)
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Couldn't find associated QMI port for 'net/%s'",
+ mm_port_get_device (data));
+ else if (out_sio_port)
+ *out_sio_port = QMI_SIO_PORT_NONE;
- qmi_client_dms_get_capabilities (
- ctx->dms_client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)load_current_capabilities_get_capabilities_ready,
- ctx);
+ return found;
}
-static void
-modem_load_current_capabilities (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+MMPortQmi *
+mm_broadband_modem_qmi_peek_port_qmi_for_data (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error)
{
- LoadCurrentCapabilitiesContext *ctx;
- QmiClient *nas_client = NULL;
- QmiClient *dms_client = NULL;
-
- /* Best way to get current capabilities (ie, enabled radios) is
- * Get System Selection Preference's "mode preference" TLV, but that's
- * only supported by NAS >= 1.1, meaning older Gobi devices don't
- * implement it.
- *
- * On these devices, the DMS Get Capabilities call appears to report
- * currently enabled radios, but this does not take the user's
- * technology preference into account.
- *
- * So in the absence of System Selection Preference, we check the
- * Technology Preference first, and if that is "AUTO" we fall back to
- * Get Capabilities.
- */
-
- mm_dbg ("loading current capabilities...");
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &nas_client,
- callback, user_data))
- return;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &dms_client,
- callback, user_data))
- return;
+ g_assert (MM_BROADBAND_MODEM_QMI_GET_CLASS (self)->peek_port_qmi_for_data);
- ctx = g_slice_new0 (LoadCurrentCapabilitiesContext);
- ctx->self = g_object_ref (self);
- ctx->nas_client = g_object_ref (nas_client);
- ctx->dms_client = g_object_ref (dms_client);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_current_capabilities);
-
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_get_system_selection_preference = qmi_client_check_version (nas_client, 1, 1);
- ctx->run_get_technology_preference = TRUE;
-
- load_current_capabilities_context_step (ctx);
+ return MM_BROADBAND_MODEM_QMI_GET_CLASS (self)->peek_port_qmi_for_data (self, data, out_sio_port, error);
}
/*****************************************************************************/
-/* Supported capabilities loading (Modem interface) */
-
-typedef struct {
- MMBroadbandModemQmi *self;
- GSimpleAsyncResult *result;
-} LoadSupportedCapabilitiesContext;
-
-static void
-load_supported_capabilities_context_complete_and_free (LoadSupportedCapabilitiesContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LoadSupportedCapabilitiesContext, ctx);
-}
-
-static GArray *
-modem_load_supported_capabilities_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
-}
+/* Create Bearer (Modem interface) */
-static void
-dms_get_capabilities_ready (QmiClientDms *client,
+static MMBaseBearer *
+modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
- LoadSupportedCapabilitiesContext *ctx)
+ GError **error)
{
- QmiMessageDmsGetCapabilitiesOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_dms_get_capabilities_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- } else if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get supported capabilities: ");
- g_simple_async_result_take_error (ctx->result, error);
- } else {
- guint i;
- MMModemCapability mask = MM_MODEM_CAPABILITY_NONE;
- MMModemCapability single;
- GArray *radio_interface_list;
- GArray *supported_combinations;
- GArray *filtered_combinations;
-
- qmi_message_dms_get_capabilities_output_get_info (
- output,
- NULL, /* info_max_tx_channel_rate */
- NULL, /* info_max_rx_channel_rate */
- NULL, /* info_data_service_capability */
- NULL, /* info_sim_capability */
- &radio_interface_list,
- NULL);
-
- for (i = 0; i < radio_interface_list->len; i++) {
- mask |= mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list,
- QmiDmsRadioInterface,
- i));
- }
-
- /* Cache supported radio interfaces */
- if (ctx->self->priv->supported_radio_interfaces)
- g_array_unref (ctx->self->priv->supported_radio_interfaces);
- ctx->self->priv->supported_radio_interfaces = g_array_ref (radio_interface_list);
-
- supported_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 7);
-
- /* Add all possible supported capability combinations, we will filter
- * them out afterwards */
-
- /* GSM/UMTS */
- single = MM_MODEM_CAPABILITY_GSM_UMTS;
- g_array_append_val (supported_combinations, single);
- /* CDMA/EVDO */
- single = MM_MODEM_CAPABILITY_CDMA_EVDO;
- g_array_append_val (supported_combinations, single);
- /* LTE only */
- single = MM_MODEM_CAPABILITY_LTE;
- g_array_append_val (supported_combinations, single);
- /* GSM/UMTS + CDMA/EVDO */
- single = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_GSM_UMTS);
- g_array_append_val (supported_combinations, single);
- /* GSM/UMTS + LTE */
- single = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (supported_combinations, single);
- /* CDMA/EVDO + LTE */
- single = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (supported_combinations, single);
- /* GSM/UMTS + CDMA/EVDO + LTE */
- single = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (supported_combinations, single);
-
- /* Now filter out based on the real capabilities of the modem */
- filtered_combinations = mm_filter_supported_capabilities (mask,
- supported_combinations);
- g_array_unref (supported_combinations);
-
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- filtered_combinations,
- (GDestroyNotify) g_array_unref);
- }
-
- if (output)
- qmi_message_dms_get_capabilities_output_unref (output);
-
- load_supported_capabilities_context_complete_and_free (ctx);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_load_supported_capabilities (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_create_bearer (MMIfaceModem *self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- LoadSupportedCapabilitiesContext *ctx;
- QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
- return;
+ MMBaseBearer *bearer;
+ GTask *task;
- ctx = g_slice_new (LoadSupportedCapabilitiesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_capabilities);
+ /* We just create a MMBearerQmi */
+ bearer = mm_bearer_qmi_new (MM_BROADBAND_MODEM_QMI (self), properties);
- mm_dbg ("loading supported capabilities...");
- qmi_client_dms_get_capabilities (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_get_capabilities_ready,
- ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
/*****************************************************************************/
-/* Current capabilities setting (Modem interface) */
+/* Create Bearer List (Modem interface) */
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *client;
- GSimpleAsyncResult *result;
- MMModemCapability capabilities;
- gboolean run_set_system_selection_preference;
- gboolean run_set_technology_preference;
-} SetCurrentCapabilitiesContext;
-
-static void
-set_current_capabilities_context_complete_and_free (SetCurrentCapabilitiesContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->client);
- g_object_unref (ctx->self);
- g_slice_free (SetCurrentCapabilitiesContext, ctx);
-}
-
-static gboolean
-set_current_capabilities_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-capabilities_power_cycle_ready (MMBroadbandModemQmi *self,
- GAsyncResult *res,
- SetCurrentCapabilitiesContext *ctx)
-{
- GError *error = NULL;
-
- if (!power_cycle_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_capabilities_context_complete_and_free (ctx);
-}
-
-static void
-capabilities_power_cycle (SetCurrentCapabilitiesContext *ctx)
-{
- /* Power cycle the modem */
- power_cycle (ctx->self,
- (GAsyncReadyCallback)capabilities_power_cycle_ready,
- ctx);
-}
-
-static void set_current_capabilities_context_step (SetCurrentCapabilitiesContext *ctx);
-
-static void
-capabilities_set_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- SetCurrentCapabilitiesContext *ctx)
+static MMBearerList *
+modem_create_bearer_list (MMIfaceModem *self)
{
- QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_nas_set_technology_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_set_technology_preference_output_get_result (output, &error) &&
- !g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- mm_dbg ("Couldn't set technology preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_technology_preference_output_unref (output);
- } else {
- if (error)
- g_error_free (error);
-
- /* Good! now reboot the modem */
- capabilities_power_cycle (ctx);
- qmi_message_nas_set_technology_preference_output_unref (output);
- return;
- }
-
- ctx->run_set_technology_preference = FALSE;
- set_current_capabilities_context_step (ctx);
-}
-
-static void
-capabilities_set_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- SetCurrentCapabilitiesContext *ctx)
-{
- QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
+ MMPortQmi *port;
+ guint n = 0;
+ guint n_multiplexed = 0;
- output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't set system selection preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
+ port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self));
+ if (!port) {
+ /* this should not happen, just fallback to defaults */
+ mm_obj_warn (self, "no port to query maximum number of supported network links");
} else {
- /* Good! now reboot the modem */
- capabilities_power_cycle (ctx);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- return;
- }
+ MMPortQmiKernelDataMode kernel_data_modes;
- /* Try with the deprecated command */
- ctx->run_set_system_selection_preference = FALSE;
- set_current_capabilities_context_step (ctx);
-}
-
-static void
-set_current_capabilities_context_step (SetCurrentCapabilitiesContext *ctx)
-{
- if (ctx->run_set_system_selection_preference) {
- QmiMessageNasSetSystemSelectionPreferenceInput *input;
- QmiNasRatModePreference pref;
+ kernel_data_modes = mm_port_qmi_get_kernel_data_modes (port);
- pref = mm_modem_capability_to_qmi_rat_mode_preference (ctx->capabilities);
- if (!pref) {
- gchar *str;
+ /* There are setups, like IPA, where there is ONLY multiplexing expected
+ * and supported. In those cases, there isn't any expected non-multiplexed
+ * bearer */
- str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled capabilities setting: '%s'",
- str);
- g_free (str);
- set_current_capabilities_context_complete_and_free (ctx);
- return;
+ if (kernel_data_modes & (QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP | MM_PORT_QMI_KERNEL_DATA_MODE_802_3)) {
+ /* The maximum number of available/connected modems is guessed from
+ * the size of the data ports list. */
+ n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self)));
+ mm_obj_dbg (self, "allowed up to %u active bearers", n);
}
- input = qmi_message_nas_set_system_selection_preference_input_new ();
- qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
- qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_system_selection_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)capabilities_set_system_selection_preference_ready,
- ctx);
- qmi_message_nas_set_system_selection_preference_input_unref (input);
- return;
- }
-
- if (ctx->run_set_technology_preference) {
- QmiMessageNasSetTechnologyPreferenceInput *input;
- QmiNasRadioTechnologyPreference pref;
-
- pref = mm_modem_capability_to_qmi_radio_technology_preference (ctx->capabilities);
- if (!pref) {
- gchar *str;
-
- str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled capabilities setting: '%s'",
- str);
- g_free (str);
- set_current_capabilities_context_complete_and_free (ctx);
- return;
+ if (kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)) {
+ /* The maximum number of multiplexed links is retrieved from the
+ * MMPortQmi */
+ n_multiplexed = mm_port_qmi_get_max_multiplexed_links (port);
+ mm_obj_dbg (self, "allowed up to %u active multiplexed bearers", n_multiplexed);
}
-
- input = qmi_message_nas_set_technology_preference_input_new ();
- qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_technology_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)capabilities_set_technology_preference_ready,
- ctx);
- qmi_message_nas_set_technology_preference_input_unref (input);
- return;
}
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting capabilities is not supported by this device");
- set_current_capabilities_context_complete_and_free (ctx);
-}
-
-static void
-set_current_capabilities (MMIfaceModem *self,
- MMModemCapability capabilities,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SetCurrentCapabilitiesContext *ctx;
- QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- ctx = g_slice_new0 (SetCurrentCapabilitiesContext);
- 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,
- set_current_capabilities);
- ctx->capabilities = capabilities;
-
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_set_system_selection_preference = qmi_client_check_version (client, 1, 1);
-
- /* Technology preference introduced in NAS 1.0, so always available */
- ctx->run_set_technology_preference = TRUE;
-
- set_current_capabilities_context_step (ctx);
+ /* by default, no multiplexing support */
+ return mm_bearer_list_new (n, n_multiplexed);
}
/*****************************************************************************/
@@ -935,20 +404,13 @@ modem_load_manufacturer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- gchar *manufacturer;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- manufacturer = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded manufacturer: %s", manufacturer);
- return manufacturer;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
dms_get_manufacturer_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsGetManufacturerOutput *output = NULL;
GError *error = NULL;
@@ -956,24 +418,21 @@ dms_get_manufacturer_ready (QmiClientDms *client,
output = qmi_client_dms_get_manufacturer_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_get_manufacturer_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get Manufacturer: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else {
const gchar *str;
qmi_message_dms_get_manufacturer_output_get_manufacturer (output, &str, NULL);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ g_task_return_pointer (task, g_strdup (str), g_free);
}
if (output)
qmi_message_dms_get_manufacturer_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -981,219 +440,159 @@ modem_load_manufacturer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_manufacturer);
-
- mm_dbg ("loading manufacturer...");
+ mm_obj_dbg (self, "loading manufacturer...");
qmi_client_dms_get_manufacturer (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_get_manufacturer_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
-/* Model loading (Modem interface) */
+/* Revision loading (Modem interface) */
static gchar *
-modem_load_model_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_load_revision_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- gchar *model;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- model = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded model: %s", model);
- return model;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-dms_get_model_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+dms_get_revision_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessageDmsGetModelOutput *output = NULL;
+ QmiMessageDmsGetRevisionOutput *output = NULL;
GError *error = NULL;
- output = qmi_client_dms_get_model_finish (client, res, &error);
+ output = qmi_client_dms_get_revision_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_dms_get_model_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get Model: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+ } else if (!qmi_message_dms_get_revision_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get Revision: ");
+ g_task_return_error (task, error);
} else {
const gchar *str;
- qmi_message_dms_get_model_output_get_model (output, &str, NULL);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ qmi_message_dms_get_revision_output_get_revision (output, &str, NULL);
+ g_task_return_pointer (task, g_strdup (str), g_free);
}
if (output)
- qmi_message_dms_get_model_output_unref (output);
+ qmi_message_dms_get_revision_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-modem_load_model (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_load_revision (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_model);
-
- mm_dbg ("loading model...");
- qmi_client_dms_get_model (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_get_model_ready,
- result);
+ mm_obj_dbg (self, "loading revision...");
+ qmi_client_dms_get_revision (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_get_revision_ready,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
-/* Revision loading (Modem interface) */
+/* Hardware Revision loading (Modem interface) */
static gchar *
-modem_load_revision_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_load_hardware_revision_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- gchar *revision;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- revision = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded revision: %s", revision);
- return revision;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-dms_get_revision_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+dms_get_hardware_revision_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessageDmsGetRevisionOutput *output = NULL;
+ QmiMessageDmsGetHardwareRevisionOutput *output = NULL;
GError *error = NULL;
- output = qmi_client_dms_get_revision_finish (client, res, &error);
+ output = qmi_client_dms_get_hardware_revision_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_dms_get_revision_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get Revision: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+ } else if (!qmi_message_dms_get_hardware_revision_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get Hardware Revision: ");
+ g_task_return_error (task, error);
} else {
const gchar *str;
- qmi_message_dms_get_revision_output_get_revision (output, &str, NULL);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ qmi_message_dms_get_hardware_revision_output_get_revision (output, &str, NULL);
+ g_task_return_pointer (task, g_strdup (str), g_free);
}
if (output)
- qmi_message_dms_get_revision_output_unref (output);
+ qmi_message_dms_get_hardware_revision_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-modem_load_revision (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_load_hardware_revision (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_revision);
-
- mm_dbg ("loading revision...");
- qmi_client_dms_get_revision (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_get_revision_ready,
- result);
+ mm_obj_dbg (self, "loading hardware revision...");
+ qmi_client_dms_get_hardware_revision (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_get_hardware_revision_ready,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Equipment Identifier loading (Modem interface) */
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClient *client;
- GSimpleAsyncResult *result;
-} LoadEquipmentIdentifierContext;
-
-static void
-load_equipment_identifier_context_complete_and_free (LoadEquipmentIdentifierContext *ctx)
-{
- 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);
-}
-
static gchar *
modem_load_equipment_identifier_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- gchar *equipment_identifier;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- equipment_identifier = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded equipment identifier: %s", equipment_identifier);
- return equipment_identifier;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
dms_get_ids_ready (QmiClientDms *client,
GAsyncResult *res,
- LoadEquipmentIdentifierContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageDmsGetIdsOutput *output = NULL;
GError *error = NULL;
const gchar *str;
@@ -1202,19 +601,21 @@ dms_get_ids_ready (QmiClientDms *client,
output = qmi_client_dms_get_ids_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- load_equipment_identifier_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_dms_get_ids_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get IDs: ");
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
qmi_message_dms_get_ids_output_unref (output);
- load_equipment_identifier_context_complete_and_free (ctx);
return;
}
+ self = g_task_get_source_object (task);
+
/* In order:
* If we have a IMEI, use it...
* Otherwise, if we have a ESN, use it...
@@ -1223,48 +624,46 @@ dms_get_ids_ready (QmiClientDms *client,
*/
if (qmi_message_dms_get_ids_output_get_imei (output, &str, NULL) &&
- str[0] != '\0' && str[0] != '0') {
- g_free (ctx->self->priv->imei);
- ctx->self->priv->imei = g_strdup (str);
+ str[0] != '\0') {
+ g_free (self->priv->imei);
+ self->priv->imei = g_strdup (str);
}
if (qmi_message_dms_get_ids_output_get_esn (output, &str, NULL) &&
- str[0] != '\0' && str[0] != '0') {
- g_free (ctx->self->priv->esn);
+ str[0] != '\0') {
+ g_clear_pointer (&self->priv->esn, g_free);
len = strlen (str);
if (len == 7)
- ctx->self->priv->esn = g_strdup_printf ("0%s", str); /* zero-pad to 8 chars */
+ self->priv->esn = g_strdup_printf ("0%s", str); /* zero-pad to 8 chars */
else if (len == 8)
- ctx->self->priv->esn = g_strdup (str);
+ self->priv->esn = g_strdup (str);
else
- g_warn_if_reached ();
+ mm_obj_dbg (self, "invalid ESN reported: '%s' (unexpected length)", str);
}
if (qmi_message_dms_get_ids_output_get_meid (output, &str, NULL) &&
- str[0] != '\0' && str[0] != '0') {
- g_free (ctx->self->priv->meid);
+ str[0] != '\0') {
+ g_clear_pointer (&self->priv->meid, g_free);
len = strlen (str);
if (len == 14)
- ctx->self->priv->meid = g_strdup (str);
+ self->priv->meid = g_strdup (str);
else
- g_warn_if_reached ();
+ mm_obj_dbg (self, "invalid MEID reported: '%s' (unexpected length)", str);
}
- if (ctx->self->priv->imei)
- str = ctx->self->priv->imei;
- else if (ctx->self->priv->esn)
- str = ctx->self->priv->esn;
- else if (ctx->self->priv->meid)
- str = ctx->self->priv->meid;
+ if (self->priv->imei)
+ str = self->priv->imei;
+ else if (self->priv->esn)
+ str = self->priv->esn;
+ else if (self->priv->meid)
+ str = self->priv->meid;
else
str = "unknown";
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ g_task_return_pointer (task, g_strdup (str), g_free);
+ g_object_unref (task);
qmi_message_dms_get_ids_output_unref (output);
- load_equipment_identifier_context_complete_and_free (ctx);
}
static void
@@ -1272,29 +671,20 @@ modem_load_equipment_identifier (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- LoadEquipmentIdentifierContext *ctx;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- ctx = g_new (LoadEquipmentIdentifierContext, 1);
- 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,
- modem_load_equipment_identifier);
-
- mm_dbg ("loading equipment identifier...");
+ mm_obj_dbg (self, "loading equipment identifier...");
qmi_client_dms_get_ids (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_get_ids_ready,
- ctx);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -1305,14 +695,7 @@ modem_load_device_identifier_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- gchar *device_identifier;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- device_identifier = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded device identifier: %s", device_identifier);
- return device_identifier;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -1320,23 +703,22 @@ modem_load_device_identifier (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- gchar *device_identifier;
+ gchar *device_identifier;
+ GTask *task;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("loading device identifier...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_device_identifier);
+ mm_obj_dbg (self, "loading device identifier...");
/* Just use dummy ATI/ATI1 replies, all the other internal info should be
* enough for uniqueness */
- device_identifier = mm_broadband_modem_create_device_identifier (MM_BROADBAND_MODEM (self), "", "");
- g_simple_async_result_set_op_res_gpointer (result,
- device_identifier,
- (GDestroyNotify)g_free);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ device_identifier = mm_broadband_modem_create_device_identifier (MM_BROADBAND_MODEM (self), "", "", &error);
+ if (!device_identifier)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, device_identifier, g_free);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -1347,21 +729,13 @@ modem_load_own_numbers_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- gchar **own_numbers;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- own_numbers = g_new0 (gchar *, 2);
- own_numbers[0] = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded own numbers: %s", own_numbers[0]);
- return own_numbers;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
dms_get_msisdn_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsGetMsisdnOutput *output = NULL;
GError *error = NULL;
@@ -1369,24 +743,24 @@ dms_get_msisdn_ready (QmiClientDms *client,
output = qmi_client_dms_get_msisdn_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_get_msisdn_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get MSISDN: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else {
const gchar *str = NULL;
+ GStrv numbers;
qmi_message_dms_get_msisdn_output_get_msisdn (output, &str, NULL);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ numbers = g_new0 (gchar *, 2);
+ numbers[0] = g_strdup (str);
+ g_task_return_pointer (task, numbers, (GDestroyNotify)g_strfreev);
}
if (output)
qmi_message_dms_get_msisdn_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -1394,745 +768,491 @@ modem_load_own_numbers (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_own_numbers);
-
- mm_dbg ("loading own numbers...");
+ mm_obj_dbg (self, "loading own numbers...");
qmi_client_dms_get_msisdn (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_get_msisdn_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Check if unlock required (Modem interface) */
+typedef enum {
+ LOAD_UNLOCK_REQUIRED_STEP_FIRST,
+ LOAD_UNLOCK_REQUIRED_STEP_CDMA,
+ LOAD_UNLOCK_REQUIRED_STEP_DMS,
+ LOAD_UNLOCK_REQUIRED_STEP_UIM,
+} LoadUnlockRequiredStep;
+
+typedef struct {
+ LoadUnlockRequiredStep step;
+ gboolean last_attempt;
+} LoadUnlockRequiredContext;
+
static MMModemLock
modem_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;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCK_UNKNOWN;
+ }
+ return (MMModemLock)value;
}
+static void load_unlock_required_context_step (GTask *task);
+
static void
-dms_uim_get_pin_status_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+unlock_required_uim_get_card_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessageDmsUimGetPinStatusOutput *output;
+ MMBroadbandModemQmi *self;
+ LoadUnlockRequiredContext *ctx;
+ QmiMessageUimGetCardStatusOutput *output;
GError *error = NULL;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
- output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error);
- if (!output) {
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_uim_get_card_status_finish (client, res, &error);
+ if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error)) {
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)) {
- /* Fatal, so that we mark the modem unusable.*/
- if (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,
- "SIM failure: %s",
- error->message);
- g_error_free (error);
- }
- /* 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!mm_qmi_uim_get_card_status_output_parse (self,
+ output,
+ &lock,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &error)) {
+ /* The device may report a SIM NOT INSERTED error if we're querying the
+ * card status soon after power on. We'll let the Modem interface generic
+ * logic retry loading the info a bit later if that's the case. This will
+ * make device detection slower when there's really no SIM card, but there's
+ * no clear better way to handle it :/ */
+ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) && !ctx->last_attempt) {
+ g_clear_error (&error);
+ g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "No card found (retry)");
}
- /* Other errors, just propagate them */
- else {
- g_prefix_error (&error, "Couldn't get PIN status: ");
- g_simple_async_result_take_error (simple, error);
- }
- } else {
- MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
- QmiDmsUimPinStatus current_status;
-
- if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
- output,
- &current_status,
- NULL, /* verify_retries_left */
- NULL, /* unblock_retries_left */
- NULL))
- lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, TRUE);
-
- if (lock == MM_MODEM_LOCK_NONE &&
- qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
- output,
- &current_status,
- NULL, /* verify_retries_left */
- NULL, /* unblock_retries_left */
- NULL))
- lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, FALSE);
-
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (lock), NULL);
- }
-
- if (output)
- qmi_message_dms_uim_get_pin_status_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-modem_load_unlock_required (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
- 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_load_unlock_required);
-
- /* CDMA-only modems don't need this */
- if (mm_iface_modem_is_cdma_only (self)) {
- mm_dbg ("Skipping unlock check in CDMA-only modem...");
- g_simple_async_result_set_op_res_gpointer (result,
- GUINT_TO_POINTER (MM_MODEM_LOCK_NONE),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
-
- mm_dbg ("loading unlock required...");
- qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_uim_get_pin_status_ready,
- result);
-}
-
-/*****************************************************************************/
-/* Check if unlock retries (Modem interface) */
-
-static MMUnlockRetries *
-modem_load_unlock_retries_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_int (task, lock);
+ g_object_unref (task);
- return MM_UNLOCK_RETRIES (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ qmi_message_uim_get_card_status_output_unref (output);
}
static void
-retry_count_dms_uim_get_pin_status_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+dms_uim_get_pin_status_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ LoadUnlockRequiredContext *ctx;
QmiMessageDmsUimGetPinStatusOutput *output;
GError *error = NULL;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+ QmiDmsUimPinStatus current_status;
output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error);
if (!output) {
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)) {
- g_prefix_error (&error, "Couldn't get unlock retries: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- MMUnlockRetries *retries;
- guint8 verify_retries_left;
- guint8 unblock_retries_left;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- retries = mm_unlock_retries_new ();
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
- output,
- NULL, /* current_status */
- &verify_retries_left,
- &unblock_retries_left,
- NULL)) {
- mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, verify_retries_left);
- mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, unblock_retries_left);
+ if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) {
+ /* We get InvalidQmiCommand on newer devices which don't like the legacy way */
+ if (g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND) ||
+ g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) {
+ g_error_free (error);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ /* Flag that the command is unsupported, and try with the new way */
+ self->priv->dms_uim_deprecated = TRUE;
+ ctx->step++;
+ load_unlock_required_context_step (task);
+ return;
}
- if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
- output,
- NULL, /* current_status */
- &verify_retries_left,
- &unblock_retries_left,
- NULL)) {
- mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, verify_retries_left);
- mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, unblock_retries_left);
+ /* Internal and uim-uninitialized errors are retry-able before being fatal */
+ if (g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_INTERNAL) ||
+ g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_UIM_UNINITIALIZED)) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Couldn't get PIN status (retry): %s",
+ error->message);
+ g_object_unref (task);
+ g_error_free (error);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ return;
}
- g_simple_async_result_set_op_res_gpointer (simple, retries, g_object_unref);
- }
-
- if (output)
+ /* Other errors, just propagate them */
+ g_prefix_error (&error, "Couldn't get PIN status: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
qmi_message_dms_uim_get_pin_status_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-modem_load_unlock_retries (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
- 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_load_unlock_retries);
-
- mm_dbg ("loading unlock retries...");
- qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)retry_count_dms_uim_get_pin_status_ready,
- result);
-}
-
-/*****************************************************************************/
-/* Load supported bands (Modem interface) */
-
-static GArray *
-modem_load_supported_bands_finish (MMIfaceModem *_self,
- GAsyncResult *res,
- GError **error)
-{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
+ /* Command succeeded, process results */
- if (self->priv->supported_bands)
- g_array_unref (self->priv->supported_bands);
+ if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
+ output,
+ &current_status,
+ NULL, /* verify_retries_left */
+ NULL, /* unblock_retries_left */
+ NULL))
+ lock = mm_modem_lock_from_qmi_uim_pin_status (current_status, TRUE);
+
+ if (lock == MM_MODEM_LOCK_NONE &&
+ qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
+ output,
+ &current_status,
+ NULL, /* verify_retries_left */
+ NULL, /* unblock_retries_left */
+ NULL)) {
+ MMModemLock lock2;
- /* Cache the supported bands value */
- self->priv->supported_bands = g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ /* We only use the PIN2 status if it isn't unknown */
+ lock2 = mm_modem_lock_from_qmi_uim_pin_status (current_status, FALSE);
+ if (lock2 != MM_MODEM_LOCK_UNKNOWN)
+ lock = lock2;
+ }
- return g_array_ref (self->priv->supported_bands);
+ /* We're done! */
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ g_task_return_int (task, lock);
+ g_object_unref (task);
}
static void
-dms_get_band_capabilities_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+load_unlock_required_context_step (GTask *task)
{
- QmiMessageDmsGetBandCapabilitiesOutput *output;
+ MMBroadbandModemQmi *self;
+ LoadUnlockRequiredContext *ctx;
GError *error = NULL;
+ QmiClient *client;
- output = qmi_client_dms_get_band_capabilities_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_dms_get_band_capabilities_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get band capabilities: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- GArray *mm_bands;
- QmiDmsBandCapability qmi_bands = 0;
- QmiDmsLteBandCapability qmi_lte_bands = 0;
-
- qmi_message_dms_get_band_capabilities_output_get_band_capability (
- output,
- &qmi_bands,
- NULL);
- qmi_message_dms_get_band_capabilities_output_get_lte_band_capability (
- output,
- &qmi_lte_bands,
- NULL);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- mm_bands = mm_modem_bands_from_qmi_band_capabilities (qmi_bands, qmi_lte_bands);
+ switch (ctx->step) {
+ case LOAD_UNLOCK_REQUIRED_STEP_FIRST:
+ ctx->step++;
+ /* Fall through */
+
+ case LOAD_UNLOCK_REQUIRED_STEP_CDMA:
+ /* CDMA-only modems don't need this */
+ if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
+ mm_obj_dbg (self, "skipping unlock check in CDMA-only modem...");
+ g_task_return_int (task, MM_MODEM_LOCK_NONE);
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case LOAD_UNLOCK_REQUIRED_STEP_DMS:
+ if (!self->priv->dms_uim_deprecated) {
+ /* Failure to get DMS client is hard really */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+ if (!client) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- if (mm_bands->len == 0) {
- g_array_unref (mm_bands);
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse the list of supported bands");
- } else {
- g_simple_async_result_set_op_res_gpointer (simple,
- mm_bands,
- (GDestroyNotify)g_array_unref);
+ mm_obj_dbg (self, "loading unlock required (DMS)...");
+ qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) dms_uim_get_pin_status_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case LOAD_UNLOCK_REQUIRED_STEP_UIM:
+ /* Failure to get UIM client at this point is hard as well */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+ if (!client) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- }
- if (output)
- qmi_message_dms_get_band_capabilities_output_unref (output);
+ mm_obj_dbg (self, "loading unlock required (UIM)...");
+ qmi_client_uim_get_card_status (QMI_CLIENT_UIM (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) unlock_required_uim_get_card_status_ready,
+ task);
+ return;
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ default:
+ g_assert_not_reached ();
+ }
}
static void
-modem_load_supported_bands (MMIfaceModem *self,
+modem_load_unlock_required (MMIfaceModem *self,
+ gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
+ LoadUnlockRequiredContext *ctx;
+ GTask *task;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
- return;
+ ctx = g_new0 (LoadUnlockRequiredContext, 1);
+ ctx->step = LOAD_UNLOCK_REQUIRED_STEP_FIRST;
+ ctx->last_attempt = last_attempt;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_bands);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
- mm_dbg ("loading band capabilities...");
- qmi_client_dms_get_band_capabilities (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_get_band_capabilities_ready,
- result);
+ load_unlock_required_context_step (task);
}
/*****************************************************************************/
-/* Load current bands (Modem interface) */
+/* Check if unlock retries (Modem interface) */
-static GArray *
-modem_load_current_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+static MMUnlockRetries *
+modem_load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ return MM_UNLOCK_RETRIES (g_task_propagate_pointer (G_TASK (res), error));
}
-#if defined WITH_NEWEST_QMI_COMMANDS
-
static void
-nas_get_rf_band_information_ready (QmiClientNas *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+unlock_retries_uim_get_card_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessageNasGetRfBandInformationOutput *output;
+ MMBroadbandModemQmi *self;
+ QmiMessageUimGetCardStatusOutput *output;
GError *error = NULL;
-
- output = qmi_client_nas_get_rf_band_information_finish (client, res, &error);
- if (!output) {
+ guint pin1_retries = 0;
+ guint puk1_retries = 0;
+ guint pin2_retries = 0;
+ guint puk2_retries = 0;
+ guint pers_retries = 0;
+ MMUnlockRetries *retries;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_uim_get_card_status_finish (client, res, &error);
+ if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error)) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_nas_get_rf_band_information_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get current band information: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- GArray *mm_bands;
- GArray *info_array = NULL;
-
- qmi_message_nas_get_rf_band_information_output_get_list (output, &info_array, NULL);
-
- mm_bands = mm_modem_bands_from_qmi_rf_band_information_array (info_array);
-
- if (mm_bands->len == 0) {
- g_array_unref (mm_bands);
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse the list of current bands");
- } else {
- g_simple_async_result_set_op_res_gpointer (simple,
- mm_bands,
- (GDestroyNotify)g_array_unref);
- }
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- if (output)
- qmi_message_nas_get_rf_band_information_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-
-static void
-load_bands_get_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get system selection preference: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- GArray *mm_bands;
- QmiNasBandPreference band_preference_mask = 0;
- QmiNasLteBandPreference lte_band_preference_mask = 0;
-
- qmi_message_nas_get_system_selection_preference_output_get_band_preference (
- output,
- &band_preference_mask,
- NULL);
-
- qmi_message_nas_get_system_selection_preference_output_get_lte_band_preference (
- output,
- &lte_band_preference_mask,
- NULL);
-
- mm_bands = mm_modem_bands_from_qmi_band_preference (band_preference_mask,
- lte_band_preference_mask);
-
- if (mm_bands->len == 0) {
- g_array_unref (mm_bands);
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse the list of current bands");
- } else {
- gchar *str;
-
- str = qmi_nas_band_preference_build_string_from_mask (band_preference_mask);
- mm_dbg ("Bands reported in system selection preference: '%s'", str);
- g_free (str);
-
- g_simple_async_result_set_op_res_gpointer (simple,
- mm_bands,
- (GDestroyNotify)g_array_unref);
- }
+ if (!mm_qmi_uim_get_card_status_output_parse (self,
+ output,
+ &lock,
+ NULL, &pin1_retries, &puk1_retries,
+ NULL, &pin2_retries, &puk2_retries,
+ &pers_retries,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- if (output)
- qmi_message_nas_get_system_selection_preference_output_unref (output);
+ retries = mm_unlock_retries_new ();
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, pin1_retries);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, puk1_retries);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, pin2_retries);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, puk2_retries);
+ if (lock >= MM_MODEM_LOCK_PH_SP_PIN)
+ mm_unlock_retries_set (retries, lock, pers_retries);
+
+ qmi_message_uim_get_card_status_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, retries, g_object_unref);
+ g_object_unref (task);
}
static void
-modem_load_current_bands (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+uim_load_unlock_retries (MMBroadbandModemQmi *self,
+ GTask *task)
{
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_current_bands);
-
- mm_dbg ("loading current bands...");
+ QmiClient *client;
+ GError *error = NULL;
-#if defined WITH_NEWEST_QMI_COMMANDS
- /* Introduced in NAS 1.19 */
- if (qmi_client_check_version (client, 1, 19)) {
- qmi_client_nas_get_rf_band_information (QMI_CLIENT_NAS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)nas_get_rf_band_information_ready,
- result);
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+ if (!client) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
-#endif
- qmi_client_nas_get_system_selection_preference (
- QMI_CLIENT_NAS (client),
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)load_bands_get_system_selection_preference_ready,
- result);
-}
-
-/*****************************************************************************/
-/* Set current bands (Modem interface) */
-
-static gboolean
-set_current_bands_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ qmi_client_uim_get_card_status (QMI_CLIENT_UIM (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) unlock_retries_uim_get_card_status_ready,
+ task);
}
static void
-bands_set_system_selection_preference_ready (QmiClientNas *client,
+unlock_retries_dms_uim_get_pin_status_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ QmiMessageDmsUimGetPinStatusOutput *output;
GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ MMUnlockRetries *retries;
+ guint8 verify_retries_left;
+ guint8 unblock_retries_left;
+
+ self = g_task_get_source_object (task);
- output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't set system selection preference: ");
- g_simple_async_result_take_error (simple, error);
-
- } else
- /* Good! TODO: do we really need to wait for the indication? */
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
-
- if (output)
- qmi_message_nas_set_system_selection_preference_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
-static void
-set_current_bands (MMIfaceModem *_self,
- GArray *bands_array,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- QmiMessageNasSetSystemSelectionPreferenceInput *input;
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
- QmiNasBandPreference qmi_bands = 0;
- QmiNasLteBandPreference qmi_lte_bands = 0;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_current_bands);
-
- /* Handle ANY separately */
- if (bands_array->len == 1 &&
- g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
- if (!self->priv->supported_bands) {
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot handle 'ANY' if supported bands are unknown");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (!qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) {
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ /* We get InvalidQmiCommand on newer devices which don't like the legacy way */
+ if (g_error_matches (error,
+ QMI_PROTOCOL_ERROR,
+ QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+ g_error_free (error);
+ /* Flag that the command is unsupported, and try with the new way */
+ self->priv->dms_uim_deprecated = TRUE;
+ uim_load_unlock_retries (self, task);
return;
}
+ g_prefix_error (&error, "Couldn't get unlock retries: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- mm_modem_bands_to_qmi_band_preference (self->priv->supported_bands,
- &qmi_bands,
- &qmi_lte_bands);
- } else
- mm_modem_bands_to_qmi_band_preference (bands_array,
- &qmi_bands,
- &qmi_lte_bands);
-
- input = qmi_message_nas_set_system_selection_preference_input_new ();
- qmi_message_nas_set_system_selection_preference_input_set_band_preference (input, qmi_bands, NULL);
- if (mm_iface_modem_is_3gpp_lte (_self))
- qmi_message_nas_set_system_selection_preference_input_set_lte_band_preference (input, qmi_lte_bands, NULL);
+ retries = mm_unlock_retries_new ();
- qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+ if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
+ output,
+ NULL, /* current_status */
+ &verify_retries_left,
+ &unblock_retries_left,
+ NULL)) {
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN, verify_retries_left);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK, unblock_retries_left);
+ }
- qmi_client_nas_set_system_selection_preference (
- QMI_CLIENT_NAS (client),
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)bands_set_system_selection_preference_ready,
- result);
- qmi_message_nas_set_system_selection_preference_input_unref (input);
-}
+ if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
+ output,
+ NULL, /* current_status */
+ &verify_retries_left,
+ &unblock_retries_left,
+ NULL)) {
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PIN2, verify_retries_left);
+ mm_unlock_retries_set (retries, MM_MODEM_LOCK_SIM_PUK2, unblock_retries_left);
+ }
-/*****************************************************************************/
-/* Load supported modes (Modem interface) */
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
-static GArray *
-modem_load_supported_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return g_array_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ g_task_return_pointer (task, retries, g_object_unref);
+ g_object_unref (task);
}
static void
-modem_load_supported_modes (MMIfaceModem *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_load_unlock_retries (MMBroadbandModemQmi *self,
+ GTask *task)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
- GArray *combinations;
- MMModemModeCombination mode;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_modes);
+ QmiClient *client;
- if (!self->priv->supported_radio_interfaces) {
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot load supported modes, no radio interface list");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (!client) {
+ /* Very unlikely that this will ever happen, but anyway, try with
+ * UIM service instead */
+ uim_load_unlock_retries (self, task);
return;
}
- /* Build combinations
- *
- * (1) If current capabilities [GSM/UMTS]:
- * [2G only]
- * [3G only]
- * [2G + 3G]
- * [2G + 3G] 2G preferred
- * [2G + 3G] 3G preferred
- *
- * (2) If current capabilities [CDMA/EVDO]:
- * [2G only]
- * [3G only]
- *
- * (3) If current capabilities [LTE]:
- * [4G only]
- *
- * (4) If current capabilities [GSM/UMTS + CDMA/EVDO]:
- * [2G only]
- * [3G only]
- * [2G + 3G]
- * [2G + 3G] 2G preferred
- * [2G + 3G] 3G preferred
- *
- * (5) If current capabilities [GSM/UMTS + LTE]:
- * [2G + 3G + 4G]
- *
- * (6) If current capabilities [CDMA/EVDO + LTE]:
- * [2G + 3G + 4G]
- *
- * (7) If current capabilities [GSM/UMTS + CDMA/EVDO + LTE]:
- * [2G + 3G + 4G]
- */
+ qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) unlock_retries_dms_uim_get_pin_status_ready,
+ task);
+}
+
+static void
+modem_load_unlock_retries (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self;
+ GTask *task;
- combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5);
+ self = MM_BROADBAND_MODEM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
- /* LTE only, don't allow further mode switching */
- if (mm_iface_modem_is_3gpp_lte_only (_self)) {
- /* 4G only */
- mode.allowed = MM_MODEM_MODE_4G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- }
- /* LTE and others, only allow to have all, no further preference */
- else if (mm_iface_modem_is_3gpp_lte (_self)) {
- /* 2G, 3G and 4G */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- }
- /* Non-LTE modem, include allowed and preferred combinations */
- else {
- MMModemMode mask_all;
- guint i;
- GArray *all;
- GArray *filtered;
-
-
- /* Build all, based on the supported radio interfaces */
- mask_all = MM_MODEM_MODE_NONE;
- for (i = 0; i < self->priv->supported_radio_interfaces->len; i++)
- mask_all |= mm_modem_mode_from_qmi_radio_interface (g_array_index (self->priv->supported_radio_interfaces,
- QmiDmsRadioInterface,
- i));
-
-
- /* 2G only */
- mode.allowed = MM_MODEM_MODE_2G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- /* 3G only */
- mode.allowed = MM_MODEM_MODE_3G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- /* 2G and 3G */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- /* 2G and 3G, 2G preferred */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_2G;
- g_array_append_val (combinations, mode);
- /* 2G and 3G, 3G preferred */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_3G;
- g_array_append_val (combinations, mode);
-
- /* Filter out those unsupported modes */
- mode.allowed = mask_all;
- mode.preferred = MM_MODEM_MODE_NONE;
- all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
- g_array_append_val (all, mode);
- filtered = mm_filter_supported_modes (all, combinations);
- g_array_unref (all);
- g_array_unref (combinations);
- combinations = filtered;
- }
-
- g_simple_async_result_set_op_res_gpointer (result, combinations, (GDestroyNotify) g_array_unref);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ mm_obj_dbg (self, "loading unlock retries...");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_load_unlock_retries (MM_BROADBAND_MODEM_QMI (self), task);
+ else
+ uim_load_unlock_retries (MM_BROADBAND_MODEM_QMI (self), task);
}
/*****************************************************************************/
@@ -2143,11 +1263,15 @@ modem_load_supported_ip_families_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_BEARER_IP_FAMILY_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMBearerIpFamily) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_IP_FAMILY_NONE;
+ }
+ return (MMBearerIpFamily)value;
}
static void
@@ -2155,37 +1279,24 @@ modem_load_supported_ip_families (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_ip_families);
+ GTask *task;
+ task = g_task_new (self, NULL, callback, user_data);
if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self)))
/* CDMA-only: IPv4 */
- g_simple_async_result_set_op_res_gpointer (
- result,
- GUINT_TO_POINTER (MM_BEARER_IP_FAMILY_IPV4),
- NULL);
+ g_task_return_int (task, MM_BEARER_IP_FAMILY_IPV4);
else
/* Assume IPv4 + IPv6 supported */
- g_simple_async_result_set_op_res_gpointer (
- result,
- GUINT_TO_POINTER (MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6 | MM_BEARER_IP_FAMILY_IPV4V6),
- NULL);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_int (task,
+ MM_BEARER_IP_FAMILY_IPV4 |
+ MM_BEARER_IP_FAMILY_IPV6 |
+ MM_BEARER_IP_FAMILY_IPV4V6);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Load signal quality (Modem interface) */
-/* Limit the value betweeen [-113,-51] and scale it to a percentage */
-#define STRENGTH_TO_QUALITY(strength) \
- (guint8)(100 - ((CLAMP (strength, -113, -51) + 51) * 100 / (-113 + 51)))
-
static gboolean
qmi_dbm_valid (gint8 dbm, QmiNasRadioInterface radio_interface)
{
@@ -2196,136 +1307,184 @@ qmi_dbm_valid (gint8 dbm, QmiNasRadioInterface radio_interface)
return (dbm > -125 && dbm < -30);
case QMI_NAS_RADIO_INTERFACE_UMTS:
return (dbm > -125 && dbm < -30);
+ case QMI_NAS_RADIO_INTERFACE_UNKNOWN:
+ case QMI_NAS_RADIO_INTERFACE_NONE:
+ case QMI_NAS_RADIO_INTERFACE_AMPS:
+ case QMI_NAS_RADIO_INTERFACE_GSM:
+ case QMI_NAS_RADIO_INTERFACE_LTE:
+ case QMI_NAS_RADIO_INTERFACE_TD_SCDMA:
+ case QMI_NAS_RADIO_INTERFACE_5GNR:
+ /* no explicit validation */
default:
break;
}
return TRUE;
}
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClient *client;
- GSimpleAsyncResult *result;
-} LoadSignalQualityContext;
-
-static void
-load_signal_quality_context_complete_and_free (LoadSignalQualityContext *ctx)
-{
- 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);
-}
-
static guint
load_signal_quality_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return 0;
+ GError *inner_error = NULL;
+ gssize value;
- return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return 0;
+ }
+ return value;
}
#if defined WITH_NEWEST_QMI_COMMANDS
static gboolean
-signal_info_get_quality (MMBroadbandModemQmi *self,
- QmiMessageNasGetSignalInfoOutput *output,
- gint8 *out_quality)
+common_signal_info_get_quality (MMBroadbandModemQmi *self,
+ gint8 cdma1x_rssi,
+ gint8 evdo_rssi,
+ gint8 gsm_rssi,
+ gint8 wcdma_rssi,
+ gint8 lte_rssi,
+ guint8 *out_quality,
+ MMModemAccessTechnology *out_act)
{
gint8 rssi_max = -125;
- gint8 rssi;
+ QmiNasRadioInterface signal_info_radio_interface = QMI_NAS_RADIO_INTERFACE_UNKNOWN;
g_assert (out_quality != NULL);
+ g_assert (out_act != NULL);
/* We do not report per-technology signal quality, so just get the highest
- * one of the ones reported. */
+ * one of the ones reported. TODO: When several technologies are in use, if
+ * the indication only contains the data of the one which passed a threshold
+ * value, we'll need to have an internal cache of per-technology values, in
+ * order to report always the one with the maximum value. */
- if (qmi_message_nas_get_signal_info_output_get_cdma_signal_strength (output, &rssi, NULL, NULL)) {
- mm_dbg ("RSSI (CDMA): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1X))
- rssi_max = MAX (rssi, rssi_max);
+ if (cdma1x_rssi < 0) {
+ mm_obj_dbg (self, "RSSI (CDMA): %d dBm", cdma1x_rssi);
+ if (qmi_dbm_valid (cdma1x_rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1X)) {
+ rssi_max = MAX (cdma1x_rssi, rssi_max);
+ signal_info_radio_interface = QMI_NAS_RADIO_INTERFACE_CDMA_1X;
+ }
}
- if (qmi_message_nas_get_signal_info_output_get_hdr_signal_strength (output, &rssi, NULL, NULL, NULL, NULL)) {
- mm_dbg ("RSSI (HDR): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO))
- rssi_max = MAX (rssi, rssi_max);
+ if (evdo_rssi < 0) {
+ mm_obj_dbg (self, "RSSI (HDR): %d dBm", evdo_rssi);
+ if (qmi_dbm_valid (evdo_rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO)) {
+ rssi_max = MAX (evdo_rssi, rssi_max);
+ signal_info_radio_interface = QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO;
+ }
}
- if (qmi_message_nas_get_signal_info_output_get_gsm_signal_strength (output, &rssi, NULL)) {
- mm_dbg ("RSSI (GSM): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_GSM))
- rssi_max = MAX (rssi, rssi_max);
+ if (gsm_rssi < 0) {
+ mm_obj_dbg (self, "RSSI (GSM): %d dBm", gsm_rssi);
+ if (qmi_dbm_valid (gsm_rssi, QMI_NAS_RADIO_INTERFACE_GSM)) {
+ rssi_max = MAX (gsm_rssi, rssi_max);
+ signal_info_radio_interface = QMI_NAS_RADIO_INTERFACE_GSM;
+ }
}
- if (qmi_message_nas_get_signal_info_output_get_wcdma_signal_strength (output, &rssi, NULL, NULL)) {
- mm_dbg ("RSSI (WCDMA): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_UMTS))
- rssi_max = MAX (rssi, rssi_max);
+ if (wcdma_rssi < 0) {
+ mm_obj_dbg (self, "RSSI (WCDMA): %d dBm", wcdma_rssi);
+ if (qmi_dbm_valid (wcdma_rssi, QMI_NAS_RADIO_INTERFACE_UMTS)) {
+ rssi_max = MAX (wcdma_rssi, rssi_max);
+ signal_info_radio_interface = QMI_NAS_RADIO_INTERFACE_UMTS;
+ }
}
- if (qmi_message_nas_get_signal_info_output_get_lte_signal_strength (output, &rssi, NULL, NULL, NULL, NULL)) {
- mm_dbg ("RSSI (LTE): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_LTE))
- rssi_max = MAX (rssi, rssi_max);
+ if (lte_rssi < 0) {
+ mm_obj_dbg (self, "RSSI (LTE): %d dBm", lte_rssi);
+ if (qmi_dbm_valid (lte_rssi, QMI_NAS_RADIO_INTERFACE_LTE)) {
+ rssi_max = MAX (lte_rssi, rssi_max);
+ signal_info_radio_interface = QMI_NAS_RADIO_INTERFACE_LTE;
+ }
}
- /* This RSSI comes as negative dBms */
- *out_quality = STRENGTH_TO_QUALITY (rssi_max);
+ if (rssi_max < 0 && rssi_max > -125) {
+ /* This RSSI comes as negative dBms */
+ *out_quality = MM_RSSI_TO_QUALITY (rssi_max);
+ *out_act = mm_modem_access_technology_from_qmi_radio_interface (signal_info_radio_interface);
+
+ mm_obj_dbg (self, "RSSI: %d dBm --> %u%%", rssi_max, *out_quality);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+signal_info_get_quality (MMBroadbandModemQmi *self,
+ QmiMessageNasGetSignalInfoOutput *output,
+ guint8 *out_quality,
+ MMModemAccessTechnology *out_act)
+{
+ gint8 cdma1x_rssi = 0;
+ gint8 evdo_rssi = 0;
+ gint8 gsm_rssi = 0;
+ gint8 wcdma_rssi = 0;
+ gint8 lte_rssi = 0;
- mm_dbg ("RSSI: %d dBm --> %u%%", rssi_max, *out_quality);
+ qmi_message_nas_get_signal_info_output_get_cdma_signal_strength (output, &cdma1x_rssi, NULL, NULL);
+ qmi_message_nas_get_signal_info_output_get_hdr_signal_strength (output, &evdo_rssi, NULL, NULL, NULL, NULL);
+ qmi_message_nas_get_signal_info_output_get_gsm_signal_strength (output, &gsm_rssi, NULL);
+ qmi_message_nas_get_signal_info_output_get_wcdma_signal_strength (output, &wcdma_rssi, NULL, NULL);
+ qmi_message_nas_get_signal_info_output_get_lte_signal_strength (output, &lte_rssi, NULL, NULL, NULL, NULL);
- return (rssi_max > -125);
+ return common_signal_info_get_quality (self, cdma1x_rssi, evdo_rssi, gsm_rssi, wcdma_rssi, lte_rssi, out_quality, out_act);
}
static void
get_signal_info_ready (QmiClientNas *client,
GAsyncResult *res,
- LoadSignalQualityContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageNasGetSignalInfoOutput *output;
GError *error = NULL;
- gint8 quality = 0;
+ guint8 quality = 0;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
output = qmi_client_nas_get_signal_info_finish (client, res, &error);
if (!output) {
- g_simple_async_result_take_error (ctx->result, error);
- load_signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_nas_get_signal_info_output_get_result (output, &error)) {
qmi_message_nas_get_signal_info_output_unref (output);
- g_simple_async_result_take_error (ctx->result, error);
- load_signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- if (!signal_info_get_quality (ctx->self, output, &quality)) {
+ self = g_task_get_source_object (task);
+
+ if (!signal_info_get_quality (self, output, &quality, &act)) {
qmi_message_nas_get_signal_info_output_unref (output);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Signal info reported invalid signal strength.");
- load_signal_quality_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Signal info reported invalid signal strength.");
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (quality),
- NULL);
+ /* We update the access technologies directly here when loading signal
+ * quality. It goes a bit out of context, but we can do it nicely */
+ mm_iface_modem_update_access_technologies (
+ MM_IFACE_MODEM (self),
+ act,
+ (MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK | MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK));
+
+ g_task_return_int (task, quality);
+ g_object_unref (task);
qmi_message_nas_get_signal_info_output_unref (output);
- load_signal_quality_context_complete_and_free (ctx);
}
-#endif /* WITH_NEWEST_QMI_COMMANDS */
+#else /* WITH_NEWEST_QMI_COMMANDS */
static gboolean
signal_strength_get_quality_and_access_tech (MMBroadbandModemQmi *self,
@@ -2343,9 +1502,9 @@ signal_strength_get_quality_and_access_tech (MMBroadbandModemQmi *self,
/* The mandatory one is always present */
qmi_message_nas_get_signal_strength_output_get_signal_strength (output, &signal_max, &main_interface, NULL);
- mm_dbg ("Signal strength (%s): %d dBm",
- qmi_nas_radio_interface_get_string (main_interface),
- signal_max);
+ mm_obj_dbg (self, "signal strength (%s): %d dBm",
+ qmi_nas_radio_interface_get_string (main_interface),
+ signal_max);
/* Treat results as invalid if main signal strength is invalid */
if (!qmi_dbm_valid (signal_max, main_interface))
@@ -2362,9 +1521,9 @@ signal_strength_get_quality_and_access_tech (MMBroadbandModemQmi *self,
element = &g_array_index (array, QmiMessageNasGetSignalStrengthOutputStrengthListElement, i);
- mm_dbg ("Signal strength (%s): %d dBm",
- qmi_nas_radio_interface_get_string (element->radio_interface),
- element->strength);
+ mm_obj_dbg (self, "signal strength (%s): %d dBm",
+ qmi_nas_radio_interface_get_string (element->radio_interface),
+ element->strength);
if (qmi_dbm_valid (element->strength, element->radio_interface)) {
signal_max = MAX (element->strength, signal_max);
@@ -2375,10 +1534,10 @@ signal_strength_get_quality_and_access_tech (MMBroadbandModemQmi *self,
if (signal_max < 0) {
/* This signal strength comes as negative dBms */
- *o_quality = STRENGTH_TO_QUALITY (signal_max);
+ *o_quality = MM_RSSI_TO_QUALITY (signal_max);
*o_act = act;
- mm_dbg ("Signal strength: %d dBm --> %u%%", signal_max, *o_quality);
+ mm_obj_dbg (self, "signal strength: %d dBm --> %u%%", signal_max, *o_quality);
}
return (signal_max < 0);
@@ -2387,8 +1546,9 @@ signal_strength_get_quality_and_access_tech (MMBroadbandModemQmi *self,
static void
get_signal_strength_ready (QmiClientNas *client,
GAsyncResult *res,
- LoadSignalQualityContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageNasGetSignalStrengthOutput *output;
GError *error = NULL;
guint8 quality = 0;
@@ -2396,207 +1556,189 @@ get_signal_strength_ready (QmiClientNas *client,
output = qmi_client_nas_get_signal_strength_finish (client, res, &error);
if (!output) {
- g_simple_async_result_take_error (ctx->result, error);
- load_signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_nas_get_signal_strength_output_get_result (output, &error)) {
qmi_message_nas_get_signal_strength_output_unref (output);
- g_simple_async_result_take_error (ctx->result, error);
- load_signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- if (!signal_strength_get_quality_and_access_tech (ctx->self, output, &quality, &act)) {
+ self = g_task_get_source_object (task);
+
+ if (!signal_strength_get_quality_and_access_tech (self, output, &quality, &act)) {
qmi_message_nas_get_signal_strength_output_unref (output);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "GetSignalStrength signal strength invalid.");
- load_signal_quality_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "GetSignalStrength signal strength invalid.");
+ g_object_unref (task);
return;
}
/* We update the access technologies directly here when loading signal
* quality. It goes a bit out of context, but we can do it nicely */
mm_iface_modem_update_access_technologies (
- MM_IFACE_MODEM (ctx->self),
+ MM_IFACE_MODEM (self),
act,
(MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK | MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK));
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (quality),
- NULL);
+ g_task_return_int (task, quality);
+ g_object_unref (task);
qmi_message_nas_get_signal_strength_output_unref (output);
- load_signal_quality_context_complete_and_free (ctx);
}
+#endif /* WITH_NEWEST_QMI_COMMANDS */
+
static void
load_signal_quality (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- LoadSignalQualityContext *ctx;
QmiClient *client = NULL;
+ GTask *task;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- ctx = g_new0 (LoadSignalQualityContext, 1);
- 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,
- load_signal_quality);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("loading signal quality...");
+ mm_obj_dbg (self, "loading signal quality...");
#if defined WITH_NEWEST_QMI_COMMANDS
- /* Signal info introduced in NAS 1.8 */
- if (qmi_client_check_version (ctx->client, 1, 8)) {
- qmi_client_nas_get_signal_info (QMI_CLIENT_NAS (ctx->client),
- NULL,
- 10,
- NULL,
- (GAsyncReadyCallback)get_signal_info_ready,
- ctx);
- return;
- }
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-
- qmi_client_nas_get_signal_strength (QMI_CLIENT_NAS (ctx->client),
+ qmi_client_nas_get_signal_info (QMI_CLIENT_NAS (client),
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_signal_info_ready,
+ task);
+#else
+ qmi_client_nas_get_signal_strength (QMI_CLIENT_NAS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)get_signal_strength_ready,
- ctx);
+ task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
}
/*****************************************************************************/
/* Powering up the modem (Modem interface) */
static gboolean
-modem_power_up_down_off_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_power_up_down_off_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
dms_set_operating_mode_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- QmiMessageDmsSetOperatingModeOutput *output = NULL;
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ QmiDmsOperatingMode mode;
+ GError *error = NULL;
+ g_autoptr(QmiMessageDmsSetOperatingModeOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+ mode = GPOINTER_TO_UINT (g_task_get_task_data (task));
output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
if (!output) {
- if (g_error_matches (error,
- QMI_CORE_ERROR,
- QMI_CORE_ERROR_UNSUPPORTED)) {
- mm_dbg ("Device doesn't support operating mode setting. Ignoring power update.");
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_error_free (error);
- } else {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_prefix_error (&error, "QMI operation failed: ");
+ /* If unsupported, just complete without errors */
+ if (g_error_matches (error, QMI_CORE_ERROR, QMI_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_dbg (self, "device doesn't support operating mode setting: ignoring power update");
+ g_clear_error (&error);
}
} else if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't set operating mode: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ /*
+ * Some new devices, like the Dell DW5770, will return an internal error when
+ * trying to bring the power mode to online.
+ *
+ * Other devices, like some rebranded EM7455 modules, will return an "invalid
+ * transition" instead when trying to bring the power mode to online.
+ *
+ * We can avoid this by sending the magic "DMS Set FCC Auth" message before
+ * retrying. Notify this to upper layers with the special MM_CORE_ERROR_RETRY
+ * error.
+ */
+ if ((mode == QMI_DMS_OPERATING_MODE_ONLINE) &&
+ ((g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INTERNAL) ||
+ g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_TRANSITION)))) {
+ g_clear_error (&error);
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_RETRY, "Invalid transition");
+ }
}
- if (output)
- qmi_message_dms_set_operating_mode_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-common_power_up_down_off (MMIfaceModem *self,
- QmiDmsOperatingMode mode,
- GAsyncReadyCallback callback,
- gpointer user_data)
+common_power_up_down_off (MMIfaceModem *self,
+ QmiDmsOperatingMode mode,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- QmiMessageDmsSetOperatingModeInput *input;
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
- GError *error = NULL;
+ GTask *task;
+ QmiClient *client = NULL;
+ g_autoptr(QmiMessageDmsSetOperatingModeInput) input = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_power_up_down_off);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (mode), NULL);
input = qmi_message_dms_set_operating_mode_input_new ();
- if (!qmi_message_dms_set_operating_mode_input_set_mode (
- input,
- mode,
- &error)) {
- qmi_message_dms_set_operating_mode_input_unref (input);
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
-
- mm_dbg ("Setting device operating mode...");
+ qmi_message_dms_set_operating_mode_input_set_mode (input, mode, NULL);
qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (client),
input,
20,
NULL,
(GAsyncReadyCallback)dms_set_operating_mode_ready,
- result);
- qmi_message_dms_set_operating_mode_input_unref (input);
+ task);
}
static void
-modem_power_off (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_power_off (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- common_power_up_down_off (self,
- QMI_DMS_OPERATING_MODE_OFFLINE,
- callback,
- user_data);
+ common_power_up_down_off (self, QMI_DMS_OPERATING_MODE_OFFLINE, callback, user_data);
}
static void
-modem_power_down (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_power_down (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- common_power_up_down_off (self,
- QMI_DMS_OPERATING_MODE_LOW_POWER,
- callback,
- user_data);
+ common_power_up_down_off (self, QMI_DMS_OPERATING_MODE_LOW_POWER, callback, user_data);
}
static void
-modem_power_up (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_power_up (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- common_power_up_down_off (self,
- QMI_DMS_OPERATING_MODE_ONLINE,
- callback,
- user_data);
+ common_power_up_down_off (self, QMI_DMS_OPERATING_MODE_ONLINE, callback, user_data);
}
/*****************************************************************************/
@@ -2607,16 +1749,21 @@ load_power_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_POWER_STATE_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemPowerState)GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+ }
+ return (MMModemPowerState)value;
}
static void
dms_get_operating_mode_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsGetOperatingModeOutput *output = NULL;
GError *error = NULL;
@@ -2624,10 +1771,10 @@ dms_get_operating_mode_ready (QmiClientDms *client,
output = qmi_client_dms_get_operating_mode_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_get_operating_mode_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get operating mode: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else {
QmiDmsOperatingMode mode = QMI_DMS_OPERATING_MODE_UNKNOWN;
@@ -2635,23 +1782,27 @@ dms_get_operating_mode_ready (QmiClientDms *client,
switch (mode) {
case QMI_DMS_OPERATING_MODE_ONLINE:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_ON), NULL);
+ g_task_return_int (task, MM_MODEM_POWER_STATE_ON);
break;
case QMI_DMS_OPERATING_MODE_LOW_POWER:
case QMI_DMS_OPERATING_MODE_PERSISTENT_LOW_POWER:
case QMI_DMS_OPERATING_MODE_MODE_ONLY_LOW_POWER:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_LOW), NULL);
+ g_task_return_int (task, MM_MODEM_POWER_STATE_LOW);
break;
case QMI_DMS_OPERATING_MODE_OFFLINE:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_OFF), NULL);
+ g_task_return_int (task, MM_MODEM_POWER_STATE_OFF);
break;
+ case QMI_DMS_OPERATING_MODE_SHUTTING_DOWN:
+ case QMI_DMS_OPERATING_MODE_FACTORY_TEST:
+ case QMI_DMS_OPERATING_MODE_RESET:
+ case QMI_DMS_OPERATING_MODE_UNKNOWN:
default:
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled power state: '%s' (%u)",
- qmi_dms_operating_mode_get_string (mode),
- mode);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled power state: '%s' (%u)",
+ qmi_dms_operating_mode_get_string (mode),
+ mode);
break;
}
}
@@ -2659,8 +1810,7 @@ dms_get_operating_mode_ready (QmiClientDms *client,
if (output)
qmi_message_dms_get_operating_mode_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -2668,26 +1818,20 @@ load_power_state (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_power_state);
-
- mm_dbg ("Getting device operating mode...");
+ mm_obj_dbg (self, "getting device operating mode...");
qmi_client_dms_get_operating_mode (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_get_operating_mode_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -2708,658 +1852,321 @@ create_sim (MMIfaceModem *self,
{
/* New QMI SIM */
mm_sim_qmi_new (MM_BASE_MODEM (self),
+ MM_BROADBAND_MODEM_QMI (self)->priv->dms_uim_deprecated,
NULL, /* cancellable */
callback,
user_data);
}
/*****************************************************************************/
-/* Factory reset (Modem interface) */
-
-static gboolean
-modem_factory_reset_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
+/* IMEI loading (3GPP interface) */
-static void
-dms_restore_factory_defaults_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+static gchar *
+modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- QmiMessageDmsRestoreFactoryDefaultsOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_dms_restore_factory_defaults_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_dms_restore_factory_defaults_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't restore factory defaults: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
-
- if (output)
- qmi_message_dms_restore_factory_defaults_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_factory_reset (MMIfaceModem *self,
- const gchar *code,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_load_imei (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- QmiMessageDmsRestoreFactoryDefaultsInput *input;
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
- GError *error = 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_factory_reset);
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
- input = qmi_message_dms_restore_factory_defaults_input_new ();
- if (!qmi_message_dms_restore_factory_defaults_input_set_service_programming_code (
- input,
- code,
- &error)) {
- qmi_message_dms_restore_factory_defaults_input_unref (input);
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("performing a factory reset...");
- qmi_client_dms_restore_factory_defaults (QMI_CLIENT_DMS (client),
- input,
- 10,
- NULL,
- (GAsyncReadyCallback)dms_restore_factory_defaults_ready,
- result);
+ if (self->priv->imei)
+ g_task_return_pointer (task, g_strdup (self->priv->imei), g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Device doesn't report a valid IMEI");
+ g_object_unref (task);
}
/*****************************************************************************/
-/* Load current modes (Modem interface) */
+/* Facility locks status loading (3GPP interface) */
typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *client;
- GSimpleAsyncResult *result;
- gboolean run_get_system_selection_preference;
- gboolean run_get_technology_preference;
-} LoadCurrentModesContext;
+ QmiClient *client;
+ guint current;
+ MMModem3gppFacility facilities;
+ MMModem3gppFacility locks;
+} LoadEnabledFacilityLocksContext;
-typedef struct {
- MMModemMode allowed;
- MMModemMode preferred;
-} LoadCurrentModesResult;
+static void get_next_facility_lock_status_via_dms (GTask *task);
static void
-load_current_modes_context_complete_and_free (LoadCurrentModesContext *ctx)
+load_enabled_facility_locks_context_free (LoadEnabledFacilityLocksContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->client);
- g_object_unref (ctx->self);
g_free (ctx);
}
-static gboolean
-load_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemMode *allowed,
- MMModemMode *preferred,
- GError **error)
+static MMModem3gppFacility
+modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- LoadCurrentModesResult *result;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return FALSE;
+ GError *inner_error = NULL;
+ gssize value;
- result = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *allowed = result->allowed;
- *preferred = result->preferred;
- return TRUE;
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_3GPP_FACILITY_NONE;
+ }
+ return (MMModem3gppFacility)value;
}
-static void load_current_modes_context_step (LoadCurrentModesContext *ctx);
-
static void
-get_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- LoadCurrentModesContext *ctx)
+get_sim_lock_status_via_get_card_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
{
- LoadCurrentModesResult *result = NULL;
- QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
+ MMBroadbandModemQmi *self;
+ LoadEnabledFacilityLocksContext *ctx;
+ QmiMessageUimGetCardStatusOutput *output;
GError *error = NULL;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+ QmiUimPinState pin1_state;
+ QmiUimPinState pin2_state;
- output = qmi_client_nas_get_technology_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_get_technology_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't get technology preference: %s", error->message);
- g_error_free (error);
- } else {
- MMModemMode allowed;
- QmiNasRadioTechnologyPreference preference_mask;
-
- qmi_message_nas_get_technology_preference_output_get_active (
- output,
- &preference_mask,
- NULL, /* duration */
- NULL);
- allowed = mm_modem_mode_from_qmi_radio_technology_preference (preference_mask);
- if (allowed == MM_MODEM_MODE_NONE) {
- gchar *str;
-
- str = qmi_nas_radio_technology_preference_build_string_from_mask (preference_mask);
- mm_dbg ("Unsupported modes reported: '%s'", str);
- g_free (str);
- } else {
- /* We got a valid value from here */
- result = g_new (LoadCurrentModesResult, 1);
- result->allowed = allowed;
- result->preferred = MM_MODEM_MODE_NONE;
- }
- }
-
- if (output)
- qmi_message_nas_get_technology_preference_output_unref (output);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- if (!result) {
- ctx->run_get_technology_preference = FALSE;
- load_current_modes_context_step (ctx);
+ output = qmi_client_uim_get_card_status_finish (client, res, &error);
+ if (!output || !qmi_message_uim_get_card_status_output_get_result (output, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ if (output)
+ qmi_message_uim_get_card_status_output_unref (output);
return;
}
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- result,
- (GDestroyNotify)g_free);
- load_current_modes_context_complete_and_free (ctx);
-}
-
-static void
-current_modes_get_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- LoadCurrentModesContext *ctx)
-{
- LoadCurrentModesResult *result = NULL;
- QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
- QmiNasRatModePreference mode_preference_mask = 0;
-
- output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't get system selection preference: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
- output,
- &mode_preference_mask,
- NULL)) {
- mm_dbg ("Mode preference not reported in system selection preference");
+ if (!mm_qmi_uim_get_card_status_output_parse (self,
+ output,
+ &lock,
+ &pin1_state, NULL, NULL, &pin2_state, NULL, NULL, NULL,
+ &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
} else {
- MMModemMode allowed;
+ ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_SIM);
+ ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_FIXED_DIALING);
- allowed = mm_modem_mode_from_qmi_rat_mode_preference (mode_preference_mask);
- if (allowed == MM_MODEM_MODE_NONE) {
- gchar *str;
-
- str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference_mask);
- mm_dbg ("Unsupported modes reported: '%s'", str);
- g_free (str);
- } else {
- QmiNasGsmWcdmaAcquisitionOrderPreference gsm_or_wcdma;
-
- /* We got a valid value from here */
- result = g_new (LoadCurrentModesResult, 1);
- result->allowed = allowed;
- result->preferred = MM_MODEM_MODE_NONE;
-
- if ((mode_preference_mask & QMI_NAS_RAT_MODE_PREFERENCE_GSM) &&
- (mode_preference_mask & QMI_NAS_RAT_MODE_PREFERENCE_UMTS) &&
- qmi_message_nas_get_system_selection_preference_output_get_gsm_wcdma_acquisition_order_preference (
- output,
- &gsm_or_wcdma,
- NULL)) {
- result->preferred = mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (gsm_or_wcdma);
- }
+ if (pin1_state == QMI_UIM_PIN_STATE_ENABLED_VERIFIED ||
+ pin1_state == QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED ||
+ pin1_state == QMI_UIM_PIN_STATE_BLOCKED) {
+ ctx->locks |= (MM_MODEM_3GPP_FACILITY_SIM);
+ }
+ if (pin2_state == QMI_UIM_PIN_STATE_ENABLED_VERIFIED ||
+ pin2_state == QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED ||
+ pin2_state == QMI_UIM_PIN_STATE_BLOCKED) {
+ ctx->locks |= (MM_MODEM_3GPP_FACILITY_FIXED_DIALING);
}
- }
-
- if (output)
- qmi_message_nas_get_system_selection_preference_output_unref (output);
- if (!result) {
- /* Try with the deprecated command */
- ctx->run_get_system_selection_preference = FALSE;
- load_current_modes_context_step (ctx);
- return;
+ g_task_return_int (task, ctx->locks);
}
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- result,
- (GDestroyNotify)g_free);
- load_current_modes_context_complete_and_free (ctx);
+ qmi_message_uim_get_card_status_output_unref (output);
+ g_object_unref (task);
}
static void
-load_current_modes_context_step (LoadCurrentModesContext *ctx)
+get_pin_lock_status_via_get_configuration_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
{
- if (ctx->run_get_system_selection_preference) {
- qmi_client_nas_get_system_selection_preference (
- ctx->client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)current_modes_get_system_selection_preference_ready,
- ctx);
- return;
- }
+ MMModem3gppFacility lock = MM_MODEM_3GPP_FACILITY_NONE;
+ QmiMessageUimGetConfigurationOutput *output;
+ LoadEnabledFacilityLocksContext *ctx;
+ MMBroadbandModemQmi *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- if (ctx->run_get_technology_preference) {
- qmi_client_nas_get_technology_preference (
- ctx->client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)get_technology_preference_ready,
- ctx);
+ output = qmi_client_uim_get_configuration_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_uim_get_configuration_output_get_result (output, &error)) {
+ g_prefix_error (&error, "QMI message Get Configuration failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ if (output)
+ qmi_message_uim_get_configuration_output_unref (output);
return;
}
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Loading current modes is not supported by this device");
- load_current_modes_context_complete_and_free (ctx);
-}
-
-static void
-load_current_modes (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- LoadCurrentModesContext *ctx;
- QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_qmi_uim_get_configuration_output_parse (self,
+ output,
+ &lock,
+ &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_uim_get_configuration_output_unref (output);
return;
+ }
- ctx = g_new0 (LoadCurrentModesContext, 1);
- 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,
- load_current_modes);
-
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_get_system_selection_preference = qmi_client_check_version (client, 1, 1);
-
- /* Technology preference introduced in NAS 1.0, so always available */
- ctx->run_get_technology_preference = TRUE;
+ ctx->locks = lock;
+ qmi_message_uim_get_configuration_output_unref (output);
- load_current_modes_context_step (ctx);
+ mm_obj_dbg (self, "Getting UIM card status to read pin lock state...");
+ qmi_client_uim_get_card_status (QMI_CLIENT_UIM (ctx->client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) get_sim_lock_status_via_get_card_status_ready,
+ task);
}
-/*****************************************************************************/
-/* Set allowed modes (Modem interface) */
-
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *client;
- GSimpleAsyncResult *result;
- MMModemMode allowed;
- MMModemMode preferred;
- gboolean run_set_system_selection_preference;
- gboolean run_set_technology_preference;
-} SetCurrentModesContext;
-
static void
-set_current_modes_context_complete_and_free (SetCurrentModesContext *ctx)
+get_facility_lock_status_via_uim (GTask *task)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->client);
- g_object_unref (ctx->self);
- g_slice_free (SetCurrentModesContext, ctx);
-}
-
-static gboolean
-set_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void set_current_modes_context_step (SetCurrentModesContext *ctx);
+ QmiMessageUimGetConfigurationInput *input;
+ LoadEnabledFacilityLocksContext *ctx;
+ MMBroadbandModemQmi *self;
-static void
-set_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- SetCurrentModesContext *ctx)
-{
- QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- output = qmi_client_nas_set_technology_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_set_technology_preference_output_get_result (output, &error) &&
- !g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- mm_dbg ("Couldn't set technology preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_technology_preference_output_unref (output);
- } else {
- if (error)
- g_error_free (error);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_modes_context_complete_and_free (ctx);
- qmi_message_nas_set_technology_preference_output_unref (output);
- return;
- }
+ mm_obj_dbg (self, "Getting UIM Get Configuration to read facility lock state...");
+ input = qmi_message_uim_get_configuration_input_new ();
+ qmi_message_uim_get_configuration_input_set_configuration_mask (
+ input,
+ QMI_UIM_CONFIGURATION_PERSONALIZATION_STATUS,
+ NULL);
- ctx->run_set_technology_preference = FALSE;
- set_current_modes_context_step (ctx);
+ qmi_client_uim_get_configuration (QMI_CLIENT_UIM (ctx->client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)get_pin_lock_status_via_get_configuration_ready,
+ task);
+ qmi_message_uim_get_configuration_input_unref (input);
}
static void
-allowed_modes_set_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- SetCurrentModesContext *ctx)
+get_sim_lock_status_via_pin_status_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ MMBroadbandModemQmi *self;
+ LoadEnabledFacilityLocksContext *ctx;
+ QmiMessageDmsUimGetPinStatusOutput *output;
+ QmiDmsUimPinStatus current_status;
GError *error = NULL;
+ gboolean pin1_enabled;
+ gboolean pin2_enabled;
- output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't set system selection preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- } else {
- /* Good! TODO: do we really need to wait for the indication? */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_modes_context_complete_and_free (ctx);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- return;
- }
-
- /* Try with the deprecated command */
- ctx->run_set_system_selection_preference = FALSE;
- set_current_modes_context_step (ctx);
-}
-
-static void
-set_current_modes_context_step (SetCurrentModesContext *ctx)
-{
- if (ctx->run_set_system_selection_preference) {
- QmiMessageNasSetSystemSelectionPreferenceInput *input;
- QmiNasRatModePreference pref;
-
- pref = mm_modem_mode_to_qmi_rat_mode_preference (ctx->allowed,
- mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self)),
- mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)));
- if (!pref) {
- gchar *str;
-
- str = mm_modem_mode_build_string_from_mask (ctx->allowed);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled allowed mode setting: '%s'",
- str);
- g_free (str);
- set_current_modes_context_complete_and_free (ctx);
- return;
- }
-
- input = qmi_message_nas_set_system_selection_preference_input_new ();
- qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
-
- /* Only set acquisition order preference if both 2G and 3G given as allowed */
- if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) &&
- ctx->allowed & MM_MODEM_MODE_2G &&
- ctx->allowed & MM_MODEM_MODE_3G) {
- QmiNasGsmWcdmaAcquisitionOrderPreference order;
-
- order = mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (ctx->preferred);
- qmi_message_nas_set_system_selection_preference_input_set_gsm_wcdma_acquisition_order_preference (input, order, NULL);
- }
-
- qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- qmi_client_nas_set_system_selection_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)allowed_modes_set_system_selection_preference_ready,
- ctx);
- qmi_message_nas_set_system_selection_preference_input_unref (input);
+ output = qmi_client_dms_uim_get_pin_status_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_dms_uim_get_pin_status_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ if (output)
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
return;
}
- if (ctx->run_set_technology_preference) {
- QmiMessageNasSetTechnologyPreferenceInput *input;
- QmiNasRadioTechnologyPreference pref;
-
- if (ctx->preferred != MM_MODEM_MODE_NONE) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot set specific preferred mode");
- set_current_modes_context_complete_and_free (ctx);
- return;
- }
-
- pref = mm_modem_mode_to_qmi_radio_technology_preference (ctx->allowed,
- mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self)));
- if (!pref) {
- gchar *str;
-
- str = mm_modem_mode_build_string_from_mask (ctx->allowed);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled allowed mode setting: '%s'",
- str);
- g_free (str);
- set_current_modes_context_complete_and_free (ctx);
- return;
- }
-
- input = qmi_message_nas_set_technology_preference_input_new ();
- qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_technology_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)set_technology_preference_ready,
- ctx);
- qmi_message_nas_set_technology_preference_input_unref (input);
+ if (qmi_message_dms_uim_get_pin_status_output_get_pin1_status (
+ output,
+ &current_status,
+ NULL, /* verify_retries_left */
+ NULL, /* unblock_retries_left */
+ &error)) {
+ pin1_enabled = mm_pin_enabled_from_qmi_uim_pin_status (current_status);
+ mm_obj_dbg (self, "PIN1 is reported %s", (pin1_enabled ? "enabled" : "disabled"));
+ } else {
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting allowed modes is not supported by this device");
- set_current_modes_context_complete_and_free (ctx);
-}
-
-static void
-set_current_modes (MMIfaceModem *self,
- MMModemMode allowed,
- MMModemMode preferred,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SetCurrentModesContext *ctx;
- QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- ctx = g_slice_new0 (SetCurrentModesContext);
- 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,
- set_current_modes);
-
- if (allowed == MM_MODEM_MODE_ANY && ctx->preferred == MM_MODEM_MODE_NONE) {
- ctx->allowed = MM_MODEM_MODE_NONE;
- if (mm_iface_modem_is_2g (self))
- ctx->allowed |= MM_MODEM_MODE_2G;
- if (mm_iface_modem_is_3g (self))
- ctx->allowed |= MM_MODEM_MODE_3G;
- if (mm_iface_modem_is_4g (self))
- ctx->allowed |= MM_MODEM_MODE_4G;
- ctx->preferred = MM_MODEM_MODE_NONE;
+ if (qmi_message_dms_uim_get_pin_status_output_get_pin2_status (
+ output,
+ &current_status,
+ NULL, /* verify_retries_left */
+ NULL, /* unblock_retries_left */
+ &error)) {
+ pin2_enabled = mm_pin_enabled_from_qmi_uim_pin_status (current_status);
+ mm_obj_dbg (self, "PIN2 is reported %s", (pin2_enabled ? "enabled" : "disabled"));
} else {
- ctx->allowed = allowed;
- ctx->preferred = preferred;
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_set_system_selection_preference = qmi_client_check_version (client, 1, 1);
+ qmi_message_dms_uim_get_pin_status_output_unref (output);
- /* Technology preference introduced in NAS 1.0, so always available */
- ctx->run_set_technology_preference = TRUE;
-
- set_current_modes_context_step (ctx);
-}
-
-/*****************************************************************************/
-/* IMEI loading (3GPP interface) */
-
-static gchar *
-modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
-{
- gchar *imei;
+ if (pin1_enabled)
+ ctx->locks |= (MM_MODEM_3GPP_FACILITY_SIM);
+ else
+ ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_SIM);
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
+ if (pin2_enabled)
+ ctx->locks |= (MM_MODEM_3GPP_FACILITY_FIXED_DIALING);
+ else
+ ctx->locks &= ~(MM_MODEM_3GPP_FACILITY_FIXED_DIALING);
- imei = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded IMEI: %s", imei);
- return imei;
+ /* No more facilities to query, all done */
+ g_task_return_int (task, ctx->locks);
+ g_object_unref (task);
}
+/* the SIM lock cannot be queried with the qmi_get_ck_status function,
+ * therefore using the PIN status */
static void
-modem_3gpp_load_imei (MMIfaceModem3gpp *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+get_sim_lock_status_via_pin_status (GTask *task)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_imei);
-
- if (self->priv->imei)
- g_simple_async_result_set_op_res_gpointer (result,
- self->priv->imei,
- NULL);
- else
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Device doesn't report a valid IMEI");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
-}
-
-/*****************************************************************************/
-/* Facility locks status loading (3GPP interface) */
-
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- QmiClient *client;
- guint current;
- MMModem3gppFacility facilities;
- MMModem3gppFacility locks;
-} LoadEnabledFacilityLocksContext;
-
-static void get_next_facility_lock_status (LoadEnabledFacilityLocksContext *ctx);
+ MMBroadbandModemQmi *self;
+ LoadEnabledFacilityLocksContext *ctx;
-static void
-load_enabled_facility_locks_context_complete_and_free (LoadEnabledFacilityLocksContext *ctx)
-{
- 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);
-}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
-static MMModem3gppFacility
-modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_3GPP_FACILITY_NONE;
-
- return ((MMModem3gppFacility) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ mm_obj_dbg (self, "retrieving PIN status to check for enabled PIN");
+ /* if the SIM is locked or not can only be queried by locking at
+ * the PIN status */
+ qmi_client_dms_uim_get_pin_status (QMI_CLIENT_DMS (ctx->client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)get_sim_lock_status_via_pin_status_ready,
+ task);
}
static void
dms_uim_get_ck_status_ready (QmiClientDms *client,
GAsyncResult *res,
- LoadEnabledFacilityLocksContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ LoadEnabledFacilityLocksContext *ctx;
gchar *facility_str;
QmiMessageDmsUimGetCkStatusOutput *output;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
facility_str = mm_modem_3gpp_facility_build_string_from_mask (1 << ctx->current);
output = qmi_client_dms_uim_get_ck_status_finish (client, res, NULL);
if (!output ||
!qmi_message_dms_uim_get_ck_status_output_get_result (output, NULL)) {
/* On errors, we'll just assume disabled */
- mm_dbg ("Couldn't query facility '%s' status, assuming disabled", facility_str);
+ mm_obj_dbg (self, "couldn't query facility '%s' status, assuming disabled", facility_str);
ctx->locks &= ~(1 << ctx->current);
} else {
QmiDmsUimFacilityState state;
@@ -3373,7 +2180,7 @@ dms_uim_get_ck_status_ready (QmiClientDms *client,
&unblock_retries_left,
NULL);
- mm_dbg ("Facility '%s' is: '%s'",
+ mm_obj_dbg (self, "facility '%s' is: '%s'",
facility_str,
qmi_dms_uim_facility_state_get_string (state));
@@ -3389,14 +2196,17 @@ dms_uim_get_ck_status_ready (QmiClientDms *client,
/* And go on with the next one */
ctx->current++;
- get_next_facility_lock_status (ctx);
+ get_next_facility_lock_status_via_dms (task);
}
static void
-get_next_facility_lock_status (LoadEnabledFacilityLocksContext *ctx)
+get_next_facility_lock_status_via_dms (GTask *task)
{
+ LoadEnabledFacilityLocksContext *ctx;
guint i;
+ ctx = g_task_get_task_data (task);
+
for (i = ctx->current; i < sizeof (MMModem3gppFacility) * 8; i++) {
guint32 facility = 1 << i;
@@ -3418,17 +2228,13 @@ get_next_facility_lock_status (LoadEnabledFacilityLocksContext *ctx)
5,
NULL,
(GAsyncReadyCallback)dms_uim_get_ck_status_ready,
- ctx);
+ task);
qmi_message_dms_uim_get_ck_status_input_unref (input);
return;
}
}
- /* No more facilities to query, all done */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (ctx->locks),
- NULL);
- load_enabled_facility_locks_context_complete_and_free (ctx);
+ get_sim_lock_status_via_pin_status (task);
}
static void
@@ -3437,20 +2243,24 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
gpointer user_data)
{
LoadEnabledFacilityLocksContext *ctx;
+ GTask *task;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
- return;
+ if (!MM_BROADBAND_MODEM_QMI (self)->priv->dms_uim_deprecated) {
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+ } else {
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM, &client,
+ callback, user_data))
+ return;
+ }
ctx = g_new (LoadEnabledFacilityLocksContext, 1);
- 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,
- modem_3gpp_load_enabled_facility_locks);
+
/* Set initial list of facilities to query */
ctx->facilities = (MM_MODEM_3GPP_FACILITY_PH_SIM |
MM_MODEM_3GPP_FACILITY_NET_PERS |
@@ -3460,7 +2270,175 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
ctx->locks = MM_MODEM_3GPP_FACILITY_NONE;
ctx->current = 0;
- get_next_facility_lock_status (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_enabled_facility_locks_context_free);
+
+ /* DMS uses get_ck_status and get_pin_status to probe facilities
+ * UIM uses get_card_status and get_configuration
+ */
+ if (!MM_BROADBAND_MODEM_QMI (self)->priv->dms_uim_deprecated)
+ get_next_facility_lock_status_via_dms (task);
+ else
+ get_facility_lock_status_via_uim (task);
+}
+
+/*****************************************************************************/
+/* Facility locks disabling (3GPP interface) */
+
+# define DISABLE_FACILITY_LOCK_CHECK_TIMEOUT_MS 100
+# define DISABLE_FACILITY_LOCK_CHECK_ATTEMPTS 10
+
+typedef struct _DisableFacilityLockContext DisableFacilityLockContext;
+struct _DisableFacilityLockContext {
+ MMModem3gppFacility facility;
+ guint remaining_attempts;
+ guint8 slot;
+};
+
+static gboolean disable_facility_lock_check (GTask *task);
+
+static gboolean
+modem_3gpp_disable_facility_lock_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_facility_lock_check_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisableFacilityLockContext *ctx;
+ MMModem3gppFacility facilities;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ facilities = modem_3gpp_load_enabled_facility_locks_finish (self, res, &error);
+ if (error) {
+ g_prefix_error (&error, "Failed to check the facility locks: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Check if the facility lock is still enabled */
+ if (facilities & ctx->facility) {
+ /* Wait again and retry */
+ g_timeout_add (DISABLE_FACILITY_LOCK_CHECK_TIMEOUT_MS,
+ (GSourceFunc)disable_facility_lock_check,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static gboolean
+disable_facility_lock_check (GTask *task)
+{
+ DisableFacilityLockContext *ctx;
+ MMIfaceModem3gpp *self;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ if (ctx->remaining_attempts) {
+ ctx->remaining_attempts--;
+ modem_3gpp_load_enabled_facility_locks (self,
+ (GAsyncReadyCallback)disable_facility_lock_check_ready,
+ task);
+ } else {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to disable the facility lock.");
+ g_object_unref (task);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+disable_facility_lock_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageUimDepersonalizationOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_uim_depersonalization_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_uim_depersonalization_output_get_result (output, &error)) {
+ g_prefix_error (&error, "QMI message Depersonalization failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ } else {
+ /* Wait defined time for lock change to propagate to Card Status */
+ g_timeout_add (DISABLE_FACILITY_LOCK_CHECK_TIMEOUT_MS,
+ (GSourceFunc)disable_facility_lock_check,
+ task);
+ }
+
+ if (output)
+ qmi_message_uim_depersonalization_output_unref (output);
+}
+
+static void
+modem_3gpp_disable_facility_lock (MMIfaceModem3gpp *self,
+ MMModem3gppFacility facility,
+ guint8 slot,
+ const gchar *key,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiUimCardApplicationPersonalizationFeature feature;
+ QmiMessageUimDepersonalizationInput *input;
+ DisableFacilityLockContext *ctx;
+ QmiClient *client = NULL;
+ GTask *task;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Choose facility to disable */
+ feature = qmi_personalization_feature_from_mm_modem_3gpp_facility (facility);
+ if (feature == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_UNKNOWN) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Not supported type of facility lock.");
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "Trying to disable %s lock on slot %d using key: %s",
+ qmi_uim_card_application_personalization_feature_get_string (feature),
+ slot, key);
+
+ input = qmi_message_uim_depersonalization_input_new ();
+ qmi_message_uim_depersonalization_input_set_info (input,
+ feature,
+ QMI_UIM_DEPERSONALIZATION_OPERATION_DEACTIVATE,
+ key,
+ NULL);
+ qmi_message_uim_depersonalization_input_set_slot (input, slot, NULL);
+
+ ctx = g_new0 (DisableFacilityLockContext, 1);
+ ctx->facility = facility;
+ ctx->slot = slot;
+ ctx->remaining_attempts = DISABLE_FACILITY_LOCK_CHECK_ATTEMPTS;
+ g_task_set_task_data (task, ctx, g_free);
+
+ qmi_client_uim_depersonalization (QMI_CLIENT_UIM (client),
+ input,
+ 30,
+ NULL,
+ (GAsyncReadyCallback) disable_facility_lock_ready,
+ task);
+ qmi_message_uim_depersonalization_input_unref (input);
}
/*****************************************************************************/
@@ -3471,11 +2449,7 @@ modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- /* We return the GList as it is */
- return (GList *) g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static MMModem3gppNetworkAvailability
@@ -3547,7 +2521,7 @@ get_3gpp_access_technology (GArray *array,
static void
nas_network_scan_ready (QmiClientNas *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageNasNetworkScanOutput *output = NULL;
GError *error = NULL;
@@ -3555,10 +2529,10 @@ nas_network_scan_ready (QmiClientNas *client,
output = qmi_client_nas_network_scan_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_nas_network_scan_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't scan networks: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else {
GList *scan_result = NULL;
GArray *info_array = NULL;
@@ -3592,16 +2566,13 @@ nas_network_scan_ready (QmiClientNas *client,
g_free (rat_array_used_flags);
}
- /* We *require* a callback in the async method, as we're not setting a
- * GDestroyNotify callback */
- g_simple_async_result_set_op_res_gpointer (simple, scan_result, NULL);
+ g_task_return_pointer (task, scan_result, (GDestroyNotify)mm_3gpp_network_info_list_free);
}
if (output)
qmi_message_nas_network_scan_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -3609,7 +2580,6 @@ modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
/* We will pass the GList in the GSimpleAsyncResult, so we must
@@ -3617,234 +2587,209 @@ modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
* passed to the caller and deallocated afterwards */
g_assert (callback != NULL);
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_scan_networks);
-
- mm_dbg ("Scanning networks...");
+ mm_obj_dbg (self, "scanning networks...");
qmi_client_nas_network_scan (QMI_CLIENT_NAS (client),
NULL,
- 100,
+ 300,
NULL,
(GAsyncReadyCallback)nas_network_scan_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Load operator name (3GPP interface) */
static gchar *
-modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *_self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-
- if (self->priv->current_operator_description)
- return g_strdup (self->priv->current_operator_description);
-
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Current operator description is still unknown");
- return NULL;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
-
- /* Just finish the async operation */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_operator_name);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
-}
-
-/*****************************************************************************/
-/* Load operator code (3GPP interface) */
-
-static gchar *
-modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *_self,
- GAsyncResult *res,
- GError **error)
+get_plmn_name_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ MMBroadbandModemQmi *self;
+ GError *error = NULL;
+ QmiNasNetworkDescriptionEncoding plmn_name_service_provider_name_encoding;
+ QmiNasNetworkDescriptionEncoding plmn_name_short_name_encoding;
+ QmiNasNetworkDescriptionEncoding plmn_name_long_name_encoding;
+ GArray *plmn_name_service_provider_name;
+ GArray *plmn_name_short_name;
+ GArray *plmn_name_long_name;
+ g_autoptr(QmiMessageNasGetPlmnNameOutput) output = NULL;
- if (self->priv->current_operator_id)
- return g_strdup (self->priv->current_operator_id);
+ self = g_task_get_source_object (task);
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Current operator MCC/MNC is still unknown");
- return NULL;
-}
+ output = qmi_client_nas_get_plmn_name_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
-static void
-modem_3gpp_load_operator_code (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *result;
+ if (!qmi_message_nas_get_plmn_name_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- /* Just finish the async operation */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_operator_code);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
-}
+ if (qmi_message_nas_get_plmn_name_output_get_3gpp_eons_plmn_name (
+ output,
+ &plmn_name_service_provider_name_encoding,
+ &plmn_name_service_provider_name,
+ &plmn_name_short_name_encoding,
+ NULL,
+ NULL,
+ &plmn_name_short_name,
+ &plmn_name_long_name_encoding,
+ NULL,
+ NULL,
+ &plmn_name_long_name,
+ NULL)) {
+ g_autofree gchar *long_name = NULL;
+ g_autofree gchar *short_name = NULL;
+ g_autofree gchar *service_name = NULL;
+
+ long_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_long_name_encoding, plmn_name_long_name);
+ short_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_short_name_encoding, plmn_name_short_name);
+ service_name = qmi_nas_read_string_from_network_description_encoded_array (plmn_name_service_provider_name_encoding, plmn_name_service_provider_name);
+ mm_obj_dbg (self, "current operator long name: %s", long_name);
+ mm_obj_dbg (self, "current operator short name: %s", short_name);
+ mm_obj_dbg (self, "current operator service name: %s", service_name);
+ if (!self->priv->current_operator_description) {
+ self->priv->current_operator_description = (service_name ? g_steal_pointer (&service_name) :
+ (long_name ? g_steal_pointer (&long_name) :
+ (short_name ? g_steal_pointer (&short_name) :
+ NULL)));
+ }
+ }
-/*****************************************************************************/
-/* Register in network (3GPP interface) */
+ if (!self->priv->current_operator_description)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Current operator description is still unknown and cannot be retrieved from MCC/MNC");
+ else
+ g_task_return_pointer (task, g_strdup (self->priv->current_operator_description), g_free);
-static gboolean
-modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ g_object_unref (task);
}
static void
-initiate_network_register_ready (QmiClientNas *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+modem_3gpp_load_operator_name (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GError *error = NULL;
- QmiMessageNasInitiateNetworkRegisterOutput *output;
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
+ QmiClient *client;
+ guint16 mcc = 0;
+ guint16 mnc = 0;
+ gboolean mnc_pcs_digit = FALSE;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(QmiMessageNasGetPlmnNameInput) input = NULL;
- output = qmi_client_nas_initiate_network_register_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- } else if (!qmi_message_nas_initiate_network_register_output_get_result (output, &error)) {
- /* NOFX is not an error, they actually play pretty well */
- if (g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- g_error_free (error);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- } else {
- g_prefix_error (&error, "Couldn't initiate network register: ");
- g_simple_async_result_take_error (simple, error);
- }
- } else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ task = g_task_new (self, NULL, callback, user_data);
- if (output)
- qmi_message_nas_initiate_network_register_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
+ if (self->priv->current_operator_description) {
+ g_task_return_pointer (task, g_strdup (self->priv->current_operator_description), g_free);
+ g_object_unref (task);
+ return;
+ }
-static void
-modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- guint16 mcc = 0;
- guint16 mnc = 0;
- QmiClient *client = NULL;
- QmiMessageNasInitiateNetworkRegisterInput *input;
- GError *error = NULL;
+ /* Check if operator id is set */
+ if (!self->priv->current_operator_id) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Current operator id is still unknown");
+ g_object_unref (task);
+ return;
+ }
/* Parse input MCC/MNC */
- if (operator_id && !mm_3gpp_parse_operator_id (operator_id, &mcc, &mnc, &error)) {
- g_assert (error != NULL);
- g_simple_async_report_take_gerror_in_idle (G_OBJECT (self),
- callback,
- user_data,
- error);
+ if (!mm_3gpp_parse_operator_id (self->priv->current_operator_id, &mcc, &mnc, &mnc_pcs_digit, &error)) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
return;
}
- /* Get NAS client */
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ /* Try to get PLMN name from MCC/MNC */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+ if (!client) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
return;
-
- input = qmi_message_nas_initiate_network_register_input_new ();
-
- if (mcc) {
- /* If the user sent a specific network to use, lock it in. */
- qmi_message_nas_initiate_network_register_input_set_action (
- input,
- QMI_NAS_NETWORK_REGISTER_TYPE_MANUAL,
- NULL);
- qmi_message_nas_initiate_network_register_input_set_manual_registration_info_3gpp (
- input,
- mcc,
- mnc,
- QMI_NAS_RADIO_INTERFACE_UNKNOWN,
- NULL);
- } else {
- /* Otherwise, automatic registration */
- qmi_message_nas_initiate_network_register_input_set_action (
- input,
- QMI_NAS_NETWORK_REGISTER_TYPE_AUTOMATIC,
- NULL);
}
- qmi_client_nas_initiate_network_register (
- QMI_CLIENT_NAS (client),
- input,
- 120,
- cancellable,
- (GAsyncReadyCallback)initiate_network_register_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_register_in_network));
+ input = qmi_message_nas_get_plmn_name_input_new ();
+ qmi_message_nas_get_plmn_name_input_set_plmn (input, mcc, mnc, NULL);
+ if (mnc_pcs_digit && mnc < 100)
+ qmi_message_nas_get_plmn_name_input_set_mnc_pcs_digit_include_status (input, mnc_pcs_digit, NULL);
- qmi_message_nas_initiate_network_register_input_unref (input);
+ qmi_client_nas_get_plmn_name (QMI_CLIENT_NAS (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)get_plmn_name_ready,
+ task);
}
/*****************************************************************************/
-/* Registration checks (3GPP interface) */
+/* Load operator code (3GPP interface) */
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *client;
- GSimpleAsyncResult *result;
-} Run3gppRegistrationChecksContext;
+static gchar *
+modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
static void
-run_3gpp_registration_checks_context_complete_and_free (Run3gppRegistrationChecksContext *ctx)
+modem_3gpp_load_operator_code (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- 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);
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->current_operator_id)
+ g_task_return_pointer (task,
+ g_strdup (self->priv->current_operator_id),
+ g_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Current operator MCC/MNC is still unknown");
+ g_object_unref (task);
}
+/*****************************************************************************/
+/* Registration checks (3GPP interface) */
+
static gboolean
modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
+#if !defined WITH_NEWEST_QMI_COMMANDS
+
static void
common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
QmiMessageNasGetServingSystemOutput *response_output,
@@ -3862,10 +2807,12 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
const gchar *description;
gboolean has_pcs_digit;
guint16 lac;
+ guint16 tac;
guint32 cid;
MMModemAccessTechnology mm_access_technologies;
MMModem3gppRegistrationState mm_cs_registration_state;
MMModem3gppRegistrationState mm_ps_registration_state;
+ gboolean operator_updated = FALSE;
if (response_output)
qmi_message_nas_get_serving_system_output_get_serving_system (
@@ -3893,7 +2840,7 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
else
qmi_indication_nas_serving_system_output_get_data_service_capability (indication_output, &data_service_capabilities, NULL);
- if (data_service_capabilities)
+ if (data_service_capabilities && data_service_capabilities->len > 0)
mm_access_technologies =
mm_modem_access_technologies_from_qmi_data_capability_array (data_service_capabilities);
else
@@ -3902,15 +2849,17 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
/* Only process 3GPP info.
* Seen the case already where 'selected_network' gives UNKNOWN but we still
- * have valid LTE info around. */
+ * have valid LTE/5GNR info around. */
if (selected_network == QMI_NAS_NETWORK_TYPE_3GPP ||
(selected_network == QMI_NAS_NETWORK_TYPE_UNKNOWN &&
(mm_access_technologies & MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK))) {
- mm_dbg ("Processing 3GPP info...");
+ mm_obj_dbg (self, "processing 3GPP info...");
} else {
MMModem3gppRegistrationState reg_state_3gpp;
- mm_dbg ("No 3GPP info given...");
+ mm_obj_dbg (self, "no 3GPP info given...");
+ if (self->priv->current_operator_id || self->priv->current_operator_description)
+ operator_updated = TRUE;
g_free (self->priv->current_operator_id);
self->priv->current_operator_id = NULL;
g_free (self->priv->current_operator_description);
@@ -3923,8 +2872,17 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp);
mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp);
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp);
+ if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), reg_state_3gpp);
+
mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
- mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), 0, 0);
+ mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), 0, 0, 0);
+ /* request to reload operator info explicitly, so that the new
+ * operator name and code is propagated to the DBus interface */
+ if (operator_updated)
+ mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL);
return;
}
@@ -3964,21 +2922,26 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
&mnc,
&description,
NULL))) {
+ gchar *new_operator_id;
+
/* When we don't have information about leading PCS digit, guess best */
- g_free (self->priv->current_operator_id);
if (mnc >= 100)
- self->priv->current_operator_id =
- g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.3" G_GUINT16_FORMAT,
- mcc,
- mnc);
+ new_operator_id = g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.3" G_GUINT16_FORMAT, mcc, mnc);
else
- self->priv->current_operator_id =
- g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.2" G_GUINT16_FORMAT,
- mcc,
- mnc);
+ new_operator_id = g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.2" G_GUINT16_FORMAT, mcc, mnc);
- g_free (self->priv->current_operator_description);
- self->priv->current_operator_description = g_strdup (description);
+ if (!self->priv->current_operator_id || !g_str_equal (self->priv->current_operator_id, new_operator_id)) {
+ operator_updated = TRUE;
+ g_free (self->priv->current_operator_id);
+ self->priv->current_operator_id = new_operator_id;
+ } else
+ g_free (new_operator_id);
+
+ if (!self->priv->current_operator_description || !g_str_equal (self->priv->current_operator_description, description)) {
+ operator_updated = TRUE;
+ g_free (self->priv->current_operator_description);
+ self->priv->current_operator_description = g_strdup (description);
+ }
}
/* If MNC comes with PCS digit, we must make sure the additional
@@ -3998,28 +2961,57 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
&has_pcs_digit,
NULL))) &&
has_pcs_digit) {
- g_free (self->priv->current_operator_id);
- self->priv->current_operator_id =
- g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.3" G_GUINT16_FORMAT,
- mcc,
- mnc);
+ gchar *new_operator_id;
+
+ new_operator_id = g_strdup_printf ("%.3" G_GUINT16_FORMAT "%.3" G_GUINT16_FORMAT, mcc, mnc);
+ if (!self->priv->current_operator_id || !g_str_equal (self->priv->current_operator_id, new_operator_id)) {
+ operator_updated = TRUE;
+ g_free (self->priv->current_operator_id);
+ self->priv->current_operator_id = new_operator_id;
+ } else
+ g_free (new_operator_id);
}
- /* Get 3GPP location LAC and CI */
+ /* Report new registration states. The QMI serving system API reports "CS"
+ * and "PS" registration states, and if the device is in LTE, we'll take the "PS"
+ * one as "EPS". But, if the device is not in LTE, we should also set the "EPS"
+ * state as unknown, so that the "PS" one takes precedence when building
+ * the consolidated registration state (otherwise we may be using some old cached
+ * "EPS" state wrongly). */
+ mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), mm_cs_registration_state);
+ mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), mm_ps_registration_state);
+ if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self),
+ (mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_LTE) ?
+ mm_ps_registration_state : MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN);
+ /* Same thing for "5GS" state */
+ if (mm_iface_modem_is_3gpp_5gnr (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self),
+ (mm_access_technologies & MM_MODEM_ACCESS_TECHNOLOGY_5GNR) ?
+ mm_ps_registration_state : MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN);
+
+
+ /* Get 3GPP location LAC/TAC and CI */
lac = 0;
+ tac = 0;
cid = 0;
if (response_output) {
qmi_message_nas_get_serving_system_output_get_lac_3gpp (response_output, &lac, NULL);
+ qmi_message_nas_get_serving_system_output_get_lte_tac (response_output, &tac, NULL);
qmi_message_nas_get_serving_system_output_get_cid_3gpp (response_output, &cid, NULL);
- } else {
+ } else if (indication_output) {
qmi_indication_nas_serving_system_output_get_lac_3gpp (indication_output, &lac, NULL);
+ qmi_indication_nas_serving_system_output_get_lte_tac (indication_output, &tac, NULL);
qmi_indication_nas_serving_system_output_get_cid_3gpp (indication_output, &cid, NULL);
}
+ /* Only update info in the interface if we get something */
+ if (cid || lac || tac)
+ mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid);
- /* Report new registration states */
- mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), mm_cs_registration_state);
- mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), mm_ps_registration_state);
- mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, cid);
+ /* request to reload operator info explicitly, so that the new
+ * operator name and code is propagated to the DBus interface */
+ if (operator_updated)
+ mm_iface_modem_3gpp_reload_current_registration_info (MM_IFACE_MODEM_3GPP (self), NULL, NULL);
/* Note: don't update access technologies with the ones retrieved here; they
* are not really the 'current' access technologies */
@@ -4028,35 +3020,38 @@ common_process_serving_system_3gpp (MMBroadbandModemQmi *self,
static void
get_serving_system_3gpp_ready (QmiClientNas *client,
GAsyncResult *res,
- Run3gppRegistrationChecksContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageNasGetServingSystemOutput *output;
GError *error = NULL;
output = qmi_client_nas_get_serving_system_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- run_3gpp_registration_checks_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_nas_get_serving_system_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get serving system: ");
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
qmi_message_nas_get_serving_system_output_unref (output);
- run_3gpp_registration_checks_context_complete_and_free (ctx);
return;
}
- common_process_serving_system_3gpp (ctx->self, output, NULL);
+ self = g_task_get_source_object (task);
+
+ common_process_serving_system_3gpp (self, output, NULL);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
qmi_message_nas_get_serving_system_output_unref (output);
- run_3gpp_registration_checks_context_complete_and_free (ctx);
}
-#if defined WITH_NEWEST_QMI_COMMANDS
+#else /* WITH_NEWEST_QMI_COMMANDS */
static gboolean
process_common_info (QmiNasServiceStatus service_status,
@@ -4068,6 +3063,8 @@ process_common_info (QmiNasServiceStatus service_status,
gboolean forbidden,
gboolean lac_valid,
guint16 lac,
+ gboolean tac_valid,
+ guint16 tac,
gboolean cid_valid,
guint32 cid,
gboolean network_id_valid,
@@ -4076,6 +3073,7 @@ process_common_info (QmiNasServiceStatus service_status,
MMModem3gppRegistrationState *mm_cs_registration_state,
MMModem3gppRegistrationState *mm_ps_registration_state,
guint16 *mm_lac,
+ guint16 *mm_tac,
guint32 *mm_cid,
gchar **mm_operator_id)
{
@@ -4101,8 +3099,9 @@ process_common_info (QmiNasServiceStatus service_status,
apply_ps = FALSE;
else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_PS)
apply_cs = FALSE;
- else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_CS_PS)
+ else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_CS_PS) {
/* both apply */ ;
+ }
/* Check if we really are roaming or forbidden */
if (forbidden_valid && forbidden)
@@ -4116,6 +3115,8 @@ process_common_info (QmiNasServiceStatus service_status,
/* If we're registered either at home or roaming, try to get LAC/CID */
if (lac_valid)
*mm_lac = lac;
+ if (tac_valid)
+ *mm_tac = tac;
if (cid_valid)
*mm_cid = cid;
}
@@ -4129,11 +3130,11 @@ process_common_info (QmiNasServiceStatus service_status,
if (network_id_valid) {
*mm_operator_id = g_malloc (7);
memcpy (*mm_operator_id, mcc, 3);
- if (mnc[2] == 0xFF) {
- memcpy (*mm_operator_id, mnc, 2);
+ if ((guint8)mnc[2] == 0xFF) {
+ memcpy (&((*mm_operator_id)[3]), mnc, 2);
(*mm_operator_id)[5] = '\0';
} else {
- memcpy (*mm_operator_id, mnc, 3);
+ memcpy (&((*mm_operator_id)[3]), mnc, 3);
(*mm_operator_id)[6] = '\0';
}
}
@@ -4142,7 +3143,8 @@ process_common_info (QmiNasServiceStatus service_status,
}
static gboolean
-process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
+process_gsm_info (MMBroadbandModemQmi *self,
+ QmiMessageNasGetSystemInfoOutput *response_output,
QmiIndicationNasSystemInfoOutput *indication_output,
MMModem3gppRegistrationState *mm_cs_registration_state,
MMModem3gppRegistrationState *mm_ps_registration_state,
@@ -4182,7 +3184,7 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, /* true_service_status */
NULL, /* preferred_data_path */
NULL) ||
- !qmi_message_nas_get_system_info_output_get_gsm_system_info (
+ !qmi_message_nas_get_system_info_output_get_gsm_system_info_v2 (
response_output,
&domain_valid, &domain,
NULL, NULL, /* service_capability */
@@ -4195,7 +3197,7 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, NULL, /* egprs support */
NULL, NULL, /* dtm_support */
NULL)) {
- mm_dbg ("No GSM service reported");
+ mm_obj_dbg (self, "no GSM service reported");
/* No GSM service */
return FALSE;
}
@@ -4206,7 +3208,7 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, /* true_service_status */
NULL, /* preferred_data_path */
NULL) ||
- !qmi_indication_nas_system_info_output_get_gsm_system_info (
+ !qmi_indication_nas_system_info_output_get_gsm_system_info_v2 (
indication_output,
&domain_valid, &domain,
NULL, NULL, /* service_capability */
@@ -4219,7 +3221,7 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, NULL, /* egprs support */
NULL, NULL, /* dtm_support */
NULL)) {
- mm_dbg ("No GSM service reported");
+ mm_obj_dbg (self, "no GSM service reported");
/* No GSM service */
return FALSE;
}
@@ -4230,14 +3232,16 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
roaming_status_valid, roaming_status,
forbidden_valid, forbidden,
lac_valid, lac,
+ FALSE, 0,
cid_valid, cid,
network_id_valid, mcc, mnc,
mm_cs_registration_state,
mm_ps_registration_state,
mm_lac,
+ NULL,
mm_cid,
mm_operator_id)) {
- mm_dbg ("No GSM service registered");
+ mm_obj_dbg (self, "no GSM service registered");
return FALSE;
}
@@ -4245,7 +3249,8 @@ process_gsm_info (QmiMessageNasGetSystemInfoOutput *response_output,
}
static gboolean
-process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
+process_wcdma_info (MMBroadbandModemQmi *self,
+ QmiMessageNasGetSystemInfoOutput *response_output,
QmiIndicationNasSystemInfoOutput *indication_output,
MMModem3gppRegistrationState *mm_cs_registration_state,
MMModem3gppRegistrationState *mm_ps_registration_state,
@@ -4287,7 +3292,7 @@ process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, /* true_service_status */
NULL, /* preferred_data_path */
NULL) ||
- !qmi_message_nas_get_system_info_output_get_wcdma_system_info (
+ !qmi_message_nas_get_system_info_output_get_wcdma_system_info_v2 (
response_output,
&domain_valid, &domain,
NULL, NULL, /* service_capability */
@@ -4301,7 +3306,7 @@ process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
&hs_service_valid, &hs_service,
NULL, NULL, /* primary_scrambling_code */
NULL)) {
- mm_dbg ("No WCDMA service reported");
+ mm_obj_dbg (self, "no WCDMA service reported");
/* No GSM service */
return FALSE;
}
@@ -4312,7 +3317,7 @@ process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, /* true_service_status */
NULL, /* preferred_data_path */
NULL) ||
- !qmi_indication_nas_system_info_output_get_wcdma_system_info (
+ !qmi_indication_nas_system_info_output_get_wcdma_system_info_v2 (
indication_output,
&domain_valid, &domain,
NULL, NULL, /* service_capability */
@@ -4326,7 +3331,7 @@ process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
&hs_service_valid, &hs_service,
NULL, NULL, /* primary_scrambling_code */
NULL)) {
- mm_dbg ("No WCDMA service reported");
+ mm_obj_dbg (self, "no WCDMA service reported");
/* No GSM service */
return FALSE;
}
@@ -4337,14 +3342,16 @@ process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
roaming_status_valid, roaming_status,
forbidden_valid, forbidden,
lac_valid, lac,
+ FALSE, 0,
cid_valid, cid,
network_id_valid, mcc, mnc,
mm_cs_registration_state,
mm_ps_registration_state,
mm_lac,
+ NULL,
mm_cid,
mm_operator_id)) {
- mm_dbg ("No WCDMA service registered");
+ mm_obj_dbg (self, "no WCDMA service registered");
return FALSE;
}
@@ -4352,11 +3359,13 @@ process_wcdma_info (QmiMessageNasGetSystemInfoOutput *response_output,
}
static gboolean
-process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
+process_lte_info (MMBroadbandModemQmi *self,
+ QmiMessageNasGetSystemInfoOutput *response_output,
QmiIndicationNasSystemInfoOutput *indication_output,
MMModem3gppRegistrationState *mm_cs_registration_state,
MMModem3gppRegistrationState *mm_ps_registration_state,
guint16 *mm_lac,
+ guint16 *mm_tac,
guint32 *mm_cid,
gchar **mm_operator_id)
{
@@ -4369,6 +3378,8 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
gboolean forbidden;
gboolean lac_valid;
guint16 lac;
+ gboolean tac_valid;
+ guint16 tac;
gboolean cid_valid;
guint32 cid;
gboolean network_id_valid;
@@ -4381,6 +3392,7 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
*mm_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
*mm_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
*mm_lac = 0;
+ *mm_tac = 0;
*mm_cid = 0;
g_free (*mm_operator_id);
*mm_operator_id = NULL;
@@ -4392,7 +3404,7 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, /* true_service_status */
NULL, /* preferred_data_path */
NULL) ||
- !qmi_message_nas_get_system_info_output_get_lte_system_info (
+ !qmi_message_nas_get_system_info_output_get_lte_system_info_v2 (
response_output,
&domain_valid, &domain,
NULL, NULL, /* service_capability */
@@ -4402,9 +3414,9 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
&cid_valid, &cid,
NULL, NULL, NULL, /* registration_reject_info */
&network_id_valid, &mcc, &mnc,
- NULL, NULL, /* tac */
+ &tac_valid, &tac,
NULL)) {
- mm_dbg ("No LTE service reported");
+ mm_obj_dbg (self, "no LTE service reported");
/* No GSM service */
return FALSE;
}
@@ -4415,7 +3427,7 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
NULL, /* true_service_status */
NULL, /* preferred_data_path */
NULL) ||
- !qmi_indication_nas_system_info_output_get_lte_system_info (
+ !qmi_indication_nas_system_info_output_get_lte_system_info_v2 (
indication_output,
&domain_valid, &domain,
NULL, NULL, /* service_capability */
@@ -4425,9 +3437,9 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
&cid_valid, &cid,
NULL, NULL, NULL, /* registration_reject_info */
&network_id_valid, &mcc, &mnc,
- NULL, NULL, /* tac */
+ &tac_valid, &tac,
NULL)) {
- mm_dbg ("No LTE service reported");
+ mm_obj_dbg (self, "no LTE service reported");
/* No GSM service */
return FALSE;
}
@@ -4438,14 +3450,16 @@ process_lte_info (QmiMessageNasGetSystemInfoOutput *response_output,
roaming_status_valid, roaming_status,
forbidden_valid, forbidden,
lac_valid, lac,
+ tac_valid, tac,
cid_valid, cid,
network_id_valid, mcc, mnc,
mm_cs_registration_state,
mm_ps_registration_state,
mm_lac,
+ mm_tac,
mm_cid,
mm_operator_id)) {
- mm_dbg ("No LTE service registered");
+ mm_obj_dbg (self, "no LTE service registered");
return FALSE;
}
@@ -4460,12 +3474,15 @@ common_process_system_info_3gpp (MMBroadbandModemQmi *self,
MMModem3gppRegistrationState cs_registration_state;
MMModem3gppRegistrationState ps_registration_state;
guint16 lac;
+ guint16 tac;
guint32 cid;
gchar *operator_id;
+ gboolean has_lte_info;
ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
lac = 0;
+ tac = 0;
cid = 0;
operator_id = NULL;
@@ -4473,25 +3490,27 @@ common_process_system_info_3gpp (MMBroadbandModemQmi *self,
* LTE > WCDMA > GSM
* The first one giving results will be the one reported.
*/
- if (!process_lte_info (response_output, indication_output,
- &cs_registration_state,
- &ps_registration_state,
- &lac,
- &cid,
- &operator_id) &&
- !process_wcdma_info (response_output, indication_output,
+ has_lte_info = process_lte_info (self, response_output, indication_output,
+ &cs_registration_state,
+ &ps_registration_state,
+ &lac,
+ &tac,
+ &cid,
+ &operator_id);
+ if (!has_lte_info &&
+ !process_wcdma_info (self, response_output, indication_output,
&cs_registration_state,
&ps_registration_state,
&lac,
&cid,
&operator_id) &&
- !process_gsm_info (response_output, indication_output,
+ !process_gsm_info (self, response_output, indication_output,
&cs_registration_state,
&ps_registration_state,
&lac,
&cid,
&operator_id)) {
- mm_dbg ("No service (GSM, WCDMA or LTE) reported");
+ mm_obj_dbg (self, "no service (GSM, WCDMA or LTE) reported");
}
/* Cache current operator ID */
@@ -4503,304 +3522,270 @@ common_process_system_info_3gpp (MMBroadbandModemQmi *self,
/* Report new registration states */
mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), cs_registration_state);
mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), ps_registration_state);
- mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, cid);
+ if (has_lte_info)
+ mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), ps_registration_state);
+ else
+ mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN);
+ mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid);
}
static void
get_system_info_ready (QmiClientNas *client,
GAsyncResult *res,
- Run3gppRegistrationChecksContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageNasGetSystemInfoOutput *output;
GError *error = NULL;
output = qmi_client_nas_get_system_info_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- run_3gpp_registration_checks_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_nas_get_system_info_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get system info: ");
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
qmi_message_nas_get_system_info_output_unref (output);
- run_3gpp_registration_checks_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
- common_process_system_info_3gpp (ctx->self, output, NULL);
+ self = g_task_get_source_object (task);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ common_process_system_info_3gpp (self, output, NULL);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
qmi_message_nas_get_system_info_output_unref (output);
- run_3gpp_registration_checks_context_complete_and_free (ctx);
}
#endif /* WITH_NEWEST_QMI_COMMANDS */
static void
-modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
+ gboolean is_cs_supported,
+ gboolean is_ps_supported,
+ gboolean is_eps_supported,
+ gboolean is_5gs_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- Run3gppRegistrationChecksContext *ctx;
+ GTask *task;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- ctx = g_new0 (Run3gppRegistrationChecksContext, 1);
- 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,
- modem_3gpp_run_registration_checks);
+ task = g_task_new (self, NULL, callback, user_data);
#if defined WITH_NEWEST_QMI_COMMANDS
- /* System Info was added in NAS 1.8 */
- if (qmi_client_check_version (client, 1, 8)) {
- qmi_client_nas_get_system_info (ctx->client,
- NULL,
- 10,
- NULL,
- (GAsyncReadyCallback)get_system_info_ready,
- ctx);
- return;
- }
-#endif /* WITH_NEWEST_QMI_COMMANDS */
-
- qmi_client_nas_get_serving_system (ctx->client,
+ qmi_client_nas_get_system_info (QMI_CLIENT_NAS (client),
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_system_info_ready,
+ task);
+#else
+ qmi_client_nas_get_serving_system (QMI_CLIENT_NAS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)get_serving_system_3gpp_ready,
- ctx);
+ task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
}
/*****************************************************************************/
/* Enable/Disable unsolicited registration events (3GPP interface) */
typedef struct {
- MMBroadbandModemQmi *self;
QmiClientNas *client;
- GSimpleAsyncResult *result;
gboolean enable; /* TRUE for enabling, FALSE for disabling */
} UnsolicitedRegistrationEventsContext;
static void
-unsolicited_registration_events_context_complete_and_free (UnsolicitedRegistrationEventsContext *ctx)
+unsolicited_registration_events_context_free (UnsolicitedRegistrationEventsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->client);
- g_object_unref (ctx->self);
g_free (ctx);
}
-static UnsolicitedRegistrationEventsContext *
-unsolicited_registration_events_context_new (MMBroadbandModemQmi *self,
- QmiClient *client,
- gboolean enable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+static GTask *
+unsolicited_registration_events_task_new (MMBroadbandModemQmi *self,
+ QmiClient *client,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
UnsolicitedRegistrationEventsContext *ctx;
+ GTask *task;
ctx = g_new0 (UnsolicitedRegistrationEventsContext, 1);
- 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,
- unsolicited_registration_events_context_new);
+ ctx->client = QMI_CLIENT_NAS (g_object_ref (client));
ctx->enable = enable;
- return ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_registration_events_context_free);
+
+ return task;
}
static gboolean
-modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
ri_serving_system_or_system_info_ready (QmiClientNas *client,
GAsyncResult *res,
- UnsolicitedRegistrationEventsContext *ctx)
+ GTask *task)
{
- QmiMessageNasRegisterIndicationsOutput *output = NULL;
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ UnsolicitedRegistrationEventsContext *ctx;
+ g_autoptr(QmiMessageNasRegisterIndicationsOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
output = qmi_client_nas_register_indications_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: '%s'", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_register_indications_output_get_result (output, &error)) {
- mm_dbg ("Couldn't register indications: '%s'", error->message);
- g_error_free (error);
+ if (!output || !qmi_message_nas_register_indications_output_get_result (output, &error)) {
+ mm_obj_dbg (self, "couldn't register indications: '%s'", error->message);
+ if (ctx->enable) {
+#if defined WITH_NEWEST_QMI_COMMANDS
+ mm_obj_dbg (self, "assuming system info indications are always enabled");
+#else
+ mm_obj_dbg (self, "assuming serving system indications are always enabled");
+#endif
+ }
}
- if (output)
- qmi_message_nas_register_indications_output_unref (output);
-
/* Just ignore errors for now */
- ctx->self->priv->unsolicited_registration_events_enabled = ctx->enable;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- unsolicited_registration_events_context_complete_and_free (ctx);
+ self->priv->unsolicited_registration_events_enabled = ctx->enable;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
+#if !defined WITH_NEWEST_QMI_COMMANDS
+
static void
-common_enable_disable_unsolicited_registration_events_serving_system (UnsolicitedRegistrationEventsContext *ctx)
+common_enable_disable_unsolicited_registration_events_serving_system (GTask *task)
{
- QmiMessageNasRegisterIndicationsInput *input;
+ UnsolicitedRegistrationEventsContext *ctx;
+ g_autoptr(QmiMessageNasRegisterIndicationsInput) input = NULL;
+ ctx = g_task_get_task_data (task);
input = qmi_message_nas_register_indications_input_new ();
qmi_message_nas_register_indications_input_set_serving_system_events (input, ctx->enable, NULL);
+ qmi_message_nas_register_indications_input_set_network_reject_information (input, ctx->enable, FALSE, NULL);
qmi_client_nas_register_indications (
ctx->client,
input,
5,
NULL,
(GAsyncReadyCallback)ri_serving_system_or_system_info_ready,
- ctx);
- qmi_message_nas_register_indications_input_unref (input);
+ task);
}
-#if defined WITH_NEWEST_QMI_COMMANDS
+#else /* WITH_NEWEST_QMI_COMMANDS */
+
static void
-common_enable_disable_unsolicited_registration_events_system_info (UnsolicitedRegistrationEventsContext *ctx)
+common_enable_disable_unsolicited_registration_events_system_info (GTask *task)
{
- QmiMessageNasRegisterIndicationsInput *input;
+ UnsolicitedRegistrationEventsContext *ctx;
+ g_autoptr(QmiMessageNasRegisterIndicationsInput) input = NULL;
+ ctx = g_task_get_task_data (task);
input = qmi_message_nas_register_indications_input_new ();
qmi_message_nas_register_indications_input_set_system_info (input, ctx->enable, NULL);
+ qmi_message_nas_register_indications_input_set_network_reject_information (input, ctx->enable, FALSE, NULL);
qmi_client_nas_register_indications (
ctx->client,
input,
5,
NULL,
(GAsyncReadyCallback)ri_serving_system_or_system_info_ready,
- ctx);
- qmi_message_nas_register_indications_input_unref (input);
+ task);
}
+
#endif /* WITH_NEWEST_QMI_COMMANDS */
static void
-modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *self,
+ gboolean cs_supported,
+ gboolean ps_supported,
+ gboolean eps_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- UnsolicitedRegistrationEventsContext *ctx;
+ GTask *task;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- ctx = unsolicited_registration_events_context_new (MM_BROADBAND_MODEM_QMI (self),
- client,
- FALSE,
- callback,
- user_data);
+ task = unsolicited_registration_events_task_new (MM_BROADBAND_MODEM_QMI (self),
+ client,
+ FALSE,
+ callback,
+ user_data);
#if defined WITH_NEWEST_QMI_COMMANDS
- /* System Info was added in NAS 1.8 */
- if (qmi_client_check_version (client, 1, 8)) {
- common_enable_disable_unsolicited_registration_events_system_info (ctx);
- return;
- }
+ common_enable_disable_unsolicited_registration_events_system_info (task);
+#else
+ common_enable_disable_unsolicited_registration_events_serving_system (task);
#endif /* WITH_NEWEST_QMI_COMMANDS */
-
- /* Ability to explicitly enable/disable serving system indications was
- * added in NAS 1.2 */
- if (qmi_client_check_version (client, 1, 2)) {
- common_enable_disable_unsolicited_registration_events_serving_system (ctx);
- return;
- }
-
- /* Devices with NAS < 1.2 will just always issue serving system indications */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Device doesn't allow disabling registration events");
- ctx->self->priv->unsolicited_registration_events_enabled = FALSE;
- unsolicited_registration_events_context_complete_and_free (ctx);
}
static void
-modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *self,
+ gboolean cs_supported,
+ gboolean ps_supported,
+ gboolean eps_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- UnsolicitedRegistrationEventsContext *ctx;
+ GTask *task;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- ctx = unsolicited_registration_events_context_new (MM_BROADBAND_MODEM_QMI (self),
- client,
- TRUE,
- callback,
- user_data);
+ task = unsolicited_registration_events_task_new (MM_BROADBAND_MODEM_QMI (self),
+ client,
+ TRUE,
+ callback,
+ user_data);
- /* Ability to explicitly enable/disable serving system indications was
- * added in NAS 1.2 */
- if (qmi_client_check_version (client, 1, 2)) {
- common_enable_disable_unsolicited_registration_events_serving_system (ctx);
- return;
- }
-
- /* Devices with NAS < 1.2 will just always issue serving system indications */
- mm_dbg ("Assuming serving system indications are always enabled");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- ctx->self->priv->unsolicited_registration_events_enabled = TRUE;
- unsolicited_registration_events_context_complete_and_free (ctx);
+#if defined WITH_NEWEST_QMI_COMMANDS
+ common_enable_disable_unsolicited_registration_events_system_info (task);
+#else
+ common_enable_disable_unsolicited_registration_events_serving_system (task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
}
/*****************************************************************************/
/* Registration checks (CDMA interface) */
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *client;
- GSimpleAsyncResult *result;
-} RunCdmaRegistrationChecksContext;
-
-static void
-run_cdma_registration_checks_context_complete_and_free (RunCdmaRegistrationChecksContext *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 (RunCdmaRegistrationChecksContext, ctx);
-}
-
static gboolean
modem_cdma_run_registration_checks_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -4861,9 +3846,9 @@ common_process_serving_system_cdma (MMBroadbandModemQmi *self,
if (selected_network == QMI_NAS_NETWORK_TYPE_3GPP2 ||
(selected_network == QMI_NAS_NETWORK_TYPE_UNKNOWN &&
(mm_access_technologies & MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK))) {
- mm_dbg ("Processing CDMA info...");
+ mm_obj_dbg (self, "processing CDMA info...");
} else {
- mm_dbg ("No CDMA info given...");
+ mm_obj_dbg (self, "no CDMA info given...");
mm_iface_modem_cdma_update_cdma1x_registration_state (MM_IFACE_MODEM_CDMA (self),
MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
0, 0);
@@ -4985,32 +3970,35 @@ common_process_serving_system_cdma (MMBroadbandModemQmi *self,
static void
get_serving_system_cdma_ready (QmiClientNas *client,
GAsyncResult *res,
- RunCdmaRegistrationChecksContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageNasGetServingSystemOutput *output;
GError *error = NULL;
output = qmi_client_nas_get_serving_system_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- run_cdma_registration_checks_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_nas_get_serving_system_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get serving system: ");
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
qmi_message_nas_get_serving_system_output_unref (output);
- run_cdma_registration_checks_context_complete_and_free (ctx);
return;
}
- common_process_serving_system_cdma (ctx->self, output, NULL);
+ self = g_task_get_source_object (task);
+
+ common_process_serving_system_cdma (self, output, NULL);
qmi_message_nas_get_serving_system_output_unref (output);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- run_cdma_registration_checks_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -5020,65 +4008,43 @@ modem_cdma_run_registration_checks (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- RunCdmaRegistrationChecksContext *ctx;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- /* Setup context */
- ctx = g_slice_new0 (RunCdmaRegistrationChecksContext);
- 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,
- modem_cdma_run_registration_checks);
-
/* TODO: Run Get System Info in NAS >= 1.8 */
- qmi_client_nas_get_serving_system (ctx->client,
+ qmi_client_nas_get_serving_system (QMI_CLIENT_NAS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)get_serving_system_cdma_ready,
- ctx);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Load initial activation state (CDMA interface) */
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientDms *client;
- GSimpleAsyncResult *result;
-} LoadActivationStateContext;
-
-static void
-load_activation_state_context_complete_and_free (LoadActivationStateContext *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 (LoadActivationStateContext, ctx);
-}
-
static MMModemCdmaActivationState
modem_cdma_load_activation_state_finish (MMIfaceModemCdma *_self,
GAsyncResult *res,
GError **error)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GError *inner_error = NULL;
+ gssize value;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN;
+ }
/* Cache the value and also return it */
- self->priv->activation_state =
- (MMModemCdmaActivationState) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ self->priv->activation_state = (MMModemCdmaActivationState)value;
return self->priv->activation_state;
}
@@ -5086,7 +4052,7 @@ modem_cdma_load_activation_state_finish (MMIfaceModemCdma *_self,
static void
get_activation_state_ready (QmiClientDms *client,
GAsyncResult *res,
- LoadActivationStateContext *ctx)
+ GTask *task)
{
QmiDmsActivationState state = QMI_DMS_ACTIVATION_STATE_NOT_ACTIVATED;
QmiMessageDmsGetActivationStateOutput *output;
@@ -5095,27 +4061,25 @@ get_activation_state_ready (QmiClientDms *client,
output = qmi_client_dms_get_activation_state_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- load_activation_state_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_dms_get_activation_state_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get activation state: ");
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
qmi_message_dms_get_activation_state_output_unref (output);
- load_activation_state_context_complete_and_free (ctx);
return;
}
qmi_message_dms_get_activation_state_output_get_info (output, &state, NULL);
qmi_message_dms_get_activation_state_output_unref (output);
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (mm_modem_cdma_activation_state_from_qmi_activation_state (state)),
- NULL);
- load_activation_state_context_complete_and_free (ctx);
+ g_task_return_int (task,
+ mm_modem_cdma_activation_state_from_qmi_activation_state (state));
+ g_object_unref (task);
}
static void
@@ -5123,29 +4087,19 @@ modem_cdma_load_activation_state (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- LoadActivationStateContext *ctx;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- /* Setup context */
- ctx = g_slice_new0 (LoadActivationStateContext);
- 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,
- modem_cdma_load_activation_state);
-
- qmi_client_dms_get_activation_state (ctx->client,
+ qmi_client_dms_get_activation_state (QMI_CLIENT_DMS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)get_activation_state_ready,
- ctx);
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -5158,14 +4112,13 @@ typedef enum {
CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS,
CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION,
CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED,
- CDMA_ACTIVATION_STEP_POWER_CYCLE,
+ CDMA_ACTIVATION_STEP_RESET,
CDMA_ACTIVATION_STEP_LAST
} CdmaActivationStep;
typedef struct {
MMBroadbandModemQmi *self;
QmiClientDms *client;
- GSimpleAsyncResult *result;
CdmaActivationStep step;
/* OTA activation... */
QmiMessageDmsActivateAutomaticInput *input_automatic;
@@ -5179,10 +4132,10 @@ typedef struct {
} CdmaActivationContext;
static void
-cdma_activation_context_complete_and_free (CdmaActivationContext *ctx)
+cdma_activation_context_free (CdmaActivationContext *ctx)
{
- /* Cleanup the activation context from the private info */
- ctx->self->priv->activation_ctx = NULL;
+ /* Cleanup the activation task from the private info */
+ ctx->self->priv->activation_task = NULL;
for (ctx->segment_i = 0; ctx->segment_i < ctx->n_segments; ctx->segment_i++)
g_array_unref (ctx->segments[ctx->segment_i]);
@@ -5192,8 +4145,6 @@ cdma_activation_context_complete_and_free (CdmaActivationContext *ctx)
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_slice_free (CdmaActivationContext, ctx);
@@ -5204,7 +4155,7 @@ modem_cdma_activate_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
@@ -5212,10 +4163,10 @@ modem_cdma_activate_manual_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void cdma_activation_context_step (CdmaActivationContext *ctx);
+static void cdma_activation_context_step (GTask *task);
static void
cdma_activation_disable_indications (CdmaActivationContext *ctx)
@@ -5235,40 +4186,47 @@ cdma_activation_disable_indications (CdmaActivationContext *ctx)
}
static void
-activation_power_cycle_ready (MMBroadbandModemQmi *self,
- GAsyncResult *res,
- CdmaActivationContext *ctx)
+activation_reset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ CdmaActivationContext *ctx;
GError *error = NULL;
- if (!power_cycle_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- cdma_activation_context_complete_and_free (ctx);
+ if (!mm_shared_qmi_reset_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* And go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
}
static gboolean
-retry_msisdn_check_cb (CdmaActivationContext *ctx)
+retry_msisdn_check_cb (GTask *task)
{
- cdma_activation_context_step (ctx);
- return FALSE;
+ cdma_activation_context_step (task);
+ return G_SOURCE_REMOVE;
}
static void
activate_manual_get_msisdn_ready (QmiClientDms *client,
GAsyncResult *res,
- CdmaActivationContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ CdmaActivationContext *ctx;
QmiMessageDmsGetMsisdnOutput *output = NULL;
GError *error = NULL;
const gchar *current_mdn = NULL;
const gchar *expected_mdn = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
qmi_message_dms_activate_manual_input_get_info (ctx->input_manual,
NULL, /* spc */
NULL, /* sid */
@@ -5281,11 +4239,11 @@ activate_manual_get_msisdn_ready (QmiClientDms *client,
qmi_message_dms_get_msisdn_output_get_result (output, NULL) &&
qmi_message_dms_get_msisdn_output_get_msisdn (output, &current_mdn, NULL) &&
g_str_equal (current_mdn, expected_mdn)) {
- mm_dbg ("MDN successfully updated to '%s'", expected_mdn);
+ mm_obj_dbg (self, "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);
+ cdma_activation_context_step (task);
return;
}
@@ -5294,17 +4252,17 @@ activate_manual_get_msisdn_ready (QmiClientDms *client,
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);
+ mm_obj_dbg (self, "MDN not yet updated, retrying...");
+ g_timeout_add (1, (GSourceFunc) retry_msisdn_check_cb, task);
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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "MDN was not correctly set during manual activation");
+ g_object_unref (task);
}
static void
@@ -5320,15 +4278,15 @@ activation_event_report_indication_cb (QmiClientDms *client,
if (!qmi_indication_dms_event_report_output_get_activation_state (output, &state, NULL))
return;
- mm_dbg ("Activation state update: '%s'",
+ mm_obj_dbg (self, "activation state update: '%s'",
qmi_dms_activation_state_get_string (state));
new = mm_modem_cdma_activation_state_from_qmi_activation_state (state);
if (self->priv->activation_state != new)
- mm_info ("Activation state changed: '%s'-->'%s'",
- mm_modem_cdma_activation_state_get_string (self->priv->activation_state),
- mm_modem_cdma_activation_state_get_string (new));
+ mm_obj_info (self, "activation state changed: '%s'-->'%s'",
+ mm_modem_cdma_activation_state_get_string (self->priv->activation_state),
+ mm_modem_cdma_activation_state_get_string (new));
/* Cache the new value */
self->priv->activation_state = new;
@@ -5345,53 +4303,58 @@ activation_event_report_indication_cb (QmiClientDms *client,
/* Now, if we have a FINAL state, finish the ongoing activation state request */
if (new != MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING) {
+ GTask *task;
CdmaActivationContext *ctx;
- g_assert (self->priv->activation_ctx != NULL);
- ctx = (CdmaActivationContext *)self->priv->activation_ctx;
+ g_assert (self->priv->activation_task != NULL);
+ task = self->priv->activation_task;
+ ctx = g_task_get_task_data (task);
/* Disable further indications. */
cdma_activation_disable_indications (ctx);
/* If there is any error, finish the async method */
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- cdma_activation_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Otherwise, go on to next step */
ctx->step++;
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
return;
}
- mm_dbg ("Activation process still ongoing...");
+ mm_obj_dbg (self, "activation process still ongoing...");
}
static void
activate_automatic_ready (QmiClientDms *client,
GAsyncResult *res,
- CdmaActivationContext *ctx)
+ GTask *task)
{
+ CdmaActivationContext *ctx;
QmiMessageDmsActivateAutomaticOutput *output;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_dms_activate_automatic_finish (client, res, &error);
if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
cdma_activation_disable_indications (ctx);
- cdma_activation_context_complete_and_free (ctx);
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_dms_activate_automatic_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't request OTA activation: ");
- g_simple_async_result_take_error (ctx->result, error);
qmi_message_dms_activate_automatic_output_unref (output);
cdma_activation_disable_indications (ctx);
- cdma_activation_context_complete_and_free (ctx);
+ g_prefix_error (&error, "Couldn't request OTA activation: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -5399,30 +4362,33 @@ activate_automatic_ready (QmiClientDms *client,
/* Keep on */
ctx->step++;
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
}
static void
activate_manual_ready (QmiClientDms *client,
GAsyncResult *res,
- CdmaActivationContext *ctx)
+ GTask *task)
{
+ CdmaActivationContext *ctx;
QmiMessageDmsActivateManualOutput *output;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
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);
+ g_prefix_error (&error, "Couldn't request manual activation: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -5433,40 +4399,43 @@ activate_manual_ready (QmiClientDms *client,
ctx->segment_i++;
if (ctx->segment_i < ctx->n_segments) {
/* There's a pending segment */
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
return;
}
}
/* No more segments to send, go on */
ctx->step++;
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
}
static void
ser_activation_state_ready (QmiClientDms *client,
GAsyncResult *res,
- CdmaActivationContext *ctx)
+ GTask *task)
{
+ CdmaActivationContext *ctx;
QmiMessageDmsSetEventReportOutput *output;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
/* We cannot ignore errors, we NEED the indications to finish the
* activation request properly */
output = qmi_client_dms_set_event_report_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_dms_set_event_report_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't set event report: ");
- g_simple_async_result_take_error (ctx->result, error);
qmi_message_dms_set_event_report_output_unref (output);
- cdma_activation_context_complete_and_free (ctx);
+ g_prefix_error (&error, "Couldn't set event report: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -5482,24 +4451,27 @@ ser_activation_state_ready (QmiClientDms *client,
/* Keep on */
ctx->step++;
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
}
static void
-cdma_activation_context_step (CdmaActivationContext *ctx)
+cdma_activation_context_step (GTask *task)
{
+ CdmaActivationContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case CDMA_ACTIVATION_STEP_FIRST:
ctx->step++;
- /* Fall down to next step */
+ /* Fall through */
case CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS:
/* Indications needed in automatic activation */
if (ctx->input_automatic) {
QmiMessageDmsSetEventReportInput *input;
- mm_info ("Activation step [1/5]: enabling indications");
-
+ mm_obj_info (ctx->self, "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 (
@@ -5508,38 +4480,37 @@ cdma_activation_context_step (CdmaActivationContext *ctx)
5,
NULL,
(GAsyncReadyCallback)ser_activation_state_ready,
- ctx);
+ task);
qmi_message_dms_set_event_report_input_unref (input);
return;
}
/* Manual activation, no indications needed */
g_assert (ctx->input_manual != NULL);
- mm_info ("Activation step [1/5]: indications not needed in manual activation");
+ mm_obj_info (ctx->self, "activation step [1/5]: indications not needed in manual activation");
ctx->step++;
- /* Fall down to next step */
+ /* Fall through */
case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION:
/* Automatic activation */
if (ctx->input_automatic) {
- mm_info ("Activation step [2/5]: requesting automatic (OTA) activation");
-
+ mm_obj_info (ctx->self, "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);
+ task);
return;
}
/* Manual activation */
g_assert (ctx->input_manual != NULL);
if (!ctx->segments)
- mm_info ("Activation step [2/5]: requesting manual activation");
+ mm_obj_info (ctx->self, "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);
+ mm_obj_info (ctx->self, "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,
@@ -5553,40 +4524,40 @@ cdma_activation_context_step (CdmaActivationContext *ctx)
10,
NULL,
(GAsyncReadyCallback)activate_manual_ready,
- ctx);
+ task);
return;
case CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED:
/* Automatic activation */
if (ctx->input_automatic) {
/* State updates via unsolicited messages */
- mm_info ("Activation step [3/5]: waiting for activation state updates");
+ mm_obj_info (ctx->self, "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);
+ mm_obj_info (ctx->self, "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);
+ task);
return;
- case CDMA_ACTIVATION_STEP_POWER_CYCLE:
- mm_info ("Activation step [4/5]: power-cycling...");
- power_cycle (ctx->self,
- (GAsyncReadyCallback)activation_power_cycle_ready,
- ctx);
+ case CDMA_ACTIVATION_STEP_RESET:
+ mm_obj_info (ctx->self, "activation step [4/5]: power-cycling...");
+ mm_shared_qmi_reset (MM_IFACE_MODEM (ctx->self),
+ (GAsyncReadyCallback)activation_reset_ready,
+ task);
return;
case CDMA_ACTIVATION_STEP_LAST:
- 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);
+ mm_obj_info (ctx->self, "activation step [5/5]: finished");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
default:
@@ -5601,47 +4572,43 @@ modem_cdma_activate (MMIfaceModemCdma *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
CdmaActivationContext *ctx;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_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);
+ task = g_task_new (self, NULL, callback, user_data);
/* 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);
+ if (self->priv->activation_task) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "An activation operation is already in progress");
+ g_object_unref (task);
return;
}
/* Setup context */
ctx = g_slice_new0 (CdmaActivationContext);
ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
- ctx->result = result;
+ ctx->client = QMI_CLIENT_DMS (g_object_ref (client));
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
+ g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_activation_context_free);
+
+ /* We keep the activation task 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);
+ self->priv->activation_task = task;
+ cdma_activation_context_step (task);
}
static void
@@ -5651,41 +4618,37 @@ modem_cdma_activate_manual (MMIfaceModemCdma *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
CdmaActivationContext *ctx;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_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);
+ task = g_task_new (self, NULL, callback, user_data);
/* 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);
+ if (self->priv->activation_task) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "An activation operation is already in progress");
+ g_object_unref (task);
return;
}
/* Setup context */
ctx = g_slice_new0 (CdmaActivationContext);
ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
- ctx->result = result;
+ ctx->client = QMI_CLIENT_DMS (g_object_ref (client));
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_activation_context_free);
- /* We keep the activation context in the private data, so that we don't
+ /* We keep the activation task in the private data, so that we don't
* allow multiple activation requests at the same time. */
- self->priv->activation_ctx = ctx;
+ self->priv->activation_task = task;
/* Build base input bundle for the Manual activation */
ctx->input_manual = qmi_message_dms_activate_manual_input_new ();
@@ -5752,7 +4715,7 @@ modem_cdma_activate_manual (MMIfaceModemCdma *_self,
#undef MAX_PRL_SEGMENT_SIZE
}
- cdma_activation_context_step (ctx);
+ cdma_activation_context_step (task);
}
/*****************************************************************************/
@@ -5764,7 +4727,7 @@ common_setup_cleanup_unsolicited_registration_events_finish (MMBroadbandModemQmi
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
#if defined WITH_NEWEST_QMI_COMMANDS
@@ -5776,7 +4739,8 @@ system_info_indication_cb (QmiClientNas *client,
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
common_process_system_info_3gpp (self, NULL, output);
}
-#endif
+
+#else /* WITH_NEWEST_QMI_COMMANDS */
static void
serving_system_indication_cb (QmiClientNas *client,
@@ -5789,31 +4753,57 @@ serving_system_indication_cb (QmiClientNas *client,
common_process_serving_system_cdma (self, NULL, output);
}
+#endif
+
+/* network reject indications enabled in both with/without newest QMI commands */
+static void
+network_reject_indication_cb (QmiClientNas *client,
+ QmiIndicationNasNetworkRejectOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiNasNetworkServiceDomain service_domain = QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN;
+ QmiNasRadioInterface radio_interface = QMI_NAS_RADIO_INTERFACE_UNKNOWN;
+ QmiNasRejectCause reject_cause = QMI_NAS_REJECT_CAUSE_NONE;
+ guint16 mcc = 0;
+ guint16 mnc = 0;
+ guint32 closed_subscriber_group = 0;
+
+ mm_obj_warn (self, "network reject indication received");
+ if (qmi_indication_nas_network_reject_output_get_service_domain (output, &service_domain, NULL))
+ mm_obj_warn (self, " service domain: %s", qmi_nas_network_service_domain_get_string (service_domain));
+ if (qmi_indication_nas_network_reject_output_get_radio_interface (output, &radio_interface, NULL))
+ mm_obj_warn (self, " radio interface: %s", qmi_nas_radio_interface_get_string (radio_interface));
+ if (qmi_indication_nas_network_reject_output_get_reject_cause (output, &reject_cause, NULL))
+ mm_obj_warn (self, " reject cause: %s", qmi_nas_reject_cause_get_string (reject_cause));
+ if (qmi_indication_nas_network_reject_output_get_plmn (output, &mcc, &mnc, NULL, NULL)) {
+ mm_obj_warn (self, " mcc: %" G_GUINT16_FORMAT, mcc);
+ mm_obj_warn (self, " mnc: %" G_GUINT16_FORMAT, mnc);
+ }
+ if (qmi_indication_nas_network_reject_output_get_closed_subscriber_group (output, &closed_subscriber_group, NULL))
+ mm_obj_warn (self, " closed subscriber group: %u", closed_subscriber_group);
+}
+
static void
common_setup_cleanup_unsolicited_registration_events (MMBroadbandModemQmi *self,
gboolean enable,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_setup_cleanup_unsolicited_registration_events);
+ task = g_task_new (self, NULL, callback, user_data);
if (enable == self->priv->unsolicited_registration_events_setup) {
- mm_dbg ("Unsolicited registration events already %s; skipping",
+ mm_obj_dbg (self, "unsolicited registration 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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -5821,42 +4811,51 @@ common_setup_cleanup_unsolicited_registration_events (MMBroadbandModemQmi *self,
self->priv->unsolicited_registration_events_setup = enable;
#if defined WITH_NEWEST_QMI_COMMANDS
- /* Signal info introduced in NAS 1.8 */
- if (qmi_client_check_version (client, 1, 8)) {
- /* Connect/Disconnect "System Info" indications */
- if (enable) {
- g_assert (self->priv->system_info_indication_id == 0);
- self->priv->system_info_indication_id =
- g_signal_connect (client,
- "system-info",
- G_CALLBACK (system_info_indication_cb),
- self);
- } else {
- g_assert (self->priv->system_info_indication_id != 0);
- g_signal_handler_disconnect (client, self->priv->system_info_indication_id);
- self->priv->system_info_indication_id = 0;
- }
- } else
+ /* Connect/Disconnect "System Info" indications */
+ if (enable) {
+ g_assert (self->priv->system_info_indication_id == 0);
+ self->priv->system_info_indication_id =
+ g_signal_connect (client,
+ "system-info",
+ G_CALLBACK (system_info_indication_cb),
+ self);
+ } else {
+ g_assert (self->priv->system_info_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->system_info_indication_id);
+ self->priv->system_info_indication_id = 0;
+ }
+#else
+ /* Connect/Disconnect "Serving System" indications */
+ if (enable) {
+ g_assert (self->priv->serving_system_indication_id == 0);
+ self->priv->serving_system_indication_id =
+ g_signal_connect (client,
+ "serving-system",
+ G_CALLBACK (serving_system_indication_cb),
+ self);
+ } else {
+ g_assert (self->priv->serving_system_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->serving_system_indication_id);
+ self->priv->serving_system_indication_id = 0;
+ }
#endif /* WITH_NEWEST_QMI_COMMANDS */
- {
- /* Connect/Disconnect "Serving System" indications */
- if (enable) {
- g_assert (self->priv->serving_system_indication_id == 0);
- self->priv->serving_system_indication_id =
- g_signal_connect (client,
- "serving-system",
- G_CALLBACK (serving_system_indication_cb),
- self);
- } else {
- g_assert (self->priv->serving_system_indication_id != 0);
- g_signal_handler_disconnect (client, self->priv->serving_system_indication_id);
- self->priv->serving_system_indication_id = 0;
- }
+
+ /* Connect/Disconnect "Network Reject" indications */
+ if (enable) {
+ g_assert (self->priv->network_reject_indication_id == 0);
+ self->priv->network_reject_indication_id =
+ g_signal_connect (client,
+ "network-reject",
+ G_CALLBACK (network_reject_indication_cb),
+ self);
+ } else {
+ g_assert (self->priv->network_reject_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->network_reject_indication_id);
+ self->priv->network_reject_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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -5900,14 +4899,7 @@ modem_cdma_load_meid_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- gchar *meid;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- meid = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded MEID: %s", meid);
- return meid;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -5916,24 +4908,18 @@ modem_cdma_load_meid (MMIfaceModemCdma *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_load_meid);
+ task = g_task_new (self, NULL, callback, user_data);
if (self->priv->meid)
- g_simple_async_result_set_op_res_gpointer (result,
- self->priv->meid,
- NULL);
+ g_task_return_pointer (task, g_strdup (self->priv->meid), g_free);
else
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Device doesn't report a valid MEID");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Device doesn't report a valid MEID");
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -5944,14 +4930,7 @@ modem_cdma_load_esn_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- gchar *esn;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- esn = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded ESN: %s", esn);
- return esn;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -5960,273 +4939,327 @@ modem_cdma_load_esn (MMIfaceModemCdma *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_load_esn);
+ task = g_task_new (self, NULL, callback, user_data);
if (self->priv->esn)
- g_simple_async_result_set_op_res_gpointer (result,
- self->priv->esn,
- NULL);
+ g_task_return_pointer (task, g_strdup (self->priv->esn), g_free);
else
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Device doesn't report a valid ESN");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Device doesn't report a valid ESN");
+ g_object_unref (task);
}
/*****************************************************************************/
-/* Enabling/disabling unsolicited events (3GPP and CDMA interface)
- *
- * If NAS >= 1.8:
- * - Config Signal Info (only when enabling)
- * - Register Indications with Signal Info
- *
- * If NAS < 1.8:
- * - Set Event Report with Signal Strength
- */
+/* Enabling/disabling unsolicited events (3GPP and CDMA interface) */
typedef struct {
- MMBroadbandModemQmi *self;
- GSimpleAsyncResult *result;
- QmiClientNas *client;
- gboolean enable;
+ QmiClientNas *client_nas;
+ QmiClientWds *client_wds;
+ gboolean enable;
} EnableUnsolicitedEventsContext;
static void
-enable_unsolicited_events_context_complete_and_free (EnableUnsolicitedEventsContext *ctx)
+enable_unsolicited_events_context_free (EnableUnsolicitedEventsContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->client);
- g_object_unref (ctx->self);
+ g_clear_object (&ctx->client_wds);
+ g_clear_object (&ctx->client_nas);
g_free (ctx);
}
static gboolean
-common_enable_disable_unsolicited_events_finish (MMBroadbandModemQmi *self,
- GAsyncResult *res,
- GError **error)
+common_enable_disable_unsolicited_events_finish (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
+ser_data_system_status_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ g_autoptr(QmiMessageWdsSetEventReportOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_wds_set_event_report_finish (client, res, &error);
+ if (!output || !qmi_message_wds_set_event_report_output_get_result (output, &error))
+ mm_obj_dbg (self, "couldn't set event report: '%s'", error->message);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_enable_disable_unsolicited_events_data_system_status (GTask *task)
+{
+ EnableUnsolicitedEventsContext *ctx;
+ g_autoptr(QmiMessageWdsSetEventReportInput) input = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_wds_set_event_report_input_new ();
+ qmi_message_wds_set_event_report_input_set_data_systems (input, ctx->enable, NULL);
+ qmi_client_wds_set_event_report (ctx->client_wds,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)ser_data_system_status_ready,
+ task);
+}
+
+#if !defined WITH_NEWEST_QMI_COMMANDS
+
+static void
ser_signal_strength_ready (QmiClientNas *client,
GAsyncResult *res,
- EnableUnsolicitedEventsContext *ctx)
+ GTask *task)
{
- QmiMessageNasSetEventReportOutput *output = NULL;
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ EnableUnsolicitedEventsContext *ctx;
+ g_autoptr(QmiMessageNasSetEventReportOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
output = qmi_client_nas_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_nas_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_nas_set_event_report_output_get_result (output, &error))
+ mm_obj_dbg (self, "couldn't enable signal strength indications: '%s'", error->message);
+ else {
+ /* Disable access technology and signal quality polling if we can use the indications */
+ mm_obj_dbg (self, "signal strength indications enabled: polling disabled");
+ g_object_set (self,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
+ MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, TRUE,
+ NULL);
}
- if (output)
- qmi_message_nas_set_event_report_output_unref (output);
+ if (!ctx->client_wds) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
- /* Just ignore errors for now */
- ctx->self->priv->unsolicited_events_enabled = ctx->enable;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_unsolicited_events_context_complete_and_free (ctx);
+ common_enable_disable_unsolicited_events_data_system_status (task);
}
static void
-common_enable_disable_unsolicited_events_signal_strength (EnableUnsolicitedEventsContext *ctx)
+common_enable_disable_unsolicited_events_signal_strength (GTask *task)
{
+ EnableUnsolicitedEventsContext *ctx;
+ g_autoptr(QmiMessageNasSetEventReportInput) input = NULL;
+ g_autoptr(GArray) thresholds = NULL;
+
/* The device doesn't really like to have many threshold values, so don't
- * grow this array without checking first */
- static const gint8 thresholds_data[] = { -80, -40, 0, 40, 80 };
- QmiMessageNasSetEventReportInput *input;
- GArray *thresholds;
+ * grow this array without checking first
+ * The values are chosen towards their results through MM_RSSI_TO_QUALITY
+ * -106 dBm gives 11%
+ * -94 dBm gives 30%
+ * -82 dBm gives 50%
+ * -69 dBm gives 70%
+ * -57 dBm gives 90%
+ */
+ static const gint8 thresholds_data[] = { -106, -94, -82, -69, -57 };
- input = qmi_message_nas_set_event_report_input_new ();
+ ctx = g_task_get_task_data (task);
- /* Prepare thresholds, separated 20 each */
+ /* Always set thresholds, both in enable and disable, or otherwise the protocol will
+ * complain with FAILURE: NoThresholdsProvided */
thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data));
+ g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
- /* Only set thresholds during enable */
- if (ctx->enable)
- g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
-
+ input = qmi_message_nas_set_event_report_input_new ();
qmi_message_nas_set_event_report_input_set_signal_strength_indicator (
input,
ctx->enable,
thresholds,
NULL);
- g_array_unref (thresholds);
qmi_client_nas_set_event_report (
- ctx->client,
+ ctx->client_nas,
input,
5,
NULL,
(GAsyncReadyCallback)ser_signal_strength_ready,
- ctx);
- qmi_message_nas_set_event_report_input_unref (input);
+ task);
}
-#if defined WITH_NEWEST_QMI_COMMANDS
+#else /* WITH_NEWEST_QMI_COMMANDS */
static void
ri_signal_info_ready (QmiClientNas *client,
GAsyncResult *res,
- EnableUnsolicitedEventsContext *ctx)
+ GTask *task)
{
- QmiMessageNasRegisterIndicationsOutput *output = NULL;
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ EnableUnsolicitedEventsContext *ctx;
+ g_autoptr(QmiMessageNasRegisterIndicationsOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
output = qmi_client_nas_register_indications_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: '%s'", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_register_indications_output_get_result (output, &error)) {
- mm_dbg ("Couldn't register indications: '%s'", error->message);
- g_error_free (error);
+ if (!output || !qmi_message_nas_register_indications_output_get_result (output, &error))
+ mm_obj_dbg (self, "couldn't register signal info indications: '%s'", error->message);
+ else {
+ /* Disable access technology and signal quality polling if we can use the indications */
+ mm_obj_dbg (self, "signal strength indications enabled: polling disabled");
+ g_object_set (self,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, TRUE,
+ MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, TRUE,
+ NULL);
}
- if (output)
- qmi_message_nas_register_indications_output_unref (output);
+ if (!ctx->client_wds) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
- /* Just ignore errors for now */
- ctx->self->priv->unsolicited_events_enabled = ctx->enable;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_unsolicited_events_context_complete_and_free (ctx);
+ common_enable_disable_unsolicited_events_data_system_status (task);
}
static void
-common_enable_disable_unsolicited_events_signal_info (EnableUnsolicitedEventsContext *ctx)
+common_enable_disable_unsolicited_events_signal_info (GTask *task)
{
- QmiMessageNasRegisterIndicationsInput *input;
+ EnableUnsolicitedEventsContext *ctx;
+ g_autoptr(QmiMessageNasRegisterIndicationsInput) input = NULL;
+ ctx = g_task_get_task_data (task);
input = qmi_message_nas_register_indications_input_new ();
qmi_message_nas_register_indications_input_set_signal_info (input, ctx->enable, NULL);
qmi_client_nas_register_indications (
- ctx->client,
+ ctx->client_nas,
input,
5,
NULL,
(GAsyncReadyCallback)ri_signal_info_ready,
- ctx);
- qmi_message_nas_register_indications_input_unref (input);
+ task);
}
static void
config_signal_info_ready (QmiClientNas *client,
GAsyncResult *res,
- EnableUnsolicitedEventsContext *ctx)
+ GTask *task)
{
- QmiMessageNasConfigSignalInfoOutput *output = NULL;
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ g_autoptr(QmiMessageNasConfigSignalInfoOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
- output = qmi_client_nas_config_signal_info_finish (client, res, &error);
- if (!output) {
- mm_dbg ("QMI operation failed: '%s'", error->message);
- g_error_free (error);
- } else if (!qmi_message_nas_config_signal_info_output_get_result (output, &error)) {
- mm_dbg ("Couldn't config signal info: '%s'", error->message);
- g_error_free (error);
- }
+ self = g_task_get_source_object (task);
- if (output)
- qmi_message_nas_config_signal_info_output_unref (output);
+ output = qmi_client_nas_config_signal_info_finish (client, res, &error);
+ if (!output || !qmi_message_nas_config_signal_info_output_get_result (output, &error))
+ mm_obj_dbg (self, "couldn't config signal info: '%s'", error->message);
/* Keep on */
- common_enable_disable_unsolicited_events_signal_info (ctx);
+ common_enable_disable_unsolicited_events_signal_info (task);
}
static void
-common_enable_disable_unsolicited_events_signal_info_config (EnableUnsolicitedEventsContext *ctx)
+common_enable_disable_unsolicited_events_signal_info_config (GTask *task)
{
- /* RSSI values go between -105 and -60 for 3GPP technologies,
- * and from -105 to -90 in 3GPP2 technologies (approx). */
- static const gint8 thresholds_data[] = { -100, -97, -95, -92, -90, -85, -80, -75, -70, -65 };
- QmiMessageNasConfigSignalInfoInput *input;
- GArray *thresholds;
+ EnableUnsolicitedEventsContext *ctx;
+ g_autoptr(QmiMessageNasConfigSignalInfoInput) input = NULL;
+
+ ctx = g_task_get_task_data (task);
/* Signal info config only to be run when enabling */
if (!ctx->enable) {
- common_enable_disable_unsolicited_events_signal_info (ctx);
+ common_enable_disable_unsolicited_events_signal_info (task);
return;
}
input = qmi_message_nas_config_signal_info_input_new ();
/* Prepare thresholds, separated 20 each */
- thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data));
- g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
+ {
+ /* RSSI values go between -105 and -60 for 3GPP technologies,
+ * and from -105 to -90 in 3GPP2 technologies (approx). */
+ static const gint8 thresholds_data[] = { -100, -97, -95, -92, -90, -85, -80, -75, -70, -65 };
+ g_autoptr(GArray) thresholds = NULL;
+
+ thresholds = g_array_sized_new (FALSE, FALSE, sizeof (gint8), G_N_ELEMENTS (thresholds_data));
+ g_array_append_vals (thresholds, thresholds_data, G_N_ELEMENTS (thresholds_data));
+ qmi_message_nas_config_signal_info_input_set_rssi_threshold (
+ input,
+ thresholds,
+ NULL);
+ }
- qmi_message_nas_config_signal_info_input_set_rssi_threshold (
- input,
- thresholds,
- NULL);
- g_array_unref (thresholds);
qmi_client_nas_config_signal_info (
- ctx->client,
+ ctx->client_nas,
input,
5,
NULL,
(GAsyncReadyCallback)config_signal_info_ready,
- ctx);
- qmi_message_nas_config_signal_info_input_unref (input);
+ task);
}
#endif /* WITH_NEWEST_QMI_COMMANDS */
static void
common_enable_disable_unsolicited_events (MMBroadbandModemQmi *self,
- gboolean enable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
EnableUnsolicitedEventsContext *ctx;
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
+ GTask *task;
+ QmiClient *client_nas = NULL;
+ QmiClient *client_wds = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_enable_disable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
if (enable == self->priv->unsolicited_events_enabled) {
- mm_dbg ("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);
+ mm_obj_dbg (self, "unsolicited events already %s; skipping",
+ enable ? "enabled" : "disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
+ self->priv->unsolicited_events_enabled = enable;
+
+ client_nas = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ client_wds = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
ctx = g_new0 (EnableUnsolicitedEventsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
ctx->enable = enable;
- ctx->result = result;
+ ctx->client_nas = client_nas ? QMI_CLIENT_NAS (g_object_ref (client_nas)) : NULL;
+ ctx->client_wds = client_wds ? QMI_CLIENT_WDS (g_object_ref (client_wds)) : NULL;
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enable_unsolicited_events_context_free);
+ if (ctx->client_nas) {
#if defined WITH_NEWEST_QMI_COMMANDS
- /* Signal info introduced in NAS 1.8 */
- if (qmi_client_check_version (client, 1, 8)) {
- common_enable_disable_unsolicited_events_signal_info_config (ctx);
+ common_enable_disable_unsolicited_events_signal_info_config (task);
+#else
+ common_enable_disable_unsolicited_events_signal_strength (task);
+#endif /* WITH_NEWEST_QMI_COMMANDS */
+ return;
+ }
+
+ if (ctx->client_wds) {
+ common_enable_disable_unsolicited_events_data_system_status (task);
return;
}
-#endif /* WITH_NEWEST_QMI_COMMANDS */
- common_enable_disable_unsolicited_events_signal_strength (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -6303,13 +5336,30 @@ common_setup_cleanup_unsolicited_events_finish (MMBroadbandModemQmi *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-event_report_indication_cb (QmiClientNas *client,
- QmiIndicationNasEventReportOutput *output,
- MMBroadbandModemQmi *self)
+wds_event_report_indication_cb (QmiClientWds *client,
+ QmiIndicationWdsEventReportOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiWdsDataSystemNetworkType preferred_network;
+
+ if (qmi_indication_wds_event_report_output_get_data_systems (output, &preferred_network, NULL, NULL)) {
+ mm_obj_dbg (self, "data systems update, preferred network: %s",
+ qmi_wds_data_system_network_type_get_string (preferred_network));
+ if (preferred_network == QMI_WDS_DATA_SYSTEM_NETWORK_TYPE_3GPP)
+ mm_iface_modem_3gpp_reload_initial_eps_bearer (MM_IFACE_MODEM_3GPP (self));
+ else
+ mm_iface_modem_3gpp_update_initial_eps_bearer (MM_IFACE_MODEM_3GPP (self), NULL);
+ }
+}
+
+static void
+nas_event_report_indication_cb (QmiClientNas *client,
+ QmiIndicationNasEventReportOutput *output,
+ MMBroadbandModemQmi *self)
{
gint8 signal_strength;
QmiNasRadioInterface signal_strength_radio_interface;
@@ -6323,12 +5373,12 @@ event_report_indication_cb (QmiClientNas *client,
guint8 quality;
/* This signal strength comes as negative dBms */
- quality = STRENGTH_TO_QUALITY (signal_strength);
+ quality = MM_RSSI_TO_QUALITY (signal_strength);
- mm_dbg ("Signal strength indication (%s): %d dBm --> %u%%",
- qmi_nas_radio_interface_get_string (signal_strength_radio_interface),
- signal_strength,
- quality);
+ mm_obj_dbg (self, "signal strength indication (%s): %d dBm --> %u%%",
+ qmi_nas_radio_interface_get_string (signal_strength_radio_interface),
+ signal_strength,
+ quality);
mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
mm_iface_modem_update_access_technologies (
@@ -6336,9 +5386,9 @@ event_report_indication_cb (QmiClientNas *client,
mm_modem_access_technology_from_qmi_radio_interface (signal_strength_radio_interface),
(MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK | MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK));
} else {
- mm_dbg ("Ignoring invalid signal strength (%s): %d dBm",
- qmi_nas_radio_interface_get_string (signal_strength_radio_interface),
- signal_strength);
+ mm_obj_dbg (self, "ignoring invalid signal strength (%s): %d dBm",
+ qmi_nas_radio_interface_get_string (signal_strength_radio_interface),
+ signal_strength);
}
}
}
@@ -6346,59 +5396,38 @@ event_report_indication_cb (QmiClientNas *client,
#if defined WITH_NEWEST_QMI_COMMANDS
static void
-signal_info_indication_cb (QmiClientNas *client,
- QmiIndicationNasSignalInfoOutput *output,
- MMBroadbandModemQmi *self)
+nas_signal_info_indication_cb (QmiClientNas *client,
+ QmiIndicationNasSignalInfoOutput *output,
+ MMBroadbandModemQmi *self)
{
- gint8 rssi_max = -125;
- gint8 rssi;
+ gint8 cdma1x_rssi = 0;
+ gint8 evdo_rssi = 0;
+ gint8 gsm_rssi = 0;
+ gint8 wcdma_rssi = 0;
+ gint8 lte_rssi = 0;
guint8 quality;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- /* We do not report per-technology signal quality, so just get the highest
- * one of the ones reported. TODO: When several technologies are in use, if
- * the indication only contains the data of the one which passed a threshold
- * value, we'll need to have an internal cache of per-technology values, in
- * order to report always the one with the maximum value. */
-
- if (qmi_indication_nas_signal_info_output_get_cdma_signal_strength (output, &rssi, NULL, NULL)) {
- mm_dbg ("RSSI (CDMA): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1X))
- rssi_max = MAX (rssi, rssi_max);
- }
-
- if (qmi_indication_nas_signal_info_output_get_hdr_signal_strength (output, &rssi, NULL, NULL, NULL, NULL)) {
- mm_dbg ("RSSI (HDR): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO))
- rssi_max = MAX (rssi, rssi_max);
- }
-
- if (qmi_indication_nas_signal_info_output_get_gsm_signal_strength (output, &rssi, NULL)) {
- mm_dbg ("RSSI (GSM): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_GSM))
- rssi_max = MAX (rssi, rssi_max);
- }
-
- if (qmi_indication_nas_signal_info_output_get_wcdma_signal_strength (output, &rssi, NULL, NULL)) {
- mm_dbg ("RSSI (WCDMA): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_UMTS))
- rssi_max = MAX (rssi, rssi_max);
- }
-
- if (qmi_indication_nas_signal_info_output_get_lte_signal_strength (output, &rssi, NULL, NULL, NULL, NULL)) {
- mm_dbg ("RSSI (LTE): %d dBm", rssi);
- if (qmi_dbm_valid (rssi, QMI_NAS_RADIO_INTERFACE_LTE))
- rssi_max = MAX (rssi, rssi_max);
- }
-
- if (rssi_max < 0) {
- /* This RSSI comes as negative dBms */
- quality = STRENGTH_TO_QUALITY (rssi_max);
-
- mm_dbg ("RSSI: %d dBm --> %u%%", rssi_max, quality);
-
+ qmi_indication_nas_signal_info_output_get_cdma_signal_strength (output, &cdma1x_rssi, NULL, NULL);
+ qmi_indication_nas_signal_info_output_get_hdr_signal_strength (output, &evdo_rssi, NULL, NULL, NULL, NULL);
+ qmi_indication_nas_signal_info_output_get_gsm_signal_strength (output, &gsm_rssi, NULL);
+ qmi_indication_nas_signal_info_output_get_wcdma_signal_strength (output, &wcdma_rssi, NULL, NULL);
+ qmi_indication_nas_signal_info_output_get_lte_signal_strength (output, &lte_rssi, NULL, NULL, NULL, NULL);
+
+ if (common_signal_info_get_quality (self,
+ cdma1x_rssi,
+ evdo_rssi,
+ gsm_rssi,
+ wcdma_rssi,
+ lte_rssi,
+ &quality,
+ &act)) {
mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality);
- } else
- mm_dbg ("Ignoring invalid signal strength: %d dBm", rssi_max);
+ mm_iface_modem_update_access_technologies (
+ MM_IFACE_MODEM (self),
+ act,
+ (MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK | MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK));
+ }
}
#endif /* WITH_NEWEST_QMI_COMMANDS */
@@ -6409,67 +5438,75 @@ common_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
+ GTask *task;
+ QmiClient *client_nas = NULL;
+ QmiClient *client_wds = NULL;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_enable_disable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
if (enable == self->priv->unsolicited_events_setup) {
- mm_dbg ("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);
+ mm_obj_dbg (self, "unsolicited events already %s; skipping",
+ enable ? "setup" : "cleanup");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
-
- /* Store new state */
self->priv->unsolicited_events_setup = enable;
+ client_nas = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ client_wds = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+
/* Connect/Disconnect "Event Report" indications */
- if (enable) {
- g_assert (self->priv->event_report_indication_id == 0);
- self->priv->event_report_indication_id =
- g_signal_connect (client,
- "event-report",
- G_CALLBACK (event_report_indication_cb),
- self);
- } else {
- g_assert (self->priv->event_report_indication_id != 0);
- g_signal_handler_disconnect (client, self->priv->event_report_indication_id);
- self->priv->event_report_indication_id = 0;
- }
+ if (client_nas) {
+ if (enable) {
+ g_assert (self->priv->nas_event_report_indication_id == 0);
+ self->priv->nas_event_report_indication_id =
+ g_signal_connect (client_nas,
+ "event-report",
+ G_CALLBACK (nas_event_report_indication_cb),
+ self);
+ } else if (self->priv->nas_event_report_indication_id != 0) {
+ g_signal_handler_disconnect (client_nas, self->priv->nas_event_report_indication_id);
+ self->priv->nas_event_report_indication_id = 0;
+ }
#if defined WITH_NEWEST_QMI_COMMANDS
- /* Connect/Disconnect "Signal Info" indications.
- * Signal info introduced in NAS 1.8 */
- if (qmi_client_check_version (client, 1, 8)) {
if (enable) {
- g_assert (self->priv->signal_info_indication_id == 0);
- self->priv->signal_info_indication_id =
- g_signal_connect (client,
+ g_assert (self->priv->nas_signal_info_indication_id == 0);
+ self->priv->nas_signal_info_indication_id =
+ g_signal_connect (client_nas,
"signal-info",
- G_CALLBACK (signal_info_indication_cb),
+ G_CALLBACK (nas_signal_info_indication_cb),
self);
- } else {
- g_assert (self->priv->signal_info_indication_id != 0);
- g_signal_handler_disconnect (client, self->priv->signal_info_indication_id);
- self->priv->signal_info_indication_id = 0;
+ } else if (self->priv->nas_signal_info_indication_id != 0) {
+ g_signal_handler_disconnect (client_nas, self->priv->nas_signal_info_indication_id);
+ self->priv->nas_signal_info_indication_id = 0;
}
- }
#endif /* WITH_NEWEST_QMI_COMMANDS */
+ }
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (client_wds) {
+ if (enable) {
+ g_assert (self->priv->wds_event_report_indication_id == 0);
+ self->priv->wds_event_report_indication_id =
+ g_signal_connect (client_wds,
+ "event-report",
+ G_CALLBACK (wds_event_report_indication_cb),
+ self);
+ } else if (self->priv->wds_event_report_indication_id != 0) {
+ g_signal_handler_disconnect (client_wds, self->priv->wds_event_report_indication_id);
+ self->priv->wds_event_report_indication_id = 0;
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -6539,6 +5576,632 @@ modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self,
}
/*****************************************************************************/
+/* Check format (3gppProfileManager interface) */
+
+static gboolean
+modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *new_id,
+ gint *min_profile_id,
+ gint *max_profile_id,
+ GEqualFunc *apn_cmp,
+ MM3gppProfileCmpFlags *profile_cmp_flags,
+ GError **error)
+{
+ if (!g_task_propagate_boolean (G_TASK (res), error)) {
+ g_assert_not_reached ();
+ return FALSE;
+ }
+ /* Generic WDS Create Profile method does NOT allow specifying a specific
+ * profile id */
+ if (new_id)
+ *new_id = FALSE;
+ if (min_profile_id)
+ *min_profile_id = 1;
+ if (max_profile_id)
+ *max_profile_id = G_MAXINT - 1;
+ /* use default string comparison method */
+ if (apn_cmp)
+ *apn_cmp = NULL;
+ /* we support everything! */
+ if (profile_cmp_flags)
+ *profile_cmp_flags = 0;
+ return TRUE;
+}
+
+static void
+modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self,
+ MMBearerIpFamily ip_type,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Get profile (3GPP profile management interface) */
+
+static MM3gppProfile *
+modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static MM3gppProfile *
+wds_profile_settings_to_3gpp_profile (MMBroadbandModemQmi *self,
+ guint profile_index,
+ QmiMessageWdsGetProfileSettingsOutput *output,
+ GError **error)
+{
+ MM3gppProfile *profile = NULL;
+ const gchar *str;
+ QmiWdsPdpType pdp_type;
+ QmiWdsAuthentication auth;
+ QmiWdsApnTypeMask apn_type;
+
+ profile = mm_3gpp_profile_new ();
+
+ /* On 3GPP modems, the modem seems to force profile-index = pdp-context-number,
+ * and so, we're just going to rely on the profile-index ourselves.*/
+ mm_3gpp_profile_set_profile_id (profile, (gint) profile_index);
+
+ if (qmi_message_wds_get_profile_settings_output_get_apn_name (output, &str, NULL))
+ mm_3gpp_profile_set_apn (profile, str);
+
+ if (qmi_message_wds_get_profile_settings_output_get_profile_name (output, &str, NULL))
+ mm_3gpp_profile_set_profile_name (profile, str);
+
+ if (qmi_message_wds_get_profile_settings_output_get_pdp_type (output, &pdp_type, NULL))
+ mm_3gpp_profile_set_ip_type (profile, mm_bearer_ip_family_from_qmi_pdp_type (pdp_type));
+
+ if (qmi_message_wds_get_profile_settings_output_get_authentication (output, &auth, NULL))
+ mm_3gpp_profile_set_allowed_auth (profile, mm_bearer_allowed_auth_from_qmi_authentication (auth));
+
+ /* ignore empty user/pass strings */
+ if (qmi_message_wds_get_profile_settings_output_get_username (output, &str, NULL) && str[0])
+ mm_3gpp_profile_set_user (profile, str);
+ if (qmi_message_wds_get_profile_settings_output_get_password (output, &str, NULL) && str[0])
+ mm_3gpp_profile_set_password (profile, str);
+
+ /* If loading APN type TLV fails, flag it as unsupported so that we don't try to use it any
+ * more. */
+ if (qmi_message_wds_get_profile_settings_output_get_apn_type_mask (output, &apn_type, NULL))
+ mm_3gpp_profile_set_apn_type (profile, mm_bearer_apn_type_from_qmi_apn_type (apn_type));
+ else if (!self->priv->apn_type_not_supported) {
+ mm_obj_dbg (self, "APN type flagged as not supported: not given in profile settings");
+ self->priv->apn_type_not_supported = TRUE;
+ }
+
+ return profile;
+}
+
+static void
+get_profile_settings_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ GError *error = NULL;
+ gint profile_id;
+ gboolean profile_disabled = FALSE;
+ MM3gppProfile *profile;
+ g_autoptr(QmiMessageWdsGetProfileSettingsOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+ profile_id = GPOINTER_TO_INT (g_task_get_task_data (task));
+
+ output = qmi_client_wds_get_profile_settings_finish (client, res, &error);
+ if (!output || !qmi_message_wds_get_profile_settings_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't load settings from profile index %u: ", profile_id);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* just ignore the profile if it's disabled */
+ qmi_message_wds_get_profile_settings_output_get_apn_disabled_flag (output, &profile_disabled, NULL);
+ if (profile_disabled) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Profile '%d' is internally disabled", profile_id);
+ g_object_unref (task);
+ return;
+ }
+
+ profile = wds_profile_settings_to_3gpp_profile (self, profile_id, output, &error);
+ if (!profile)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, profile, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+ g_autoptr(QmiMessageWdsGetProfileSettingsInput) input = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL);
+
+ input = qmi_message_wds_get_profile_settings_input_new ();
+ qmi_message_wds_get_profile_settings_input_set_profile_id (
+ input,
+ QMI_WDS_PROFILE_TYPE_3GPP,
+ profile_id,
+ NULL);
+ qmi_client_wds_get_profile_settings (QMI_CLIENT_WDS (client),
+ input,
+ 3,
+ NULL,
+ (GAsyncReadyCallback)get_profile_settings_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* List profiles (3GPP profile management interface) */
+
+typedef struct {
+ GList *profiles;
+ GArray *qmi_profiles;
+ guint i;
+} ListProfilesContext;
+
+static void
+list_profiles_context_free (ListProfilesContext *ctx)
+{
+ g_clear_pointer (&ctx->qmi_profiles, (GDestroyNotify)g_array_unref);
+ mm_3gpp_profile_list_free (ctx->profiles);
+ g_slice_free (ListProfilesContext, ctx);
+}
+
+static gboolean
+modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **out_profiles,
+ GError **error)
+{
+ ListProfilesContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (out_profiles)
+ *out_profiles = g_steal_pointer (&ctx->profiles);
+ return TRUE;
+}
+
+static void get_next_profile_settings (GTask *task);
+
+static void
+get_next_profile_settings_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ MM3gppProfile *profile;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ profile = modem_3gpp_profile_manager_get_profile_finish (self, res, &error);
+ if (!profile) {
+ g_prefix_error (&error, "Couldn't load settings from profile index %u: ", ctx->i);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->profiles = g_list_append (ctx->profiles, profile);
+
+ /* Keep on */
+ ctx->i++;
+ get_next_profile_settings (task);
+}
+
+static void
+get_next_profile_settings (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ ListProfilesContext *ctx;
+ QmiMessageWdsGetProfileListOutputProfileListProfile *current;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->i >= ctx->qmi_profiles->len) {
+ /* All done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ current = &g_array_index (ctx->qmi_profiles, QmiMessageWdsGetProfileListOutputProfileListProfile, ctx->i);
+ modem_3gpp_profile_manager_get_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ current->profile_index,
+ (GAsyncReadyCallback)get_next_profile_settings_ready,
+ task);
+}
+
+static void
+get_profile_list_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ GError *error = NULL;
+ GArray *qmi_profiles = NULL;
+ g_autoptr(QmiMessageWdsGetProfileListOutput) output = NULL;
+
+ ctx = g_slice_new0 (ListProfilesContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free);
+
+ output = qmi_client_wds_get_profile_list_finish (client, res, &error);
+ if (!output || !qmi_message_wds_get_profile_list_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ qmi_message_wds_get_profile_list_output_get_profile_list (output, &qmi_profiles, NULL);
+
+ /* empty list? */
+ if (!qmi_profiles || !qmi_profiles->len) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->qmi_profiles = g_array_ref (qmi_profiles);
+ get_next_profile_settings (task);
+}
+
+static void
+modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+ g_autoptr(QmiMessageWdsGetProfileListInput) input = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ input = qmi_message_wds_get_profile_list_input_new ();
+ qmi_message_wds_get_profile_list_input_set_profile_type (input, QMI_WDS_PROFILE_TYPE_3GPP, NULL);
+
+ qmi_client_wds_get_profile_list (QMI_CLIENT_WDS (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_profile_list_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Store profile (3GPP profile management interface) */
+
+typedef struct {
+ QmiClientWds *client;
+ gint profile_id;
+ gchar *profile_name;
+ gchar *apn;
+ gchar *user;
+ gchar *password;
+ QmiWdsApnTypeMask qmi_apn_type;
+ QmiWdsAuthentication qmi_auth;
+ QmiWdsPdpType qmi_pdp_type;
+} StoreProfileContext;
+
+static void
+store_profile_context_free (StoreProfileContext *ctx)
+{
+ g_free (ctx->profile_name);
+ g_free (ctx->apn);
+ g_free (ctx->user);
+ g_free (ctx->password);
+ g_clear_object (&ctx->client);
+ g_slice_free (StoreProfileContext, ctx);
+}
+
+static gint
+modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ StoreProfileContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return MM_3GPP_PROFILE_ID_UNKNOWN;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ return ctx->profile_id;
+}
+
+static void store_profile_run (GTask *task);
+
+static void
+modify_profile_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ GError *error = NULL;
+ g_autoptr(QmiMessageWdsModifyProfileOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_wds_modify_profile_finish (client, res, &error);
+ if (!output) {
+ g_task_return_error (task, error);
+ } else if (!qmi_message_wds_modify_profile_output_get_result (output, &error)) {
+ QmiWdsDsProfileError ds_profile_error;
+
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE) &&
+ !self->priv->apn_type_not_supported) {
+ /* we'll retry the operation without APN type, which is a setting available only
+ * in newer devices. */
+ mm_obj_dbg (self, "APN type flagged as not supported: failed to modify profile");
+ self->priv->apn_type_not_supported = TRUE;
+ g_clear_error (&error);
+ store_profile_run (task);
+ return;
+ }
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
+ qmi_message_wds_modify_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) {
+ g_prefix_error (&error, "DS profile error: %s: ", qmi_wds_ds_profile_error_get_string (ds_profile_error));
+ }
+ g_prefix_error (&error, "Couldn't modify profile: ");
+ g_task_return_error (task, error);
+ } else {
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+create_profile_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ StoreProfileContext *ctx;
+ GError *error = NULL;
+ guint8 profile_index;
+ g_autoptr(QmiMessageWdsCreateProfileOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_wds_create_profile_finish (client, res, &error);
+ if (!output) {
+ g_task_return_error (task, error);
+ } else if (!qmi_message_wds_create_profile_output_get_result (output, &error)) {
+ QmiWdsDsProfileError ds_profile_error;
+
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_PROFILE_TYPE) &&
+ !self->priv->apn_type_not_supported) {
+ /* we'll retry the operation without APN type, which is a setting available only
+ * in newer devices. */
+ mm_obj_dbg (self, "APN type flagged as not supported: failed to create profile");
+ self->priv->apn_type_not_supported = TRUE;
+ g_clear_error (&error);
+ store_profile_run (task);
+ return;
+ }
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_EXTENDED_INTERNAL) &&
+ qmi_message_wds_create_profile_output_get_extended_error_code (output, &ds_profile_error, NULL)) {
+ g_prefix_error (&error, "DS profile error: %s: ", qmi_wds_ds_profile_error_get_string (ds_profile_error));
+ }
+ g_prefix_error (&error, "Couldn't create profile: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_wds_create_profile_output_get_profile_identifier (output, NULL, &profile_index, &error)) {
+ g_task_return_error (task, error);
+ } else {
+ ctx->profile_id = profile_index;
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+store_profile_run (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ StoreProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_autoptr(QmiMessageWdsCreateProfileInput) input = NULL;
+
+ /* when creating, we cannot select which profile id to use */
+ input = qmi_message_wds_create_profile_input_new ();
+ qmi_message_wds_create_profile_input_set_profile_type (input, QMI_WDS_PROFILE_TYPE_3GPP, NULL);
+ qmi_message_wds_create_profile_input_set_profile_name (input, ctx->profile_name, NULL);
+ qmi_message_wds_create_profile_input_set_pdp_type (input, ctx->qmi_pdp_type, NULL);
+ qmi_message_wds_create_profile_input_set_apn_name (input, ctx->apn, NULL);
+ qmi_message_wds_create_profile_input_set_authentication (input, ctx->qmi_auth, NULL);
+ qmi_message_wds_create_profile_input_set_username (input, ctx->user, NULL);
+ qmi_message_wds_create_profile_input_set_password (input, ctx->password, NULL);
+ if (!self->priv->apn_type_not_supported)
+ qmi_message_wds_create_profile_input_set_apn_type_mask (input, ctx->qmi_apn_type, NULL);
+
+ qmi_client_wds_create_profile (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)create_profile_ready,
+ task);
+ } else {
+ g_autoptr(QmiMessageWdsModifyProfileInput) input = NULL;
+
+ input = qmi_message_wds_modify_profile_input_new ();
+ qmi_message_wds_modify_profile_input_set_profile_identifier (input, QMI_WDS_PROFILE_TYPE_3GPP, ctx->profile_id, NULL);
+ qmi_message_wds_modify_profile_input_set_profile_name (input, ctx->profile_name, NULL);
+ qmi_message_wds_modify_profile_input_set_pdp_type (input, ctx->qmi_pdp_type, NULL);
+ qmi_message_wds_modify_profile_input_set_apn_name (input, ctx->apn, NULL);
+ qmi_message_wds_modify_profile_input_set_authentication (input, ctx->qmi_auth, NULL);
+ qmi_message_wds_modify_profile_input_set_username (input, ctx->user, NULL);
+ qmi_message_wds_modify_profile_input_set_password (input, ctx->password, NULL);
+ if (!self->priv->apn_type_not_supported)
+ qmi_message_wds_modify_profile_input_set_apn_type_mask (input, ctx->qmi_apn_type, NULL);
+
+ qmi_client_wds_modify_profile (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)modify_profile_ready,
+ task);
+ }
+}
+
+static void
+modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ StoreProfileContext *ctx;
+ QmiClient *client = NULL;
+ MMBearerIpFamily ip_type;
+ MMBearerApnType apn_type;
+ MMBearerAllowedAuth allowed_auth;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (StoreProfileContext);
+ ctx->client = QMI_CLIENT_WDS (g_object_ref (client));
+ g_task_set_task_data (task, ctx, (GDestroyNotify)store_profile_context_free);
+
+ /* Note: may be UNKNOWN */
+ ctx->profile_id = mm_3gpp_profile_get_profile_id (profile);
+
+ ctx->profile_name = g_strdup (mm_3gpp_profile_get_profile_name (profile));
+
+ ctx->apn = g_strdup (mm_3gpp_profile_get_apn (profile));
+
+ apn_type = mm_3gpp_profile_get_apn_type (profile);
+ ctx->qmi_apn_type = mm_bearer_apn_type_to_qmi_apn_type (apn_type, self);
+
+ ctx->user = g_strdup (mm_3gpp_profile_get_user (profile));
+ ctx->password = g_strdup (mm_3gpp_profile_get_password (profile));
+ allowed_auth = mm_3gpp_profile_get_allowed_auth (profile);
+ if ((allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) || ctx->user || ctx->password) {
+ GError *error = NULL;
+
+ ctx->qmi_auth = mm_bearer_allowed_auth_to_qmi_authentication (allowed_auth, self, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ ip_type = mm_3gpp_profile_get_ip_type (profile);
+ if (!mm_bearer_ip_family_to_qmi_pdp_type (ip_type, &ctx->qmi_pdp_type)) {
+ g_autofree gchar *ip_type_str = NULL;
+
+ ip_type_str = mm_bearer_ip_family_build_string_from_mask (ip_type);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid IP type specified: %s", ip_type_str);
+ g_object_unref (task);
+ return;
+ }
+
+ store_profile_run (task);
+}
+
+/*****************************************************************************/
+/* Delete profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+delete_profile_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(QmiMessageWdsDeleteProfileOutput) output = NULL;
+
+ output = qmi_client_wds_delete_profile_finish (client, res, &error);
+ if (!output || !qmi_message_wds_delete_profile_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't delete profile: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+ gint profile_id;
+ g_autoptr(QmiMessageWdsDeleteProfileInput) input = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ mm_obj_dbg (self, "deleting profile '%d'", profile_id);
+
+ input = qmi_message_wds_delete_profile_input_new ();
+ qmi_message_wds_delete_profile_input_set_profile_identifier (input, QMI_WDS_PROFILE_TYPE_3GPP, profile_id, NULL);
+
+ qmi_client_wds_delete_profile (QMI_CLIENT_WDS (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)delete_profile_ready,
+ task);
+}
+
+/*****************************************************************************/
/* Check support (Messaging interface) */
static gboolean
@@ -6546,22 +6209,20 @@ messaging_check_support_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- /* no error expected here */
- return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
parent_messaging_check_support_ready (MMIfaceModemMessaging *_self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- self->priv->messaging_fallback_at = iface_modem_messaging_parent->check_support_finish (_self, res, NULL);
+ self->priv->messaging_fallback_at_only = iface_modem_messaging_parent->check_support_finish (_self, res, NULL);
- g_simple_async_result_set_op_res_gboolean (simple, self->priv->messaging_fallback_at);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, self->priv->messaging_fallback_at_only);
+ g_object_unref (task);
}
static void
@@ -6569,29 +6230,26 @@ messaging_check_support (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- MMPortQmi *port;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
- port = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
/* If we have support for the WMS client, messaging is supported */
- if (!port || !mm_port_qmi_peek_client (port, QMI_SERVICE_WMS, MM_PORT_QMI_FLAG_DEFAULT)) {
+ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL)) {
/* Try to fallback to AT support */
iface_modem_messaging_parent->check_support (
self,
(GAsyncReadyCallback)parent_messaging_check_support_ready,
- result);
+ task);
return;
}
- 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);
+ mm_obj_dbg (self, "messaging capabilities supported");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -6608,11 +6266,13 @@ messaging_load_supported_storages_finish (MMIfaceModemMessaging *_self,
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
MMSmsStorage supported;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->load_supported_storages_finish (_self, res, mem1, mem2, mem3, error);
}
+ g_assert (g_task_propagate_boolean (G_TASK (res), NULL));
+
*mem1 = g_array_sized_new (FALSE, FALSE, sizeof (MMSmsStorage), 2);
/* Add SM storage only if not CDMA-only */
if (!mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
@@ -6632,21 +6292,17 @@ messaging_load_supported_storages (MMIfaceModemMessaging *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
iface_modem_messaging_parent->load_supported_storages (_self, callback, user_data);
return;
}
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_load_supported_storages);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -6659,12 +6315,12 @@ modem_messaging_setup_sms_format_finish (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->setup_sms_format_finish (_self, res, error);
}
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -6673,21 +6329,17 @@ modem_messaging_setup_sms_format (MMIfaceModemMessaging *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->setup_sms_format (_self, callback, user_data);
}
/* noop */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_messaging_setup_sms_format);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -6700,18 +6352,18 @@ messaging_set_default_storage_finish (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->set_default_storage_finish (_self, res, error);
}
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);;
}
static void
wms_set_routes_ready (QmiClientWms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageWmsSetRoutesOutput *output = NULL;
GError *error = NULL;
@@ -6719,19 +6371,17 @@ wms_set_routes_ready (QmiClientWms *client,
output = qmi_client_wms_set_routes_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_wms_set_routes_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't set routes: ");
- g_simple_async_result_take_error (simple, error);
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
if (output)
qmi_message_wms_set_routes_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -6741,28 +6391,22 @@ messaging_set_default_storage (MMIfaceModemMessaging *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
QmiMessageWmsSetRoutesInput *input;
GArray *routes_array;
QmiMessageWmsSetRoutesInputRouteListElement route;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
iface_modem_messaging_parent->set_default_storage (_self, storage, callback, user_data);
return;
}
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_WMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- messaging_set_default_storage);
-
/* Build routes array and add it as input
* Just worry about Class 0 and Class 1 messages for now */
input = qmi_message_wms_set_routes_input_new ();
@@ -6776,13 +6420,13 @@ messaging_set_default_storage (MMIfaceModemMessaging *_self,
g_array_append_val (routes_array, route);
qmi_message_wms_set_routes_input_set_route_list (input, routes_array, NULL);
- mm_dbg ("setting default messaging routes...");
+ mm_obj_dbg (self, "setting default messaging routes...");
qmi_client_wms_set_routes (QMI_CLIENT_WMS (client),
input,
5,
NULL,
(GAsyncReadyCallback)wms_set_routes_ready,
- result);
+ g_task_new (self, NULL, callback, user_data));
qmi_message_wms_set_routes_input_unref (input);
g_array_unref (routes_array);
@@ -6811,8 +6455,6 @@ typedef enum {
} LoadInitialSmsPartsStep;
typedef struct {
- MMBroadbandModemQmi *self;
- GSimpleAsyncResult *result;
QmiClientWms *client;
MMSmsStorage storage;
LoadInitialSmsPartsStep step;
@@ -6823,16 +6465,12 @@ typedef struct {
} LoadInitialSmsPartsContext;
static void
-load_initial_sms_parts_context_complete_and_free (LoadInitialSmsPartsContext *ctx)
+load_initial_sms_parts_context_free (LoadInitialSmsPartsContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
-
if (ctx->message_array)
g_array_unref (ctx->message_array);
g_object_unref (ctx->client);
- g_object_unref (ctx->self);
g_slice_free (LoadInitialSmsPartsContext, ctx);
}
@@ -6843,15 +6481,15 @@ load_initial_sms_parts_finish (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->load_initial_sms_parts_finish (_self, res, error);
}
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);;
}
-static void read_next_sms_part (LoadInitialSmsPartsContext *ctx);
+static void read_next_sms_part (GTask *task);
static void
add_new_read_sms_part (MMIfaceModemMessaging *self,
@@ -6859,6 +6497,7 @@ add_new_read_sms_part (MMIfaceModemMessaging *self,
guint32 index,
QmiWmsMessageTagType tag,
QmiWmsMessageFormat format,
+ gboolean transfer_route,
GArray *data)
{
MMSmsPart *part = NULL;
@@ -6869,6 +6508,7 @@ add_new_read_sms_part (MMIfaceModemMessaging *self,
part = mm_sms_part_cdma_new_from_binary_pdu (index,
(guint8 *)data->data,
data->len,
+ self,
&error);
break;
@@ -6877,25 +6517,27 @@ add_new_read_sms_part (MMIfaceModemMessaging *self,
part = mm_sms_part_3gpp_new_from_binary_pdu (index,
(guint8 *)data->data,
data->len,
+ self,
+ transfer_route,
&error);
break;
case QMI_WMS_MESSAGE_FORMAT_MWI:
- mm_dbg ("Don't know how to process 'message waiting indicator' messages");
+ mm_obj_dbg (self, "don't know how to process 'message waiting indicator' messages");
break;
default:
- mm_dbg ("Unhandled message format '%u'", format);
+ mm_obj_dbg (self, "unhandled message format '%u'", format);
break;
}
if (part) {
- mm_dbg ("Correctly parsed PDU (%d)", index);
+ mm_obj_dbg (self, "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);
+ mm_obj_dbg (self, "error parsing PDU (%d): %s", index, error->message);
g_error_free (error);
}
}
@@ -6903,19 +6545,24 @@ add_new_read_sms_part (MMIfaceModemMessaging *self,
static void
wms_raw_read_ready (QmiClientWms *client,
GAsyncResult *res,
- LoadInitialSmsPartsContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ LoadInitialSmsPartsContext *ctx;
QmiMessageWmsRawReadOutput *output = NULL;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Ignore errors, just keep on with the next messages */
output = qmi_client_wms_raw_read_finish (client, res, &error);
if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
+ mm_obj_dbg (self, "QMI operation failed: %s", error->message);
g_error_free (error);
} else if (!qmi_message_wms_raw_read_output_get_result (output, &error)) {
- mm_dbg ("Couldn't read raw message: %s", error->message);
+ mm_obj_dbg (self, "couldn't read raw message: %s", error->message);
g_error_free (error);
} else {
QmiWmsMessageTagType tag;
@@ -6933,11 +6580,12 @@ wms_raw_read_ready (QmiClientWms *client,
&format,
&data,
NULL);
- add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (ctx->self),
+ add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self),
mm_sms_storage_to_qmi_storage_type (ctx->storage),
message->memory_index,
tag,
format,
+ FALSE,
data);
}
@@ -6946,17 +6594,20 @@ wms_raw_read_ready (QmiClientWms *client,
/* Keep on reading parts */
ctx->i++;
- read_next_sms_part (ctx);
+ read_next_sms_part (task);
}
-static void load_initial_sms_parts_step (LoadInitialSmsPartsContext *ctx);
+static void load_initial_sms_parts_step (GTask *task);
static void
-read_next_sms_part (LoadInitialSmsPartsContext *ctx)
+read_next_sms_part (GTask *task)
{
+ LoadInitialSmsPartsContext *ctx;
QmiMessageWmsListMessagesOutputMessageListElement *message;
QmiMessageWmsRawReadInput *input;
+ ctx = g_task_get_task_data (task);
+
if (ctx->i >= ctx->message_array->len ||
!ctx->message_array) {
/* If we just listed all SMS, we're done. Otherwise go to next tag. */
@@ -6966,7 +6617,7 @@ read_next_sms_part (LoadInitialSmsPartsContext *ctx)
ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST;
else
ctx->step++;
- load_initial_sms_parts_step (ctx);
+ load_initial_sms_parts_step (task);
return;
}
@@ -7000,33 +6651,39 @@ read_next_sms_part (LoadInitialSmsPartsContext *ctx)
3,
NULL,
(GAsyncReadyCallback)wms_raw_read_ready,
- ctx);
+ task);
qmi_message_wms_raw_read_input_unref (input);
}
static void
wms_list_messages_ready (QmiClientWms *client,
GAsyncResult *res,
- LoadInitialSmsPartsContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ LoadInitialSmsPartsContext *ctx;
QmiMessageWmsListMessagesOutput *output = NULL;
GError *error = NULL;
GArray *message_array;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_wms_list_messages_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- load_initial_sms_parts_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_wms_list_messages_output_get_result (output, &error)) {
/* Ignore error, keep on */
- g_debug ("Couldn't read SMS messages: %s", error->message);
+ mm_obj_dbg (self, "couldn't read SMS messages: %s", error->message);
g_error_free (error);
ctx->step++;
- load_initial_sms_parts_step (ctx);
+ load_initial_sms_parts_step (task);
+ qmi_message_wms_list_messages_output_unref (output);
return;
}
@@ -7036,113 +6693,138 @@ wms_list_messages_ready (QmiClientWms *client,
NULL);
/* Keep a reference to the array ourselves */
+ if (ctx->message_array)
+ g_array_unref (ctx->message_array);
ctx->message_array = g_array_ref (message_array);
qmi_message_wms_list_messages_output_unref (output);
/* Start reading parts */
ctx->i = 0;
- read_next_sms_part (ctx);
+ read_next_sms_part (task);
}
static void
-load_initial_sms_parts_step (LoadInitialSmsPartsContext *ctx)
+load_initial_sms_parts_step (GTask *task)
{
+ MMBroadbandModemQmi *self;
+ LoadInitialSmsPartsContext *ctx;
QmiMessageWmsListMessagesInput *input;
gint mode = -1;
gint tag_type = -1;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case LOAD_INITIAL_SMS_PARTS_STEP_FIRST:
ctx->step++;
- /* Fall down */
+ /* Fall through */
+
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))) {
+ if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LAST;
- load_initial_sms_parts_step (ctx);
+ load_initial_sms_parts_step (task);
return;
}
ctx->step++;
- /* Fall down */
+ /* Fall through */
+
case LOAD_INITIAL_SMS_PARTS_STEP_3GPP_LIST_ALL:
- mm_dbg ("loading all 3GPP messages from storage '%s'...",
+ mm_obj_dbg (self, "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_obj_dbg (self, "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_obj_dbg (self, "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_obj_dbg (self, "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_obj_dbg (self, "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 */
+ /* Fall through */
+
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))) {
+ if (!mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) {
ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LAST;
- load_initial_sms_parts_step (ctx);
+ load_initial_sms_parts_step (task);
return;
}
ctx->step++;
- /* Fall down */
+ /* Fall through */
+
case LOAD_INITIAL_SMS_PARTS_STEP_CDMA_LIST_ALL:
- mm_dbg ("loading all CDMA messages from storage '%s'...",
+ mm_obj_dbg (self, "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_CDMA_LIST_MT_READ:
- mm_dbg ("loading CDMA MT-read messages from storage '%s'...",
+ mm_obj_dbg (self, "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_CDMA_LIST_MT_NOT_READ:
- mm_dbg ("loading CDMA MT-not-read messages from storage '%s'...",
+ mm_obj_dbg (self, "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_CDMA_LIST_MO_SENT:
- mm_dbg ("loading CDMA MO-sent messages from storage '%s'...",
+ mm_obj_dbg (self, "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_CDMA_LIST_MO_NOT_SENT:
- mm_dbg ("loading CDMA MO-not-sent messages from storage '%s'...",
+ mm_obj_dbg (self, "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 */
+ /* Fall through */
+
case LOAD_INITIAL_SMS_PARTS_STEP_LAST:
/* All steps done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- load_initial_sms_parts_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
g_assert (mode != -1);
@@ -7166,7 +6848,7 @@ load_initial_sms_parts_step (LoadInitialSmsPartsContext *ctx)
5,
NULL,
(GAsyncReadyCallback)wms_list_messages_ready,
- ctx);
+ task);
qmi_message_wms_list_messages_input_unref (input);
}
@@ -7178,36 +6860,35 @@ load_initial_sms_parts (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
LoadInitialSmsPartsContext *ctx;
+ GTask *task;
QmiClient *client = NULL;
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->load_initial_sms_parts (_self, storage, callback, user_data);
}
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_WMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WMS, &client,
+ callback, user_data))
return;
ctx = g_slice_new0 (LoadInitialSmsPartsContext);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx->client = QMI_CLIENT_WMS (g_object_ref (client));
ctx->storage = storage;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_initial_sms_parts);
ctx->step = LOAD_INITIAL_SMS_PARTS_STEP_FIRST;
- load_initial_sms_parts_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_initial_sms_parts_context_free);
+
+ load_initial_sms_parts_step (task);
}
/*****************************************************************************/
-/* Setup/Cleanup unsolicited event handlers (Messaging interface) */
+/* Common setup/cleanup unsolicited event handlers (Messaging interface) */
typedef struct {
- MMIfaceModemMessaging *self;
+ MMBroadbandModemQmi *self;
QmiClientWms *client;
QmiWmsStorageType storage;
guint32 memory_index;
@@ -7234,10 +6915,10 @@ wms_indication_raw_read_ready (QmiClientWms *client,
output = qmi_client_wms_raw_read_finish (client, res, &error);
if (!output) {
- mm_dbg ("QMI operation failed: %s", error->message);
+ mm_obj_dbg (ctx->self, "QMI operation failed: %s", error->message);
g_error_free (error);
} else if (!qmi_message_wms_raw_read_output_get_result (output, &error)) {
- mm_dbg ("Couldn't read raw message: %s", error->message);
+ mm_obj_dbg (ctx->self, "couldn't read raw message: %s", error->message);
g_error_free (error);
} else {
QmiWmsMessageTagType tag;
@@ -7255,6 +6936,7 @@ wms_indication_raw_read_ready (QmiClientWms *client,
ctx->memory_index,
tag,
format,
+ FALSE,
data);
}
@@ -7265,14 +6947,87 @@ wms_indication_raw_read_ready (QmiClientWms *client,
}
static void
+wms_send_ack_ready (QmiClientWms *client,
+ GAsyncResult *res,
+ MMBroadbandModemQmi *self)
+{
+ g_autoptr(QmiMessageWmsSendAckOutput) output = NULL;
+ g_autoptr(GError) error= NULL;
+
+ output = qmi_client_wms_send_ack_finish (client, res, &error);
+ if (!output) {
+ mm_obj_dbg (self, "QMI operation failed: '%s'", error->message);
+ }
+ g_object_unref (self);
+}
+
+static void
messaging_event_report_indication_cb (QmiClientNas *client,
QmiIndicationWmsEventReportOutput *output,
MMBroadbandModemQmi *self)
{
QmiWmsStorageType storage;
guint32 memory_index;
+ QmiWmsAckIndicator ack_ind;
+ guint32 transaction_id;
+ QmiWmsMessageFormat msg_format;
+ QmiWmsMessageTagType tag;
+ GArray *raw_data = NULL;
+
+ /* Handle transfer-route MT messages */
+ if (qmi_indication_wms_event_report_output_get_transfer_route_mt_message (
+ output,
+ &ack_ind,
+ &transaction_id,
+ &msg_format,
+ &raw_data,
+ NULL)) {
+ mm_obj_dbg (self, "Got transfer-route MT message");
+ /* If this is the first of a multi-part message, send an ACK to get the
+ * second part */
+ if (ack_ind == QMI_WMS_ACK_INDICATOR_SEND) {
+ g_autoptr(QmiMessageWmsSendAckInput) ack_input = NULL;
+ QmiWmsMessageProtocol message_protocol;
+ /* Need to ack message */
+ mm_obj_dbg (self, "Need to ACK indicator");
+ switch (msg_format) {
+ case QMI_WMS_MESSAGE_FORMAT_CDMA:
+ message_protocol = QMI_WMS_MESSAGE_PROTOCOL_CDMA;
+ break;
+ case QMI_WMS_MESSAGE_FORMAT_MWI:
+ case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT:
+ case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST:
+ default:
+ message_protocol = QMI_WMS_MESSAGE_PROTOCOL_WCDMA;
+ break;
+ }
+ ack_input = qmi_message_wms_send_ack_input_new();
+ qmi_message_wms_send_ack_input_set_information (ack_input,
+ transaction_id,
+ message_protocol,
+ TRUE,
+ NULL);
+ qmi_client_wms_send_ack (QMI_CLIENT_WMS (client),
+ ack_input,
+ MM_BASE_SMS_DEFAULT_SEND_TIMEOUT,
+ NULL,
+ (GAsyncReadyCallback)wms_send_ack_ready,
+ g_object_ref (self));
+ }
- /* Currently ignoring transfer-route MT messages */
+ /* Defaults for transfer-route messages, which are not stored anywhere */
+ storage = QMI_WMS_STORAGE_TYPE_NONE;
+ memory_index = 0;
+ tag = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ;
+ add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self),
+ storage,
+ memory_index,
+ tag,
+ msg_format,
+ TRUE,
+ raw_data);
+ return;
+ }
if (qmi_indication_wms_event_report_output_get_mt_message (
output,
@@ -7284,7 +7039,7 @@ messaging_event_report_indication_cb (QmiClientNas *client,
ctx = g_slice_new (IndicationRawReadContext);
ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx->client = QMI_CLIENT_WMS (g_object_ref (client));
ctx->storage = storage;
ctx->memory_index = memory_index;
@@ -7317,61 +7072,23 @@ messaging_event_report_indication_cb (QmiClientNas *client,
}
static gboolean
-messaging_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *_self,
- GAsyncResult *res,
- GError **error)
-{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->cleanup_unsolicited_events_finish (_self, res, error);
- }
-
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static gboolean
-messaging_setup_unsolicited_events_finish (MMIfaceModemMessaging *_self,
- GAsyncResult *res,
- GError **error)
+common_setup_cleanup_messaging_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean enable,
+ GError **error)
{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
-
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->setup_unsolicited_events_finish (_self, res, error);
- }
-
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-common_setup_cleanup_messaging_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_WMS, &client,
- callback, user_data))
- return;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_setup_cleanup_messaging_unsolicited_events);
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WMS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ error);
+ if (!client)
+ return FALSE;
if (enable == self->priv->messaging_unsolicited_events_setup) {
- mm_dbg ("Messaging 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;
+ mm_obj_dbg (self, "messaging unsolicited events already %s; skipping",
+ enable ? "setup" : "cleanup");
+ return TRUE;
}
/* Store new state */
@@ -7391,67 +7108,128 @@ common_setup_cleanup_messaging_unsolicited_events (MMBroadbandModemQmi *self,
self->priv->messaging_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);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Cleanup unsolicited event handlers (Messaging interface) */
+
+static gboolean
+messaging_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-messaging_cleanup_unsolicited_events (MMIfaceModemMessaging *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+parent_messaging_cleanup_unsolicited_events_ready (MMIfaceModemMessaging *_self,
+ GAsyncResult *res,
+ GTask *task)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GError *error = NULL;
+
+ if (!iface_modem_messaging_parent->cleanup_unsolicited_events_finish (_self, res, &error)) {
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "cleaning up parent messaging unsolicited events failed: %s", error->message);
+ g_clear_error (&error);
+ }
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->cleanup_unsolicited_events (_self, callback, user_data);
+ /* handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
}
- common_setup_cleanup_messaging_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
- FALSE,
- callback,
- user_data);
+ /* Disable QMI indications */
+ if (!common_setup_cleanup_messaging_unsolicited_events (self, FALSE, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-messaging_setup_unsolicited_events (MMIfaceModemMessaging *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+messaging_cleanup_unsolicited_events (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Disable AT URCs parent and chain QMI indications disabling */
+ iface_modem_messaging_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_messaging_cleanup_unsolicited_events_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Setup unsolicited event handlers (Messaging interface) */
+
+static gboolean
+messaging_setup_unsolicited_events_finish (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_messaging_setup_unsolicited_events_ready (MMIfaceModemMessaging *_self,
+ GAsyncResult *res,
+ GTask *task)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GError *error = NULL;
+
+ if (!iface_modem_messaging_parent->setup_unsolicited_events_finish (_self, res, &error)) {
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "setting up parent messaging unsolicited events failed: %s", error->message);
+ g_clear_error (&error);
+ }
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
- return iface_modem_messaging_parent->setup_unsolicited_events (_self, callback, user_data);
+ /* handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
}
- common_setup_cleanup_messaging_unsolicited_events (MM_BROADBAND_MODEM_QMI (self),
- TRUE,
- callback,
- user_data);
+ /* Enable QMI indications */
+ if (!common_setup_cleanup_messaging_unsolicited_events (self, TRUE, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+messaging_setup_unsolicited_events (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* Enable AT URCs parent and chain QMI indication enabling */
+ iface_modem_messaging_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_messaging_setup_unsolicited_events_ready,
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
/* Enable/Disable unsolicited events (Messaging interface) */
typedef struct {
- MMBroadbandModemQmi *self;
- GSimpleAsyncResult *result;
- QmiClientWms *client;
gboolean enable;
} EnableMessagingUnsolicitedEventsContext;
-static void
-enable_messaging_unsolicited_events_context_complete_and_free (EnableMessagingUnsolicitedEventsContext *ctx)
-{
- 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);
-}
-
static gboolean
messaging_disable_unsolicited_events_finish (MMIfaceModemMessaging *_self,
GAsyncResult *res,
@@ -7459,12 +7237,12 @@ messaging_disable_unsolicited_events_finish (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at && iface_modem_messaging_parent->disable_unsolicited_events_finish) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only && iface_modem_messaging_parent->disable_unsolicited_events_finish) {
return iface_modem_messaging_parent->disable_unsolicited_events_finish (_self, res, error);
}
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
@@ -7474,28 +7252,33 @@ messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->enable_unsolicited_events_finish (_self, res, error);
}
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
ser_messaging_indicator_ready (QmiClientWms *client,
GAsyncResult *res,
- EnableMessagingUnsolicitedEventsContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ EnableMessagingUnsolicitedEventsContext *ctx;
QmiMessageWmsSetEventReportOutput *output = NULL;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_wms_set_event_report_finish (client, res, &error);
if (!output) {
- mm_dbg ("QMI operation failed: '%s'", error->message);
+ mm_obj_dbg (self, "QMI operation failed: '%s'", error->message);
g_error_free (error);
} else if (!qmi_message_wms_set_event_report_output_get_result (output, &error)) {
- mm_dbg ("Couldn't set event report: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't set event report: '%s'", error->message);
g_error_free (error);
}
@@ -7503,9 +7286,9 @@ ser_messaging_indicator_ready (QmiClientWms *client,
qmi_message_wms_set_event_report_output_unref (output);
/* Just ignore errors for now */
- ctx->self->priv->messaging_unsolicited_events_enabled = ctx->enable;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_messaging_unsolicited_events_context_complete_and_free (ctx);
+ self->priv->messaging_unsolicited_events_enabled = ctx->enable;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -7515,34 +7298,29 @@ common_enable_disable_messaging_unsolicited_events (MMBroadbandModemQmi *self,
gpointer user_data)
{
EnableMessagingUnsolicitedEventsContext *ctx;
- GSimpleAsyncResult *result;
+ GTask *task;
QmiClient *client = NULL;
QmiMessageWmsSetEventReportInput *input;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_WMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WMS, &client,
+ callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- common_enable_disable_messaging_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
if (enable == self->priv->messaging_unsolicited_events_enabled) {
- mm_dbg ("Messaging 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);
+ mm_obj_dbg (self, "messaging unsolicited events already %s; skipping",
+ enable ? "enabled" : "disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- ctx = g_new0 (EnableMessagingUnsolicitedEventsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx = g_new (EnableMessagingUnsolicitedEventsContext, 1);
ctx->enable = enable;
- ctx->result = result;
+
+ g_task_set_task_data (task, ctx, g_free);
input = qmi_message_wms_set_event_report_input_new ();
@@ -7551,12 +7329,12 @@ common_enable_disable_messaging_unsolicited_events (MMBroadbandModemQmi *self,
ctx->enable,
NULL);
qmi_client_wms_set_event_report (
- ctx->client,
+ QMI_CLIENT_WMS (client),
input,
5,
NULL,
(GAsyncReadyCallback)ser_messaging_indicator_ready,
- ctx);
+ task);
qmi_message_wms_set_event_report_input_unref (input);
}
@@ -7567,20 +7345,16 @@ messaging_disable_unsolicited_events (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
/* 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);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -7600,8 +7374,8 @@ messaging_enable_unsolicited_events (MMIfaceModemMessaging *_self,
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->enable_unsolicited_events (_self, callback, user_data);
}
@@ -7619,8 +7393,8 @@ messaging_create_sms (MMIfaceModemMessaging *_self)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- /* Handle fallback */
- if (self->priv->messaging_fallback_at) {
+ /* Handle AT URC only fallback */
+ if (self->priv->messaging_fallback_at_only) {
return iface_modem_messaging_parent->create_sms (_self);
}
@@ -7631,903 +7405,147 @@ messaging_create_sms (MMIfaceModemMessaging *_self)
/* Location capabilities loading (Location interface) */
static MMModemLocationSource
-location_load_capabilities_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
+location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_LOCATION_SOURCE_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
}
static void
-parent_load_capabilities_ready (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+shared_qmi_location_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
{
MMModemLocationSource sources;
GError *error = NULL;
- MMPortQmi *port;
- sources = iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ sources = mm_shared_qmi_location_load_capabilities_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- port = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
-
- /* Now our own checks */
-
- /* If we have support for the PDS client, GPS and A-GPS location is supported */
- if (port && mm_port_qmi_peek_client (port,
- QMI_SERVICE_PDS,
- MM_PORT_QMI_FLAG_DEFAULT))
- sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW |
- MM_MODEM_LOCATION_SOURCE_AGPS);
-
/* If the modem is CDMA, we have support for CDMA BS location */
if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
sources |= MM_MODEM_LOCATION_SOURCE_CDMA_BS;
+ /* If the modem is 3GPP, we have support for 3GPP LAC/CI location */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
+ sources |= MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI;
+
/* So we're done, complete */
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (sources),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, sources);
+ g_object_unref (task);
}
static void
location_load_capabilities (MMIfaceModemLocation *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- location_load_capabilities);
+ task = g_task_new (self, NULL, callback, user_data);
- /* Chain up parent's setup */
- iface_modem_location_parent->load_capabilities (
+ /* Chain up shared QMI setup, which takes care of running the PARENT
+ * setup as well as processing GPS-related checks. */
+ mm_shared_qmi_location_load_capabilities (
self,
- (GAsyncReadyCallback)parent_load_capabilities_ready,
- result);
-}
-
-/*****************************************************************************/
-/* Load SUPL server */
-
-static gchar *
-location_load_supl_server_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
-{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
-}
-
-static void
-get_agps_config_ready (QmiClientPds *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- QmiMessagePdsGetAgpsConfigOutput *output = NULL;
- GError *error = NULL;
- guint32 ip;
- guint32 port;
- GArray *url;
- gchar *str;
-
- output = qmi_client_pds_get_agps_config_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- if (!qmi_message_pds_get_agps_config_output_get_result (output, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- str = NULL;
-
- /* Prefer IP/PORT to URL */
- if (qmi_message_pds_get_agps_config_output_get_location_server_address (
- output,
- &ip,
- &port,
- NULL) &&
- ip != 0 &&
- port != 0) {
- struct in_addr a = { .s_addr = ip };
- gchar buf[INET_ADDRSTRLEN + 1];
-
- memset (buf, 0, sizeof (buf));
-
- if (!inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot convert numeric IP address to string");
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- str = g_strdup_printf ("%s:%u", buf, port);
- }
-
- if (!str &&
- qmi_message_pds_get_agps_config_output_get_location_server_url (
- output,
- &url,
- NULL)) {
- str = g_convert (url->data, url->len, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
- }
-
- if (!str)
- str = g_strdup ("");
-
- qmi_message_pds_get_agps_config_output_unref (output);
-
- g_simple_async_result_set_op_res_gpointer (simple, str, g_free);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static void
-location_load_supl_server (MMIfaceModemLocation *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- QmiClient *client = NULL;
- GSimpleAsyncResult *simple;
- QmiMessagePdsGetAgpsConfigInput *input;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_PDS, &client,
- callback, user_data)) {
- return;
- }
-
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- location_load_supl_server);
-
- input = qmi_message_pds_get_agps_config_input_new ();
-
- /* For multimode devices, prefer UMTS by default */
- if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
- qmi_message_pds_get_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_UMTS, NULL);
- else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
- qmi_message_pds_get_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_CDMA, NULL);
-
- qmi_client_pds_get_agps_config (
- QMI_CLIENT_PDS (client),
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)get_agps_config_ready,
- simple);
- qmi_message_pds_get_agps_config_input_unref (input);
-}
-
-/*****************************************************************************/
-/* Set SUPL server */
-
-static gboolean
-location_set_supl_server_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-set_agps_config_ready (QmiClientPds *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
-{
- QmiMessagePdsSetAgpsConfigOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_set_agps_config_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- if (!qmi_message_pds_set_agps_config_output_get_result (output, &error)) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- qmi_message_pds_set_agps_config_output_unref (output);
-
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
-}
-
-static gboolean
-parse_as_ip_port (const gchar *supl,
- guint32 *out_ip,
- guint32 *out_port)
-{
- gboolean valid = FALSE;
- gchar **split;
- guint port;
- guint32 ip;
-
- split = g_strsplit (supl, ":", -1);
- if (g_strv_length (split) != 2)
- goto out;
-
- if (!mm_get_uint_from_str (split[1], &port))
- goto out;
- if (port == 0 || port > G_MAXUINT16)
- goto out;
- if (inet_pton (AF_INET, split[0], &ip) <= 0)
- goto out;
-
- *out_ip = ip;
- *out_port = port;
- valid = TRUE;
-
-out:
- g_strfreev (split);
- return valid;
-}
-
-static gboolean
-parse_as_url (const gchar *supl,
- GArray **out_url)
-{
- gchar *utf16;
- gsize utf16_len;
-
- utf16 = g_convert (supl, -1, "UTF-16BE", "UTF-8", NULL, &utf16_len, NULL);
- *out_url = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), utf16_len),
- utf16,
- utf16_len);
- g_free (utf16);
- return TRUE;
-}
-
-static void
-location_set_supl_server (MMIfaceModemLocation *self,
- const gchar *supl,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- QmiClient *client = NULL;
- GSimpleAsyncResult *simple;
- QmiMessagePdsSetAgpsConfigInput *input;
- guint32 ip;
- guint32 port;
- GArray *url;
-
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_PDS, &client,
- callback, user_data)) {
- return;
- }
-
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- location_set_supl_server);
-
- input = qmi_message_pds_set_agps_config_input_new ();
-
- /* For multimode devices, prefer UMTS by default */
- if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
- qmi_message_pds_set_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_UMTS, NULL);
- else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
- qmi_message_pds_set_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_CDMA, NULL);
-
- if (parse_as_ip_port (supl, &ip, &port))
- qmi_message_pds_set_agps_config_input_set_location_server_address (input, ip, port, NULL);
- else if (parse_as_url (supl, &url)) {
- qmi_message_pds_set_agps_config_input_set_location_server_url (input, url, NULL);
- g_array_unref (url);
- } else
- g_assert_not_reached ();
-
- qmi_client_pds_set_agps_config (
- QMI_CLIENT_PDS (client),
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)set_agps_config_ready,
- simple);
- qmi_message_pds_set_agps_config_input_unref (input);
+ (GAsyncReadyCallback)shared_qmi_location_load_capabilities_ready,
+ task);
}
/*****************************************************************************/
/* Disable location gathering (Location interface) */
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientPds *client;
- GSimpleAsyncResult *result;
- MMModemLocationSource source;
- /* Default tracking session (for A-GPS disabling) */
- QmiPdsOperatingMode session_operation;
- guint8 data_timeout;
- guint32 interval;
- guint32 accuracy_threshold;
-} DisableLocationGatheringContext;
-
-static void
-disable_location_gathering_context_complete_and_free (DisableLocationGatheringContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->client)
- g_object_unref (ctx->client);
- g_object_unref (ctx->self);
- g_slice_free (DisableLocationGatheringContext, ctx);
-}
-
static gboolean
-disable_location_gathering_finish (MMIfaceModemLocation *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-gps_service_state_stop_ready (QmiClientPds *client,
- GAsyncResult *res,
- DisableLocationGatheringContext *ctx)
-{
- QmiMessagePdsSetGpsServiceStateOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_set_gps_service_state_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- disable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_set_gps_service_state_output_get_result (output, &error)) {
- if (!g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- g_prefix_error (&error, "Couldn't set GPS service state: ");
- g_simple_async_result_take_error (ctx->result, error);
- disable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_set_gps_service_state_output_unref (output);
- return;
- }
-
- g_error_free (error);
- }
-
- qmi_message_pds_set_gps_service_state_output_unref (output);
-
- g_assert (ctx->self->priv->location_event_report_indication_id != 0);
- g_signal_handler_disconnect (client, ctx->self->priv->location_event_report_indication_id);
- ctx->self->priv->location_event_report_indication_id = 0;
-
- mm_dbg ("GPS stopped");
- ctx->self->priv->enabled_sources &= ~ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disable_location_gathering_context_complete_and_free (ctx);
-}
-
-static void
-set_default_tracking_session_stop_ready (QmiClientPds *client,
- GAsyncResult *res,
- DisableLocationGatheringContext *ctx)
+disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
{
- QmiMessagePdsSetDefaultTrackingSessionOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_set_default_tracking_session_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- disable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_set_default_tracking_session_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't set default tracking session: ");
- g_simple_async_result_take_error (ctx->result, error);
- disable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_set_default_tracking_session_output_unref (output);
- return;
- }
-
- qmi_message_pds_set_default_tracking_session_output_unref (output);
-
- /* Done */
- mm_dbg ("A-GPS disabled");
- ctx->self->priv->enabled_sources &= ~ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disable_location_gathering_context_complete_and_free (ctx);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-get_default_tracking_session_stop_ready (QmiClientPds *client,
- GAsyncResult *res,
- DisableLocationGatheringContext *ctx)
+shared_qmi_disable_location_gathering_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessagePdsSetDefaultTrackingSessionInput *input;
- QmiMessagePdsGetDefaultTrackingSessionOutput *output = NULL;
GError *error = NULL;
- output = qmi_client_pds_get_default_tracking_session_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- disable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_get_default_tracking_session_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get default tracking session: ");
- g_simple_async_result_take_error (ctx->result, error);
- disable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_get_default_tracking_session_output_unref (output);
- return;
- }
-
- qmi_message_pds_get_default_tracking_session_output_get_info (
- output,
- &ctx->session_operation,
- &ctx->data_timeout,
- &ctx->interval,
- &ctx->accuracy_threshold,
- NULL);
-
- qmi_message_pds_get_default_tracking_session_output_unref (output);
-
- if (ctx->session_operation == QMI_PDS_OPERATING_MODE_STANDALONE) {
- /* Done */
- mm_dbg ("A-GPS already disabled");
- ctx->self->priv->enabled_sources &= ~ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- input = qmi_message_pds_set_default_tracking_session_input_new ();
- qmi_message_pds_set_default_tracking_session_input_set_info (
- input,
- QMI_PDS_OPERATING_MODE_STANDALONE,
- ctx->data_timeout,
- ctx->interval,
- ctx->accuracy_threshold,
- NULL);
- qmi_client_pds_set_default_tracking_session (
- ctx->client,
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)set_default_tracking_session_stop_ready,
- ctx);
- qmi_message_pds_set_default_tracking_session_input_unref (input);
+ if (!mm_shared_qmi_disable_location_gathering_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-disable_location_gathering (MMIfaceModemLocation *self,
+disable_location_gathering (MMIfaceModemLocation *_self,
MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- DisableLocationGatheringContext *ctx;
- QmiClient *client = NULL;
- GSimpleAsyncResult *result;
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- disable_location_gathering);
+ task = g_task_new (self, NULL, callback, user_data);
/* Nothing to be done to disable 3GPP or CDMA locations */
- if (source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI ||
- source == MM_MODEM_LOCATION_SOURCE_CDMA_BS) {
- /* Just mark it as disabled */
- MM_BROADBAND_MODEM_QMI (self)->priv->enabled_sources &= ~source;
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
- return;
- }
-
- /* Setup context and client */
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_PDS, &client,
- callback, user_data)) {
- g_object_unref (result);
- return;
- }
- ctx = g_slice_new0 (DisableLocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
- ctx->result = result;
- ctx->source = source;
-
- /* Disable A-GPS? */
- if (source == MM_MODEM_LOCATION_SOURCE_AGPS) {
- qmi_client_pds_get_default_tracking_session (
- ctx->client,
- NULL,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)get_default_tracking_session_stop_ready,
- ctx);
- return;
- }
-
- /* Only stop GPS engine if no GPS-related sources enabled */
- if (source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
- MMModemLocationSource tmp;
-
- /* If no more GPS sources enabled, stop GPS */
- tmp = ctx->self->priv->enabled_sources;
- tmp &= ~source;
- if (!(tmp & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) {
- QmiMessagePdsSetGpsServiceStateInput *input;
-
- input = qmi_message_pds_set_gps_service_state_input_new ();
- qmi_message_pds_set_gps_service_state_input_set_state (input, FALSE, NULL);
- qmi_client_pds_set_gps_service_state (
- ctx->client,
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_service_state_stop_ready,
- ctx);
- qmi_message_pds_set_gps_service_state_input_unref (input);
- return;
- }
-
- /* Otherwise, we have more GPS sources enabled, we shouldn't stop GPS, just
- * return */
- ctx->self->priv->enabled_sources &= ~source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disable_location_gathering_context_complete_and_free (ctx);
- return;
- }
+ if (source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI || source == MM_MODEM_LOCATION_SOURCE_CDMA_BS)
+ self->priv->enabled_sources &= ~source;
- /* The QMI implementation has a fixed set of capabilities supported. Arriving
- * here means we tried to disable one which wasn't set as supported, which should
- * not happen */
- g_assert_not_reached ();
+ mm_shared_qmi_disable_location_gathering (
+ _self,
+ source,
+ (GAsyncReadyCallback) shared_qmi_disable_location_gathering_ready,
+ task);
}
/*****************************************************************************/
/* Enable location gathering (Location interface) */
-static void
-location_event_report_indication_cb (QmiClientPds *client,
- QmiIndicationPdsEventReportOutput *output,
- MMBroadbandModemQmi *self)
-{
- QmiPdsPositionSessionStatus session_status;
- const gchar *nmea;
-
- if (qmi_indication_pds_event_report_output_get_position_session_status (
- output,
- &session_status,
- NULL)) {
- mm_dbg ("[GPS] session status changed: '%s'",
- qmi_pds_position_session_status_get_string (session_status));
- }
-
- if (qmi_indication_pds_event_report_output_get_nmea_position (
- output,
- &nmea,
- NULL)) {
- mm_dbg ("[NMEA] %s", nmea);
- mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
- }
-}
-
-typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientPds *client;
- GSimpleAsyncResult *result;
- MMModemLocationSource source;
- /* Default tracking session (for A-GPS enabling) */
- QmiPdsOperatingMode session_operation;
- guint8 data_timeout;
- guint32 interval;
- guint32 accuracy_threshold;
-} EnableLocationGatheringContext;
-
-static void
-enable_location_gathering_context_complete_and_free (EnableLocationGatheringContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- if (ctx->client)
- g_object_unref (ctx->client);
- g_object_unref (ctx->self);
- g_slice_free (EnableLocationGatheringContext, ctx);
-}
-
static gboolean
enable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-ser_location_ready (QmiClientPds *client,
- GAsyncResult *res,
- EnableLocationGatheringContext *ctx)
+shared_qmi_enable_location_gathering_ready (MMIfaceModemLocation *_self,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessagePdsSetEventReportOutput *output = NULL;
- GError *error = NULL;
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ MMModemLocationSource source;
+ GError *error = NULL;
- output = qmi_client_pds_set_event_report_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
+ if (!mm_shared_qmi_enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- if (!qmi_message_pds_set_event_report_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't set event report: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_set_event_report_output_unref (output);
- return;
- }
-
- qmi_message_pds_set_event_report_output_unref (output);
-
- mm_dbg ("Adding location event report indication handling");
- g_assert (ctx->self->priv->location_event_report_indication_id == 0);
- ctx->self->priv->location_event_report_indication_id =
- g_signal_connect (client,
- "event-report",
- G_CALLBACK (location_event_report_indication_cb),
- ctx->self);
-
- /* Done */
- mm_dbg ("GPS started");
- ctx->self->priv->enabled_sources |= ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_location_gathering_context_complete_and_free (ctx);
-}
-
-static void
-auto_tracking_state_start_ready (QmiClientPds *client,
- GAsyncResult *res,
- EnableLocationGatheringContext *ctx)
-{
- QmiMessagePdsSetEventReportInput *input;
- QmiMessagePdsSetAutoTrackingStateOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_set_auto_tracking_state_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_set_auto_tracking_state_output_get_result (output, &error)) {
- if (!g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- g_prefix_error (&error, "Couldn't set auto-tracking state: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_set_auto_tracking_state_output_unref (output);
- return;
- }
- g_error_free (error);
- }
-
- qmi_message_pds_set_auto_tracking_state_output_unref (output);
-
- /* Only gather standard NMEA traces */
- input = qmi_message_pds_set_event_report_input_new ();
- qmi_message_pds_set_event_report_input_set_nmea_position_reporting (input, TRUE, NULL);
- qmi_client_pds_set_event_report (
- ctx->client,
- input,
- 5,
- NULL,
- (GAsyncReadyCallback)ser_location_ready,
- ctx);
- qmi_message_pds_set_event_report_input_unref (input);
-}
-
-static void
-gps_service_state_start_ready (QmiClientPds *client,
- GAsyncResult *res,
- EnableLocationGatheringContext *ctx)
-{
- QmiMessagePdsSetAutoTrackingStateInput *input;
- QmiMessagePdsSetGpsServiceStateOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_set_gps_service_state_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_set_gps_service_state_output_get_result (output, &error)) {
- if (!g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- g_prefix_error (&error, "Couldn't set GPS service state: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_set_gps_service_state_output_unref (output);
- return;
- }
- g_error_free (error);
- }
-
- qmi_message_pds_set_gps_service_state_output_unref (output);
-
- /* Enable auto-tracking for a continuous fix */
- input = qmi_message_pds_set_auto_tracking_state_input_new ();
- qmi_message_pds_set_auto_tracking_state_input_set_state (input, TRUE, NULL);
- qmi_client_pds_set_auto_tracking_state (
- ctx->client,
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)auto_tracking_state_start_ready,
- ctx);
- qmi_message_pds_set_auto_tracking_state_input_unref (input);
-}
-
-static void
-set_default_tracking_session_start_ready (QmiClientPds *client,
- GAsyncResult *res,
- EnableLocationGatheringContext *ctx)
-{
- QmiMessagePdsSetDefaultTrackingSessionOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_set_default_tracking_session_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_set_default_tracking_session_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't set default tracking session: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_set_default_tracking_session_output_unref (output);
- return;
- }
-
- qmi_message_pds_set_default_tracking_session_output_unref (output);
-
- /* Done */
- mm_dbg ("A-GPS enabled");
- ctx->self->priv->enabled_sources |= ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_location_gathering_context_complete_and_free (ctx);
-}
-
-static void
-get_default_tracking_session_start_ready (QmiClientPds *client,
- GAsyncResult *res,
- EnableLocationGatheringContext *ctx)
-{
- QmiMessagePdsSetDefaultTrackingSessionInput *input;
- QmiMessagePdsGetDefaultTrackingSessionOutput *output = NULL;
- GError *error = NULL;
-
- output = qmi_client_pds_get_default_tracking_session_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- if (!qmi_message_pds_get_default_tracking_session_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get default tracking session: ");
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- qmi_message_pds_get_default_tracking_session_output_unref (output);
- return;
- }
-
- qmi_message_pds_get_default_tracking_session_output_get_info (
- output,
- &ctx->session_operation,
- &ctx->data_timeout,
- &ctx->interval,
- &ctx->accuracy_threshold,
- NULL);
-
- qmi_message_pds_get_default_tracking_session_output_unref (output);
-
- if (ctx->session_operation == QMI_PDS_OPERATING_MODE_MS_ASSISTED) {
- /* Done */
- mm_dbg ("A-GPS already enabled");
- ctx->self->priv->enabled_sources |= ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- input = qmi_message_pds_set_default_tracking_session_input_new ();
- qmi_message_pds_set_default_tracking_session_input_set_info (
- input,
- QMI_PDS_OPERATING_MODE_MS_ASSISTED,
- ctx->data_timeout,
- ctx->interval,
- ctx->accuracy_threshold,
- NULL);
- qmi_client_pds_set_default_tracking_session (
- ctx->client,
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)set_default_tracking_session_start_ready,
- ctx);
- qmi_message_pds_set_default_tracking_session_input_unref (input);
-}
-
-static void
-parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
- GAsyncResult *res,
- EnableLocationGatheringContext *ctx)
-{
- GError *error = NULL;
- QmiClient *client;
-
- if (!iface_modem_location_parent->enable_location_gathering_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
/* Nothing else needed in the QMI side for LAC/CI */
- if (ctx->source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) {
- ctx->self->priv->enabled_sources |= ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_location_gathering_context_complete_and_free (ctx);
+ if (source == MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI &&
+ mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
+ self->priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -8535,94 +7553,38 @@ parent_enable_location_gathering_ready (MMIfaceModemLocation *self,
* location source, so that we get up to date BS location information.
* Note that we don't care for when the registration checks get finished.
*/
- if (ctx->source == MM_MODEM_LOCATION_SOURCE_CDMA_BS &&
- mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
+ if (source == MM_MODEM_LOCATION_SOURCE_CDMA_BS &&
+ mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) {
/* Reload registration to get LAC/CI */
- mm_iface_modem_cdma_run_registration_checks (MM_IFACE_MODEM_CDMA (ctx->self), NULL, NULL);
+ mm_iface_modem_cdma_run_registration_checks (MM_IFACE_MODEM_CDMA (self), NULL, NULL);
/* Just mark it as enabled */
- ctx->self->priv->enabled_sources |= ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
-
- /* Setup context and client */
- client = peek_qmi_client (ctx->self, QMI_SERVICE_PDS, &error);
- if (!client) {
- g_simple_async_result_take_error (ctx->result, error);
- enable_location_gathering_context_complete_and_free (ctx);
- return;
- }
- ctx->client = g_object_ref (client);
-
- /* Enabling A-GPS? */
- if (ctx->source == MM_MODEM_LOCATION_SOURCE_AGPS) {
- qmi_client_pds_get_default_tracking_session (
- ctx->client,
- NULL,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)get_default_tracking_session_start_ready,
- ctx);
- return;
- }
-
- /* NMEA and RAW are both enabled in the same way */
- if (ctx->source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW)) {
- /* Only start GPS engine if not done already */
- if (!(ctx->self->priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
- MM_MODEM_LOCATION_SOURCE_GPS_RAW))) {
- QmiMessagePdsSetGpsServiceStateInput *input;
-
- input = qmi_message_pds_set_gps_service_state_input_new ();
- qmi_message_pds_set_gps_service_state_input_set_state (input, TRUE, NULL);
- qmi_client_pds_set_gps_service_state (
- ctx->client,
- input,
- 10,
- NULL, /* cancellable */
- (GAsyncReadyCallback)gps_service_state_start_ready,
- ctx);
- qmi_message_pds_set_gps_service_state_input_unref (input);
- return;
- }
-
- /* GPS already started, we're done */
- ctx->self->priv->enabled_sources |= ctx->source;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enable_location_gathering_context_complete_and_free (ctx);
+ self->priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- /* The QMI implementation has a fixed set of capabilities supported. Arriving
- * here means we tried to enable one which wasn't set as supported, which should
- * not happen */
- g_assert_not_reached ();
+ /* Otherwise, we're done */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-enable_location_gathering (MMIfaceModemLocation *self,
- MMModemLocationSource source,
- GAsyncReadyCallback callback,
- gpointer user_data)
+enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- EnableLocationGatheringContext *ctx;
+ GTask *task;
- ctx = g_slice_new0 (EnableLocationGatheringContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enable_location_gathering);
- /* Store source to enable, there will be only one! */
- ctx->source = source;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
- /* Chain up parent's gathering enable */
- iface_modem_location_parent->enable_location_gathering (
- MM_IFACE_MODEM_LOCATION (ctx->self),
- ctx->source,
- (GAsyncReadyCallback)parent_enable_location_gathering_ready,
- ctx);
+ mm_shared_qmi_enable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)shared_qmi_enable_location_gathering_ready,
+ task);
}
/*****************************************************************************/
@@ -8633,8 +7595,7 @@ 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));
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -8642,26 +7603,23 @@ oma_check_support (MMIfaceModemOma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- MMPortQmi *port;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- oma_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
- 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_port_qmi_peek_client (port, QMI_SERVICE_OMA, MM_PORT_QMI_FLAG_DEFAULT)) {
- mm_dbg ("OMA capabilities not supported");
- g_simple_async_result_set_op_res_gboolean (result, FALSE);
+ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_OMA,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL)) {
+ mm_obj_dbg (self, "OMA capabilities not supported");
+ g_task_return_boolean (task, FALSE);
} else {
- mm_dbg ("OMA capabilities supported");
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ mm_obj_dbg (self, "OMA capabilities supported");
+ g_task_return_boolean (task, TRUE);
}
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -8672,23 +7630,28 @@ 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;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMOmaFeature) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_OMA_FEATURE_NONE;
+ }
+ return (MMOmaFeature)value;
}
static void
oma_get_feature_setting_ready (QmiClientOma *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
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);
+ g_task_return_error (task, error);
else {
MMOmaFeature features = MM_OMA_FEATURE_NONE;
gboolean enabled;
@@ -8714,14 +7677,13 @@ oma_get_feature_setting_ready (QmiClientOma *client,
enabled)
features |= MM_OMA_FEATURE_HANDS_FREE_ACTIVATION;
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER ((guint)features), NULL);
+ g_task_return_int (task, features);
}
if (output)
qmi_message_oma_get_feature_setting_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -8731,9 +7693,9 @@ oma_load_features (MMIfaceModemOma *self,
{
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
return;
qmi_client_oma_get_feature_setting (
@@ -8742,10 +7704,7 @@ oma_load_features (MMIfaceModemOma *self,
5,
NULL,
(GAsyncReadyCallback)oma_get_feature_setting_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- oma_load_features));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -8756,28 +7715,27 @@ oma_setup_finish (MMIfaceModemOma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
oma_set_feature_setting_ready (QmiClientOma *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
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);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
if (output)
qmi_message_oma_set_feature_setting_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -8789,9 +7747,9 @@ oma_setup (MMIfaceModemOma *self,
QmiClient *client = NULL;
QmiMessageOmaSetFeatureSettingInput *input;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
return;
input = qmi_message_oma_set_feature_setting_input_new ();
@@ -8814,10 +7772,7 @@ oma_setup (MMIfaceModemOma *self,
5,
NULL,
(GAsyncReadyCallback)oma_set_feature_setting_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- oma_setup));
+ g_task_new (self, NULL, callback, user_data));
qmi_message_oma_set_feature_setting_input_unref (input);
}
@@ -8830,28 +7785,27 @@ 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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
oma_start_session_ready (QmiClientOma *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
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);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
if (output)
qmi_message_oma_start_session_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -8863,9 +7817,9 @@ oma_start_client_initiated_session (MMIfaceModemOma *self,
QmiClient *client = NULL;
QmiMessageOmaStartSessionInput *input;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_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 */
@@ -8885,10 +7839,7 @@ oma_start_client_initiated_session (MMIfaceModemOma *self,
5,
NULL,
(GAsyncReadyCallback)oma_start_session_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- oma_start_client_initiated_session));
+ g_task_new (self, NULL, callback, user_data));
qmi_message_oma_start_session_input_unref (input);
}
@@ -8901,28 +7852,26 @@ 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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
oma_send_selection_ready (QmiClientOma *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
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);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
if (output)
qmi_message_oma_send_selection_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -8935,9 +7884,9 @@ oma_accept_network_initiated_session (MMIfaceModemOma *self,
QmiClient *client = NULL;
QmiMessageOmaSendSelectionInput *input;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
return;
input = qmi_message_oma_send_selection_input_new ();
@@ -8953,10 +7902,7 @@ oma_accept_network_initiated_session (MMIfaceModemOma *self,
5,
NULL,
(GAsyncReadyCallback)oma_send_selection_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- oma_accept_network_initiated_session));
+ g_task_new (self, NULL, callback, user_data));
qmi_message_oma_send_selection_input_unref (input);
}
@@ -8969,28 +7915,27 @@ oma_cancel_session_finish (MMIfaceModemOma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
oma_cancel_session_ready (QmiClientOma *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
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);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
if (output)
qmi_message_oma_cancel_session_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -9000,9 +7945,9 @@ oma_cancel_session (MMIfaceModemOma *self,
{
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_OMA, &client,
+ callback, user_data))
return;
qmi_client_oma_cancel_session (
@@ -9011,10 +7956,7 @@ oma_cancel_session (MMIfaceModemOma *self,
5,
NULL,
(GAsyncReadyCallback)oma_cancel_session_ready,
- g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- oma_cancel_session));
+ g_task_new (self, NULL, callback, user_data));
}
/*****************************************************************************/
@@ -9058,7 +8000,7 @@ oma_event_report_indication_cb (QmiClientNas *client,
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);
+ mm_obj_warn (self, "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),
@@ -9072,7 +8014,7 @@ 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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -9081,25 +8023,21 @@ common_setup_cleanup_oma_unsolicited_events (MMBroadbandModemQmi *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_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);
+ task = g_task_new (self, NULL, callback, user_data);
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);
+ mm_obj_dbg (self, "OMA unsolicited events already %s; skipping",
+ enable ? "setup" : "cleanup");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -9120,9 +8058,8 @@ common_setup_cleanup_oma_unsolicited_events (MMBroadbandModemQmi *self,
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -9151,44 +8088,36 @@ oma_setup_unsolicited_events (MMIfaceModemOma *self,
/* 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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
ser_oma_indicator_ready (QmiClientOma *client,
GAsyncResult *res,
- EnableOmaUnsolicitedEventsContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ EnableOmaUnsolicitedEventsContext *ctx;
QmiMessageOmaSetEventReportOutput *output = NULL;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_oma_set_event_report_finish (client, res, &error);
if (!output) {
- mm_dbg ("QMI operation failed: '%s'", error->message);
+ mm_obj_dbg (self, "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);
+ mm_obj_dbg (self, "couldn't set event report: '%s'", error->message);
g_error_free (error);
}
@@ -9196,9 +8125,9 @@ ser_oma_indicator_ready (QmiClientOma *client,
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);
+ self->priv->oma_unsolicited_events_enabled = ctx->enable;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -9208,34 +8137,29 @@ common_enable_disable_oma_unsolicited_events (MMBroadbandModemQmi *self,
gpointer user_data)
{
EnableOmaUnsolicitedEventsContext *ctx;
- GSimpleAsyncResult *result;
+ GTask *task;
QmiClient *client = NULL;
QmiMessageOmaSetEventReportInput *input;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_OMA, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_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);
+ task = g_task_new (self, NULL, callback, user_data);
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);
+ mm_obj_dbg (self, "OMA unsolicited events already %s; skipping",
+ enable ? "enabled" : "disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- ctx = g_slice_new0 (EnableOmaUnsolicitedEventsContext);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx = g_new (EnableOmaUnsolicitedEventsContext, 1);
ctx->enable = enable;
- ctx->result = result;
+
+ g_task_set_task_data (task, ctx, g_free);
input = qmi_message_oma_set_event_report_input_new ();
qmi_message_oma_set_event_report_input_set_session_state_reporting (
@@ -9247,12 +8171,12 @@ common_enable_disable_oma_unsolicited_events (MMBroadbandModemQmi *self,
ctx->enable,
NULL);
qmi_client_oma_set_event_report (
- ctx->client,
+ QMI_CLIENT_OMA (client),
input,
5,
NULL,
(GAsyncReadyCallback)ser_oma_indicator_ready,
- ctx);
+ task);
qmi_message_oma_set_event_report_input_unref (input);
}
@@ -9279,13 +8203,2054 @@ oma_enable_unsolicited_events (MMIfaceModemOma *self,
}
/*****************************************************************************/
+/* Check support (3GPP USSD interface) */
+
+static gboolean
+modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If we have support for the Voice client, USSD is supported */
+ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL)) {
+ mm_obj_dbg (self, "USSD capabilities not supported");
+ g_task_return_boolean (task, FALSE);
+ } else {
+ mm_obj_dbg (self, "USSD capabilities supported");
+ g_task_return_boolean (task, TRUE);
+ }
+
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* USSD encode/decode helpers */
+
+static GArray *
+ussd_encode (const gchar *command,
+ QmiVoiceUssDataCodingScheme *scheme,
+ GError **error)
+{
+ gsize command_len;
+ g_autoptr(GByteArray) barray = NULL;
+ g_autoptr(GError) inner_error = NULL;
+
+ command_len = strlen (command);
+
+ if (g_str_is_ascii (command)) {
+ barray = g_byte_array_sized_new (command_len);
+ g_byte_array_append (barray, (const guint8 *)command, command_len);
+
+ *scheme = QMI_VOICE_USS_DATA_CODING_SCHEME_ASCII;
+ return (GArray *) g_steal_pointer (&barray);
+ }
+
+ barray = mm_modem_charset_bytearray_from_utf8 (command, MM_MODEM_CHARSET_UCS2, FALSE, &inner_error);
+ if (!barray) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Failed to encode USSD command in UCS2 charset: %s", inner_error->message);
+ return NULL;
+ }
+
+ *scheme = QMI_VOICE_USS_DATA_CODING_SCHEME_UCS2;
+ return (GArray *) g_steal_pointer (&barray);
+}
+
+static gchar *
+ussd_decode (QmiVoiceUssDataCodingScheme scheme,
+ GArray *data,
+ GError **error)
+{
+ gchar *decoded = NULL;
+
+ if (scheme == QMI_VOICE_USS_DATA_CODING_SCHEME_ASCII) {
+ decoded = g_strndup ((const gchar *) data->data, data->len);
+ if (!decoded)
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Error decoding USSD command in 0x%04x scheme (ASCII charset)",
+ scheme);
+ } else if (scheme == QMI_VOICE_USS_DATA_CODING_SCHEME_UCS2) {
+ decoded = mm_modem_charset_bytearray_to_utf8 ((GByteArray *) data, MM_MODEM_CHARSET_UCS2, FALSE, error);
+ if (!decoded)
+ g_prefix_error (error, "Error decoding USSD command in 0x%04x scheme (UCS2 charset): ", scheme);
+ } else
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Failed to decode USSD command in unsupported 0x%04x scheme", scheme);
+
+ return decoded;
+}
+
+/*****************************************************************************/
+/* USSD indications */
+
+static void
+process_ussd_message (MMBroadbandModemQmi *self,
+ QmiVoiceUserAction user_action,
+ gchar *utf8_take,
+ GError *error_take)
+{
+ MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE;
+ g_autoptr(GTask) task = NULL;
+ g_autofree gchar *utf8 = utf8_take;
+ g_autoptr(GError) error = error_take;
+
+ task = g_steal_pointer (&self->priv->pending_ussd_action);
+
+ if (error) {
+ g_assert (!utf8);
+ if (task)
+ g_task_return_error (task, g_steal_pointer (&error));
+ else
+ mm_obj_dbg (self, "USSD operation failed: %s", error->message);
+ return;
+ }
+
+ switch (user_action) {
+ case QMI_VOICE_USER_ACTION_NOT_REQUIRED:
+ /* no response, or a response to user's request? */
+ if (!utf8 || task)
+ break;
+ /* Network-initiated USSD-Notify */
+ mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), utf8);
+ g_clear_pointer (&utf8, g_free);
+ break;
+ case QMI_VOICE_USER_ACTION_REQUIRED:
+ /* further action required */
+ ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE;
+ /* no response, or a response to user's request? */
+ if (!utf8 || task)
+ break;
+ /* Network-initiated USSD-Request */
+ mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), utf8);
+ g_clear_pointer (&utf8, g_free);
+ break;
+ case QMI_VOICE_USER_ACTION_UNKNOWN:
+ default:
+ /* Not an indication */
+ break;
+ }
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state);
+
+ if (!task) {
+ if (utf8)
+ mm_obj_dbg (self, "ignoring unprocessed USSD message: %s", utf8);
+ return;
+ }
+
+ /* Complete the pending action, if any */
+ if (utf8)
+ g_task_return_pointer (task, g_steal_pointer (&utf8), g_free);
+ else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "USSD action response not processed correctly");
+}
+
+static void
+ussd_indication_cb (QmiClientVoice *client,
+ QmiIndicationVoiceUssdOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiVoiceUserAction user_action = QMI_VOICE_USER_ACTION_UNKNOWN;
+ QmiVoiceUssDataCodingScheme scheme;
+ GArray *uss_data = NULL;
+ gchar *utf8 = NULL;
+ GError *error = NULL;
+
+ qmi_indication_voice_ussd_output_get_user_action (output, &user_action, NULL);
+ if (qmi_indication_voice_ussd_output_get_uss_data_utf16 (output, &uss_data, NULL) && uss_data)
+ utf8 = g_convert ((const gchar *) uss_data->data, (2 * uss_data->len), "UTF-8", "UTF-16LE", NULL, NULL, &error);
+ else if (qmi_indication_voice_ussd_output_get_uss_data (output, &scheme, &uss_data, NULL) && uss_data)
+ utf8 = ussd_decode(scheme, uss_data, &error);
+
+ process_ussd_message (self, user_action, utf8, error);
+}
+
+static void
+ussd_release_indication_cb (QmiClientVoice *client,
+ MMBroadbandModemQmi *self)
+{
+ GTask *pending_task;
+ GError *error;
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
+ MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network");
+
+ pending_task = g_steal_pointer (&self->priv->pending_ussd_action);
+ if (pending_task) {
+ g_task_return_error (pending_task, error);
+ g_object_unref (pending_task);
+ return;
+ }
+
+ /* If no pending task, just report the error */
+ mm_obj_dbg (self, "USSD release indication: %s", error->message);
+ g_error_free (error);
+}
+
+/*****************************************************************************/
+/* Setup/cleanup unsolicited events */
+
+static gboolean
+common_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+common_3gpp_ussd_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (setup == self->priv->ussd_unsolicited_events_setup) {
+ mm_obj_dbg (self, "USSD unsolicited events already %s; skipping",
+ setup ? "setup" : "cleanup");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ self->priv->ussd_unsolicited_events_setup = setup;
+
+ if (setup) {
+ g_assert (self->priv->ussd_indication_id == 0);
+ self->priv->ussd_indication_id =
+ g_signal_connect (client,
+ "ussd",
+ G_CALLBACK (ussd_indication_cb),
+ self);
+ g_assert (self->priv->ussd_release_indication_id == 0);
+ self->priv->ussd_release_indication_id =
+ g_signal_connect (client,
+ "release-ussd",
+ G_CALLBACK (ussd_release_indication_cb),
+ self);
+ } else {
+ g_assert (self->priv->ussd_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->ussd_indication_id);
+ self->priv->ussd_indication_id = 0;
+ g_assert (self->priv->ussd_release_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->ussd_release_indication_id);
+ self->priv->ussd_release_indication_id = 0;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_3gpp_ussd_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data);
+}
+
+static void
+modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_3gpp_ussd_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Enable/disable unsolicited events */
+
+static gboolean
+common_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ussd_indication_register_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceIndicationRegisterOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_voice_indication_register_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_indication_register_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't register voice USSD indications: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_3gpp_ussd_enable_disable_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceIndicationRegisterInput) input = NULL;
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (enable == self->priv->ussd_unsolicited_events_enabled) {
+ mm_obj_dbg (self, "USSD unsolicited events already %s; skipping",
+ enable ? "enabled" : "disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ self->priv->ussd_unsolicited_events_enabled = enable;
+
+ input = qmi_message_voice_indication_register_input_new ();
+ qmi_message_voice_indication_register_input_set_ussd_notification_events (input, enable, NULL);
+ qmi_client_voice_indication_register (QMI_CLIENT_VOICE (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) ussd_indication_register_ready,
+ task);
+}
+
+static void
+modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_3gpp_ussd_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data);
+}
+
+static void
+modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_3gpp_ussd_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Send command (3GPP/USSD interface) */
+
+static gchar *
+modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+voice_answer_ussd_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ MMBroadbandModemQmi *self)
+{
+ g_autoptr(QmiMessageVoiceAnswerUssdOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_voice_answer_ussd_finish (client, res, &error);
+ if (!output)
+ g_prefix_error (&error, "QMI operation failed: ");
+ else if (!qmi_message_voice_answer_ussd_output_get_result (output, &error))
+ g_prefix_error (&error, "Couldn't answer USSD operation: ");
+
+ process_ussd_message (self, QMI_VOICE_USER_ACTION_UNKNOWN, error ? NULL : g_strdup (""), error);
+
+ /* balance out the full reference we received */
+ g_object_unref (self);
+}
+
+static void
+voice_originate_ussd_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ MMBroadbandModemQmi *self)
+{
+ g_autoptr(QmiMessageVoiceOriginateUssdOutput) output = NULL;
+ QmiVoiceUssDataCodingScheme scheme;
+ GError *error = NULL;
+ GArray *uss_data = NULL;
+ gchar *utf8 = NULL;
+
+ output = qmi_client_voice_originate_ussd_finish (client, res, &error);
+ if (!output)
+ g_prefix_error (&error, "QMI operation failed: ");
+ else if (!qmi_message_voice_originate_ussd_output_get_result (output, &error))
+ g_prefix_error (&error, "Couldn't originate USSD operation: ");
+ else if (qmi_message_voice_originate_ussd_output_get_uss_data_utf16 (output, &uss_data, NULL) && uss_data)
+ utf8 = g_convert ((const gchar *) uss_data->data, (2 * uss_data->len), "UTF-8", "UTF-16LE", NULL, NULL, &error);
+ else if (qmi_message_voice_originate_ussd_output_get_uss_data (output, &scheme, &uss_data, NULL) && uss_data)
+ utf8 = ussd_decode (scheme, uss_data, &error);
+
+ process_ussd_message (self, QMI_VOICE_USER_ACTION_UNKNOWN, utf8, error);
+
+ /* balance out the full reference we received */
+ g_object_unref (self);
+}
+
+static void
+modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
+ QmiClient *client;
+ QmiVoiceUssDataCodingScheme scheme = QMI_VOICE_USS_DATA_CODING_SCHEME_UNKNOWN;
+ g_autoptr(GArray) encoded = NULL;
+ GError *error = NULL;
+ MMModem3gppUssdSessionState state;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Fail if there is an ongoing operation already */
+ if (self->priv->pending_ussd_action) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "there is already an ongoing USSD operation");
+ g_object_unref (task);
+ return;
+ }
+
+ encoded = ussd_encode (command, &scheme, &error);
+ if (!encoded) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ state = mm_iface_modem_3gpp_ussd_get_state (MM_IFACE_MODEM_3GPP_USSD (self));
+
+ /* Cache the action, as it may be completed via URCs */
+ self->priv->pending_ussd_action = task;
+ mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE);
+
+ switch (state) {
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE: {
+ g_autoptr(QmiMessageVoiceOriginateUssdInput) input = NULL;
+
+ input = qmi_message_voice_originate_ussd_input_new ();
+ qmi_message_voice_originate_ussd_input_set_uss_data (input, scheme, encoded, NULL);
+ qmi_client_voice_originate_ussd (QMI_CLIENT_VOICE (client), input, 100, NULL,
+ (GAsyncReadyCallback) voice_originate_ussd_ready,
+ g_object_ref (self)); /* full reference! */
+ return;
+ }
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE: {
+ g_autoptr(QmiMessageVoiceAnswerUssdInput) input = NULL;
+
+ input = qmi_message_voice_answer_ussd_input_new ();
+ qmi_message_voice_answer_ussd_input_set_uss_data (input, scheme, encoded, NULL);
+ qmi_client_voice_answer_ussd (QMI_CLIENT_VOICE (client), input, 100, NULL,
+ (GAsyncReadyCallback) voice_answer_ussd_ready,
+ g_object_ref (self)); /* full reference! */
+ return;
+ }
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN:
+ case MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE:
+ default:
+ g_assert_not_reached ();
+ return;
+ }
+}
+
+/*****************************************************************************/
+/* Cancel USSD (3GPP/USSD interface) */
+
+static gboolean
+modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_cancel_ussd_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceCancelUssdOutput) output = NULL;
+ MMBroadbandModemQmi *self;
+ GTask *pending_task;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_voice_cancel_ussd_finish (client, res, &error);
+ if (!output)
+ g_prefix_error (&error, "QMI operation failed: ");
+ else if (!qmi_message_voice_cancel_ussd_output_get_result (output, &error))
+ g_prefix_error (&error, "Couldn't cancel USSD operation: ");
+
+ /* Complete the pending action, regardless of the operation result */
+ pending_task = g_steal_pointer (&self->priv->pending_ussd_action);
+ if (pending_task) {
+ g_task_return_new_error (pending_task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "USSD session was cancelled");
+ g_object_unref (pending_task);
+ }
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
+ MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ qmi_client_voice_cancel_ussd (QMI_CLIENT_VOICE (client), NULL, 100, NULL,
+ (GAsyncReadyCallback) voice_cancel_ussd_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Check support (Voice interface) */
+
+static gboolean
+modem_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If we have support for the Voice client, Voice is supported */
+ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL)) {
+ mm_obj_dbg (self, "Voice capabilities not supported");
+ g_task_return_boolean (task, FALSE);
+ } else {
+ /*
+ * In case of QMI, we don't need polling as call list
+ * will be dynamically updated by All Call Status indication.
+ * If an AT URC is received, reload the call list through QMI.
+ */
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, TRUE,
+ MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, TRUE,
+ NULL);
+ mm_obj_dbg (self, "Voice capabilities supported");
+ g_task_return_boolean (task, TRUE);
+ }
+
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* All Call Status indications */
+
+static void
+all_call_status_indication_cb (QmiClientVoice *client,
+ QmiIndicationVoiceAllCallStatusOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ GArray *qmi_remote_party_number_list = NULL;
+ GArray *qmi_call_information_list = NULL;
+ GList *call_info_list = NULL;
+ guint i;
+ guint j;
+
+ qmi_indication_voice_all_call_status_output_get_remote_party_number (output, &qmi_remote_party_number_list, NULL);
+ qmi_indication_voice_all_call_status_output_get_call_information (output, &qmi_call_information_list, NULL);
+
+ if (!qmi_remote_party_number_list || !qmi_call_information_list) {
+ mm_obj_dbg (self, "Ignoring All Call Status indication. Remote party number or call information not available");
+ return;
+ }
+
+ for (i = 0; i < qmi_call_information_list->len; i++) {
+ QmiIndicationVoiceAllCallStatusOutputCallInformationCall qmi_call_information;
+
+ qmi_call_information = g_array_index (qmi_call_information_list,
+ QmiIndicationVoiceAllCallStatusOutputCallInformationCall,
+ i);
+ for (j = 0; j < qmi_remote_party_number_list->len; j++) {
+ QmiIndicationVoiceAllCallStatusOutputRemotePartyNumberCall qmi_remote_party_number;
+
+ qmi_remote_party_number = g_array_index (qmi_remote_party_number_list,
+ QmiIndicationVoiceAllCallStatusOutputRemotePartyNumberCall,
+ j);
+ if (qmi_call_information.id == qmi_remote_party_number.id) {
+ MMCallInfo *call_info;
+
+ call_info = g_slice_new0 (MMCallInfo);
+ call_info->index = qmi_call_information.id;
+ call_info->number = g_strdup (qmi_remote_party_number.type);
+
+ switch (qmi_call_information.state) {
+ case QMI_VOICE_CALL_STATE_UNKNOWN:
+ call_info->state = MM_CALL_STATE_UNKNOWN;
+ break;
+ case QMI_VOICE_CALL_STATE_ORIGINATION:
+ case QMI_VOICE_CALL_STATE_CC_IN_PROGRESS:
+ call_info->state = MM_CALL_STATE_DIALING;
+ break;
+ case QMI_VOICE_CALL_STATE_ALERTING:
+ call_info->state = MM_CALL_STATE_RINGING_OUT;
+ break;
+ case QMI_VOICE_CALL_STATE_SETUP:
+ case QMI_VOICE_CALL_STATE_INCOMING:
+ call_info->state = MM_CALL_STATE_RINGING_IN;
+ break;
+ case QMI_VOICE_CALL_STATE_CONVERSATION:
+ call_info->state = MM_CALL_STATE_ACTIVE;
+ break;
+ case QMI_VOICE_CALL_STATE_HOLD:
+ call_info->state = MM_CALL_STATE_HELD;
+ break;
+ case QMI_VOICE_CALL_STATE_WAITING:
+ call_info->state = MM_CALL_STATE_WAITING;
+ break;
+ case QMI_VOICE_CALL_STATE_DISCONNECTING:
+ case QMI_VOICE_CALL_STATE_END:
+ call_info->state = MM_CALL_STATE_TERMINATED;
+ break;
+ default:
+ call_info->state = MM_CALL_STATE_UNKNOWN;
+ break;
+ }
+
+ switch (qmi_call_information.direction) {
+ case QMI_VOICE_CALL_DIRECTION_UNKNOWN:
+ call_info->direction = MM_CALL_DIRECTION_UNKNOWN;
+ break;
+ case QMI_VOICE_CALL_DIRECTION_MO:
+ call_info->direction = MM_CALL_DIRECTION_OUTGOING;
+ break;
+ case QMI_VOICE_CALL_DIRECTION_MT:
+ call_info->direction = MM_CALL_DIRECTION_INCOMING;
+ break;
+ default:
+ call_info->direction = MM_CALL_DIRECTION_UNKNOWN;
+ break;
+ }
+
+ call_info_list = g_list_append (call_info_list, call_info);
+ }
+ }
+ }
+
+ mm_iface_modem_voice_report_all_calls (MM_IFACE_MODEM_VOICE (self), call_info_list);
+ mm_3gpp_call_info_list_free (call_info_list);
+}
+
+/*****************************************************************************/
+/* Supplementary service indication */
+
+static void
+supplementary_service_indication_cb (QmiClientVoice *client,
+ QmiIndicationVoiceSupplementaryServiceOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiVoiceSupplementaryServiceNotificationType notification_type;
+ guint8 call_id = 0;
+ g_autoptr(MMCallList) call_list = NULL;
+ MMBaseCall *call = NULL;
+
+ if (!qmi_indication_voice_supplementary_service_output_get_info (output, &call_id, &notification_type, NULL)) {
+ mm_obj_dbg (self, "Ignoring supplementary service indication: no call id or notification type given");
+ return;
+ }
+
+ /* Retrieve list of known calls */
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &call_list,
+ NULL);
+ if (!call_list) {
+ mm_obj_dbg (self, "Ignoring supplementary service indication: no call list exists");
+ return;
+ }
+
+ call = mm_call_list_get_call_by_index (call_list, call_id);
+ if (!call) {
+ mm_obj_dbg (self, "Ignoring supplementary service indication: no matching call exists");
+ return;
+ }
+
+ if (notification_type == QMI_VOICE_SUPPLEMENTARY_SERVICE_NOTIFICATION_TYPE_CALL_IS_ON_HOLD)
+ mm_base_call_change_state (call, MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
+ else if (notification_type == QMI_VOICE_SUPPLEMENTARY_SERVICE_NOTIFICATION_TYPE_CALL_IS_RETRIEVED)
+ mm_base_call_change_state (call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
+ else if (notification_type == QMI_VOICE_SUPPLEMENTARY_SERVICE_NOTIFICATION_TYPE_OUTGOING_CALL_IS_WAITING)
+ mm_base_call_change_state (call, MM_CALL_STATE_WAITING, MM_CALL_STATE_REASON_REFUSED_OR_BUSY);
+ else
+ mm_obj_dbg (self, "Ignoring supplementary service indication: unhandled notification type");
+}
+
+/*****************************************************************************/
+/* Setup/cleanup unsolicited events */
+
+static void
+parent_voice_setup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->setup_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "setting up parent voice unsolicited events failed: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_cleanup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->cleanup_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "cleaning up parent voice unsolicited events failed: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static gboolean
+common_voice_setup_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (setup == self->priv->all_call_status_unsolicited_events_setup) {
+ mm_obj_dbg (self, "voice unsolicited events already %s; skipping",
+ setup ? "setup" : "cleanup");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ self->priv->all_call_status_unsolicited_events_setup = setup;
+
+ if (setup) {
+ /* Connect QMI indications signals for calls */
+ g_assert (self->priv->all_call_status_indication_id == 0);
+ self->priv->all_call_status_indication_id =
+ g_signal_connect (client,
+ "all-call-status",
+ G_CALLBACK (all_call_status_indication_cb),
+ self);
+
+ /* Setup AT URCs as fall back for calls */
+ if (iface_modem_voice_parent->setup_unsolicited_events) {
+ iface_modem_voice_parent->setup_unsolicited_events (
+ MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback) parent_voice_setup_unsolicited_events_ready,
+ task);
+ return;
+ }
+
+ } else {
+ /* Disconnect QMI indications signals for calls */
+ g_assert (self->priv->all_call_status_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->all_call_status_indication_id);
+ self->priv->all_call_status_indication_id = 0;
+
+ /* Cleanup AT URCs as fall back for calls */
+ if (iface_modem_voice_parent->cleanup_unsolicited_events) {
+ iface_modem_voice_parent->cleanup_unsolicited_events (
+ MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback) parent_voice_cleanup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data);
+}
+
+static void
+modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_voice_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Enable/disable unsolicited events */
+
+static gboolean
+common_voice_enable_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+call_indication_register_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceIndicationRegisterOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_voice_indication_register_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_indication_register_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't register voice call indications: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+common_voice_enable_disable_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceIndicationRegisterInput) input = NULL;
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (enable == self->priv->all_call_status_unsolicited_events_enabled) {
+ mm_obj_dbg (self, "voice unsolicited events already %s; skipping",
+ enable ? "enabled" : "disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ self->priv->all_call_status_unsolicited_events_enabled = enable;
+
+ input = qmi_message_voice_indication_register_input_new ();
+ qmi_message_voice_indication_register_input_set_call_notification_events (input, enable, NULL);
+ qmi_message_voice_indication_register_input_set_supplementary_service_notification_events (input, enable, NULL);
+ qmi_client_voice_indication_register (QMI_CLIENT_VOICE (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) call_indication_register_ready,
+ task);
+}
+
+static void
+modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data);
+}
+
+static void
+modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Setup/cleanup in-call unsolicited events */
+
+static gboolean
+common_voice_setup_cleanup_in_call_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+common_voice_setup_in_call_cleanup_unsolicited_events (MMBroadbandModemQmi *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (setup == self->priv->supplementary_service_unsolicited_events_setup) {
+ mm_obj_dbg (self, "Supplementary service unsolicited events already %s; skipping",
+ setup ? "setup" : "cleanup");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ self->priv->supplementary_service_unsolicited_events_setup = setup;
+
+ if (setup) {
+ g_assert (self->priv->supplementary_service_indication_id == 0);
+ self->priv->supplementary_service_indication_id =
+ g_signal_connect (client,
+ "supplementary-service",
+ G_CALLBACK (supplementary_service_indication_cb),
+ self);
+ } else {
+ g_assert (self->priv->supplementary_service_indication_id != 0);
+ g_signal_handler_disconnect (client, self->priv->supplementary_service_indication_id);
+ self->priv->supplementary_service_indication_id = 0;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_setup_in_call_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_voice_setup_in_call_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), TRUE, callback, user_data);
+}
+
+static void
+modem_voice_cleanup_in_call_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_voice_setup_in_call_cleanup_unsolicited_events (MM_BROADBAND_MODEM_QMI (self), FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Load full list of calls (Voice interface) */
+
+static gboolean
+modem_voice_load_call_list_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GList **out_call_info_list,
+ GError **error)
+{
+ GList *call_info_list;
+ GError *inner_error = NULL;
+
+ call_info_list = g_task_propagate_pointer (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_assert (!call_info_list);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *out_call_info_list = call_info_list;
+ return TRUE;
+}
+
+static gboolean
+process_get_all_call_info (QmiClientVoice *client,
+ QmiMessageVoiceGetAllCallInfoOutput *output,
+ GList **out_call_info_list,
+ GError **error)
+{
+ GArray *qmi_remote_party_number_list = NULL;
+ GArray *qmi_call_information_list = NULL;
+ GList *call_info_list = NULL;
+ guint i;
+ guint j;
+
+ /* If TLVs missing, report an error */
+ if (!qmi_message_voice_get_all_call_info_output_get_remote_party_number (output, &qmi_remote_party_number_list, NULL) ||
+ !qmi_message_voice_get_all_call_info_output_get_call_information (output, &qmi_call_information_list, NULL)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Remote party number or call information not available");
+ return FALSE;
+ }
+
+ /* If there are no ongoing calls, the lists will be NULL */
+ if (!qmi_remote_party_number_list || !qmi_call_information_list) {
+ *out_call_info_list = NULL;
+ return TRUE;
+ }
+
+ for (i = 0; i < qmi_call_information_list->len; i++) {
+ QmiMessageVoiceGetAllCallInfoOutputCallInformationCall qmi_call_information;
+
+ qmi_call_information = g_array_index (qmi_call_information_list,
+ QmiMessageVoiceGetAllCallInfoOutputCallInformationCall,
+ i);
+ for (j = 0; j < qmi_remote_party_number_list->len; j++) {
+ QmiMessageVoiceGetAllCallInfoOutputRemotePartyNumberCall qmi_remote_party_number;
+
+ qmi_remote_party_number = g_array_index (qmi_remote_party_number_list,
+ QmiMessageVoiceGetAllCallInfoOutputRemotePartyNumberCall,
+ j);
+ if (qmi_call_information.id == qmi_remote_party_number.id) {
+ MMCallInfo *call_info;
+
+ call_info = g_slice_new0 (MMCallInfo);
+ call_info->index = qmi_call_information.id;
+ call_info->number = g_strdup (qmi_remote_party_number.type);
+
+ switch (qmi_call_information.state) {
+ case QMI_VOICE_CALL_STATE_UNKNOWN:
+ call_info->state = MM_CALL_STATE_UNKNOWN;
+ break;
+ case QMI_VOICE_CALL_STATE_ORIGINATION:
+ case QMI_VOICE_CALL_STATE_CC_IN_PROGRESS:
+ call_info->state = MM_CALL_STATE_DIALING;
+ break;
+ case QMI_VOICE_CALL_STATE_ALERTING:
+ call_info->state = MM_CALL_STATE_RINGING_OUT;
+ break;
+ case QMI_VOICE_CALL_STATE_SETUP:
+ case QMI_VOICE_CALL_STATE_INCOMING:
+ call_info->state = MM_CALL_STATE_RINGING_IN;
+ break;
+ case QMI_VOICE_CALL_STATE_CONVERSATION:
+ call_info->state = MM_CALL_STATE_ACTIVE;
+ break;
+ case QMI_VOICE_CALL_STATE_HOLD:
+ call_info->state = MM_CALL_STATE_HELD;
+ break;
+ case QMI_VOICE_CALL_STATE_WAITING:
+ call_info->state = MM_CALL_STATE_WAITING;
+ break;
+ case QMI_VOICE_CALL_STATE_DISCONNECTING:
+ case QMI_VOICE_CALL_STATE_END:
+ call_info->state = MM_CALL_STATE_TERMINATED;
+ break;
+ default:
+ call_info->state = MM_CALL_STATE_UNKNOWN;
+ break;
+ }
+
+ switch (qmi_call_information.direction) {
+ case QMI_VOICE_CALL_DIRECTION_UNKNOWN:
+ call_info->direction = MM_CALL_DIRECTION_UNKNOWN;
+ break;
+ case QMI_VOICE_CALL_DIRECTION_MO:
+ call_info->direction = MM_CALL_DIRECTION_OUTGOING;
+ break;
+ case QMI_VOICE_CALL_DIRECTION_MT:
+ call_info->direction = MM_CALL_DIRECTION_INCOMING;
+ break;
+ default:
+ call_info->direction = MM_CALL_DIRECTION_UNKNOWN;
+ break;
+ }
+
+ call_info_list = g_list_append (call_info_list, call_info);
+ }
+ }
+ }
+
+ *out_call_info_list = call_info_list;
+ return TRUE;
+}
+
+static void
+modem_voice_load_call_list_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceGetAllCallInfoOutput) output = NULL;
+ GError *error = NULL;
+ GList *call_info_list = NULL;
+
+ /* Parse QMI message */
+ output = qmi_client_voice_get_all_call_info_finish (client, res, &error);
+
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_get_all_call_info_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't run Get All Call Info action: ");
+ g_task_return_error (task, error);
+ } else if (!process_get_all_call_info (client, output, &call_info_list, &error)) {
+ g_prefix_error (&error, "Couldn't process Get All Call Info action: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_pointer (task, call_info_list, (GDestroyNotify)mm_3gpp_call_info_list_free);
+
+ g_object_unref (task);
+}
+
+static void
+modem_voice_load_call_list (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ GTask *task;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Update call list through QMI instead of AT+CLCC */
+ qmi_client_voice_get_all_call_info (QMI_CLIENT_VOICE (client),
+ NULL, /* no input data */
+ 10,
+ NULL,
+ (GAsyncReadyCallback) modem_voice_load_call_list_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Create CALL (Voice interface) */
+
+static MMBaseCall *
+modem_voice_create_call (MMIfaceModemVoice *self,
+ MMCallDirection direction,
+ const gchar *number)
+{
+ return mm_call_qmi_new (MM_BASE_MODEM (self),
+ direction,
+ number);
+}
+
+/*****************************************************************************/
+/* Common manage calls (Voice interface) */
+
+static gboolean
+common_manage_calls_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+common_manage_calls_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceManageCallsOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_voice_manage_calls_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_manage_calls_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't process manage calls action: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+common_manage_calls (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data,
+ QmiMessageVoiceManageCallsInput *input)
+{
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!input) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot perform call management operation: invalid input");
+ g_object_unref (task);
+ return;
+ }
+
+ qmi_client_voice_manage_calls (QMI_CLIENT_VOICE (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) common_manage_calls_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Hold and accept (Voice interface) */
+
+static gboolean
+modem_voice_hold_and_accept_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_manage_calls_finish (self, res, error);
+}
+
+static void
+modem_voice_hold_and_accept (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL;
+
+ input = qmi_message_voice_manage_calls_input_new ();
+ qmi_message_voice_manage_calls_input_set_service_type (
+ input,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_HOLD_ACTIVE_ACCEPT_WAITING_OR_HELD,
+ NULL);
+
+ common_manage_calls (self, callback, user_data, input);
+}
+
+/*****************************************************************************/
+/* Hangup and accept (Voice interface) */
+
+static gboolean
+modem_voice_hangup_and_accept_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_manage_calls_finish (self, res, error);
+}
+
+static void
+modem_voice_hangup_and_accept (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL;
+
+ input = qmi_message_voice_manage_calls_input_new ();
+ qmi_message_voice_manage_calls_input_set_service_type (
+ input,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING,
+ NULL);
+
+ common_manage_calls (self, callback, user_data, input);
+}
+
+/*****************************************************************************/
+/* Hangup all (Voice interface) */
+
+static gboolean
+modem_voice_hangup_all_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_manage_calls_finish (self, res, error);
+}
+
+static void
+modem_voice_hangup_all (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL;
+
+ input = qmi_message_voice_manage_calls_input_new ();
+ qmi_message_voice_manage_calls_input_set_service_type (
+ input,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_END_ALL_CALLS,
+ NULL);
+
+ common_manage_calls (self, callback, user_data, input);
+}
+
+/*****************************************************************************/
+/* Join multiparty (Voice interface) */
+
+static gboolean
+modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_manage_calls_finish (self, res, error);
+}
+
+static void
+modem_voice_join_multiparty (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL;
+
+ input = qmi_message_voice_manage_calls_input_new ();
+ qmi_message_voice_manage_calls_input_set_service_type (
+ input,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_MAKE_CONFERENCE_CALL,
+ NULL);
+
+ common_manage_calls (self, callback, user_data, input);
+}
+
+/*****************************************************************************/
+/* Leave multiparty (Voice interface) */
+
+static gboolean
+modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_manage_calls_finish (self, res, error);
+}
+
+static void
+modem_voice_leave_multiparty (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL;
+ guint idx = 0;
+
+ idx = mm_base_call_get_index (call);
+ if (idx != 0) {
+ input = qmi_message_voice_manage_calls_input_new ();
+ qmi_message_voice_manage_calls_input_set_call_id (
+ input,
+ idx,
+ NULL );
+ qmi_message_voice_manage_calls_input_set_service_type (
+ input,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_HOLD_ALL_EXCEPT_SPECIFIED_CALL,
+ NULL);
+ }
+
+ common_manage_calls (self, callback, user_data, input);
+}
+
+/*****************************************************************************/
+/* Transfer (Voice interface) */
+
+static gboolean
+modem_voice_transfer_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_manage_calls_finish (self, res, error);
+}
+
+static void
+modem_voice_transfer (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceManageCallsInput) input = NULL;
+
+ input = qmi_message_voice_manage_calls_input_new ();
+ qmi_message_voice_manage_calls_input_set_service_type (
+ input,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_TYPE_EXPLICIT_CALL_TRANSFER,
+ NULL);
+
+ common_manage_calls (self, callback, user_data, input);
+}
+
+/*****************************************************************************/
+/* Call waiting setup (Voice interface) */
+
+static gboolean
+modem_voice_call_waiting_setup_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+call_waiting_setup_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceSetSupplementaryServiceOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_voice_set_supplementary_service_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_set_supplementary_service_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't setup call waiting: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
+}
+
+static void
+modem_voice_call_waiting_setup (MMIfaceModemVoice *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceSetSupplementaryServiceInput) input = NULL;
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ input = qmi_message_voice_set_supplementary_service_input_new ();
+ qmi_message_voice_set_supplementary_service_input_set_supplementary_service_information (
+ input,
+ enable ? QMI_VOICE_SUPPLEMENTARY_SERVICE_ACTION_ACTIVATE : QMI_VOICE_SUPPLEMENTARY_SERVICE_ACTION_DEACTIVATE,
+ QMI_VOICE_SUPPLEMENTARY_SERVICE_REASON_CALL_WAITING,
+ NULL);
+
+ qmi_client_voice_set_supplementary_service (QMI_CLIENT_VOICE (client),
+ input,
+ 30,
+ NULL,
+ (GAsyncReadyCallback) call_waiting_setup_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Call waiting query (Voice interface) */
+
+typedef enum {
+ CLASS_NONE = 0x00,
+ CLASS_VOICE = 0x01,
+ CLASS_DATA = 0x02,
+ CLASS_FAX = 0x04,
+ CLASS_SMS = 0x08,
+ CLASS_DATACIRCUITSYNC = 0x10,
+ CLASS_DATACIRCUITASYNC = 0x20,
+ CLASS_PACKETACCESS = 0x40,
+ CLASS_PADACCESS = 0x80
+} SupplementaryServiceInformationClass;
+
+typedef enum {
+ ALL_TELESERVICES = CLASS_VOICE + CLASS_FAX + CLASS_SMS,
+ ALL_DATA_TELESERVICES = CLASS_FAX + CLASS_SMS,
+ ALL_TELESERVICES_EXCEPT_SMS = CLASS_VOICE + CLASS_FAX,
+ ALL_BEARER_SERVICES = CLASS_DATACIRCUITSYNC + CLASS_DATACIRCUITASYNC,
+ ALL_ASYNC_SERVICES = CLASS_DATACIRCUITASYNC + CLASS_PACKETACCESS,
+ ALL_SYNC_SERVICES = CLASS_DATACIRCUITSYNC + CLASS_PACKETACCESS,
+ ALL_SYNC_SERVICES_AND_TELEPHONY = CLASS_DATACIRCUITSYNC + CLASS_VOICE
+} SupplementaryServiceInformationClassCombination;
+
+static gboolean
+modem_voice_call_waiting_query_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ gboolean *status,
+ GError **error)
+{
+ gboolean ret;
+ GError *inner_error = NULL;
+
+ ret = g_task_propagate_boolean (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *status = ret;
+ return TRUE;
+}
+
+static void
+call_wait_query_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageVoiceGetCallWaitingOutput) output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_voice_get_call_waiting_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_get_call_waiting_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't query call waiting: ");
+ g_task_return_error (task, error);
+ } else {
+ guint8 service_class = 0;
+
+ if (!qmi_message_voice_get_call_waiting_output_get_service_class (output, &service_class, &error)) {
+ g_prefix_error (&error, "Couldn't get call waiting service class: ");
+ g_task_return_error (task, error);
+ } else if (service_class == CLASS_VOICE || service_class == ALL_TELESERVICES || service_class == ALL_TELESERVICES_EXCEPT_SMS || service_class == ALL_SYNC_SERVICES_AND_TELEPHONY) {
+ g_task_return_boolean (task, TRUE);
+ } else {
+ g_task_return_boolean (task, FALSE);
+ }
+ }
+
+ g_object_unref (task);
+}
+
+static void
+modem_voice_call_waiting_query (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageVoiceGetCallWaitingInput) input = NULL;
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ input = qmi_message_voice_get_call_waiting_input_new ();
+ qmi_client_voice_get_call_waiting (QMI_CLIENT_VOICE (client),
+ input,
+ 30,
+ NULL,
+ (GAsyncReadyCallback) call_wait_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer info loading */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+get_lte_attach_parameters_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageWdsGetLteAttachParametersOutput) output = NULL;
+ GError *error = NULL;
+ MMBearerProperties *properties;
+ const gchar *apn;
+ QmiWdsIpSupportType ip_support_type;
+
+ output = qmi_client_wds_get_lte_attach_parameters_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_wds_get_lte_attach_parameters_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get LTE attach parameters: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ properties = mm_bearer_properties_new ();
+ if (qmi_message_wds_get_lte_attach_parameters_output_get_apn (output, &apn, NULL))
+ mm_bearer_properties_set_apn (properties, apn);
+ if (qmi_message_wds_get_lte_attach_parameters_output_get_ip_support_type (output, &ip_support_type, NULL)) {
+ MMBearerIpFamily ip_family;
+
+ ip_family = mm_bearer_ip_family_from_qmi_ip_support_type (ip_support_type);
+ if (ip_family != MM_BEARER_IP_FAMILY_NONE)
+ mm_bearer_properties_set_ip_type (properties, ip_family);
+ }
+ g_task_return_pointer (task, properties, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ qmi_client_wds_get_lte_attach_parameters (QMI_CLIENT_WDS (client),
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) get_lte_attach_parameters_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer settings setting */
+
+typedef enum {
+ SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST,
+ SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE,
+ SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN,
+ SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE,
+ SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP,
+ SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LAST_SETTING,
+} SetInitialEpsBearerSettingsStep;
+
+typedef struct {
+ SetInitialEpsBearerSettingsStep step;
+ MM3gppProfile *profile;
+ MMModemPowerState power_state;
+} SetInitialEpsBearerSettingsContext;
+
+static void
+set_initial_eps_bearer_settings_context_free (SetInitialEpsBearerSettingsContext *ctx)
+{
+ g_clear_object (&ctx->profile);
+ g_slice_free (SetInitialEpsBearerSettingsContext, ctx);
+}
+
+static gboolean
+modem_3gpp_set_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void set_initial_eps_bearer_settings_step (GTask *task);
+
+static void
+set_initial_eps_bearer_power_up_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetInitialEpsBearerSettingsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!modem_power_up_down_off_finish (self, res, &error)) {
+ g_prefix_error (&error, "Couldn't power up modem: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_modify_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ SetInitialEpsBearerSettingsContext *ctx;
+ g_autoptr(MM3gppProfile) stored = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error);
+ if (!stored) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_modify_profile (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ SetInitialEpsBearerSettingsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_iface_modem_3gpp_profile_manager_set_profile (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ ctx->profile,
+ TRUE,
+ (GAsyncReadyCallback)set_initial_eps_bearer_modify_profile_ready,
+ task);
+}
+
+static void
+set_initial_eps_bearer_power_down_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetInitialEpsBearerSettingsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!modem_power_up_down_off_finish (self, res, &error)) {
+ g_prefix_error (&error, "Couldn't power down modem: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_load_power_state_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetInitialEpsBearerSettingsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->power_state = load_power_state_finish (self, res, &error);
+ if (ctx->power_state == MM_MODEM_POWER_STATE_UNKNOWN) {
+ g_prefix_error (&error, "Couldn't load power state: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_initial_eps_bearer_settings_step (task);
+}
+
+static void
+set_initial_eps_bearer_settings_step (GTask *task)
+{
+ SetInitialEpsBearerSettingsContext *ctx;
+ MMBroadbandModemQmi *self;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LOAD_POWER_STATE:
+ mm_obj_dbg (self, "querying current power state...");
+ load_power_state (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback) set_initial_eps_bearer_load_power_state_ready,
+ task);
+ return;
+
+ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_DOWN:
+ if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
+ mm_obj_dbg (self, "powering down before changing initial EPS bearer settings...");
+ modem_power_down (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback) set_initial_eps_bearer_power_down_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_MODIFY_PROFILE:
+ mm_obj_dbg (self, "modifying initial EPS bearer settings profile...");
+ set_initial_eps_bearer_modify_profile (task);
+ return;
+
+ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_POWER_UP:
+ if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
+ mm_obj_dbg (self, "powering up after changing initial EPS bearer settings...");
+ modem_power_up (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback) set_initial_eps_bearer_power_up_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SET_INITIAL_EPS_BEARER_SETTINGS_STEP_LAST_SETTING:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_3gpp_set_initial_eps_bearer_settings (MMIfaceModem3gpp *_self,
+ MMBearerProperties *config,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ SetInitialEpsBearerSettingsContext *ctx;
+ GTask *task;
+ MM3gppProfile *profile;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->default_attach_pdn) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown default LTE attach APN index");
+ g_object_unref (task);
+ return;
+ }
+
+ profile = mm_bearer_properties_peek_3gpp_profile (config);
+ mm_3gpp_profile_set_profile_id (profile, self->priv->default_attach_pdn);
+
+ ctx = g_slice_new0 (SetInitialEpsBearerSettingsContext);
+ ctx->profile = g_object_ref (profile);
+ ctx->step = SET_INITIAL_EPS_BEARER_SETTINGS_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) set_initial_eps_bearer_settings_context_free);
+
+ set_initial_eps_bearer_settings_step (task);
+}
+
+/*****************************************************************************/
+/* Initial EPS bearer settings loading */
+
+static MMBearerProperties *
+modem_3gpp_load_initial_eps_bearer_settings_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_BEARER_PROPERTIES (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+load_initial_eps_bearer_get_profile_ready (MMIfaceModem3gppProfileManager *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+ MMBearerProperties *properties;
+
+ profile = mm_iface_modem_3gpp_profile_manager_get_profile_finish (_self, res, &error);
+ if (!profile) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ properties = mm_bearer_properties_new_from_profile (profile, &error);
+ if (!properties)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, properties, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+load_initial_eps_bearer_get_profile_settings (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+
+ self = g_task_get_source_object (task);
+
+ mm_iface_modem_3gpp_profile_manager_get_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ self->priv->default_attach_pdn,
+ (GAsyncReadyCallback)load_initial_eps_bearer_get_profile_ready,
+ task);
+}
+
+static void
+load_initial_eps_bearer_get_lte_attach_pdn_list_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageWdsGetLteAttachPdnListOutput) output = NULL;
+ MMBroadbandModemQmi *self;
+ GError *error = NULL;
+ GArray *current_list = NULL;
+ guint i;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_wds_get_lte_attach_pdn_list_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_wds_get_lte_attach_pdn_list_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get LTE attach PDN list: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ qmi_message_wds_get_lte_attach_pdn_list_output_get_current_list (output, &current_list, NULL);
+ if (!current_list || !current_list->len) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Undefined list of LTE attach PDN");
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "Found %u LTE attach PDNs defined", current_list->len);
+ for (i = 0; i < current_list->len; i++) {
+ if (i == 0) {
+ self->priv->default_attach_pdn = g_array_index (current_list, guint16, i);
+ mm_obj_dbg (self, "Default LTE attach PDN profile: %u", self->priv->default_attach_pdn);
+ } else
+ mm_obj_dbg (self, "Additional LTE attach PDN profile: %u", g_array_index (current_list, guint16, i));
+ }
+
+ load_initial_eps_bearer_get_profile_settings (task);
+}
+
+static void
+modem_3gpp_load_initial_eps_bearer_settings (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Default attach PDN is assumed to never change during runtime
+ * (we don't change it) so just load it the first time */
+ if (!self->priv->default_attach_pdn) {
+ QmiClient *client;
+ GError *error = NULL;
+
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ &error);
+ if (!client) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "querying LTE attach PDN list...");
+ qmi_client_wds_get_lte_attach_pdn_list (QMI_CLIENT_WDS (client),
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)load_initial_eps_bearer_get_lte_attach_pdn_list_ready,
+ task);
+ return;
+ }
+
+ load_initial_eps_bearer_get_profile_settings (task);
+}
+
+/*****************************************************************************/
/* Check firmware support (Firmware interface) */
typedef struct {
- gchar *build_id;
- GArray *modem_unique_id;
- GArray *pri_unique_id;
- gboolean current;
+ gchar *build_id;
+ GArray *modem_unique_id;
+ GArray *pri_unique_id;
+ gboolean current;
} FirmwarePair;
static void
@@ -9298,245 +10263,260 @@ firmware_pair_free (FirmwarePair *pair)
}
typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientDms *client;
- GSimpleAsyncResult *result;
- GList *pairs;
- GList *l;
-} FirmwareCheckSupportContext;
+ QmiClientDms *client;
+ GList *pairs;
+ FirmwarePair *current_pair;
+ MMFirmwareProperties *current_firmware;
+ gboolean skip_image_info;
+} FirmwareListPreloadContext;
static void
-firmware_check_support_context_complete_and_free (FirmwareCheckSupportContext *ctx)
+firmware_list_preload_context_free (FirmwareListPreloadContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
+ g_clear_object (&ctx->current_firmware);
+ g_clear_pointer (&ctx->current_pair, (GDestroyNotify)firmware_pair_free);
g_list_free_full (ctx->pairs, (GDestroyNotify)firmware_pair_free);
- g_object_unref (ctx->self);
g_object_unref (ctx->client);
- g_slice_free (FirmwareCheckSupportContext, ctx);
+ g_slice_free (FirmwareListPreloadContext, ctx);
}
static gboolean
-firmware_check_support_finish (MMIfaceModemFirmware *self,
- GAsyncResult *res,
- GError **error)
+firmware_list_preload_finish (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GError **error)
{
- /* Never fails, just says TRUE or FALSE */
- return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+store_preloaded_firmware_image_info (MMBroadbandModemQmi *self,
+ MMFirmwareProperties *firmware,
+ gboolean running)
+{
+ self->priv->firmware_list = g_list_append (self->priv->firmware_list, g_object_ref (firmware));
+
+ /* If this is is also the running image, keep an extra reference to it */
+ if (running) {
+ if (self->priv->current_firmware)
+ mm_obj_warn (self, "a running firmware is already set (%s), not setting '%s'",
+ mm_firmware_properties_get_unique_id (self->priv->current_firmware),
+ mm_firmware_properties_get_unique_id (firmware));
+ else
+ self->priv->current_firmware = g_object_ref (firmware);
+ }
}
-static void get_next_image_info (FirmwareCheckSupportContext *ctx);
+static void get_next_image_info (GTask *task);
static void
get_pri_image_info_ready (QmiClientDms *client,
GAsyncResult *res,
- FirmwareCheckSupportContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ FirmwareListPreloadContext *ctx;
QmiMessageDmsGetStoredImageInfoOutput *output;
- GError *error = NULL;
- FirmwarePair *current;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- current = (FirmwarePair *)ctx->l->data;
+ g_assert (ctx->current_pair);
+ g_assert (ctx->current_firmware);
output = qmi_client_dms_get_stored_image_info_finish (client, res, &error);
- if (!output ||
- !qmi_message_dms_get_stored_image_info_output_get_result (output, &error)) {
- mm_warn ("Couldn't get detailed info for PRI image with build ID '%s': %s",
- current->build_id,
- error->message);
+ if (!output || !qmi_message_dms_get_stored_image_info_output_get_result (output, &error)) {
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND))
+ ctx->skip_image_info = TRUE;
+ else
+ mm_obj_dbg (self, "couldn't get detailed info for PRI image with build ID '%s': %s",
+ ctx->current_pair->build_id, error->message);
g_error_free (error);
- } else {
- gchar *unique_id_str;
- MMFirmwareProperties *firmware;
-
- firmware = mm_firmware_properties_new (MM_FIRMWARE_IMAGE_TYPE_GOBI,
- current->build_id);
-
- unique_id_str = mm_utils_bin2hexstr ((const guint8 *)current->pri_unique_id->data,
- current->pri_unique_id->len);
- mm_firmware_properties_set_gobi_pri_unique_id (firmware, unique_id_str);
- g_free (unique_id_str);
-
- unique_id_str = mm_utils_bin2hexstr ((const guint8 *)current->modem_unique_id->data,
- current->modem_unique_id->len);
- mm_firmware_properties_set_gobi_modem_unique_id (firmware, unique_id_str);
- g_free (unique_id_str);
-
- /* Boot version (optional) */
- {
- guint16 boot_major_version;
- guint16 boot_minor_version;
-
- if (qmi_message_dms_get_stored_image_info_output_get_boot_version (
- output,
- &boot_major_version,
- &boot_minor_version,
- NULL)) {
- gchar *aux;
-
- aux = g_strdup_printf ("%u.%u", boot_major_version, boot_minor_version);
- mm_firmware_properties_set_gobi_boot_version (firmware, aux);
- g_free (aux);
- }
- }
-
- /* PRI version (optional) */
- {
- guint32 pri_version;
- const gchar *pri_info;
+ goto out;
+ }
- if (qmi_message_dms_get_stored_image_info_output_get_pri_version (
- output,
- &pri_version,
- &pri_info,
- NULL)) {
- gchar *aux;
+ /* Boot version (optional) */
+ {
+ guint16 boot_major_version;
+ guint16 boot_minor_version;
- aux = g_strdup_printf ("%u", pri_version);
- mm_firmware_properties_set_gobi_pri_version (firmware, aux);
- g_free (aux);
+ if (qmi_message_dms_get_stored_image_info_output_get_boot_version (
+ output,
+ &boot_major_version,
+ &boot_minor_version,
+ NULL)) {
+ gchar *aux;
- mm_firmware_properties_set_gobi_pri_info (firmware, pri_info);
- }
+ aux = g_strdup_printf ("%u.%u", boot_major_version, boot_minor_version);
+ mm_firmware_properties_set_gobi_boot_version (ctx->current_firmware, aux);
+ g_free (aux);
}
+ }
- /* Add firmware image to our internal list */
- ctx->self->priv->firmware_list = g_list_append (ctx->self->priv->firmware_list,
- firmware);
+ /* PRI version (optional) */
+ {
+ guint32 pri_version;
+ const gchar *pri_info;
- /* If this is is also the current image running, keep it */
- if (current->current) {
- if (ctx->self->priv->current_firmware)
- mm_warn ("A current firmware is already set (%s), not setting '%s' as current",
- mm_firmware_properties_get_unique_id (ctx->self->priv->current_firmware),
- current->build_id);
- else
- ctx->self->priv->current_firmware = g_object_ref (firmware);
+ if (qmi_message_dms_get_stored_image_info_output_get_pri_version (
+ output,
+ &pri_version,
+ &pri_info,
+ NULL)) {
+ gchar *aux;
- }
+ aux = g_strdup_printf ("%u", pri_version);
+ mm_firmware_properties_set_gobi_pri_version (ctx->current_firmware, aux);
+ g_free (aux);
- qmi_message_dms_get_stored_image_info_output_unref (output);
+ mm_firmware_properties_set_gobi_pri_info (ctx->current_firmware, pri_info);
+ }
}
+out:
+
+ /* We're done with this image */
+ store_preloaded_firmware_image_info (self, ctx->current_firmware, ctx->current_pair->current);
+ g_clear_object (&ctx->current_firmware);
+ g_clear_pointer (&ctx->current_pair, (GDestroyNotify)firmware_pair_free);
+
/* Go on to the next one */
- ctx->l = g_list_next (ctx->l);
- get_next_image_info (ctx);
+ get_next_image_info (task);
+
+ if (output)
+ qmi_message_dms_get_stored_image_info_output_unref (output);
}
-static void
-get_next_image_info (FirmwareCheckSupportContext *ctx)
+static MMFirmwareProperties *
+create_firmware_properties_from_pair (FirmwarePair *pair,
+ GError **error)
{
- QmiMessageDmsGetStoredImageInfoInputImage image_id;
- QmiMessageDmsGetStoredImageInfoInput *input;
- FirmwarePair *current;
+ gchar *pri_unique_id_str = NULL;
+ gchar *modem_unique_id_str = NULL;
+ gchar *firmware_unique_id_str = NULL;
+ MMFirmwareProperties *firmware = NULL;
- if (!ctx->l) {
- /* We're done */
+ /* If the string is ASCII, use it without converting to HEX */
- if (!ctx->self->priv->firmware_list) {
- mm_warn ("No valid firmware images listed. "
- "Assuming firmware unsupported.");
- g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- firmware_check_support_context_complete_and_free (ctx);
- return;
- }
+ pri_unique_id_str = mm_qmi_unique_id_to_firmware_unique_id (pair->pri_unique_id, error);
+ if (!pri_unique_id_str)
+ goto out;
- current = (FirmwarePair *)ctx->l->data;
+ modem_unique_id_str = mm_qmi_unique_id_to_firmware_unique_id (pair->modem_unique_id, error);
+ if (!modem_unique_id_str)
+ goto out;
- /* Query PRI image info */
- image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI;
- image_id.unique_id = current->pri_unique_id;
- image_id.build_id = current->build_id;
- input = qmi_message_dms_get_stored_image_info_input_new ();
- qmi_message_dms_get_stored_image_info_input_set_image (input, &image_id, NULL);
- qmi_client_dms_get_stored_image_info (ctx->client,
- input,
- 10,
- NULL,
- (GAsyncReadyCallback)get_pri_image_info_ready,
- ctx);
- qmi_message_dms_get_stored_image_info_input_unref (input);
+ /* We will always append the PRI unique ID to the build id to form the unique id
+ * used by the API, because it may happen that a device holds multiple PRI images
+ * for the same build ID.
+ *
+ * E.g. we could have a single modem image (e.g. 02.14.03.00) and then two or more
+ * different PRI images with the same build ID (e.g. 02.14.03.00_VODAFONE) but
+ * different unique IDs (e.g. 000.008_000 and 000.016_000).
+ */
+ firmware_unique_id_str = g_strdup_printf ("%s_%s", pair->build_id, pri_unique_id_str);
+
+ firmware = mm_firmware_properties_new (MM_FIRMWARE_IMAGE_TYPE_GOBI, firmware_unique_id_str);
+ mm_firmware_properties_set_gobi_pri_unique_id (firmware, pri_unique_id_str);
+ mm_firmware_properties_set_gobi_modem_unique_id (firmware, modem_unique_id_str);
+
+out:
+ g_free (firmware_unique_id_str);
+ g_free (pri_unique_id_str);
+ g_free (modem_unique_id_str);
+
+ return firmware;
}
static void
-list_stored_images_ready (QmiClientDms *client,
- GAsyncResult *res,
- FirmwareCheckSupportContext *ctx)
+get_next_image_info (GTask *task)
{
- GArray *array;
- gint pri_id;
- gint modem_id;
- guint i;
- guint j;
- QmiMessageDmsListStoredImagesOutputListImage *image_pri;
- QmiMessageDmsListStoredImagesOutputListImage *image_modem;
- QmiMessageDmsListStoredImagesOutput *output;
+ MMBroadbandModemQmi *self;
+ FirmwareListPreloadContext *ctx;
+ GError *error = NULL;
- output = qmi_client_dms_list_stored_images_finish (client, res, NULL);
- if (!output ||
- !qmi_message_dms_list_stored_images_output_get_result (output, NULL)) {
- /* Assume firmware unsupported */
- g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
- firmware_check_support_context_complete_and_free (ctx);
- if (output)
- qmi_message_dms_list_stored_images_output_unref (output);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!ctx->pairs) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- qmi_message_dms_list_stored_images_output_get_list (
- output,
- &array,
- NULL);
-
- /* Find which index corresponds to each image type */
- pri_id = -1;
- modem_id = -1;
- for (i = 0; i < array->len; i++) {
- QmiMessageDmsListStoredImagesOutputListImage *image;
+ /* Take next pair to process from list head */
+ ctx->current_pair = (FirmwarePair *)ctx->pairs->data;
+ ctx->pairs = g_list_delete_link (ctx->pairs, ctx->pairs);
- image = &g_array_index (array,
- QmiMessageDmsListStoredImagesOutputListImage,
- i);
-
- switch (image->type) {
- case QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI:
- if (pri_id != -1)
- mm_warn ("Multiple array elements found with PRI type");
- else
- pri_id = (gint)i;
- break;
- case QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM:
- if (modem_id != -1)
- mm_warn ("Multiple array elements found with MODEM type");
- else
- modem_id = (gint)i;
- break;
- default:
- break;
- }
+ /* Build firmware properties */
+ ctx->current_firmware = create_firmware_properties_from_pair (ctx->current_pair, &error);
+ if (!ctx->current_firmware) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- if (pri_id < 0 || modem_id < 0) {
- mm_warn ("We need both PRI (%s) and MODEM (%s) images. "
- "Assuming firmware unsupported.",
- pri_id < 0 ? "not found" : "found",
- modem_id < 0 ? "not found" : "found");
- g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
- firmware_check_support_context_complete_and_free (ctx);
- qmi_message_dms_list_stored_images_output_unref (output);
+ /* Now, load additional optional information for the PRI image */
+ if (!ctx->skip_image_info) {
+ QmiMessageDmsGetStoredImageInfoInputImage image_id;
+ QmiMessageDmsGetStoredImageInfoInput *input;
+
+ image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI;
+ image_id.unique_id = ctx->current_pair->pri_unique_id;
+ image_id.build_id = ctx->current_pair->build_id;
+ input = qmi_message_dms_get_stored_image_info_input_new ();
+ qmi_message_dms_get_stored_image_info_input_set_image (input, &image_id, NULL);
+ qmi_client_dms_get_stored_image_info (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)get_pri_image_info_ready,
+ task);
+ qmi_message_dms_get_stored_image_info_input_unref (input);
return;
}
- /* Loop PRI images and try to find a pairing MODEM image with same boot ID */
- image_pri = &g_array_index (array,
- QmiMessageDmsListStoredImagesOutputListImage,
- pri_id);
- image_modem = &g_array_index (array,
- QmiMessageDmsListStoredImagesOutputListImage,
- modem_id);
+ /* If we shouldn't be loading additional image info, we're done with this image */
+ store_preloaded_firmware_image_info (self, ctx->current_firmware, ctx->current_pair->current);
+ g_clear_object (&ctx->current_firmware);
+ g_clear_pointer (&ctx->current_pair, (GDestroyNotify)firmware_pair_free);
+
+ /* Go on to the next one */
+ get_next_image_info (task);
+}
+
+static gboolean
+match_images (const gchar *pri_id, const gchar *modem_id)
+{
+ gsize modem_id_len;
+
+ if (!pri_id || !modem_id)
+ return FALSE;
+
+ if (g_str_equal (pri_id, modem_id))
+ return TRUE;
+
+ /* If the Modem image build_id ends in '?' just use a prefix match. eg,
+ * assume that modem="02.08.02.00_?" matches pri="02.08.02.00_ATT" or
+ * pri="02.08.02.00_GENERIC".
+ */
+ modem_id_len = strlen (modem_id);
+ if (modem_id[modem_id_len - 1] != '?')
+ return FALSE;
+
+ return strncmp (pri_id, modem_id, modem_id_len - 1) == 0;
+}
+
+static GList *
+find_image_pairs (MMBroadbandModemQmi *self,
+ QmiMessageDmsListStoredImagesOutputListImage *image_pri,
+ QmiMessageDmsListStoredImagesOutputListImage *image_modem,
+ GError **error)
+{
+ guint i, j;
+ GList *pairs = NULL;
+ /* Loop PRI images and try to find a pairing MODEM image with same build ID */
for (i = 0; i < image_pri->sublist->len; i++) {
QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement *subimage_pri;
@@ -9550,79 +10530,217 @@ list_stored_images_ready (QmiClientDms *client,
QmiMessageDmsListStoredImagesOutputListImageSublistSublistElement,
j);
- if (g_str_equal (subimage_pri->build_id, subimage_modem->build_id)) {
+ if (match_images (subimage_pri->build_id, subimage_modem->build_id)) {
FirmwarePair *pair;
- mm_dbg ("Found pairing PRI+MODEM images with build ID '%s'", subimage_pri->build_id);
+ mm_obj_dbg (self, "found pairing PRI+MODEM images with build ID '%s'", subimage_pri->build_id);
pair = g_slice_new (FirmwarePair);
pair->build_id = g_strdup (subimage_pri->build_id);
pair->modem_unique_id = g_array_ref (subimage_modem->unique_id);
pair->pri_unique_id = g_array_ref (subimage_pri->unique_id);
+
+ /* We're using the PRI 'index_of_running_image' only as source to select
+ * which is the current running firmware. This avoids issues with the wrong
+ * 'index_of_running_image' reported for the MODEM images, see:
+ * https://forum.sierrawireless.com/t/mc74xx-wrong-running-image-in-qmi-get-stored-images/8998
+ */
pair->current = (image_pri->index_of_running_image == i ? TRUE : FALSE);
- ctx->pairs = g_list_append (ctx->pairs, pair);
+
+ pairs = g_list_append (pairs, pair);
break;
}
}
if (j == image_modem->sublist->len)
- mm_dbg ("Pairing for PRI image with build ID '%s' not found", subimage_pri->build_id);
+ mm_obj_dbg (self, "pairing for PRI image with build ID '%s' not found", subimage_pri->build_id);
}
+ if (!pairs)
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, "No valid PRI+MODEM pairs found");
+
+ return pairs;
+}
+
+static gboolean
+find_image_type_indices (MMBroadbandModemQmi *self,
+ GArray *array,
+ QmiMessageDmsListStoredImagesOutputListImage **image_pri,
+ QmiMessageDmsListStoredImagesOutputListImage **image_modem,
+ GError **error)
+{
+ guint i;
+
+ g_assert (array);
+ g_assert (image_pri);
+ g_assert (image_modem);
+
+ *image_pri = NULL;
+ *image_modem = NULL;
+
+ /* The MODEM image list is usually given before the PRI image list, but
+ * let's not assume that. Try to find both lists and report at which index
+ * we can find each. */
+
+ for (i = 0; i < array->len; i++) {
+ QmiMessageDmsListStoredImagesOutputListImage *image;
+
+ image = &g_array_index (array, QmiMessageDmsListStoredImagesOutputListImage, i);
+ switch (image->type) {
+ case QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI:
+ if (*image_pri != NULL)
+ mm_obj_dbg (self, "multiple array elements found with PRI type: ignoring additional list at index %u", i);
+ else
+ *image_pri = image;
+ break;
+ case QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM:
+ if (*image_modem != NULL)
+ mm_obj_dbg (self, "multiple array elements found with MODEM type: ignoring additional list at index %u", i);
+ else
+ *image_modem = image;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!(*image_pri) || !(*image_modem)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Missing image list: pri list %s, modem list %s",
+ !(*image_pri) ? "not found" : "found",
+ !(*image_modem) ? "not found" : "found");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+list_stored_images_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ FirmwareListPreloadContext *ctx;
+ GArray *array;
+ QmiMessageDmsListStoredImagesOutputListImage *image_pri;
+ QmiMessageDmsListStoredImagesOutputListImage *image_modem;
+ QmiMessageDmsListStoredImagesOutput *output;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* Read array from output */
+ output = qmi_client_dms_list_stored_images_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_dms_list_stored_images_output_get_result (output, &error) ||
+ !qmi_message_dms_list_stored_images_output_get_list (output, &array, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* Find which index corresponds to each image type */
+ if (!find_image_type_indices (self, array, &image_pri, &image_modem, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* Build firmware PRI+MODEM pair list */
+ ctx->pairs = find_image_pairs (self, image_pri, image_modem, &error);
if (!ctx->pairs) {
- mm_warn ("No valid PRI+MODEM pairs found. "
- "Assuming firmware unsupported.");
- g_simple_async_result_set_op_res_gboolean (ctx->result, FALSE);
- firmware_check_support_context_complete_and_free (ctx);
- qmi_message_dms_list_stored_images_output_unref (output);
- return;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
}
- /* Firmware is supported; now keep on loading info for each image and cache it */
- qmi_message_dms_list_stored_images_output_unref (output);
+ /* Now keep on loading info for each image and cache it */
+ get_next_image_info (task);
+
+out:
- ctx->l = ctx->pairs;
- get_next_image_info (ctx);
+ if (output)
+ qmi_message_dms_list_stored_images_output_unref (output);
}
static void
-firmware_check_support (MMIfaceModemFirmware *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+firmware_list_preload (MMBroadbandModemQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- FirmwareCheckSupportContext *ctx;
- QmiClient *client = NULL;
+ FirmwareListPreloadContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
- ctx = g_slice_new0 (FirmwareCheckSupportContext);
- 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,
- firmware_check_support);
+ ctx = g_slice_new0 (FirmwareListPreloadContext);
+ ctx->client = QMI_CLIENT_DMS (g_object_ref (client));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)firmware_list_preload_context_free);
- mm_dbg ("loading firmware images...");
+ mm_obj_dbg (self, "loading firmware images...");
qmi_client_dms_list_stored_images (QMI_CLIENT_DMS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)list_stored_images_ready,
- ctx);
+ task);
}
/*****************************************************************************/
/* Load firmware list (Firmware interface) */
+static void
+firmware_list_free (GList *firmware_list)
+{
+ g_list_free_full (firmware_list, g_object_unref);
+}
+
static GList *
firmware_load_list_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
- return (GList *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+firmware_load_list_preloaded (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ GList *dup;
+
+ self = g_task_get_source_object (task);
+ g_assert (self->priv->firmware_list_preloaded);
+
+ dup = g_list_copy_deep (self->priv->firmware_list, (GCopyFunc)g_object_ref, NULL);
+ if (dup)
+ g_task_return_pointer (task, dup, (GDestroyNotify)firmware_list_free);
+ else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "firmware list unknown");
+ g_object_unref (task);
+}
+
+static void
+firmware_list_preload_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!firmware_list_preload_finish (self, res, &error)) {
+ mm_obj_dbg (self, "firmware list loading failed: %s", error ? error->message : "unsupported");
+ g_clear_error (&error);
+ }
+
+ firmware_load_list_preloaded (task);
}
static void
@@ -9631,21 +10749,20 @@ firmware_load_list (MMIfaceModemFirmware *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
- GList *dup;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- firmware_load_list);
+ GTask *task;
- /* We'll return the new list of new references we create here */
- dup = g_list_copy (self->priv->firmware_list);
- g_list_foreach (dup, (GFunc)g_object_ref, NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->firmware_list_preloaded) {
+ firmware_load_list_preloaded (task);
+ return;
+ }
- g_simple_async_result_set_op_res_gpointer (result, dup, NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ self->priv->firmware_list_preloaded = TRUE;
+ firmware_list_preload (self,
+ (GAsyncReadyCallback)firmware_list_preload_ready,
+ task);
}
/*****************************************************************************/
@@ -9656,7 +10773,7 @@ firmware_load_current_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
- return (MMFirmwareProperties *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -9665,39 +10782,29 @@ firmware_load_current (MMIfaceModemFirmware *_self,
gpointer user_data)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- firmware_load_current);
-
- /* We'll return the reference we create here */
- g_simple_async_result_set_op_res_gpointer (
- result,
- self->priv->current_firmware ? g_object_ref (self->priv->current_firmware) : NULL,
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ if (self->priv->current_firmware)
+ g_task_return_pointer (task,
+ g_object_ref (self->priv->current_firmware),
+ g_object_unref);
+ else
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "current firmware unknown");
+ g_object_unref (task);
}
/*****************************************************************************/
/* Change current firmware (Firmware interface) */
typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientDms *client;
- GSimpleAsyncResult *result;
MMFirmwareProperties *firmware;
} FirmwareChangeCurrentContext;
static void
-firmware_change_current_context_complete_and_free (FirmwareChangeCurrentContext *ctx)
+firmware_change_current_context_free (FirmwareChangeCurrentContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_object_unref (ctx->client);
if (ctx->firmware)
g_object_unref (ctx->firmware);
g_slice_free (FirmwareChangeCurrentContext, ctx);
@@ -9708,51 +10815,55 @@ firmware_change_current_finish (MMIfaceModemFirmware *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-firmware_power_cycle_ready (MMBroadbandModemQmi *self,
- GAsyncResult *res,
- FirmwareChangeCurrentContext *ctx)
+firmware_reset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
- if (!power_cycle_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
+ if (!mm_shared_qmi_reset_finish (self, res, &error))
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- firmware_change_current_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
}
static void
firmware_select_stored_image_ready (QmiClientDms *client,
GAsyncResult *res,
- FirmwareChangeCurrentContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
QmiMessageDmsSetFirmwarePreferenceOutput *output;
GError *error = NULL;
output = qmi_client_dms_set_firmware_preference_finish (client, res, &error);
if (!output) {
- g_simple_async_result_take_error (ctx->result, error);
- firmware_change_current_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_dms_set_firmware_preference_output_get_result (output, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- firmware_change_current_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
qmi_message_dms_set_firmware_preference_output_unref (output);
return;
}
+ self = g_task_get_source_object (task);
+
qmi_message_dms_set_firmware_preference_output_unref (output);
/* Now, go into offline mode */
- power_cycle (ctx->self,
- (GAsyncReadyCallback)firmware_power_cycle_ready,
- ctx);
+ mm_shared_qmi_reset (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)firmware_reset_ready,
+ task);
}
static MMFirmwareProperties *
@@ -9797,57 +10908,56 @@ find_firmware_properties_by_gobi_pri_info_substring (MMBroadbandModemQmi *self,
}
static void
-firmware_change_current (MMIfaceModemFirmware *self,
- const gchar *unique_id,
- GAsyncReadyCallback callback,
- gpointer user_data)
+firmware_change_current (MMIfaceModemFirmware *_self,
+ const gchar *unique_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- QmiMessageDmsSetFirmwarePreferenceInput *input;
- FirmwareChangeCurrentContext *ctx;
- QmiClient *client = NULL;
- GArray *array;
- QmiMessageDmsSetFirmwarePreferenceInputListImage modem_image_id;
- QmiMessageDmsSetFirmwarePreferenceInputListImage pri_image_id;
- guint8 *tmp;
- gsize tmp_len;
+ MMBroadbandModemQmi *self;
+ GTask *task;
+ FirmwareChangeCurrentContext *ctx;
+ GError *error = NULL;
+ QmiClient *client = NULL;
+ GArray *array;
+ QmiMessageDmsSetFirmwarePreferenceInput *input = NULL;
+ QmiMessageDmsSetFirmwarePreferenceInputListImage modem_image_id = { 0 };
+ QmiMessageDmsSetFirmwarePreferenceInputListImage pri_image_id = { 0 };
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ self = MM_BROADBAND_MODEM_QMI (_self);
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
return;
ctx = g_slice_new0 (FirmwareChangeCurrentContext);
- 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,
- firmware_change_current);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)firmware_change_current_context_free);
/* Look for the firmware image with the requested unique ID */
- ctx->firmware = find_firmware_properties_by_unique_id (ctx->self, unique_id);
+ ctx->firmware = find_firmware_properties_by_unique_id (self, unique_id);
if (!ctx->firmware) {
guint n = 0;
/* Ok, let's look at the PRI info */
- ctx->firmware = find_firmware_properties_by_gobi_pri_info_substring (ctx->self, unique_id, &n);
+ ctx->firmware = find_firmware_properties_by_gobi_pri_info_substring (self, unique_id, &n);
if (n > 1) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "Multiple firmware images (%u) found matching '%s' as PRI info substring",
- n, unique_id);
- firmware_change_current_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Multiple firmware images (%u) found matching '%s' as PRI info substring",
+ n, unique_id);
+ g_object_unref (task);
return;
}
if (n == 0) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "Firmware with unique ID '%s' wasn't found",
- unique_id);
- firmware_change_current_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "Firmware with unique ID '%s' wasn't found",
+ unique_id);
+ g_object_unref (task);
return;
}
@@ -9855,38 +10965,42 @@ firmware_change_current (MMIfaceModemFirmware *self,
}
/* If we're already in the requested firmware, we're done */
- if (ctx->self->priv->current_firmware &&
- g_str_equal (mm_firmware_properties_get_unique_id (ctx->self->priv->current_firmware),
+ if (self->priv->current_firmware &&
+ g_str_equal (mm_firmware_properties_get_unique_id (self->priv->current_firmware),
mm_firmware_properties_get_unique_id (ctx->firmware))) {
- mm_dbg ("Modem is already running firmware image '%s'",
- mm_firmware_properties_get_unique_id (ctx->self->priv->current_firmware));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- firmware_change_current_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "modem is already running firmware image '%s'",
+ mm_firmware_properties_get_unique_id (self->priv->current_firmware));
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Modem image ID */
- tmp_len = 0;
- tmp = (guint8 *)mm_utils_hexstr2bin (mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware), &tmp_len);
modem_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_MODEM;
modem_image_id.build_id = (gchar *)mm_firmware_properties_get_unique_id (ctx->firmware);
- modem_image_id.unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len);
- g_array_insert_vals (modem_image_id.unique_id, 0, tmp, tmp_len);
- g_free (tmp);
+ modem_image_id.unique_id = mm_firmware_unique_id_to_qmi_unique_id (mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware), &error);
+ if (!modem_image_id.unique_id) {
+ g_prefix_error (&error, "Couldn't build modem image unique id: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
/* PRI image ID */
- tmp_len = 0;
- tmp = (guint8 *)mm_utils_hexstr2bin (mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware), &tmp_len);
pri_image_id.type = QMI_DMS_FIRMWARE_IMAGE_TYPE_PRI;
pri_image_id.build_id = (gchar *)mm_firmware_properties_get_unique_id (ctx->firmware);
- pri_image_id.unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len);
- g_array_insert_vals (pri_image_id.unique_id, 0, tmp, tmp_len);
- g_free (tmp);
+ pri_image_id.unique_id = mm_firmware_unique_id_to_qmi_unique_id (mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware), &error);
+ if (!pri_image_id.unique_id) {
+ g_prefix_error (&error, "Couldn't build PRI image unique id: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
- mm_dbg ("Changing Gobi firmware to MODEM '%s' and PRI '%s' with Build ID '%s'...",
- mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware),
- mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware),
- unique_id);
+ mm_obj_dbg (self, "changing Gobi firmware to MODEM '%s' and PRI '%s' with Build ID '%s'...",
+ mm_firmware_properties_get_gobi_modem_unique_id (ctx->firmware),
+ mm_firmware_properties_get_gobi_pri_unique_id (ctx->firmware),
+ unique_id);
/* Build array of image IDs */
array = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageDmsSetFirmwarePreferenceInputListImage), 2);
@@ -9895,16 +11009,23 @@ firmware_change_current (MMIfaceModemFirmware *self,
input = qmi_message_dms_set_firmware_preference_input_new ();
qmi_message_dms_set_firmware_preference_input_set_list (input, array, NULL);
+ g_array_unref (array);
+
qmi_client_dms_set_firmware_preference (
- ctx->client,
+ QMI_CLIENT_DMS (client),
input,
10,
NULL,
(GAsyncReadyCallback)firmware_select_stored_image_ready,
- ctx);
- g_array_unref (modem_image_id.unique_id);
- g_array_unref (pri_image_id.unique_id);
- qmi_message_dms_set_firmware_preference_input_unref (input);
+ task);
+
+out:
+ if (modem_image_id.unique_id)
+ g_array_unref (modem_image_id.unique_id);
+ if (pri_image_id.unique_id)
+ g_array_unref (pri_image_id.unique_id);
+ if (input)
+ qmi_message_dms_set_firmware_preference_input_unref (input);
}
/*****************************************************************************/
@@ -9916,7 +11037,7 @@ signal_check_support_finish (MMIfaceModemSignal *self,
GError **error)
{
- return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -9924,25 +11045,22 @@ signal_check_support (MMIfaceModemSignal *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
- MMPortQmi *port;
- gboolean supported = FALSE;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- signal_check_support);
+ GTask *task;
- port = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
+ task = g_task_new (self, NULL, callback, user_data);
/* If NAS service is available, assume either signal info or signal strength are supported */
- if (port)
- supported = !!mm_port_qmi_peek_client (port, QMI_SERVICE_NAS, MM_PORT_QMI_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);
+ if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL)) {
+ mm_obj_dbg (self, "extended signal capabilities not supported");
+ g_task_return_boolean (task, FALSE);
+ } else {
+ mm_obj_dbg (self, "extended signal capabilities supported");
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -9961,45 +11079,35 @@ typedef struct {
MMSignal *gsm;
MMSignal *umts;
MMSignal *lte;
+ MMSignal *nr5g;
} SignalLoadValuesResult;
typedef struct {
- MMBroadbandModemQmi *self;
- QmiClientNas *client;
- GSimpleAsyncResult *result;
- SignalLoadValuesStep step;
+ QmiClientNas *client;
+ 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_clear_object (&result->cdma);
+ g_clear_object (&result->evdo);
+ g_clear_object (&result->gsm);
+ g_clear_object (&result->lte);
g_slice_free (SignalLoadValuesResult, result);
}
static void
-signal_load_values_context_complete_and_free (SignalLoadValuesContext *ctx)
+signal_load_values_context_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_clear_pointer (&ctx->values_result, (GDestroyNotify)signal_load_values_result_free);
g_slice_free (SignalLoadValuesContext, ctx);
}
static gdouble
-get_db_from_sinr_level (QmiNasEvdoSinrLevel level)
+get_db_from_sinr_level (MMBroadbandModemQmi *self,
+ QmiNasEvdoSinrLevel level)
{
switch (level) {
case QMI_NAS_EVDO_SINR_LEVEL_0: return -9.0;
@@ -10012,58 +11120,63 @@ get_db_from_sinr_level (QmiNasEvdoSinrLevel level)
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);
+ mm_obj_warn (self, "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)
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
{
SignalLoadValuesResult *values_result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ values_result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!values_result)
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;
-
+ *nr5g = values_result->nr5g ? g_object_ref (values_result->nr5g) : NULL;
+ signal_load_values_result_free (values_result);
return TRUE;
}
-static void signal_load_values_context_step (SignalLoadValuesContext *ctx);
+static void signal_load_values_context_step (GTask *task);
static void
signal_load_values_get_signal_strength_ready (QmiClientNas *client,
GAsyncResult *res,
- SignalLoadValuesContext *ctx)
+ GTask *task)
{
- QmiMessageNasGetSignalStrengthOutput *output;
- GArray *array;
- gint32 aux_int32;
- gint16 aux_int16;
- gint8 aux_int8;
- QmiNasRadioInterface radio_interface;
- QmiNasEvdoSinrLevel sinr;
+ MMBroadbandModemQmi *self;
+ SignalLoadValuesContext *ctx;
+ GArray *array;
+ gint32 aux_int32;
+ gint16 aux_int16;
+ gint8 aux_int8;
+ QmiNasRadioInterface radio_interface;
+ QmiNasEvdoSinrLevel sinr;
+ g_autoptr(QmiMessageNasGetSignalStrengthOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
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);
+ signal_load_values_context_step (task);
return;
}
@@ -10112,6 +11225,11 @@ signal_load_values_get_signal_strength_ready (QmiClientNas *client,
ctx->values_result->lte = mm_signal_new ();
mm_signal_set_rssi (ctx->values_result->lte, (gdouble)element->rssi);
break;
+ case QMI_NAS_RADIO_INTERFACE_UNKNOWN:
+ case QMI_NAS_RADIO_INTERFACE_NONE:
+ case QMI_NAS_RADIO_INTERFACE_AMPS:
+ case QMI_NAS_RADIO_INTERFACE_TD_SCDMA:
+ case QMI_NAS_RADIO_INTERFACE_5GNR:
default:
break;
}
@@ -10141,6 +11259,13 @@ signal_load_values_get_signal_strength_ready (QmiClientNas *client,
mm_signal_set_ecio (ctx->values_result->umts, ((gdouble)element->ecio) * (-0.5));
break;
default:
+ case QMI_NAS_RADIO_INTERFACE_GSM:
+ case QMI_NAS_RADIO_INTERFACE_LTE:
+ case QMI_NAS_RADIO_INTERFACE_UNKNOWN:
+ case QMI_NAS_RADIO_INTERFACE_NONE:
+ case QMI_NAS_RADIO_INTERFACE_AMPS:
+ case QMI_NAS_RADIO_INTERFACE_TD_SCDMA:
+ case QMI_NAS_RADIO_INTERFACE_5GNR:
break;
}
}
@@ -10174,37 +11299,39 @@ signal_load_values_get_signal_strength_ready (QmiClientNas *client,
/* 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));
+ mm_signal_set_sinr (ctx->values_result->evdo, get_db_from_sinr_level (self, sinr));
}
- qmi_message_nas_get_signal_strength_output_unref (output);
-
/* Go on */
ctx->step++;
- signal_load_values_context_step (ctx);
+ signal_load_values_context_step (task);
}
static void
signal_load_values_get_signal_info_ready (QmiClientNas *client,
GAsyncResult *res,
- SignalLoadValuesContext *ctx)
+ GTask *task)
{
- QmiMessageNasGetSignalInfoOutput *output;
- gint8 rssi;
- gint16 ecio;
- QmiNasEvdoSinrLevel sinr_level;
- gint32 io;
- gint8 rsrq;
- gint16 rsrp;
- gint16 snr;
+ MMBroadbandModemQmi *self;
+ SignalLoadValuesContext *ctx;
+ gint8 rssi;
+ gint16 ecio;
+ QmiNasEvdoSinrLevel sinr_level;
+ gint32 io;
+ gint8 rsrq;
+ gint16 rsrp;
+ gint16 snr;
+ gint16 rsrq_5g;
+ g_autoptr(QmiMessageNasGetSignalInfoOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
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);
+ signal_load_values_context_step (task);
return;
}
@@ -10231,7 +11358,7 @@ signal_load_values_get_signal_info_ready (QmiClientNas *client,
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_sinr (ctx->values_result->evdo, get_db_from_sinr_level (self, sinr_level));
mm_signal_set_io (ctx->values_result->evdo, (gdouble)io);
}
@@ -10267,16 +11394,31 @@ signal_load_values_get_signal_info_ready (QmiClientNas *client,
mm_signal_set_snr (ctx->values_result->lte, (0.1) * ((gdouble)snr));
}
- qmi_message_nas_get_signal_info_output_unref (output);
+ /* 5G */
+ if (qmi_message_nas_get_signal_info_output_get_5g_signal_strength (output,
+ &rsrp,
+ &snr,
+ NULL)) {
+ ctx->values_result->nr5g = mm_signal_new ();
+ mm_signal_set_rsrp (ctx->values_result->nr5g, (gdouble)rsrp);
+ mm_signal_set_snr (ctx->values_result->nr5g, (gdouble)snr);
+ }
+
+ if (qmi_message_nas_get_signal_info_output_get_5g_signal_strength_extended (output,
+ &rsrq_5g,
+ NULL)) {
+ mm_signal_set_rsrq (ctx->values_result->nr5g, (gdouble)rsrq_5g);
+ }
/* Keep on */
ctx->step++;
- signal_load_values_context_step (ctx);
+ signal_load_values_context_step (task);
}
static void
-signal_load_values_context_step (SignalLoadValuesContext *ctx)
+signal_load_values_context_step (GTask *task)
{
+ SignalLoadValuesContext *ctx;
#define VALUES_RESULT_LOADED(ctx) \
(ctx->values_result && \
@@ -10286,67 +11428,65 @@ signal_load_values_context_step (SignalLoadValuesContext *ctx)
ctx->values_result->umts || \
ctx->values_result->lte))
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST:
ctx->step++;
- /* Fall down */
+ /* Fall through */
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);
+ qmi_client_nas_get_signal_info (ctx->client,
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)signal_load_values_get_signal_info_ready,
+ task);
+ return;
+
+ case SIGNAL_LOAD_VALUES_STEP_SIGNAL_STRENGTH:
+ /* If already loaded with signal info, don't try signal strength */
+ if (!VALUES_RESULT_LOADED (ctx)) {
+ g_autoptr(QmiMessageNasGetSignalStrengthInput) input = NULL;
+
+ 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,
+ task);
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 */
+ /* Fall through */
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);
+ if (VALUES_RESULT_LOADED (ctx))
+ g_task_return_pointer (task,
+ g_steal_pointer (&ctx->values_result),
+ (GDestroyNotify)signal_load_values_result_free);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "No way to load extended signal information");
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -10355,102 +11495,301 @@ signal_load_values_context_step (SignalLoadValuesContext *ctx)
}
static void
-signal_load_values (MMIfaceModemSignal *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
SignalLoadValuesContext *ctx;
- QmiClient *client = NULL;
+ GTask *task;
+ QmiClient *client = NULL;
- mm_dbg ("loading extended signal information...");
+ mm_obj_dbg (self, "loading extended signal information...");
- if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_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->client = QMI_CLIENT_NAS (g_object_ref (client));
ctx->step = SIGNAL_LOAD_VALUES_STEP_SIGNAL_FIRST;
- signal_load_values_context_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)signal_load_values_context_free);
+
+ signal_load_values_context_step (task);
+}
+
+/*****************************************************************************/
+/* Reset data interfaces during initialization */
+
+typedef struct {
+ GList *ports;
+ MMPort *data;
+ MMPortQmi *qmi;
+} ResetPortsContext;
+
+static void
+reset_ports_context_free (ResetPortsContext *ctx)
+{
+ g_assert (!ctx->data);
+ g_assert (!ctx->qmi);
+ g_list_free_full (ctx->ports, g_object_unref);
+ g_slice_free (ResetPortsContext, ctx);
+}
+
+static gboolean
+reset_ports_finish (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void reset_next_port (GTask *task);
+
+static void
+port_qmi_reset_ready (MMPortQmi *qmi,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ ResetPortsContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_qmi_reset_finish (qmi, res, &error))
+ mm_obj_warn (self, "couldn't reset QMI port '%s' with data interface '%s': %s",
+ mm_port_get_device (MM_PORT (ctx->qmi)),
+ mm_port_get_device (ctx->data),
+ error->message);
+
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->qmi);
+ reset_next_port (task);
+}
+
+static void
+reset_next_port (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ ResetPortsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!ctx->ports) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* steal full data port reference from list head */
+ ctx->data = ctx->ports->data;
+ ctx->ports = g_list_delete_link (ctx->ports, ctx->ports);
+
+ ctx->qmi = mm_broadband_modem_qmi_get_port_qmi_for_data (self, ctx->data, NULL, NULL);
+ if (!ctx->qmi) {
+ mm_obj_dbg (self, "no QMI port associated to data port '%s': ignoring data interface reset",
+ mm_port_get_device (ctx->data));
+ g_clear_object (&ctx->data);
+ reset_next_port (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "running QMI port '%s' reset with data interface '%s'",
+ mm_port_get_device (MM_PORT (ctx->qmi)), mm_port_get_device (ctx->data));
+
+ mm_port_qmi_reset (ctx->qmi,
+ ctx->data,
+ (GAsyncReadyCallback) port_qmi_reset_ready,
+ task);
+}
+
+static void
+reset_ports (MMBroadbandModemQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ ResetPortsContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (ResetPortsContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)reset_ports_context_free);
+
+ ctx->ports = mm_base_modem_find_ports (MM_BASE_MODEM (self),
+ MM_PORT_SUBSYS_UNKNOWN,
+ MM_PORT_TYPE_NET);
+
+ reset_next_port (task);
}
/*****************************************************************************/
/* First enabling step */
static gboolean
-enabling_started_finish (MMBroadbandModem *self,
- GAsyncResult *res,
- GError **error)
+enabling_started_finish (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-parent_enabling_started_ready (MMBroadbandModem *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+wds_set_autoconnect_settings_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ MMBroadbandModemQmi *self;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(QmiMessageWdsSetAutoconnectSettingsOutput) output = NULL;
- if (!MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->enabling_started_finish (
- self,
- res,
- &error)) {
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_wds_set_autoconnect_settings_finish (client, res, &error);
+ if (!output || !qmi_message_wds_set_autoconnect_settings_output_get_result (output, &error))
+ mm_obj_warn (self, "failed disabling autoconnect: %s", error->message);
+ else
+ mm_obj_info (self, "autoconnect explicitly disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+wds_get_autoconnect_settings_ready (QmiClientWds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self;
+ QmiWdsAutoconnectSetting autoconnect_setting;
+ g_autoptr(GError) error = NULL;
+ g_autoptr(QmiMessageWdsSetAutoconnectSettingsInput) input = NULL;
+ g_autoptr(QmiMessageWdsGetAutoconnectSettingsOutput) output = NULL;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_wds_get_autoconnect_settings_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_wds_get_autoconnect_settings_output_get_result (output, &error) ||
+ !qmi_message_wds_get_autoconnect_settings_output_get_status (output, &autoconnect_setting, &error)) {
+ mm_obj_warn (self, "failed checking whether autoconnect is disabled or not: %s", error->message);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (autoconnect_setting != QMI_WDS_AUTOCONNECT_SETTING_ENABLED) {
+ mm_obj_dbg (self, "autoconnect is already disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "need to explicitly disable autoconnect");
+ input = qmi_message_wds_set_autoconnect_settings_input_new ();
+ qmi_message_wds_set_autoconnect_settings_input_set_status (input, QMI_WDS_AUTOCONNECT_SETTING_DISABLED, NULL);
+ qmi_client_wds_set_autoconnect_settings (client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) wds_set_autoconnect_settings_ready,
+ task);
+}
+
+static void
+parent_enabling_started_ready (MMBroadbandModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ QmiClient *client = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->enabling_started_finish (_self, res, &error)) {
/* Don't treat this as fatal. Parent enabling may fail if it cannot grab a primary
* AT port, which isn't really an issue in QMI-based modems */
- mm_dbg ("Couldn't start parent enabling: %s", error->message);
- g_error_free (error);
+ mm_obj_dbg (self, "couldn't start parent enabling: %s", error->message);
+ }
+
+ /* If the autoconnect check has already been done, we're finished */
+ if (self->priv->autoconnect_checked) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* The connection logic doesn't work properly when the device is already set
+ * to autoconnect, so automatically disable autoconnect ourselves. */
+ mm_obj_dbg (self, "need to check whether autoconnect is disabled or not...");
+ self->priv->autoconnect_checked = TRUE;
+
+ /* Use default WDS client to query autoconnect settings */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_WDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+
+ if (!client) {
+ mm_obj_warn (self, "cannot check whether autoconnect is disabled or not: couldn't peek default WDS client");
+ /* not fatal, just assume autoconnect is disabled */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
}
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ qmi_client_wds_get_autoconnect_settings (QMI_CLIENT_WDS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) wds_get_autoconnect_settings_ready,
+ task);
}
static void
-enabling_started (MMBroadbandModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+enabling_started (MMBroadbandModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enabling_started);
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->enabling_started (
self,
(GAsyncReadyCallback)parent_enabling_started_ready,
- result);
+ task);
}
/*****************************************************************************/
/* First initialization step */
+static const QmiService qmi_services[] = {
+ QMI_SERVICE_DMS,
+ QMI_SERVICE_NAS,
+ QMI_SERVICE_WDS,
+ QMI_SERVICE_WMS,
+ QMI_SERVICE_PDS,
+ QMI_SERVICE_OMA,
+ QMI_SERVICE_UIM,
+ QMI_SERVICE_LOC,
+ QMI_SERVICE_PDC,
+ QMI_SERVICE_VOICE,
+};
+
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMPortQmi *qmi;
- QmiService services[32];
guint service_index;
} InitializationStartedContext;
static void
-initialization_started_context_complete_and_free (InitializationStartedContext *ctx)
+initialization_started_context_free (InitializationStartedContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->qmi)
g_object_unref (ctx->qmi);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -10459,17 +11798,13 @@ initialization_started_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- /* Just parent's pointer passed here */
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
parent_initialization_started_ready (MMBroadbandModem *self,
GAsyncResult *res,
- InitializationStartedContext *ctx)
+ GTask *task)
{
gpointer parent_ctx;
GError *error = NULL;
@@ -10481,100 +11816,231 @@ parent_initialization_started_ready (MMBroadbandModem *self,
if (error) {
/* Don't treat this as fatal. Parent initialization may fail if it cannot grab a primary
* AT port, which isn't really an issue in QMI-based modems */
- mm_dbg ("Couldn't start parent initialization: %s", error->message);
+ mm_obj_dbg (self, "couldn't start parent initialization: %s", error->message);
g_error_free (error);
}
- g_simple_async_result_set_op_res_gpointer (ctx->result, parent_ctx, NULL);
- initialization_started_context_complete_and_free (ctx);
+ /* Just parent's pointer passed here */
+ g_task_return_pointer (task, parent_ctx, NULL);
+ g_object_unref (task);
}
static void
-parent_initialization_started (InitializationStartedContext *ctx)
+parent_initialization_started (GTask *task)
{
+ MMBroadbandModem *self;
+
+ self = g_task_get_source_object (task);
MM_BROADBAND_MODEM_CLASS (mm_broadband_modem_qmi_parent_class)->initialization_started (
- ctx->self,
+ self,
(GAsyncReadyCallback)parent_initialization_started_ready,
- ctx);
+ task);
+}
+
+static void
+qmi_device_removed_cb (QmiDevice *device,
+ MMBroadbandModemQmi *self)
+{
+ /* Reprobe the modem here so we can get notifications back. */
+ mm_obj_info (self, "connection to qmi-proxy for %s lost, reprobing",
+ qmi_device_get_path_display (device));
+
+ g_signal_handler_disconnect (device, self->priv->qmi_device_removed_id);
+ self->priv->qmi_device_removed_id = 0;
+
+ mm_base_modem_set_reprobe (MM_BASE_MODEM (self), TRUE);
+ mm_base_modem_set_valid (MM_BASE_MODEM (self), FALSE);
+}
+
+static gboolean
+track_qmi_device_removed (MMBroadbandModemQmi *self,
+ MMPortQmi *qmi,
+ GError **error)
+{
+ QmiDevice *device;
+
+ device = mm_port_qmi_peek_device (qmi);
+ if (!device) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot track QMI device removal: QMI port no longer available");
+ return FALSE;
+ }
+
+ self->priv->qmi_device_removed_id = g_signal_connect (
+ device,
+ QMI_DEVICE_SIGNAL_REMOVED,
+ G_CALLBACK (qmi_device_removed_cb),
+ self);
+ return TRUE;
+}
+
+static void
+untrack_qmi_device_removed (MMBroadbandModemQmi *self,
+ MMPortQmi *qmi)
+{
+ QmiDevice *device;
+
+ if (self->priv->qmi_device_removed_id == 0)
+ return;
+
+ device = mm_port_qmi_peek_device (qmi);
+ if (!device)
+ return;
+
+ g_signal_handler_disconnect (device, self->priv->qmi_device_removed_id);
+ self->priv->qmi_device_removed_id = 0;
}
-static void allocate_next_client (InitializationStartedContext *ctx);
+static void allocate_next_client (GTask *task);
static void
qmi_port_allocate_client_ready (MMPortQmi *qmi,
GAsyncResult *res,
- InitializationStartedContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ InitializationStartedContext *ctx;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (!mm_port_qmi_allocate_client_finish (qmi, res, &error)) {
- mm_dbg ("Couldn't allocate client for service '%s': %s",
- qmi_service_get_string (ctx->services[ctx->service_index]),
- error->message);
+ mm_obj_dbg (self, "couldn't allocate client for service '%s': %s",
+ qmi_service_get_string (qmi_services[ctx->service_index]),
+ error->message);
g_error_free (error);
}
ctx->service_index++;
- allocate_next_client (ctx);
+ allocate_next_client (task);
}
static void
-allocate_next_client (InitializationStartedContext *ctx)
+allocate_next_client (GTask *task)
{
- if (ctx->services[ctx->service_index] == QMI_SERVICE_UNKNOWN) {
- /* Done we are, launch parent's callback */
- parent_initialization_started (ctx);
+ InitializationStartedContext *ctx;
+ MMBroadbandModemQmi *self;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->service_index == G_N_ELEMENTS (qmi_services)) {
+ GError *error = NULL;
+
+ /* Done we are, track device removal and launch next step */
+ if (!track_qmi_device_removed (self, ctx->qmi, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ parent_initialization_started (task);
return;
}
/* Otherwise, allocate next client */
mm_port_qmi_allocate_client (ctx->qmi,
- ctx->services[ctx->service_index],
+ qmi_services[ctx->service_index],
MM_PORT_QMI_FLAG_DEFAULT,
NULL,
(GAsyncReadyCallback)qmi_port_allocate_client_ready,
- ctx);
+ task);
}
static void
qmi_port_open_ready_no_data_format (MMPortQmi *qmi,
GAsyncResult *res,
- InitializationStartedContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!mm_port_qmi_open_finish (qmi, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- initialization_started_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- allocate_next_client (ctx);
+ allocate_next_client (task);
}
static void
qmi_port_open_ready (MMPortQmi *qmi,
GAsyncResult *res,
- InitializationStartedContext *ctx)
+ GTask *task)
{
+ MMBroadbandModemQmi *self;
+ InitializationStartedContext *ctx;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (!mm_port_qmi_open_finish (qmi, res, &error)) {
/* Really, really old devices (Gobi 1K, 2008-era firmware) may not
* support SetDataFormat, so if we get an error opening the port
* try without it. The qmi_wwan driver will fix up any issues that
* the device might have between raw-ip and 802.3 mode anyway.
*/
+ mm_obj_dbg (self, "couldn't open QMI port with data format update: %s", error->message);
+ g_error_free (error);
mm_port_qmi_open (ctx->qmi,
FALSE,
NULL,
(GAsyncReadyCallback)qmi_port_open_ready_no_data_format,
- ctx);
+ task);
return;
}
- allocate_next_client (ctx);
+ allocate_next_client (task);
+}
+
+static void
+initialization_open_port (GTask *task)
+{
+ InitializationStartedContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ mm_port_qmi_open (ctx->qmi,
+ TRUE,
+ NULL,
+ (GAsyncReadyCallback)qmi_port_open_ready,
+ task);
+}
+
+static void
+reset_ports_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!reset_ports_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ initialization_open_port (task);
+}
+
+static void
+initialization_reset_ports (GTask *task)
+{
+ MMBroadbandModemQmi *self;
+
+ self = g_task_get_source_object (task);
+
+ /* reseting the data interfaces is really only needed if the device
+ * hasn't been hotplugged */
+ if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (self))) {
+ mm_obj_dbg (self, "not running data interface reset procedure: device is hotplugged");
+ initialization_open_port (task);
+ return;
+ }
+
+ reset_ports (self, (GAsyncReadyCallback)reset_ports_ready, task);
}
static void
@@ -10583,45 +12049,39 @@ initialization_started (MMBroadbandModem *self,
gpointer user_data)
{
InitializationStartedContext *ctx;
+ GTask *task;
+ GError *error = NULL;
ctx = g_new0 (InitializationStartedContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- initialization_started);
- ctx->qmi = mm_base_modem_get_port_qmi (MM_BASE_MODEM (self));
+ ctx->qmi = mm_broadband_modem_qmi_get_port_qmi (MM_BROADBAND_MODEM_QMI (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_started_context_free);
/* This may happen if we unplug the modem unexpectedly */
if (!ctx->qmi) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot initialize: QMI port went missing");
- initialization_started_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot initialize: QMI port no longer available");
+ g_object_unref (task);
return;
}
if (mm_port_qmi_is_open (ctx->qmi)) {
- /* Nothing to be done, just launch parent's callback */
- parent_initialization_started (ctx);
+ /* Nothing to be done, just track device removal and launch parent's
+ * callback */
+ if (!track_qmi_device_removed (MM_BROADBAND_MODEM_QMI (self), ctx->qmi, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ parent_initialization_started (task);
return;
}
- /* Setup services to open */
- ctx->services[0] = QMI_SERVICE_DMS;
- ctx->services[1] = QMI_SERVICE_NAS;
- ctx->services[2] = QMI_SERVICE_WMS;
- ctx->services[3] = QMI_SERVICE_PDS;
- ctx->services[4] = QMI_SERVICE_OMA;
- ctx->services[5] = QMI_SERVICE_UNKNOWN;
-
- /* Now open our QMI port */
- mm_port_qmi_open (ctx->qmi,
- TRUE,
- NULL,
- (GAsyncReadyCallback)qmi_port_open_ready,
- ctx);
+ initialization_reset_ports (task);
}
/*****************************************************************************/
@@ -10639,6 +12099,11 @@ mm_broadband_modem_qmi_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* QMI bearer supports NET only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, TRUE,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, FALSE,
NULL);
}
@@ -10654,16 +12119,8 @@ mm_broadband_modem_qmi_init (MMBroadbandModemQmi *self)
static void
finalize (GObject *object)
{
- MMPortQmi *qmi;
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (object);
- qmi = mm_base_modem_peek_port_qmi (MM_BASE_MODEM (self));
- /* If we did open the QMI port during initialization, close it now */
- if (qmi &&
- mm_port_qmi_is_open (qmi)) {
- mm_port_qmi_close (qmi);
- }
-
g_free (self->priv->imei);
g_free (self->priv->meid);
g_free (self->priv->esn);
@@ -10671,8 +12128,6 @@ finalize (GObject *object)
g_free (self->priv->current_operator_description);
if (self->priv->supported_bands)
g_array_unref (self->priv->supported_bands);
- if (self->priv->supported_radio_interfaces)
- g_array_unref (self->priv->supported_radio_interfaces);
G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->finalize (object);
}
@@ -10681,8 +12136,21 @@ static void
dispose (GObject *object)
{
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (object);
+ MMPortQmi *qmi;
+
+ /* If any port cleanup is needed, it must be done during dispose(), as
+ * the modem object will be affected by an explicit g_object_run_dispose()
+ * that will remove all port references right away */
+ qmi = mm_broadband_modem_qmi_peek_port_qmi (self);
+ if (qmi) {
+ /* Disconnect signal handler for qmi-proxy disappearing, if it exists */
+ untrack_qmi_device_removed (self, qmi);
+ /* If we did open the QMI port during initialization, close it now */
+ if (mm_port_qmi_is_open (qmi))
+ mm_port_qmi_close (qmi, NULL, NULL);
+ }
- g_list_free_full (self->priv->firmware_list, (GDestroyNotify)g_object_unref);
+ g_list_free_full (self->priv->firmware_list, g_object_unref);
self->priv->firmware_list = NULL;
g_clear_object (&self->priv->current_firmware);
@@ -10694,18 +12162,20 @@ static void
iface_modem_init (MMIfaceModem *iface)
{
/* Initialization steps */
- iface->load_current_capabilities = modem_load_current_capabilities;
- iface->load_current_capabilities_finish = modem_load_current_capabilities_finish;
- iface->load_supported_capabilities = modem_load_supported_capabilities;
- iface->load_supported_capabilities_finish = modem_load_supported_capabilities_finish;
- iface->set_current_capabilities = set_current_capabilities;
- iface->set_current_capabilities_finish = set_current_capabilities_finish;
+ iface->load_current_capabilities = mm_shared_qmi_load_current_capabilities;
+ iface->load_current_capabilities_finish = mm_shared_qmi_load_current_capabilities_finish;
+ iface->load_supported_capabilities = mm_shared_qmi_load_supported_capabilities;
+ iface->load_supported_capabilities_finish = mm_shared_qmi_load_supported_capabilities_finish;
+ iface->set_current_capabilities = mm_shared_qmi_set_current_capabilities;
+ iface->set_current_capabilities_finish = mm_shared_qmi_set_current_capabilities_finish;
iface->load_manufacturer = modem_load_manufacturer;
iface->load_manufacturer_finish = modem_load_manufacturer_finish;
- iface->load_model = modem_load_model;
- iface->load_model_finish = modem_load_model_finish;
+ iface->load_model = mm_shared_qmi_load_model;
+ iface->load_model_finish = mm_shared_qmi_load_model_finish;
iface->load_revision = modem_load_revision;
iface->load_revision_finish = modem_load_revision_finish;
+ iface->load_hardware_revision = modem_load_hardware_revision;
+ iface->load_hardware_revision_finish = modem_load_hardware_revision_finish;
iface->load_equipment_identifier = modem_load_equipment_identifier;
iface->load_equipment_identifier_finish = modem_load_equipment_identifier_finish;
iface->load_device_identifier = modem_load_device_identifier;
@@ -10716,18 +12186,24 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_unlock_required_finish = modem_load_unlock_required_finish;
iface->load_unlock_retries = modem_load_unlock_retries;
iface->load_unlock_retries_finish = modem_load_unlock_retries_finish;
- iface->load_supported_bands = modem_load_supported_bands;
- iface->load_supported_bands_finish = modem_load_supported_bands_finish;
- iface->load_supported_modes = modem_load_supported_modes;
- iface->load_supported_modes_finish = modem_load_supported_modes_finish;
+ iface->load_supported_bands = mm_shared_qmi_load_supported_bands;
+ iface->load_supported_bands_finish = mm_shared_qmi_load_supported_bands_finish;
+ iface->load_supported_modes = mm_shared_qmi_load_supported_modes;
+ iface->load_supported_modes_finish = mm_shared_qmi_load_supported_modes_finish;
iface->load_power_state = load_power_state;
iface->load_power_state_finish = load_power_state_finish;
iface->load_supported_ip_families = modem_load_supported_ip_families;
iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish;
+ iface->load_carrier_config = mm_shared_qmi_load_carrier_config;
+ iface->load_carrier_config_finish = mm_shared_qmi_load_carrier_config_finish;
+ iface->setup_carrier_config = mm_shared_qmi_setup_carrier_config;
+ iface->setup_carrier_config_finish = mm_shared_qmi_setup_carrier_config_finish;
/* Enabling/disabling */
iface->modem_power_up = modem_power_up;
iface->modem_power_up_finish = modem_power_up_down_off_finish;
+ iface->fcc_unlock = mm_shared_qmi_fcc_unlock;
+ iface->fcc_unlock_finish = mm_shared_qmi_fcc_unlock_finish;
iface->modem_after_power_up = NULL;
iface->modem_after_power_up_finish = NULL;
iface->modem_power_down = modem_power_down;
@@ -10740,16 +12216,16 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_supported_charsets_finish = NULL;
iface->setup_charset = NULL;
iface->setup_charset_finish = NULL;
- iface->load_current_modes = load_current_modes;
- iface->load_current_modes_finish = load_current_modes_finish;
- iface->set_current_modes = set_current_modes;
- iface->set_current_modes_finish = set_current_modes_finish;
+ iface->load_current_modes = mm_shared_qmi_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_qmi_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_qmi_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_qmi_set_current_modes_finish;
iface->load_signal_quality = load_signal_quality;
iface->load_signal_quality_finish = load_signal_quality_finish;
- iface->load_current_bands = modem_load_current_bands;
- iface->load_current_bands_finish = modem_load_current_bands_finish;
- iface->set_current_bands = set_current_bands;
- iface->set_current_bands_finish = set_current_bands_finish;
+ iface->load_current_bands = mm_shared_qmi_load_current_bands;
+ iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish;
+ iface->set_current_bands = mm_shared_qmi_set_current_bands;
+ iface->set_current_bands_finish = mm_shared_qmi_set_current_bands_finish;
/* Don't try to load access technologies, as we would be using parent's
* generic method (QCDM based). Access technologies are already reported via
@@ -10760,14 +12236,23 @@ iface_modem_init (MMIfaceModem *iface)
/* Create QMI-specific SIM */
iface->create_sim = create_sim;
iface->create_sim_finish = create_sim_finish;
-
- /* Create QMI-specific bearer */
+ iface->load_sim_slots = mm_shared_qmi_load_sim_slots;
+ iface->load_sim_slots_finish = mm_shared_qmi_load_sim_slots_finish;
+ iface->set_primary_sim_slot = mm_shared_qmi_set_primary_sim_slot;
+ iface->set_primary_sim_slot_finish = mm_shared_qmi_set_primary_sim_slot_finish;
+ iface->setup_sim_hot_swap = mm_shared_qmi_setup_sim_hot_swap;
+ iface->setup_sim_hot_swap_finish = mm_shared_qmi_setup_sim_hot_swap_finish;
+
+ /* Create QMI-specific bearer and bearer list */
iface->create_bearer = modem_create_bearer;
iface->create_bearer_finish = modem_create_bearer_finish;
+ iface->create_bearer_list = modem_create_bearer_list;
/* Other actions */
- iface->factory_reset = modem_factory_reset;
- iface->factory_reset_finish = modem_factory_reset_finish;
+ iface->reset = mm_shared_qmi_reset;
+ iface->reset_finish = mm_shared_qmi_reset_finish;
+ iface->factory_reset = mm_shared_qmi_factory_reset;
+ iface->factory_reset_finish = mm_shared_qmi_factory_reset_finish;
}
static void
@@ -10800,22 +12285,108 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
/* Other actions */
iface->scan_networks = modem_3gpp_scan_networks;
iface->scan_networks_finish = modem_3gpp_scan_networks_finish;
- iface->register_in_network = modem_3gpp_register_in_network;
- iface->register_in_network_finish = modem_3gpp_register_in_network_finish;
+ iface->register_in_network = mm_shared_qmi_3gpp_register_in_network;
+ iface->register_in_network_finish = mm_shared_qmi_3gpp_register_in_network_finish;
iface->run_registration_checks = modem_3gpp_run_registration_checks;
iface->run_registration_checks_finish = modem_3gpp_run_registration_checks_finish;
iface->load_operator_code = modem_3gpp_load_operator_code;
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_initial_eps_bearer = modem_3gpp_load_initial_eps_bearer;
+ iface->load_initial_eps_bearer_finish = modem_3gpp_load_initial_eps_bearer_finish;
+ iface->load_initial_eps_bearer_settings = modem_3gpp_load_initial_eps_bearer_settings;
+ iface->load_initial_eps_bearer_settings_finish = modem_3gpp_load_initial_eps_bearer_settings_finish;
+ iface->set_initial_eps_bearer_settings = modem_3gpp_set_initial_eps_bearer_settings;
+ iface->set_initial_eps_bearer_settings_finish = modem_3gpp_set_initial_eps_bearer_settings_finish;
+ iface->disable_facility_lock = modem_3gpp_disable_facility_lock;
+ iface->disable_facility_lock_finish = modem_3gpp_disable_facility_lock_finish;
}
static void
-iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface)
+iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
{
- /* Assume we don't have USSD support */
+ /* No explicit check support for the profile management feature, just
+ * rely on the generic way to check for support */
iface->check_support = NULL;
iface->check_support_finish = NULL;
+
+ /* Additional actions */
+ iface->get_profile = modem_3gpp_profile_manager_get_profile;
+ iface->get_profile_finish = modem_3gpp_profile_manager_get_profile_finish;
+ iface->list_profiles = modem_3gpp_profile_manager_list_profiles;
+ iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish;
+ iface->check_format = modem_3gpp_profile_manager_check_format;
+ iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish;
+ iface->check_activated_profile = NULL;
+ iface->check_activated_profile_finish = NULL;
+ iface->deactivate_profile = NULL;
+ iface->deactivate_profile_finish = NULL;
+ iface->store_profile = modem_3gpp_profile_manager_store_profile;
+ iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish;
+ iface->delete_profile = modem_3gpp_profile_manager_delete_profile;
+ iface->delete_profile_finish = modem_3gpp_profile_manager_delete_profile_finish;
+}
+
+static void
+iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface)
+{
+ iface->check_support = modem_3gpp_ussd_check_support;
+ iface->check_support_finish = modem_3gpp_ussd_check_support_finish;
+ iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = common_3gpp_ussd_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = common_3gpp_ussd_setup_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = common_3gpp_ussd_enable_disable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = common_3gpp_ussd_enable_disable_unsolicited_events_finish;
+ iface->send = modem_3gpp_ussd_send;
+ iface->send_finish = modem_3gpp_ussd_send_finish;
+ iface->cancel = modem_3gpp_ussd_cancel;
+ iface->cancel_finish = modem_3gpp_ussd_cancel_finish;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+
+ iface->check_support = modem_voice_check_support;
+ iface->check_support_finish = modem_voice_check_support_finish;
+ iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = common_voice_setup_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = common_voice_enable_disable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = common_voice_enable_disable_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = common_voice_setup_cleanup_unsolicited_events_finish;
+
+ iface->setup_in_call_unsolicited_events = modem_voice_setup_in_call_unsolicited_events;
+ iface->setup_in_call_unsolicited_events_finish = common_voice_setup_cleanup_in_call_unsolicited_events_finish;
+ iface->cleanup_in_call_unsolicited_events = modem_voice_cleanup_in_call_unsolicited_events;
+ iface->cleanup_in_call_unsolicited_events_finish = common_voice_setup_cleanup_in_call_unsolicited_events_finish;
+
+ iface->create_call = modem_voice_create_call;
+ iface->load_call_list = modem_voice_load_call_list;
+ iface->load_call_list_finish = modem_voice_load_call_list_finish;
+ iface->hold_and_accept = modem_voice_hold_and_accept;
+ iface->hold_and_accept_finish = modem_voice_hold_and_accept_finish;
+ iface->hangup_and_accept = modem_voice_hangup_and_accept;
+ iface->hangup_and_accept_finish = modem_voice_hangup_and_accept_finish;
+ iface->hangup_all = modem_voice_hangup_all;
+ iface->hangup_all_finish = modem_voice_hangup_all_finish;
+ iface->join_multiparty = modem_voice_join_multiparty;
+ iface->join_multiparty_finish = modem_voice_join_multiparty_finish;
+ iface->leave_multiparty = modem_voice_leave_multiparty;
+ iface->leave_multiparty_finish = modem_voice_leave_multiparty_finish;
+ iface->transfer = modem_voice_transfer;
+ iface->transfer_finish = modem_voice_transfer_finish;
+ iface->call_waiting_setup = modem_voice_call_waiting_setup;
+ iface->call_waiting_setup_finish = modem_voice_call_waiting_setup_finish;
+ iface->call_waiting_query = modem_voice_call_waiting_query;
+ iface->call_waiting_query_finish = modem_voice_call_waiting_query_finish;
}
static void
@@ -10880,14 +12451,21 @@ iface_modem_location_init (MMIfaceModemLocation *iface)
iface->load_capabilities = location_load_capabilities;
iface->load_capabilities_finish = location_load_capabilities_finish;
- iface->load_supl_server = location_load_supl_server;
- iface->load_supl_server_finish = location_load_supl_server_finish;
- iface->set_supl_server = location_set_supl_server;
- iface->set_supl_server_finish = location_set_supl_server_finish;
iface->enable_location_gathering = enable_location_gathering;
iface->enable_location_gathering_finish = enable_location_gathering_finish;
iface->disable_location_gathering = disable_location_gathering;
iface->disable_location_gathering_finish = disable_location_gathering_finish;
+
+ iface->load_supl_server = mm_shared_qmi_location_load_supl_server;
+ iface->load_supl_server_finish = mm_shared_qmi_location_load_supl_server_finish;
+ iface->set_supl_server = mm_shared_qmi_location_set_supl_server;
+ iface->set_supl_server_finish = mm_shared_qmi_location_set_supl_server_finish;
+ iface->load_supported_assistance_data = mm_shared_qmi_location_load_supported_assistance_data;
+ iface->load_supported_assistance_data_finish = mm_shared_qmi_location_load_supported_assistance_data_finish;
+ iface->inject_assistance_data = mm_shared_qmi_location_inject_assistance_data;
+ iface->inject_assistance_data_finish = mm_shared_qmi_location_inject_assistance_data_finish;
+ iface->load_assistance_data_servers = mm_shared_qmi_location_load_assistance_data_servers;
+ iface->load_assistance_data_servers_finish = mm_shared_qmi_location_load_assistance_data_servers_finish;
}
static void
@@ -10927,8 +12505,6 @@ iface_modem_oma_init (MMIfaceModemOma *iface)
static void
iface_modem_firmware_init (MMIfaceModemFirmware *iface)
{
- iface->check_support = firmware_check_support;
- iface->check_support_finish = firmware_check_support_finish;
iface->load_list = firmware_load_list;
iface->load_list_finish = firmware_load_list_finish;
iface->load_current = firmware_load_current;
@@ -10937,6 +12513,19 @@ iface_modem_firmware_init (MMIfaceModemFirmware *iface)
iface->change_current_finish = firmware_change_current_finish;
}
+static MMIfaceModemLocation *
+peek_parent_location_interface (MMSharedQmi *self)
+{
+ return iface_modem_location_parent;
+}
+
+static void
+shared_qmi_init (MMSharedQmi *iface)
+{
+ iface->peek_client = shared_qmi_peek_client;
+ iface->peek_parent_location_interface = peek_parent_location_interface;
+}
+
static void
mm_broadband_modem_qmi_class_init (MMBroadbandModemQmiClass *klass)
{
@@ -10945,6 +12534,8 @@ mm_broadband_modem_qmi_class_init (MMBroadbandModemQmiClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandModemQmiPrivate));
+ klass->peek_port_qmi_for_data = peek_port_qmi_for_data;
+
object_class->finalize = finalize;
object_class->dispose = dispose;
diff --git a/src/mm-broadband-modem-qmi.h b/src/mm-broadband-modem-qmi.h
index 6c79e5eb..9b712109 100644
--- a/src/mm-broadband-modem-qmi.h
+++ b/src/mm-broadband-modem-qmi.h
@@ -36,14 +36,31 @@ struct _MMBroadbandModemQmi {
struct _MMBroadbandModemQmiClass{
MMBroadbandModemClass parent;
+
+ MMPortQmi * (* peek_port_qmi_for_data) (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error);
};
GType mm_broadband_modem_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandModemQmi, g_object_unref)
-MMBroadbandModemQmi *mm_broadband_modem_qmi_new (const gchar *device,
+MMBroadbandModemQmi *mm_broadband_modem_qmi_new (const gchar *device,
const gchar **drivers,
- const gchar *plugin,
- guint16 vendor_id,
- guint16 product_id);
+ const gchar *plugin,
+ guint16 vendor_id,
+ guint16 product_id);
+
+MMPortQmi *mm_broadband_modem_qmi_peek_port_qmi (MMBroadbandModemQmi *self);
+MMPortQmi *mm_broadband_modem_qmi_peek_port_qmi_for_data (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error);
+MMPortQmi *mm_broadband_modem_qmi_get_port_qmi (MMBroadbandModemQmi *self);
+MMPortQmi *mm_broadband_modem_qmi_get_port_qmi_for_data (MMBroadbandModemQmi *self,
+ MMPort *data,
+ QmiSioPort *out_sio_port,
+ GError **error);
#endif /* MM_BROADBAND_MODEM_QMI_H */
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index 17c128ce..6b4e7b2e 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -12,7 +12,9 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Google, Inc.
+ * Copyright (C) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ * Copyright (C) 2019 Purism SPC
+ * Copyright (C) 2011 - 2021 Google, Inc.
*/
#include <config.h>
@@ -30,73 +32,92 @@
#include "mm-broadband-modem.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
#include "mm-iface-modem-3gpp-ussd.h"
#include "mm-iface-modem-cdma.h"
#include "mm-iface-modem-simple.h"
#include "mm-iface-modem-location.h"
#include "mm-iface-modem-messaging.h"
+#include "mm-iface-modem-voice.h"
#include "mm-iface-modem-time.h"
#include "mm-iface-modem-firmware.h"
+#include "mm-iface-modem-sar.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-call-list.h"
#include "mm-base-sim.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-modem-helpers.h"
#include "mm-error-helpers.h"
#include "mm-port-serial-qcdm.h"
#include "libqcdm/src/errors.h"
#include "libqcdm/src/commands.h"
+#include "libqcdm/src/logs.h"
+#include "libqcdm/src/log-items.h"
+#include "mm-helper-enums-types.h"
static void iface_modem_init (MMIfaceModem *iface);
static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface);
+static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface);
static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface);
static void iface_modem_cdma_init (MMIfaceModemCdma *iface);
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_voice_init (MMIfaceModemVoice *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);
+static void iface_modem_sar_init (MMIfaceModemSar *iface);
G_DEFINE_TYPE_EXTENDED (MMBroadbandModem, mm_broadband_modem, MM_TYPE_BASE_MODEM, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_SIMPLE, iface_modem_simple_init)
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_VOICE, iface_modem_voice_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_SAR, iface_modem_sar_init)
G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_FIRMWARE, iface_modem_firmware_init))
enum {
PROP_0,
PROP_MODEM_DBUS_SKELETON,
PROP_MODEM_3GPP_DBUS_SKELETON,
+ PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON,
PROP_MODEM_3GPP_USSD_DBUS_SKELETON,
PROP_MODEM_CDMA_DBUS_SKELETON,
PROP_MODEM_SIMPLE_DBUS_SKELETON,
PROP_MODEM_LOCATION_DBUS_SKELETON,
PROP_MODEM_MESSAGING_DBUS_SKELETON,
+ PROP_MODEM_VOICE_DBUS_SKELETON,
PROP_MODEM_TIME_DBUS_SKELETON,
PROP_MODEM_SIGNAL_DBUS_SKELETON,
PROP_MODEM_OMA_DBUS_SKELETON,
PROP_MODEM_FIRMWARE_DBUS_SKELETON,
+ PROP_MODEM_SAR_DBUS_SKELETON,
PROP_MODEM_SIM,
+ PROP_MODEM_SIM_SLOTS,
PROP_MODEM_BEARER_LIST,
PROP_MODEM_STATE,
PROP_MODEM_3GPP_REGISTRATION_STATE,
PROP_MODEM_3GPP_CS_NETWORK_SUPPORTED,
PROP_MODEM_3GPP_PS_NETWORK_SUPPORTED,
PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED,
+ PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED,
PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS,
+ PROP_MODEM_3GPP_INITIAL_EPS_BEARER,
PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE,
PROP_MODEM_CDMA_EVDO_REGISTRATION_STATE,
PROP_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED,
@@ -104,10 +125,24 @@ enum {
PROP_MODEM_MESSAGING_SMS_LIST,
PROP_MODEM_MESSAGING_SMS_PDU_MODE,
PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE,
+ PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS,
+ PROP_MODEM_VOICE_CALL_LIST,
PROP_MODEM_SIMPLE_STATUS,
+ PROP_MODEM_SIM_HOT_SWAP_SUPPORTED,
+ PROP_MODEM_SIM_HOT_SWAP_CONFIGURED,
+ PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
+ PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED,
+ PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED,
+ PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED,
+ PROP_MODEM_CARRIER_CONFIG_MAPPING,
+ PROP_MODEM_FIRMWARE_IGNORE_CARRIER,
+ PROP_FLOW_CONTROL,
+ PROP_INDICATORS_DISABLED,
PROP_LAST
};
+static GParamSpec *properties[PROP_LAST];
+
/* When CIND is supported, invalid indicators are marked with this value */
#define CIND_INDICATOR_INVALID 255
#define CIND_INDICATOR_IS_VALID(u) (u != CIND_INDICATOR_INVALID)
@@ -117,16 +152,25 @@ typedef struct _PortsContext PortsContext;
struct _MMBroadbandModemPrivate {
/* Broadband modem specific implementation */
PortsContext *enabled_ports_ctx;
+ PortsContext *sim_hot_swap_ports_ctx;
+ PortsContext *in_call_ports_ctx;
gboolean modem_init_run;
+ gboolean sim_hot_swap_supported;
+ gboolean sim_hot_swap_configured;
+ gboolean periodic_signal_check_disabled;
+ gboolean periodic_access_tech_check_disabled;
/*<--- Modem interface --->*/
/* Properties */
GObject *modem_dbus_skeleton;
MMBaseSim *modem_sim;
+ GPtrArray *modem_sim_slots;
MMBearerList *modem_bearer_list;
MMModemState modem_state;
+ gchar *carrier_config_mapping;
/* Implementation helpers */
MMModemCharset modem_current_charset;
+ gboolean modem_cind_disabled;
gboolean modem_cind_support_checked;
gboolean modem_cind_supported;
guint modem_cind_indicator_signal_quality;
@@ -134,6 +178,12 @@ struct _MMBroadbandModemPrivate {
guint modem_cind_max_signal_quality;
guint modem_cind_indicator_roaming;
guint modem_cind_indicator_service;
+ MM3gppCmerMode modem_cmer_enable_mode;
+ MM3gppCmerMode modem_cmer_disable_mode;
+ MM3gppCmerInd modem_cmer_ind;
+ gboolean modem_cgerep_support_checked;
+ gboolean modem_cgerep_supported;
+ MMFlowControl flow_control;
/*<--- Modem 3GPP interface --->*/
/* Properties */
@@ -142,16 +192,22 @@ struct _MMBroadbandModemPrivate {
gboolean modem_3gpp_cs_network_supported;
gboolean modem_3gpp_ps_network_supported;
gboolean modem_3gpp_eps_network_supported;
+ gboolean modem_3gpp_5gs_network_supported;
/* Implementation helpers */
GPtrArray *modem_3gpp_registration_regex;
MMModem3gppFacility modem_3gpp_ignored_facility_locks;
+ MMBaseBearer *modem_3gpp_initial_eps_bearer;
+
+ /*<--- Modem 3GPP Profile Manager interface --->*/
+ /* Properties */
+ GObject *modem_3gpp_profile_manager_dbus_skeleton;
/*<--- Modem 3GPP USSD interface --->*/
/* Properties */
GObject *modem_3gpp_ussd_dbus_skeleton;
/* Implementation helpers */
gboolean use_unencoded_ussd;
- GSimpleAsyncResult *pending_ussd_action;
+ GTask *pending_ussd_action;
/*<--- Modem CDMA interface --->*/
/* Properties */
@@ -165,6 +221,7 @@ struct _MMBroadbandModemPrivate {
gboolean checked_sprint_support;
gboolean has_spservice;
gboolean has_speri;
+ gint evdo_pilot_rssi;
/*<--- Modem Simple interface --->*/
/* Properties */
@@ -174,6 +231,7 @@ struct _MMBroadbandModemPrivate {
/*<--- Modem Location interface --->*/
/* Properties */
GObject *modem_location_dbus_skeleton;
+ gboolean modem_location_allow_gps_unmanaged_always;
/*<--- Modem Messaging interface --->*/
/* Properties */
@@ -188,6 +246,13 @@ struct _MMBroadbandModemPrivate {
gboolean mem2_storage_locked;
MMSmsStorage current_sms_mem2_storage;
+ /*<--- Modem Voice interface --->*/
+ /* Properties */
+ GObject *modem_voice_dbus_skeleton;
+ MMCallList *modem_voice_call_list;
+ gboolean periodic_call_list_check_disabled;
+ gboolean indication_call_list_reload_enabled;
+ gboolean clcc_supported;
/*<--- Modem Time interface --->*/
/* Properties */
@@ -203,33 +268,128 @@ struct _MMBroadbandModemPrivate {
/*<--- Modem Firmware interface --->*/
/* Properties */
- GObject *modem_firmware_dbus_skeleton;
+ GObject *modem_firmware_dbus_skeleton;
+
+ /*<--- Modem Sar interface --->*/
+ /* Properties */
+ GObject *modem_sar_dbus_skeleton;
+
+ gboolean modem_firmware_ignore_carrier;
};
/*****************************************************************************/
+/* Generic ports open/close context */
-static gboolean
-response_processor_string_ignore_at_errors (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+struct _PortsContext {
+ volatile gint ref_count;
+ MMPortSerialAt *primary;
+ gboolean primary_open;
+ MMPortSerialAt *secondary;
+ gboolean secondary_open;
+ MMPortSerialQcdm *qcdm;
+ gboolean qcdm_open;
+};
+
+static PortsContext *
+ports_context_ref (PortsContext *ctx)
{
- if (error) {
- /* Ignore AT errors (ie, ERROR or CMx ERROR) */
- if (error->domain != MM_MOBILE_EQUIPMENT_ERROR || last_command)
- *result_error = g_error_copy (error);
+ g_atomic_int_inc (&ctx->ref_count);
+ return ctx;
+}
+
+static void
+ports_context_unref (PortsContext *ctx)
+{
+ if (g_atomic_int_dec_and_test (&ctx->ref_count)) {
+ if (ctx->primary) {
+ if (ctx->primary_open)
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->primary));
+ g_object_unref (ctx->primary);
+ }
+ if (ctx->secondary) {
+ if (ctx->secondary_open)
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->secondary));
+ g_object_unref (ctx->secondary);
+ }
+ if (ctx->qcdm) {
+ if (ctx->qcdm_open)
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
+ g_object_unref (ctx->qcdm);
+ }
+ g_free (ctx);
+ }
+}
+static gboolean
+ports_context_open (MMBroadbandModem *self,
+ PortsContext *ctx,
+ gboolean disable_at_init_sequence,
+ gboolean with_at_secondary,
+ gboolean with_qcdm,
+ GError **error)
+{
+ /* Open primary */
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ if (!ctx->primary) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get primary port");
+ return FALSE;
+ }
+ /* If we'll need to run modem initialization, disable port init sequence */
+ if (disable_at_init_sequence)
+ g_object_set (ctx->primary,
+ MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE,
+ NULL);
+ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->primary), error)) {
+ g_prefix_error (error, "Couldn't open primary port: ");
return FALSE;
}
+ ctx->primary_open = TRUE;
+
+ /* Open secondary (optional) */
+ if (with_at_secondary) {
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ if (ctx->secondary) {
+ /* If we'll need to run modem initialization, disable port init sequence */
+ if (disable_at_init_sequence)
+ g_object_set (ctx->secondary,
+ MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE,
+ NULL);
+ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->secondary), error)) {
+ g_prefix_error (error, "Couldn't open secondary port: ");
+ return FALSE;
+ }
+ ctx->secondary_open = TRUE;
+ }
+ }
+
+ /* Open qcdm (optional) */
+ if (with_qcdm) {
+ ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
+ if (ctx->qcdm) {
+ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), error)) {
+ g_prefix_error (error, "Couldn't open QCDM port: ");
+ return FALSE;
+ }
+ ctx->qcdm_open = TRUE;
+ }
+ }
- *result = g_variant_new_string (response);
return TRUE;
}
+static PortsContext *
+ports_context_new (void)
+{
+ PortsContext *ctx;
+
+ ctx = g_new0 (PortsContext, 1);
+ ctx->ref_count = 1;
+ return ctx;
+}
+
/*****************************************************************************/
/* Create Bearer (Modem interface) */
@@ -238,57 +398,59 @@ modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMBaseBearer *bearer;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- mm_dbg ("New bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer));
-
- return g_object_ref (bearer);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
broadband_bearer_new_ready (GObject *source,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMBaseBearer *bearer = NULL;
GError *error = NULL;
bearer = mm_broadband_bearer_new_finish (res, &error);
if (!bearer)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple,
- bearer,
- (GDestroyNotify)g_object_unref);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
static void
modem_create_bearer (MMIfaceModem *self,
- MMBearerProperties *properties,
+ MMBearerProperties *props,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- /* Set a new ref to the bearer object as result */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_create_bearer);
+ task = g_task_new (self, NULL, callback, user_data);
/* We just create a MMBroadbandBearer */
- mm_dbg ("Creating Broadband bearer in broadband modem");
+ mm_obj_dbg (self, "creating broadband bearer in broadband modem...");
mm_broadband_bearer_new (MM_BROADBAND_MODEM (self),
- properties,
+ props,
NULL, /* cancellable */
(GAsyncReadyCallback)broadband_bearer_new_ready,
- result);
+ task);
+}
+
+/*****************************************************************************/
+/* Create Bearer List (Modem interface) */
+
+static MMBearerList *
+modem_create_bearer_list (MMIfaceModem *self)
+{
+ guint n;
+
+ /* The maximum number of available/connected modems is guessed from
+ * the size of the data ports list. */
+ n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (self)));
+ mm_obj_dbg (self, "allowed up to %u active bearers", n);
+
+ /* by default, no multiplexing support */
+ return mm_bearer_list_new (n, 0);
}
/*****************************************************************************/
@@ -318,22 +480,17 @@ modem_create_sim (MMIfaceModem *self,
/* Capabilities loading (Modem interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMModemCapability caps;
MMPortSerialQcdm *qcdm_port;
} LoadCapabilitiesContext;
static void
-load_capabilities_context_complete_and_free (LoadCapabilitiesContext *ctx)
+load_capabilities_context_free (LoadCapabilitiesContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
if (ctx->qcdm_port) {
mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm_port));
g_object_unref (ctx->qcdm_port);
}
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (LoadCapabilitiesContext, ctx);
}
@@ -342,45 +499,83 @@ modem_load_current_capabilities_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- MMModemCapability caps;
- gchar *caps_str;
+ GError *inner_error = NULL;
+ gssize value;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return MM_MODEM_CAPABILITY_NONE;
-
- caps = (MMModemCapability) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- caps_str = mm_modem_capability_build_string_from_mask (caps);
- mm_dbg ("loaded current capabilities: %s", caps_str);
- g_free (caps_str);
- return caps;
+ }
+ return (MMModemCapability)value;
}
static void
current_capabilities_ws46_test_ready (MMBaseModem *self,
GAsyncResult *res,
- LoadCapabilitiesContext *ctx)
+ GTask *task)
{
+ LoadCapabilitiesContext *ctx;
const gchar *response;
+ GArray *modes;
+ guint i;
+ gboolean is_2g = FALSE;
+ gboolean is_3g = FALSE;
+ gboolean is_4g = FALSE;
+ gboolean is_5g = FALSE;
+
+ ctx = g_task_get_task_data (task);
/* Completely ignore errors in AT+WS46=? */
response = mm_base_modem_at_command_finish (self, res, NULL);
- if (response &&
- (strstr (response, "28") != NULL || /* 4G only */
- strstr (response, "30") != NULL || /* 2G/4G */
- strstr (response, "31") != NULL)) { /* 3G/4G */
- /* Add LTE caps */
+ if (!response)
+ goto out;
+
+ modes = mm_3gpp_parse_ws46_test_response (response, self, NULL);
+ if (!modes)
+ goto out;
+
+ /* Process list of modes to gather supported ones */
+ for (i = 0; i < modes->len; i++) {
+ if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_2G)
+ is_2g = TRUE;
+ if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_3G)
+ is_3g = TRUE;
+ if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_4G)
+ is_4g = TRUE;
+ if (g_array_index (modes, MMModemMode, i) & MM_MODEM_MODE_5G)
+ is_5g = TRUE;
+ }
+
+ /* Add capabilities from modes */
+ if (is_2g || is_3g)
+ ctx->caps |= MM_MODEM_CAPABILITY_GSM_UMTS;
+ if (is_4g)
ctx->caps |= MM_MODEM_CAPABILITY_LTE;
- }
+ if (is_5g)
+ ctx->caps |= MM_MODEM_CAPABILITY_5GNR;
+
+ /* The +CGSM capability is saying that the modem is a 3GPP modem, but that
+ * doesn't necessarily mean it's a GSM/UMTS modem, it could be a LTE-only
+ * or 5GNR-only device. We did add the GSM_UMTS capability when +CGSM was
+ * found, so now we'll check if the device only reports 4G or 5G modes, and
+ * remove the capability if so.
+ *
+ * Note that we don't change the default +CGSM -> GSM/UMTS logic, we just
+ * fix it up.
+ */
+ if ((is_4g || is_5g) && !is_2g && !is_3g)
+ ctx->caps &= ~MM_MODEM_CAPABILITY_GSM_UMTS;
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (ctx->caps),
- NULL);
- load_capabilities_context_complete_and_free (ctx);
+ g_array_unref (modes);
+
+out:
+ g_task_return_int (task, ctx->caps);
+ g_object_unref (task);
}
typedef struct {
- gchar *name;
+ const gchar *name;
MMModemCapability bits;
} ModemCaps;
@@ -404,27 +599,30 @@ static const ModemCaps modem_caps[] = {
{ NULL }
};
-static gboolean
-parse_caps_gcap (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **variant,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+parse_caps_gcap (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
const ModemCaps *cap = modem_caps;
guint32 ret = 0;
+ *result = NULL;
+ *result_error = NULL;
+
if (!response)
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
/* Some modems (Huawei E160g) won't respond to +GCAP with no SIM, but
* will respond to ATI. Ignore the error and continue.
*/
- if (strstr (response, "+CME ERROR:"))
- return FALSE;
+ if (strstr (response, "+CME ERROR:") || (error && error->domain == MM_MOBILE_EQUIPMENT_ERROR))
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
while (cap->name) {
if (strstr (response, cap->name))
@@ -434,24 +632,37 @@ parse_caps_gcap (MMBaseModem *self,
/* No result built? */
if (ret == 0)
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
- *variant = g_variant_new_uint32 (ret);
- return TRUE;
+ *result = g_variant_new_uint32 (ret);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
-static gboolean
-parse_caps_cpin (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+parse_caps_cpin (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
- if (!response)
- return FALSE;
+ *result = NULL;
+ *result_error = NULL;
+
+ if (!response) {
+ if (error &&
+ (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_BUSY) ||
+ g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG))) {
+ /* At least, it's a GSM modem */
+ *result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
+ }
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
if (strcasestr (response, "SIM PIN") ||
strcasestr (response, "SIM PUK") ||
@@ -471,23 +682,27 @@ parse_caps_cpin (MMBaseModem *self,
strcasestr (response, "READY")) {
/* At least, it's a GSM modem */
*result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
- return FALSE;
+
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
-static gboolean
-parse_caps_cgmm (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+parse_caps_cgmm (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
+ *result = NULL;
+ *result_error = NULL;
+
if (!response)
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
/* This check detects some really old Motorola GPRS dongles and phones */
if (strstr (response, "GSM900") ||
@@ -496,9 +711,10 @@ parse_caps_cgmm (MMBaseModem *self,
strstr (response, "GSM850")) {
/* At least, it's a GSM modem */
*result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
- return FALSE;
+
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
static const MMBaseModemAtCommand capabilities[] = {
@@ -512,23 +728,25 @@ static const MMBaseModemAtCommand capabilities[] = {
static void
capabilities_sequence_ready (MMBaseModem *self,
GAsyncResult *res,
- LoadCapabilitiesContext *ctx)
+ GTask *task)
{
+ LoadCapabilitiesContext *ctx;
GError *error = NULL;
GVariant *result;
+ ctx = g_task_get_task_data (task);
+
result = mm_base_modem_at_sequence_finish (self, res, NULL, &error);
if (!result) {
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,
- "%s",
- "Failed to determine modem capabilities.");
- }
- load_capabilities_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "%s",
+ "Failed to determine modem capabilities.");
+ g_object_unref (task);
return;
}
@@ -549,51 +767,57 @@ capabilities_sequence_ready (MMBaseModem *self,
if (ctx->caps & MM_MODEM_CAPABILITY_GSM_UMTS &&
!(ctx->caps & MM_MODEM_CAPABILITY_LTE)) {
mm_base_modem_at_command (
- MM_BASE_MODEM (ctx->self),
+ self,
"+WS46=?",
3,
TRUE, /* allow caching, it's a test command */
(GAsyncReadyCallback)current_capabilities_ws46_test_ready,
- ctx);
+ task);
return;
}
/* Otherwise, just set the already retrieved capabilities */
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (ctx->caps),
- NULL);
- load_capabilities_context_complete_and_free (ctx);
+ g_task_return_int (task, ctx->caps);
+ g_object_unref (task);
}
static void
-load_current_capabilities_at (LoadCapabilitiesContext *ctx)
+load_current_capabilities_at (GTask *task)
{
+ MMBroadbandModem *self;
+
+ self = g_task_get_source_object (task);
+
/* Launch sequence, we will expect a "u" GVariant */
mm_base_modem_at_sequence (
- MM_BASE_MODEM (ctx->self),
+ MM_BASE_MODEM (self),
capabilities,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
(GAsyncReadyCallback)capabilities_sequence_ready,
- ctx);
+ task);
}
static void
mode_pref_qcdm_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- LoadCapabilitiesContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self;
+ LoadCapabilitiesContext *ctx;
QcdmResult *result;
gint err = QCDM_SUCCESS;
- u_int8_t pref = 0;
+ uint8_t pref = 0;
GError *error = NULL;
GByteArray *response;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
/* Fall back to AT checking */
- mm_dbg ("Failed to load NV ModePref: %s", error->message);
+ mm_obj_dbg (self, "failed to load NV ModePref: %s", error->message);
g_error_free (error);
goto at_caps;
}
@@ -604,7 +828,7 @@ mode_pref_qcdm_ready (MMPortSerialQcdm *port,
&err);
g_byte_array_unref (response);
if (!result) {
- mm_dbg ("Failed to parse NV ModePref result: %d", err);
+ mm_obj_dbg (self, "failed to parse NV ModePref result: %d", err);
g_byte_array_unref (response);
goto at_caps;
}
@@ -612,7 +836,7 @@ mode_pref_qcdm_ready (MMPortSerialQcdm *port,
err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_MODE_PREF_ITEM_MODE_PREF, &pref);
qcdm_result_unref (result);
if (err) {
- mm_dbg ("Failed to read NV ModePref: %d", err);
+ mm_obj_dbg (self, "failed to read NV ModePref: %d", err);
goto at_caps;
}
@@ -644,33 +868,34 @@ mode_pref_qcdm_ready (MMPortSerialQcdm *port,
}
if (ctx->caps != MM_MODEM_CAPABILITY_NONE) {
- g_simple_async_result_set_op_res_gpointer (
- ctx->result,
- GUINT_TO_POINTER (ctx->caps),
- NULL);
- load_capabilities_context_complete_and_free (ctx);
+ g_task_return_int (task, ctx->caps);
+ g_object_unref (task);
return;
}
at_caps:
- load_current_capabilities_at (ctx);
+ load_current_capabilities_at (task);
}
static void
-load_current_capabilities_qcdm (LoadCapabilitiesContext *ctx)
+load_current_capabilities_qcdm (GTask *task)
{
+ MMBroadbandModem *self;
+ LoadCapabilitiesContext *ctx;
GByteArray *cmd;
GError *error = NULL;
- ctx->qcdm_port = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (ctx->self));
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->qcdm_port = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
g_assert (ctx->qcdm_port);
if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm_port), &error)) {
- mm_dbg ("Failed to open QCDM port for NV ModePref request: %s",
- error->message);
+ mm_obj_dbg (self, "failed to open QCDM port for NV ModePref request: %s", error->message);
g_error_free (error);
ctx->qcdm_port = NULL;
- load_current_capabilities_at (ctx);
+ load_current_capabilities_at (task);
return;
}
@@ -685,7 +910,7 @@ load_current_capabilities_qcdm (LoadCapabilitiesContext *ctx)
3,
NULL,
(GAsyncReadyCallback)mode_pref_qcdm_ready,
- ctx);
+ task);
g_byte_array_unref (cmd);
}
@@ -695,20 +920,18 @@ modem_load_current_capabilities (MMIfaceModem *self,
gpointer user_data)
{
LoadCapabilitiesContext *ctx;
+ GTask *task;
- mm_dbg ("loading current capabilities...");
+ mm_obj_dbg (self, "loading current capabilities...");
+ task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (LoadCapabilitiesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_current_capabilities);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_capabilities_context_free);
if (mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self)))
- load_current_capabilities_qcdm (ctx);
+ load_current_capabilities_qcdm (task);
else
- load_current_capabilities_at (ctx);
+ load_current_capabilities_at (task);
}
/*****************************************************************************/
@@ -740,14 +963,14 @@ modem_load_manufacturer_finish (MMIfaceModem *self,
result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
if (result) {
manufacturer = sanitize_info_reply (result, "GMI:");
- mm_dbg ("loaded manufacturer: %s", manufacturer);
+ mm_obj_dbg (self, "loaded manufacturer: %s", manufacturer);
}
return manufacturer;
}
static const MMBaseModemAtCommand manufacturers[] = {
- { "+CGMI", 3, TRUE, response_processor_string_ignore_at_errors },
- { "+GMI", 3, TRUE, response_processor_string_ignore_at_errors },
+ { "+CGMI", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { "+GMI", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
{ NULL }
};
@@ -756,7 +979,7 @@ modem_load_manufacturer (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading manufacturer...");
+ mm_obj_dbg (self, "loading manufacturer...");
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
manufacturers,
@@ -780,14 +1003,14 @@ modem_load_model_finish (MMIfaceModem *self,
result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
if (result) {
model = sanitize_info_reply (result, "GMM:");
- mm_dbg ("loaded model: %s", model);
+ mm_obj_dbg (self, "loaded model: %s", model);
}
return model;
}
static const MMBaseModemAtCommand models[] = {
- { "+CGMM", 3, TRUE, response_processor_string_ignore_at_errors },
- { "+GMM", 3, TRUE, response_processor_string_ignore_at_errors },
+ { "+CGMM", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { "+GMM", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
{ NULL }
};
@@ -796,7 +1019,7 @@ modem_load_model (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading model...");
+ mm_obj_dbg (self, "loading model...");
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
models,
@@ -820,14 +1043,14 @@ modem_load_revision_finish (MMIfaceModem *self,
result = mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
if (result) {
revision = sanitize_info_reply (result, "GMR:");
- mm_dbg ("loaded revision: %s", revision);
+ mm_obj_dbg (self, "loaded revision: %s", revision);
}
return revision;
}
static const MMBaseModemAtCommand revisions[] = {
- { "+CGMR", 3, TRUE, response_processor_string_ignore_at_errors },
- { "+GMR", 3, TRUE, response_processor_string_ignore_at_errors },
+ { "+CGMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { "+GMR", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
{ NULL }
};
@@ -836,7 +1059,7 @@ modem_load_revision (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading revision...");
+ mm_obj_dbg (self, "loading revision...");
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
revisions,
@@ -863,7 +1086,7 @@ modem_load_equipment_identifier_finish (MMIfaceModem *self,
/* Modems put all sorts of things into the GSN response; sanitize it */
if (mm_parse_gsn (equip_id, &imei, &meid, &esn)) {
- g_free (equip_id);
+ g_clear_pointer (&equip_id, g_free);
if (imei)
equip_id = g_strdup (imei);
@@ -871,22 +1094,23 @@ modem_load_equipment_identifier_finish (MMIfaceModem *self,
equip_id = g_strdup (meid);
else if (esn)
equip_id = g_strdup (esn);
+ else
+ g_assert_not_reached ();
+
g_free (esn);
g_free (meid);
g_free (imei);
-
- g_assert (equip_id);
} else {
/* Leave whatever the modem returned alone */
}
- mm_dbg ("loaded equipment identifier: %s", equip_id);
+ mm_obj_dbg (self, "loaded equipment identifier: %s", equip_id);
}
return equip_id;
}
static const MMBaseModemAtCommand equipment_identifiers[] = {
- { "+CGSN", 3, TRUE, response_processor_string_ignore_at_errors },
- { "+GSN", 3, TRUE, response_processor_string_ignore_at_errors },
+ { "+CGSN", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { "+GSN", 3, TRUE, mm_base_modem_response_processor_string_ignore_at_errors },
{ NULL }
};
@@ -897,7 +1121,7 @@ modem_load_equipment_identifier (MMIfaceModem *self,
{
const MMBaseModemAtCommand *commands = equipment_identifiers;
- mm_dbg ("loading equipment identifier...");
+ mm_obj_dbg (self, "loading equipment identifier...");
/* On CDMA-only (non-3GPP) modems, just try +GSN */
if (mm_iface_modem_is_cdma_only (self))
@@ -933,9 +1157,9 @@ modem_load_device_identifier_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- GError *inner_error = NULL;
- gpointer ctx = NULL;
- gchar *device_identifier;
+ GError *inner_error = NULL;
+ gpointer ctx = NULL;
+ gchar *device_identifier;
mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, &ctx, &inner_error);
if (inner_error) {
@@ -944,11 +1168,17 @@ modem_load_device_identifier_finish (MMIfaceModem *self,
}
g_assert (ctx != NULL);
- device_identifier = (mm_broadband_modem_create_device_identifier (
- MM_BROADBAND_MODEM (self),
- ((DeviceIdentifierContext *)ctx)->ati,
- ((DeviceIdentifierContext *)ctx)->ati1));
- mm_dbg ("loaded device identifier: %s", device_identifier);
+ device_identifier = mm_broadband_modem_create_device_identifier (
+ MM_BROADBAND_MODEM (self),
+ ((DeviceIdentifierContext *)ctx)->ati,
+ ((DeviceIdentifierContext *)ctx)->ati1,
+ &inner_error);
+ if (!device_identifier) {
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ mm_obj_dbg (self, "loaded device identifier: %s", device_identifier);
return device_identifier;
}
@@ -985,7 +1215,7 @@ modem_load_device_identifier (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading device identifier...");
+ mm_obj_dbg (self, "loading device identifier...");
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
device_identifier_steps,
@@ -999,17 +1229,12 @@ modem_load_device_identifier (MMIfaceModem *self,
/* Load own numbers (Modem interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMPortSerialQcdm *qcdm;
} OwnNumbersContext;
static void
-own_numbers_context_complete_and_free (OwnNumbersContext *ctx)
+own_numbers_context_free (OwnNumbersContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
if (ctx->qcdm) {
mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
g_object_unref (ctx->qcdm);
@@ -1022,16 +1247,13 @@ modem_load_own_numbers_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
mdn_qcdm_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- OwnNumbersContext *ctx)
+ GTask *task)
{
QcdmResult *result;
gint err = QCDM_SUCCESS;
@@ -1041,8 +1263,8 @@ mdn_qcdm_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- own_numbers_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1050,13 +1272,14 @@ mdn_qcdm_ready (MMPortSerialQcdm *port,
result = qcdm_cmd_nv_get_mdn_result ((const gchar *) response->data,
response->len,
&err);
+ g_byte_array_unref (response);
if (!result) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse NV MDN command result: %d",
- err);
- own_numbers_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse NV MDN command result: %d",
+ err);
+ g_object_unref (task);
return;
}
@@ -1075,37 +1298,40 @@ mdn_qcdm_ready (MMPortSerialQcdm *port,
valid = g_ascii_isdigit (*p++) || (*p == '+');
if (valid) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_strdupv ((gchar **) numbers),
- NULL);
+ g_task_return_pointer (task,
+ g_strdupv ((gchar **) numbers),
+ (GDestroyNotify)g_strfreev);
} else {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s",
- "MDN from NV memory appears invalid");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "%s",
+ "MDN from NV memory appears invalid");
}
} else {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s",
- "Failed retrieve MDN");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "%s",
+ "Failed retrieve MDN");
}
+ g_object_unref (task);
qcdm_result_unref (result);
- own_numbers_context_complete_and_free (ctx);
}
static void
modem_load_own_numbers_done (MMIfaceModem *self,
GAsyncResult *res,
- OwnNumbersContext *ctx)
+ GTask *task)
{
+ OwnNumbersContext *ctx;
const gchar *result;
GError *error = NULL;
GStrv numbers;
+ ctx = g_task_get_task_data (task);
+
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!result) {
/* try QCDM */
@@ -1123,20 +1349,16 @@ modem_load_own_numbers_done (MMIfaceModem *self,
3,
NULL,
(GAsyncReadyCallback)mdn_qcdm_ready,
- ctx);
+ task);
g_byte_array_unref (mdn);
return;
}
+ g_task_return_error (task, error);
} else {
- numbers = mm_3gpp_parse_cnum_exec_response (result, &error);
- if (numbers)
- g_simple_async_result_set_op_res_gpointer (ctx->result, numbers, NULL);
+ numbers = mm_3gpp_parse_cnum_exec_response (result);
+ g_task_return_pointer (task, numbers, (GDestroyNotify)g_strfreev);
}
-
- if (error)
- g_simple_async_result_take_error (ctx->result, error);
-
- own_numbers_context_complete_and_free (ctx);
+ g_object_unref (task);
}
static void
@@ -1146,32 +1368,31 @@ modem_load_own_numbers (MMIfaceModem *self,
{
OwnNumbersContext *ctx;
GError *error = NULL;
+ GTask *task;
ctx = g_new0 (OwnNumbersContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_own_numbers);
ctx->qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
if (ctx->qcdm) {
if (mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), &error)) {
ctx->qcdm = g_object_ref (ctx->qcdm);
} else {
- mm_dbg ("Couldn't open QCDM port: (%d) %s",
- error ? error->code : -1,
- error ? error->message : "(unknown)");
+ mm_obj_dbg (self, "couldn't open QCDM port: (%d) %s",
+ error ? error->code : -1,
+ error ? error->message : "(unknown)");
ctx->qcdm = NULL;
}
}
- mm_dbg ("loading own numbers...");
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)own_numbers_context_free);
+
+ mm_obj_dbg (self, "loading own numbers...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CNUM",
3,
FALSE,
(GAsyncReadyCallback)modem_load_own_numbers_done,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -1208,17 +1429,21 @@ modem_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;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCK_UNKNOWN;
+ }
+ return (MMModemLock)value;
}
static void
cpin_query_ready (MMIfaceModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
@@ -1227,9 +1452,8 @@ cpin_query_ready (MMIfaceModem *self,
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1257,118 +1481,224 @@ cpin_query_ready (MMIfaceModem *self,
}
}
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (lock),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, lock);
+ g_object_unref (task);
}
static void
modem_load_unlock_required (MMIfaceModem *self,
+ gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_unlock_required);
+ task = g_task_new (self, NULL, callback, user_data);
/* CDMA-only modems don't need this */
if (mm_iface_modem_is_cdma_only (self)) {
- mm_dbg ("Skipping unlock check in CDMA-only modem...");
- g_simple_async_result_set_op_res_gpointer (result,
- GUINT_TO_POINTER (MM_MODEM_LOCK_NONE),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ mm_obj_dbg (self, "skipping unlock check in CDMA-only modem...");
+ g_task_return_int (task, MM_MODEM_LOCK_NONE);
+ g_object_unref (task);
return;
}
- mm_dbg ("checking if unlock required...");
+ mm_obj_dbg (self, "checking if unlock required...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CPIN?",
- 3,
+ 10,
FALSE,
(GAsyncReadyCallback)cpin_query_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Supported modes loading (Modem interface) */
typedef struct {
- GSimpleAsyncResult *result;
+ MMUnlockRetries *retries;
+ guint i;
+} LoadUnlockRetriesContext;
+
+typedef struct {
+ MMModemLock lock;
+ const gchar *command;
+} UnlockRetriesMap;
+
+static const UnlockRetriesMap unlock_retries_map [] = {
+ { MM_MODEM_LOCK_SIM_PIN, "+CSIM=10,\"0020000100\"" },
+ { MM_MODEM_LOCK_SIM_PUK, "+CSIM=10,\"002C000100\"" },
+ { MM_MODEM_LOCK_SIM_PIN2, "+CSIM=10,\"0020008100\"" },
+ { MM_MODEM_LOCK_SIM_PUK2, "+CSIM=10,\"002C008100\"" },
+};
+
+static void
+load_unlock_retries_context_free (LoadUnlockRetriesContext *ctx)
+{
+ g_object_unref (ctx->retries);
+ g_slice_free (LoadUnlockRetriesContext, ctx);
+}
+
+static MMUnlockRetries *
+load_unlock_retries_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void load_unlock_retries_context_step (GTask *task);
+
+static void
+csim_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadUnlockRetriesContext *ctx;
+ const gchar *response;
+ GError *error = NULL;
+ gint val;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ mm_obj_dbg (self, "couldn't load retry count for lock '%s': %s",
+ mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock),
+ error->message);
+ goto next;
+ }
+
+ val = mm_parse_csim_response (response, &error);
+ if (val < 0) {
+ mm_obj_dbg (self, "couldn't parse retry count value for lock '%s': %s",
+ mm_modem_lock_get_string (unlock_retries_map[ctx->i].lock),
+ error->message);
+ goto next;
+ }
+
+ mm_unlock_retries_set (ctx->retries, unlock_retries_map[ctx->i].lock, val);
+
+next:
+ g_clear_error (&error);
+
+ /* Go to next lock value */
+ ctx->i++;
+ load_unlock_retries_context_step (task);
+}
+
+static void
+load_unlock_retries_context_step (GTask *task)
+{
MMBroadbandModem *self;
+ LoadUnlockRetriesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->i == G_N_ELEMENTS (unlock_retries_map)) {
+ g_task_return_pointer (task, g_object_ref (ctx->retries), g_object_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ unlock_retries_map[ctx->i].command,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)csim_ready,
+ task);
+}
+
+static void
+load_unlock_retries (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ LoadUnlockRetriesContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (LoadUnlockRetriesContext);
+ ctx->retries = mm_unlock_retries_new ();
+ ctx->i = 0;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_unlock_retries_context_free);
+
+ load_unlock_retries_context_step (task);
+}
+
+/*****************************************************************************/
+/* Supported modes loading (Modem interface) */
+
+typedef struct {
MMModemMode mode;
gboolean run_cnti;
gboolean run_ws46;
gboolean run_gcap;
} LoadSupportedModesContext;
-static void
-load_supported_modes_context_complete_and_free (LoadSupportedModesContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LoadSupportedModesContext, ctx);
-}
-
static GArray *
modem_load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
+ GError *inner_error = NULL;
+ gssize value;
GArray *modes;
MMModemModeCombination mode;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return NULL;
+ }
/* Build a mask with all supported modes */
modes = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
- mode.allowed = (MMModemMode) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ mode.allowed = (MMModemMode) value;
mode.preferred = MM_MODEM_MODE_NONE;
g_array_append_val (modes, mode);
return modes;
}
-static void load_supported_modes_step (LoadSupportedModesContext *ctx);
+static void load_supported_modes_step (GTask *task);
static void
-supported_modes_gcap_ready (MMBaseModem *self,
+supported_modes_gcap_ready (MMBaseModem *_self,
GAsyncResult *res,
- LoadSupportedModesContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ LoadSupportedModesContext *ctx;
const gchar *response;
GError *error = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_base_modem_at_command_finish (_self, res, &error);
if (!error) {
MMModemMode mode = MM_MODEM_MODE_NONE;
if (strstr (response, "IS")) {
/* IS-856 is the EV-DO family */
if (strstr (response, "856")) {
- if (!ctx->self->priv->modem_cdma_evdo_network_supported) {
- ctx->self->priv->modem_cdma_evdo_network_supported = TRUE;
- g_object_notify (G_OBJECT (ctx->self), MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED);
+ if (!self->priv->modem_cdma_evdo_network_supported) {
+ self->priv->modem_cdma_evdo_network_supported = TRUE;
+ g_object_notify (G_OBJECT (self), MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED);
}
- mm_dbg ("Device allows (CDMA) 3G network mode");
+ mm_obj_dbg (self, "device allows (CDMA) 3G network mode");
mode |= MM_MODEM_MODE_3G;
}
/* IS-707 is the 1xRTT family, which we consider as 2G */
if (strstr (response, "707")) {
- if (!ctx->self->priv->modem_cdma_cdma1x_network_supported) {
- ctx->self->priv->modem_cdma_cdma1x_network_supported = TRUE;
- g_object_notify (G_OBJECT (ctx->self), MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED);
+ if (!self->priv->modem_cdma_cdma1x_network_supported) {
+ self->priv->modem_cdma_cdma1x_network_supported = TRUE;
+ g_object_notify (G_OBJECT (self), MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED);
}
- mm_dbg ("Device allows (CDMA) 2G network mode");
+ mm_obj_dbg (self, "device allows (CDMA) 2G network mode");
mode |= MM_MODEM_MODE_2G;
}
}
@@ -1387,116 +1717,84 @@ supported_modes_gcap_ready (MMBaseModem *self,
}
if (error) {
- mm_dbg ("Generic query of supported CDMA networks failed: '%s'", error->message);
+ mm_obj_dbg (self, "generic query of supported CDMA networks failed: '%s'", error->message);
g_error_free (error);
/* Use defaults */
- if (ctx->self->priv->modem_cdma_cdma1x_network_supported) {
- mm_dbg ("Assuming device allows (CDMA) 2G network mode");
+ if (self->priv->modem_cdma_cdma1x_network_supported) {
+ mm_obj_dbg (self, "assuming device allows (CDMA) 2G network mode");
ctx->mode |= MM_MODEM_MODE_2G;
}
- if (ctx->self->priv->modem_cdma_evdo_network_supported) {
- mm_dbg ("Assuming device allows (CDMA) 3G network mode");
+ if (self->priv->modem_cdma_evdo_network_supported) {
+ mm_obj_dbg (self, "assuming device allows (CDMA) 3G network mode");
ctx->mode |= MM_MODEM_MODE_3G;
}
}
/* Now keep on with the loading, we're probably finishing now */
ctx->run_gcap = FALSE;
- load_supported_modes_step (ctx);
+ load_supported_modes_step (task);
}
static void
supported_modes_ws46_test_ready (MMBroadbandModem *self,
GAsyncResult *res,
- LoadSupportedModesContext *ctx)
+ GTask *task)
{
+ LoadSupportedModesContext *ctx;
const gchar *response;
- GError *error = NULL;
-
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!error) {
- MMModemMode mode = MM_MODEM_MODE_NONE;
-
- /*
- * More than one numeric ID may appear in the list, that's why
- * they are checked separately.
- *
- * NOTE: Do not skip WS46 prefix; it would break Cinterion handling.
- *
- * From 3GPP TS 27.007 v.11.2.0, section 5.9
- * 12 GSM Digital Cellular Systems (GERAN only)
- * 22 UTRAN only
- * 25 3GPP Systems (GERAN, UTRAN and E-UTRAN)
- * 28 E-UTRAN only
- * 29 GERAN and UTRAN
- * 30 GERAN and E-UTRAN
- * 31 UTRAN and E-UTRAN
- */
-
- if (strstr (response, "12") != NULL) {
- mm_dbg ("Device allows (3GPP) 2G-only network mode");
- mode |= MM_MODEM_MODE_2G;
- }
+ GArray *modes;
+ GError *error = NULL;
+ guint i;
- if (strstr (response, "22") != NULL) {
- mm_dbg ("Device allows (3GPP) 3G-only network mode");
- mode |= MM_MODEM_MODE_3G;
- }
+ ctx = g_task_get_task_data (task);
- if (strstr (response, "28") != NULL) {
- mm_dbg ("Device allows (3GPP) 4G-only network mode");
- mode |= MM_MODEM_MODE_4G;
- }
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ mm_obj_dbg (self, "generic query of supported 3GPP networks with WS46=? failed: '%s'", error->message);
+ g_error_free (error);
+ goto out;
+ }
- if (strstr (response, "29") != NULL) {
- mm_dbg ("Device allows (3GPP) 2G/3G network mode");
- mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- }
+ modes = mm_3gpp_parse_ws46_test_response (response, self, &error);
+ if (!modes) {
+ mm_obj_dbg (self, "parsing WS46=? response failed: '%s'", error->message);
+ g_error_free (error);
+ goto out;
+ }
- if (strstr (response, "30") != NULL) {
- mm_dbg ("Device allows (3GPP) 2G/4G network mode");
- mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
- }
+ for (i = 0; i < modes->len; i++) {
+ MMModemMode mode;
+ gchar *str;
- if (strstr (response, "31") != NULL) {
- mm_dbg ("Device allows (3GPP) 3G/4G network mode");
- mode |= (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- }
+ mode = g_array_index (modes, MMModemMode, i);
- if (strstr (response, "25") != NULL) {
- if (mm_iface_modem_is_3gpp_lte (MM_IFACE_MODEM (self))) {
- mm_dbg ("Device allows every supported 3GPP network mode (2G/3G/4G)");
- mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- } else {
- mm_dbg ("Device allows every supported 3GPP network mode (2G/3G)");
- mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- }
- }
+ ctx->mode |= mode;
- /* If no expected ID found, log error */
- if (mode == MM_MODEM_MODE_NONE)
- mm_dbg ("Invalid list of supported networks reported by WS46=?: '%s'", response);
- else
- ctx->mode |= mode;
- } else {
- mm_dbg ("Generic query of supported 3GPP networks with WS46=? failed: '%s'", error->message);
- g_error_free (error);
+ str = mm_modem_mode_build_string_from_mask (mode);
+ mm_obj_dbg (self, "device allows (3GPP) mode combination: %s", str);
+ g_free (str);
}
+ g_array_unref (modes);
+
+out:
/* Now keep on with the loading, we may need CDMA-specific checks */
ctx->run_ws46 = FALSE;
- load_supported_modes_step (ctx);
+ load_supported_modes_step (task);
}
static void
supported_modes_cnti_ready (MMBroadbandModem *self,
GAsyncResult *res,
- LoadSupportedModesContext *ctx)
+ GTask *task)
{
+ LoadSupportedModesContext *ctx;
const gchar *response;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!error) {
MMModemMode mode = MM_MODEM_MODE_NONE;
@@ -1507,7 +1805,7 @@ supported_modes_cnti_ready (MMBroadbandModem *self,
if (g_strstr_len (lower, -1, "gsm") ||
g_strstr_len (lower, -1, "gprs") ||
g_strstr_len (lower, -1, "edge")) {
- mm_dbg ("Device allows (3GPP) 2G networks");
+ mm_obj_dbg (self, "device allows (3GPP) 2G networks");
mode |= MM_MODEM_MODE_2G;
}
@@ -1515,12 +1813,12 @@ supported_modes_cnti_ready (MMBroadbandModem *self,
g_strstr_len (lower, -1, "hsdpa") ||
g_strstr_len (lower, -1, "hsupa") ||
g_strstr_len (lower, -1, "hspa+")) {
- mm_dbg ("Device allows (3GPP) 3G networks");
+ mm_obj_dbg (self, "device allows (3GPP) 3G networks");
mode |= MM_MODEM_MODE_3G;
}
if (g_strstr_len (lower, -1, "lte")) {
- mm_dbg ("Device allows (3GPP) 4G networks");
+ mm_obj_dbg (self, "device allows (3GPP) 4G networks");
mode |= MM_MODEM_MODE_4G;
}
@@ -1528,67 +1826,72 @@ supported_modes_cnti_ready (MMBroadbandModem *self,
/* If no expected ID found, log error */
if (mode == MM_MODEM_MODE_NONE)
- mm_dbg ("Invalid list of supported networks reported by *CNTI: '%s'", response);
+ mm_obj_dbg (self, "invalid list of supported networks reported by *CNTI: '%s'", response);
else
ctx->mode |= mode;
} else {
- mm_dbg ("Generic query of supported 3GPP networks with *CNTI failed: '%s'", error->message);
+ mm_obj_dbg (self, "generic query of supported 3GPP networks with *CNTI failed: '%s'", error->message);
g_error_free (error);
}
/* Now keep on with the loading */
ctx->run_cnti = FALSE;
- load_supported_modes_step (ctx);
+ load_supported_modes_step (task);
}
static void
-load_supported_modes_step (LoadSupportedModesContext *ctx)
+load_supported_modes_step (GTask *task)
{
+ MMBroadbandModem *self;
+ LoadSupportedModesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (ctx->run_cnti) {
mm_base_modem_at_command (
- MM_BASE_MODEM (ctx->self),
+ MM_BASE_MODEM (self),
"*CNTI=2",
3,
FALSE,
(GAsyncReadyCallback)supported_modes_cnti_ready,
- ctx);
+ task);
return;
}
if (ctx->run_ws46) {
mm_base_modem_at_command (
- MM_BASE_MODEM (ctx->self),
+ MM_BASE_MODEM (self),
"+WS46=?",
3,
TRUE, /* allow caching, it's a test command */
(GAsyncReadyCallback)supported_modes_ws46_test_ready,
- ctx);
+ task);
return;
}
if (ctx->run_gcap) {
mm_base_modem_at_command (
- MM_BASE_MODEM (ctx->self),
+ MM_BASE_MODEM (self),
"+GCAP",
3,
TRUE, /* allow caching */
(GAsyncReadyCallback)supported_modes_gcap_ready,
- ctx);
+ task);
return;
}
/* All done.
* If no mode found, error */
if (ctx->mode == MM_MODEM_MODE_NONE)
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't retrieve supported modes");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't retrieve supported modes");
else
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (ctx->mode),
- NULL);
- load_supported_modes_context_complete_and_free (ctx);
+ g_task_return_int (task, ctx->mode);
+
+ g_object_unref (task);
}
static void
@@ -1597,28 +1900,27 @@ modem_load_supported_modes (MMIfaceModem *self,
gpointer user_data)
{
LoadSupportedModesContext *ctx;
+ GTask *task;
- mm_dbg ("loading supported modes...");
- ctx = g_slice_new0 (LoadSupportedModesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_modes);
+ mm_obj_dbg (self, "loading supported modes...");
+ ctx = g_new0 (LoadSupportedModesContext, 1);
ctx->mode = MM_MODEM_MODE_NONE;
- if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
+ if (mm_iface_modem_is_3gpp (self)) {
/* Run +WS46=? and *CNTI=2 */
ctx->run_ws46 = TRUE;
ctx->run_cnti = TRUE;
}
- if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self))) {
+ if (mm_iface_modem_is_cdma (self)) {
/* Run +GCAP in order to know if the modem is CDMA1x only or CDMA1x/EV-DO */
ctx->run_gcap = TRUE;
}
- load_supported_modes_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ load_supported_modes_step (task);
}
/*****************************************************************************/
@@ -1629,17 +1931,21 @@ modem_load_supported_ip_families_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_BEARER_IP_FAMILY_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMBearerIpFamily) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_BEARER_IP_FAMILY_NONE;
+ }
+ return (MMBearerIpFamily)value;
}
static void
supported_ip_families_cgdcont_test_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -1649,7 +1955,7 @@ supported_ip_families_cgdcont_test_ready (MMBaseModem *self,
if (response) {
GList *formats, *l;
- formats = mm_3gpp_parse_cgdcont_test_response (response, &error);
+ formats = mm_3gpp_parse_cgdcont_test_response (response, self, &error);
for (l = formats; l; l = g_list_next (l))
mask |= ((MM3gppPdpContextFormat *)(l->data))->pdp_type;
@@ -1657,12 +1963,11 @@ supported_ip_families_cgdcont_test_ready (MMBaseModem *self,
}
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (mask), NULL);
+ g_task_return_int (task, mask);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -1670,21 +1975,14 @@ modem_load_supported_ip_families (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- mm_dbg ("loading supported IP families...");
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_ip_families);
+ mm_obj_dbg (self, "loading supported IP families...");
+ task = g_task_new (self, NULL, callback, user_data);
- if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
- g_simple_async_result_set_op_res_gpointer (
- result,
- GUINT_TO_POINTER (MM_BEARER_IP_FAMILY_IPV4),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (mm_iface_modem_is_cdma_only (self)) {
+ g_task_return_int (task, MM_BEARER_IP_FAMILY_IPV4);
+ g_object_unref (task);
return;
}
@@ -1695,26 +1993,64 @@ modem_load_supported_ip_families (MMIfaceModem *self,
3,
TRUE, /* allow caching, it's a test command */
(GAsyncReadyCallback)supported_ip_families_cgdcont_test_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Signal quality loading (Modem interface) */
+static void
+qcdm_evdo_pilot_sets_log_handle (MMPortSerialQcdm *port,
+ GByteArray *log_buffer,
+ gpointer user_data)
+{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (user_data);
+ QcdmResult *result;
+ uint32_t num_active = 0;
+ uint32_t pilot_pn = 0;
+ uint32_t pilot_energy = 0;
+ int32_t rssi_dbm = 0;
+
+ result = qcdm_log_item_evdo_pilot_sets_v2_new ((const char *) log_buffer->data,
+ log_buffer->len,
+ NULL);
+ if (!result)
+ return;
+
+ if (!qcdm_log_item_evdo_pilot_sets_v2_get_num (result,
+ QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE,
+ &num_active)) {
+ qcdm_result_unref (result);
+ return;
+ }
+
+ if (num_active > 0 &&
+ qcdm_log_item_evdo_pilot_sets_v2_get_pilot (result,
+ QCDM_LOG_ITEM_EVDO_PILOT_SETS_V2_TYPE_ACTIVE,
+ 0,
+ &pilot_pn,
+ &pilot_energy,
+ &rssi_dbm)) {
+ mm_obj_dbg (self, "EVDO active pilot RSSI: %ddBm", rssi_dbm);
+ self->priv->evdo_pilot_rssi = rssi_dbm;
+ }
+
+ qcdm_result_unref (result);
+}
+
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- MMPortSerial *port;
+ MMPortSerial *at_port;
+ MMPortSerial *qcdm_port;
} SignalQualityContext;
static void
-signal_quality_context_complete_and_free (SignalQualityContext *ctx)
+signal_quality_context_free (SignalQualityContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- if (ctx->port)
- g_object_unref (ctx->port);
+ g_clear_object (&ctx->at_port);
+ if (ctx->qcdm_port) {
+ mm_port_serial_close (ctx->qcdm_port);
+ g_object_unref (ctx->qcdm_port);
+ }
g_free (ctx);
}
@@ -1723,17 +2059,28 @@ modem_load_signal_quality_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), error);
+ return value < 0 ? 0 : value;
+}
+
+static guint
+signal_quality_evdo_pilot_sets (MMBroadbandModem *self)
+{
+ if (self->priv->modem_cdma_evdo_registration_state == MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
return 0;
- return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ if (self->priv->evdo_pilot_rssi >= 0)
+ return 0;
+
+ return MM_RSSI_TO_QUALITY (self->priv->evdo_pilot_rssi);
}
static void
signal_quality_csq_ready (MMBroadbandModem *self,
GAsyncResult *res,
- SignalQualityContext *ctx)
+ GTask *task)
{
GError *error = NULL;
GVariant *result;
@@ -1741,8 +2088,8 @@ signal_quality_csq_ready (MMBroadbandModem *self,
result = mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self), res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1755,25 +2102,26 @@ signal_quality_csq_ready (MMBroadbandModem *self,
result_str = mm_strip_tag (result_str, "+CSQ:");
if (sscanf (result_str, "%d, %d", &quality, &ber)) {
if (quality == 99) {
- /* 99 means unknown, no service, etc */
- quality = 0;
+ /* 99 can mean unknown, no service, etc. But the modem may
+ * also only report CDMA 1x quality in CSQ, so try EVDO via
+ * QCDM log messages too.
+ */
+ quality = signal_quality_evdo_pilot_sets (self);
} 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);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_int (task, quality);
+ g_object_unref (task);
return;
}
}
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse signal quality results");
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse signal quality results");
+ g_object_unref (task);
}
/* Some modems want +CSQ, others want +CSQ?, and some of both types
@@ -1781,23 +2129,29 @@ signal_quality_csq_ready (MMBroadbandModem *self,
* try the other command if the first one fails.
*/
static const MMBaseModemAtCommand signal_quality_csq_sequence[] = {
- { "+CSQ", 3, TRUE, response_processor_string_ignore_at_errors },
- { "+CSQ?", 3, TRUE, response_processor_string_ignore_at_errors },
+ { "+CSQ", 3, FALSE, mm_base_modem_response_processor_string_ignore_at_errors },
+ { "+CSQ?", 3, FALSE, mm_base_modem_response_processor_string_ignore_at_errors },
{ NULL }
};
static void
-signal_quality_csq (SignalQualityContext *ctx)
+signal_quality_csq (GTask *task)
{
+ MMBroadbandModem *self;
+ SignalQualityContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (ctx->self),
- MM_PORT_SERIAL_AT (ctx->port),
+ MM_BASE_MODEM (self),
+ MM_PORT_SERIAL_AT (ctx->at_port),
signal_quality_csq_sequence,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
NULL, /* cancellable */
(GAsyncReadyCallback)signal_quality_csq_ready,
- ctx);
+ task);
}
static guint
@@ -1824,7 +2178,7 @@ normalize_ciev_cind_signal_quality (guint quality,
static void
signal_quality_cind_ready (MMBroadbandModem *self,
GAsyncResult *res,
- SignalQualityContext *ctx)
+ GTask *task)
{
GError *error = NULL;
const gchar *result;
@@ -1839,19 +2193,17 @@ signal_quality_cind_ready (MMBroadbandModem *self,
indicators = mm_3gpp_parse_cind_read_response (result, &error);
if (!indicators) {
- mm_dbg ("(%s) Could not parse CIND signal quality results: %s",
- mm_port_get_device (MM_PORT (ctx->port)),
- error->message);
+ mm_obj_dbg (self, "could not parse CIND signal quality results: %s", error->message);
g_clear_error (&error);
goto try_csq;
}
if (indicators->len < self->priv->modem_cind_indicator_signal_quality) {
- mm_dbg ("(%s) Could not parse CIND signal quality results; signal "
- "index (%u) outside received range (0-%u)",
- mm_port_get_device (MM_PORT (ctx->port)),
- self->priv->modem_cind_indicator_signal_quality,
- indicators->len);
+ mm_obj_dbg (self,
+ "could not parse CIND signal quality results; signal "
+ "index (%u) outside received range (0-%u)",
+ self->priv->modem_cind_indicator_signal_quality,
+ indicators->len);
} else {
quality = g_array_index (indicators,
guint8,
@@ -1864,10 +2216,8 @@ signal_quality_cind_ready (MMBroadbandModem *self,
if (quality > 0) {
/* +CIND success */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (quality),
- NULL);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_int (task, quality);
+ g_object_unref (task);
return;
}
@@ -1877,27 +2227,33 @@ try_csq:
* report zero even though they have signal. So if we get zero signal
* from +CIND, try CSQ too. (bgo #636040)
*/
- signal_quality_csq (ctx);
+ signal_quality_csq (task);
}
static void
-signal_quality_cind (SignalQualityContext *ctx)
+signal_quality_cind (GTask *task)
{
- mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->self),
- MM_PORT_SERIAL_AT (ctx->port),
+ MMBroadbandModem *self;
+ SignalQualityContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ MM_PORT_SERIAL_AT (ctx->at_port),
"+CIND?",
- 3,
+ 5,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)signal_quality_cind_ready,
- ctx);
+ task);
}
static void
signal_quality_qcdm_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- SignalQualityContext *ctx)
+ GTask *task)
{
QcdmResult *result;
guint32 num = 0, quality = 0, i;
@@ -1908,8 +2264,8 @@ signal_quality_qcdm_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -1919,12 +2275,12 @@ signal_quality_qcdm_ready (MMPortSerialQcdm *port,
&err);
g_byte_array_unref (response);
if (!result) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse pilot sets command result: %d",
- err);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse pilot sets command result: %d",
+ err);
+ g_object_unref (task);
return;
}
@@ -1955,69 +2311,90 @@ signal_quality_qcdm_ready (MMPortSerialQcdm *port,
quality = (guint32) (100 - (best_db * 100 / (WORST_ECIO - BEST_ECIO)));
}
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (quality),
- NULL);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_int (task, quality);
+ g_object_unref (task);
}
static void
-signal_quality_qcdm (SignalQualityContext *ctx)
+signal_quality_qcdm (GTask *task)
{
+ MMBroadbandModem *self;
+ SignalQualityContext *ctx;
GByteArray *pilot_sets;
+ guint quality;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* If EVDO is active try that signal strength first */
+ quality = signal_quality_evdo_pilot_sets (self);
+ if (quality > 0) {
+ g_task_return_int (task, quality);
+ g_object_unref (task);
+ return;
+ }
/* Use CDMA1x pilot EC/IO if we can */
pilot_sets = g_byte_array_sized_new (25);
pilot_sets->len = qcdm_cmd_pilot_sets_new ((char *) pilot_sets->data, 25);
g_assert (pilot_sets->len);
- mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->port),
+ mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->qcdm_port),
pilot_sets,
3,
NULL,
(GAsyncReadyCallback)signal_quality_qcdm_ready,
- ctx);
+ task);
g_byte_array_unref (pilot_sets);
}
static void
-modem_load_signal_quality (MMIfaceModem *self,
+modem_load_signal_quality (MMIfaceModem *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
SignalQualityContext *ctx;
GError *error = NULL;
+ GTask *task;
- mm_dbg ("loading signal quality...");
+ mm_obj_dbg (self, "loading signal quality...");
ctx = g_new0 (SignalQualityContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_signal_quality);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)signal_quality_context_free);
/* Check whether we can get a non-connected AT port */
- ctx->port = (MMPortSerial *)mm_base_modem_get_best_at_port (MM_BASE_MODEM (self), &error);
- if (ctx->port) {
- 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);
+ ctx->at_port = (MMPortSerial *)mm_base_modem_get_best_at_port (MM_BASE_MODEM (self), &error);
+ if (ctx->at_port) {
+ if (!self->priv->modem_cind_disabled &&
+ self->priv->modem_cind_supported &&
+ CIND_INDICATOR_IS_VALID (self->priv->modem_cind_indicator_signal_quality))
+ signal_quality_cind (task);
else
- signal_quality_csq (ctx);
+ signal_quality_csq (task);
return;
}
/* If no best AT port available (all connected), try with QCDM ports */
- ctx->port = (MMPortSerial *)mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
- if (ctx->port) {
- g_error_free (error);
- signal_quality_qcdm (ctx);
- return;
+ ctx->qcdm_port = (MMPortSerial *)mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
+ if (ctx->qcdm_port) {
+ g_clear_error (&error);
+
+ /* Need to open QCDM port as it may be closed/blocked */
+ if (mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm_port), &error)) {
+ g_object_ref (ctx->qcdm_port);
+ signal_quality_qcdm (task);
+ return;
+ }
+
+ ctx->qcdm_port = NULL;
+ mm_obj_dbg (self, "couldn't open QCDM port: %s", error->message);
}
/* Return the error we got when getting best AT port */
- g_simple_async_result_take_error (ctx->result, error);
- signal_quality_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -2037,20 +2414,17 @@ modem_load_access_technologies_finish (MMIfaceModem *self,
{
AccessTechAndMask *tech;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ tech = g_task_propagate_pointer (G_TASK (res), error);
+ if (!tech)
return FALSE;
- tech = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- g_assert (tech);
-
*access_technologies = tech->access_technologies;
*mask = tech->mask;
+ g_free (tech);
return TRUE;
}
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMPortSerialQcdm *port;
guint32 opmode;
@@ -2065,31 +2439,35 @@ typedef struct {
} AccessTechContext;
static void
-access_tech_context_complete_and_free (AccessTechContext *ctx,
- GError *error, /* takes ownership */
- gboolean idle)
+access_tech_context_free (AccessTechContext *ctx)
+{
+ if (ctx->port) {
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->port));
+ g_object_unref (ctx->port);
+ }
+ g_free (ctx);
+}
+
+static AccessTechAndMask *
+access_tech_and_mask_new (MMBroadbandModem *self,
+ AccessTechContext *ctx)
{
AccessTechAndMask *tech;
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
guint mask = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- goto done;
- }
-
if (ctx->fallback_mask) {
- mm_dbg ("Fallback access technology: 0x%08x", ctx->fallback_act);
+ mm_obj_dbg (self, "fallback access technology: 0x%08x", ctx->fallback_act);
act = ctx->fallback_act;
mask = ctx->fallback_mask;
goto done;
}
- mm_dbg ("QCDM operating mode: %d", ctx->opmode);
- mm_dbg ("QCDM system mode: %d", ctx->sysmode);
- mm_dbg ("QCDM hybrid pref: %d", ctx->hybrid);
- mm_dbg ("QCDM WCDMA open: %d", ctx->wcdma_open);
- mm_dbg ("QCDM EVDO open: %d", ctx->evdo_open);
+ mm_obj_dbg (self, "QCDM operating mode: %d", ctx->opmode);
+ mm_obj_dbg (self, "QCDM system mode: %d", ctx->sysmode);
+ mm_obj_dbg (self, "QCDM hybrid pref: %d", ctx->hybrid);
+ mm_obj_dbg (self, "QCDM WCDMA open: %d", ctx->wcdma_open);
+ mm_obj_dbg (self, "QCDM EVDO open: %d", ctx->evdo_open);
if (ctx->opmode == QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) {
switch (ctx->sysmode) {
@@ -2107,7 +2485,6 @@ access_tech_context_complete_and_free (AccessTechContext *ctx,
mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK;
break;
case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GSM:
- case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA:
case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_GW:
if (ctx->wcdma_open) {
/* Assume UMTS; can't yet determine UMTS/HSxPA/HSPA+ with QCDM */
@@ -2118,38 +2495,36 @@ access_tech_context_complete_and_free (AccessTechContext *ctx,
}
mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
break;
+ case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_WCDMA:
+ if (ctx->wcdma_open) {
+ /* Assume UMTS; can't yet determine UMTS/HSxPA/HSPA+ with QCDM */
+ act = MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ }
+ mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
+ break;
case QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_LTE:
act = MM_MODEM_ACCESS_TECHNOLOGY_LTE;
mask = MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK;
break;
+ default:
+ break;
}
}
done:
- if (error == NULL) {
- tech = g_new0 (AccessTechAndMask, 1);
- tech->access_technologies = act;
- tech->mask = mask;
- g_simple_async_result_set_op_res_gpointer (ctx->result, tech, g_free);
- }
-
- if (idle)
- g_simple_async_result_complete_in_idle (ctx->result);
- else
- g_simple_async_result_complete (ctx->result);
-
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- if (ctx->port)
- g_object_unref (ctx->port);
- g_free (ctx);
+ tech = g_new0 (AccessTechAndMask, 1);
+ tech->access_technologies = act;
+ tech->mask = mask;
+ return tech;
}
static void
access_tech_qcdm_wcdma_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- AccessTechContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self;
+ AccessTechContext *ctx;
QcdmResult *result;
gint err = QCDM_SUCCESS;
guint8 l1;
@@ -2158,10 +2533,14 @@ access_tech_qcdm_wcdma_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- access_tech_context_complete_and_free (ctx, error, FALSE);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Parse the response */
result = qcdm_cmd_wcdma_subsys_state_info_result ((const gchar *) response->data,
response->len,
@@ -2173,18 +2552,23 @@ access_tech_qcdm_wcdma_ready (MMPortSerialQcdm *port,
if (l1 == QCDM_WCDMA_L1_STATE_PCH ||
l1 == QCDM_WCDMA_L1_STATE_FACH ||
- l1 == QCDM_WCDMA_L1_STATE_DCH)
+ l1 == QCDM_WCDMA_L1_STATE_DCH ||
+ l1 == QCDM_WCDMA_L1_STATE_PCH_SLEEP)
ctx->wcdma_open = TRUE;
}
- access_tech_context_complete_and_free (ctx, NULL, FALSE);
+ g_task_return_pointer (task,
+ access_tech_and_mask_new (MM_BROADBAND_MODEM (self), ctx),
+ g_free);
+ g_object_unref (task);
}
static void
access_tech_qcdm_gsm_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- AccessTechContext *ctx)
+ GTask *task)
{
+ AccessTechContext *ctx;
GByteArray *cmd;
QcdmResult *result;
gint err = QCDM_SUCCESS;
@@ -2195,7 +2579,8 @@ access_tech_qcdm_gsm_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- access_tech_context_complete_and_free (ctx, error, FALSE);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -2205,14 +2590,17 @@ access_tech_qcdm_gsm_ready (MMPortSerialQcdm *port,
&err);
g_byte_array_unref (response);
if (!result) {
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse GSM subsys command result: %d",
- err);
- access_tech_context_complete_and_free (ctx, error, FALSE);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse GSM subsys command result: %d",
+ err);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_OP_MODE, &opmode);
qcdm_result_get_u8 (result, QCDM_CMD_GSM_SUBSYS_STATE_INFO_ITEM_CM_SYS_MODE, &sysmode);
qcdm_result_unref (result);
@@ -2230,15 +2618,17 @@ access_tech_qcdm_gsm_ready (MMPortSerialQcdm *port,
3,
NULL,
(GAsyncReadyCallback)access_tech_qcdm_wcdma_ready,
- ctx);
+ task);
g_byte_array_unref (cmd);
}
static void
access_tech_qcdm_hdr_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- AccessTechContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self;
+ AccessTechContext *ctx;
QcdmResult *result;
gint err = QCDM_SUCCESS;
guint8 session = 0;
@@ -2248,10 +2638,14 @@ access_tech_qcdm_hdr_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- access_tech_context_complete_and_free (ctx, error, FALSE);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Parse the response */
result = qcdm_cmd_hdr_subsys_state_info_result ((const gchar *) response->data,
response->len,
@@ -2268,14 +2662,16 @@ access_tech_qcdm_hdr_ready (MMPortSerialQcdm *port,
ctx->evdo_open = TRUE;
}
- access_tech_context_complete_and_free (ctx, NULL, FALSE);
+ g_task_return_pointer (task, access_tech_and_mask_new (self, ctx), g_free);
+ g_object_unref (task);
}
static void
access_tech_qcdm_cdma_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- AccessTechContext *ctx)
+ GTask *task)
{
+ AccessTechContext *ctx;
GByteArray *cmd;
QcdmResult *result;
gint err = QCDM_SUCCESS;
@@ -2285,7 +2681,8 @@ access_tech_qcdm_cdma_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- access_tech_context_complete_and_free (ctx, error, FALSE);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -2295,14 +2692,17 @@ access_tech_qcdm_cdma_ready (MMPortSerialQcdm *port,
&err);
g_byte_array_unref (response);
if (!result) {
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse CM subsys command result: %d",
- err);
- access_tech_context_complete_and_free (ctx, error, FALSE);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse CM subsys command result: %d",
+ err);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_OPERATING_MODE, &ctx->opmode);
qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &ctx->sysmode);
qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_HYBRID_PREF, &hybrid);
@@ -2320,7 +2720,7 @@ access_tech_qcdm_cdma_ready (MMPortSerialQcdm *port,
3,
NULL,
(GAsyncReadyCallback)access_tech_qcdm_hdr_ready,
- ctx);
+ task);
g_byte_array_unref (cmd);
}
@@ -2345,9 +2745,9 @@ access_tech_from_cdma_registration_state (MMBroadbandModem *self,
ctx->fallback_mask = MM_IFACE_MODEM_CDMA_ALL_ACCESS_TECHNOLOGIES_MASK;
}
- mm_dbg ("EVDO registration: %d", self->priv->modem_cdma_evdo_registration_state);
- mm_dbg ("CDMA1x registration: %d", self->priv->modem_cdma_cdma1x_registration_state);
- mm_dbg ("Fallback access tech: 0x%08x", ctx->fallback_act);
+ mm_obj_dbg (self, "EVDO registration: %d", self->priv->modem_cdma_evdo_registration_state);
+ mm_obj_dbg (self, "CDMA1x registration: %d", self->priv->modem_cdma_cdma1x_registration_state);
+ mm_obj_dbg (self, "fallback access tech: 0x%08x", ctx->fallback_act);
}
static void
@@ -2356,6 +2756,7 @@ modem_load_access_technologies (MMIfaceModem *self,
gpointer user_data)
{
AccessTechContext *ctx;
+ GTask *task;
GByteArray *cmd;
GError *error = NULL;
@@ -2364,66 +2765,77 @@ modem_load_access_technologies (MMIfaceModem *self,
* registration state
*/
ctx = g_new0 (AccessTechContext, 1);
- ctx->self = g_object_ref (self);
- ctx->port = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_access_technologies);
-
- if (!ctx->port) {
- if (mm_iface_modem_is_cdma (self)) {
- /* If we don't have a QCDM port but the modem is CDMA-only, then
- * guess access technologies from the registration information.
+ ctx->port = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)access_tech_context_free);
+
+ if (ctx->port) {
+ /* Need to open QCDM port as it may be closed/blocked */
+ if (mm_port_serial_open (MM_PORT_SERIAL (ctx->port), &error)) {
+ g_object_ref (ctx->port);
+
+ mm_obj_dbg (self, "loading access technologies via QCDM...");
+
+ /* FIXME: we may want to run both the CDMA and 3GPP in sequence to ensure
+ * that a multi-mode device that's in CDMA-mode but still has 3GPP capabilities
+ * will get the correct access tech, since the 3GPP check is run first.
*/
- access_tech_from_cdma_registration_state (MM_BROADBAND_MODEM (self), ctx);
- } else {
- error = g_error_new_literal (MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot get 3GPP access technology without a QCDM port");
- }
- access_tech_context_complete_and_free (ctx, error, TRUE);
- return;
- }
- mm_dbg ("loading access technologies via QCDM...");
+ if (mm_iface_modem_is_3gpp (self)) {
+ cmd = g_byte_array_sized_new (50);
+ cmd->len = qcdm_cmd_gsm_subsys_state_info_new ((char *) cmd->data, 50);
+ g_assert (cmd->len);
+
+ mm_port_serial_qcdm_command (ctx->port,
+ cmd,
+ 3,
+ NULL,
+ (GAsyncReadyCallback)access_tech_qcdm_gsm_ready,
+ task);
+ g_byte_array_unref (cmd);
+ return;
+ }
- /* FIXME: we may want to run both the CDMA and 3GPP in sequence to ensure
- * that a multi-mode device that's in CDMA-mode but still has 3GPP capabilities
- * will get the correct access tech, since the 3GPP check is run first.
- */
+ if (mm_iface_modem_is_cdma (self)) {
+ cmd = g_byte_array_sized_new (50);
+ cmd->len = qcdm_cmd_cm_subsys_state_info_new ((char *) cmd->data, 50);
+ g_assert (cmd->len);
+
+ mm_port_serial_qcdm_command (ctx->port,
+ cmd,
+ 3,
+ NULL,
+ (GAsyncReadyCallback)access_tech_qcdm_cdma_ready,
+ task);
+ g_byte_array_unref (cmd);
+ return;
+ }
- if (mm_iface_modem_is_3gpp (self)) {
- cmd = g_byte_array_sized_new (50);
- cmd->len = qcdm_cmd_gsm_subsys_state_info_new ((char *) cmd->data, 50);
- g_assert (cmd->len);
+ g_assert_not_reached ();
+ }
- mm_port_serial_qcdm_command (ctx->port,
- cmd,
- 3,
- NULL,
- (GAsyncReadyCallback)access_tech_qcdm_gsm_ready,
- ctx);
- g_byte_array_unref (cmd);
- return;
+ ctx->port = NULL;
+ mm_obj_dbg (self, "couldn't open QCDM port: %s", error->message);
+ g_clear_error (&error);
}
+ /* Fall back if we don't have a QCDM port or it couldn't be opened */
if (mm_iface_modem_is_cdma (self)) {
- cmd = g_byte_array_sized_new (50);
- cmd->len = qcdm_cmd_cm_subsys_state_info_new ((char *) cmd->data, 50);
- g_assert (cmd->len);
-
- mm_port_serial_qcdm_command (ctx->port,
- cmd,
- 3,
- NULL,
- (GAsyncReadyCallback)access_tech_qcdm_cdma_ready,
- ctx);
- g_byte_array_unref (cmd);
- return;
+ /* If we don't have a QCDM port but the modem is CDMA-only, then
+ * guess access technologies from the registration information.
+ */
+ access_tech_from_cdma_registration_state (MM_BROADBAND_MODEM (self), ctx);
+ g_task_return_pointer (task,
+ access_tech_and_mask_new (MM_BROADBAND_MODEM (self), ctx),
+ g_free);
+ } else {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot get 3GPP access technology without a QCDM port");
}
-
- g_assert_not_reached ();
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -2434,50 +2846,361 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-ciev_received (MMPortSerialAt *port,
- GMatchInfo *info,
+bearer_report_disconnected (MMBaseBearer *bearer,
+ gpointer user_data)
+{
+ gint profile_id;
+
+ profile_id = GPOINTER_TO_INT (user_data);
+
+ /* If we're told to disconnect a single context and this is not the
+ * bearer associated to that context, ignore operation */
+ if ((profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) && (mm_base_bearer_get_profile_id (bearer) != profile_id))
+ return;
+
+ /* If already disconnected, ignore operation */
+ if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_DISCONNECTED)
+ return;
+
+ mm_obj_info (bearer, "explicitly disconnected");
+ mm_base_bearer_report_connection_status (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+}
+
+static void
+bearer_list_report_disconnections (MMBroadbandModem *self,
+ gint profile_id)
+{
+ g_autoptr(MMBearerList) list = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_BEARER_LIST, &list,
+ NULL);
+
+ /* If empty bearer list, nothing else to do */
+ if (list)
+ mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_report_disconnected, GINT_TO_POINTER (profile_id));
+}
+
+static void
+cgev_process_detach (MMBroadbandModem *self,
+ MM3gppCgev type)
+{
+ if (type == MM_3GPP_CGEV_NW_DETACH) {
+ mm_obj_info (self, "network forced PS detach: all contexts have been deactivated");
+ bearer_list_report_disconnections (self, MM_3GPP_PROFILE_ID_UNKNOWN);
+ return;
+ }
+
+ if (type == MM_3GPP_CGEV_ME_DETACH) {
+ mm_obj_info (self, "mobile equipment forced PS detach: all contexts have been deactivated");
+ bearer_list_report_disconnections (self, MM_3GPP_PROFILE_ID_UNKNOWN);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+cgev_process_primary (MMBroadbandModem *self,
+ MM3gppCgev type,
+ const gchar *str)
+{
+ GError *error = NULL;
+ guint cid = 0;
+
+ if (!mm_3gpp_parse_cgev_indication_primary (str, type, &cid, &error)) {
+ mm_obj_warn (self, "couldn't parse cid info from +CGEV indication '%s': %s", str, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ switch (type) {
+ case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+ mm_obj_info (self, "network request to activate context (cid %u)", cid);
+ break;
+ case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+ mm_obj_info (self, "mobile equipment request to activate context (cid %u)", cid);
+ break;
+ case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+ mm_obj_info (self, "network request to deactivate context (cid %u)", cid);
+ bearer_list_report_disconnections (self, (gint)cid);
+ break;
+ case MM_3GPP_CGEV_ME_DEACT_PRIMARY:
+ mm_obj_info (self, "mobile equipment request to deactivate context (cid %u)", cid);
+ bearer_list_report_disconnections (self, (gint)cid);
+ break;
+ case MM_3GPP_CGEV_UNKNOWN:
+ case MM_3GPP_CGEV_NW_DETACH:
+ case MM_3GPP_CGEV_ME_DETACH:
+ case MM_3GPP_CGEV_NW_CLASS:
+ case MM_3GPP_CGEV_ME_CLASS:
+ case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+ case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_DEACT_SECONDARY:
+ case MM_3GPP_CGEV_NW_DEACT_PDP:
+ case MM_3GPP_CGEV_ME_DEACT_PDP:
+ case MM_3GPP_CGEV_NW_MODIFY:
+ case MM_3GPP_CGEV_ME_MODIFY:
+ case MM_3GPP_CGEV_REJECT:
+ case MM_3GPP_CGEV_NW_REACT:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+cgev_process_secondary (MMBroadbandModem *self,
+ MM3gppCgev type,
+ const gchar *str)
+{
+ GError *error = NULL;
+ guint p_cid = 0;
+ guint cid = 0;
+
+ if (!mm_3gpp_parse_cgev_indication_secondary (str, type, &p_cid, &cid, NULL, &error)) {
+ mm_obj_warn (self, "couldn't parse p_cid/cid info from +CGEV indication '%s': %s", str, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ switch (type) {
+ case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+ mm_obj_info (self, "network request to activate secondary context (cid %u, primary cid %u)", cid, p_cid);
+ break;
+ case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+ mm_obj_info (self, "mobile equipment request to activate secondary context (cid %u, primary cid %u)", cid, p_cid);
+ break;
+ case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+ mm_obj_info (self, "network request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid);
+ bearer_list_report_disconnections (self, (gint)cid);
+ break;
+ case MM_3GPP_CGEV_ME_DEACT_SECONDARY:
+ mm_obj_info (self, "mobile equipment request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid);
+ bearer_list_report_disconnections (self, (gint)cid);
+ break;
+ case MM_3GPP_CGEV_UNKNOWN:
+ case MM_3GPP_CGEV_NW_DETACH:
+ case MM_3GPP_CGEV_ME_DETACH:
+ case MM_3GPP_CGEV_NW_CLASS:
+ case MM_3GPP_CGEV_ME_CLASS:
+ case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+ case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_DEACT_PRIMARY:
+ case MM_3GPP_CGEV_NW_DEACT_PDP:
+ case MM_3GPP_CGEV_ME_DEACT_PDP:
+ case MM_3GPP_CGEV_NW_MODIFY:
+ case MM_3GPP_CGEV_ME_MODIFY:
+ case MM_3GPP_CGEV_REJECT:
+ case MM_3GPP_CGEV_NW_REACT:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+cgev_process_pdp (MMBroadbandModem *self,
+ MM3gppCgev type,
+ const gchar *str)
+{
+ GError *error = NULL;
+ gchar *pdp_type = NULL;
+ gchar *pdp_addr = NULL;
+ guint cid = 0;
+
+ if (!mm_3gpp_parse_cgev_indication_pdp (str, type, &pdp_type, &pdp_addr, &cid, &error)) {
+ mm_obj_warn (self, "couldn't parse PDP info from +CGEV indication '%s': %s", str, error->message);
+ g_error_free (error);
+ return;
+ }
+
+ switch (type) {
+ case MM_3GPP_CGEV_REJECT:
+ mm_obj_info (self, "network request to activate context (type %s, address %s) has been automatically rejected", pdp_type, pdp_addr);
+ break;
+ case MM_3GPP_CGEV_NW_REACT:
+ /* NOTE: we don't currently notify about automatic reconnections like this one */
+ if (cid)
+ mm_obj_info (self, "network request to reactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid);
+ else
+ mm_obj_info (self, "network request to reactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr);
+ break;
+ case MM_3GPP_CGEV_NW_DEACT_PDP:
+ if (cid) {
+ mm_obj_info (self, "network request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid);
+ bearer_list_report_disconnections (self, (gint)cid);
+ } else
+ mm_obj_info (self, "network request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr);
+ break;
+ case MM_3GPP_CGEV_ME_DEACT_PDP:
+ if (cid) {
+ mm_obj_info (self, "mobile equipment request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid);
+ bearer_list_report_disconnections (self, (gint)cid);
+ } else
+ mm_obj_info (self, "mobile equipment request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr);
+ break;
+ case MM_3GPP_CGEV_UNKNOWN:
+ case MM_3GPP_CGEV_NW_DETACH:
+ case MM_3GPP_CGEV_ME_DETACH:
+ case MM_3GPP_CGEV_NW_CLASS:
+ case MM_3GPP_CGEV_ME_CLASS:
+ case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+ case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+ case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_DEACT_PRIMARY:
+ case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_DEACT_SECONDARY:
+ case MM_3GPP_CGEV_NW_MODIFY:
+ case MM_3GPP_CGEV_ME_MODIFY:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ g_free (pdp_addr);
+ g_free (pdp_type);
+}
+
+static void
+cgev_received (MMPortSerialAt *port,
+ GMatchInfo *info,
MMBroadbandModem *self)
{
- gint ind = 0;
- gchar *item;
+ gchar *str;
+ MM3gppCgev type;
- item = g_match_info_fetch (info, 1);
- if (item)
- ind = atoi (item);
+ str = mm_get_string_unquoted_from_match_info (info, 1);
+ if (!str)
+ return;
- /* Handle signal quality change indication */
- if (ind == self->priv->modem_cind_indicator_signal_quality ||
- g_str_equal (item, "signal")) {
- gchar *value;
+ type = mm_3gpp_parse_cgev_indication_action (str);
- value = g_match_info_fetch (info, 2);
- if (value) {
- gint quality = 0;
+ switch (type) {
+ case MM_3GPP_CGEV_NW_DETACH:
+ case MM_3GPP_CGEV_ME_DETACH:
+ cgev_process_detach (self, type);
+ break;
+ case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+ case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_DEACT_PRIMARY:
+ cgev_process_primary (self, type, str);
+ break;
+ case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+ case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_DEACT_SECONDARY:
+ cgev_process_secondary (self, type, str);
+ break;
+ case MM_3GPP_CGEV_NW_DEACT_PDP:
+ case MM_3GPP_CGEV_ME_DEACT_PDP:
+ case MM_3GPP_CGEV_REJECT:
+ case MM_3GPP_CGEV_NW_REACT:
+ cgev_process_pdp (self, type, str);
+ break;
+ case MM_3GPP_CGEV_NW_CLASS:
+ case MM_3GPP_CGEV_ME_CLASS:
+ case MM_3GPP_CGEV_NW_MODIFY:
+ case MM_3GPP_CGEV_ME_MODIFY:
+ /* ignore */
+ break;
+ case MM_3GPP_CGEV_UNKNOWN:
+ default:
+ mm_obj_dbg (self, "unhandled +CGEV indication: %s", str);
+ break;
+ }
- quality = atoi (value);
+ g_free (str);
+}
- mm_iface_modem_update_signal_quality (
- MM_IFACE_MODEM (self),
- normalize_ciev_cind_signal_quality (quality,
- self->priv->modem_cind_min_signal_quality,
- self->priv->modem_cind_max_signal_quality));
- g_free (value);
- }
+static void
+set_cgev_unsolicited_events_handlers (MMBroadbandModem *self,
+ gboolean enable)
+{
+ MMPortSerialAt *ports[2];
+ GRegex *cgev_regex;
+ guint i;
+
+ cgev_regex = mm_3gpp_cgev_regex_get ();
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Enable unsolicited events in given port */
+ for (i = 0; i < 2; i++) {
+ if (!ports[i])
+ continue;
+
+ /* Set/unset unsolicited CGEV event handler */
+ mm_obj_dbg (self, "%s 3GPP +CGEV unsolicited events handlers in %s",
+ enable ? "setting" : "removing",
+ mm_port_get_device (MM_PORT (ports[i])));
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ cgev_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn) cgev_received : NULL,
+ enable ? self : NULL,
+ NULL);
}
- g_free (item);
+ g_regex_unref (cgev_regex);
+}
+
+static void
+ciev_signal_received (MMBroadbandModem *self,
+ GMatchInfo *match_info)
+{
+ guint quality;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &quality)) {
+ mm_obj_dbg (self, "couldn't parse signal quality value from +CIEV");
+ return;
+ }
+
+ mm_iface_modem_update_signal_quality (
+ MM_IFACE_MODEM (self),
+ normalize_ciev_cind_signal_quality (quality,
+ self->priv->modem_cind_min_signal_quality,
+ self->priv->modem_cind_max_signal_quality));
+}
+
+static void
+ciev_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModem *self)
+{
+ guint ind;
+ gchar *item;
+
+ item = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (!item)
+ return;
+
+ /* numeric index? */
+ if (mm_get_uint_from_str (item, &ind)) {
+ if (ind == self->priv->modem_cind_indicator_signal_quality)
+ ciev_signal_received (self, match_info);
+ }
+ /* string index? */
+ else {
+ if (g_str_equal (item, "signal"))
+ ciev_signal_received (self, match_info);
+ }
- /* FIXME: handle roaming and service indicators.
- * ... wait, arent these already handle by unsolicited CREG responses? */
+ g_free (item);
}
static void
-set_unsolicited_events_handlers (MMBroadbandModem *self,
- gboolean enable)
+set_ciev_unsolicited_events_handlers (MMBroadbandModem *self,
+ gboolean enable)
{
MMPortSerialAt *ports[2];
GRegex *ciev_regex;
@@ -2493,9 +3216,9 @@ set_unsolicited_events_handlers (MMBroadbandModem *self,
continue;
/* Set/unset unsolicited CIEV event handler */
- mm_dbg ("(%s) %s 3GPP unsolicited events handlers",
- mm_port_get_device (MM_PORT (ports[i])),
- enable ? "Setting" : "Removing");
+ mm_obj_dbg (self, "%s 3GPP +CIEV unsolicited events handlers in %s",
+ enable ? "setting" : "removing",
+ mm_port_get_device (MM_PORT (ports[i])));
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
ciev_regex,
@@ -2508,9 +3231,113 @@ set_unsolicited_events_handlers (MMBroadbandModem *self,
}
static void
+support_checked_setup_unsolicited_events (GTask *task)
+{
+ MMBroadbandModem *self;
+
+ self = g_task_get_source_object (task);
+
+ if (self->priv->modem_cind_supported)
+ set_ciev_unsolicited_events_handlers (self, TRUE);
+
+ if (self->priv->modem_cgerep_supported)
+ set_cgev_unsolicited_events_handlers (self, TRUE);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void check_and_setup_3gpp_urc_support (GTask *task);
+
+static void
+cgerep_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ const gchar *result;
+
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (!result) {
+ mm_obj_dbg (self, "+CGEREP check failed: %s", error->message);
+ mm_obj_dbg (self, "packet domain event reporting is unsupported");
+ g_error_free (error);
+ goto out;
+ }
+
+ mm_obj_dbg (self, "packet domain event reporting is supported");
+ self->priv->modem_cgerep_supported = TRUE;
+
+out:
+ /* go on with remaining checks */
+ check_and_setup_3gpp_urc_support (task);
+}
+
+static void
+cmer_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MM3gppCmerMode supported_modes = MM_3GPP_CMER_MODE_NONE;
+ MM3gppCmerInd supported_inds = MM_3GPP_CMER_IND_NONE;
+ GError *error = NULL;
+ const gchar *result;
+ gchar *aux;
+
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error || !mm_3gpp_parse_cmer_test_response (result, self, &supported_modes, &supported_inds, &error)) {
+ mm_obj_dbg (self, "+CMER check failed: %s", error->message);
+ mm_obj_dbg (self, "generic indications are unsupported");
+ g_error_free (error);
+ goto out;
+ }
+
+ aux = mm_3gpp_cmer_mode_build_string_from_mask (supported_modes);
+ mm_obj_dbg (self, "supported +CMER modes: %s", aux);
+ g_free (aux);
+
+ aux = mm_3gpp_cmer_ind_build_string_from_mask (supported_inds);
+ mm_obj_dbg (self, "supported +CMER indication settings: %s", aux);
+ g_free (aux);
+
+ /* Flag +CMER supported values */
+
+ if (supported_modes & MM_3GPP_CMER_MODE_FORWARD_URCS)
+ self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_FORWARD_URCS;
+ else if (supported_modes & MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED)
+ self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED;
+ else if (supported_modes & MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED)
+ self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED;
+
+ aux = mm_3gpp_cmer_mode_build_string_from_mask (self->priv->modem_cmer_enable_mode);
+ mm_obj_dbg (self, "+CMER enable mode: %s", aux);
+ g_free (aux);
+
+ if (supported_modes & MM_3GPP_CMER_MODE_DISCARD_URCS)
+ self->priv->modem_cmer_disable_mode = MM_3GPP_CMER_MODE_DISCARD_URCS;
+
+ aux = mm_3gpp_cmer_mode_build_string_from_mask (self->priv->modem_cmer_disable_mode);
+ mm_obj_dbg (self, "+CMER disable mode: %s", aux);
+ g_free (aux);
+
+ if (supported_inds & MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND)
+ self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND;
+ else if (supported_inds & MM_3GPP_CMER_IND_ENABLE_ALL)
+ self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_ENABLE_ALL;
+
+ aux = mm_3gpp_cmer_ind_build_string_from_mask (self->priv->modem_cmer_ind);
+ mm_obj_dbg (self, "+CMER indication setting: %s", aux);
+ g_free (aux);
+
+out:
+ /* go on with remaining checks */
+ check_and_setup_3gpp_urc_support (task);
+}
+
+static void
cind_format_check_ready (MMBroadbandModem *self,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
GHashTable *indicators = NULL;
GError *error = NULL;
@@ -2521,11 +3348,11 @@ cind_format_check_ready (MMBroadbandModem *self,
if (error ||
!(indicators = mm_3gpp_parse_cind_test_response (result, &error))) {
/* unsupported indications */
- mm_dbg ("Marking indications as unsupported: '%s'", error->message);
+ mm_obj_dbg (self, "+CIND check failed: %s", error->message);
+ mm_obj_dbg (self, "generic indications are unsupported");
g_error_free (error);
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ /* go on with remaining checks */
+ check_and_setup_3gpp_urc_support (task);
return;
}
@@ -2540,11 +3367,10 @@ cind_format_check_ready (MMBroadbandModem *self,
self->priv->modem_cind_min_signal_quality = mm_3gpp_cind_response_get_min (r);
self->priv->modem_cind_max_signal_quality = mm_3gpp_cind_response_get_max (r);
- mm_dbg ("Modem supports signal quality indications via CIND at index '%u'"
- "(min: %u, max: %u)",
- self->priv->modem_cind_indicator_signal_quality,
- self->priv->modem_cind_min_signal_quality,
- self->priv->modem_cind_max_signal_quality);
+ mm_obj_dbg (self, "signal quality indications via CIND are supported at index '%u' (min: %u, max: %u)",
+ self->priv->modem_cind_indicator_signal_quality,
+ self->priv->modem_cind_min_signal_quality,
+ self->priv->modem_cind_max_signal_quality);
} else
self->priv->modem_cind_indicator_signal_quality = CIND_INDICATOR_INVALID;
@@ -2552,8 +3378,8 @@ cind_format_check_ready (MMBroadbandModem *self,
r = g_hash_table_lookup (indicators, "roam");
if (r) {
self->priv->modem_cind_indicator_roaming = mm_3gpp_cind_response_get_index (r);
- mm_dbg ("Modem supports roaming indications via CIND at index '%u'",
- self->priv->modem_cind_indicator_roaming);
+ mm_obj_dbg (self, "roaming indications via CIND are supported at index '%u'",
+ self->priv->modem_cind_indicator_roaming);
} else
self->priv->modem_cind_indicator_roaming = CIND_INDICATOR_INVALID;
@@ -2561,97 +3387,113 @@ cind_format_check_ready (MMBroadbandModem *self,
r = g_hash_table_lookup (indicators, "service");
if (r) {
self->priv->modem_cind_indicator_service = mm_3gpp_cind_response_get_index (r);
- mm_dbg ("Modem supports service indications via CIND at index '%u'",
- self->priv->modem_cind_indicator_service);
+ mm_obj_dbg (self, "service indications via CIND are supported at index '%u'",
+ self->priv->modem_cind_indicator_service);
} else
self->priv->modem_cind_indicator_service = CIND_INDICATOR_INVALID;
g_hash_table_destroy (indicators);
- /* Now, keep on setting up the ports */
- set_unsolicited_events_handlers (self, TRUE);
-
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ /* Check +CMER required format */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CMER=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)cmer_format_check_ready,
+ task);
}
static void
-modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+check_and_setup_3gpp_urc_support (GTask *task)
{
- MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
- GSimpleAsyncResult *result;
+ MMBroadbandModem *self;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_events);
+ self = g_task_get_source_object (task);
- /* Load supported indicators */
- if (!self->priv->modem_cind_support_checked) {
- mm_dbg ("Checking indicator support...");
+ /* Check support for +CIEV indications, managed with +CIND/+CMER */
+ if (!self->priv->modem_cind_disabled && !self->priv->modem_cind_support_checked) {
+ mm_obj_dbg (self, "checking indicator support...");
self->priv->modem_cind_support_checked = TRUE;
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CIND=?",
3,
TRUE,
(GAsyncReadyCallback)cind_format_check_ready,
- result);
+ task);
return;
}
- /* If supported, go on */
- if (self->priv->modem_cind_supported)
- set_unsolicited_events_handlers (self, TRUE);
+ /* Check support for +CGEV indications, managed with +CGEREP */
+ if (!self->priv->modem_cgerep_support_checked) {
+ mm_obj_dbg (self, "checking packet domain event reporting...");
+ self->priv->modem_cgerep_support_checked = TRUE;
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CGEREP=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)cgerep_format_check_ready,
+ task);
+ return;
+ }
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ support_checked_setup_unsolicited_events (task);
}
static void
-modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ check_and_setup_3gpp_urc_support (task);
+}
+
+static void
+modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
- /* If supported, go on */
- if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
- set_unsolicited_events_handlers (self, FALSE);
+ if (!self->priv->modem_cind_disabled && self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
+ set_ciev_unsolicited_events_handlers (self, FALSE);
+
+ if (self->priv->modem_cgerep_supported)
+ set_cgev_unsolicited_events_handlers (self, FALSE);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
/* Enabling/disabling unsolicited events (3GPP interface) */
typedef struct {
- MMBroadbandModem *self;
- gchar *command;
- gboolean enable;
- GSimpleAsyncResult *result;
- gboolean cmer_primary_done;
- gboolean cmer_secondary_done;
+ gboolean enable;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ gchar *cmer_command;
+ gboolean cmer_primary_done;
+ gboolean cmer_secondary_done;
+ gchar *cgerep_command;
+ gboolean cgerep_primary_done;
+ gboolean cgerep_secondary_done;
} UnsolicitedEventsContext;
static void
-unsolicited_events_context_complete_and_free (UnsolicitedEventsContext *ctx)
+unsolicited_events_context_free (UnsolicitedEventsContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx->command);
+ if (ctx->secondary)
+ g_object_unref (ctx->secondary);
+ if (ctx->primary)
+ g_object_unref (ctx->primary);
+ g_free (ctx->cgerep_command);
+ g_free (ctx->cmer_command);
g_free (ctx);
}
@@ -2660,64 +3502,89 @@ modem_3gpp_enable_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void run_unsolicited_events_setup (UnsolicitedEventsContext *ctx);
+static void run_unsolicited_events_setup (GTask *task);
static void
unsolicited_events_setup_ready (MMBroadbandModem *self,
- GAsyncResult *res,
- UnsolicitedEventsContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ UnsolicitedEventsContext *ctx;
+ GError *error = NULL;
- mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!error) {
- /* Run on next port, if any */
- run_unsolicited_events_setup (ctx);
- return;
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) {
+ mm_obj_dbg (self, "couldn't %s event reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
}
- mm_dbg ("Couldn't %s event reporting: '%s'",
- ctx->enable ? "enable" : "disable",
- error->message);
- g_error_free (error);
- /* Consider this operation complete, ignoring errors */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- unsolicited_events_context_complete_and_free (ctx);
+ /* Continue on next port/command */
+ run_unsolicited_events_setup (task);
}
static void
-run_unsolicited_events_setup (UnsolicitedEventsContext *ctx)
+run_unsolicited_events_setup (GTask *task)
{
- MMPortSerialAt *port = NULL;
+ MMBroadbandModem *self;
+ UnsolicitedEventsContext *ctx;
+ MMPortSerialAt *port = NULL;
+ const gchar *command = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- if (!ctx->cmer_primary_done) {
+ /* CMER on primary port */
+ if (!ctx->cmer_primary_done && ctx->cmer_command && ctx->primary && !self->priv->modem_cind_disabled) {
+ mm_obj_dbg (self, "%s +CIND event reporting in primary port...", ctx->enable ? "enabling" : "disabling");
ctx->cmer_primary_done = TRUE;
- port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self));
- } else if (!ctx->cmer_secondary_done) {
+ command = ctx->cmer_command;
+ port = ctx->primary;
+ }
+ /* CMER on secondary port */
+ else if (!ctx->cmer_secondary_done && ctx->cmer_command && ctx->secondary && !self->priv->modem_cind_disabled) {
+ mm_obj_dbg (self, "%s +CIND event reporting in secondary port...", ctx->enable ? "enabling" : "disabling");
ctx->cmer_secondary_done = TRUE;
- port = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (ctx->self));
+ command = ctx->cmer_command;
+ port = ctx->secondary;
+ }
+ /* CGEREP on primary port */
+ else if (!ctx->cgerep_primary_done && ctx->cgerep_command && ctx->primary) {
+ mm_obj_dbg (self, "%s +CGEV event reporting in primary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->cgerep_primary_done = TRUE;
+ command = ctx->cgerep_command;
+ port = ctx->primary;
+ }
+ /* CGEREP on secondary port */
+ else if (!ctx->cgerep_secondary_done && ctx->cgerep_command && ctx->secondary) {
+ mm_obj_dbg (self, "%s +CGEV event reporting in secondary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->cgerep_secondary_done = TRUE;
+ port = ctx->secondary;
+ command = ctx->cgerep_command;
}
/* Enable unsolicited events in given port */
- if (port) {
- mm_base_modem_at_command_full (MM_BASE_MODEM (ctx->self),
+ if (port && command) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
port,
- ctx->command,
+ command,
3,
FALSE,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)unsolicited_events_setup_ready,
- ctx);
+ task);
return;
}
- /* If no more ports, we're fully done now */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- unsolicited_events_context_complete_and_free (ctx);
+ /* Fully done now */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -2725,30 +3592,25 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
- GSimpleAsyncResult *result;
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ GTask *task;
+ UnsolicitedEventsContext *ctx;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_enable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
- /* If supported, go on */
- if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) {
- UnsolicitedEventsContext *ctx;
+ ctx = g_new0 (UnsolicitedEventsContext, 1);
+ ctx->enable = TRUE;
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free);
- ctx = g_new0 (UnsolicitedEventsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->enable = TRUE;
- ctx->command = g_strdup ("+CMER=3,0,0,1");
- ctx->result = result;
- run_unsolicited_events_setup (ctx);
- return;
- }
+ if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
+ ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_enable_mode, self->priv->modem_cmer_ind);
+
+ if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported)
+ ctx->cgerep_command = g_strdup ("+CGEREP=2");
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ run_unsolicited_events_setup (task);
}
static void
@@ -2756,50 +3618,43 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
- GSimpleAsyncResult *result;
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ GTask *task;
+ UnsolicitedEventsContext *ctx;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_disable_unsolicited_events);
+ task = g_task_new (self, NULL, callback, user_data);
- /* If supported, go on */
- if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) {
- UnsolicitedEventsContext *ctx;
+ ctx = g_new0 (UnsolicitedEventsContext, 1);
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+ g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free);
- ctx = g_new0 (UnsolicitedEventsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->command = g_strdup ("+CMER=0");
- ctx->result = result;
- run_unsolicited_events_setup (ctx);
- return;
- }
+ if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported)
+ ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_disable_mode, MM_3GPP_CMER_IND_NONE);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported)
+ ctx->cgerep_command = g_strdup ("+CGEREP=0");
+
+ run_unsolicited_events_setup (task);
}
/*****************************************************************************/
/* Setting modem charset (Modem interface) */
typedef struct {
- GSimpleAsyncResult *result;
MMModemCharset charset;
/* Commands to try in the sequence:
* First one with quotes
* Second without.
* + last NUL */
- MMBaseModemAtCommand charset_commands[3];
+ MMBaseModemAtCommandAlloc charset_commands[3];
} SetupCharsetContext;
static void
setup_charset_context_free (SetupCharsetContext *ctx)
{
- g_object_unref (ctx->result);
- g_free (ctx->charset_commands[0].command);
- g_free (ctx->charset_commands[1].command);
+ mm_base_modem_at_command_alloc_clear (&ctx->charset_commands[0]);
+ mm_base_modem_at_command_alloc_clear (&ctx->charset_commands[1]);
g_free (ctx);
}
@@ -2808,23 +3663,23 @@ modem_setup_charset_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return FALSE;
-
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
current_charset_query_ready (MMBroadbandModem *self,
GAsyncResult *res,
- SetupCharsetContext *ctx)
+ GTask *task)
{
+ SetupCharsetContext *ctx;
GError *error = NULL;
const gchar *response;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else {
MMModemCharset current;
const gchar *p;
@@ -2837,35 +3692,33 @@ current_charset_query_ready (MMBroadbandModem *self,
current = mm_modem_charset_from_string (p);
if (ctx->charset != current)
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Modem failed to change character set to %s",
- mm_modem_charset_to_string (ctx->charset));
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Modem failed to change character set to %s",
+ mm_modem_charset_to_string (ctx->charset));
else {
/* We'll keep track ourselves of the current charset.
* TODO: Make this a property so that plugins can also store it. */
self->priv->modem_current_charset = current;
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
}
}
- g_simple_async_result_complete (ctx->result);
- setup_charset_context_free (ctx);
+ g_object_unref (task);
}
static void
charset_change_ready (MMBroadbandModem *self,
GAsyncResult *res,
- SetupCharsetContext *ctx)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- g_simple_async_result_complete (ctx->result);
- setup_charset_context_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -2875,7 +3728,7 @@ charset_change_ready (MMBroadbandModem *self,
3,
FALSE,
(GAsyncReadyCallback)current_charset_query_ready,
- ctx);
+ task);
}
static void
@@ -2886,6 +3739,7 @@ modem_setup_charset (MMIfaceModem *self,
{
SetupCharsetContext *ctx;
const gchar *charset_str;
+ GTask *task;
/* NOTE: we already notified that CDMA-only modems couldn't load supported
* charsets, so we'll never get here in such a case */
@@ -2894,22 +3748,19 @@ modem_setup_charset (MMIfaceModem *self,
/* Build charset string to use */
charset_str = mm_modem_charset_to_string (charset);
if (!charset_str) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled character set 0x%X",
- charset);
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ modem_setup_charset,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled character set 0x%X",
+ charset);
return;
}
/* Setup context, including commands to try */
ctx = g_new0 (SetupCharsetContext, 1);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_setup_charset);
ctx->charset = charset;
/* First try, with quotes */
ctx->charset_commands[0].command = g_strdup_printf ("+CSCS=\"%s\"", charset_str);
@@ -2925,14 +3776,17 @@ modem_setup_charset (MMIfaceModem *self,
ctx->charset_commands[1].allow_cached = FALSE;
ctx->charset_commands[1].response_processor = mm_base_modem_response_processor_no_result;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)setup_charset_context_free);
+
/* Launch sequence */
mm_base_modem_at_sequence (
MM_BASE_MODEM (self),
- ctx->charset_commands,
+ (const MMBaseModemAtCommand *)ctx->charset_commands,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
(GAsyncReadyCallback)charset_change_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -2943,38 +3797,39 @@ modem_load_supported_charsets_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_CHARSET_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_CHARSET_UNKNOWN;
+ }
+ return (MMModemCharset)value;
}
static void
cscs_format_check_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MMModemCharset charsets = MM_MODEM_CHARSET_UNKNOWN;
const gchar *response;
GError *error = NULL;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (self, res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else if (!mm_3gpp_parse_cscs_test_response (response, &charsets))
- g_simple_async_result_set_error (
- simple,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
- "Failed to parse the supported character "
- "sets response");
+ "Failed to parse the supported character sets response");
else
- g_simple_async_result_set_op_res_gpointer (simple,
- GUINT_TO_POINTER (charsets),
- NULL);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_int (task, charsets);
+
+ g_object_unref (task);
}
static void
@@ -2982,21 +3837,15 @@ modem_load_supported_charsets (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_load_supported_charsets);
+ task = g_task_new (self, NULL, callback, user_data);
/* CDMA-only modems don't need this */
if (mm_iface_modem_is_cdma_only (self)) {
- mm_dbg ("Skipping supported charset loading in CDMA-only modem...");
- g_simple_async_result_set_op_res_gpointer (result,
- GUINT_TO_POINTER (MM_MODEM_CHARSET_UNKNOWN),
- NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ mm_obj_dbg (self, "skipping supported charset loading in CDMA-only modem...");
+ g_task_return_int (task, MM_MODEM_CHARSET_UNKNOWN);
+ g_object_unref (task);
return;
}
@@ -3005,19 +3854,126 @@ modem_load_supported_charsets (MMIfaceModem *self,
3,
TRUE,
(GAsyncReadyCallback)cscs_format_check_ready,
- result);
+ task);
}
/*****************************************************************************/
/* configuring flow control (Modem interface) */
static gboolean
-modem_setup_flow_control_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_setup_flow_control_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- /* Completely ignore errors */
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ifc_test_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModem *self;
+ GError *error = NULL;
+ const gchar *response;
+ const gchar *cmd;
+ MMFlowControl flow_control_supported;
+ MMFlowControl flow_control_selected = MM_FLOW_CONTROL_UNKNOWN;
+ MMFlowControl flow_control_requested;
+ gchar *flow_control_supported_str = NULL;
+ gchar *flow_control_selected_str = NULL;
+ MMPortSerialAt *port;
+
+ self = MM_BROADBAND_MODEM (_self);
+
+ /* Completely ignore errors in AT+IFC=? */
+ response = mm_base_modem_at_command_finish (_self, res, &error);
+ if (!response)
+ goto out;
+
+ /* Parse response */
+ flow_control_supported = mm_parse_ifc_test_response (response, self, &error);
+ if (flow_control_supported == MM_FLOW_CONTROL_UNKNOWN)
+ goto out;
+ flow_control_supported_str = mm_flow_control_build_string_from_mask (flow_control_supported);
+
+ port = mm_base_modem_peek_best_at_port (_self, &error);
+ if (!port)
+ goto out;
+
+ flow_control_requested = mm_port_serial_get_flow_control (MM_PORT_SERIAL (port));
+ if (flow_control_requested != MM_FLOW_CONTROL_UNKNOWN) {
+ gchar *flow_control_requested_str;
+
+ flow_control_requested_str = mm_flow_control_build_string_from_mask (flow_control_requested);
+
+ /* If flow control settings requested via udev tag are not supported by
+ * the modem, we trigger a fatal error */
+ if (!(flow_control_supported & flow_control_requested)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Explicitly requested flow control settings (%s) are not supported by the device (%s)",
+ flow_control_requested_str, flow_control_supported_str);
+ g_object_unref (task);
+ g_free (flow_control_requested_str);
+ g_free (flow_control_supported_str);
+ return;
+ }
+
+ mm_obj_dbg (self, "flow control settings explicitly requested: %s", flow_control_requested_str);
+ flow_control_selected = flow_control_requested;
+ flow_control_selected_str = flow_control_requested_str;
+ } else {
+ /* If flow control is not set explicitly by udev tags,
+ * we prefer the methods in this order:
+ * RTS/CTS
+ * XON/XOFF
+ * None.
+ */
+ if (flow_control_supported & MM_FLOW_CONTROL_RTS_CTS)
+ flow_control_selected = MM_FLOW_CONTROL_RTS_CTS;
+ else if (flow_control_supported & MM_FLOW_CONTROL_XON_XOFF)
+ flow_control_selected = MM_FLOW_CONTROL_XON_XOFF;
+ else if (flow_control_supported & MM_FLOW_CONTROL_NONE)
+ flow_control_selected = MM_FLOW_CONTROL_NONE;
+ else
+ g_assert_not_reached ();
+ flow_control_selected_str = mm_flow_control_build_string_from_mask (flow_control_selected);
+ mm_obj_dbg (self, "flow control settings automatically selected: %s", flow_control_selected_str);
+ }
+
+ /* Select flow control for all connections */
+ self->priv->flow_control = flow_control_selected;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FLOW_CONTROL]);
+
+ /* Set flow control settings and ignore result */
+ switch (flow_control_selected) {
+ case MM_FLOW_CONTROL_RTS_CTS:
+ cmd = "+IFC=2,2";
+ break;
+ case MM_FLOW_CONTROL_XON_XOFF:
+ cmd = "+IFC=1,1";
+ break;
+ case MM_FLOW_CONTROL_NONE:
+ cmd = "+IFC=0,0";
+ break;
+ case MM_FLOW_CONTROL_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ mm_base_modem_at_command (_self, cmd, 3, FALSE, NULL, NULL);
+
+out:
+ g_free (flow_control_supported_str);
+ g_free (flow_control_selected_str);
+
+ /* Ignore errors */
+ if (error) {
+ mm_obj_dbg (self, "couldn't load supported flow control methods: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -3025,117 +3981,80 @@ modem_setup_flow_control (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
- /* By default, try to set XOFF/XON flow control */
+ /* Query supported flow control methods */
mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+IFC=1,1",
+ "+IFC=?",
3,
- FALSE,
- NULL,
- NULL);
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_setup_flow_control);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ TRUE,
+ (GAsyncReadyCallback)ifc_test_ready,
+ task);
}
/*****************************************************************************/
/* Power state loading (Modem interface) */
static MMModemPowerState
-load_power_state_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+modem_load_power_state_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_POWER_STATE_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemPowerState)GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_POWER_STATE_UNKNOWN;
+ }
+ return (MMModemPowerState)value;
}
static void
cfun_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *result;
- guint state;
+ MMModemPowerState state;
GError *error = NULL;
- result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!result) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- return;
- }
-
- /* Parse power state reply */
- result = mm_strip_tag (result, "+CFUN:");
- if (!mm_get_uint_from_str (result, &state)) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse +CFUN? response '%s'",
- result);
- } else {
- switch (state) {
- case 0:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_OFF), NULL);
- break;
- case 1:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_ON), NULL);
- break;
- case 4:
- g_simple_async_result_set_op_res_gpointer (simple, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_LOW), NULL);
- break;
- default:
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled power state: '%u'",
- state);
- break;
- }
- }
+ result = mm_base_modem_at_command_finish (self, res, &error);
+ if (!result || !mm_3gpp_parse_cfun_query_generic_response (result, &state, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, state);;
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-load_power_state (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_load_power_state (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_power_state);
+ task = g_task_new (self, NULL, callback, user_data);
/* CDMA-only modems don't need this */
if (mm_iface_modem_is_cdma_only (self)) {
- mm_dbg ("Assuming full power state in CDMA-only modem...");
- g_simple_async_result_set_op_res_gpointer (result, GUINT_TO_POINTER (MM_MODEM_POWER_STATE_ON), NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ mm_obj_dbg (self, "assuming full power state in CDMA-only modem...");
+ g_task_return_int (task, MM_MODEM_POWER_STATE_ON);
+ g_object_unref (task);
return;
}
- mm_dbg ("loading power state...");
+ mm_obj_dbg (self, "loading power state...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CFUN?",
3,
FALSE,
(GAsyncReadyCallback)cfun_query_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -3157,11 +4076,11 @@ modem_power_up (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
/* CDMA-only modems don't need this */
if (mm_iface_modem_is_cdma_only (self))
- mm_dbg ("Skipping Power-up in CDMA-only modem...");
+ mm_obj_dbg (self, "skipping power-up in CDMA-only modem...");
else
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CFUN=1",
@@ -3170,13 +4089,169 @@ modem_power_up (MMIfaceModem *self,
NULL,
NULL);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_power_up);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Reprobing the modem if the SIM changed across a power-off or power-down */
+
+typedef struct {
+ MMBaseSim *sim;
+ guint retries;
+} SimSwapContext;
+
+static gboolean load_sim_identifier (GTask *task);
+
+static void
+sim_swap_context_free (SimSwapContext *ctx)
+{
+ g_clear_object (&ctx->sim);
+ g_slice_free (SimSwapContext, ctx);
+}
+
+static gboolean
+modem_check_for_sim_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+load_sim_identifier_ready (MMBaseSim *sim,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModem *self;
+ SimSwapContext *ctx;
+ const gchar *cached_simid;
+ gchar *current_simid;
+ GError *error = NULL;
+
+ self = MM_BROADBAND_MODEM (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+ cached_simid = mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (sim));
+ current_simid = MM_BASE_SIM_GET_CLASS (sim)->load_sim_identifier_finish (sim, res, &error);
+
+ if (error) {
+ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ if (ctx->retries > 0) {
+ mm_obj_warn (self, "could not load SIM identifier: %s (%d retries left)",
+ error->message, ctx->retries);
+ --ctx->retries;
+ g_clear_error (&error);
+ g_timeout_add_seconds (1, (GSourceFunc) load_sim_identifier, task);
+ return;
+ }
+
+ mm_obj_warn (self, "could not load SIM identifier: %s", error->message);
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ if (g_strcmp0 (current_simid, cached_simid) != 0) {
+ mm_obj_info (self, "sim identifier has changed: %s -> %s - possible SIM swap",
+ cached_simid, current_simid);
+ mm_broadband_modem_sim_hot_swap_detected (self);
+ }
+
+ g_task_return_boolean (task, TRUE);
+
+out:
+ g_free (current_simid);
+ g_object_unref (task);
+}
+
+static gboolean
+load_sim_identifier (GTask *task)
+{
+ SimSwapContext *ctx = g_task_get_task_data (task);
+
+ MM_BASE_SIM_GET_CLASS (ctx->sim)->load_sim_identifier (
+ ctx->sim,
+ (GAsyncReadyCallback)load_sim_identifier_ready,
+ task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+modem_check_for_sim_swap (MMIfaceModem *self,
+ const gchar *iccid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ SimSwapContext *ctx;
+
+ mm_obj_dbg (self, "checking if SIM was swapped...");
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (SimSwapContext);
+ ctx->retries = 3;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sim_swap_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM, &ctx->sim,
+ NULL);
+ if (!ctx->sim) {
+ MMModemState modem_state;
+
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+
+ if (modem_state == MM_MODEM_STATE_FAILED) {
+ mm_obj_info (self, "new SIM detected, handle as SIM hot-swap");
+ mm_broadband_modem_sim_hot_swap_detected (MM_BROADBAND_MODEM (self));
+ g_task_return_boolean (task, TRUE);
+ } else {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "could not acquire sim object");
+ }
+ g_object_unref (task);
+ return;
+ }
+
+ /* We may or may not get the new SIM identifier (iccid). In case
+ * we've got it, the load_sim_identifier phase can be skipped. */
+ if (iccid) {
+ const gchar *cached_simid;
+
+ cached_simid = mm_gdbus_sim_get_sim_identifier (MM_GDBUS_SIM (ctx->sim));
+ if (!cached_simid || g_strcmp0 (iccid, cached_simid) != 0) {
+ mm_obj_info (self, "detected ICCID change (%s -> %s), handle as SIM hot-swap",
+ cached_simid ? cached_simid : "<none>",
+ iccid);
+ mm_broadband_modem_sim_hot_swap_detected (MM_BROADBAND_MODEM (self));
+ } else
+ mm_obj_dbg (self, "ICCID not changed");
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_BASE_SIM_GET_CLASS (ctx->sim)->load_sim_identifier ||
+ !MM_BASE_SIM_GET_CLASS (ctx->sim)->load_sim_identifier_finish) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "sim identifier could not be loaded");
+ g_object_unref (task);
+ return;
+ }
+
+ load_sim_identifier (task);
}
/*****************************************************************************/
@@ -3223,7 +4298,7 @@ modem_3gpp_load_imei_finish (MMIfaceModem3gpp *self,
result = mm_strip_tag (result, "+CGSN:");
mm_parse_gsn (result, &imei, NULL, NULL);
- mm_dbg ("loaded IMEI: %s", imei);
+ mm_obj_dbg (self, "loaded IMEI: %s", imei);
return imei;
}
@@ -3232,7 +4307,7 @@ modem_3gpp_load_imei (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading IMEI...");
+ mm_obj_dbg (self, "loading IMEI...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CGSN",
3,
@@ -3245,44 +4320,40 @@ modem_3gpp_load_imei (MMIfaceModem3gpp *self,
/* Facility locks status loading (3GPP interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
guint current;
MMModem3gppFacility facilities;
MMModem3gppFacility locks;
} LoadEnabledFacilityLocksContext;
-static void get_next_facility_lock_status (LoadEnabledFacilityLocksContext *ctx);
-
-static void
-load_enabled_facility_locks_context_complete_and_free (LoadEnabledFacilityLocksContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
+static void get_next_facility_lock_status (GTask *task);
static MMModem3gppFacility
modem_3gpp_load_enabled_facility_locks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_3GPP_FACILITY_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return ((MMModem3gppFacility) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_3GPP_FACILITY_NONE;
+ }
+ return (MMModem3gppFacility)value;
}
static void
clck_single_query_ready (MMBaseModem *self,
GAsyncResult *res,
- LoadEnabledFacilityLocksContext *ctx)
+ GTask *task)
{
+ LoadEnabledFacilityLocksContext *ctx;
const gchar *response;
gboolean enabled = FALSE;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (self, res, NULL);
if (response &&
mm_3gpp_parse_clck_write_response (response, &enabled) &&
@@ -3295,16 +4366,21 @@ clck_single_query_ready (MMBaseModem *self,
/* And go on with the next one */
ctx->current++;
- get_next_facility_lock_status (ctx);
+ get_next_facility_lock_status (task);
}
static void
-get_next_facility_lock_status (LoadEnabledFacilityLocksContext *ctx)
+get_next_facility_lock_status (GTask *task)
{
+ MMBroadbandModem *self;
+ LoadEnabledFacilityLocksContext *ctx;
guint i;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
for (i = ctx->current; i < sizeof (MMModem3gppFacility) * 8; i++) {
- guint32 facility = 1 << i;
+ guint32 facility = 1u << i;
/* Found the next one to query! */
if (ctx->facilities & facility) {
@@ -3316,46 +4392,47 @@ get_next_facility_lock_status (LoadEnabledFacilityLocksContext *ctx)
/* Query current */
cmd = g_strdup_printf ("+CLCK=\"%s\",2",
mm_3gpp_facility_to_acronym (facility));
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
3,
FALSE,
(GAsyncReadyCallback)clck_single_query_ready,
- ctx);
+ task);
g_free (cmd);
return;
}
}
/* No more facilities to query, all done */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (ctx->locks),
- NULL);
- load_enabled_facility_locks_context_complete_and_free (ctx);
+ g_task_return_int (task, ctx->locks);
+ g_object_unref (task);
}
static void
clck_test_ready (MMBaseModem *self,
GAsyncResult *res,
- LoadEnabledFacilityLocksContext *ctx)
+ GTask *task)
{
+ LoadEnabledFacilityLocksContext *ctx;
const gchar *response;
GError *error = NULL;
response = mm_base_modem_at_command_finish (self, res, &error);
if (!response) {
- g_simple_async_result_take_error (ctx->result, error);
- load_enabled_facility_locks_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
if (!mm_3gpp_parse_clck_test_response (response, &ctx->facilities)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse list of available lock facilities: '%s'",
- response);
- load_enabled_facility_locks_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse list of available lock facilities: '%s'",
+ response);
+ g_object_unref (task);
return;
}
@@ -3364,14 +4441,14 @@ clck_test_ready (MMBaseModem *self,
gchar *str;
str = mm_modem_3gpp_facility_build_string_from_mask (MM_BROADBAND_MODEM (self)->priv->modem_3gpp_ignored_facility_locks);
- mm_dbg ("Ignoring facility locks: '%s'", str);
+ mm_obj_dbg (self, "ignoring facility locks: '%s'", str);
g_free (str);
ctx->facilities &= ~MM_BROADBAND_MODEM (self)->priv->modem_3gpp_ignored_facility_locks;
}
/* Go on... */
- get_next_facility_lock_status (ctx);
+ get_next_facility_lock_status (task);
}
static void
@@ -3380,24 +4457,23 @@ modem_3gpp_load_enabled_facility_locks (MMIfaceModem3gpp *self,
gpointer user_data)
{
LoadEnabledFacilityLocksContext *ctx;
+ GTask *task;
ctx = g_new (LoadEnabledFacilityLocksContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_enabled_facility_locks);
ctx->facilities = MM_MODEM_3GPP_FACILITY_NONE;
ctx->locks = MM_MODEM_3GPP_FACILITY_NONE;
ctx->current = 0;
- mm_dbg ("loading enabled facility locks...");
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ mm_obj_dbg (self, "loading enabled facility locks...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CLCK=?",
3,
TRUE,
(GAsyncReadyCallback)clck_test_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -3409,16 +4485,23 @@ modem_3gpp_load_operator_code_finish (MMIfaceModem3gpp *self,
GError **error)
{
const gchar *result;
- gchar *operator_code;
+ gchar *operator_code = NULL;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (!result)
return NULL;
- operator_code = mm_3gpp_parse_operator (result, MM_MODEM_CHARSET_UNKNOWN);
- if (operator_code)
- mm_dbg ("loaded Operator Code: %s", operator_code);
+ if (!mm_3gpp_parse_cops_read_response (result,
+ NULL, /* mode */
+ NULL, /* format */
+ &operator_code,
+ NULL, /* act */
+ error))
+ return NULL;
+ mm_3gpp_normalize_operator (&operator_code, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self);
+ if (operator_code)
+ mm_obj_dbg (self, "loaded Operator Code: %s", operator_code);
return operator_code;
}
@@ -3427,13 +4510,9 @@ modem_3gpp_load_operator_code (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading Operator Code...");
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+COPS=3,2;+COPS?",
- 3,
- FALSE,
- callback,
- user_data);
+ mm_obj_dbg (self, "loading Operator Code...");
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=3,2", 3, FALSE, NULL, NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 3, FALSE, callback, user_data);
}
/*****************************************************************************/
@@ -3445,16 +4524,23 @@ modem_3gpp_load_operator_name_finish (MMIfaceModem3gpp *self,
GError **error)
{
const gchar *result;
- gchar *operator_name;
+ gchar *operator_name = NULL;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
if (!result)
return NULL;
- operator_name = mm_3gpp_parse_operator (result, MM_BROADBAND_MODEM (self)->priv->modem_current_charset);
- if (operator_name)
- mm_dbg ("loaded Operator Name: %s", operator_name);
+ if (!mm_3gpp_parse_cops_read_response (result,
+ NULL, /* mode */
+ NULL, /* format */
+ &operator_name,
+ NULL, /* act */
+ error))
+ return NULL;
+ mm_3gpp_normalize_operator (&operator_name, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self);
+ if (operator_name)
+ mm_obj_dbg (self, "loaded Operator Name: %s", operator_name);
return operator_name;
}
@@ -3463,9 +4549,37 @@ modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading Operator Name...");
+ mm_obj_dbg (self, "loading Operator Name...");
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS=3,0", 3, FALSE, NULL, NULL);
+ mm_base_modem_at_command (MM_BASE_MODEM (self), "+COPS?", 3, FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* UE mode of operation for EPS loading (3GPP interface) */
+
+static MMModem3gppEpsUeModeOperation
+modem_3gpp_load_eps_ue_mode_operation_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ MMModem3gppEpsUeModeOperation mode;
+ const gchar *result;
+
+ result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!result || !mm_3gpp_parse_cemode_query_response (result, &mode, error))
+ return MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN;
+
+ return mode;
+}
+
+static void
+modem_3gpp_load_eps_ue_mode_operation (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_obj_dbg (self, "loading UE mode of operation for EPS...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+COPS=3,0;+COPS?",
+ "+CEMODE?",
3,
FALSE,
callback,
@@ -3473,39 +4587,61 @@ modem_3gpp_load_operator_name (MMIfaceModem3gpp *self,
}
/*****************************************************************************/
-/* Subscription State Loading (3GPP interface) */
+/* UE mode of operation for EPS settin (3GPP interface) */
-static MMModem3gppSubscriptionState
-modem_3gpp_load_subscription_state_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+static gboolean
+modem_3gpp_set_eps_ue_mode_operation_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)));
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
}
static void
-modem_3gpp_load_subscription_state (MMIfaceModem3gpp *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_set_eps_ue_mode_operation (MMIfaceModem3gpp *self,
+ MMModem3gppEpsUeModeOperation mode,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ gchar *cmd;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_load_subscription_state);
+ mm_obj_dbg (self, "updating UE mode of operation for EPS...");
+ cmd = mm_3gpp_build_cemode_set_request (mode);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ callback,
+ user_data);
+ g_free (cmd);
+}
- g_simple_async_result_set_op_res_gpointer (
- result,
- GUINT_TO_POINTER (MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN),
- NULL);
+/*****************************************************************************/
+/* Set packet service state (3GPP interface) */
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+static gboolean
+modem_3gpp_set_packet_service_state_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_3gpp_set_packet_service_state (MMIfaceModem3gpp *self,
+ MMModem3gppPacketServiceState state,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autofree gchar *cmd = NULL;
+
+ cmd = mm_3gpp_build_cgatt_set_request (state);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 10,
+ FALSE,
+ callback,
+ user_data);
}
/*****************************************************************************/
@@ -3516,7 +4652,7 @@ modem_3gpp_setup_unsolicited_registration_events_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -3525,33 +4661,51 @@ registration_state_changed (MMPortSerialAt *port,
MMBroadbandModem *self)
{
MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- gulong lac = 0, cell_id = 0;
+ gulong lac = 0, tac = 0, cell_id = 0;
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
gboolean cgreg = FALSE;
gboolean cereg = FALSE;
+ gboolean c5greg = FALSE;
GError *error = NULL;
if (!mm_3gpp_parse_creg_response (match_info,
+ self,
&state,
&lac,
&cell_id,
&act,
&cgreg,
&cereg,
+ &c5greg,
&error)) {
- mm_warn ("error parsing unsolicited registration: %s",
- error && error->message ? error->message : "(unknown)");
+ mm_obj_warn (self, "error parsing unsolicited registration: %s",
+ error && error->message ? error->message : "(unknown)");
g_clear_error (&error);
return;
}
- /* Report new registration state */
+ /* Report new registration state and fix LAC/TAC.
+ * According to 3GPP TS 27.007:
+ * - If CREG reports <AcT> 7 (LTE) then the <lac> field contains TAC
+ * - CEREG/C5GREG always reports TAC
+ */
if (cgreg)
mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), state);
- else if (cereg)
+ else if (cereg) {
+ tac = lac;
+ lac = 0;
mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), state);
- else
+ } else if (c5greg) {
+ tac = lac;
+ lac = 0;
+ mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), state);
+ } else {
+ if (act == MM_MODEM_ACCESS_TECHNOLOGY_LTE) {
+ tac = lac;
+ lac = 0;
+ }
mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), state);
+ }
/* Only update access technologies from CREG/CGREG response if the modem
* doesn't have custom commands for access technology loading, otherwise
@@ -3562,7 +4716,7 @@ registration_state_changed (MMPortSerialAt *port,
MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies == NULL)
mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act);
- mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, cell_id);
+ mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cell_id);
}
static void
@@ -3570,16 +4724,11 @@ modem_3gpp_setup_unsolicited_registration_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MMPortSerialAt *ports[2];
GPtrArray *array;
guint i;
guint j;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_setup_unsolicited_registration_events);
+ GTask *task;
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
@@ -3590,8 +4739,8 @@ modem_3gpp_setup_unsolicited_registration_events (MMIfaceModem3gpp *self,
if (!ports[i])
continue;
- mm_dbg ("(%s) setting up 3GPP unsolicited registration messages handlers",
- mm_port_get_device (MM_PORT (ports[i])));
+ mm_obj_dbg (self, "setting up 3GPP unsolicited registration messages handlers in %s",
+ mm_port_get_device (MM_PORT (ports[i])));
for (j = 0; j < array->len; j++) {
mm_port_serial_at_add_unsolicited_msg_handler (
MM_PORT_SERIAL_AT (ports[i]),
@@ -3603,9 +4752,9 @@ modem_3gpp_setup_unsolicited_registration_events (MMIfaceModem3gpp *self,
}
mm_3gpp_creg_regex_destroy (array);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -3616,7 +4765,7 @@ modem_3gpp_cleanup_unsolicited_registration_events_finish (MMIfaceModem3gpp *sel
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -3624,16 +4773,11 @@ modem_3gpp_cleanup_unsolicited_registration_events (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MMPortSerialAt *ports[2];
GPtrArray *array;
guint i;
guint j;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_cleanup_unsolicited_registration_events);
+ GTask *task;
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
@@ -3644,9 +4788,8 @@ modem_3gpp_cleanup_unsolicited_registration_events (MMIfaceModem3gpp *self,
if (!ports[i])
continue;
- mm_dbg ("(%s) cleaning up unsolicited registration messages handlers",
- mm_port_get_device (MM_PORT (ports[i])));
-
+ mm_obj_dbg (self, "cleaning up unsolicited registration messages handlers in %s",
+ mm_port_get_device (MM_PORT (ports[i])));
for (j = 0; j < array->len; j++) {
mm_port_serial_at_add_unsolicited_msg_handler (
MM_PORT_SERIAL_AT (ports[i]),
@@ -3658,9 +4801,9 @@ modem_3gpp_cleanup_unsolicited_registration_events (MMIfaceModem3gpp *self,
}
mm_3gpp_creg_regex_destroy (array);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -3677,7 +4820,7 @@ modem_3gpp_scan_networks_finish (MMIfaceModem3gpp *self,
if (!result)
return NULL;
- return mm_3gpp_parse_cops_test_response (result, error);
+ return mm_3gpp_parse_cops_test_response (result, MM_BROADBAND_MODEM (self)->priv->modem_current_charset, self, error);
}
static void
@@ -3687,7 +4830,7 @@ modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
{
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+COPS=?",
- 120,
+ 300,
FALSE,
callback,
user_data);
@@ -3697,34 +4840,109 @@ modem_3gpp_scan_networks (MMIfaceModem3gpp *self,
/* Register in network (3GPP interface) */
static gboolean
-modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+cops_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- gchar *command;
+ GError *error = NULL;
- /* If the user sent a specific network to use, lock it in. */
- if (operator_id)
- command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id);
- /* If no specific network was given, and the modem is not registered and not
- * searching, kick it to search for a network. Also do auto registration if
- * the modem had been set to manual registration last time but now is not.
- */
+ if (!mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error))
+ g_task_return_error (task, error);
else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+cops_ascii_set_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ g_autoptr(GError) error = NULL;
+
+ if (!mm_base_modem_at_command_full_finish (_self, res, &error)) {
+ /* If it failed with an unsupported error, retry with current modem charset */
+ if (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED)) {
+ g_autoptr(GError) enc_error = NULL;
+ g_autofree gchar *operator_id_enc = NULL;
+ gchar *operator_id;
+
+ /* try to encode to current charset */
+ operator_id = g_task_get_task_data (task);
+ operator_id_enc = mm_modem_charset_str_from_utf8 (operator_id, self->priv->modem_current_charset, FALSE, &enc_error);
+ if (!operator_id_enc) {
+ mm_obj_dbg (self, "couldn't convert operator id to current charset: %s", enc_error->message);
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ /* retry only if encoded string is different to the non-encoded one */
+ if (g_strcmp0 (operator_id, operator_id_enc) != 0) {
+ g_autofree gchar *command = NULL;
+
+ command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id_enc);
+ mm_base_modem_at_command_full (_self,
+ mm_base_modem_peek_best_at_port (_self, NULL),
+ command,
+ 120,
+ FALSE,
+ FALSE, /* raw */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)cops_set_ready,
+ task);
+ return;
+ }
+ }
+ g_task_return_error (task, g_steal_pointer (&error));
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gchar *command;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ /* Trigger automatic network registration if no explicit operator id given */
+ if (!operator_id) {
/* Note that '+COPS=0,,' (same but with commas) won't work in some Nokia
* phones */
- command = g_strdup ("+COPS=0");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
+ "+COPS=0",
+ 120,
+ FALSE,
+ FALSE, /* raw */
+ cancellable,
+ (GAsyncReadyCallback)cops_set_ready,
+ task);
+ return;
+ }
+ /* Store operator id in context, in case we need to retry with the current
+ * modem charset */
+ g_task_set_task_data (task, g_strdup (operator_id), g_free);
+
+ /* Use the operator id given in ASCII initially */
+ command = g_strdup_printf ("+COPS=1,2,\"%s\"", operator_id);
mm_base_modem_at_command_full (MM_BASE_MODEM (self),
mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), NULL),
command,
@@ -3732,8 +4950,8 @@ modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
FALSE,
FALSE, /* raw */
cancellable,
- callback,
- user_data);
+ (GAsyncReadyCallback)cops_ascii_set_ready,
+ task);
g_free (command);
}
@@ -3741,80 +4959,90 @@ modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
/* Registration checks (3GPP interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- gboolean cs_supported;
- gboolean ps_supported;
- gboolean eps_supported;
+ gboolean is_cs_supported;
+ gboolean is_ps_supported;
+ gboolean is_eps_supported;
+ gboolean is_5gs_supported;
gboolean run_cs;
gboolean run_ps;
gboolean run_eps;
+ gboolean run_5gs;
gboolean running_cs;
gboolean running_ps;
gboolean running_eps;
- GError *cs_error;
- GError *ps_error;
- GError *eps_error;
+ gboolean running_5gs;
+ GError *error_cs;
+ GError *error_ps;
+ GError *error_eps;
+ GError *error_5gs;
} RunRegistrationChecksContext;
static void
-run_registration_checks_context_complete_and_free (RunRegistrationChecksContext *ctx)
+run_registration_checks_context_free (RunRegistrationChecksContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->cs_error)
- g_error_free (ctx->cs_error);
- if (ctx->ps_error)
- g_error_free (ctx->ps_error);
- if (ctx->eps_error)
- g_error_free (ctx->eps_error);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
+ g_clear_error (&ctx->error_cs);
+ g_clear_error (&ctx->error_ps);
+ g_clear_error (&ctx->error_eps);
+ g_clear_error (&ctx->error_5gs);
g_free (ctx);
}
static gboolean
-modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void run_registration_checks_context_step (RunRegistrationChecksContext *ctx);
+static void run_registration_checks_context_step (GTask *task);
+
+static void
+run_registration_checks_context_set_error (RunRegistrationChecksContext *ctx,
+ GError *error)
+{
+ g_assert (error != NULL);
+ if (ctx->running_cs)
+ ctx->error_cs = error;
+ else if (ctx->running_ps)
+ ctx->error_ps = error;
+ else if (ctx->running_eps)
+ ctx->error_eps = error;
+ else if (ctx->running_5gs)
+ ctx->error_5gs = error;
+ else
+ g_assert_not_reached ();
+}
static void
registration_status_check_ready (MMBroadbandModem *self,
- GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
const gchar *response;
GError *error = NULL;
- GMatchInfo *match_info;
+ GMatchInfo *match_info = NULL;
guint i;
gboolean parsed;
- gboolean cgreg;
- gboolean cereg;
- MMModem3gppRegistrationState state;
- MMModemAccessTechnology act;
- gulong lac;
- gulong cid;
+ gboolean cgreg = FALSE;
+ gboolean cereg = FALSE;
+ gboolean c5greg = FALSE;
+ MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ gulong lac = 0;
+ gulong tac = 0;
+ gulong cid = 0;
+
+ ctx = g_task_get_task_data (task);
/* Only one must be running */
- g_assert ((ctx->running_cs ? 1 : 0) +
- (ctx->running_ps ? 1 : 0) +
- (ctx->running_eps ? 1 : 0) == 1);
+ g_assert ((ctx->running_cs + ctx->running_ps + ctx->running_eps + ctx->running_5gs) == 1);
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (!response) {
- g_assert (error != NULL);
- if (ctx->running_cs)
- ctx->cs_error = error;
- else if (ctx->running_ps)
- ctx->ps_error = error;
- else
- ctx->eps_error = error;
-
- run_registration_checks_context_step (ctx);
+ run_registration_checks_context_set_error (ctx, error);
+ run_registration_checks_context_step (task);
return;
}
@@ -3823,7 +5051,7 @@ registration_status_check_ready (MMBroadbandModem *self,
*/
if (!response[0]) {
/* Done */
- run_registration_checks_context_step (ctx);
+ run_registration_checks_context_step (task);
return;
}
@@ -3831,8 +5059,7 @@ registration_status_check_ready (MMBroadbandModem *self,
for (i = 0;
i < self->priv->modem_3gpp_registration_regex->len;
i++) {
- if (g_regex_match ((GRegex *)g_ptr_array_index (
- self->priv->modem_3gpp_registration_regex, i),
+ if (g_regex_match ((GRegex *)g_ptr_array_index (self->priv->modem_3gpp_registration_regex, i),
response,
0,
&match_info))
@@ -3846,30 +5073,20 @@ registration_status_check_ready (MMBroadbandModem *self,
MM_CORE_ERROR_FAILED,
"Unknown registration status response: '%s'",
response);
- if (ctx->running_cs)
- ctx->cs_error = error;
- else if (ctx->running_ps)
- ctx->ps_error = error;
- else
- ctx->eps_error = error;
-
- run_registration_checks_context_step (ctx);
+ run_registration_checks_context_set_error (ctx, error);
+ run_registration_checks_context_step (task);
return;
}
- cgreg = FALSE;
- cereg = FALSE;
- state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- lac = 0;
- cid = 0;
parsed = mm_3gpp_parse_creg_response (match_info,
+ self,
&state,
&lac,
&cid,
&act,
&cgreg,
&cereg,
+ &c5greg,
&error);
g_match_info_free (match_info);
@@ -3879,60 +5096,89 @@ registration_status_check_ready (MMBroadbandModem *self,
MM_CORE_ERROR_FAILED,
"Error parsing registration response: '%s'",
response);
- if (ctx->running_cs)
- ctx->cs_error = error;
- else if (ctx->running_ps)
- ctx->ps_error = error;
- else
- ctx->eps_error = error;
- run_registration_checks_context_step (ctx);
+ run_registration_checks_context_set_error (ctx, error);
+ run_registration_checks_context_step (task);
return;
}
- /* Report new registration state */
+ /* Report new registration state and fix LAC/TAC.
+ * According to 3GPP TS 27.007:
+ * - If CREG reports <AcT> 7 (LTE) then the <lac> field contains TAC
+ * - CEREG always reports TAC
+ */
if (cgreg) {
if (ctx->running_cs)
- mm_dbg ("Got PS registration state when checking CS registration state");
+ mm_obj_dbg (self, "got PS registration state when checking CS registration state");
else if (ctx->running_eps)
- mm_dbg ("Got PS registration state when checking EPS registration state");
+ mm_obj_dbg (self, "got PS registration state when checking EPS registration state");
+ else if (ctx->running_5gs)
+ mm_obj_dbg (self, "got PS registration state when checking 5GS registration state");
mm_iface_modem_3gpp_update_ps_registration_state (MM_IFACE_MODEM_3GPP (self), state);
} else if (cereg) {
+ tac = lac;
+ lac = 0;
if (ctx->running_cs)
- mm_dbg ("Got EPS registration state when checking CS registration state");
+ mm_obj_dbg (self, "got EPS registration state when checking CS registration state");
else if (ctx->running_ps)
- mm_dbg ("Got EPS registration state when checking PS registration state");
+ mm_obj_dbg (self, "got EPS registration state when checking PS registration state");
+ else if (ctx->running_5gs)
+ mm_obj_dbg (self, "got EPS registration state when checking 5GS registration state");
mm_iface_modem_3gpp_update_eps_registration_state (MM_IFACE_MODEM_3GPP (self), state);
+ } else if (c5greg) {
+ tac = lac;
+ lac = 0;
+ if (ctx->running_cs)
+ mm_obj_dbg (self, "got 5GS registration state when checking CS registration state");
+ else if (ctx->running_ps)
+ mm_obj_dbg (self, "got 5GS registration state when checking PS registration state");
+ else if (ctx->running_eps)
+ mm_obj_dbg (self, "got 5GS registration state when checking EPS registration state");
+ mm_iface_modem_3gpp_update_5gs_registration_state (MM_IFACE_MODEM_3GPP (self), state);
} else {
+ if (act == MM_MODEM_ACCESS_TECHNOLOGY_LTE) {
+ tac = lac;
+ lac = 0;
+ }
if (ctx->running_ps)
- mm_dbg ("Got CS registration state when checking PS registration state");
+ mm_obj_dbg (self, "got CS registration state when checking PS registration state");
else if (ctx->running_eps)
- mm_dbg ("Got CS registration state when checking EPS registration state");
+ mm_obj_dbg (self, "got CS registration state when checking EPS registration state");
+ else if (ctx->running_5gs)
+ mm_obj_dbg (self, "got CS registration state when checking 5GS registration state");
mm_iface_modem_3gpp_update_cs_registration_state (MM_IFACE_MODEM_3GPP (self), state);
}
mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act);
- mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, cid);
+ mm_iface_modem_3gpp_update_location (MM_IFACE_MODEM_3GPP (self), lac, tac, cid);
- run_registration_checks_context_step (ctx);
+ run_registration_checks_context_step (task);
}
static void
-run_registration_checks_context_step (RunRegistrationChecksContext *ctx)
+run_registration_checks_context_step (GTask *task)
{
+ MMBroadbandModem *self;
+ RunRegistrationChecksContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
ctx->running_cs = FALSE;
ctx->running_ps = FALSE;
ctx->running_eps = FALSE;
+ ctx->running_5gs = FALSE;
if (ctx->run_cs) {
ctx->running_cs = TRUE;
ctx->run_cs = FALSE;
/* Check current CS-registration state. */
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CREG?",
10,
FALSE,
(GAsyncReadyCallback)registration_status_check_ready,
- ctx);
+ task);
return;
}
@@ -3940,12 +5186,12 @@ run_registration_checks_context_step (RunRegistrationChecksContext *ctx)
ctx->running_ps = TRUE;
ctx->run_ps = FALSE;
/* Check current PS-registration state. */
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CGREG?",
10,
FALSE,
(GAsyncReadyCallback)registration_status_check_ready,
- ctx);
+ task);
return;
}
@@ -3953,70 +5199,108 @@ run_registration_checks_context_step (RunRegistrationChecksContext *ctx)
ctx->running_eps = TRUE;
ctx->run_eps = FALSE;
/* Check current EPS-registration state. */
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CEREG?",
10,
FALSE,
(GAsyncReadyCallback)registration_status_check_ready,
- ctx);
+ task);
+ return;
+ }
+
+ if (ctx->run_5gs) {
+ ctx->running_5gs = TRUE;
+ ctx->run_5gs = FALSE;
+ /* Check current 5GS-registration state. */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+C5GREG?",
+ 10,
+ FALSE,
+ (GAsyncReadyCallback)registration_status_check_ready,
+ task);
return;
}
/* If all run checks returned errors we fail */
- if ((ctx->cs_supported || ctx->ps_supported || ctx->eps_supported) &&
- (!ctx->cs_supported || ctx->cs_error) &&
- (!ctx->ps_supported || ctx->ps_error) &&
- (!ctx->eps_supported || ctx->eps_error)) {
- /* Prefer the EPS, and then PS error if any */
- if (ctx->eps_error) {
- g_simple_async_result_set_from_error (ctx->result, ctx->eps_error);
- ctx->eps_error = NULL;
- } else if (ctx->ps_error) {
- g_simple_async_result_set_from_error (ctx->result, ctx->ps_error);
- ctx->ps_error = NULL;
- } else if (ctx->cs_error) {
- g_simple_async_result_set_from_error (ctx->result, ctx->cs_error);
- ctx->cs_error = NULL;
- } else
+ if ((ctx->is_cs_supported || ctx->is_ps_supported || ctx->is_eps_supported || ctx->is_5gs_supported) &&
+ (!ctx->is_cs_supported || ctx->error_cs) &&
+ (!ctx->is_ps_supported || ctx->error_ps) &&
+ (!ctx->is_eps_supported || ctx->error_eps) &&
+ (!ctx->is_5gs_supported || ctx->error_5gs)) {
+ /* When reporting errors, prefer the 5GS, then EPS, then PS, then CS */
+ if (ctx->error_5gs)
+ error = g_steal_pointer (&ctx->error_5gs);
+ else if (ctx->error_eps)
+ error = g_steal_pointer (&ctx->error_eps);
+ else if (ctx->error_ps)
+ error = g_steal_pointer (&ctx->error_ps);
+ else if (ctx->error_cs)
+ error = g_steal_pointer (&ctx->error_cs);
+ else
g_assert_not_reached ();
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ }
- run_registration_checks_context_complete_and_free (ctx);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
+ gboolean is_cs_supported,
+ gboolean is_ps_supported,
+ gboolean is_eps_supported,
+ gboolean is_5gs_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
RunRegistrationChecksContext *ctx;
+ GTask *task;
ctx = g_new0 (RunRegistrationChecksContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_run_registration_checks);
- ctx->cs_supported = cs_supported;
- ctx->ps_supported = ps_supported;
- ctx->eps_supported = eps_supported;
- ctx->run_cs = cs_supported;
- ctx->run_ps = ps_supported;
- ctx->run_eps = eps_supported;
+ ctx->is_cs_supported = is_cs_supported;
+ ctx->is_ps_supported = is_ps_supported;
+ ctx->is_eps_supported = is_eps_supported;
+ ctx->is_5gs_supported = is_5gs_supported;
+ ctx->run_cs = is_cs_supported;
+ ctx->run_ps = is_ps_supported;
+ ctx->run_eps = is_eps_supported;
+ ctx->run_5gs = is_5gs_supported;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)run_registration_checks_context_free);
+
+ run_registration_checks_context_step (task);
+}
+
+/*****************************************************************************/
+/* Create initial EPS bearer object */
+
+static MMBaseBearer *
+modem_3gpp_create_initial_eps_bearer (MMIfaceModem3gpp *self,
+ MMBearerProperties *config)
+{
+ MMBaseBearer *bearer;
- run_registration_checks_context_step (ctx);
+ /* NOTE: by default we create a bearer object that is CONNECTED but which doesn't
+ * have an associated data interface already set. This is so that upper layers don't
+ * attempt connection through this bearer object. */
+ bearer = g_object_new (MM_TYPE_BASE_BEARER,
+ MM_BASE_BEARER_MODEM, MM_BASE_MODEM (self),
+ MM_BASE_BEARER_CONFIG, config,
+ "bearer-type", MM_BEARER_TYPE_DEFAULT_ATTACH,
+ "connected", TRUE,
+ NULL);
+ mm_base_bearer_export (bearer);
+ return bearer;
}
/*****************************************************************************/
/* Enable/Disable unsolicited registration events (3GPP interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
gboolean enable; /* TRUE for enabling, FALSE for disabling */
gboolean run_cs;
gboolean run_ps;
@@ -4032,43 +5316,40 @@ typedef struct {
} UnsolicitedRegistrationEventsContext;
static void
-unsolicited_registration_events_context_complete_and_free (UnsolicitedRegistrationEventsContext *ctx)
+unsolicited_registration_events_context_free (UnsolicitedRegistrationEventsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->cs_error)
g_error_free (ctx->cs_error);
if (ctx->ps_error)
g_error_free (ctx->ps_error);
if (ctx->eps_error)
g_error_free (ctx->eps_error);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx);
}
-static UnsolicitedRegistrationEventsContext *
-unsolicited_registration_events_context_new (MMBroadbandModem *self,
- gboolean enable,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
- GAsyncReadyCallback callback,
- gpointer user_data)
+static GTask *
+unsolicited_registration_events_task_new (MMBroadbandModem *self,
+ gboolean enable,
+ gboolean cs_supported,
+ gboolean ps_supported,
+ gboolean eps_supported,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
UnsolicitedRegistrationEventsContext *ctx;
+ GTask *task;
ctx = g_new0 (UnsolicitedRegistrationEventsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- unsolicited_registration_events_context_new);
ctx->enable = enable;
ctx->run_cs = cs_supported;
ctx->run_ps = ps_supported;
ctx->run_eps = eps_supported;
- return ctx;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task,
+ ctx,
+ (GDestroyNotify)unsolicited_registration_events_context_free);
+ return task;
}
static gboolean
@@ -4076,26 +5357,30 @@ modem_3gpp_enable_disable_unsolicited_registration_events_finish (MMIfaceModem3g
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static gboolean
-parse_registration_setup_reply (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
+static MMBaseModemAtResponseProcessorResult
+parse_registration_setup_reply (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
const GError *error,
- GVariant **result,
- GError **result_error)
+ GVariant **result,
+ GError **result_error)
{
+ *result_error = NULL;
+
/* If error, try next command */
- if (error)
- return FALSE;
+ if (error) {
+ *result = NULL;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
+ }
/* Set COMMAND as result! */
*result = g_variant_new_string (command);
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
static const MMBaseModemAtCommand cs_registration_sequence[] = {
@@ -4140,17 +5425,20 @@ static const MMBaseModemAtCommand eps_unregistration_sequence[] = {
{ NULL }
};
-static void unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsContext *ctx);
+static void unsolicited_registration_events_context_step (GTask *task);
static void
unsolicited_registration_events_sequence_ready (MMBroadbandModem *self,
GAsyncResult *res,
- UnsolicitedRegistrationEventsContext *ctx)
+ GTask *task)
{
+ UnsolicitedRegistrationEventsContext *ctx;
GError *error = NULL;
GVariant *command;
MMPortSerialAt *secondary;
+ ctx = g_task_get_task_data (task);
+
/* Only one must be running */
g_assert ((ctx->running_cs ? 1 : 0) +
(ctx->running_ps ? 1 : 0) +
@@ -4163,9 +5451,9 @@ unsolicited_registration_events_sequence_ready (MMBroadbandModem *self,
mm_base_modem_at_command_full_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- mm_dbg ("%s unsolicited registration events in secondary port failed: '%s'",
- ctx->enable ? "Enabling" : "Disabling",
- error->message);
+ mm_obj_dbg (self, "%s unsolicited registration events in secondary port failed: %s",
+ ctx->enable ? "enabling" : "disabling",
+ error->message);
/* Keep errors reported */
if (ctx->running_cs && !ctx->cs_error)
ctx->cs_error = error;
@@ -4192,7 +5480,7 @@ unsolicited_registration_events_sequence_ready (MMBroadbandModem *self,
}
/* Done with primary and secondary, keep on */
- unsolicited_registration_events_context_step (ctx);
+ unsolicited_registration_events_context_step (task);
return;
}
@@ -4203,9 +5491,9 @@ unsolicited_registration_events_sequence_ready (MMBroadbandModem *self,
error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"AT sequence failed");
- mm_dbg ("%s unsolicited registration events in primary port failed: '%s'",
- ctx->enable ? "Enabling" : "Disabling",
- error->message);
+ mm_obj_dbg (self, "%s unsolicited registration events in primary port failed: %s",
+ ctx->enable ? "enabling" : "disabling",
+ error->message);
/* Keep errors reported */
if (ctx->running_cs)
ctx->cs_error = error;
@@ -4233,7 +5521,7 @@ unsolicited_registration_events_sequence_ready (MMBroadbandModem *self,
FALSE, /* raw */
NULL, /* cancellable */
(GAsyncReadyCallback)unsolicited_registration_events_sequence_ready,
- ctx);
+ task);
return;
}
@@ -4253,17 +5541,24 @@ unsolicited_registration_events_sequence_ready (MMBroadbandModem *self,
NULL, /* response processor context free */
NULL, /* cancellable */
(GAsyncReadyCallback)unsolicited_registration_events_sequence_ready,
- ctx);
+ task);
return;
}
/* We're done */
- unsolicited_registration_events_context_step (ctx);
+ unsolicited_registration_events_context_step (task);
}
static void
-unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsContext *ctx)
+unsolicited_registration_events_context_step (GTask *task)
{
+ MMBroadbandModem *self;
+ UnsolicitedRegistrationEventsContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
ctx->running_cs = FALSE;
ctx->running_ps = FALSE;
ctx->running_eps = FALSE;
@@ -4273,14 +5568,14 @@ unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsConte
ctx->running_cs = TRUE;
ctx->run_cs = FALSE;
mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (ctx->self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
ctx->enable ? cs_registration_sequence : cs_unregistration_sequence,
NULL, /* response processor context */
NULL, /* response processor context free */
NULL, /* cancellable */
(GAsyncReadyCallback)unsolicited_registration_events_sequence_ready,
- ctx);
+ task);
return;
}
@@ -4288,14 +5583,14 @@ unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsConte
ctx->running_ps = TRUE;
ctx->run_ps = FALSE;
mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (ctx->self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
ctx->enable ? ps_registration_sequence : ps_unregistration_sequence,
NULL, /* response processor context */
NULL, /* response processor context free */
NULL, /* cancellable */
(GAsyncReadyCallback)unsolicited_registration_events_sequence_ready,
- ctx);
+ task);
return;
}
@@ -4303,14 +5598,14 @@ unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsConte
ctx->running_eps = TRUE;
ctx->run_eps = FALSE;
mm_base_modem_at_sequence_full (
- MM_BASE_MODEM (ctx->self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)),
+ MM_BASE_MODEM (self),
+ mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
ctx->enable ? eps_registration_sequence : eps_unregistration_sequence,
NULL, /* response processor context */
NULL, /* response processor context free */
NULL, /* cancellable */
(GAsyncReadyCallback)unsolicited_registration_events_sequence_ready,
- ctx);
+ task);
return;
}
@@ -4318,17 +5613,21 @@ unsolicited_registration_events_context_step (UnsolicitedRegistrationEventsConte
* If we have any error reported, we'll propagate it. EPS errors take
* 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);
+ g_propagate_error (&error, ctx->eps_error);
ctx->eps_error = NULL;
} else if (ctx->ps_error) {
- g_simple_async_result_take_error (ctx->result, ctx->ps_error);
+ g_propagate_error (&error, ctx->ps_error);
ctx->ps_error = NULL;
} else if (ctx->cs_error) {
- g_simple_async_result_take_error (ctx->result, ctx->cs_error);
+ g_propagate_error (&error, ctx->cs_error);
ctx->cs_error = NULL;
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- unsolicited_registration_events_context_complete_and_free (ctx);
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -4340,13 +5639,13 @@ modem_3gpp_disable_unsolicited_registration_events (MMIfaceModem3gpp *self,
gpointer user_data)
{
unsolicited_registration_events_context_step (
- unsolicited_registration_events_context_new (MM_BROADBAND_MODEM (self),
- FALSE,
- cs_supported,
- ps_supported,
- eps_supported,
- callback,
- user_data));
+ unsolicited_registration_events_task_new (MM_BROADBAND_MODEM (self),
+ FALSE,
+ cs_supported,
+ ps_supported,
+ eps_supported,
+ callback,
+ user_data));
}
static void
@@ -4358,190 +5657,172 @@ modem_3gpp_enable_unsolicited_registration_events (MMIfaceModem3gpp *self,
gpointer user_data)
{
unsolicited_registration_events_context_step (
- unsolicited_registration_events_context_new (MM_BROADBAND_MODEM (self),
- TRUE,
- cs_supported,
- ps_supported,
- eps_supported,
- callback,
- user_data));
+ unsolicited_registration_events_task_new (MM_BROADBAND_MODEM (self),
+ TRUE,
+ cs_supported,
+ ps_supported,
+ eps_supported,
+ callback,
+ user_data));
}
/*****************************************************************************/
/* Cancel USSD (3GPP/USSD interface) */
static gboolean
-modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_ussd_cancel_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cancel_command_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (error)
- g_simple_async_result_take_error (simple, error);
- else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- /* Complete the pending action, if any */
+ /* Complete the pending action, regardless of the CUSD result */
if (self->priv->pending_ussd_action) {
- g_simple_async_result_set_error (self->priv->pending_ussd_action,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "USSD session was cancelled");
- g_simple_async_result_complete_in_idle (self->priv->pending_ussd_action);
- g_object_unref (self->priv->pending_ussd_action);
+ GTask *pending_task;
+
+ pending_task = self->priv->pending_ussd_action;
self->priv->pending_ussd_action = NULL;
+
+ g_task_return_new_error (pending_task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "USSD session was cancelled");
+ g_object_unref (pending_task);
}
mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_ussd_cancel);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CUSD=2",
10,
TRUE,
(GAsyncReadyCallback)cancel_command_ready,
- result);
+ task);
}
/*****************************************************************************/
/* Send command (3GPP/USSD interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- gchar *command;
- gboolean current_is_unencoded;
- gboolean encoded_used;
- gboolean unencoded_used;
+ gchar *command;
+ gboolean current_is_unencoded;
+ gboolean encoded_used;
+ gboolean unencoded_used;
} Modem3gppUssdSendContext;
static void
-modem_3gpp_ussd_send_context_complete_and_free (Modem3gppUssdSendContext *ctx)
+modem_3gpp_ussd_send_context_free (Modem3gppUssdSendContext *ctx)
{
- /* We check for result, as we may have already set it in
- * priv->pending_ussd_request */
- if (ctx->result) {
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- }
- g_object_unref (ctx->self);
g_free (ctx->command);
g_slice_free (Modem3gppUssdSendContext, ctx);
}
-static const gchar *
-modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error)
+static gchar *
+modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- /* We can return the string as constant because it is owned by the async
- * result, which will be valid during the whole call of its callback, which
- * is when we're actually calling finish() */
- return (const gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
-static void modem_3gpp_ussd_context_step (Modem3gppUssdSendContext *ctx);
+static void modem_3gpp_ussd_context_step (MMBroadbandModem *self);
static void cusd_process_string (MMBroadbandModem *self,
const gchar *str);
static void
-ussd_send_command_ready (MMBroadbandModem *self,
- GAsyncResult *res,
- Modem3gppUssdSendContext *ctx)
+ussd_send_command_ready (MMBaseModem *_self,
+ GAsyncResult *res)
{
- GError *error = NULL;
- const gchar *reply;
+ MMBroadbandModem *self;
+ Modem3gppUssdSendContext *ctx;
+ GError *error = NULL;
+ const gchar *response;
- g_assert (ctx->result == NULL);
+ self = MM_BROADBAND_MODEM (_self);
- reply = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ response = mm_base_modem_at_command_finish (_self, res, &error);
if (error) {
/* Some immediate error happened when sending the USSD request */
- mm_dbg ("Error sending USSD request: '%s'", error->message);
+ mm_obj_dbg (self, "error sending USSD request: '%s'", error->message);
g_error_free (error);
if (self->priv->pending_ussd_action) {
- /* Recover result */
- ctx->result = self->priv->pending_ussd_action;
- self->priv->pending_ussd_action = NULL;
- modem_3gpp_ussd_context_step (ctx);
+ modem_3gpp_ussd_context_step (self);
return;
}
+ }
- /* So the USSD action was completed already... */
- mm_dbg ("USSD action already completed via URCs");
- modem_3gpp_ussd_send_context_complete_and_free (ctx);
+ if (!self->priv->pending_ussd_action) {
+ mm_obj_dbg (self, "USSD operation finished already via URCs");
return;
}
/* Cache the hint for the next time we send something */
- if (!ctx->self->priv->use_unencoded_ussd &&
- ctx->current_is_unencoded) {
- mm_dbg ("Will assume we want unencoded USSD commands");
- ctx->self->priv->use_unencoded_ussd = TRUE;
- } else if (ctx->self->priv->use_unencoded_ussd &&
- !ctx->current_is_unencoded) {
- mm_dbg ("Will assume we want encoded USSD commands");
- ctx->self->priv->use_unencoded_ussd = FALSE;
+ ctx = g_task_get_task_data (self->priv->pending_ussd_action);
+ if (!self->priv->use_unencoded_ussd && ctx->current_is_unencoded) {
+ mm_obj_dbg (self, "will assume we want unencoded USSD commands");
+ self->priv->use_unencoded_ussd = TRUE;
+ } else if (self->priv->use_unencoded_ussd && !ctx->current_is_unencoded) {
+ mm_obj_dbg (self, "will assume we want encoded USSD commands");
+ self->priv->use_unencoded_ussd = FALSE;
}
- if (!self->priv->pending_ussd_action)
- mm_dbg ("USSD operation finished already via URCs");
- else if (reply && reply[0]) {
- reply = mm_strip_tag (reply, "+CUSD:");
- cusd_process_string (ctx->self, reply);
+ if (response && response[0]) {
+ response = mm_strip_tag (response, "+CUSD:");
+ cusd_process_string (self, response);
}
-
- modem_3gpp_ussd_send_context_complete_and_free (ctx);
}
static void
-modem_3gpp_ussd_context_send_encoded (Modem3gppUssdSendContext *ctx)
+modem_3gpp_ussd_context_send_encoded (MMBroadbandModem *self)
{
- gchar *at_command = NULL;
- GError *error = NULL;
- guint scheme = 0;
- gchar *encoded;
+ Modem3gppUssdSendContext *ctx;
+ gchar *at_command = NULL;
+ GError *error = NULL;
+ guint scheme = 0;
+ gchar *encoded;
+
+ g_assert (self->priv->pending_ussd_action);
+ ctx = g_task_get_task_data (self->priv->pending_ussd_action);
/* Encode USSD command */
- encoded = mm_iface_modem_3gpp_ussd_encode (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
- ctx->command,
- &scheme,
- &error);
+ encoded = mm_iface_modem_3gpp_ussd_encode (MM_IFACE_MODEM_3GPP_USSD (self), ctx->command, &scheme, &error);
if (!encoded) {
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
- MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
- g_simple_async_result_take_error (ctx->result, error);
- modem_3gpp_ussd_send_context_complete_and_free (ctx);
+ GTask *task;
+
+ task = self->priv->pending_ussd_action;
+ self->priv->pending_ussd_action = NULL;
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -4551,25 +5832,23 @@ modem_3gpp_ussd_context_send_encoded (Modem3gppUssdSendContext *ctx)
at_command = g_strdup_printf ("+CUSD=1,\"%s\",%d", encoded, scheme);
g_free (encoded);
- /* Cache the action, as it may be completed via URCs.
- * There shouldn't be any previous action pending. */
- g_warn_if_fail (ctx->self->priv->pending_ussd_action == NULL);
- ctx->self->priv->pending_ussd_action = ctx->result;
- ctx->result = NULL;
-
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
at_command,
10,
FALSE,
(GAsyncReadyCallback)ussd_send_command_ready,
- ctx);
+ NULL);
g_free (at_command);
}
static void
-modem_3gpp_ussd_context_send_unencoded (Modem3gppUssdSendContext *ctx)
+modem_3gpp_ussd_context_send_unencoded (MMBroadbandModem *self)
{
- gchar *at_command = NULL;
+ Modem3gppUssdSendContext *ctx;
+ gchar *at_command = NULL;
+
+ g_assert (self->priv->pending_ussd_action);
+ ctx = g_task_get_task_data (self->priv->pending_ussd_action);
/* Build AT command with action unencoded */
ctx->unencoded_used = TRUE;
@@ -4578,128 +5857,150 @@ modem_3gpp_ussd_context_send_unencoded (Modem3gppUssdSendContext *ctx)
ctx->command,
MM_MODEM_GSM_USSD_SCHEME_7BIT);
- /* Cache the action, as it may be completed via URCs.
- * There shouldn't be any previous action pending. */
- g_warn_if_fail (ctx->self->priv->pending_ussd_action == NULL);
- ctx->self->priv->pending_ussd_action = ctx->result;
- ctx->result = NULL;
-
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
at_command,
10,
FALSE,
(GAsyncReadyCallback)ussd_send_command_ready,
- ctx);
+ NULL);
g_free (at_command);
}
static void
-modem_3gpp_ussd_context_step (Modem3gppUssdSendContext *ctx)
+modem_3gpp_ussd_context_step (MMBroadbandModem *self)
{
- if (ctx->encoded_used &&
- ctx->unencoded_used) {
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
+ Modem3gppUssdSendContext *ctx;
+
+ g_assert (self->priv->pending_ussd_action);
+ ctx = g_task_get_task_data (self->priv->pending_ussd_action);
+
+ if (ctx->encoded_used && ctx->unencoded_used) {
+ GTask *task;
+
+ task = self->priv->pending_ussd_action;
+ self->priv->pending_ussd_action = NULL;
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Sending USSD command failed");
- modem_3gpp_ussd_send_context_complete_and_free (ctx);
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Sending USSD command failed");
+ g_object_unref (task);
return;
}
- if (ctx->self->priv->use_unencoded_ussd) {
+ if (self->priv->use_unencoded_ussd) {
if (!ctx->unencoded_used)
- modem_3gpp_ussd_context_send_unencoded (ctx);
+ modem_3gpp_ussd_context_send_unencoded (self);
else if (!ctx->encoded_used)
- modem_3gpp_ussd_context_send_encoded (ctx);
+ modem_3gpp_ussd_context_send_encoded (self);
else
g_assert_not_reached ();
} else {
if (!ctx->encoded_used)
- modem_3gpp_ussd_context_send_encoded (ctx);
+ modem_3gpp_ussd_context_send_encoded (self);
else if (!ctx->unencoded_used)
- modem_3gpp_ussd_context_send_unencoded (ctx);
+ modem_3gpp_ussd_context_send_unencoded (self);
else
g_assert_not_reached ();
}
}
static void
-modem_3gpp_ussd_send (MMIfaceModem3gppUssd *self,
- const gchar *command,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_send (MMIfaceModem3gppUssd *_self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ MMBroadbandModem *self;
+ GTask *task;
Modem3gppUssdSendContext *ctx;
+ self = MM_BROADBAND_MODEM (_self);
+
+ task = g_task_new (self, NULL, callback, user_data);
ctx = g_slice_new0 (Modem3gppUssdSendContext);
- /* We're going to steal the string result in finish() so we must have a
- * callback specified. */
- g_assert (callback != NULL);
- ctx->self = g_object_ref (self);
ctx->command = g_strdup (command);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_ussd_send);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) modem_3gpp_ussd_send_context_free);
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
- MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE);
+ /* Fail if there is an ongoing operation already */
+ if (self->priv->pending_ussd_action) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "there is already an ongoing USSD operation");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Cache the action, as it may be completed via URCs */
+ self->priv->pending_ussd_action = task;
- modem_3gpp_ussd_context_step (ctx);
+ mm_iface_modem_3gpp_ussd_update_state (_self, MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE);
+
+ modem_3gpp_ussd_context_step (self);
}
/*****************************************************************************/
/* USSD Encode/Decode (3GPP/USSD interface) */
static gchar *
-modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *self,
- const gchar *command,
- guint *scheme,
- GError **error)
-{
- MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
- GByteArray *ussd_command;
- gchar *hex = NULL;
-
- ussd_command = g_byte_array_new ();
-
- /* encode to the current charset */
- if (mm_modem_charset_byte_array_append (ussd_command,
- command,
- FALSE,
- broadband->priv->modem_current_charset)) {
- *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
- /* convert to hex representation */
- hex = mm_utils_bin2hexstr (ussd_command->data, ussd_command->len);
+modem_3gpp_ussd_encode (MMIfaceModem3gppUssd *_self,
+ const gchar *command,
+ guint *scheme,
+ GError **error)
+{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ g_autoptr(GByteArray) ussd_command = NULL;
+
+ /* Encode to the current charset (as per AT+CSCS, which is what most modems
+ * (except for Huawei it seems) will ask for. */
+ ussd_command = mm_modem_charset_bytearray_from_utf8 (command, self->priv->modem_current_charset, FALSE, error);
+ if (!ussd_command) {
+ g_prefix_error (error, "Failed to encode USSD command: ");
+ return NULL;
}
- g_byte_array_free (ussd_command, TRUE);
+ /* The scheme value does NOT represent the encoding used to encode the string
+ * we're giving. This scheme reflects the encoding that the modem should use when
+ * sending the data out to the network. We're hardcoding this to GSM-7 because
+ * USSD commands fit well in GSM-7, unlike USSD responses that may contain code
+ * points that may only be encoded in UCS-2. */
+ *scheme = MM_MODEM_GSM_USSD_SCHEME_7BIT;
- return hex;
+ /* convert to hex representation */
+ return (gchar *) mm_utils_bin2hexstr (ussd_command->data, ussd_command->len);
}
static gchar *
-modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self,
- const gchar *reply,
- GError **error)
-{
- MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
+modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self,
+ const gchar *reply,
+ GError **error)
+{
+ MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
+ guint8 *bin = NULL;
+ gsize bin_len = 0;
+ g_autoptr(GByteArray) barray = NULL;
+
+ bin = (guint8 *) mm_utils_hexstr2bin (reply, -1, &bin_len, error);
+ if (!bin) {
+ g_prefix_error (error, "Couldn't convert HEX string to binary: ");
+ return NULL;
+ }
+ barray = g_byte_array_new_take (bin, bin_len);
- return mm_modem_charset_hex_to_utf8 (reply,
- broadband->priv->modem_current_charset);
+ /* Decode from current charset (as per AT+CSCS, which is what most modems
+ * (except for Huawei it seems) will ask for. */
+ return mm_modem_charset_bytearray_to_utf8 (barray, broadband->priv->modem_current_charset, FALSE, error);
}
/*****************************************************************************/
/* Setup/Cleanup unsolicited result codes (3GPP/USSD interface) */
static gboolean
-modem_3gpp_ussd_setup_cleanup_unsolicited_result_codes_finish (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gchar *
@@ -4749,120 +6050,100 @@ decode_ussd_response (MMBroadbandModem *self,
static void
cusd_process_string (MMBroadbandModem *self,
- const gchar *str)
+ const gchar *str)
{
- MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE;
+ MMModem3gppUssdSessionState ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE;
+ GError *error = NULL;
+ gint status;
+ GTask *task;
+ gchar *converted = NULL;
+
+ /* If there is a pending action, it is ALWAYS completed here */
+ task = self->priv->pending_ussd_action;
+ self->priv->pending_ussd_action = NULL;
if (!str || !isdigit (*str)) {
- if (self->priv->pending_ussd_action)
- g_simple_async_result_set_error (self->priv->pending_ussd_action,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Invalid USSD response received: '%s'",
- str ? str : "(none)");
- else
- mm_warn ("Received invalid USSD network-initiated request: '%s'",
- str ? str : "(none)");
- } else {
- gint status;
-
- status = g_ascii_digit_value (*str);
- switch (status) {
- case 0: /* no further action required */ {
- gchar *converted;
- GError *error = NULL;
-
- converted = decode_ussd_response (self, str, &error);
- if (self->priv->pending_ussd_action) {
- /* Response to the user's request */
- if (error)
- g_simple_async_result_take_error (self->priv->pending_ussd_action, error);
- else
- g_simple_async_result_set_op_res_gpointer (self->priv->pending_ussd_action,
- converted,
- g_free);
- } else {
- if (error) {
- mm_warn ("Invalid network initiated USSD notification: %s",
- error->message);
- g_error_free (error);
- } else {
- /* Network-initiated USSD-Notify */
- mm_iface_modem_3gpp_ussd_update_network_notification (
- MM_IFACE_MODEM_3GPP_USSD (self),
- converted);
- g_free (converted);
- }
- }
- break;
- }
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid USSD message received: '%s'", str ? str : "(none)");
+ goto out;
+ }
- case 1: /* further action required */ {
- gchar *converted;
- GError *error = NULL;
-
- ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE;
- converted = decode_ussd_response (self, str, &error);
- if (self->priv->pending_ussd_action) {
- if (error)
- g_simple_async_result_take_error (self->priv->pending_ussd_action, error);
- else
- g_simple_async_result_set_op_res_gpointer (self->priv->pending_ussd_action,
- converted,
- g_free);
- } else {
- if (error) {
- mm_warn ("Invalid network initiated USSD request: %s",
- error->message);
- g_error_free (error);
- } else {
- /* Network-initiated USSD-Request */
- mm_iface_modem_3gpp_ussd_update_network_request (
- MM_IFACE_MODEM_3GPP_USSD (self),
- converted);
- g_free (converted);
- }
- }
+ status = g_ascii_digit_value (*str);
+ switch (status) {
+ case 0:
+ /* no further action required */
+ converted = decode_ussd_response (self, str, &error);
+ if (!converted)
break;
- }
- case 2:
- if (self->priv->pending_ussd_action)
- g_simple_async_result_set_error (self->priv->pending_ussd_action,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "USSD terminated by network.");
+ /* Response to the user's request? */
+ if (task)
break;
- case 4:
- if (self->priv->pending_ussd_action)
- g_simple_async_result_set_error (self->priv->pending_ussd_action,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Operation not supported.");
+ /* Network-initiated USSD-Notify */
+ mm_iface_modem_3gpp_ussd_update_network_notification (MM_IFACE_MODEM_3GPP_USSD (self), converted);
+ g_clear_pointer (&converted, g_free);
+ break;
+
+ case 1:
+ /* further action required */
+ ussd_state = MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE;
+
+ converted = decode_ussd_response (self, str, &error);
+ if (!converted)
break;
- default:
- if (self->priv->pending_ussd_action)
- g_simple_async_result_set_error (self->priv->pending_ussd_action,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled USSD reply: %s (%d)",
- str,
- status);
+ /* Response to the user's request? */
+ if (task)
break;
- }
+
+ /* Network-initiated USSD-Request */
+ mm_iface_modem_3gpp_ussd_update_network_request (MM_IFACE_MODEM_3GPP_USSD (self), converted);
+ g_clear_pointer (&converted, g_free);
+ break;
+
+ case 2:
+ /* Response to the user's request? */
+ if (task)
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "USSD terminated by network");
+ break;
+
+ case 4:
+ /* Response to the user's request? */
+ if (task)
+ error = g_error_new (MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "Operation not supported");
+ break;
+
+ default:
+ /* Response to the user's request? */
+ if (task)
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unhandled USSD reply: %s (%d)", str, status);
+ break;
}
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
- ussd_state);
+out:
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self), ussd_state);
/* Complete the pending action */
- if (self->priv->pending_ussd_action) {
- g_simple_async_result_complete_in_idle (self->priv->pending_ussd_action);
- g_object_unref (self->priv->pending_ussd_action);
- self->priv->pending_ussd_action = NULL;
+ if (task) {
+ if (error)
+ g_task_return_error (task, error);
+ else if (converted)
+ g_task_return_pointer (task, converted, g_free);
+ else
+ g_assert_not_reached ();
+ return;
+ }
+
+ /* If no pending task, just report the error */
+ if (error) {
+ mm_obj_warn (self, "invalid USSD message: %s", error->message);
+ g_error_free (error);
}
+
+ g_assert (!converted);
}
static void
@@ -4872,7 +6153,7 @@ cusd_received (MMPortSerialAt *port,
{
gchar *str;
- mm_dbg ("Unsolicited USSD URC received");
+ mm_obj_dbg (self, "unsolicited USSD URC received");
str = g_match_info_fetch (info, 1);
cusd_process_string (self, str);
g_free (str);
@@ -4884,15 +6165,10 @@ set_unsolicited_result_code_handlers (MMIfaceModem3gppUssd *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MMPortSerialAt *ports[2];
GRegex *cusd_regex;
guint i;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_unsolicited_events_handlers);
+ GTask *task;
cusd_regex = mm_3gpp_cusd_regex_get ();
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
@@ -4903,9 +6179,9 @@ set_unsolicited_result_code_handlers (MMIfaceModem3gppUssd *self,
if (!ports[i])
continue;
/* Set/unset unsolicited CUSD event handler */
- mm_dbg ("(%s) %s unsolicited result code handlers",
- mm_port_get_device (MM_PORT (ports[i])),
- enable ? "Setting" : "Removing");
+ mm_obj_dbg (self, "%s unsolicited result code handlers in %s",
+ enable ? "setting" : "removing",
+ mm_port_get_device (MM_PORT (ports[i])));
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
cusd_regex,
@@ -4915,23 +6191,24 @@ set_unsolicited_result_code_handlers (MMIfaceModem3gppUssd *self,
}
g_regex_unref (cusd_regex);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_3gpp_ussd_setup_unsolicited_result_codes (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_setup_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
set_unsolicited_result_code_handlers (self, TRUE, callback, user_data);
}
static void
-modem_3gpp_ussd_cleanup_unsolicited_result_codes (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_cleanup_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
set_unsolicited_result_code_handlers (self, FALSE, callback, user_data);
}
@@ -4940,71 +6217,63 @@ modem_3gpp_ussd_cleanup_unsolicited_result_codes (MMIfaceModem3gppUssd *self,
/* Enable/Disable URCs (3GPP/USSD interface) */
static gboolean
-modem_3gpp_ussd_enable_disable_unsolicited_result_codes_finish (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error)
+modem_3gpp_ussd_enable_disable_unsolicited_events_finish (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
urc_enable_disable_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-modem_3gpp_ussd_disable_unsolicited_result_codes (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_disable_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_ussd_disable_unsolicited_result_codes);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CUSD=0",
3,
TRUE,
(GAsyncReadyCallback)urc_enable_disable_ready,
- result);
+ task);
}
static void
-modem_3gpp_ussd_enable_unsolicited_result_codes (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_enable_unsolicited_events (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_ussd_enable_unsolicited_result_codes);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CUSD=1",
3,
TRUE,
(GAsyncReadyCallback)urc_enable_disable_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -5015,27 +6284,25 @@ modem_3gpp_ussd_check_support_finish (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cusd_format_check_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -5043,12 +6310,9 @@ modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_ussd_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
/* Check USSD support */
mm_base_modem_at_command (MM_BASE_MODEM (self),
@@ -5056,7 +6320,7 @@ modem_3gpp_ussd_check_support (MMIfaceModem3gppUssd *self,
3,
TRUE,
(GAsyncReadyCallback)cusd_format_check_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -5067,28 +6331,26 @@ modem_messaging_check_support_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cnmi_format_check_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* CNMI command is supported; assume we have full messaging capabilities */
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -5096,22 +6358,18 @@ modem_messaging_check_support (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_messaging_check_support);
+ task = g_task_new (self, NULL, callback, user_data);
/* We assume that CDMA-only modems don't have messaging capabilities */
if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
- g_simple_async_result_set_error (
- result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"CDMA-only modems don't have messaging capabilities");
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
return;
}
@@ -5121,7 +6379,7 @@ modem_messaging_check_support (MMIfaceModemMessaging *self,
3,
TRUE,
(GAsyncReadyCallback)cnmi_format_check_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -5155,21 +6413,21 @@ modem_messaging_load_supported_storages_finish (MMIfaceModemMessaging *self,
{
SupportedStoragesResult *result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- result = (SupportedStoragesResult *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*mem1 = g_array_ref (result->mem1);
*mem2 = g_array_ref (result->mem2);
*mem3 = g_array_ref (result->mem3);
-
+ supported_storages_result_free (result);
return TRUE;
}
static void
cpms_format_check_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
@@ -5177,9 +6435,8 @@ cpms_format_check_ready (MMBroadbandModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -5189,23 +6446,18 @@ cpms_format_check_ready (MMBroadbandModem *self,
if (!mm_3gpp_parse_cpms_test_response (response,
&result->mem1,
&result->mem2,
- &result->mem3)) {
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse supported storages reply: '%s'",
- response);
+ &result->mem3,
+ &error)) {
supported_storages_result_free (result);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- g_simple_async_result_set_op_res_gpointer (simple,
- result,
- (GDestroyNotify)supported_storages_result_free);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_pointer (task,
+ result,
+ (GDestroyNotify)supported_storages_result_free);
+ g_object_unref (task);
}
static void
@@ -5213,12 +6465,9 @@ modem_messaging_load_supported_storages (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_messaging_load_supported_storages);
+ task = g_task_new (self, NULL, callback, user_data);
/* Check support storages */
mm_base_modem_at_command (MM_BASE_MODEM (self),
@@ -5226,7 +6475,80 @@ modem_messaging_load_supported_storages (MMIfaceModemMessaging *self,
3,
TRUE,
(GAsyncReadyCallback)cpms_format_check_ready,
- result);
+ task);
+}
+
+/*****************************************************************************/
+/* Init current SMS storages (Messaging interface) */
+
+static gboolean
+modem_messaging_init_current_storages_finish (MMIfaceModemMessaging *_self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+cpms_query_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ MMSmsStorage mem1;
+ MMSmsStorage mem2;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Parse reply */
+ if (!mm_3gpp_parse_cpms_query_response (response,
+ &mem1,
+ &mem2,
+ &error)) {
+ g_task_return_error (task, error);
+ } else {
+ gchar *aux;
+
+ self->priv->current_sms_mem1_storage = mem1;
+ self->priv->current_sms_mem2_storage = mem2;
+
+ mm_obj_dbg (self, "current storages initialized:");
+
+ aux = mm_common_build_sms_storages_string (&mem1, 1);
+ mm_obj_dbg (self, " mem1 (list/read/delete) storages: '%s'", aux);
+ g_free (aux);
+
+ aux = mm_common_build_sms_storages_string (&mem2, 1);
+ mm_obj_dbg (self, " mem2 (write/send) storages: '%s'", aux);
+ g_free (aux);
+
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+modem_messaging_init_current_storages (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Check support storages */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CPMS?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback)cpms_query_ready,
+ task);
}
/*****************************************************************************/
@@ -5272,12 +6594,10 @@ mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
typedef struct {
- GSimpleAsyncResult *result;
- MMBroadbandModem *self;
MMSmsStorage previous_mem1;
gboolean mem1_locked;
MMSmsStorage previous_mem2;
@@ -5285,38 +6605,33 @@ typedef struct {
} LockSmsStoragesContext;
static void
-lock_sms_storages_context_complete_and_free (LockSmsStoragesContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (LockSmsStoragesContext, ctx);
-}
-
-static void
-lock_storages_cpms_set_ready (MMBaseModem *self,
+lock_storages_cpms_set_ready (MMBaseModem *_self,
GAsyncResult *res,
- LockSmsStoragesContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ LockSmsStoragesContext *ctx;
GError *error = NULL;
- mm_base_modem_at_command_finish (self, res, &error);
+ ctx = g_task_get_task_data (task);
+
+ mm_base_modem_at_command_finish (_self, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
/* Reset previous storages and set unlocked */
if (ctx->mem1_locked) {
- ctx->self->priv->current_sms_mem1_storage = ctx->previous_mem1;
- ctx->self->priv->mem1_storage_locked = FALSE;
+ self->priv->current_sms_mem1_storage = ctx->previous_mem1;
+ self->priv->mem1_storage_locked = FALSE;
}
if (ctx->mem2_locked) {
- ctx->self->priv->current_sms_mem2_storage = ctx->previous_mem2;
- ctx->self->priv->mem2_storage_locked = FALSE;
+ self->priv->current_sms_mem2_storage = ctx->previous_mem2;
+ self->priv->mem2_storage_locked = FALSE;
}
+ g_task_return_error (task, error);
}
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
- lock_sms_storages_context_complete_and_free (ctx);
+ g_object_unref (task);
}
void
@@ -5327,7 +6642,8 @@ mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self,
gpointer user_data)
{
LockSmsStoragesContext *ctx;
- gchar *cmd;
+ GTask *task;
+ gchar *cmd = NULL;
gchar *mem1_str = NULL;
gchar *mem2_str = NULL;
@@ -5335,10 +6651,11 @@ mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self,
* error */
if ((mem1 != MM_SMS_STORAGE_UNKNOWN && self->priv->mem1_storage_locked) ||
(mem2 != MM_SMS_STORAGE_UNKNOWN && self->priv->mem2_storage_locked)) {
- g_simple_async_report_error_in_idle (
- G_OBJECT (self),
+ g_task_report_new_error (
+ self,
callback,
user_data,
+ mm_broadband_modem_lock_sms_storages,
MM_CORE_ERROR,
MM_CORE_ERROR_RETRY,
"SMS storage currently locked, try again later");
@@ -5349,50 +6666,63 @@ mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self,
g_assert (mem1 != MM_SMS_STORAGE_UNKNOWN ||
mem2 != MM_SMS_STORAGE_UNKNOWN);
- ctx = g_slice_new0 (LockSmsStoragesContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_broadband_modem_lock_sms_storages);
+ ctx = g_new0 (LockSmsStoragesContext, 1);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+ /* Some modems may not support empty string parameters, then if mem1 is
+ * UNKNOWN, and current sms mem1 storage has a valid value, we send again
+ * the already locked mem1 value in place of an empty string.
+ * This way we also avoid to confuse the environment of other sync operation
+ * that have potentially locked mem1 previously.
+ */
if (mem1 != MM_SMS_STORAGE_UNKNOWN) {
ctx->mem1_locked = TRUE;
ctx->previous_mem1 = self->priv->current_sms_mem1_storage;
- self->priv->mem1_storage_locked = TRUE;
+
self->priv->current_sms_mem1_storage = mem1;
- mem1_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem1_storage), -1);
+ self->priv->mem1_storage_locked = TRUE;
+ } else if (self->priv->current_sms_mem1_storage != MM_SMS_STORAGE_UNKNOWN) {
+ mm_obj_dbg (self, "given sms mem1 storage is unknown. Using current sms mem1 storage value '%s' instead",
+ mm_sms_storage_get_string (self->priv->current_sms_mem1_storage));
+ } else {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Cannot lock mem2 storage alone when current mem1 storage is unknown");
+ g_object_unref (task);
+ return;
}
+ mem1_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem1_storage), -1);
if (mem2 != MM_SMS_STORAGE_UNKNOWN) {
ctx->mem2_locked = TRUE;
ctx->previous_mem2 = self->priv->current_sms_mem2_storage;
+
self->priv->mem2_storage_locked = TRUE;
self->priv->current_sms_mem2_storage = mem2;
+
mem2_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem2_storage), -1);
}
- /* We don't touch 'mem3' here */
+ g_assert (mem1_str != NULL);
- mm_dbg ("Locking SMS storages to: mem1 (%s), mem2 (%s)...",
- mem1_str ? mem1_str : "none",
- mem2_str ? mem2_str : "none");
+ /* We don't touch 'mem3' here */
+ mm_obj_dbg (self, "locking SMS storages to: mem1 (%s), mem2 (%s)...",
+ mem1_str, mem2_str ? mem2_str : "none");
if (mem2_str)
- cmd = g_strdup_printf ("+CPMS=\"%s\",\"%s\"",
- mem1_str ? mem1_str : "",
- mem2_str);
- else if (mem1_str)
- cmd = g_strdup_printf ("+CPMS=\"%s\"", mem1_str);
+ cmd = g_strdup_printf ("+CPMS=\"%s\",\"%s\"", mem1_str, mem2_str);
else
- g_assert_not_reached ();
+ cmd = g_strdup_printf ("+CPMS=\"%s\"", mem1_str);
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
3,
FALSE,
(GAsyncReadyCallback)lock_storages_cpms_set_ready,
- ctx);
+ task);
g_free (mem1_str);
g_free (mem2_str);
g_free (cmd);
@@ -5406,23 +6736,22 @@ modem_messaging_set_default_storage_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cpms_set_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -5433,25 +6762,39 @@ modem_messaging_set_default_storage (MMIfaceModemMessaging *_self,
{
MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
gchar *cmd;
- GSimpleAsyncResult *result;
+ gchar *mem1_str;
gchar *mem_str;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_messaging_set_default_storage);
+ /* We provide the current sms storage for mem1 if not UNKNOWN */
+ if (self->priv->current_sms_mem1_storage == MM_SMS_STORAGE_UNKNOWN) {
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ modem_messaging_set_default_storage,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot set default storage when current mem1 storage is unknown");
+ return;
+ }
/* Set defaults as current */
self->priv->current_sms_mem2_storage = storage;
+ mem1_str = g_ascii_strup (mm_sms_storage_get_string (self->priv->current_sms_mem1_storage), -1);
mem_str = g_ascii_strup (mm_sms_storage_get_string (storage), -1);
- cmd = g_strdup_printf ("+CPMS=\"\",\"%s\",\"%s\"", mem_str, mem_str);
+
+ cmd = g_strdup_printf ("+CPMS=\"%s\",\"%s\",\"%s\"", mem1_str, mem_str, mem_str);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
mm_base_modem_at_command (MM_BASE_MODEM (self),
cmd,
3,
FALSE,
(GAsyncReadyCallback)cpms_set_ready,
- result);
+ task);
+ g_free (mem1_str);
g_free (mem_str);
g_free (cmd);
}
@@ -5464,34 +6807,32 @@ modem_messaging_setup_sms_format_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
cmgf_set_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- mm_dbg ("Failed to set preferred SMS mode: '%s'; assuming text mode'",
- error->message);
+ mm_obj_dbg (self, "failed to set preferred SMS mode: %s; assuming text mode'", error->message);
g_error_free (error);
self->priv->modem_messaging_sms_pdu_mode = FALSE;
} else
- mm_dbg ("Successfully set preferred SMS mode: '%s'",
- self->priv->modem_messaging_sms_pdu_mode ? "PDU" : "text");
+ mm_obj_dbg (self, "successfully set preferred SMS mode: '%s'",
+ self->priv->modem_messaging_sms_pdu_mode ? "PDU" : "text");
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
set_preferred_sms_format (MMBroadbandModem *self,
- GSimpleAsyncResult *result)
+ GTask *task)
{
gchar *cmd;
@@ -5502,14 +6843,14 @@ set_preferred_sms_format (MMBroadbandModem *self,
3,
TRUE,
(GAsyncReadyCallback)cmgf_set_ready,
- result);
+ task);
g_free (cmd);
}
static void
cmgf_format_check_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *response;
@@ -5522,8 +6863,7 @@ cmgf_format_check_ready (MMBroadbandModem *self,
&sms_pdu_supported,
&sms_text_supported,
&error)) {
- mm_dbg ("Failed to query supported SMS modes: '%s'",
- error->message);
+ mm_obj_dbg (self, "failed to query supported SMS modes: %s", error->message);
g_error_free (error);
}
@@ -5531,16 +6871,16 @@ cmgf_format_check_ready (MMBroadbandModem *self,
self->priv->modem_messaging_sms_pdu_mode = TRUE;
if (!sms_pdu_supported) {
if (sms_text_supported) {
- mm_dbg ("PDU mode not supported, will try to use Text mode");
+ mm_obj_dbg (self, "PDU mode not supported, will try to use Text mode");
self->priv->modem_messaging_sms_pdu_mode = FALSE;
} else
- mm_dbg ("Neither PDU nor Text modes are reported as supported; "
- "will anyway default to PDU mode");
+ mm_obj_dbg (self, "neither PDU nor Text modes are reported as supported; "
+ "will anyway default to PDU mode");
}
self->priv->sms_supported_modes_checked = TRUE;
- set_preferred_sms_format (self, simple);
+ set_preferred_sms_format (self, task);
}
static void
@@ -5548,17 +6888,14 @@ modem_messaging_setup_sms_format (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_messaging_setup_sms_format);
+ task = g_task_new (self, NULL, callback, user_data);
/* If we already checked for supported SMS types, go on to select the
* preferred format. */
if (MM_BROADBAND_MODEM (self)->priv->sms_supported_modes_checked) {
- set_preferred_sms_format (MM_BROADBAND_MODEM (self), result);
+ set_preferred_sms_format (MM_BROADBAND_MODEM (self), task);
return;
}
@@ -5568,7 +6905,7 @@ modem_messaging_setup_sms_format (MMIfaceModemMessaging *self,
3,
TRUE,
(GAsyncReadyCallback)cmgf_format_check_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -5579,32 +6916,21 @@ modem_messaging_setup_cleanup_unsolicited_events_finish (MMIfaceModemMessaging *
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
guint idx;
} SmsPartContext;
static void
-sms_part_context_complete_and_free (SmsPartContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
-static void
sms_part_ready (MMBroadbandModem *self,
GAsyncResult *res,
- SmsPartContext *ctx)
+ GTask *task)
{
+ SmsPartContext *ctx;
MMSmsPart *part;
- gint rv, status, tpdu_len;
- gchar pdu[MM_SMS_PART_3GPP_MAX_PDU_LEN + 1];
+ MM3gppPduInfo *info;
const gchar *response;
GError *error = NULL;
@@ -5615,69 +6941,69 @@ sms_part_ready (MMBroadbandModem *self,
if (error) {
/* We're really ignoring this error afterwards, as we don't have a callback
* passed to the async operation, so just log the error here. */
- mm_warn ("Couldn't retrieve SMS part: '%s'",
- error->message);
- g_simple_async_result_take_error (ctx->result, error);
- sms_part_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't retrieve SMS part: '%s'", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- 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,
- MM_CORE_ERROR_FAILED,
- "Failed to parse CMGR response (parsed %d items)", rv);
- mm_warn ("Couldn't retrieve SMS part: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- sms_part_context_complete_and_free (ctx);
+ ctx = g_task_get_task_data (task);
+
+ info = mm_3gpp_parse_cmgr_read_response (response, ctx->idx, &error);
+ if (!info) {
+ mm_obj_warn (self, "couldn't parse SMS part: '%s'", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- part = mm_sms_part_3gpp_new_from_pdu (ctx->idx, pdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, self, &error);
if (part) {
- mm_dbg ("Correctly parsed PDU (%d)", ctx->idx);
+ mm_obj_dbg (self, "correctly parsed PDU (%d)", ctx->idx);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
part,
MM_SMS_STATE_RECEIVED,
self->priv->modem_messaging_sms_default_storage);
} else {
/* Don't treat the error as critical */
- mm_dbg ("Error parsing PDU (%d): %s", ctx->idx, error->message);
+ mm_obj_dbg (self, "error parsing PDU (%d): %s", ctx->idx, error->message);
g_error_free (error);
}
/* All done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_part_context_complete_and_free (ctx);
+ mm_3gpp_pdu_info_free (info);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
indication_lock_storages_ready (MMBroadbandModem *self,
GAsyncResult *res,
- SmsPartContext *ctx)
+ GTask *task)
{
+ SmsPartContext *ctx;
gchar *command;
GError *error = NULL;
if (!mm_broadband_modem_lock_sms_storages_finish (self, res, &error)) {
/* TODO: we should either make this lock() never fail, by automatically
* retrying after some time, or otherwise retry here. */
- g_simple_async_result_take_error (ctx->result, error);
- sms_part_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Storage now set and locked */
/* Retrieve the message */
+ ctx = g_task_get_task_data (task);
command = g_strdup_printf ("+CMGR=%d", ctx->idx);
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
command,
10,
FALSE,
(GAsyncReadyCallback)sms_part_ready,
- ctx);
+ task);
g_free (command);
}
@@ -5687,6 +7013,7 @@ cmti_received (MMPortSerialAt *port,
MMBroadbandModem *self)
{
SmsPartContext *ctx;
+ GTask *task;
guint idx = 0;
MMSmsStorage storage;
gchar *str;
@@ -5698,7 +7025,7 @@ cmti_received (MMPortSerialAt *port,
str = mm_get_string_unquoted_from_match_info (info, 1);
storage = mm_common_get_sms_storage_from_string (str, NULL);
if (storage == MM_SMS_STORAGE_UNKNOWN) {
- mm_dbg ("Skipping CMTI indication, unknown storage '%s' reported", str);
+ mm_obj_dbg (self, "skipping CMTI indication, unknown storage '%s' reported", str);
g_free (str);
return;
}
@@ -5708,21 +7035,22 @@ cmti_received (MMPortSerialAt *port,
if (mm_sms_list_has_part (self->priv->modem_messaging_sms_list,
storage,
idx)) {
- mm_dbg ("Skipping CMTI indication, part already processed");
+ mm_obj_dbg (self, "skipping CMTI indication, part already processed");
return;
}
- ctx = g_new0 (SmsPartContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self), NULL, NULL, cmti_received);
+ ctx = g_new (SmsPartContext, 1);
ctx->idx = idx;
+ task = g_task_new (self, NULL, NULL, NULL);
+ g_task_set_task_data (task, ctx, g_free);
+
/* First, request to set the proper storage to read from */
- mm_broadband_modem_lock_sms_storages (ctx->self,
+ mm_broadband_modem_lock_sms_storages (self,
storage,
MM_SMS_STORAGE_UNKNOWN,
(GAsyncReadyCallback)indication_lock_storages_ready,
- ctx);
+ task);
}
static void
@@ -5735,7 +7063,7 @@ cds_received (MMPortSerialAt *port,
guint length;
gchar *pdu;
- mm_dbg ("Got new non-stored message indication");
+ mm_obj_dbg (self, "got new non-stored message indication");
if (!mm_get_uint_from_match_info (info, 1, &length))
return;
@@ -5744,16 +7072,16 @@ cds_received (MMPortSerialAt *port,
if (!pdu)
return;
- part = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, pdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (SMS_PART_INVALID_INDEX, pdu, self, &error);
if (part) {
- mm_dbg ("Correctly parsed non-stored PDU");
+ mm_obj_dbg (self, "correctly parsed non-stored PDU");
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
part,
MM_SMS_STATE_RECEIVED,
MM_SMS_STORAGE_UNKNOWN);
} else {
/* Don't treat the error as critical */
- mm_dbg ("Error parsing non-stored PDU: %s", error->message);
+ mm_obj_dbg (self, "error parsing non-stored PDU: %s", error->message);
g_error_free (error);
}
}
@@ -5764,31 +7092,26 @@ set_messaging_unsolicited_events_handlers (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
MMPortSerialAt *ports[2];
GRegex *cmti_regex;
GRegex *cds_regex;
guint i;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- set_messaging_unsolicited_events_handlers);
+ GTask *task;
cmti_regex = mm_3gpp_cmti_regex_get ();
cds_regex = mm_3gpp_cds_regex_get ();
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
- /* Enable unsolicited events in given port */
+ /* Add messaging unsolicited events handler for port primary and secondary */
for (i = 0; i < 2; i++) {
if (!ports[i])
continue;
/* Set/unset unsolicited CMTI event handler */
- mm_dbg ("(%s) %s messaging unsolicited events handlers",
- mm_port_get_device (MM_PORT (ports[i])),
- enable ? "Setting" : "Removing");
+ mm_obj_dbg (self, "%s messaging unsolicited events handlers in %s",
+ enable ? "setting" : "removing",
+ mm_port_get_device (MM_PORT (ports[i])));
mm_port_serial_at_add_unsolicited_msg_handler (
ports[i],
cmti_regex,
@@ -5805,9 +7128,10 @@ set_messaging_unsolicited_events_handlers (MMIfaceModemMessaging *self,
g_regex_unref (cmti_regex);
g_regex_unref (cds_regex);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -5834,39 +7158,34 @@ modem_messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- GError *inner_error = NULL;
-
- mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return FALSE;
- }
-
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static gboolean
-cnmi_response_processor (MMBaseModem *self,
- gpointer none,
- const gchar *command,
- const gchar *response,
- gboolean last_command,
- const GError *error,
- GVariant **result,
- GError **result_error)
+static MMBaseModemAtResponseProcessorResult
+cnmi_response_processor (MMBaseModem *self,
+ gpointer none,
+ const gchar *command,
+ const gchar *response,
+ gboolean last_command,
+ const GError *error,
+ GVariant **result,
+ GError **result_error)
{
+ *result = NULL;
+ *result_error = NULL;
+
if (error) {
/* If we get a not-supported error and we're not in the last command, we
* won't set 'result_error', so we'll keep on the sequence */
- if (!g_error_matches (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_SUPPORTED) ||
- last_command)
+ if (!g_error_matches (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_SUPPORTED) || last_command) {
*result_error = g_error_copy (error);
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_FAILURE;
+ }
- return FALSE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
}
- *result = NULL;
- return TRUE;
+ return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
}
static const MMBaseModemAtCommand cnmi_sequence[] = {
@@ -5884,43 +7203,111 @@ static const MMBaseModemAtCommand cnmi_sequence[] = {
};
static void
+modem_messaging_enable_unsolicited_events_secondary_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *inner_error = NULL;
+ MMPortSerialAt *secondary;
+
+ secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Since the secondary is not required, we don't propagate the error anywhere */
+ mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self), res, NULL, &inner_error);
+ if (inner_error) {
+ mm_obj_dbg (self, "failed to enable messaging unsolicited events on secondary port %s: %s",
+ mm_port_get_device (MM_PORT (secondary)),
+ inner_error->message);
+ g_error_free (inner_error);
+ }
+
+ mm_obj_dbg (self, "messaging unsolicited events enabled on secondary port %s",
+ mm_port_get_device (MM_PORT (secondary)));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_messaging_enable_unsolicited_events_primary_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *inner_error = NULL;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+
+ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ mm_base_modem_at_sequence_full_finish (MM_BASE_MODEM (self), res, NULL, &inner_error);
+ if (inner_error) {
+ g_task_return_error (task, inner_error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "messaging unsolicited events enabled on primary port %s",
+ mm_port_get_device (MM_PORT (primary)));
+
+ /* Try to enable unsolicited events for secondary port */
+ if (secondary) {
+ mm_obj_dbg (self, "enabling messaging unsolicited events on secondary port %s",
+ mm_port_get_device (MM_PORT (secondary)));
+ mm_base_modem_at_sequence_full (
+ MM_BASE_MODEM (self),
+ secondary,
+ cnmi_sequence,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ NULL,
+ (GAsyncReadyCallback)modem_messaging_enable_unsolicited_events_secondary_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
modem_messaging_enable_unsolicited_events (MMIfaceModemMessaging *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_base_modem_at_sequence (
+ GTask *task;
+ MMPortSerialAt *primary;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ primary = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+
+ /* Enable unsolicited events for primary port */
+ mm_obj_dbg (self, "enabling messaging unsolicited events on primary port %s",
+ mm_port_get_device (MM_PORT (primary)));
+ mm_base_modem_at_sequence_full (
MM_BASE_MODEM (self),
+ primary,
cnmi_sequence,
NULL, /* response_processor_context */
NULL, /* response_processor_context_free */
- callback,
- user_data);
+ NULL,
+ (GAsyncReadyCallback)modem_messaging_enable_unsolicited_events_primary_ready,
+ task);
}
/*****************************************************************************/
/* Load initial list of SMS parts (Messaging interface) */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMSmsStorage list_storage;
} ListPartsContext;
-static void
-list_parts_context_complete_and_free (ListPartsContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gboolean
modem_messaging_load_initial_sms_parts_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static MMSmsState
@@ -5956,8 +7343,9 @@ sms_pdu_type_from_str (const gchar *str)
static void
sms_text_part_list_ready (MMBroadbandModem *self,
GAsyncResult *res,
- ListPartsContext *ctx)
+ GTask *task)
{
+ ListPartsContext *ctx;
GRegex *r;
GMatchInfo *match_info = NULL;
const gchar *response;
@@ -5965,8 +7353,8 @@ sms_text_part_list_ready (MMBroadbandModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- list_parts_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -5975,84 +7363,96 @@ sms_text_part_list_ready (MMBroadbandModem *self,
0, 0, NULL);
g_assert (r);
- if (!g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, NULL)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't parse SMS list response");
- list_parts_context_complete_and_free (ctx);
+ if (!g_regex_match (r, response, 0, &match_info)) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't parse SMS list response");
+ g_object_unref (task);
g_match_info_free (match_info);
g_regex_unref (r);
return;
}
+ ctx = g_task_get_task_data (task);
+
while (g_match_info_matches (match_info)) {
- MMSmsPart *part;
- guint matches, idx;
- gchar *number, *timestamp, *text, *ucs2_text, *stat;
- gsize ucs2_len = 0;
- GByteArray *raw;
+ MMSmsPart *part;
+ guint matches;
+ guint idx;
+ g_autofree gchar *number_enc = NULL;
+ g_autofree gchar *number = NULL;
+ g_autofree gchar *timestamp = NULL;
+ g_autofree gchar *text_enc = NULL;
+ g_autofree gchar *text = NULL;
+ g_autofree gchar *stat = NULL;
+ g_autoptr(GByteArray) raw = NULL;
+ g_autoptr(GError) inner_error = NULL;
matches = g_match_info_get_match_count (match_info);
if (matches != 7) {
- mm_dbg ("Failed to match entire CMGL response (count %d)", matches);
+ mm_obj_dbg (self, "failed to match entire CMGL response (count %d)", matches);
goto next;
}
if (!mm_get_uint_from_match_info (match_info, 1, &idx)) {
- mm_dbg ("Failed to convert message index");
+ mm_obj_dbg (self, "failed to convert message index");
goto next;
}
/* Get part state */
stat = mm_get_string_unquoted_from_match_info (match_info, 2);
if (!stat) {
- mm_dbg ("Failed to get part status");
+ mm_obj_dbg (self, "failed to get part status");
goto next;
}
/* Get and parse number */
- number = mm_get_string_unquoted_from_match_info (match_info, 3);
+ number_enc = mm_get_string_unquoted_from_match_info (match_info, 3);
+ if (!number_enc) {
+ mm_obj_dbg (self, "failed to get message sender number");
+ goto next;
+ }
+ number = mm_modem_charset_str_to_utf8 (number_enc, -1, self->priv->modem_current_charset, FALSE, &inner_error);
if (!number) {
- mm_dbg ("Failed to get message sender number");
- g_free (stat);
+ mm_obj_dbg (self, "failed to convert message sender number to UTF-8: %s", inner_error->message);
goto next;
}
- number = mm_broadband_modem_take_and_convert_to_utf8 (MM_BROADBAND_MODEM (self),
- number);
-
/* Get and parse timestamp (always expected in ASCII) */
timestamp = mm_get_string_unquoted_from_match_info (match_info, 5);
+ if (timestamp && !g_str_is_ascii (timestamp)) {
+ mm_obj_dbg (self, "failed to parse input timestamp as ASCII");
+ goto next;
+ }
/* Get and parse text */
- text = mm_broadband_modem_take_and_convert_to_utf8 (MM_BROADBAND_MODEM (self),
- g_match_info_fetch (match_info, 6));
+ text_enc = g_match_info_fetch (match_info, 6);
+ text = mm_modem_charset_str_to_utf8 (text_enc, -1, self->priv->modem_current_charset, FALSE, &inner_error);
+ if (!text) {
+ mm_obj_dbg (self, "failed to convert message text to UTF-8: %s", inner_error->message);
+ goto next;
+ }
/* The raw SMS data can only be GSM, UCS2, or unknown (8-bit), so we
* need to convert to UCS2 here.
*/
- ucs2_text = g_convert (text, -1, "UCS-2BE//TRANSLIT", "UTF-8", NULL, &ucs2_len, NULL);
- g_assert (ucs2_text);
- raw = g_byte_array_sized_new (ucs2_len);
- g_byte_array_append (raw, (const guint8 *) ucs2_text, ucs2_len);
- g_free (ucs2_text);
+ raw = mm_modem_charset_bytearray_from_utf8 (text, MM_MODEM_CHARSET_UCS2, FALSE, NULL);
+ g_assert (raw);
/* all take() methods pass ownership of the value as well */
- part = mm_sms_part_new (idx,
- sms_pdu_type_from_str (stat));
- mm_sms_part_take_number (part, number);
- mm_sms_part_take_timestamp (part, timestamp);
- mm_sms_part_take_text (part, text);
- mm_sms_part_take_data (part, raw);
+ part = mm_sms_part_new (idx, sms_pdu_type_from_str (stat));
+ mm_sms_part_take_number (part, g_steal_pointer (&number));
+ mm_sms_part_take_timestamp (part, g_steal_pointer (&timestamp));
+ mm_sms_part_take_text (part, g_steal_pointer (&text));
+ mm_sms_part_take_data (part, g_steal_pointer (&raw));
mm_sms_part_set_class (part, -1);
- mm_dbg ("Correctly parsed SMS list entry (%d)", idx);
+ mm_obj_dbg (self, "correctly parsed SMS list entry (%d)", idx);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
part,
sms_state_from_str (stat),
ctx->list_storage);
- g_free (stat);
next:
g_match_info_next (match_info, NULL);
}
@@ -6060,8 +7460,8 @@ next:
g_regex_unref (r);
/* We consider all done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- list_parts_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static MMSmsState
@@ -6084,8 +7484,9 @@ sms_state_from_index (guint index)
static void
sms_pdu_part_list_ready (MMBroadbandModem *self,
GAsyncResult *res,
- ListPartsContext *ctx)
+ GTask *task)
{
+ ListPartsContext *ctx;
const gchar *response;
GError *error = NULL;
GList *info_list;
@@ -6096,32 +7497,34 @@ sms_pdu_part_list_ready (MMBroadbandModem *self,
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- list_parts_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
info_list = mm_3gpp_parse_pdu_cmgl_response (response, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- list_parts_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
for (l = info_list; l; l = g_list_next (l)) {
MM3gppPduInfo *info = l->data;
MMSmsPart *part;
- part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (info->index, info->pdu, self, &error);
if (part) {
- mm_dbg ("Correctly parsed PDU (%d)", info->index);
+ mm_obj_dbg (self, "correctly parsed PDU (%d)", info->index);
mm_iface_modem_messaging_take_part (MM_IFACE_MODEM_MESSAGING (self),
part,
sms_state_from_index (info->status),
ctx->list_storage);
} else {
/* Don't treat the error as critical */
- mm_dbg ("Error parsing PDU (%d): %s", info->index, error->message);
+ mm_obj_dbg (self, "error parsing PDU (%d): %s", info->index, error->message);
g_clear_error (&error);
}
}
@@ -6129,22 +7532,22 @@ sms_pdu_part_list_ready (MMBroadbandModem *self,
mm_3gpp_pdu_info_list_free (info_list);
/* We consider all done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- list_parts_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
list_parts_lock_storages_ready (MMBroadbandModem *self,
GAsyncResult *res,
- ListPartsContext *ctx)
+ GTask *task)
{
GError *error = NULL;
if (!mm_broadband_modem_lock_sms_storages_finish (self, res, &error)) {
/* TODO: we should either make this lock() never fail, by automatically
* retrying after some time, or otherwise retry here. */
- g_simple_async_result_take_error (ctx->result, error);
- list_parts_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -6161,7 +7564,7 @@ list_parts_lock_storages_ready (MMBroadbandModem *self,
(GAsyncReadyCallback) (MM_BROADBAND_MODEM (self)->priv->modem_messaging_sms_pdu_mode ?
sms_pdu_part_list_ready :
sms_text_part_list_ready),
- ctx);
+ task);
}
static void
@@ -6171,24 +7574,22 @@ modem_messaging_load_initial_sms_parts (MMIfaceModemMessaging *self,
gpointer user_data)
{
ListPartsContext *ctx;
+ GTask *task;
- ctx = g_new0 (ListPartsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_messaging_load_initial_sms_parts);
+ ctx = g_new (ListPartsContext, 1);
ctx->list_storage = storage;
- mm_dbg ("Listing SMS parts in storage '%s'",
- mm_sms_storage_get_string (storage));
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ mm_obj_dbg (self, "listing SMS parts in storage '%s'", mm_sms_storage_get_string (storage));
/* First, request to set the proper storage to read from */
- mm_broadband_modem_lock_sms_storages (ctx->self,
+ mm_broadband_modem_lock_sms_storages (MM_BROADBAND_MODEM (self),
storage,
MM_SMS_STORAGE_UNKNOWN,
(GAsyncReadyCallback)list_parts_lock_storages_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -6201,6 +7602,937 @@ modem_messaging_create_sms (MMIfaceModemMessaging *self)
}
/*****************************************************************************/
+/* Check if Voice supported (Voice interface) */
+
+static gboolean
+modem_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ignore_sim_related_errors (GError **error)
+{
+ g_assert (error && *error);
+ 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_PIN) ||
+ g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK) ||
+ 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_BUSY) ||
+ g_error_matches (*error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
+ g_clear_error (error);
+ }
+}
+
+static void
+clcc_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ /* +CLCC supported unless we got any error response */
+ self->priv->clcc_supported = !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL);
+
+ /* If +CLCC unsupported we disable polling in the parent directly */
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, !self->priv->clcc_supported,
+ NULL);
+
+ /* ATH command is supported; assume we have full voice capabilities */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+ath_format_check_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ /* Ignore some errors that the module may return when there is no SIM inserted or
+ * if the SIM is PIN-locked. We do need the voice interface exposed even in those
+ * cases, in order to support emergency calls */
+ ignore_sim_related_errors (&error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ /* Also check if +CLCC is supported */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CLCC=?",
+ 3,
+ /* Do NOT cache as the reply may be different if PIN locked
+ * or unlocked. E.g. we may not support +CLCC for emergency
+ * voice calls. */
+ FALSE,
+ (GAsyncReadyCallback)clcc_format_check_ready,
+ task);
+}
+
+static void
+modem_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* We assume that all modems have voice capabilities, but ... */
+
+ /* Check ATH support */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "H",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)ath_format_check_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Load full list of calls (Voice interface) */
+
+static gboolean
+modem_voice_load_call_list_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GList **out_call_info_list,
+ GError **error)
+{
+ GList *call_info_list;
+ GError *inner_error = NULL;
+
+ call_info_list = g_task_propagate_pointer (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_assert (!call_info_list);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *out_call_info_list = call_info_list;
+ return TRUE;
+}
+
+static void
+clcc_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ const gchar *response;
+ GError *error = NULL;
+ GList *call_info_list = NULL;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_3gpp_parse_clcc_response (response, self, &call_info_list, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, call_info_list, (GDestroyNotify)mm_3gpp_call_info_list_free);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_load_call_list (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CLCC",
+ 5,
+ FALSE,
+ (GAsyncReadyCallback)clcc_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup/cleanup voice related in-call unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_setup_cleanup_in_call_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+in_call_event_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMBroadbandModem *self)
+{
+ MMCallInfo call_info;
+ gchar *str;
+
+ call_info.index = 0;
+ call_info.direction = MM_CALL_DIRECTION_UNKNOWN;
+ call_info.state = MM_CALL_STATE_TERMINATED;
+ call_info.number = NULL;
+
+ str = g_match_info_fetch (info, 1);
+ mm_obj_dbg (self, "call terminated: %s", str);
+ g_free (str);
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+set_voice_in_call_unsolicited_events_handlers (MMBroadbandModem *self,
+ PortsContext *ports_ctx,
+ gboolean enable)
+{
+ MMPortSerialAt *ports[2];
+ GRegex *in_call_event_regex;
+ guint i;
+
+ in_call_event_regex = g_regex_new ("\\r\\n(NO CARRIER|BUSY|NO ANSWER|NO DIALTONE)(\\r)?\\r\\n$",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
+ ports[0] = MM_PORT_SERIAL_AT (ports_ctx->primary);
+ ports[1] = MM_PORT_SERIAL_AT (ports_ctx->secondary);
+
+ /* Enable unsolicited events in given port */
+ for (i = 0; i < 2; i++) {
+ if (!ports[i])
+ continue;
+
+ mm_obj_dbg (self, "%s voice in-call unsolicited events handlers in %s",
+ enable ? "setting" : "removing",
+ mm_port_get_device (MM_PORT (ports[i])));
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ in_call_event_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn) in_call_event_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+
+ g_regex_unref (in_call_event_regex);
+}
+
+static void
+modem_voice_setup_in_call_unsolicited_events (MMIfaceModemVoice *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModem *self;
+ GTask *task;
+ GError *error = NULL;
+
+ self = MM_BROADBAND_MODEM (_self);
+ if (!self->priv->in_call_ports_ctx) {
+ PortsContext *ctx;
+
+ mm_obj_dbg (self, "setting up in-call ports context");
+ ctx = ports_context_new ();
+ if (!ports_context_open (self, ctx, FALSE, TRUE, FALSE, &error)) {
+ ports_context_unref (ctx);
+ g_prefix_error (&error, "Couldn't open ports in-call: ");
+ } else {
+ set_voice_in_call_unsolicited_events_handlers (self, ctx, TRUE);
+ self->priv->in_call_ports_ctx = ctx;
+ }
+ } else
+ mm_obj_dbg (self, "in-call ports context already set up");
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_cleanup_in_call_unsolicited_events (MMIfaceModemVoice *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModem *self;
+ GTask *task;
+
+ self = MM_BROADBAND_MODEM (_self);
+ if (self->priv->in_call_ports_ctx) {
+ mm_obj_dbg (self, "cleaning up in-call ports context");
+ set_voice_in_call_unsolicited_events_handlers (self, self->priv->in_call_ports_ctx, FALSE);
+ g_clear_pointer (&self->priv->in_call_ports_ctx, (GDestroyNotify) ports_context_unref);
+ } else
+ mm_obj_dbg (self, "in-call ports context already cleaned up");
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Setup/cleanup voice related unsolicited events (Voice interface) */
+
+static gboolean
+modem_voice_setup_cleanup_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+ccwa_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMBroadbandModem *self)
+{
+ MMCallInfo call_info;
+ gboolean indication_call_list_reload_enabled = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled,
+ NULL);
+
+ if (indication_call_list_reload_enabled) {
+ mm_obj_dbg (self, "call waiting, refreshing call list");
+ mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL);
+ return;
+ }
+
+ call_info.index = 0;
+ call_info.direction = MM_CALL_DIRECTION_INCOMING;
+ call_info.state = MM_CALL_STATE_WAITING;
+ call_info.number = mm_get_string_unquoted_from_match_info (info, 1);
+
+ mm_obj_dbg (self, "call waiting (%s)", call_info.number);
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+
+ g_free (call_info.number);
+}
+
+static void
+ring_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMBroadbandModem *self)
+{
+ MMCallInfo call_info;
+ gboolean indication_call_list_reload_enabled = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled,
+ NULL);
+
+ if (indication_call_list_reload_enabled) {
+ mm_obj_dbg (self, "ringing, refreshing call list");
+ mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL);
+ return;
+ }
+
+ call_info.index = 0;
+ call_info.direction = MM_CALL_DIRECTION_INCOMING;
+ call_info.state = MM_CALL_STATE_RINGING_IN;
+ call_info.number = NULL;
+
+ mm_obj_dbg (self, "ringing");
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+cring_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMBroadbandModem *self)
+{
+ MMCallInfo call_info;
+ gchar *str;
+ gboolean indication_call_list_reload_enabled = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled,
+ NULL);
+
+ if (indication_call_list_reload_enabled) {
+ mm_obj_dbg (self, "ringing, refreshing call list");
+ mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL);
+ return;
+ }
+
+ /* We could have "VOICE" or "DATA". Now consider only "VOICE" */
+ str = mm_get_string_unquoted_from_match_info (info, 1);
+ mm_obj_dbg (self, "ringing (%s)", str);
+ g_free (str);
+
+ call_info.index = 0;
+ call_info.direction = MM_CALL_DIRECTION_INCOMING;
+ call_info.state = MM_CALL_STATE_RINGING_IN;
+ call_info.number = NULL;
+
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+}
+
+static void
+clip_received (MMPortSerialAt *port,
+ GMatchInfo *info,
+ MMBroadbandModem *self)
+{
+ MMCallInfo call_info;
+ gboolean indication_call_list_reload_enabled = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED, &indication_call_list_reload_enabled,
+ NULL);
+
+ if (indication_call_list_reload_enabled) {
+ mm_obj_dbg (self, "ringing, refreshing call list");
+ mm_iface_modem_voice_reload_all_calls (MM_IFACE_MODEM_VOICE (self), NULL, NULL);
+ return;
+ }
+
+ call_info.index = 0;
+ call_info.direction = MM_CALL_DIRECTION_INCOMING;
+ call_info.state = MM_CALL_STATE_RINGING_IN;
+ call_info.number = mm_get_string_unquoted_from_match_info (info, 1);
+
+ mm_obj_dbg (self, "ringing (%s)", call_info.number);
+ mm_iface_modem_voice_report_call (MM_IFACE_MODEM_VOICE (self), &call_info);
+
+ g_free (call_info.number);
+}
+
+static void
+set_voice_unsolicited_events_handlers (MMIfaceModemVoice *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMPortSerialAt *ports[2];
+ GRegex *cring_regex;
+ GRegex *ring_regex;
+ GRegex *clip_regex;
+ GRegex *ccwa_regex;
+ guint i;
+ GTask *task;
+
+ cring_regex = mm_voice_cring_regex_get ();
+ ring_regex = mm_voice_ring_regex_get ();
+ clip_regex = mm_voice_clip_regex_get ();
+ ccwa_regex = mm_voice_ccwa_regex_get ();
+ ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
+ ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
+
+ /* Enable unsolicited events in given port */
+ for (i = 0; i < 2; i++) {
+ if (!ports[i])
+ continue;
+
+ /* Set/unset unsolicited CMTI event handler */
+ mm_obj_dbg (self, "%s voice unsolicited events handlers in %s",
+ enable ? "setting" : "removing",
+ mm_port_get_device (MM_PORT (ports[i])));
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ cring_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn) cring_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ ring_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn) ring_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ clip_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn) clip_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ ccwa_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn) ccwa_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+
+ g_regex_unref (ccwa_regex);
+ g_regex_unref (clip_regex);
+ g_regex_unref (cring_regex);
+ g_regex_unref (ring_regex);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_setup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ set_voice_unsolicited_events_handlers (self, TRUE, callback, user_data);
+}
+
+static void
+modem_voice_cleanup_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ set_voice_unsolicited_events_handlers (self, FALSE, callback, user_data);
+}
+
+/*****************************************************************************/
+/* Enable unsolicited events (CALL indications) (Voice interface) */
+
+typedef struct {
+ gboolean enable;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ gchar *clip_command;
+ gboolean clip_primary_done;
+ gboolean clip_secondary_done;
+ gchar *crc_command;
+ gboolean crc_primary_done;
+ gboolean crc_secondary_done;
+ gchar *ccwa_command;
+ gboolean ccwa_primary_done;
+ gboolean ccwa_secondary_done;
+} VoiceUnsolicitedEventsContext;
+
+static void
+voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx)
+{
+ g_clear_object (&ctx->secondary);
+ g_clear_object (&ctx->primary);
+ g_free (ctx->clip_command);
+ g_free (ctx->crc_command);
+ g_free (ctx->ccwa_command);
+ g_slice_free (VoiceUnsolicitedEventsContext, ctx);
+}
+
+static gboolean
+modem_voice_enable_disable_unsolicited_events_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void run_voice_unsolicited_events_setup (GTask *task);
+
+static void
+voice_unsolicited_events_setup_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) {
+ mm_obj_dbg (self, "couldn't %s voice event reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
+ }
+
+ /* Continue on next port/command */
+ run_voice_unsolicited_events_setup (task);
+}
+
+static void
+run_voice_unsolicited_events_setup (GTask *task)
+{
+ MMBroadbandModem *self;
+ VoiceUnsolicitedEventsContext *ctx;
+ MMPortSerialAt *port = NULL;
+ const gchar *command = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* CLIP on primary port */
+ if (!ctx->clip_primary_done && ctx->clip_command && ctx->primary) {
+ mm_obj_dbg (self, "%s +CLIP calling line reporting in primary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->clip_primary_done = TRUE;
+ command = ctx->clip_command;
+ port = ctx->primary;
+ }
+ /* CLIP on secondary port */
+ else if (!ctx->clip_secondary_done && ctx->clip_command && ctx->secondary) {
+ mm_obj_dbg (self, "%s +CLIP calling line reporting in secondary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->clip_secondary_done = TRUE;
+ command = ctx->clip_command;
+ port = ctx->secondary;
+ }
+ /* CRC on primary port */
+ else if (!ctx->crc_primary_done && ctx->crc_command && ctx->primary) {
+ mm_obj_dbg (self, "%s +CRC extended format of incoming call indications in primary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->crc_primary_done = TRUE;
+ command = ctx->crc_command;
+ port = ctx->primary;
+ }
+ /* CRC on secondary port */
+ else if (!ctx->crc_secondary_done && ctx->crc_command && ctx->secondary) {
+ mm_obj_dbg (self, "%s +CRC extended format of incoming call indications in secondary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->crc_secondary_done = TRUE;
+ command = ctx->crc_command;
+ port = ctx->secondary;
+ }
+ /* CCWA on primary port */
+ else if (!ctx->ccwa_primary_done && ctx->ccwa_command && ctx->primary) {
+ mm_obj_dbg (self, "%s +CCWA call waiting indications in primary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->ccwa_primary_done = TRUE;
+ command = ctx->ccwa_command;
+ port = ctx->primary;
+ }
+ /* CCWA on secondary port */
+ else if (!ctx->ccwa_secondary_done && ctx->ccwa_command && ctx->secondary) {
+ mm_obj_dbg (self, "%s +CCWA call waiting indications in secondary port...", ctx->enable ? "enabling" : "disabling");
+ ctx->ccwa_secondary_done = TRUE;
+ command = ctx->ccwa_command;
+ port = ctx->secondary;
+ }
+
+ /* Enable/Disable unsolicited events in given port */
+ if (port && command) {
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ port,
+ command,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)voice_unsolicited_events_setup_ready,
+ task);
+ return;
+ }
+
+ /* Fully done now */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+}
+
+static void
+modem_voice_enable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (VoiceUnsolicitedEventsContext);
+ ctx->enable = TRUE;
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+
+ /* enable +CLIP URCs with calling line identity */
+ ctx->clip_command = g_strdup ("+CLIP=1");
+ /* enable +CRING URCs instead of plain RING */
+ ctx->crc_command = g_strdup ("+CRC=1");
+ /* enable +CCWA call waiting indications */
+ ctx->ccwa_command = g_strdup ("+CCWA=1");
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free);
+
+ run_voice_unsolicited_events_setup (task);
+}
+
+static void
+modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (VoiceUnsolicitedEventsContext);
+ ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
+ ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
+
+ /* disable +CLIP URCs with calling line identity */
+ ctx->clip_command = g_strdup ("+CLIP=0");
+ /* disable +CRING URCs instead of plain RING */
+ ctx->crc_command = g_strdup ("+CRC=0");
+ /* disable +CCWA call waiting indications */
+ ctx->ccwa_command = g_strdup ("+CCWA=0");
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify) voice_unsolicited_events_context_free);
+
+ run_voice_unsolicited_events_setup (task);
+}
+
+/*****************************************************************************/
+/* Create CALL (Voice interface) */
+
+static MMBaseCall *
+modem_voice_create_call (MMIfaceModemVoice *_self,
+ MMCallDirection direction,
+ const gchar *number)
+{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+
+ return mm_base_call_new (MM_BASE_MODEM (self),
+ direction,
+ number,
+ /* If +CLCC is supported, we want no incoming timeout.
+ * Also, we're able to support detailed call state updates without
+ * additional vendor-specific commands. */
+ self->priv->clcc_supported, /* skip incoming timeout */
+ self->priv->clcc_supported, /* dialing->ringing supported */
+ self->priv->clcc_supported); /* ringing->active supported */
+}
+
+/*****************************************************************************/
+/* Hold and accept (Voice interface) */
+
+static gboolean
+modem_voice_hold_and_accept_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_voice_hold_and_accept (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CHLD=2",
+ 20,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Hangup and accept (Voice interface) */
+
+static gboolean
+modem_voice_hangup_and_accept_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_voice_hangup_and_accept (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CHLD=1",
+ 20,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Hangup all (Voice interface) */
+
+static gboolean
+modem_voice_hangup_all_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_voice_hangup_all (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CHUP",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Join multiparty (Voice interface) */
+
+static gboolean
+modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_voice_join_multiparty (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CHLD=3",
+ 20,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Leave multiparty (Voice interface) */
+
+static gboolean
+modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+chld_leave_multiparty_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_voice_leave_multiparty (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ guint idx;
+ gchar *cmd;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ idx = mm_base_call_get_index (call);
+ if (!idx) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "unknown call index");
+ g_object_unref (task);
+ return;
+ }
+
+ cmd = g_strdup_printf ("+CHLD=2%u", idx);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 20,
+ FALSE,
+ (GAsyncReadyCallback) chld_leave_multiparty_ready,
+ task);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+/* Transfer (Voice interface) */
+
+static gboolean
+modem_voice_transfer_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_voice_transfer (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CHLD=4",
+ 20,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Call waiting setup (Voice interface) */
+
+static gboolean
+modem_voice_call_waiting_setup_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_voice_call_waiting_setup (MMIfaceModemVoice *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ gchar *cmd;
+
+ /* Enabling or disabling the call waiting service will only be allowed when
+ * the modem is registered in the network, and so, CCWA URC handling will
+ * always be setup at this point (as it's part of the modem enabling phase).
+ * So, just enable or disable the service (second field) but leaving URCs
+ * (first field) always enabled. */
+ cmd = g_strdup_printf ("+CCWA=1,%u", enable);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 60,
+ FALSE,
+ callback,
+ user_data);
+ g_free (cmd);
+}
+
+/*****************************************************************************/
+/* Call waiting query (Voice interface) */
+
+static gboolean
+modem_voice_call_waiting_query_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ gboolean *status,
+ GError **error)
+{
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return FALSE;
+
+ return mm_3gpp_parse_ccwa_service_query_response (response, self, status, error);
+}
+
+static void
+modem_voice_call_waiting_query (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ /* This operation will only be allowed while enabled, and so, CCWA URC
+ * handling would always be enabled at this point. So, just perform the
+ * query, but leaving URCs enabled either way. */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CCWA=1,2",
+ 60,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
/* ESN loading (CDMA interface) */
static gchar *
@@ -6217,7 +8549,7 @@ modem_cdma_load_esn_finish (MMIfaceModemCdma *self,
result = mm_strip_tag (result, "+GSN:");
mm_parse_gsn (result, NULL, NULL, &esn);
- mm_dbg ("loaded ESN: %s", esn);
+ mm_obj_dbg (self, "loaded ESN: %s", esn);
return esn;
}
@@ -6226,7 +8558,7 @@ modem_cdma_load_esn (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- mm_dbg ("loading ESN...");
+ mm_obj_dbg (self, "loading ESN...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+GSN",
3,
@@ -6252,7 +8584,7 @@ modem_cdma_load_meid_finish (MMIfaceModemCdma *self,
result = mm_strip_tag (result, "+GSN:");
mm_parse_gsn (result, NULL, &meid, NULL);
- mm_dbg ("loaded MEID: %s", meid);
+ mm_obj_dbg (self, "loaded MEID: %s", meid);
return meid;
}
@@ -6262,7 +8594,7 @@ modem_cdma_load_meid (MMIfaceModemCdma *self,
gpointer user_data)
{
/* Some devices return both the MEID and the ESN in the +GSN response */
- mm_dbg ("loading MEID...");
+ mm_obj_dbg (self, "loading MEID...");
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+GSN",
3,
@@ -6272,6 +8604,168 @@ modem_cdma_load_meid (MMIfaceModemCdma *self,
}
/*****************************************************************************/
+/* Setup/Cleanup unsolicited events (CDMA interface) */
+
+typedef struct {
+ gboolean setup;
+ MMPortSerialQcdm *qcdm;
+ gboolean close_port;
+} CdmaUnsolicitedEventsContext;
+
+static void
+cdma_unsolicited_events_context_free (CdmaUnsolicitedEventsContext *ctx)
+{
+ if (ctx->qcdm && ctx->close_port)
+ mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
+ g_clear_object (&ctx->qcdm);
+
+ g_free (ctx);
+}
+
+static void
+logcmd_qcdm_ready (MMPortSerialQcdm *port,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModem *self;
+ CdmaUnsolicitedEventsContext *ctx;
+ QcdmResult *result;
+ gint err = QCDM_SUCCESS;
+ GByteArray *response;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ response = mm_port_serial_qcdm_command_finish (port, res, &error);
+ if (error) {
+ ctx->close_port = TRUE;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Parse the response */
+ result = qcdm_cmd_log_config_set_mask_result ((const gchar *) response->data,
+ response->len,
+ &err);
+ g_byte_array_unref (response);
+ if (!result) {
+ ctx->close_port = TRUE;
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse Log Config Set Mask command result: %d",
+ err);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_port_serial_qcdm_add_unsolicited_msg_handler (port,
+ DM_LOG_ITEM_EVDO_PILOT_SETS_V2,
+ ctx->setup ? qcdm_evdo_pilot_sets_log_handle : NULL,
+ self,
+ NULL);
+
+ qcdm_result_unref (result);
+
+ /* Balance the mm_port_seral_open() from modem_cdma_setup_cleanup_unsolicited_events().
+ * We want to close it in either case:
+ * (a) we're cleaning up and setup opened the port
+ * (b) if it was unexpectedly closed before cleanup and thus cleanup opened it
+ *
+ * Setup should leave the port open to allow log messages to be received
+ * and sent to handlers.
+ */
+ ctx->close_port = ctx->setup ? FALSE : TRUE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_cdma_setup_cleanup_unsolicited_events (MMBroadbandModem *self,
+ gboolean setup,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CdmaUnsolicitedEventsContext *ctx;
+ GTask *task;
+ GByteArray *logcmd;
+ uint16_t log_items[] = { DM_LOG_ITEM_EVDO_PILOT_SETS_V2, 0 };
+ GError *error = NULL;
+
+ ctx = g_new0 (CdmaUnsolicitedEventsContext, 1);
+ ctx->setup = TRUE;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)cdma_unsolicited_events_context_free);
+
+ ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
+ if (!ctx->qcdm) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Setup must open the QCDM port and keep it open to receive unsolicited
+ * events. Cleanup expects the port to already be opened from setup, but
+ * if not we still want to open it and try to disable log messages.
+ */
+ if (setup || !mm_port_serial_is_open (MM_PORT_SERIAL (ctx->qcdm))) {
+ if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), &error)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ logcmd = g_byte_array_sized_new (512);
+ logcmd->len = qcdm_cmd_log_config_set_mask_new ((char *) logcmd->data,
+ 512,
+ 0x01, /* Equipment ID */
+ setup ? log_items : NULL);
+ assert (logcmd->len);
+
+ mm_port_serial_qcdm_command (ctx->qcdm,
+ logcmd,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)logcmd_qcdm_ready,
+ task);
+ g_byte_array_unref (logcmd);
+}
+
+static gboolean
+modem_cdma_setup_cleanup_unsolicited_events_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_cdma_setup_unsolicited_events (MMIfaceModemCdma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ modem_cdma_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM (self),
+ TRUE,
+ callback,
+ user_data);
+}
+
+static void
+modem_cdma_cleanup_unsolicited_events (MMIfaceModemCdma *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ modem_cdma_setup_cleanup_unsolicited_events (MM_BROADBAND_MODEM (self),
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
/* HDR state check (CDMA interface) */
typedef struct {
@@ -6280,20 +8774,11 @@ typedef struct {
guint8 almp_state;
} HdrStateResults;
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- MMPortSerialQcdm *qcdm;
-} HdrStateContext;
-
static void
-hdr_state_context_complete_and_free (HdrStateContext *ctx)
+hdr_state_cleanup_port (MMPortSerial *port)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->qcdm);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
+ mm_port_serial_close (port);
+ g_object_unref (port);
}
static gboolean
@@ -6306,20 +8791,22 @@ modem_cdma_get_hdr_state_finish (MMIfaceModemCdma *self,
{
HdrStateResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*hybrid_mode = results->hybrid_mode;
*session_state = results->session_state;
*almp_state = results->almp_state;
+ g_free (results);
+
return TRUE;
}
static void
hdr_subsys_state_info_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- HdrStateContext *ctx)
+ GTask *task)
{
QcdmResult *result;
HdrStateResults *results;
@@ -6329,8 +8816,8 @@ hdr_subsys_state_info_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- g_simple_async_result_set_from_error (ctx->result, error);
- hdr_state_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -6338,13 +8825,14 @@ hdr_subsys_state_info_ready (MMPortSerialQcdm *port,
result = qcdm_cmd_hdr_subsys_state_info_result ((const gchar *) response->data,
response->len,
&err);
+ g_byte_array_unref (response);
if (!result) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse HDR subsys state info command result: %d",
- err);
- hdr_state_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse HDR subsys state info command result: %d",
+ err);
+ g_object_unref (task);
return;
}
@@ -6357,8 +8845,8 @@ hdr_subsys_state_info_ready (MMPortSerialQcdm *port,
qcdm_result_get_u8 (result, QCDM_CMD_HDR_SUBSYS_STATE_INFO_ITEM_ALMP_STATE, &results->almp_state);
qcdm_result_unref (result);
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, (GDestroyNotify)g_free);
- hdr_state_context_complete_and_free (ctx);
+ g_task_return_pointer (task, results, g_free);
+ g_object_unref (task);
}
static void
@@ -6367,40 +8855,43 @@ modem_cdma_get_hdr_state (MMIfaceModemCdma *self,
gpointer user_data)
{
MMPortSerialQcdm *qcdm;
- HdrStateContext *ctx;
+ GTask *task;
GByteArray *hdrstate;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
if (!qcdm) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot get HDR state without a QCDM port");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot get HDR state without a QCDM port");
+ g_object_unref (task);
return;
}
- /* Setup context */
- ctx = g_new0 (HdrStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_get_hdr_state);
- ctx->qcdm = g_object_ref (qcdm);
+ if (!mm_port_serial_open (MM_PORT_SERIAL (qcdm), &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_set_task_data (task,
+ g_object_ref (qcdm),
+ (GDestroyNotify) hdr_state_cleanup_port);
/* Setup command */
hdrstate = g_byte_array_sized_new (25);
hdrstate->len = qcdm_cmd_hdr_subsys_state_info_new ((gchar *) hdrstate->data, 25);
g_assert (hdrstate->len);
- mm_port_serial_qcdm_command (ctx->qcdm,
+ mm_port_serial_qcdm_command (qcdm,
hdrstate,
3,
NULL,
(GAsyncReadyCallback)hdr_subsys_state_info_ready,
- ctx);
+ task);
g_byte_array_unref (hdrstate);
}
@@ -6412,20 +8903,11 @@ typedef struct {
guint operating_mode;
} CallManagerStateResults;
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- MMPortSerialQcdm *qcdm;
-} CallManagerStateContext;
-
static void
-call_manager_state_context_complete_and_free (CallManagerStateContext *ctx)
+cm_state_cleanup_port (MMPortSerial *port)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->qcdm);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
+ mm_port_serial_close (port);
+ g_object_unref (port);
}
static gboolean
@@ -6435,21 +8917,23 @@ modem_cdma_get_call_manager_state_finish (MMIfaceModemCdma *self,
guint *operating_mode,
GError **error)
{
- CallManagerStateResults *results;
+ CallManagerStateResults *result;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- *system_mode = results->system_mode;
- *operating_mode = results->operating_mode;
+ *system_mode = result->system_mode;
+ *operating_mode = result->operating_mode;
+ g_free (result);
+
return TRUE;
}
static void
cm_subsys_state_info_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- CallManagerStateContext *ctx)
+ GTask *task)
{
QcdmResult *result;
CallManagerStateResults *results;
@@ -6459,8 +8943,8 @@ cm_subsys_state_info_ready (MMPortSerialQcdm *port,
response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- call_manager_state_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -6470,12 +8954,12 @@ cm_subsys_state_info_ready (MMPortSerialQcdm *port,
&err);
g_byte_array_unref (response);
if (!result) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse CM subsys state info command result: %d",
- err);
- call_manager_state_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse CM subsys state info command result: %d",
+ err);
+ g_object_unref (task);
return;
}
@@ -6485,8 +8969,8 @@ cm_subsys_state_info_ready (MMPortSerialQcdm *port,
qcdm_result_get_u32 (result, QCDM_CMD_CM_SUBSYS_STATE_INFO_ITEM_SYSTEM_MODE, &results->system_mode);
qcdm_result_unref (result);
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, (GDestroyNotify)g_free);
- call_manager_state_context_complete_and_free (ctx);
+ g_task_return_pointer (task, results, g_free);
+ g_object_unref (task);
}
static void
@@ -6495,40 +8979,43 @@ modem_cdma_get_call_manager_state (MMIfaceModemCdma *self,
gpointer user_data)
{
MMPortSerialQcdm *qcdm;
- CallManagerStateContext *ctx;
+ GTask *task;
GByteArray *cmstate;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
if (!qcdm) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot get call manager state without a QCDM port");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot get call manager state without a QCDM port");
+ g_object_unref (task);
return;
}
- /* Setup context */
- ctx = g_new0 (CallManagerStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_get_call_manager_state);
- ctx->qcdm = g_object_ref (qcdm);
+ if (!mm_port_serial_open (MM_PORT_SERIAL (qcdm), &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_set_task_data (task,
+ g_object_ref (qcdm),
+ (GDestroyNotify) cm_state_cleanup_port);
/* Setup command */
cmstate = g_byte_array_sized_new (25);
cmstate->len = qcdm_cmd_cm_subsys_state_info_new ((gchar *) cmstate->data, 25);
g_assert (cmstate->len);
- mm_port_serial_qcdm_command (ctx->qcdm,
+ mm_port_serial_qcdm_command (qcdm,
cmstate,
3,
NULL,
(GAsyncReadyCallback)cm_subsys_state_info_ready,
- ctx);
+ task);
g_byte_array_unref (cmstate);
}
@@ -6542,21 +9029,30 @@ typedef struct {
guint band;
} Cdma1xServingSystemResults;
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- MMPortSerialQcdm *qcdm;
-} Cdma1xServingSystemContext;
+static void
+cdma1x_serving_system_state_cleanup_port (MMPortSerial *port)
+{
+ mm_port_serial_close (port);
+ g_object_unref (port);
+}
static void
-cdma1x_serving_system_context_complete_and_free (Cdma1xServingSystemContext *ctx)
+cdma1x_serving_system_complete_and_free (GTask *task,
+ guint sid,
+ guint nid,
+ guint class,
+ guint band)
{
- g_simple_async_result_complete (ctx->result);
- if (ctx->qcdm)
- g_object_unref (ctx->qcdm);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
+ Cdma1xServingSystemResults *results;
+
+ results = g_new0 (Cdma1xServingSystemResults, 1);
+ results->sid = sid;
+ results->band = band;
+ results->class = class;
+ results->nid = nid;
+
+ g_task_return_pointer (task, results, g_free);
+ g_object_unref (task);
}
static GError *
@@ -6580,21 +9076,23 @@ modem_cdma_get_cdma1x_serving_system_finish (MMIfaceModemCdma *self,
{
Cdma1xServingSystemResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = (Cdma1xServingSystemResults *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*sid = results->sid;
*nid = results->nid;
*class = results->class;
*band = results->band;
+ g_free (results);
+
return TRUE;
}
static void
css_query_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- Cdma1xServingSystemContext *ctx)
+ GTask *task)
{
GError *error = NULL;
const gchar *result;
@@ -6605,12 +9103,11 @@ css_query_ready (MMIfaceModemCdma *self,
gboolean class_ok = FALSE;
gboolean band_ok = FALSE;
gboolean success = FALSE;
- Cdma1xServingSystemResults *results;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- cdma1x_serving_system_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -6631,15 +9128,7 @@ css_query_ready (MMIfaceModemCdma *self,
/* Format is "<band_class>,<band>,<sid>" */
r = g_regex_new ("\\s*([^,]*?)\\s*,\\s*([^,]*?)\\s*,\\s*(\\d+)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- if (!r) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse Serving System results (regex creation failed).");
- cdma1x_serving_system_context_complete_and_free (ctx);
- return;
- }
+ g_assert (r);
g_regex_match (r, result, 0, &match_info);
if (g_match_info_get_match_count (match_info) >= 3) {
@@ -6672,12 +9161,11 @@ css_query_ready (MMIfaceModemCdma *self,
}
if (!success) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Could not parse Serving System results");
- cdma1x_serving_system_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Could not parse Serving System results");
+ g_object_unref (task);
return;
}
@@ -6701,29 +9189,32 @@ css_query_ready (MMIfaceModemCdma *self,
/* 99999 means unknown/no service */
if (sid == MM_MODEM_CDMA_SID_UNKNOWN) {
- g_simple_async_result_take_error (ctx->result,
- cdma1x_serving_system_no_service_error ());
- cdma1x_serving_system_context_complete_and_free (ctx);
+ g_task_return_error (task, cdma1x_serving_system_no_service_error ());
+ g_object_unref (task);
return;
}
- results = g_new0 (Cdma1xServingSystemResults, 1);
- results->sid = sid;
- results->band = band;
- results->class = class;
/* No means to get NID with AT commands right now */
- results->nid = MM_MODEM_CDMA_NID_UNKNOWN;
+ cdma1x_serving_system_complete_and_free (task, sid, MM_MODEM_CDMA_NID_UNKNOWN, class, band);
+}
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, (GDestroyNotify)g_free);
- cdma1x_serving_system_context_complete_and_free (ctx);
+static void
+serving_system_query_css (GTask *task)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (g_task_get_source_object (task)),
+ "+CSS?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)css_query_ready,
+ task);
}
static void
qcdm_cdma_status_ready (MMPortSerialQcdm *port,
GAsyncResult *res,
- Cdma1xServingSystemContext *ctx)
+ GTask *task)
{
- Cdma1xServingSystemResults *results;
+ MMBroadbandModem *self;
QcdmResult *result = NULL;
guint32 sid = MM_MODEM_CDMA_SID_UNKNOWN;
guint32 nid = MM_MODEM_CDMA_NID_UNKNOWN;
@@ -6732,24 +9223,28 @@ qcdm_cdma_status_ready (MMPortSerialQcdm *port,
GError *error = NULL;
GByteArray *response;
+ self = g_task_get_source_object (task);
+
response = mm_port_serial_qcdm_command_finish (port, res, &error);
- if (error ||
- (result = qcdm_cmd_cdma_status_result ((const gchar *) response->data,
- response->len,
- &err)) == NULL) {
+ if (error) {
+ mm_obj_dbg (self, "failed to get cdma status: %s", error->message);
+ g_clear_error (&error);
+
+ /* Fall back to AT+CSS */
+ serving_system_query_css (task);
+ return;
+ }
+
+ result = qcdm_cmd_cdma_status_result ((const gchar *) response->data,
+ response->len,
+ &err);
+ if (!result) {
if (err != QCDM_SUCCESS)
- mm_dbg ("Failed to parse cdma status command result: %d", err);
- /* If there was some error, fall back to use +CSS like we did before QCDM */
- mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
- "+CSS?",
- 3,
- FALSE,
- (GAsyncReadyCallback)css_query_ready,
- ctx);
- if (error)
- g_error_free (error);
- if (response)
- g_byte_array_unref (response);
+ mm_obj_dbg (self, "failed to parse cdma status command result: %d", err);
+ g_byte_array_unref (response);
+
+ /* Fall back to AT+CSS */
+ serving_system_query_css (task);
return;
}
@@ -6766,20 +9261,15 @@ qcdm_cdma_status_ready (MMPortSerialQcdm *port,
nid = MM_MODEM_CDMA_NID_UNKNOWN;
}
- mm_dbg ("CDMA 1x Status RX state: %d", rxstate);
- mm_dbg ("CDMA 1x Status SID: %d", sid);
- mm_dbg ("CDMA 1x Status NID: %d", nid);
-
- results = g_new0 (Cdma1xServingSystemResults, 1);
- results->sid = sid;
- results->nid = nid;
- if (sid != MM_MODEM_CDMA_SID_UNKNOWN) {
- results->band = 'Z';
- results->class = 0;
- }
+ mm_obj_dbg (self, "CDMA 1x Status RX state: %d", rxstate);
+ mm_obj_dbg (self, "CDMA 1x Status SID: %d", sid);
+ mm_obj_dbg (self, "CDMA 1x Status NID: %d", nid);
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, (GDestroyNotify)g_free);
- cdma1x_serving_system_context_complete_and_free (ctx);
+ cdma1x_serving_system_complete_and_free (task,
+ sid,
+ nid,
+ 0,
+ (sid == MM_MODEM_CDMA_SID_UNKNOWN) ? 0 : 'Z');
}
static void
@@ -6787,41 +9277,44 @@ modem_cdma_get_cdma1x_serving_system (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- Cdma1xServingSystemContext *ctx;
+ GError *error = NULL;
+ GByteArray *cdma_status;
+ GTask *task;
+ MMPortSerialQcdm *qcdm;
- /* Setup context */
- ctx = g_new0 (Cdma1xServingSystemContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_get_cdma1x_serving_system);
- ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
+ task = g_task_new (self, NULL, callback, user_data);
- if (ctx->qcdm) {
- GByteArray *cdma_status;
+ qcdm = mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
+ if (!qcdm) {
+ /* Fall back to AT+CSS */
+ serving_system_query_css (task);
+ return;
+ }
+
+ if (!mm_port_serial_open (MM_PORT_SERIAL (qcdm), &error)) {
+ mm_obj_dbg (self, "failed to open QCDM port for serving-system request: %s", error->message);
+ g_error_free (error);
- /* Setup command */
- cdma_status = g_byte_array_sized_new (25);
- cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25);
- g_assert (cdma_status->len);
- mm_port_serial_qcdm_command (ctx->qcdm,
- cdma_status,
- 3,
- NULL,
- (GAsyncReadyCallback)qcdm_cdma_status_ready,
- ctx);
- g_byte_array_unref (cdma_status);
+ /* Fall back to AT+CSS */
+ serving_system_query_css (task);
return;
}
- /* Try with AT if we don't have QCDM */
- mm_base_modem_at_command (MM_BASE_MODEM (self),
- "+CSS?",
- 3,
- FALSE,
- (GAsyncReadyCallback)css_query_ready,
- ctx);
+ g_task_set_task_data (task,
+ g_object_ref (qcdm),
+ (GDestroyNotify) cdma1x_serving_system_state_cleanup_port);
+
+ /* Setup command */
+ cdma_status = g_byte_array_sized_new (25);
+ cdma_status->len = qcdm_cmd_cdma_status_new ((char *) cdma_status->data, 25);
+ g_assert (cdma_status->len);
+ mm_port_serial_qcdm_command (qcdm,
+ cdma_status,
+ 3,
+ NULL,
+ (GAsyncReadyCallback) qcdm_cdma_status_ready,
+ task);
+ g_byte_array_unref (cdma_status);
}
/*****************************************************************************/
@@ -6833,42 +9326,46 @@ modem_cdma_get_service_status_finish (MMIfaceModemCdma *self,
gboolean *has_cdma_service,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ GError *inner_error = NULL;
+ gboolean value;
+
+ value = g_task_propagate_boolean (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
return FALSE;
+ }
- *has_cdma_service = g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (res));
+ *has_cdma_service = value;
return TRUE;
}
static void
cad_query_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
const gchar *result;
result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else {
guint cad;
/* Strip any leading command tag and spaces */
result = mm_strip_tag (result, "+CAD:");
if (!mm_get_uint_from_str (result, &cad))
- g_simple_async_result_set_error (simple,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to parse +CAD response '%s'",
- result);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse +CAD response '%s'",
+ result);
else
/* 1 == CDMA service */
- g_simple_async_result_set_op_res_gboolean (simple, (cad == 1));
+ g_task_return_boolean (task, (cad == 1));
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
@@ -6876,19 +9373,16 @@ modem_cdma_get_service_status (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_get_service_status);
+ task = g_task_new (self, NULL, callback, user_data);
mm_base_modem_at_command (MM_BASE_MODEM (self),
"+CAD?",
3,
FALSE,
(GAsyncReadyCallback)cad_query_ready,
- result);
+ task);
}
/*****************************************************************************/
@@ -6900,32 +9394,27 @@ typedef struct {
} DetailedRegistrationStateResults;
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
MMPortSerialAt *port;
MMModemCdmaRegistrationState cdma1x_state;
MMModemCdmaRegistrationState evdo_state;
- GError *error;
} DetailedRegistrationStateContext;
-static void
-detailed_registration_state_context_complete_and_free (DetailedRegistrationStateContext *ctx)
+static DetailedRegistrationStateResults *
+detailed_registration_state_result_new (DetailedRegistrationStateContext *ctx)
{
- if (ctx->error)
- g_simple_async_result_take_error (ctx->result, ctx->error);
- else {
- DetailedRegistrationStateResults *results;
+ DetailedRegistrationStateResults *results;
- results = g_new (DetailedRegistrationStateResults, 1);
- results->detailed_cdma1x_state = ctx->cdma1x_state;
- results->detailed_evdo_state = ctx->evdo_state;
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, g_free);
- }
+ results = g_new (DetailedRegistrationStateResults, 1);
+ results->detailed_cdma1x_state = ctx->cdma1x_state;
+ results->detailed_evdo_state = ctx->evdo_state;
+
+ return results;
+}
- g_simple_async_result_complete (ctx->result);
+static void
+detailed_registration_state_context_free (DetailedRegistrationStateContext *ctx)
+{
g_object_unref (ctx->port);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -6938,29 +9427,36 @@ modem_cdma_get_detailed_registration_state_finish (MMIfaceModemCdma *self,
{
DetailedRegistrationStateResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*detailed_cdma1x_state = results->detailed_cdma1x_state;
*detailed_evdo_state = results->detailed_evdo_state;
+ g_free (results);
return TRUE;
}
static void
speri_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
+ DetailedRegistrationStateContext *ctx;
gboolean roaming = FALSE;
const gchar *response;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
/* silently discard SPERI errors */
g_error_free (error);
- detailed_registration_state_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ detailed_registration_state_result_new (ctx),
+ g_free);
+ g_object_unref (task);
return;
}
@@ -6968,8 +9464,11 @@ speri_ready (MMIfaceModemCdma *self,
response = mm_strip_tag (response, "$SPERI:");
if (!response ||
!mm_cdma_parse_eri (response, &roaming, NULL, NULL)) {
- mm_warn ("Couldn't parse SPERI response '%s'", response);
- detailed_registration_state_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't parse SPERI response '%s'", response);
+ g_task_return_pointer (task,
+ detailed_registration_state_result_new (ctx),
+ g_free);
+ g_object_unref (task);
return;
}
@@ -6989,21 +9488,27 @@ speri_ready (MMIfaceModemCdma *self,
ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_HOME;
}
- detailed_registration_state_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ detailed_registration_state_result_new (ctx),
+ g_free);
+ g_object_unref (task);
}
static void
spservice_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- DetailedRegistrationStateContext *ctx)
+ GTask *task)
{
+ DetailedRegistrationStateContext *ctx;
+ GError *error = NULL;
const gchar *response;
MMModemCdmaRegistrationState cdma1x_state;
MMModemCdmaRegistrationState evdo_state;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &ctx->error);
- if (ctx->error) {
- detailed_registration_state_context_complete_and_free (ctx);
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -7013,21 +9518,27 @@ spservice_ready (MMIfaceModemCdma *self,
if (!mm_cdma_parse_spservice_read_response (response,
&cdma1x_state,
&evdo_state)) {
- ctx->error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't parse SPSERVICE response '%s'",
- response);
- detailed_registration_state_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse SPSERVICE response '%s'",
+ response);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Store new intermediate results */
ctx->cdma1x_state = cdma1x_state;
ctx->evdo_state = evdo_state;
/* If SPERI not supported, we're done */
- if (!ctx->self->priv->has_speri) {
- detailed_registration_state_context_complete_and_free (ctx);
+ if (!MM_BROADBAND_MODEM (self)->priv->has_speri) {
+ g_task_return_pointer (task,
+ detailed_registration_state_result_new (ctx),
+ g_free);
+ g_object_unref (task);
return;
}
@@ -7037,7 +9548,7 @@ spservice_ready (MMIfaceModemCdma *self,
3,
FALSE,
(GAsyncReadyCallback)speri_ready,
- ctx);
+ task);
}
static void
@@ -7050,30 +9561,30 @@ modem_cdma_get_detailed_registration_state (MMIfaceModemCdma *self,
MMPortSerialAt *port;
GError *error = NULL;
DetailedRegistrationStateContext *ctx;
+ GTask *task;
/* The default implementation to get detailed registration state
* requires the use of an AT port; so if we cannot get any, just
* return the error */
port = mm_base_modem_peek_best_at_port (MM_BASE_MODEM (self), &error);
if (!port) {
- g_simple_async_report_take_gerror_in_idle (G_OBJECT (self),
- callback,
- user_data,
- error);
+ g_task_report_error (self,
+ callback,
+ user_data,
+ modem_cdma_get_detailed_registration_state,
+ error);
return;
}
/* Setup context */
ctx = g_new0 (DetailedRegistrationStateContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_get_detailed_registration_state);
ctx->port = g_object_ref (port);
ctx->cdma1x_state = cdma1x_state;
ctx->evdo_state = evdo_state;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)detailed_registration_state_context_free);
+
/* NOTE: If we get this generic implementation of getting detailed
* registration state called, we DO know that we have Sprint commands
* supported, we checked it in setup_registration_checks() */
@@ -7082,7 +9593,7 @@ modem_cdma_get_detailed_registration_state (MMIfaceModemCdma *self,
3,
FALSE,
(GAsyncReadyCallback)spservice_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -7097,58 +9608,45 @@ typedef struct {
} SetupRegistrationChecksResults;
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- GError *error;
gboolean has_qcdm_port;
gboolean has_sprint_commands;
} SetupRegistrationChecksContext;
-static void
-setup_registration_checks_context_complete_and_free (SetupRegistrationChecksContext *ctx)
+static SetupRegistrationChecksResults *
+setup_registration_checks_results_new (MMBroadbandModem *self,
+ SetupRegistrationChecksContext *ctx)
{
- if (ctx->error)
- g_simple_async_result_take_error (ctx->result, ctx->error);
- else {
- SetupRegistrationChecksResults *results;
-
- results = g_new0 (SetupRegistrationChecksResults, 1);
-
- /* Skip QCDM steps if no QCDM port */
- if (!ctx->has_qcdm_port) {
- mm_dbg ("Will skip all QCDM-based registration checks");
- results->skip_qcdm_call_manager_step = TRUE;
- results->skip_qcdm_hdr_step = TRUE;
- }
+ SetupRegistrationChecksResults *results;
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_detailed_registration_state ==
- modem_cdma_get_detailed_registration_state) {
- /* Skip CDMA1x Serving System check if we have Sprint specific
- * commands AND if the default detailed registration checker
- * is the generic one. Implementations knowing that their
- * CSS response is undesired, should either setup NULL callbacks
- * for the specific step, or subclass this setup and return
- * FALSE themselves. */
- if (ctx->has_sprint_commands) {
- mm_dbg ("Will skip CDMA1x Serving System check, "
- "we do have Sprint commands");
- results->skip_at_cdma1x_serving_system_step = TRUE;
- } else {
- /* If there aren't Sprint specific commands, and the detailed
- * registration state getter wasn't subclassed, skip the step */
- mm_dbg ("Will skip generic detailed registration check, we "
- "don't have Sprint commands");
- results->skip_detailed_registration_state = TRUE;
- }
+ results = g_new0 (SetupRegistrationChecksResults, 1);
+
+ /* Skip QCDM steps if no QCDM port */
+ if (!ctx->has_qcdm_port) {
+ mm_obj_dbg (self, "will skip all QCDM-based registration checks");
+ results->skip_qcdm_call_manager_step = TRUE;
+ results->skip_qcdm_hdr_step = TRUE;
+ }
+
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state ==
+ modem_cdma_get_detailed_registration_state) {
+ /* Skip CDMA1x Serving System check if we have Sprint specific
+ * commands AND if the default detailed registration checker
+ * is the generic one. Implementations knowing that their
+ * CSS response is undesired, should either setup NULL callbacks
+ * for the specific step, or subclass this setup and return
+ * FALSE themselves. */
+ if (ctx->has_sprint_commands) {
+ mm_obj_dbg (self, "will skip CDMA1x Serving System check, we do have Sprint commands");
+ results->skip_at_cdma1x_serving_system_step = TRUE;
+ } else {
+ /* If there aren't Sprint specific commands, and the detailed
+ * registration state getter wasn't subclassed, skip the step */
+ mm_obj_dbg (self, "will skip generic detailed registration check, we don't have Sprint commands");
+ results->skip_detailed_registration_state = TRUE;
}
-
- g_simple_async_result_set_op_res_gpointer (ctx->result, results, g_free);
}
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
+ return results;
}
static gboolean
@@ -7163,86 +9661,104 @@ modem_cdma_setup_registration_checks_finish (MMIfaceModemCdma *self,
{
SetupRegistrationChecksResults *results;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ results = g_task_propagate_pointer (G_TASK (res), error);
+ if (!results)
return FALSE;
- results = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
*skip_qcdm_call_manager_step = results->skip_qcdm_call_manager_step;
*skip_qcdm_hdr_step = results->skip_qcdm_hdr_step;
*skip_at_cdma_service_status_step = results->skip_at_cdma_service_status_step;
*skip_at_cdma1x_serving_system_step = results->skip_at_cdma1x_serving_system_step;
*skip_detailed_registration_state = results->skip_detailed_registration_state;
+ g_free (results);
return TRUE;
}
static void
-speri_check_ready (MMIfaceModemCdma *self,
+speri_check_ready (MMIfaceModemCdma *_self,
GAsyncResult *res,
- SetupRegistrationChecksContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ SetupRegistrationChecksContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error)
g_error_free (error);
else
/* We DO have SPERI */
- ctx->self->priv->has_speri = TRUE;
+ self->priv->has_speri = TRUE;
/* All done */
- ctx->self->priv->checked_sprint_support = TRUE;
- setup_registration_checks_context_complete_and_free (ctx);
+ self->priv->checked_sprint_support = TRUE;
+ g_task_return_pointer (task,
+ setup_registration_checks_results_new (self, ctx),
+ g_free);
+ g_object_unref (task);
}
static void
-spservice_check_ready (MMIfaceModemCdma *self,
+spservice_check_ready (MMIfaceModemCdma *_self,
GAsyncResult *res,
- SetupRegistrationChecksContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
+ SetupRegistrationChecksContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
g_error_free (error);
- ctx->self->priv->checked_sprint_support = TRUE;
- setup_registration_checks_context_complete_and_free (ctx);
+ self->priv->checked_sprint_support = TRUE;
+ g_task_return_pointer (task,
+ setup_registration_checks_results_new (self, ctx),
+ g_free);
+ g_object_unref (task);
return;
}
/* We DO have SPSERVICE, look for SPERI */
ctx->has_sprint_commands = TRUE;
- ctx->self->priv->has_spservice = TRUE;
+ self->priv->has_spservice = TRUE;
mm_base_modem_at_command (MM_BASE_MODEM (self),
"$SPERI?",
3,
FALSE,
(GAsyncReadyCallback)speri_check_ready,
- ctx);
+ task);
}
static void
-modem_cdma_setup_registration_checks (MMIfaceModemCdma *self,
+modem_cdma_setup_registration_checks (MMIfaceModemCdma *_self,
GAsyncReadyCallback callback,
gpointer user_data)
{
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
SetupRegistrationChecksContext *ctx;
+ GTask *task;
ctx = g_new0 (SetupRegistrationChecksContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_setup_registration_checks);
/* Check if we have a QCDM port */
ctx->has_qcdm_port = !!mm_base_modem_peek_port_qcdm (MM_BASE_MODEM (self));
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
/* If we have cached results of Sprint command checking, use them */
- if (ctx->self->priv->checked_sprint_support) {
- ctx->has_sprint_commands = ctx->self->priv->has_spservice;
+ if (self->priv->checked_sprint_support) {
+ ctx->has_sprint_commands = self->priv->has_spservice;
/* Completes in idle */
- setup_registration_checks_context_complete_and_free (ctx);
+ g_task_return_pointer (task,
+ setup_registration_checks_results_new (self, ctx),
+ g_free);
+ g_object_unref (task);
return;
}
@@ -7252,7 +9768,7 @@ modem_cdma_setup_registration_checks (MMIfaceModemCdma *self,
3,
FALSE,
(GAsyncReadyCallback)spservice_check_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -7260,14 +9776,13 @@ modem_cdma_setup_registration_checks (MMIfaceModemCdma *self,
typedef struct {
MMBroadbandModem *self;
- GSimpleAsyncResult *result;
GCancellable *cancellable;
GTimer *timer;
guint max_registration_time;
} RegisterInCdmaNetworkContext;
static void
-register_in_cdma_network_context_complete_and_free (RegisterInCdmaNetworkContext *ctx)
+register_in_cdma_network_context_free (RegisterInCdmaNetworkContext *ctx)
{
/* If our cancellable reference is still around, clear it */
if (ctx->self->priv->modem_cdma_pending_registration_cancellable ==
@@ -7278,8 +9793,6 @@ register_in_cdma_network_context_complete_and_free (RegisterInCdmaNetworkContext
if (ctx->timer)
g_timer_destroy (ctx->timer);
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->cancellable);
g_object_unref (ctx->self);
g_free (ctx);
@@ -7290,7 +9803,7 @@ modem_cdma_register_in_network_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
#undef REG_IS_IDLE
@@ -7305,30 +9818,33 @@ modem_cdma_register_in_network_finish (MMIfaceModemCdma *self,
static void run_cdma_registration_checks_ready (MMBroadbandModem *self,
GAsyncResult *res,
- RegisterInCdmaNetworkContext *ctx);
+ GTask *task);
static gboolean
-run_cdma_registration_checks_again (RegisterInCdmaNetworkContext *ctx)
+run_cdma_registration_checks_again (GTask *task)
{
/* Get fresh registration state */
mm_iface_modem_cdma_run_registration_checks (
- MM_IFACE_MODEM_CDMA (ctx->self),
+ MM_IFACE_MODEM_CDMA (g_task_get_source_object (task)),
(GAsyncReadyCallback)run_cdma_registration_checks_ready,
- ctx);
- return FALSE;
+ task);
+ return G_SOURCE_REMOVE;
}
static void
run_cdma_registration_checks_ready (MMBroadbandModem *self,
GAsyncResult *res,
- RegisterInCdmaNetworkContext *ctx)
+ GTask *task)
{
+ RegisterInCdmaNetworkContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
mm_iface_modem_cdma_run_registration_checks_finish (MM_IFACE_MODEM_CDMA (self), res, &error);
if (error) {
- mm_dbg ("CDMA registration check failed: '%s'", error->message);
+ mm_obj_dbg (self, "CDMA registration check failed: %s", error->message);
mm_iface_modem_cdma_update_cdma1x_registration_state (
MM_IFACE_MODEM_CDMA (self),
MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
@@ -7341,26 +9857,25 @@ run_cdma_registration_checks_ready (MMBroadbandModem *self,
MM_IFACE_MODEM_CDMA (self),
MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
- g_simple_async_result_take_error (ctx->result, error);
- register_in_cdma_network_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* If we got registered in at least one CDMA network, end registration checks */
if (REG_IS_DONE (self->priv->modem_cdma_cdma1x_registration_state) ||
REG_IS_DONE (self->priv->modem_cdma_evdo_registration_state)) {
- mm_dbg ("Modem is currently registered in a CDMA network "
- "(CDMA1x: '%s', EV-DO: '%s')",
- REG_IS_DONE (self->priv->modem_cdma_cdma1x_registration_state) ? "yes" : "no",
- REG_IS_DONE (self->priv->modem_cdma_evdo_registration_state) ? "yes" : "no");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_cdma_network_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "registered in a CDMA network (CDMA1x: '%s', EV-DO: '%s')",
+ REG_IS_DONE (self->priv->modem_cdma_cdma1x_registration_state) ? "yes" : "no",
+ REG_IS_DONE (self->priv->modem_cdma_evdo_registration_state) ? "yes" : "no");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Don't spend too much time waiting to get registered */
if (g_timer_elapsed (ctx->timer, NULL) > ctx->max_registration_time) {
- mm_dbg ("CDMA registration check timed out");
+ mm_obj_dbg (self, "CDMA registration check timed out");
mm_iface_modem_cdma_update_cdma1x_registration_state (
MM_IFACE_MODEM_CDMA (self),
MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN,
@@ -7372,55 +9887,55 @@ run_cdma_registration_checks_ready (MMBroadbandModem *self,
mm_iface_modem_cdma_update_access_technologies (
MM_IFACE_MODEM_CDMA (self),
MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
- g_simple_async_result_take_error (
- ctx->result,
- mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT));
- register_in_cdma_network_context_complete_and_free (ctx);
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Check again in a few seconds. */
- mm_dbg ("Modem not yet registered in a CDMA network... will recheck soon");
+ mm_obj_dbg (self, "not yet registered in a CDMA network... will recheck soon");
g_timeout_add_seconds (3,
(GSourceFunc)run_cdma_registration_checks_again,
- ctx);
+ task);
}
static void
-modem_cdma_register_in_network (MMIfaceModemCdma *self,
+modem_cdma_register_in_network (MMIfaceModemCdma *_self,
guint max_registration_time,
GAsyncReadyCallback callback,
gpointer user_data)
{
- MMBroadbandModem *broadband = MM_BROADBAND_MODEM (self);
+ MMBroadbandModem *self = MM_BROADBAND_MODEM (_self);
RegisterInCdmaNetworkContext *ctx;
+ GTask *task;
/* (Try to) cancel previous registration request */
- if (broadband->priv->modem_cdma_pending_registration_cancellable) {
- g_cancellable_cancel (broadband->priv->modem_cdma_pending_registration_cancellable);
- g_clear_object (&broadband->priv->modem_cdma_pending_registration_cancellable);
+ if (self->priv->modem_cdma_pending_registration_cancellable) {
+ g_cancellable_cancel (self->priv->modem_cdma_pending_registration_cancellable);
+ g_clear_object (&self->priv->modem_cdma_pending_registration_cancellable);
}
ctx = g_new0 (RegisterInCdmaNetworkContext, 1);
ctx->self = g_object_ref (self);
ctx->max_registration_time = max_registration_time;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_cdma_register_in_network);
ctx->cancellable = g_cancellable_new ();
/* Keep an accessible reference to the cancellable, so that we can cancel
* previous request when needed */
- broadband->priv->modem_cdma_pending_registration_cancellable =
+ self->priv->modem_cdma_pending_registration_cancellable =
g_object_ref (ctx->cancellable);
/* Get fresh registration state */
ctx->timer = g_timer_new ();
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_cdma_network_context_free);
+
mm_iface_modem_cdma_run_registration_checks (
- MM_IFACE_MODEM_CDMA (self),
+ _self,
(GAsyncReadyCallback)run_cdma_registration_checks_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -7431,11 +9946,15 @@ modem_location_load_capabilities_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_LOCATION_SOURCE_NONE;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLocationSource) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
}
static void
@@ -7443,27 +9962,19 @@ modem_location_load_capabilities (MMIfaceModemLocation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_location_load_capabilities);
+ task = g_task_new (self, NULL, callback, user_data);
/* Default location capabilities supported by the generic broadband
* implementation are only LAC-CI in 3GPP-enabled modems. And even this,
* will only be true if the modem supports CREG/CGREG=2 */
if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
- g_simple_async_result_set_op_res_gpointer (result,
- GUINT_TO_POINTER (MM_MODEM_LOCATION_SOURCE_NONE),
- NULL);
+ g_task_return_int (task, MM_MODEM_LOCATION_SOURCE_NONE);
else
- g_simple_async_result_set_op_res_gpointer (result,
- GUINT_TO_POINTER (MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI),
- NULL);
+ g_task_return_int (task, MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -7474,7 +9985,7 @@ enable_location_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
@@ -7483,12 +9994,7 @@ enable_location_gathering (MMIfaceModemLocation *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
-
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enable_location_gathering);
+ GTask *task;
/* 3GPP modems need to re-run registration checks when enabling the 3GPP
* location source, so that we get up to date LAC/CI location information.
@@ -7505,9 +10011,720 @@ enable_location_gathering (MMIfaceModemLocation *self,
}
/* Done we are */
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load network time (Time interface) */
+
+static gchar *
+modem_time_load_network_time_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ gchar *result = NULL;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return NULL;
+ if (!mm_parse_cclk_response (response, &result, NULL, error))
+ return NULL;
+ return result;
+}
+
+static void
+modem_time_load_network_time (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CCLK?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load network timezone (Time interface) */
+
+static MMNetworkTimezone *
+modem_time_load_network_timezone_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ const gchar *response;
+ MMNetworkTimezone *tz = NULL;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response)
+ return NULL;
+ if (!mm_parse_cclk_response (response, NULL, &tz, error))
+ return NULL;
+ return tz;
+}
+
+static void
+modem_time_load_network_timezone (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CCLK?",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Check support (Time interface) */
+
+static const MMBaseModemAtCommand time_check_sequence[] = {
+ { "+CTZU=1", 3, TRUE, mm_base_modem_response_processor_no_result_continue },
+ { "+CCLK?", 3, TRUE, mm_base_modem_response_processor_string },
+ { NULL }
+};
+
+static gboolean
+modem_time_check_support_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_sequence_finish (MM_BASE_MODEM (self), res, NULL, error);
+}
+
+static void
+modem_time_check_support (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_sequence (MM_BASE_MODEM (self),
+ time_check_sequence,
+ NULL, /* response_processor_context */
+ NULL, /* response_processor_context_free */
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Check support (Signal interface) */
+
+static gboolean
+modem_signal_check_support_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+}
+
+static void
+modem_signal_check_support (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CESQ=?",
+ 3,
+ TRUE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Load extended signal information (Signal interface) */
+
+static gboolean
+modem_signal_load_values_finish (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g,
+ GError **error)
+{
+ const gchar *response;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
+ if (!response || !mm_3gpp_cesq_response_to_signal_info (response, self, gsm, umts, lte, error))
+ return FALSE;
+
+ if (cdma)
+ *cdma = NULL;
+ if (evdo)
+ *evdo = NULL;
+ if (nr5g)
+ *nr5g = NULL;
+
+ return TRUE;
+}
+
+static void
+modem_signal_load_values (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CESQ",
+ 3,
+ FALSE,
+ callback,
+ user_data);
+}
+
+/*****************************************************************************/
+/* Check support (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_check_support_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+profile_manager_cgdcont_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ mm_base_modem_at_command_finish (self, res, &error);
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_check_support (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (!mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))) {
+ g_task_return_boolean (task, FALSE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Query with CGDCONT=? */
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CGDCONT=?",
+ 3,
+ TRUE, /* allow caching, it's a test command */
+ (GAsyncReadyCallback)profile_manager_cgdcont_test_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* List profiles (3GPP profile management interface) */
+
+typedef struct {
+ GList *profiles;
+} ListProfilesContext;
+
+static void
+list_profiles_context_free (ListProfilesContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->profiles);
+ g_slice_free (ListProfilesContext, ctx);
+}
+
+static gboolean
+modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **out_profiles,
+ GError **error)
+{
+ ListProfilesContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (out_profiles)
+ *out_profiles = g_steal_pointer (&ctx->profiles);
+ return TRUE;
+}
+
+static void
+profile_manager_cgdcont_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ const gchar *response;
+ GError *error = NULL;
+ GList *pdp_context_list;
+
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* may return NULL without error if response is empty */
+ pdp_context_list = mm_3gpp_parse_cgdcont_read_response (response, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (ListProfilesContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free);
+ ctx->profiles = mm_3gpp_profile_list_new_from_pdp_context_list (pdp_context_list);
+ mm_3gpp_pdp_context_list_free (pdp_context_list);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Query with CGDCONT? */
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CGDCONT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)profile_manager_cgdcont_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Check format (3GPP profile management interface) */
+
+typedef struct {
+ MMBearerIpFamily ip_type;
+ guint min_profile_id;
+ guint max_profile_id;
+} CheckFormatContext;
+
+static void
+check_format_context_free (CheckFormatContext *ctx)
+{
+ g_slice_free (CheckFormatContext, ctx);
+}
+
+static gboolean
+modem_3gpp_profile_manager_check_format_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *new_id,
+ gint *min_profile_id,
+ gint *max_profile_id,
+ GEqualFunc *apn_cmp,
+ MM3gppProfileCmpFlags *profile_cmp_flags,
+ GError **error)
+{
+ CheckFormatContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (new_id)
+ *new_id = TRUE;
+ if (min_profile_id)
+ *min_profile_id = (gint) ctx->min_profile_id;
+ if (max_profile_id)
+ *max_profile_id = (gint) ctx->max_profile_id;
+ if (apn_cmp)
+ *apn_cmp = (GEqualFunc) mm_3gpp_cmp_apn_name;
+ if (profile_cmp_flags)
+ *profile_cmp_flags = (MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH | MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE);
+ return TRUE;
+}
+
+static void
+check_format_cgdcont_test_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ CheckFormatContext *ctx;
+ const gchar *response;
+ GList *format_list = NULL;
+ g_autofree gchar *ip_family_str = NULL;
+ g_autoptr(GError) error = NULL;
+ gboolean checked = FALSE;
+
+ ctx = g_task_get_task_data (task);
+
+ ip_family_str = mm_bearer_ip_family_build_string_from_mask (ctx->ip_type);
+
+ response = mm_base_modem_at_command_full_finish (self, res, &error);
+ if (!response)
+ mm_obj_dbg (self, "failed checking context definition format: %s", error->message);
+ else {
+ format_list = mm_3gpp_parse_cgdcont_test_response (response, self, &error);
+ if (error)
+ mm_obj_dbg (self, "error parsing +CGDCONT test response: %s", error->message);
+ else if (mm_3gpp_pdp_context_format_list_find_range (format_list, ctx->ip_type,
+ &ctx->min_profile_id, &ctx->max_profile_id))
+ checked = TRUE;
+ }
+
+ if (!checked) {
+ ctx->min_profile_id = 1;
+ ctx->max_profile_id = G_MAXINT-1;
+ mm_obj_dbg (self, "unknown +CGDCONT format details for PDP type '%s', using defaults: minimum %d, maximum %d",
+ ip_family_str, ctx->min_profile_id, ctx->max_profile_id);
+ } else
+ mm_obj_dbg (self, "+CGDCONT format details for PDP type '%s': minimum %d, maximum %d",
+ ip_family_str, ctx->min_profile_id, ctx->max_profile_id);
+
+ mm_3gpp_pdp_context_format_list_free (format_list);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_check_format (MMIfaceModem3gppProfileManager *self,
+ MMBearerIpFamily ip_type,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ CheckFormatContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (CheckFormatContext);
+ ctx->ip_type = ip_type;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)check_format_context_free);
+
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+CGDCONT=?",
+ 3,
+ TRUE, /* cached */
+ (GAsyncReadyCallback)check_format_cgdcont_test_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Delete profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_delete_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+profile_manager_cgdcont_reset_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ gint profile_id;
+
+ profile_id = GPOINTER_TO_INT (g_task_get_task_data (task));
+
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "attempting to reset context with id '%d' failed: %s", profile_id, error->message);
+ g_task_return_error (task, error);
+ } else {
+ mm_obj_dbg (self, "reseted context with profile id '%d'", profile_id);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+profile_manager_cgdel_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+ g_autofree gchar *cmd = NULL;
+ gint profile_id;
+
+ profile_id = GPOINTER_TO_INT (g_task_get_task_data (task));
+
+ if (mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_obj_dbg (self, "deleted context with profile id '%d'", profile_id);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "attempting to delete context with id '%d' failed: %s", profile_id, error->message);
+
+ /* From 3GPP TS 27.007 (v16.3.0):
+ * A special form of the set command, +CGDCONT=<cid> causes the values for
+ * context number <cid> to become undefined.
+ */
+ cmd = g_strdup_printf ("+CGDCONT=%d", profile_id);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)profile_manager_cgdcont_reset_ready,
+ task);
+}
+
+static void
+modem_3gpp_profile_manager_delete_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autofree gchar *cmd = NULL;
+ GTask *task;
+ gint profile_id;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL);
+
+ cmd = g_strdup_printf ("+CGDEL=%d", profile_id);
+
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)profile_manager_cgdel_set_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Deactivate profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_check_activated_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *out_activated,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gboolean result;
+
+ result = g_task_propagate_boolean (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_activated)
+ *out_activated = result;
+ return TRUE;
+}
+
+static void
+check_activated_profile_cgact_query_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MM3gppProfile *profile;
+ const gchar *response;
+ GError *error = NULL;
+ GList *pdp_context_active_list = NULL;
+ GList *l;
+ gint profile_id;
+ gboolean activated = FALSE;
+ g_autofree gchar *cmd = NULL;
+
+ response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
+ if (response)
+ pdp_context_active_list = mm_3gpp_parse_cgact_read_response (response, &error);
+
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ profile = g_task_get_task_data (task);
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+
+ for (l = pdp_context_active_list; l; l = g_list_next (l)) {
+ MM3gppPdpContextActive *iter = l->data;
+
+ if ((gint)iter->cid == profile_id) {
+ activated = iter->active;
+ break;
+ }
+ }
+ mm_3gpp_pdp_context_active_list_free (pdp_context_active_list);
+
+ if (!l)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Profile '%d' not found in CGACT? response",
+ profile_id);
+ else
+ g_task_return_boolean (task, activated);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_check_activated_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gint profile_id;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, g_object_ref (profile), g_object_unref);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+
+ mm_obj_dbg (self, "checking if profile with id '%d' is already activated...", profile_id);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "+CGACT?",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)check_activated_profile_cgact_query_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Deactivate profile (3GPP profile management interface) */
+
+static gboolean
+modem_3gpp_profile_manager_deactivate_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+deactivate_profile_cgact_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_deactivate_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gint profile_id;
+ g_autofree gchar *cmd = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ mm_obj_dbg (self, "deactivating profile with id '%d'...", profile_id);
+
+ cmd = g_strdup_printf ("+CGACT=0,%d", profile_id);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ cmd,
+ MM_BASE_BEARER_DEFAULT_DISCONNECTION_TIMEOUT,
+ FALSE,
+ (GAsyncReadyCallback)deactivate_profile_cgact_set_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Store profile (3GPP profile management interface) */
+
+static gint
+modem_3gpp_profile_manager_store_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return MM_3GPP_PROFILE_ID_UNKNOWN;
+
+ return GPOINTER_TO_INT (g_task_get_task_data (G_TASK (res)));
+}
+
+static void
+store_profile_cgdcont_set_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_profile_manager_store_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *profile,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ gint profile_id;
+ MMBearerIpFamily ip_type;
+ const gchar *apn;
+ const gchar *pdp_type;
+ g_autofree gchar *ip_type_str = NULL;
+ g_autofree gchar *quoted_apn = NULL;
+ g_autofree gchar *cmd = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL);
+
+ ip_type = mm_3gpp_profile_get_ip_type (profile);
+ g_assert (ip_type != MM_BEARER_IP_FAMILY_NONE);
+ g_assert (ip_type != MM_BEARER_IP_FAMILY_ANY);
+ ip_type_str = mm_bearer_ip_family_build_string_from_mask (ip_type);
+ pdp_type = mm_3gpp_get_pdp_type_from_ip_family (ip_type);
+ g_assert (pdp_type);
+
+ apn = mm_3gpp_profile_get_apn (profile);
+ quoted_apn = mm_port_serial_at_quote_string (apn);
+
+ mm_obj_dbg (self, "storing profile '%d': apn '%s', ip type '%s'",
+ profile_id, apn, ip_type_str);
+
+ cmd = g_strdup_printf ("+CGDCONT=%d,\"%s\",%s", profile_id, pdp_type, quoted_apn);
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ cmd,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback) store_profile_cgdcont_set_ready,
+ task);
}
/*****************************************************************************/
@@ -7538,7 +10755,7 @@ setup_ports (MMBroadbandModem *self)
MMPortSerialAt *ports[2];
GRegex *regex;
GPtrArray *array;
- gint i, j;
+ guint i, j;
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
@@ -7615,50 +10832,6 @@ setup_ports (MMBroadbandModem *self)
}
/*****************************************************************************/
-/* Generic ports open/close context */
-
-struct _PortsContext {
- volatile gint ref_count;
-
- MMPortSerialAt *primary;
- gboolean primary_open;
- MMPortSerialAt *secondary;
- gboolean secondary_open;
- MMPortSerialQcdm *qcdm;
- gboolean qcdm_open;
-};
-
-static PortsContext *
-ports_context_ref (PortsContext *ctx)
-{
- g_atomic_int_inc (&ctx->ref_count);
- return ctx;
-}
-
-static void
-ports_context_unref (PortsContext *ctx)
-{
- if (g_atomic_int_dec_and_test (&ctx->ref_count)) {
- if (ctx->primary) {
- if (ctx->primary_open)
- mm_port_serial_close (MM_PORT_SERIAL (ctx->primary));
- g_object_unref (ctx->primary);
- }
- if (ctx->secondary) {
- if (ctx->secondary_open)
- mm_port_serial_close (MM_PORT_SERIAL (ctx->secondary));
- g_object_unref (ctx->secondary);
- }
- if (ctx->qcdm) {
- if (ctx->qcdm_open)
- mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
- g_object_unref (ctx->qcdm);
- }
- g_free (ctx);
- }
-}
-
-/*****************************************************************************/
/* Initialization started/stopped */
static gboolean
@@ -7673,73 +10846,12 @@ initialization_stopped (MMBroadbandModem *self,
return TRUE;
}
-typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
- PortsContext *ports;
-} InitializationStartedContext;
-
-static void
-initialization_started_context_complete_and_free (InitializationStartedContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- ports_context_unref (ctx->ports);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
static gpointer
initialization_started_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- gpointer ref;
-
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- ref = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
- return ref ? ports_context_ref (ref) : NULL;
-}
-
-static gboolean
-open_ports_initialization (MMBroadbandModem *self,
- PortsContext *ctx,
- GError **error)
-{
- ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
- if (!ctx->primary) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get primary port");
- return FALSE;
- }
-
- /* Open and send first commands to the primary serial port.
- * We do keep the primary port open during the whole initialization
- * sequence. Note that this port is not really passed to the interfaces,
- * they will get the primary port themselves. */
- if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->primary), error)) {
- g_prefix_error (error, "Couldn't open primary port: ");
- return FALSE;
- }
-
- ctx->primary_open = TRUE;
-
- /* Try to disable echo */
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- ctx->primary,
- "E0", 3,
- FALSE, FALSE, NULL, NULL, NULL);
- /* Try to get extended errors */
- mm_base_modem_at_command_full (MM_BASE_MODEM (self),
- ctx->primary,
- "+CMEE=1", 3,
- FALSE, FALSE, NULL, NULL, NULL);
-
- return TRUE;
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
@@ -7748,26 +10860,20 @@ initialization_started (MMBroadbandModem *self,
gpointer user_data)
{
GError *error = NULL;
- InitializationStartedContext *ctx;
+ GTask *task;
+ PortsContext *ctx;
- ctx = g_new0 (InitializationStartedContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- initialization_started);
- ctx->ports = g_new0 (PortsContext, 1);
- ctx->ports->ref_count = 1;
-
- if (!open_ports_initialization (self, ctx->ports, &error)) {
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Open ports for initialization, just the primary AT port */
+ ctx = ports_context_new ();
+ if (!ports_context_open (self, ctx, FALSE, FALSE, FALSE, &error)) {
+ ports_context_unref (ctx);
g_prefix_error (&error, "Couldn't open ports during modem initialization: ");
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
} else
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- ports_context_ref (ctx->ports),
- (GDestroyNotify)ports_context_unref);
-
- initialization_started_context_complete_and_free (ctx);
+ g_task_return_pointer (task, ctx, (GDestroyNotify)ports_context_unref);
+ g_object_unref (task);
}
/*****************************************************************************/
@@ -7781,6 +10887,7 @@ disabling_stopped (MMBroadbandModem *self,
ports_context_unref (self->priv->enabled_ports_ctx);
self->priv->enabled_ports_ctx = NULL;
}
+
return TRUE;
}
@@ -7820,19 +10927,14 @@ enabling_modem_init (MMBroadbandModem *self,
/* Enabling started */
typedef struct {
- MMBroadbandModem *self;
- GSimpleAsyncResult *result;
PortsContext *ports;
gboolean modem_init_required;
} EnablingStartedContext;
static void
-enabling_started_context_complete_and_free (EnablingStartedContext *ctx)
+enabling_started_context_free (EnablingStartedContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
ports_context_unref (ctx->ports);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (EnablingStartedContext, ctx);
}
@@ -7841,13 +10943,20 @@ enabling_started_finish (MMBroadbandModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static gboolean
-enabling_after_modem_init_timeout (EnablingStartedContext *ctx)
+enabling_after_modem_init_timeout (GTask *task)
{
+ MMBroadbandModem *self;
+ EnablingStartedContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Reset init sequence enabled flags and run them explicitly */
+ g_assert (ctx->modem_init_required);
g_object_set (ctx->ports->primary,
MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, TRUE,
NULL);
@@ -7860,117 +10969,64 @@ enabling_after_modem_init_timeout (EnablingStartedContext *ctx)
}
/* Store enabled ports context and complete */
- ctx->self->priv->enabled_ports_ctx = ports_context_ref (ctx->ports);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_started_context_complete_and_free (ctx);
- return FALSE;
+ self->priv->enabled_ports_ctx = ports_context_ref (ctx->ports);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
static void
enabling_modem_init_ready (MMBroadbandModem *self,
GAsyncResult *res,
- EnablingStartedContext *ctx)
+ GTask *task)
{
GError *error = NULL;
- if (!MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_modem_init_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_started_context_complete_and_free (ctx);
+ if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Specify that the modem init was run once */
- ctx->self->priv->modem_init_run = TRUE;
+ self->priv->modem_init_run = TRUE;
/* After the modem init sequence, give a 500ms period for the modem to settle */
- mm_dbg ("Giving some time to settle the modem...");
- g_timeout_add (500, (GSourceFunc)enabling_after_modem_init_timeout, ctx);
+ mm_obj_dbg (self, "giving some time to settle the modem...");
+ g_timeout_add (500, (GSourceFunc)enabling_after_modem_init_timeout, task);
}
static void
enabling_flash_done (MMPortSerial *port,
GAsyncResult *res,
- EnablingStartedContext *ctx)
+ GTask *task)
{
+ MMBroadbandModem *self;
+ EnablingStartedContext *ctx;
GError *error = NULL;
if (!mm_port_serial_flash_finish (port, res, &error)) {
g_prefix_error (&error, "Primary port flashing failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- enabling_started_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (ctx->modem_init_required) {
- mm_dbg ("Running modem initialization sequence...");
- MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_modem_init (ctx->self,
- (GAsyncReadyCallback)enabling_modem_init_ready,
- ctx);
+ mm_obj_dbg (self, "running initialization sequence...");
+ MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init (self,
+ (GAsyncReadyCallback)enabling_modem_init_ready,
+ task);
return;
}
/* Store enabled ports context and complete */
- ctx->self->priv->enabled_ports_ctx = ports_context_ref (ctx->ports);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_started_context_complete_and_free (ctx);
-}
-
-static gboolean
-open_ports_enabling (MMBroadbandModem *self,
- PortsContext *ctx,
- gboolean modem_init_required,
- GError **error)
-{
- /* Open primary */
- ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self));
- if (!ctx->primary) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get primary port");
- return FALSE;
- }
-
- /* If we'll need to run modem initialization, disable port init sequence */
- if (modem_init_required)
- g_object_set (ctx->primary,
- MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE,
- NULL);
-
-
- if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->primary), error)) {
- g_prefix_error (error, "Couldn't open primary port: ");
- return FALSE;
- }
-
- ctx->primary_open = TRUE;
-
- /* Open secondary (optional) */
- ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self));
- if (ctx->secondary) {
- /* If we'll need to run modem initialization, disable port init sequence */
- if (modem_init_required)
- g_object_set (ctx->secondary,
- MM_PORT_SERIAL_AT_INIT_SEQUENCE_ENABLED, FALSE,
- NULL);
- if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->secondary), error)) {
- g_prefix_error (error, "Couldn't open secondary port: ");
- return FALSE;
- }
- ctx->secondary_open = TRUE;
- }
-
- /* Open qcdm (optional) */
- ctx->qcdm = mm_base_modem_get_port_qcdm (MM_BASE_MODEM (self));
- if (ctx->qcdm) {
- if (!mm_port_serial_open (MM_PORT_SERIAL (ctx->qcdm), error)) {
- g_prefix_error (error, "Couldn't open QCDM port: ");
- return FALSE;
- }
- ctx->qcdm_open = TRUE;
- }
-
- return TRUE;
+ self->priv->enabled_ports_ctx = ports_context_ref (ctx->ports);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -7980,63 +11036,119 @@ enabling_started (MMBroadbandModem *self,
{
GError *error = NULL;
EnablingStartedContext *ctx;
+ GTask *task;
ctx = g_slice_new0 (EnablingStartedContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enabling_started);
- ctx->ports = g_new0 (PortsContext, 1);
- ctx->ports->ref_count = 1;
+ ctx->ports = ports_context_new ();
/* Skip modem initialization if the device was hotplugged OR if we already
* did it (i.e. don't reinitialize if the modem got disabled and enabled
* again) */
- if (ctx->self->priv->modem_init_run)
- mm_dbg ("Skipping modem initialization: not first enabling");
- else if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (ctx->self))) {
- ctx->self->priv->modem_init_run = TRUE;
- mm_dbg ("Skipping modem initialization: device hotplugged");
- } else if (!MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_modem_init ||
- !MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_modem_init_finish)
- mm_dbg ("Skipping modem initialization: not required");
+ if (self->priv->modem_init_run)
+ mm_obj_dbg (self, "skipping initialization: not first enabling");
+ else if (mm_base_modem_get_hotplugged (MM_BASE_MODEM (self))) {
+ self->priv->modem_init_run = TRUE;
+ mm_obj_dbg (self, "skipping initialization: device hotplugged");
+ } else if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init ||
+ !MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_modem_init_finish)
+ mm_obj_dbg (self, "skipping initialization: not required");
else
ctx->modem_init_required = TRUE;
- /* Enabling */
- if (!open_ports_enabling (self, ctx->ports, ctx->modem_init_required, &error)) {
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_started_context_free);
+
+ /* Open ports for enabling, including secondary AT port and QCDM if available */
+ if (!ports_context_open (self, ctx->ports, ctx->modem_init_required, TRUE, TRUE, &error)) {
g_prefix_error (&error, "Couldn't open ports during modem enabling: ");
- g_simple_async_result_take_error (ctx->result, error);
- enabling_started_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Ports were correctly opened, now flash the primary port */
- mm_dbg ("Flashing primary AT port before enabling...");
+ mm_obj_dbg (self, "flashing primary AT port before enabling...");
mm_port_serial_flash (MM_PORT_SERIAL (ctx->ports->primary),
100,
FALSE,
(GAsyncReadyCallback)enabling_flash_done,
- ctx);
+ task);
+}
+
+/*****************************************************************************/
+/* First registration checks */
+
+static void
+modem_3gpp_run_registration_checks_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error)) {
+ mm_obj_warn (self, "initial 3GPP registration check failed: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ mm_obj_dbg (self, "initial 3GPP registration checks finished");
+}
+
+static void
+modem_cdma_run_registration_checks_ready (MMIfaceModemCdma *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ if (!mm_iface_modem_cdma_run_registration_checks_finish (self, res, &error)) {
+ mm_obj_warn (self, "initial CDMA registration check failed: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+ mm_obj_dbg (self, "initial CDMA registration checks finished");
+}
+
+static gboolean
+schedule_initial_registration_checks_cb (MMBroadbandModem *self)
+{
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
+ mm_iface_modem_3gpp_run_registration_checks (MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback) modem_3gpp_run_registration_checks_ready,
+ NULL);
+ if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
+ mm_iface_modem_cdma_run_registration_checks (MM_IFACE_MODEM_CDMA (self),
+ (GAsyncReadyCallback) modem_cdma_run_registration_checks_ready,
+ NULL);
+ /* We got a full reference, so balance it out here */
+ g_object_unref (self);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+schedule_initial_registration_checks (MMBroadbandModem *self)
+{
+ g_idle_add ((GSourceFunc) schedule_initial_registration_checks_cb, g_object_ref (self));
}
/*****************************************************************************/
typedef enum {
+ /* When user requests a disable operation, the process starts here */
DISABLING_STEP_FIRST,
DISABLING_STEP_WAIT_FOR_FINAL_STATE,
DISABLING_STEP_DISCONNECT_BEARERS,
+ /* When the disabling is launched due to a failed enable, the process
+ * starts here */
+ DISABLING_STEP_FIRST_AFTER_ENABLE_FAILED,
DISABLING_STEP_IFACE_SIMPLE,
DISABLING_STEP_IFACE_FIRMWARE,
+ DISABLING_STEP_IFACE_VOICE,
DISABLING_STEP_IFACE_SIGNAL,
DISABLING_STEP_IFACE_OMA,
DISABLING_STEP_IFACE_TIME,
DISABLING_STEP_IFACE_MESSAGING,
DISABLING_STEP_IFACE_LOCATION,
- DISABLING_STEP_IFACE_CONTACTS,
DISABLING_STEP_IFACE_CDMA,
DISABLING_STEP_IFACE_3GPP_USSD,
+ DISABLING_STEP_IFACE_3GPP_PROFILE_MANAGER,
DISABLING_STEP_IFACE_3GPP,
DISABLING_STEP_IFACE_MODEM,
DISABLING_STEP_LAST,
@@ -8044,136 +11156,120 @@ typedef enum {
typedef struct {
MMBroadbandModem *self;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- DisablingStep step;
- MMModemState previous_state;
- gboolean disabled;
+ gboolean state_updates;
+ DisablingStep step;
+ MMModemState previous_state;
+ gboolean disabled;
} DisablingContext;
-static void disabling_step (DisablingContext *ctx);
+static void disabling_step (GTask *task);
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_free (DisablingContext *ctx)
{
GError *error = NULL;
- g_simple_async_result_complete_in_idle (ctx->result);
-
if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->disabling_stopped &&
!MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->disabling_stopped (ctx->self, &error)) {
- mm_warn ("Error when stopping the disabling sequence: %s", error->message);
+ mm_obj_warn (ctx->self, "error when stopping the disabling sequence: %s", error->message);
g_error_free (error);
}
- if (ctx->disabled)
- mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self),
- MM_MODEM_STATE_DISABLED,
- MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
- else if (ctx->previous_state != MM_MODEM_STATE_DISABLED) {
- /* Fallback to previous state */
- mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self),
- ctx->previous_state,
- MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
+ /* Only perform state updates if we're asked to do so */
+ if (ctx->state_updates) {
+ if (ctx->disabled)
+ mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self),
+ MM_MODEM_STATE_DISABLED,
+ MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
+ else if (ctx->previous_state != MM_MODEM_STATE_DISABLED) {
+ /* Fallback to previous state */
+ mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self),
+ ctx->previous_state,
+ MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
+ }
}
- g_object_unref (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->self);
g_free (ctx);
}
static gboolean
-disabling_context_complete_and_free_if_cancelled (DisablingContext *ctx)
+common_disable_finish (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
-
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Disabling cancelled");
- disabling_context_complete_and_free (ctx);
- return TRUE;
-}
-
-static gboolean
-disable_finish (MMBaseModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
#undef INTERFACE_DISABLE_READY_FN
-#define INTERFACE_DISABLE_READY_FN(NAME,TYPE,FATAL_ERRORS) \
- static void \
- NAME##_disable_ready (MMBroadbandModem *self, \
- GAsyncResult *result, \
- DisablingContext *ctx) \
- { \
- GError *error = NULL; \
- \
- if (!mm_##NAME##_disable_finish (TYPE (self), \
- result, \
- &error)) { \
- if (FATAL_ERRORS) { \
- g_simple_async_result_take_error (ctx->result, error); \
- disabling_context_complete_and_free (ctx); \
- return; \
- } \
- \
- mm_dbg ("Couldn't disable interface: '%s'", \
- error->message); \
- g_error_free (error); \
- return; \
- } \
- \
- /* Go on to next step */ \
- ctx->step++; \
- disabling_step (ctx); \
- }
-
-INTERFACE_DISABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
-INTERFACE_DISABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
-INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, TRUE)
-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)
+#define INTERFACE_DISABLE_READY_FN(NAME,TYPE,WARN_ERRORS) \
+ static void \
+ NAME##_disable_ready (MMBroadbandModem *self, \
+ GAsyncResult *result, \
+ GTask *task) \
+ { \
+ DisablingContext *ctx; \
+ g_autoptr(GError) error = NULL; \
+ \
+ if (!mm_##NAME##_disable_finish (TYPE (self), result, &error)) { \
+ if (WARN_ERRORS) \
+ mm_obj_warn (self, "couldn't disable interface: %s", error->message); \
+ else \
+ mm_obj_dbg (self, "couldn't disable interface: %s", error->message); \
+ } \
+ \
+ /* Go on to next step */ \
+ ctx = g_task_get_task_data (task); \
+ ctx->step++; \
+ disabling_step (task); \
+ }
+
+INTERFACE_DISABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
+INTERFACE_DISABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
+INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
+INTERFACE_DISABLE_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE)
+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_voice, MM_IFACE_MODEM_VOICE, 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,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
if (!mm_bearer_list_disconnect_all_bearers_finish (list, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- disabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- disabling_step (ctx);
+ disabling_step (task);
}
static void
disabling_wait_for_final_state_ready (MMIfaceModem *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
ctx->previous_state = mm_iface_modem_wait_for_final_state_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- disabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -8186,200 +11282,295 @@ disabling_wait_for_final_state_ready (MMIfaceModem *self,
* Note that we do consider here UNKNOWN and FAILED status on purpose,
* as the MMManager will try to disable every modem before removing
* it. */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disabling_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+ case MM_MODEM_STATE_INITIALIZING:
+ case MM_MODEM_STATE_DISABLING:
+ case MM_MODEM_STATE_ENABLING:
+ case MM_MODEM_STATE_ENABLED:
+ case MM_MODEM_STATE_SEARCHING:
+ case MM_MODEM_STATE_REGISTERED:
+ case MM_MODEM_STATE_DISCONNECTING:
+ case MM_MODEM_STATE_CONNECTING:
+ case MM_MODEM_STATE_CONNECTED:
default:
break;
}
/* We're in a final state now, go on */
+ g_assert (ctx->state_updates);
mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self),
MM_MODEM_STATE_DISABLING,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
ctx->step++;
- disabling_step (ctx);
+ disabling_step (task);
}
static void
-disabling_step (DisablingContext *ctx)
+disabling_step (GTask *task)
{
- /* Don't run new steps if we're cancelled */
- if (disabling_context_complete_and_free_if_cancelled (ctx))
- return;
+ DisablingContext *ctx;
+
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_WAIT_FOR_FINAL_STATE:
+ /* cancellability allowed at this point */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self),
MM_MODEM_STATE_UNKNOWN, /* just any */
(GAsyncReadyCallback)disabling_wait_for_final_state_ready,
- ctx);
+ task);
return;
case DISABLING_STEP_DISCONNECT_BEARERS:
+ /* cancellability allowed at this point */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
if (ctx->self->priv->modem_bearer_list) {
mm_bearer_list_disconnect_all_bearers (
ctx->self->priv->modem_bearer_list,
(GAsyncReadyCallback)bearer_list_disconnect_all_bearers_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_FIRST_AFTER_ENABLE_FAILED:
+ /* From this point onwards, the disabling sequence will NEVER fail, all
+ * errors will be treated as non-fatal, including a possible task
+ * cancellation. */
+ g_task_set_check_cancellable (task, FALSE);
+ ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_SIMPLE:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_FIRMWARE:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_IFACE_VOICE:
+ if (ctx->self->priv->modem_voice_dbus_skeleton) {
+ mm_obj_dbg (ctx->self, "modem has voice capabilities, disabling the Voice interface...");
+ mm_iface_modem_voice_disable (MM_IFACE_MODEM_VOICE (ctx->self),
+ (GAsyncReadyCallback)iface_modem_voice_disable_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
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_obj_dbg (ctx->self, "modem has extended signal reporting capabilities, disabling the Signal interface...");
mm_iface_modem_signal_disable (MM_IFACE_MODEM_SIGNAL (ctx->self),
(GAsyncReadyCallback)iface_modem_signal_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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_obj_dbg (ctx->self, "modem has OMA capabilities, disabling the OMA interface...");
mm_iface_modem_oma_disable (MM_IFACE_MODEM_OMA (ctx->self),
(GAsyncReadyCallback)iface_modem_oma_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_TIME:
if (ctx->self->priv->modem_time_dbus_skeleton) {
- mm_dbg ("Modem has time capabilities, disabling the Time interface...");
- /* Disabling the Modem Time interface */
+ mm_obj_dbg (ctx->self, "modem has time capabilities, disabling the Time interface...");
mm_iface_modem_time_disable (MM_IFACE_MODEM_TIME (ctx->self),
(GAsyncReadyCallback)iface_modem_time_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_MESSAGING:
if (ctx->self->priv->modem_messaging_dbus_skeleton) {
- mm_dbg ("Modem has messaging capabilities, disabling the Messaging interface...");
- /* Disabling the Modem Messaging interface */
+ mm_obj_dbg (ctx->self, "modem has messaging capabilities, disabling the Messaging interface...");
mm_iface_modem_messaging_disable (MM_IFACE_MODEM_MESSAGING (ctx->self),
(GAsyncReadyCallback)iface_modem_messaging_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_LOCATION:
if (ctx->self->priv->modem_location_dbus_skeleton) {
- mm_dbg ("Modem has location capabilities, disabling the Location interface...");
- /* Disabling the Modem Location interface */
+ mm_obj_dbg (ctx->self, "modem has location capabilities, disabling the Location interface...");
mm_iface_modem_location_disable (MM_IFACE_MODEM_LOCATION (ctx->self),
(GAsyncReadyCallback)iface_modem_location_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
- ctx->step++;
-
- case DISABLING_STEP_IFACE_CONTACTS:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_CDMA:
if (ctx->self->priv->modem_cdma_dbus_skeleton) {
- mm_dbg ("Modem has CDMA capabilities, disabling the Modem CDMA interface...");
- /* Disabling the Modem CDMA interface */
+ mm_obj_dbg (ctx->self, "modem has CDMA capabilities, disabling the Modem CDMA interface...");
mm_iface_modem_cdma_disable (MM_IFACE_MODEM_CDMA (ctx->self),
(GAsyncReadyCallback)iface_modem_cdma_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_3GPP_USSD:
if (ctx->self->priv->modem_3gpp_ussd_dbus_skeleton) {
- mm_dbg ("Modem has 3GPP/USSD capabilities, disabling the Modem 3GPP/USSD interface...");
- /* Disabling the Modem 3GPP USSD interface */
+ mm_obj_dbg (ctx->self, "modem has 3GPP/USSD capabilities, disabling the Modem 3GPP/USSD interface...");
mm_iface_modem_3gpp_ussd_disable (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
(GAsyncReadyCallback)iface_modem_3gpp_ussd_disable_ready,
- ctx);
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_IFACE_3GPP_PROFILE_MANAGER:
+ if (ctx->self->priv->modem_3gpp_profile_manager_dbus_skeleton) {
+ mm_obj_dbg (ctx->self, "modem has 3GPP profile management capabilities, disabling the Modem 3GPP Profile Manager interface...");
+ mm_iface_modem_3gpp_profile_manager_disable (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self),
+ (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_disable_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_3GPP:
if (ctx->self->priv->modem_3gpp_dbus_skeleton) {
- mm_dbg ("Modem has 3GPP capabilities, disabling the Modem 3GPP interface...");
- /* Disabling the Modem 3GPP interface */
+ mm_obj_dbg (ctx->self, "modem has 3GPP capabilities, disabling the Modem 3GPP interface...");
mm_iface_modem_3gpp_disable (MM_IFACE_MODEM_3GPP (ctx->self),
(GAsyncReadyCallback)iface_modem_3gpp_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_IFACE_MODEM:
/* This skeleton may be NULL when mm_base_modem_disable() gets called at
* the same time as modem object disposal. */
if (ctx->self->priv->modem_dbus_skeleton) {
- /* Disabling the Modem interface */
+ mm_obj_dbg (ctx->self, "disabling the Modem interface...");
mm_iface_modem_disable (MM_IFACE_MODEM (ctx->self),
(GAsyncReadyCallback)iface_modem_disable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_LAST:
- ctx->disabled = TRUE;
/* All disabled without errors! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disabling_context_complete_and_free (ctx);
+ ctx->disabled = TRUE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
}
static void
-disable (MMBaseModem *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+common_disable (MMBroadbandModem *self,
+ gboolean state_updates,
+ DisablingStep first_step,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
ctx = g_new0 (DisablingContext, 1);
ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, disable);
- ctx->cancellable = (cancellable ? g_object_ref (cancellable) : NULL);
- ctx->step = DISABLING_STEP_FIRST;
+ ctx->state_updates = state_updates;
+ ctx->step = first_step;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ disabling_step (task);
+}
+
+/* Implicit disabling after failed enable */
+
+static gboolean
+enable_failed_finish (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ /* The implicit disabling should never ever fail */
+ g_assert (common_disable_finish (self, res, NULL));
+ return TRUE;
+}
+
+static void
+enable_failed (MMBroadbandModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_disable (self,
+ FALSE, /* don't perform state updates */
+ DISABLING_STEP_FIRST_AFTER_ENABLE_FAILED,
+ NULL, /* no cancellable */
+ callback,
+ user_data);
+}
- disabling_step (ctx);
+/* User-requested disable operation */
+
+static gboolean
+disable_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return common_disable_finish (MM_BROADBAND_MODEM (self), res, error);
+}
+
+static void
+disable (MMBaseModem *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ common_disable (MM_BROADBAND_MODEM (self),
+ TRUE, /* perform state updates */
+ DISABLING_STEP_FIRST,
+ cancellable,
+ callback,
+ user_data);
}
/*****************************************************************************/
@@ -8390,14 +11581,15 @@ typedef enum {
ENABLING_STEP_STARTED,
ENABLING_STEP_IFACE_MODEM,
ENABLING_STEP_IFACE_3GPP,
+ ENABLING_STEP_IFACE_3GPP_PROFILE_MANAGER,
ENABLING_STEP_IFACE_3GPP_USSD,
ENABLING_STEP_IFACE_CDMA,
- ENABLING_STEP_IFACE_CONTACTS,
ENABLING_STEP_IFACE_LOCATION,
ENABLING_STEP_IFACE_MESSAGING,
ENABLING_STEP_IFACE_TIME,
ENABLING_STEP_IFACE_SIGNAL,
ENABLING_STEP_IFACE_OMA,
+ ENABLING_STEP_IFACE_VOICE,
ENABLING_STEP_IFACE_FIRMWARE,
ENABLING_STEP_IFACE_SIMPLE,
ENABLING_STEP_LAST,
@@ -8405,20 +11597,18 @@ typedef enum {
typedef struct {
MMBroadbandModem *self;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- EnablingStep step;
- MMModemState previous_state;
- gboolean enabled;
+ EnablingStep step;
+ MMModemState previous_state;
+ gboolean enabled;
+ GError *saved_error;
} EnablingContext;
-static void enabling_step (EnablingContext *ctx);
+static void enabling_step (GTask *task);
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_free (EnablingContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
+ g_assert (!ctx->saved_error);
if (ctx->enabled)
mm_iface_modem_update_state (MM_IFACE_MODEM (ctx->self),
@@ -8431,110 +11621,118 @@ enabling_context_complete_and_free (EnablingContext *ctx)
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
}
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->self);
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,
- "Enabling cancelled");
- enabling_context_complete_and_free (ctx);
- return TRUE;
-}
-
-static gboolean
enable_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return FALSE;
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
- return TRUE;
+static void
+enable_failed_ready (MMBroadbandModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* The disabling run after a failed enable will never fail */
+ g_assert (enable_failed_finish (self, res, NULL));
+
+ g_assert (ctx->saved_error);
+ g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
+ g_object_unref (task);
}
#undef INTERFACE_ENABLE_READY_FN
-#define INTERFACE_ENABLE_READY_FN(NAME,TYPE,FATAL_ERRORS) \
- static void \
- NAME##_enable_ready (MMBroadbandModem *self, \
- GAsyncResult *result, \
- EnablingContext *ctx) \
- { \
- GError *error = NULL; \
- \
- if (!mm_##NAME##_enable_finish (TYPE (self), \
- result, \
- &error)) { \
- if (FATAL_ERRORS) { \
- g_simple_async_result_take_error (ctx->result, error); \
- enabling_context_complete_and_free (ctx); \
- return; \
- } \
- \
- mm_dbg ("Couldn't enable interface: '%s'", \
- error->message); \
- g_error_free (error); \
- } \
- \
- /* Go on to next step */ \
- ctx->step++; \
- enabling_step (ctx); \
- }
-
-INTERFACE_ENABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
-INTERFACE_ENABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
-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)
+#define INTERFACE_ENABLE_READY_FN(NAME,TYPE,FATAL_ERRORS) \
+ static void \
+ NAME##_enable_ready (MMBroadbandModem *self, \
+ GAsyncResult *result, \
+ GTask *task) \
+ { \
+ EnablingContext *ctx; \
+ g_autoptr(GError) error = NULL; \
+ \
+ ctx = g_task_get_task_data (task); \
+ \
+ if (!mm_##NAME##_enable_finish (TYPE (self), result, &error)) { \
+ if (FATAL_ERRORS) { \
+ mm_obj_warn (self, "couldn't enable interface: '%s'", error->message); \
+ g_assert (!ctx->saved_error); \
+ ctx->saved_error = g_steal_pointer (&error); \
+ mm_obj_dbg (self, "running implicit disable after failed enable..."); \
+ enable_failed (self, (GAsyncReadyCallback) enable_failed_ready, task); \
+ return; \
+ } \
+ \
+ mm_obj_dbg (self, "couldn't enable interface: '%s'", error->message); \
+ } \
+ \
+ /* Go on to next step */ \
+ ctx->step++; \
+ enabling_step (task); \
+ }
+
+INTERFACE_ENABLE_READY_FN (iface_modem, MM_IFACE_MODEM, TRUE)
+INTERFACE_ENABLE_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
+INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE)
+INTERFACE_ENABLE_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
+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_voice, MM_IFACE_MODEM_VOICE, 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,
GAsyncResult *result,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
if (!MM_BROADBAND_MODEM_GET_CLASS (self)->enabling_started_finish (self, result, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- enabling_step (ctx);
+ enabling_step (task);
}
static void
enabling_wait_for_final_state_ready (MMIfaceModem *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
ctx->previous_state = mm_iface_modem_wait_for_final_state_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (ctx->previous_state >= MM_MODEM_STATE_ENABLED) {
/* Just return success, don't relaunch enabling */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -8545,26 +11743,32 @@ enabling_wait_for_final_state_ready (MMIfaceModem *self,
MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED);
ctx->step++;
- enabling_step (ctx);
+ enabling_step (task);
}
static void
-enabling_step (EnablingContext *ctx)
+enabling_step (GTask *task)
{
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_WAIT_FOR_FINAL_STATE:
mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self),
MM_MODEM_STATE_UNKNOWN, /* just any */
(GAsyncReadyCallback)enabling_wait_for_final_state_ready,
- ctx);
+ task);
return;
case ENABLING_STEP_STARTED:
@@ -8572,141 +11776,177 @@ enabling_step (EnablingContext *ctx)
MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_started_finish) {
MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->enabling_started (ctx->self,
(GAsyncReadyCallback)enabling_started_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_MODEM:
+ /* From now on, the failure to enable one of the mandatory interfaces
+ * will trigger the implicit disabling process */
+
g_assert (ctx->self->priv->modem_dbus_skeleton != NULL);
/* Enabling the Modem interface */
mm_iface_modem_enable (MM_IFACE_MODEM (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_enable_ready,
- ctx);
+ task);
return;
case ENABLING_STEP_IFACE_3GPP:
if (ctx->self->priv->modem_3gpp_dbus_skeleton) {
- mm_dbg ("Modem has 3GPP capabilities, enabling the Modem 3GPP interface...");
+ mm_obj_dbg (ctx->self, "modem has 3GPP capabilities, enabling the Modem 3GPP interface...");
/* Enabling the Modem 3GPP interface */
mm_iface_modem_3gpp_enable (MM_IFACE_MODEM_3GPP (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_3gpp_enable_ready,
- ctx);
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_IFACE_3GPP_PROFILE_MANAGER:
+ if (ctx->self->priv->modem_3gpp_profile_manager_dbus_skeleton) {
+ mm_obj_dbg (ctx->self, "modem has 3GPP profile management capabilities, enabling the Modem 3GPP Profile Manager interface...");
+ mm_iface_modem_3gpp_profile_manager_enable (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self),
+ (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_enable_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_3GPP_USSD:
if (ctx->self->priv->modem_3gpp_ussd_dbus_skeleton) {
- mm_dbg ("Modem has 3GPP/USSD capabilities, enabling the Modem 3GPP/USSD interface...");
+ mm_obj_dbg (ctx->self, "modem has 3GPP/USSD capabilities, enabling the Modem 3GPP/USSD interface...");
mm_iface_modem_3gpp_ussd_enable (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
(GAsyncReadyCallback)iface_modem_3gpp_ussd_enable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_CDMA:
if (ctx->self->priv->modem_cdma_dbus_skeleton) {
- mm_dbg ("Modem has CDMA capabilities, enabling the Modem CDMA interface...");
+ mm_obj_dbg (ctx->self, "modem has CDMA capabilities, enabling the Modem CDMA interface...");
/* Enabling the Modem CDMA interface */
mm_iface_modem_cdma_enable (MM_IFACE_MODEM_CDMA (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_cdma_enable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
- ctx->step++;
-
- case ENABLING_STEP_IFACE_CONTACTS:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_LOCATION:
if (ctx->self->priv->modem_location_dbus_skeleton) {
- mm_dbg ("Modem has location capabilities, enabling the Location interface...");
+ mm_obj_dbg (ctx->self, "modem has location capabilities, enabling the Location interface...");
/* Enabling the Modem Location interface */
mm_iface_modem_location_enable (MM_IFACE_MODEM_LOCATION (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_location_enable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_MESSAGING:
if (ctx->self->priv->modem_messaging_dbus_skeleton) {
- mm_dbg ("Modem has messaging capabilities, enabling the Messaging interface...");
+ mm_obj_dbg (ctx->self, "modem has messaging capabilities, enabling the Messaging interface...");
/* Enabling the Modem Messaging interface */
mm_iface_modem_messaging_enable (MM_IFACE_MODEM_MESSAGING (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_messaging_enable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_TIME:
if (ctx->self->priv->modem_time_dbus_skeleton) {
- mm_dbg ("Modem has time capabilities, enabling the Time interface...");
+ mm_obj_dbg (ctx->self, "modem has time capabilities, enabling the Time interface...");
/* Enabling the Modem Time interface */
mm_iface_modem_time_enable (MM_IFACE_MODEM_TIME (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_time_enable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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...");
+ mm_obj_dbg (ctx->self, "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,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_signal_enable_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_OMA:
if (ctx->self->priv->modem_oma_dbus_skeleton) {
- mm_dbg ("Modem has OMA capabilities, enabling the OMA interface...");
+ mm_obj_dbg (ctx->self, "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,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_oma_enable_ready,
- ctx);
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_IFACE_VOICE:
+ if (ctx->self->priv->modem_voice_dbus_skeleton) {
+ mm_obj_dbg (ctx->self, "modem has voice capabilities, enabling the Voice interface...");
+ /* Enabling the Modem Voice interface */
+ mm_iface_modem_voice_enable (MM_IFACE_MODEM_VOICE (ctx->self),
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)iface_modem_voice_enable_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_FIRMWARE:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_IFACE_SIMPLE:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_LAST:
ctx->enabled = TRUE;
+
+ /* Once all interfaces have been enabled, trigger registration checks in
+ * 3GPP and CDMA modems. We have to do this at this point so that e.g.
+ * location interface gets proper registration related info reported.
+ *
+ * We do this in an idle so that we don't mess up the logs at this point
+ * with the new requests being triggered.
+ */
+ schedule_initial_registration_checks (ctx->self);
+
/* All enabled without errors! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -8718,9 +11958,9 @@ enable (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, enable);
+ task = g_task_new (self, cancellable, callback, user_data);
/* Check state before launching modem enabling */
switch (MM_BROADBAND_MODEM (self)->priv->modem_state) {
@@ -8730,47 +11970,36 @@ enable (MMBaseModem *self,
break;
case MM_MODEM_STATE_FAILED:
- case MM_MODEM_STATE_INITIALIZING:
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot enable modem: "
- "device not fully initialized yet");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot enable modem: initialization failed");
break;
case MM_MODEM_STATE_LOCKED:
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot enable modem: device locked");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot enable modem: device locked");
break;
- case MM_MODEM_STATE_DISABLED: {
+ case MM_MODEM_STATE_INITIALIZING:
+ case MM_MODEM_STATE_DISABLED:
+ case MM_MODEM_STATE_DISABLING: {
EnablingContext *ctx;
ctx = g_new0 (EnablingContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = result;
- ctx->cancellable = g_object_ref (cancellable);
+ ctx->self = MM_BROADBAND_MODEM (g_object_ref (self));
ctx->step = ENABLING_STEP_FIRST;
- enabling_step (ctx);
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ enabling_step (task);
return;
}
- case MM_MODEM_STATE_DISABLING:
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot enable modem: "
- "currently being disabled");
- break;
-
case MM_MODEM_STATE_ENABLING:
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "Cannot enable modem: "
- "already being enabled");
+ g_assert_not_reached ();
break;
case MM_MODEM_STATE_ENABLED:
@@ -8780,13 +12009,220 @@ enable (MMBaseModem *self,
case MM_MODEM_STATE_CONNECTING:
case MM_MODEM_STATE_CONNECTED:
/* Just return success, don't relaunch enabling */
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_task_return_boolean (task, TRUE);
break;
+
+ default:
+ g_assert_not_reached ();
}
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
}
+/*****************************************************************************/
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef enum {
+ SYNCING_STEP_FIRST,
+ SYNCING_STEP_IFACE_MODEM,
+ SYNCING_STEP_IFACE_3GPP,
+ SYNCING_STEP_IFACE_TIME,
+ SYNCING_STEP_LAST,
+} SyncingStep;
+
+typedef struct {
+ SyncingStep step;
+} SyncingContext;
+
+static void syncing_step (GTask *task);
+
+static gboolean
+synchronize_finish (MMBaseModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+iface_modem_time_sync_ready (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_time_sync_finish (self, res, &error))
+ mm_obj_warn (self, "time interface synchronization failed: %s", error->message);
+
+ /* Go on to next step */
+ ctx->step++;
+ syncing_step (task);
+}
+
+static void
+iface_modem_3gpp_sync_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_3gpp_sync_finish (self, res, &error))
+ mm_obj_warn (self, "3GPP interface synchronization failed: %s", error->message);
+
+ /* Go on to next step */
+ ctx->step++;
+ syncing_step (task);
+}
+
+static void
+iface_modem_sync_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ MMModemLock lock;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_sync_finish (self, res, &error))
+ mm_obj_warn (self, "modem interface synchronization failed: %s", error->message);
+
+ /* The synchronization logic only runs on modems that were enabled before
+ * the suspend/resume cycle, and therefore we should not get SIM-PIN locked
+ * at this point, unless the SIM was swapped. */
+ lock = mm_iface_modem_get_unlock_required (self);
+ if (lock == MM_MODEM_LOCK_UNKNOWN || lock == MM_MODEM_LOCK_SIM_PIN || lock == MM_MODEM_LOCK_SIM_PUK) {
+ /* Abort the sync() operation right away, and report a new SIM event that will
+ * disable the modem and trigger a full reprobe */
+ mm_obj_warn (self, "SIM is locked... synchronization aborted");
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Locked SIM found during modem interface synchronization");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Not locked, go on to next step */
+ mm_obj_dbg (self, "modem unlocked, continue synchronization");
+ ctx->step++;
+ syncing_step (task);
+ return;
+}
+
+static void
+syncing_step (GTask *task)
+{
+ MMBroadbandModem *self;
+ SyncingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SYNCING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_IFACE_MODEM:
+ /*
+ * Start interface Modem synchronization.
+ * We want to make sure that the SIM is unlocked and not swapped before
+ * synchronizing other interfaces.
+ */
+ if (!self->priv->modem_dbus_skeleton) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Synchronization aborted: no modem exposed in DBus");
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_info (self, "resume synchronization state (%d/%d): modem interface sync",
+ ctx->step, SYNCING_STEP_LAST);
+ mm_iface_modem_sync (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)iface_modem_sync_ready,
+ task);
+ return;
+
+ case SYNCING_STEP_IFACE_3GPP:
+ /*
+ * Start interface 3GPP synchronization.
+ * We hardly depend on the registration and bearer status,
+ * therefore we cannot continue with the other steps until
+ * this one is finished.
+ */
+ if (self->priv->modem_3gpp_dbus_skeleton) {
+ mm_obj_info (self, "resume synchronization state (%d/%d): 3GPP interface sync",
+ ctx->step, SYNCING_STEP_LAST);
+ mm_iface_modem_3gpp_sync (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)iface_modem_3gpp_sync_ready, task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_IFACE_TIME:
+ /*
+ * Synchronize asynchronously the Time interface.
+ */
+ if (self->priv->modem_time_dbus_skeleton) {
+ mm_obj_info (self, "resume synchronization state (%d/%d): time interface sync",
+ ctx->step, SYNCING_STEP_LAST);
+ mm_iface_modem_time_sync (MM_IFACE_MODEM_TIME (self), (GAsyncReadyCallback)iface_modem_time_sync_ready, task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_LAST:
+ mm_obj_info (self, "resume synchronization state (%d/%d): all done",
+ ctx->step, SYNCING_STEP_LAST);
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+/* 'sync' as function name conflicts with a declared function in unistd.h */
+static void
+synchronize (MMBaseModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncingContext *ctx;
+ GTask *task;
+
+ task = g_task_new (MM_BROADBAND_MODEM (self), NULL, callback, user_data);
+
+ /* Synchronization after resume is not needed on modems that have never
+ * been enabled.
+ */
+ if (MM_BROADBAND_MODEM (self)->priv->modem_state < MM_MODEM_STATE_ENABLED) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Synchronization after resume not needed in modem state '%s'",
+ mm_modem_state_get_string (MM_BROADBAND_MODEM (self)->priv->modem_state));
+ g_object_unref (task);
+ return;
+ }
+
+ /* Create SyncingContext */
+ ctx = g_new0 (SyncingContext, 1);
+ ctx->step = SYNCING_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
+
+ syncing_step (task);
+}
+
+#endif
/*****************************************************************************/
@@ -8797,86 +12233,71 @@ typedef enum {
INITIALIZE_STEP_SETUP_SIMPLE_STATUS,
INITIALIZE_STEP_IFACE_MODEM,
INITIALIZE_STEP_IFACE_3GPP,
+ INITIALIZE_STEP_JUMP_TO_LIMITED,
+ INITIALIZE_STEP_IFACE_3GPP_PROFILE_MANAGER,
INITIALIZE_STEP_IFACE_3GPP_USSD,
INITIALIZE_STEP_IFACE_CDMA,
- INITIALIZE_STEP_IFACE_CONTACTS,
INITIALIZE_STEP_IFACE_LOCATION,
INITIALIZE_STEP_IFACE_MESSAGING,
INITIALIZE_STEP_IFACE_TIME,
INITIALIZE_STEP_IFACE_SIGNAL,
INITIALIZE_STEP_IFACE_OMA,
+ INITIALIZE_STEP_IFACE_SAR,
+ INITIALIZE_STEP_FALLBACK_LIMITED,
+ INITIALIZE_STEP_IFACE_VOICE,
INITIALIZE_STEP_IFACE_FIRMWARE,
+ INITIALIZE_STEP_SIM_HOT_SWAP,
INITIALIZE_STEP_IFACE_SIMPLE,
INITIALIZE_STEP_LAST,
} InitializeStep;
typedef struct {
MMBroadbandModem *self;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
InitializeStep step;
gpointer ports_ctx;
} InitializeContext;
-static void initialize_step (InitializeContext *ctx);
+static void initialize_step (GTask *task);
static void
-initialize_context_complete_and_free (InitializeContext *ctx)
+initialize_context_free (InitializeContext *ctx)
{
GError *error = NULL;
- g_simple_async_result_complete_in_idle (ctx->result);
-
if (ctx->ports_ctx &&
MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_stopped &&
!MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_stopped (ctx->self, ctx->ports_ctx, &error)) {
- mm_warn ("Error when stopping the initialization sequence: %s", error->message);
+ mm_obj_warn (ctx->self, "error when stopping the initialization sequence: %s", error->message);
g_error_free (error);
}
- g_object_unref (ctx->result);
- g_object_unref (ctx->cancellable);
g_object_unref (ctx->self);
g_free (ctx);
}
static gboolean
-initialize_context_complete_and_free_if_cancelled (InitializeContext *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,
- "Initialization cancelled");
- initialize_context_complete_and_free (ctx);
- return TRUE;
-}
-
-static gboolean
initialize_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return FALSE;
-
- return TRUE;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
initialization_started_ready (MMBroadbandModem *self,
GAsyncResult *result,
- InitializeContext *ctx)
+ GTask *task)
{
+ InitializeContext *ctx;
GError *error = NULL;
gpointer ports_ctx;
+ ctx = g_task_get_task_data (task);
+
/* May return NULL without error */
ports_ctx = MM_BROADBAND_MODEM_GET_CLASS (self)->initialization_started_finish (self, result, &error);
if (error) {
- mm_warn ("Couldn't start initialization: %s", error->message);
+ mm_obj_warn (self, "couldn't start initialization: %s", error->message);
g_error_free (error);
/* There is no Modem interface yet, so just update the variable directly */
@@ -8884,7 +12305,7 @@ initialization_started_ready (MMBroadbandModem *self,
/* Just jump to the last step */
ctx->step = INITIALIZE_STEP_LAST;
- initialize_step (ctx);
+ initialize_step (task);
return;
}
@@ -8893,23 +12314,26 @@ initialization_started_ready (MMBroadbandModem *self,
/* Go on to next step */
ctx->step++;
- initialize_step (ctx);
+ initialize_step (task);
}
static void
iface_modem_initialize_ready (MMBroadbandModem *self,
GAsyncResult *result,
- InitializeContext *ctx)
+ GTask *task)
{
+ InitializeContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
/* If the modem interface fails to get initialized, we will move the modem
* to a FAILED state. Note that in this case we still export the interface. */
if (!mm_iface_modem_initialize_finish (MM_IFACE_MODEM (self), result, &error)) {
MMModemStateFailedReason failed_reason = MM_MODEM_STATE_FAILED_REASON_UNKNOWN;
/* Report the new FAILED state */
- mm_warn ("Modem couldn't be initialized: %s", error->message);
+ mm_obj_warn (self, "modem couldn't be initialized: %s", error->message);
if (g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
@@ -8927,10 +12351,10 @@ iface_modem_initialize_ready (MMBroadbandModem *self,
mm_iface_modem_update_failed_state (MM_IFACE_MODEM (self), failed_reason);
- /* Jump to the firmware step. We allow firmware switching even in failed
- * state */
- ctx->step = INITIALIZE_STEP_IFACE_FIRMWARE;
- initialize_step (ctx);
+ /* Jump to the fallback step when on failure, we will allow some additional
+ * interfaces even in failed state. */
+ ctx->step = INITIALIZE_STEP_FALLBACK_LIMITED;
+ initialize_step (task);
return;
}
@@ -8938,20 +12362,9 @@ iface_modem_initialize_ready (MMBroadbandModem *self,
mm_iface_modem_bind_simple_status (MM_IFACE_MODEM (self),
self->priv->modem_simple_status);
- /* If we find ourselves in a LOCKED state, we shouldn't keep on
- * the initialization sequence. Instead, we will re-initialize once
- * we are unlocked. */
- if (ctx->self->priv->modem_state == MM_MODEM_STATE_LOCKED) {
- /* Jump to the Firmware interface. We do allow modems to export
- * both the Firmware and Simple interfaces when locked. */
- ctx->step = INITIALIZE_STEP_IFACE_FIRMWARE;
- initialize_step (ctx);
- return;
- }
-
/* Go on to next step */
ctx->step++;
- initialize_step (ctx);
+ initialize_step (task);
}
#undef INTERFACE_INIT_READY_FN
@@ -8959,14 +12372,17 @@ iface_modem_initialize_ready (MMBroadbandModem *self,
static void \
NAME##_initialize_ready (MMBroadbandModem *self, \
GAsyncResult *result, \
- InitializeContext *ctx) \
+ GTask *task) \
{ \
+ InitializeContext *ctx; \
GError *error = NULL; \
\
+ ctx = g_task_get_task_data (task); \
+ \
if (!mm_##NAME##_initialize_finish (TYPE (self), result, &error)) { \
if (FATAL_ERRORS) { \
- mm_warn ("Couldn't initialize interface: '%s'", \
- error->message); \
+ mm_obj_warn (self, "couldn't initialize interface: '%s'", \
+ error->message); \
g_error_free (error); \
\
/* Report the new FAILED state */ \
@@ -8975,12 +12391,12 @@ iface_modem_initialize_ready (MMBroadbandModem *self,
\
/* Just jump to the last step */ \
ctx->step = INITIALIZE_STEP_LAST; \
- initialize_step (ctx); \
+ initialize_step (task); \
return; \
} \
\
- mm_dbg ("Couldn't initialize interface: '%s'", \
- error->message); \
+ mm_obj_dbg (self, "couldn't initialize interface: '%s'", \
+ error->message); \
/* Just shutdown this interface */ \
mm_##NAME##_shutdown (TYPE (self)); \
g_error_free (error); \
@@ -8991,47 +12407,56 @@ iface_modem_initialize_ready (MMBroadbandModem *self,
\
/* Go on to next step */ \
ctx->step++; \
- initialize_step (ctx); \
+ initialize_step (task); \
}
-INTERFACE_INIT_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
-INTERFACE_INIT_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
-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)
+INTERFACE_INIT_READY_FN (iface_modem_3gpp, MM_IFACE_MODEM_3GPP, TRUE)
+INTERFACE_INIT_READY_FN (iface_modem_3gpp_profile_manager, MM_IFACE_MODEM_3GPP_PROFILE_MANAGER, FALSE)
+INTERFACE_INIT_READY_FN (iface_modem_3gpp_ussd, MM_IFACE_MODEM_3GPP_USSD, FALSE)
+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_voice, MM_IFACE_MODEM_VOICE, 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)
+INTERFACE_INIT_READY_FN (iface_modem_sar, MM_IFACE_MODEM_SAR, FALSE)
static void
-initialize_step (InitializeContext *ctx)
+initialize_step (GTask *task)
{
+ InitializeContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialize_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZE_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_SETUP_PORTS:
if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->setup_ports)
MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->setup_ports (ctx->self);
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_STARTED:
if (MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_started &&
MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_started_finish) {
MM_BROADBAND_MODEM_GET_CLASS (ctx->self)->initialization_started (ctx->self,
(GAsyncReadyCallback)initialization_started_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_SETUP_SIMPLE_STATUS:
/* Simple status must be created before any interface initialization,
@@ -9039,151 +12464,266 @@ initialize_step (InitializeContext *ctx)
*/
if (!ctx->self->priv->modem_simple_status)
ctx->self->priv->modem_simple_status = mm_simple_status_new ();
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_IFACE_MODEM:
/* Initialize the Modem interface */
mm_iface_modem_initialize (MM_IFACE_MODEM (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_initialize_ready,
- ctx);
+ task);
return;
case INITIALIZE_STEP_IFACE_3GPP:
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
/* Initialize the 3GPP interface */
mm_iface_modem_3gpp_initialize (MM_IFACE_MODEM_3GPP (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_3gpp_initialize_ready,
- ctx);
+ task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZE_STEP_JUMP_TO_LIMITED:
+ if (ctx->self->priv->modem_state == MM_MODEM_STATE_LOCKED) {
+ /* Jump to the fallback step when locked, we will allow some additional
+ * interfaces even in locked state. */
+ ctx->step = INITIALIZE_STEP_FALLBACK_LIMITED;
+ initialize_step (task);
return;
}
+ ctx->step++;
+ /* fall through */
- /* Fall down to next step */
+ case INITIALIZE_STEP_IFACE_3GPP_PROFILE_MANAGER:
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
+ /* Initialize the 3GPP Profile Manager interface */
+ mm_iface_modem_3gpp_profile_manager_initialize (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self),
+ (GAsyncReadyCallback)iface_modem_3gpp_profile_manager_initialize_ready,
+ task);
+ return;
+ }
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_IFACE_3GPP_USSD:
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self))) {
/* Initialize the 3GPP/USSD interface */
mm_iface_modem_3gpp_ussd_initialize (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
(GAsyncReadyCallback)iface_modem_3gpp_ussd_initialize_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_IFACE_CDMA:
if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
/* Initialize the CDMA interface */
mm_iface_modem_cdma_initialize (MM_IFACE_MODEM_CDMA (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_cdma_initialize_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
- ctx->step++;
-
- case INITIALIZE_STEP_IFACE_CONTACTS:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_IFACE_LOCATION:
/* Initialize the Location interface */
mm_iface_modem_location_initialize (MM_IFACE_MODEM_LOCATION (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_location_initialize_ready,
- ctx);
+ task);
return;
case INITIALIZE_STEP_IFACE_MESSAGING:
/* Initialize the Messaging interface */
mm_iface_modem_messaging_initialize (MM_IFACE_MODEM_MESSAGING (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_messaging_initialize_ready,
- ctx);
+ task);
return;
case INITIALIZE_STEP_IFACE_TIME:
/* Initialize the Time interface */
mm_iface_modem_time_initialize (MM_IFACE_MODEM_TIME (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_time_initialize_ready,
- ctx);
+ task);
return;
case INITIALIZE_STEP_IFACE_SIGNAL:
/* Initialize the Signal interface */
mm_iface_modem_signal_initialize (MM_IFACE_MODEM_SIGNAL (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_signal_initialize_ready,
- ctx);
+ task);
return;
case INITIALIZE_STEP_IFACE_OMA:
/* Initialize the Oma interface */
mm_iface_modem_oma_initialize (MM_IFACE_MODEM_OMA (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_oma_initialize_ready,
- ctx);
+ task);
+ return;
+
+ case INITIALIZE_STEP_IFACE_SAR:
+ /* Initialize the SAR interface */
+ mm_iface_modem_sar_initialize (MM_IFACE_MODEM_SAR (ctx->self),
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)iface_modem_sar_initialize_ready,
+ task);
+ return;
+
+ case INITIALIZE_STEP_FALLBACK_LIMITED:
+ /* All the initialization steps after this one will be run both on
+ * successful and locked/failed initializations. */
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZE_STEP_IFACE_VOICE:
+ /* Initialize the Voice interface */
+ mm_iface_modem_voice_initialize (MM_IFACE_MODEM_VOICE (ctx->self),
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)iface_modem_voice_initialize_ready,
+ task);
return;
case INITIALIZE_STEP_IFACE_FIRMWARE:
/* Initialize the Firmware interface */
mm_iface_modem_firmware_initialize (MM_IFACE_MODEM_FIRMWARE (ctx->self),
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)iface_modem_firmware_initialize_ready,
- ctx);
+ task);
return;
+ case INITIALIZE_STEP_SIM_HOT_SWAP:
+ /* Create the SIM hot swap ports context only if not already done before
+ * (we may be re-running the initialization step after SIM-PIN unlock) */
+ if (!ctx->self->priv->sim_hot_swap_ports_ctx) {
+ gboolean is_sim_hot_swap_supported = FALSE;
+ gboolean is_sim_hot_swap_configured = FALSE;
+
+ g_object_get (ctx->self,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, &is_sim_hot_swap_supported,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, &is_sim_hot_swap_configured,
+ NULL);
+
+ if (is_sim_hot_swap_supported) {
+
+ if (!is_sim_hot_swap_configured) {
+ mm_obj_warn (ctx->self, "SIM hot swap supported but not configured. Skipping opening ports");
+ } else {
+ PortsContext *ports;
+ GError *error = NULL;
+
+ mm_obj_dbg (ctx->self, "creating ports context for SIM hot swap");
+ ports = ports_context_new ();
+ if (!ports_context_open (ctx->self, ports, FALSE, FALSE, FALSE, &error)) {
+ mm_obj_warn (ctx->self, "couldn't open ports during Modem SIM hot swap enabling: %s",
+ error ? error->message : "unknown reason");
+ g_error_free (error);
+ } else {
+ ctx->self->priv->sim_hot_swap_ports_ctx = ports_context_ref (ports);
+ }
+
+ ports_context_unref (ports);
+ }
+ }
+ } else
+ mm_obj_dbg (ctx->self, "ports context for SIM hot swap already available");
+
+ ctx->step++;
+ /* fall through */
+
case INITIALIZE_STEP_IFACE_SIMPLE:
if (ctx->self->priv->modem_state != MM_MODEM_STATE_FAILED)
mm_iface_modem_simple_initialize (MM_IFACE_MODEM_SIMPLE (ctx->self));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZE_STEP_LAST:
if (ctx->self->priv->modem_state == MM_MODEM_STATE_FAILED) {
-
+ GError *error;
if (!ctx->self->priv->modem_dbus_skeleton) {
/* Error setting up ports. Abort without even exporting the
* Modem interface */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_ABORTED,
- "Modem is unusable, "
- "cannot fully initialize");
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_ABORTED,
+ "Modem is unusable, "
+ "cannot fully initialize");
} else {
- /* Fatal SIM failure :-( */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Modem is unusable, "
- "cannot fully initialize");
- /* Ensure we only leave the Modem interface around */
+ /* Fatal SIM, firmware, or modem failure :-( */
+ gboolean is_sim_hot_swap_supported = FALSE;
+ gboolean is_sim_hot_swap_configured = FALSE;
+ MMModemStateFailedReason reason;
+
+ reason = mm_gdbus_modem_get_state_failed_reason (MM_GDBUS_MODEM (ctx->self->priv->modem_dbus_skeleton));
+
+ g_object_get (ctx->self,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED, &is_sim_hot_swap_supported,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, &is_sim_hot_swap_configured,
+ NULL);
+
+ if (reason == MM_MODEM_STATE_FAILED_REASON_SIM_MISSING) {
+ if (!is_sim_hot_swap_supported) {
+ mm_obj_dbg (ctx->self, "SIM is missing, but this modem does not support SIM hot swap.");
+ } else if (!is_sim_hot_swap_configured) {
+ mm_obj_warn (ctx->self, "SIM is missing, but SIM hot swap could not be configured.");
+ } else if (!ctx->self->priv->sim_hot_swap_ports_ctx) {
+ mm_obj_err (ctx->self, "SIM is missing and SIM hot swap is configured, but ports are not opened.");
+ } else {
+ mm_obj_dbg (ctx->self, "SIM is missing, but SIM hot swap is enabled; waiting for SIM...");
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Modem is unusable due to SIM missing, "
+ "cannot fully initialize, waiting for SIM insertion.");
+ goto sim_hot_swap_enabled;
+ }
+ }
+
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Modem is unusable, "
+ "cannot fully initialize");
+sim_hot_swap_enabled:
+ /* Ensure we only leave the Modem and Firmware interfaces
+ * around. A failure could be caused by firmware issues, which
+ * a firmware update, switch, or provisioning could fix. We also
+ * leave the Voice interface around so that we can attempt
+ * emergency voice calls.
+ */
mm_iface_modem_3gpp_shutdown (MM_IFACE_MODEM_3GPP (ctx->self));
+ mm_iface_modem_3gpp_profile_manager_shutdown (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (ctx->self));
mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (ctx->self));
mm_iface_modem_cdma_shutdown (MM_IFACE_MODEM_CDMA (ctx->self));
mm_iface_modem_location_shutdown (MM_IFACE_MODEM_LOCATION (ctx->self));
+ mm_iface_modem_signal_shutdown (MM_IFACE_MODEM_SIGNAL (ctx->self));
mm_iface_modem_messaging_shutdown (MM_IFACE_MODEM_MESSAGING (ctx->self));
mm_iface_modem_time_shutdown (MM_IFACE_MODEM_TIME (ctx->self));
mm_iface_modem_simple_shutdown (MM_IFACE_MODEM_SIMPLE (ctx->self));
}
- initialize_context_complete_and_free (ctx);
+
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (ctx->self->priv->modem_state == MM_MODEM_STATE_LOCKED) {
/* We're locked :-/ */
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Modem is currently locked, "
- "cannot fully initialize");
- initialize_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Modem is currently locked, "
+ "cannot fully initialize");
+ g_object_unref (task);
return;
}
@@ -9193,9 +12733,12 @@ initialize_step (InitializeContext *ctx)
MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialize_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -9207,19 +12750,19 @@ initialize (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, initialize);
+ task = g_task_new (self, cancellable, callback, user_data);
/* Check state before launching modem initialization */
switch (MM_BROADBAND_MODEM (self)->priv->modem_state) {
case MM_MODEM_STATE_FAILED:
/* NOTE: this will only happen if we ever support hot-plugging SIMs */
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot initialize modem: "
- "device is unusable");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot initialize modem: "
+ "device is unusable");
break;
case MM_MODEM_STATE_UNKNOWN:
@@ -9227,26 +12770,26 @@ initialize (MMBaseModem *self,
InitializeContext *ctx;
ctx = g_new0 (InitializeContext, 1);
- ctx->self = g_object_ref (self);
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = result;
+ ctx->self = MM_BROADBAND_MODEM (g_object_ref (self));
ctx->step = INITIALIZE_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialize_context_free);
+
/* Set as being initialized, even if we were locked before */
mm_iface_modem_update_state (MM_IFACE_MODEM (self),
MM_MODEM_STATE_INITIALIZING,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
- initialize_step (ctx);
+ initialize_step (task);
return;
}
case MM_MODEM_STATE_INITIALIZING:
- g_simple_async_result_set_error (result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "Cannot initialize modem: "
- "already being initialized");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "Cannot initialize modem: "
+ "already being initialized");
break;
case MM_MODEM_STATE_DISABLED:
@@ -9259,63 +12802,77 @@ initialize (MMBaseModem *self,
case MM_MODEM_STATE_CONNECTING:
case MM_MODEM_STATE_CONNECTED:
/* Just return success, don't relaunch initialization */
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
+ g_task_return_boolean (task, TRUE);
break;
+
+ default:
+ g_assert_not_reached ();
}
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_object_unref (task);
}
/*****************************************************************************/
-gchar *
-mm_broadband_modem_take_and_convert_to_utf8 (MMBroadbandModem *self,
- gchar *str)
-{
- /* should only be used AFTER current charset is set */
- if (self->priv->modem_current_charset == MM_MODEM_CHARSET_UNKNOWN)
- return str;
-
- return mm_charset_take_and_convert_to_utf8 (str,
- self->priv->modem_current_charset);
-}
-
-gchar *
-mm_broadband_modem_take_and_convert_to_current_charset (MMBroadbandModem *self,
- gchar *str)
-{
- /* should only be used AFTER current charset is set */
- if (self->priv->modem_current_charset == MM_MODEM_CHARSET_UNKNOWN)
- return str;
-
- return mm_utf8_take_and_convert_to_charset (str, self->priv->modem_current_charset);
-}
-
MMModemCharset
mm_broadband_modem_get_current_charset (MMBroadbandModem *self)
{
return self->priv->modem_current_charset;
}
+/*****************************************************************************/
+
gchar *
-mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
- const gchar *ati,
- const gchar *ati1)
+mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
+ const gchar *ati,
+ const gchar *ati1,
+ GError **error)
{
- return (mm_create_device_identifier (
+ gchar *device_identifier;
+
+ /* do nothing if device has gone already */
+ if (!self->priv->modem_dbus_skeleton) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Modem interface skeleton unavailable");
+ return NULL;
+ }
+
+ device_identifier = mm_create_device_identifier (
mm_base_modem_get_vendor_id (MM_BASE_MODEM (self)),
mm_base_modem_get_product_id (MM_BASE_MODEM (self)),
+ self,
ati,
ati1,
mm_gdbus_modem_get_equipment_identifier (
- MM_GDBUS_MODEM (MM_BROADBAND_MODEM (self)->priv->modem_dbus_skeleton)),
+ MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)),
mm_gdbus_modem_get_revision (
- MM_GDBUS_MODEM (MM_BROADBAND_MODEM (self)->priv->modem_dbus_skeleton)),
+ MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)),
mm_gdbus_modem_get_model (
- MM_GDBUS_MODEM (MM_BROADBAND_MODEM (self)->priv->modem_dbus_skeleton)),
+ MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)),
mm_gdbus_modem_get_manufacturer (
- MM_GDBUS_MODEM (MM_BROADBAND_MODEM (self)->priv->modem_dbus_skeleton))));
+ MM_GDBUS_MODEM (self->priv->modem_dbus_skeleton)));
+
+ if (!device_identifier) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unable to generate a device identifier");
+ return NULL;
+ }
+
+ return device_identifier;
+}
+
+/*****************************************************************************/
+
+void
+mm_broadband_modem_sim_hot_swap_detected (MMBroadbandModem *self)
+{
+ if (self->priv->sim_hot_swap_ports_ctx) {
+ mm_obj_dbg (self, "releasing SIM hot swap ports context");
+ ports_context_unref (self->priv->sim_hot_swap_ports_ctx);
+ self->priv->sim_hot_swap_ports_ctx = NULL;
+ }
+
+ mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
}
/*****************************************************************************/
@@ -9333,6 +12890,9 @@ mm_broadband_modem_new (const gchar *device,
MM_BASE_MODEM_PLUGIN, plugin,
MM_BASE_MODEM_VENDOR_ID, vendor_id,
MM_BASE_MODEM_PRODUCT_ID, product_id,
+ /* Generic bearer supports TTY only */
+ MM_BASE_MODEM_DATA_NET_SUPPORTED, FALSE,
+ MM_BASE_MODEM_DATA_TTY_SUPPORTED, TRUE,
NULL);
}
@@ -9353,6 +12913,10 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem_3gpp_dbus_skeleton);
self->priv->modem_3gpp_dbus_skeleton = g_value_dup_object (value);
break;
+ case PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON:
+ g_clear_object (&self->priv->modem_3gpp_profile_manager_dbus_skeleton);
+ self->priv->modem_3gpp_profile_manager_dbus_skeleton = g_value_dup_object (value);
+ break;
case PROP_MODEM_3GPP_USSD_DBUS_SKELETON:
g_clear_object (&self->priv->modem_3gpp_ussd_dbus_skeleton);
self->priv->modem_3gpp_ussd_dbus_skeleton = g_value_dup_object (value);
@@ -9373,6 +12937,10 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem_messaging_dbus_skeleton);
self->priv->modem_messaging_dbus_skeleton = g_value_dup_object (value);
break;
+ case PROP_MODEM_VOICE_DBUS_SKELETON:
+ g_clear_object (&self->priv->modem_voice_dbus_skeleton);
+ self->priv->modem_voice_dbus_skeleton = g_value_dup_object (value);
+ break;
case PROP_MODEM_TIME_DBUS_SKELETON:
g_clear_object (&self->priv->modem_time_dbus_skeleton);
self->priv->modem_time_dbus_skeleton = g_value_dup_object (value);
@@ -9389,10 +12957,18 @@ set_property (GObject *object,
g_clear_object (&self->priv->modem_firmware_dbus_skeleton);
self->priv->modem_firmware_dbus_skeleton = g_value_dup_object (value);
break;
+ case PROP_MODEM_SAR_DBUS_SKELETON:
+ g_clear_object (&self->priv->modem_sar_dbus_skeleton);
+ self->priv->modem_sar_dbus_skeleton = g_value_dup_object (value);
+ break;
case PROP_MODEM_SIM:
g_clear_object (&self->priv->modem_sim);
self->priv->modem_sim = g_value_dup_object (value);
break;
+ case PROP_MODEM_SIM_SLOTS:
+ g_clear_pointer (&self->priv->modem_sim_slots, g_ptr_array_unref);
+ self->priv->modem_sim_slots = g_value_dup_boxed (value);
+ break;
case PROP_MODEM_BEARER_LIST:
g_clear_object (&self->priv->modem_bearer_list);
self->priv->modem_bearer_list = g_value_dup_object (value);
@@ -9412,9 +12988,16 @@ set_property (GObject *object,
case PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED:
self->priv->modem_3gpp_eps_network_supported = g_value_get_boolean (value);
break;
+ case PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED:
+ self->priv->modem_3gpp_5gs_network_supported = g_value_get_boolean (value);
+ break;
case PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS:
self->priv->modem_3gpp_ignored_facility_locks = g_value_get_flags (value);
break;
+ case PROP_MODEM_3GPP_INITIAL_EPS_BEARER:
+ g_clear_object (&self->priv->modem_3gpp_initial_eps_bearer);
+ self->priv->modem_3gpp_initial_eps_bearer = g_value_dup_object (value);
+ break;
case PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE:
self->priv->modem_cdma_cdma1x_registration_state = g_value_get_enum (value);
break;
@@ -9437,10 +13020,47 @@ set_property (GObject *object,
case PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE:
self->priv->modem_messaging_sms_default_storage = g_value_get_enum (value);
break;
+ case PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS:
+ self->priv->modem_location_allow_gps_unmanaged_always = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_VOICE_CALL_LIST:
+ g_clear_object (&self->priv->modem_voice_call_list);
+ self->priv->modem_voice_call_list = g_value_dup_object (value);
+ break;
case PROP_MODEM_SIMPLE_STATUS:
g_clear_object (&self->priv->modem_simple_status);
self->priv->modem_simple_status = g_value_dup_object (value);
break;
+ case PROP_MODEM_SIM_HOT_SWAP_SUPPORTED:
+ self->priv->sim_hot_swap_supported = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_SIM_HOT_SWAP_CONFIGURED:
+ self->priv->sim_hot_swap_configured = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
+ self->priv->periodic_signal_check_disabled = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED:
+ self->priv->periodic_access_tech_check_disabled = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED:
+ self->priv->periodic_call_list_check_disabled = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED:
+ self->priv->indication_call_list_reload_enabled = g_value_get_boolean (value);
+ break;
+ case PROP_MODEM_CARRIER_CONFIG_MAPPING:
+ self->priv->carrier_config_mapping = g_value_dup_string (value);
+ break;
+ case PROP_MODEM_FIRMWARE_IGNORE_CARRIER:
+ self->priv->modem_firmware_ignore_carrier = g_value_get_boolean (value);
+ break;
+ case PROP_FLOW_CONTROL:
+ self->priv->flow_control = g_value_get_flags (value);
+ break;
+ case PROP_INDICATORS_DISABLED:
+ self->priv->modem_cind_disabled = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -9462,6 +13082,9 @@ get_property (GObject *object,
case PROP_MODEM_3GPP_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_3gpp_dbus_skeleton);
break;
+ case PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON:
+ g_value_set_object (value, self->priv->modem_3gpp_profile_manager_dbus_skeleton);
+ break;
case PROP_MODEM_3GPP_USSD_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_3gpp_ussd_dbus_skeleton);
break;
@@ -9477,6 +13100,9 @@ get_property (GObject *object,
case PROP_MODEM_MESSAGING_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_messaging_dbus_skeleton);
break;
+ case PROP_MODEM_VOICE_DBUS_SKELETON:
+ g_value_set_object (value, self->priv->modem_voice_dbus_skeleton);
+ break;
case PROP_MODEM_TIME_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_time_dbus_skeleton);
break;
@@ -9489,9 +13115,15 @@ get_property (GObject *object,
case PROP_MODEM_FIRMWARE_DBUS_SKELETON:
g_value_set_object (value, self->priv->modem_firmware_dbus_skeleton);
break;
+ case PROP_MODEM_SAR_DBUS_SKELETON:
+ g_value_set_object (value, self->priv->modem_sar_dbus_skeleton);
+ break;
case PROP_MODEM_SIM:
g_value_set_object (value, self->priv->modem_sim);
break;
+ case PROP_MODEM_SIM_SLOTS:
+ g_value_set_boxed (value, self->priv->modem_sim_slots);
+ break;
case PROP_MODEM_BEARER_LIST:
g_value_set_object (value, self->priv->modem_bearer_list);
break;
@@ -9510,9 +13142,15 @@ get_property (GObject *object,
case PROP_MODEM_3GPP_EPS_NETWORK_SUPPORTED:
g_value_set_boolean (value, self->priv->modem_3gpp_eps_network_supported);
break;
+ case PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED:
+ g_value_set_boolean (value, self->priv->modem_3gpp_5gs_network_supported);
+ break;
case PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS:
g_value_set_flags (value, self->priv->modem_3gpp_ignored_facility_locks);
break;
+ case PROP_MODEM_3GPP_INITIAL_EPS_BEARER:
+ g_value_set_object (value, self->priv->modem_3gpp_initial_eps_bearer);
+ break;
case PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE:
g_value_set_enum (value, self->priv->modem_cdma_cdma1x_registration_state);
break;
@@ -9534,9 +13172,45 @@ get_property (GObject *object,
case PROP_MODEM_MESSAGING_SMS_DEFAULT_STORAGE:
g_value_set_enum (value, self->priv->modem_messaging_sms_default_storage);
break;
+ case PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS:
+ g_value_set_boolean (value, self->priv->modem_location_allow_gps_unmanaged_always);
+ break;
+ case PROP_MODEM_VOICE_CALL_LIST:
+ g_value_set_object (value, self->priv->modem_voice_call_list);
+ break;
case PROP_MODEM_SIMPLE_STATUS:
g_value_set_object (value, self->priv->modem_simple_status);
break;
+ case PROP_MODEM_SIM_HOT_SWAP_SUPPORTED:
+ g_value_set_boolean (value, self->priv->sim_hot_swap_supported);
+ break;
+ case PROP_MODEM_SIM_HOT_SWAP_CONFIGURED:
+ g_value_set_boolean (value, self->priv->sim_hot_swap_configured);
+ break;
+ case PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED:
+ g_value_set_boolean (value, self->priv->periodic_signal_check_disabled);
+ break;
+ case PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED:
+ g_value_set_boolean (value, self->priv->periodic_access_tech_check_disabled);
+ break;
+ case PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED:
+ g_value_set_boolean (value, self->priv->periodic_call_list_check_disabled);
+ break;
+ case PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED:
+ g_value_set_boolean (value, self->priv->indication_call_list_reload_enabled);
+ break;
+ case PROP_MODEM_CARRIER_CONFIG_MAPPING:
+ g_value_set_string (value, self->priv->carrier_config_mapping);
+ break;
+ case PROP_MODEM_FIRMWARE_IGNORE_CARRIER:
+ g_value_set_boolean (value, self->priv->modem_firmware_ignore_carrier);
+ break;
+ case PROP_FLOW_CONTROL:
+ g_value_set_flags (value, self->priv->flow_control);
+ break;
+ case PROP_INDICATORS_DISABLED:
+ g_value_set_boolean (value, self->priv->modem_cind_disabled);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -9557,6 +13231,7 @@ mm_broadband_modem_init (MMBroadbandModem *self)
self->priv->modem_3gpp_cs_network_supported = TRUE;
self->priv->modem_3gpp_ps_network_supported = TRUE;
self->priv->modem_3gpp_eps_network_supported = FALSE;
+ self->priv->modem_3gpp_5gs_network_supported = FALSE;
self->priv->modem_3gpp_ignored_facility_locks = MM_MODEM_3GPP_FACILITY_NONE;
self->priv->modem_cdma_cdma1x_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
self->priv->modem_cdma_evdo_registration_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
@@ -9565,6 +13240,15 @@ mm_broadband_modem_init (MMBroadbandModem *self)
self->priv->modem_messaging_sms_default_storage = MM_SMS_STORAGE_UNKNOWN;
self->priv->current_sms_mem1_storage = MM_SMS_STORAGE_UNKNOWN;
self->priv->current_sms_mem2_storage = MM_SMS_STORAGE_UNKNOWN;
+ self->priv->sim_hot_swap_supported = FALSE;
+ self->priv->periodic_signal_check_disabled = FALSE;
+ self->priv->periodic_access_tech_check_disabled = FALSE;
+ self->priv->periodic_call_list_check_disabled = FALSE;
+ self->priv->indication_call_list_reload_enabled = FALSE;
+ self->priv->modem_cmer_enable_mode = MM_3GPP_CMER_MODE_NONE;
+ self->priv->modem_cmer_disable_mode = MM_3GPP_CMER_MODE_NONE;
+ self->priv->modem_cmer_ind = MM_3GPP_CMER_IND_NONE;
+ self->priv->flow_control = MM_FLOW_CONTROL_NONE;
}
static void
@@ -9575,9 +13259,17 @@ finalize (GObject *object)
if (self->priv->enabled_ports_ctx)
ports_context_unref (self->priv->enabled_ports_ctx);
+ if (self->priv->sim_hot_swap_ports_ctx)
+ ports_context_unref (self->priv->sim_hot_swap_ports_ctx);
+
+ if (self->priv->in_call_ports_ctx)
+ ports_context_unref (self->priv->in_call_ports_ctx);
+
if (self->priv->modem_3gpp_registration_regex)
mm_3gpp_creg_regex_destroy (self->priv->modem_3gpp_registration_regex);
+ g_free (self->priv->carrier_config_mapping);
+
G_OBJECT_CLASS (mm_broadband_modem_parent_class)->finalize (object);
}
@@ -9596,6 +13288,11 @@ dispose (GObject *object)
g_clear_object (&self->priv->modem_3gpp_dbus_skeleton);
}
+ if (self->priv->modem_3gpp_profile_manager_dbus_skeleton) {
+ mm_iface_modem_3gpp_profile_manager_shutdown (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (object));
+ g_clear_object (&self->priv->modem_3gpp_profile_manager_dbus_skeleton);
+ }
+
if (self->priv->modem_3gpp_ussd_dbus_skeleton) {
mm_iface_modem_3gpp_ussd_shutdown (MM_IFACE_MODEM_3GPP_USSD (object));
g_clear_object (&self->priv->modem_3gpp_ussd_dbus_skeleton);
@@ -9611,11 +13308,21 @@ dispose (GObject *object)
g_clear_object (&self->priv->modem_location_dbus_skeleton);
}
+ if (self->priv->modem_signal_dbus_skeleton) {
+ mm_iface_modem_signal_shutdown (MM_IFACE_MODEM_SIGNAL (object));
+ g_clear_object (&self->priv->modem_signal_dbus_skeleton);
+ }
+
if (self->priv->modem_messaging_dbus_skeleton) {
mm_iface_modem_messaging_shutdown (MM_IFACE_MODEM_MESSAGING (object));
g_clear_object (&self->priv->modem_messaging_dbus_skeleton);
}
+ if (self->priv->modem_voice_dbus_skeleton) {
+ mm_iface_modem_voice_shutdown (MM_IFACE_MODEM_VOICE (object));
+ g_clear_object (&self->priv->modem_voice_dbus_skeleton);
+ }
+
if (self->priv->modem_time_dbus_skeleton) {
mm_iface_modem_time_shutdown (MM_IFACE_MODEM_TIME (object));
g_clear_object (&self->priv->modem_time_dbus_skeleton);
@@ -9626,9 +13333,22 @@ dispose (GObject *object)
g_clear_object (&self->priv->modem_simple_dbus_skeleton);
}
+ if (self->priv->modem_firmware_dbus_skeleton) {
+ mm_iface_modem_firmware_shutdown (MM_IFACE_MODEM_FIRMWARE (object));
+ g_clear_object (&self->priv->modem_firmware_dbus_skeleton);
+ }
+
+ if (self->priv->modem_sar_dbus_skeleton) {
+ mm_iface_modem_sar_shutdown (MM_IFACE_MODEM_SAR (object));
+ g_clear_object (&self->priv->modem_sar_dbus_skeleton);
+ }
+
+ g_clear_object (&self->priv->modem_3gpp_initial_eps_bearer);
g_clear_object (&self->priv->modem_sim);
+ g_clear_pointer (&self->priv->modem_sim_slots, g_ptr_array_unref);
g_clear_object (&self->priv->modem_bearer_list);
g_clear_object (&self->priv->modem_messaging_sms_list);
+ g_clear_object (&self->priv->modem_voice_call_list);
g_clear_object (&self->priv->modem_simple_status);
G_OBJECT_CLASS (mm_broadband_modem_parent_class)->dispose (object);
@@ -9654,18 +13374,23 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_own_numbers_finish = modem_load_own_numbers_finish;
iface->load_unlock_required = modem_load_unlock_required;
iface->load_unlock_required_finish = modem_load_unlock_required_finish;
+ iface->load_unlock_retries = load_unlock_retries;
+ iface->load_unlock_retries_finish = load_unlock_retries_finish;
iface->create_sim = modem_create_sim;
iface->create_sim_finish = modem_create_sim_finish;
iface->load_supported_modes = modem_load_supported_modes;
iface->load_supported_modes_finish = modem_load_supported_modes_finish;
- iface->load_power_state = load_power_state;
- iface->load_power_state_finish = load_power_state_finish;
+ iface->load_power_state = modem_load_power_state;
+ iface->load_power_state_finish = modem_load_power_state_finish;
iface->load_supported_ip_families = modem_load_supported_ip_families;
iface->load_supported_ip_families_finish = modem_load_supported_ip_families_finish;
+ iface->create_bearer_list = modem_create_bearer_list;
/* Enabling steps */
iface->modem_power_up = modem_power_up;
iface->modem_power_up_finish = modem_power_up_finish;
+ iface->check_for_sim_swap = modem_check_for_sim_swap;
+ iface->check_for_sim_swap_finish = modem_check_for_sim_swap_finish;
iface->setup_flow_control = modem_setup_flow_control;
iface->setup_flow_control_finish = modem_setup_flow_control_finish;
iface->load_supported_charsets = modem_load_supported_charsets;
@@ -9693,6 +13418,8 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
iface->load_imei_finish = modem_3gpp_load_imei_finish;
iface->load_enabled_facility_locks = modem_3gpp_load_enabled_facility_locks;
iface->load_enabled_facility_locks_finish = modem_3gpp_load_enabled_facility_locks_finish;
+ iface->load_eps_ue_mode_operation = modem_3gpp_load_eps_ue_mode_operation;
+ iface->load_eps_ue_mode_operation_finish = modem_3gpp_load_eps_ue_mode_operation_finish;
/* Enabling steps */
iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events;
@@ -9719,14 +13446,39 @@ 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;
iface->register_in_network_finish = modem_3gpp_register_in_network_finish;
iface->scan_networks = modem_3gpp_scan_networks;
iface->scan_networks_finish = modem_3gpp_scan_networks_finish;
+ iface->set_eps_ue_mode_operation = modem_3gpp_set_eps_ue_mode_operation;
+ iface->set_eps_ue_mode_operation_finish = modem_3gpp_set_eps_ue_mode_operation_finish;
+ iface->create_initial_eps_bearer = modem_3gpp_create_initial_eps_bearer;
+ iface->set_packet_service_state = modem_3gpp_set_packet_service_state;
+ iface->set_packet_service_state_finish = modem_3gpp_set_packet_service_state_finish;
+}
+
+static void
+iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManager *iface)
+{
+ /* Initialization steps */
+ iface->check_support = modem_3gpp_profile_manager_check_support;
+ iface->check_support_finish = modem_3gpp_profile_manager_check_support_finish;
+
+ /* User actions */
+ iface->list_profiles = modem_3gpp_profile_manager_list_profiles;
+ iface->list_profiles_finish = modem_3gpp_profile_manager_list_profiles_finish;
+ iface->delete_profile = modem_3gpp_profile_manager_delete_profile;
+ iface->delete_profile_finish = modem_3gpp_profile_manager_delete_profile_finish;
+ iface->check_format = modem_3gpp_profile_manager_check_format;
+ iface->check_format_finish = modem_3gpp_profile_manager_check_format_finish;
+ iface->check_activated_profile = modem_3gpp_profile_manager_check_activated_profile;
+ iface->check_activated_profile_finish = modem_3gpp_profile_manager_check_activated_profile_finish;
+ iface->deactivate_profile = modem_3gpp_profile_manager_deactivate_profile;
+ iface->deactivate_profile_finish = modem_3gpp_profile_manager_deactivate_profile_finish;
+ iface->store_profile = modem_3gpp_profile_manager_store_profile;
+ iface->store_profile_finish = modem_3gpp_profile_manager_store_profile_finish;
}
static void
@@ -9737,16 +13489,16 @@ iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssd *iface)
iface->check_support_finish = modem_3gpp_ussd_check_support_finish;
/* Enabling steps */
- iface->setup_unsolicited_result_codes = modem_3gpp_ussd_setup_unsolicited_result_codes;
- iface->setup_unsolicited_result_codes_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_result_codes_finish;
- iface->enable_unsolicited_result_codes = modem_3gpp_ussd_enable_unsolicited_result_codes;
- iface->enable_unsolicited_result_codes_finish = modem_3gpp_ussd_enable_disable_unsolicited_result_codes_finish;
+ iface->setup_unsolicited_events = modem_3gpp_ussd_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_3gpp_ussd_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish;
/* Disabling steps */
- iface->cleanup_unsolicited_result_codes_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_result_codes_finish;
- iface->cleanup_unsolicited_result_codes = modem_3gpp_ussd_cleanup_unsolicited_result_codes;
- iface->disable_unsolicited_result_codes = modem_3gpp_ussd_disable_unsolicited_result_codes;
- iface->disable_unsolicited_result_codes_finish = modem_3gpp_ussd_enable_disable_unsolicited_result_codes_finish;
+ iface->cleanup_unsolicited_events_finish = modem_3gpp_ussd_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_3gpp_ussd_cleanup_unsolicited_events;
+ iface->disable_unsolicited_events = modem_3gpp_ussd_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_3gpp_ussd_enable_disable_unsolicited_events_finish;
/* Additional actions */
iface->encode = modem_3gpp_ussd_encode;
@@ -9767,6 +13519,10 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
iface->load_meid_finish = modem_cdma_load_meid_finish;
/* Registration check steps */
+ iface->setup_unsolicited_events = modem_cdma_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_cdma_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_cdma_setup_cleanup_unsolicited_events_finish;
iface->setup_registration_checks = modem_cdma_setup_registration_checks;
iface->setup_registration_checks_finish = modem_cdma_setup_registration_checks_finish;
iface->get_call_manager_state = modem_cdma_get_call_manager_state;
@@ -9819,16 +13575,68 @@ iface_modem_messaging_init (MMIfaceModemMessaging *iface)
iface->cleanup_unsolicited_events = modem_messaging_cleanup_unsolicited_events;
iface->cleanup_unsolicited_events_finish = modem_messaging_setup_cleanup_unsolicited_events_finish;
iface->create_sms = modem_messaging_create_sms;
+ iface->init_current_storages = modem_messaging_init_current_storages;
+ iface->init_current_storages_finish = modem_messaging_init_current_storages_finish;
+}
+
+static void
+iface_modem_voice_init (MMIfaceModemVoice *iface)
+{
+ iface->check_support = modem_voice_check_support;
+ iface->check_support_finish = modem_voice_check_support_finish;
+ iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_voice_setup_cleanup_unsolicited_events_finish;
+ iface->enable_unsolicited_events = modem_voice_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_voice_enable_disable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_voice_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_voice_enable_disable_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_voice_setup_cleanup_unsolicited_events_finish;
+
+ iface->setup_in_call_unsolicited_events = modem_voice_setup_in_call_unsolicited_events;
+ iface->setup_in_call_unsolicited_events_finish = modem_voice_setup_cleanup_in_call_unsolicited_events_finish;
+ iface->cleanup_in_call_unsolicited_events = modem_voice_cleanup_in_call_unsolicited_events;
+ iface->cleanup_in_call_unsolicited_events_finish = modem_voice_setup_cleanup_in_call_unsolicited_events_finish;
+
+ iface->create_call = modem_voice_create_call;
+ iface->load_call_list = modem_voice_load_call_list;
+ iface->load_call_list_finish = modem_voice_load_call_list_finish;
+ iface->hold_and_accept = modem_voice_hold_and_accept;
+ iface->hold_and_accept_finish = modem_voice_hold_and_accept_finish;
+ iface->hangup_and_accept = modem_voice_hangup_and_accept;
+ iface->hangup_and_accept_finish = modem_voice_hangup_and_accept_finish;
+ iface->hangup_all = modem_voice_hangup_all;
+ iface->hangup_all_finish = modem_voice_hangup_all_finish;
+ iface->join_multiparty = modem_voice_join_multiparty;
+ iface->join_multiparty_finish = modem_voice_join_multiparty_finish;
+ iface->leave_multiparty = modem_voice_leave_multiparty;
+ iface->leave_multiparty_finish = modem_voice_leave_multiparty_finish;
+ iface->transfer = modem_voice_transfer;
+ iface->transfer_finish = modem_voice_transfer_finish;
+ iface->call_waiting_setup = modem_voice_call_waiting_setup;
+ iface->call_waiting_setup_finish = modem_voice_call_waiting_setup_finish;
+ iface->call_waiting_query = modem_voice_call_waiting_query;
+ iface->call_waiting_query_finish = modem_voice_call_waiting_query_finish;
}
static void
iface_modem_time_init (MMIfaceModemTime *iface)
{
+ iface->check_support = modem_time_check_support;
+ iface->check_support_finish = modem_time_check_support_finish;
+ iface->load_network_time = modem_time_load_network_time;
+ iface->load_network_time_finish = modem_time_load_network_time_finish;
+ iface->load_network_timezone = modem_time_load_network_timezone;
+ iface->load_network_timezone_finish = modem_time_load_network_timezone_finish;
}
static void
iface_modem_signal_init (MMIfaceModemSignal *iface)
{
+ iface->check_support = modem_signal_check_support;
+ iface->check_support_finish = modem_signal_check_support_finish;
+ iface->load_values = modem_signal_load_values;
+ iface->load_values_finish = modem_signal_load_values_finish;
}
static void
@@ -9842,6 +13650,12 @@ iface_modem_firmware_init (MMIfaceModemFirmware *iface)
}
static void
+iface_modem_sar_init (MMIfaceModemSar *iface)
+{
+
+}
+
+static void
mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -9862,6 +13676,11 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
base_modem_class->disable = disable;
base_modem_class->disable_finish = disable_finish;
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ base_modem_class->sync = synchronize;
+ base_modem_class->sync_finish = synchronize_finish;
+#endif
+
klass->setup_ports = setup_ports;
klass->initialization_started = initialization_started;
klass->initialization_started_finish = initialization_started_finish;
@@ -9881,6 +13700,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_3GPP_DBUS_SKELETON);
g_object_class_override_property (object_class,
+ PROP_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_3GPP_USSD_DBUS_SKELETON,
MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON);
@@ -9901,6 +13724,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_MESSAGING_DBUS_SKELETON);
g_object_class_override_property (object_class,
+ PROP_MODEM_VOICE_DBUS_SKELETON,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_TIME_DBUS_SKELETON,
MM_IFACE_MODEM_TIME_DBUS_SKELETON);
@@ -9913,6 +13740,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_OMA_DBUS_SKELETON);
g_object_class_override_property (object_class,
+ PROP_MODEM_SAR_DBUS_SKELETON,
+ MM_IFACE_MODEM_SAR_DBUS_SKELETON);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_FIRMWARE_DBUS_SKELETON,
MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON);
@@ -9921,6 +13752,10 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_SIM);
g_object_class_override_property (object_class,
+ PROP_MODEM_SIM_SLOTS,
+ MM_IFACE_MODEM_SIM_SLOTS);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_BEARER_LIST,
MM_IFACE_MODEM_BEARER_LIST);
@@ -9945,10 +13780,18 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED);
g_object_class_override_property (object_class,
+ PROP_MODEM_3GPP_5GS_NETWORK_SUPPORTED,
+ MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_3GPP_IGNORED_FACILITY_LOCKS,
MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS);
g_object_class_override_property (object_class,
+ PROP_MODEM_3GPP_INITIAL_EPS_BEARER,
+ MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_CDMA_CDMA1X_REGISTRATION_STATE,
MM_IFACE_MODEM_CDMA_CDMA1X_REGISTRATION_STATE);
@@ -9977,6 +13820,63 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE);
g_object_class_override_property (object_class,
+ PROP_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS,
+ MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_VOICE_CALL_LIST,
+ MM_IFACE_MODEM_VOICE_CALL_LIST);
+
+ g_object_class_override_property (object_class,
PROP_MODEM_SIMPLE_STATUS,
MM_IFACE_MODEM_SIMPLE_STATUS);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_SIM_HOT_SWAP_SUPPORTED,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_SIM_HOT_SWAP_CONFIGURED,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED,
+ MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_PERIODIC_CALL_LIST_CHECK_DISABLED,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_INDICATION_CALL_LIST_RELOAD_ENABLED,
+ MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_CARRIER_CONFIG_MAPPING,
+ MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING);
+
+ g_object_class_override_property (object_class,
+ PROP_MODEM_FIRMWARE_IGNORE_CARRIER,
+ MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER);
+
+ properties[PROP_FLOW_CONTROL] =
+ g_param_spec_flags (MM_BROADBAND_MODEM_FLOW_CONTROL,
+ "Flow control",
+ "Flow control settings to use in the connected TTY",
+ MM_TYPE_FLOW_CONTROL,
+ MM_FLOW_CONTROL_NONE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_FLOW_CONTROL, properties[PROP_FLOW_CONTROL]);
+
+ properties[PROP_INDICATORS_DISABLED] =
+ g_param_spec_boolean (MM_BROADBAND_MODEM_INDICATORS_DISABLED,
+ "Disable indicators",
+ "Avoid explicitly setting up +CIND URCs",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_INDICATORS_DISABLED, properties[PROP_INDICATORS_DISABLED]);
}
diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h
index 4201412e..2ba05799 100644
--- a/src/mm-broadband-modem.h
+++ b/src/mm-broadband-modem.h
@@ -13,6 +13,7 @@
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2011 Red Hat, Inc.
* Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2015 - Marco Bascetta <marco.bascetta@sadel.it>
*/
#ifndef MM_BROADBAND_MODEM_H
@@ -23,6 +24,7 @@
#include <ModemManager.h>
+#include "mm-modem-helpers.h"
#include "mm-charsets.h"
#include "mm-base-modem.h"
@@ -37,6 +39,9 @@ typedef struct _MMBroadbandModem MMBroadbandModem;
typedef struct _MMBroadbandModemClass MMBroadbandModemClass;
typedef struct _MMBroadbandModemPrivate MMBroadbandModemPrivate;
+#define MM_BROADBAND_MODEM_FLOW_CONTROL "broadband-modem-flow-control"
+#define MM_BROADBAND_MODEM_INDICATORS_DISABLED "broadband-modem-indicators-disabled"
+
struct _MMBroadbandModem {
MMBaseModem parent;
MMBroadbandModemPrivate *priv;
@@ -87,6 +92,7 @@ struct _MMBroadbandModemClass {
};
GType mm_broadband_modem_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBroadbandModem, g_object_unref)
MMBroadbandModem *mm_broadband_modem_new (const gchar *device,
const gchar **drivers,
@@ -94,25 +100,14 @@ MMBroadbandModem *mm_broadband_modem_new (const gchar *device,
guint16 vendor_id,
guint16 product_id);
-/* Convert the given string, which comes in the charset currently set in the
- * modem, to UTF-8. Given in the API so that subclasses can also use it directly.
- */
-gchar *mm_broadband_modem_take_and_convert_to_utf8 (MMBroadbandModem *self,
- gchar *str);
-
-/* Convert the given string, which comes in UTF-8, to the charset currently set
- * in the modem. Given in the API so that subclasses can also use it directly.
- */
-gchar *mm_broadband_modem_take_and_convert_to_current_charset (MMBroadbandModem *self,
- gchar *str);
-
MMModemCharset mm_broadband_modem_get_current_charset (MMBroadbandModem *self);
/* Create a unique device identifier string using the ATI and ATI1 replies and some
* additional internal info */
-gchar *mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
- const gchar *ati,
- const gchar *ati1);
+gchar *mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
+ const gchar *ati,
+ const gchar *ati1,
+ GError **error);
/* Locking/unlocking SMS storages */
void mm_broadband_modem_lock_sms_storages (MMBroadbandModem *self,
@@ -126,5 +121,7 @@ gboolean mm_broadband_modem_lock_sms_storages_finish (MMBroadbandModem *self,
void mm_broadband_modem_unlock_sms_storages (MMBroadbandModem *self,
gboolean mem1,
gboolean mem2);
+/* Helper to update SIM hot swap */
+void mm_broadband_modem_sim_hot_swap_detected (MMBroadbandModem *self);
#endif /* MM_BROADBAND_MODEM_H */
diff --git a/src/mm-call-list.c b/src/mm-call-list.c
new file mode 100644
index 00000000..66e8382c
--- /dev/null
+++ b/src/mm-call-list.c
@@ -0,0 +1,321 @@
+/* -*- 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) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem-messaging.h"
+#include "mm-call-list.h"
+#include "mm-base-call.h"
+#include "mm-log.h"
+
+G_DEFINE_TYPE (MMCallList, mm_call_list, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+ PROP_MODEM,
+ PROP_LAST
+};
+static GParamSpec *properties[PROP_LAST];
+
+enum {
+ SIGNAL_CALL_ADDED,
+ SIGNAL_CALL_DELETED,
+ SIGNAL_LAST
+};
+static guint signals[SIGNAL_LAST];
+
+struct _MMCallListPrivate {
+ /* The owner modem */
+ MMBaseModem *modem;
+ /* List of call objects */
+ GList *list;
+};
+
+/*****************************************************************************/
+
+void
+mm_call_list_foreach (MMCallList *self,
+ MMCallListForeachFunc callback,
+ gpointer user_data)
+{
+ GList *l;
+
+ g_assert (callback);
+ for (l = self->priv->list; l; l = g_list_next (l))
+ callback (MM_BASE_CALL (l->data), user_data);
+}
+
+/*****************************************************************************/
+
+guint
+mm_call_list_get_count (MMCallList *self)
+{
+ return g_list_length (self->priv->list);
+}
+
+GStrv
+mm_call_list_get_paths (MMCallList *self)
+{
+ GStrv path_list = NULL;
+ GList *l;
+ guint i;
+
+ path_list = g_new0 (gchar *, 1 + g_list_length (self->priv->list));
+
+ for (i = 0, l = self->priv->list; l; l = g_list_next (l)) {
+ const gchar *path;
+
+ /* Don't try to add NULL paths (not yet exported CALL objects) */
+ path = mm_base_call_get_path (MM_BASE_CALL (l->data));
+ if (path)
+ path_list[i++] = g_strdup (path);
+ }
+
+ return path_list;
+}
+
+/*****************************************************************************/
+
+MMBaseCall *
+mm_call_list_get_first_incoming_call (MMCallList *self,
+ MMCallState incoming_state)
+{
+ GList *l;
+
+ g_assert (incoming_state == MM_CALL_STATE_RINGING_IN || incoming_state == MM_CALL_STATE_WAITING);
+
+ for (l = self->priv->list; l; l = g_list_next (l)) {
+ MMBaseCall *call;
+ MMCallState state;
+ MMCallDirection direction;
+
+ call = MM_BASE_CALL (l->data);
+
+ g_object_get (call,
+ "state", &state,
+ "direction", &direction,
+ NULL);
+
+ if (direction == MM_CALL_DIRECTION_INCOMING &&
+ state == incoming_state) {
+ return call;
+ }
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+static guint
+cmp_call_by_path (MMBaseCall *call,
+ const gchar *path)
+{
+ return g_strcmp0 (mm_base_call_get_path (call), path);
+}
+
+MMBaseCall *
+mm_call_list_get_call (MMCallList *self,
+ const gchar *call_path)
+{
+ GList *l;
+
+ l = g_list_find_custom (self->priv->list,
+ (gpointer)call_path,
+ (GCompareFunc)cmp_call_by_path);
+
+ return (l ? MM_BASE_CALL (l->data) : NULL);
+}
+
+static guint
+cmp_call_by_index (MMBaseCall *call,
+ guint8 *index)
+{
+ return mm_base_call_get_index (call) - *index;
+}
+
+MMBaseCall *
+mm_call_list_get_call_by_index (MMCallList *self,
+ guint8 index)
+{
+ GList *l;
+
+ l = g_list_find_custom (self->priv->list,
+ (gpointer)&index,
+ (GCompareFunc)cmp_call_by_index);
+
+ return (l ? MM_BASE_CALL (l->data) : NULL);
+}
+
+gboolean
+mm_call_list_delete_call (MMCallList *self,
+ const gchar *call_path,
+ GError **error)
+{
+ GList *l;
+ MMBaseCall *call;
+
+ l = g_list_find_custom (self->priv->list,
+ (gpointer)call_path,
+ (GCompareFunc)cmp_call_by_path);
+ if (!l) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No call found with path '%s'",
+ call_path);
+ return FALSE;
+ }
+
+ call = MM_BASE_CALL (l->data);
+ mm_base_call_unexport (call);
+ g_signal_emit (self, signals[SIGNAL_CALL_DELETED], 0, call_path);
+
+ g_object_unref (call);
+ self->priv->list = g_list_delete_link (self->priv->list, l);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+void
+mm_call_list_add_call (MMCallList *self,
+ MMBaseCall *call)
+{
+ self->priv->list = g_list_prepend (self->priv->list, g_object_ref (call));
+ g_signal_emit (self, signals[SIGNAL_CALL_ADDED], 0,
+ mm_base_call_get_path (call),
+ FALSE);
+}
+
+/*****************************************************************************/
+
+MMCallList *
+mm_call_list_new (MMBaseModem *modem)
+{
+ /* Create the object */
+ return g_object_new (MM_TYPE_CALL_LIST,
+ MM_CALL_LIST_MODEM, modem,
+ NULL);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMCallList *self = MM_CALL_LIST (object);
+
+ switch (prop_id) {
+ case PROP_MODEM:
+ g_clear_object (&self->priv->modem);
+ self->priv->modem = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMCallList *self = MM_CALL_LIST (object);
+
+ switch (prop_id) {
+ case PROP_MODEM:
+ g_value_set_object (value, self->priv->modem);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mm_call_list_init (MMCallList *self)
+{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_CALL_LIST,
+ MMCallListPrivate);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMCallList *self = MM_CALL_LIST (object);
+
+ g_clear_object (&self->priv->modem);
+ g_list_free_full (self->priv->list, g_object_unref);
+ self->priv->list = NULL;
+
+ G_OBJECT_CLASS (mm_call_list_parent_class)->dispose (object);
+}
+
+static void
+mm_call_list_class_init (MMCallListClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMCallListPrivate));
+
+ /* Virtual methods */
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
+
+ /* Properties */
+ properties[PROP_MODEM] =
+ g_param_spec_object (MM_CALL_LIST_MODEM,
+ "Modem",
+ "The Modem which owns this CALL list",
+ MM_TYPE_BASE_MODEM,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
+
+ /* Signals */
+ signals[SIGNAL_CALL_ADDED] =
+ g_signal_new (MM_CALL_ADDED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMCallListClass, call_added),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+
+ signals[SIGNAL_CALL_DELETED] =
+ g_signal_new (MM_CALL_DELETED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMCallListClass, call_deleted),
+ NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, G_TYPE_STRING);
+}
diff --git a/src/mm-call-list.h b/src/mm-call-list.h
new file mode 100644
index 00000000..f77a5b37
--- /dev/null
+++ b/src/mm-call-list.h
@@ -0,0 +1,87 @@
+/* -*- 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) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ */
+
+#ifndef MM_CALL_LIST_H
+#define MM_CALL_LIST_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-base-modem.h"
+#include "mm-base-call.h"
+
+#define MM_TYPE_CALL_LIST (mm_call_list_get_type ())
+#define MM_CALL_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_LIST, MMCallList))
+#define MM_CALL_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_LIST, MMCallListClass))
+#define MM_IS_CALL_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_LIST))
+#define MM_IS_CALL_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_LIST))
+#define MM_CALL_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_LIST, MMCallListClass))
+
+typedef struct _MMCallList MMCallList;
+typedef struct _MMCallListClass MMCallListClass;
+typedef struct _MMCallListPrivate MMCallListPrivate;
+
+#define MM_CALL_LIST_MODEM "call-list-modem"
+
+#define MM_CALL_ADDED "call-added"
+#define MM_CALL_DELETED "call-deleted"
+
+struct _MMCallList {
+ GObject parent;
+ MMCallListPrivate *priv;
+};
+
+struct _MMCallListClass {
+ GObjectClass parent;
+
+ /* Signals */
+ void (* call_added) (MMCallList *self,
+ const gchar *call_path,
+ gboolean received);
+ void (* call_deleted) (MMCallList *self,
+ const gchar *call_path);
+};
+
+GType mm_call_list_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallList, g_object_unref)
+
+MMCallList *mm_call_list_new (MMBaseModem *modem);
+
+GStrv mm_call_list_get_paths (MMCallList *self);
+guint mm_call_list_get_count (MMCallList *self);
+
+void mm_call_list_add_call (MMCallList *self,
+ MMBaseCall *call);
+
+MMBaseCall *mm_call_list_get_call (MMCallList *self,
+ const gchar *call_path);
+
+MMBaseCall *mm_call_list_get_call_by_index (MMCallList *self,
+ guint8 index);
+
+gboolean mm_call_list_delete_call (MMCallList *self,
+ const gchar *call_path,
+ GError **error);
+
+MMBaseCall *mm_call_list_get_first_incoming_call (MMCallList *self,
+ MMCallState incoming_state);
+
+typedef void (* MMCallListForeachFunc) (MMBaseCall *call,
+ gpointer user_data);
+void mm_call_list_foreach (MMCallList *self,
+ MMCallListForeachFunc callback,
+ gpointer user_data);
+
+#endif /* MM_CALL_LIST_H */
diff --git a/src/mm-call-qmi.c b/src/mm-call-qmi.c
new file mode 100644
index 00000000..e072f0e5
--- /dev/null
+++ b/src/mm-call-qmi.c
@@ -0,0 +1,365 @@
+/* -*- 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) 2021 Joel Selvaraj <jo@jsfamily.in>
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-broadband-modem-qmi.h"
+#include "mm-modem-helpers-qmi.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-call-qmi.h"
+#include "mm-base-modem.h"
+#include "mm-log-object.h"
+
+G_DEFINE_TYPE (MMCallQmi, mm_call_qmi, MM_TYPE_BASE_CALL)
+
+/*****************************************************************************/
+
+static gboolean
+ensure_qmi_client (MMCallQmi *self,
+ QmiService service,
+ QmiClient **o_client,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBaseModem *modem = NULL;
+ QmiClient *client;
+ MMPortQmi *port;
+
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &modem,
+ NULL);
+ g_assert (MM_IS_BASE_MODEM (modem));
+
+ port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (modem));
+ g_object_unref (modem);
+
+ if (!port) {
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ ensure_qmi_client,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek QMI port");
+ return FALSE;
+ }
+
+ client = mm_port_qmi_peek_client (port,
+ service,
+ MM_PORT_QMI_FLAG_DEFAULT);
+ if (!client) {
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ ensure_qmi_client,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek client for service '%s'",
+ qmi_service_get_string (service));
+ return FALSE;
+ }
+
+ *o_client = client;
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Start the CALL */
+
+static gboolean
+call_start_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_dial_call_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageVoiceDialCallOutput *output;
+ GError *error = NULL;
+ MMBaseCall *self;
+
+ self = MM_BASE_CALL (g_task_get_source_object (task));
+
+ output = qmi_client_voice_dial_call_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_dial_call_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't create call: ");
+ g_task_return_error (task, error);
+ } else {
+ guint8 call_id = 0;
+
+ qmi_message_voice_dial_call_output_get_call_id (output, &call_id, NULL);
+ if (call_id == 0) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid call index");
+ } else {
+ mm_base_call_set_index (self, call_id);
+ g_task_return_boolean (task, TRUE);
+ }
+ }
+
+ if (output)
+ qmi_message_voice_dial_call_output_unref (output);
+
+ g_object_unref (task);
+}
+
+static void
+call_start (MMBaseCall *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ GTask *task;
+ QmiMessageVoiceDialCallInput *input;
+
+ /* Ensure Voice client */
+ if (!ensure_qmi_client (MM_CALL_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ input = qmi_message_voice_dial_call_input_new ();
+ qmi_message_voice_dial_call_input_set_calling_number (
+ input,
+ mm_gdbus_call_get_number (MM_GDBUS_CALL (self)),
+ NULL);
+
+ mm_obj_dbg (self, "starting call");
+ qmi_client_voice_dial_call (QMI_CLIENT_VOICE (client),
+ input,
+ 90,
+ NULL,
+ (GAsyncReadyCallback) voice_dial_call_ready,
+ task);
+ qmi_message_voice_dial_call_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Accept the call */
+
+static gboolean
+call_accept_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_answer_call_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageVoiceAnswerCallOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_voice_answer_call_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_answer_call_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't accept call: ");
+ g_task_return_error (task, error);
+ } else {
+ g_task_return_boolean (task, TRUE);
+ }
+
+ if (output)
+ qmi_message_voice_answer_call_output_unref (output);
+
+ g_object_unref (task);
+}
+
+static void
+call_accept (MMBaseCall *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ GTask *task;
+ guint8 call_id;
+ QmiMessageVoiceAnswerCallInput *input;
+
+ /* Ensure Voice client */
+ if (!ensure_qmi_client (MM_CALL_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ call_id = mm_base_call_get_index (self);
+ if (call_id == 0) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid call index");
+ g_object_unref (task);
+ return;
+ }
+
+ input = qmi_message_voice_answer_call_input_new ();
+ qmi_message_voice_answer_call_input_set_call_id (
+ input,
+ call_id,
+ NULL);
+
+ mm_obj_dbg (self, "Accepting call with id: %u", call_id);
+ qmi_client_voice_answer_call (QMI_CLIENT_VOICE (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) voice_answer_call_ready,
+ task);
+ qmi_message_voice_answer_call_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Hangup the call */
+
+static gboolean
+call_hangup_finish (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+voice_end_call_ready (QmiClientVoice *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageVoiceEndCallOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_voice_end_call_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_voice_end_call_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't hangup call: ");
+ g_task_return_error (task, error);
+ } else {
+ g_task_return_boolean (task, TRUE);
+ }
+
+ if (output)
+ qmi_message_voice_end_call_output_unref (output);
+
+ g_object_unref (task);
+}
+
+static void
+call_hangup (MMBaseCall *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+ GTask *task;
+ guint8 call_id;
+ QmiMessageVoiceEndCallInput *input;
+
+ /* Ensure Voice client */
+ if (!ensure_qmi_client (MM_CALL_QMI (self),
+ QMI_SERVICE_VOICE, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ call_id = mm_base_call_get_index (self);
+ if (call_id == 0) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid call index");
+ g_object_unref (task);
+ return;
+ }
+
+ input = qmi_message_voice_end_call_input_new ();
+ qmi_message_voice_end_call_input_set_call_id (
+ input,
+ call_id,
+ NULL);
+
+ mm_obj_dbg (self, "Hanging up call with id: %u", call_id);
+ qmi_client_voice_end_call (QMI_CLIENT_VOICE (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) voice_end_call_ready,
+ task);
+ qmi_message_voice_end_call_input_unref (input);
+}
+
+/*****************************************************************************/
+
+MMBaseCall *
+mm_call_qmi_new (MMBaseModem *modem,
+ MMCallDirection direction,
+ const gchar *number)
+{
+ return MM_BASE_CALL (g_object_new (MM_TYPE_CALL_QMI,
+ MM_BASE_CALL_MODEM, modem,
+ "direction", direction,
+ "number", number,
+ MM_BASE_CALL_SKIP_INCOMING_TIMEOUT, TRUE,
+ MM_BASE_CALL_SUPPORTS_DIALING_TO_RINGING, TRUE,
+ MM_BASE_CALL_SUPPORTS_RINGING_TO_ACTIVE, TRUE,
+ NULL));
+}
+
+static void
+mm_call_qmi_init (MMCallQmi *self)
+{
+}
+
+static void
+mm_call_qmi_class_init (MMCallQmiClass *klass)
+{
+ MMBaseCallClass *base_call_class = MM_BASE_CALL_CLASS (klass);
+
+ base_call_class->start = call_start;
+ base_call_class->start_finish = call_start_finish;
+ base_call_class->accept = call_accept;
+ base_call_class->accept_finish = call_accept_finish;
+ base_call_class->hangup = call_hangup;
+ base_call_class->hangup_finish = call_hangup_finish;
+}
diff --git a/src/mm-call-qmi.h b/src/mm-call-qmi.h
new file mode 100644
index 00000000..78402539
--- /dev/null
+++ b/src/mm-call-qmi.h
@@ -0,0 +1,52 @@
+/* -*- 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) 2021 Joel Selvaraj <jo@jsfamily.in>
+ */
+
+#ifndef MM_CALL_QMI_H
+#define MM_CALL_QMI_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-base-call.h"
+
+#define MM_TYPE_CALL_QMI (mm_call_qmi_get_type ())
+#define MM_CALL_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CALL_QMI, MMCallQmi))
+#define MM_CALL_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CALL_QMI, MMCallQmiClass))
+#define MM_IS_CALL_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CALL_QMI))
+#define MM_IS_CALL_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CALL_QMI))
+#define MM_CALL_QMI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CALL_QMI, MMCallQmiClass))
+
+typedef struct _MMCallQmi MMCallQmi;
+typedef struct _MMCallQmiClass MMCallQmiClass;
+
+struct _MMCallQmi {
+ MMBaseCall parent;
+};
+
+struct _MMCallQmiClass {
+ MMBaseCallClass parent;
+};
+
+GType mm_call_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCallQmi, g_object_unref)
+
+MMBaseCall *mm_call_qmi_new (MMBaseModem *modem,
+ MMCallDirection direction,
+ const gchar *number);
+
+#endif /* MM_CALL_QMI_H */
diff --git a/src/mm-charsets.c b/src/mm-charsets.c
index ffdcad52..a48da368 100644
--- a/src/mm-charsets.c
+++ b/src/mm-charsets.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2010 Red Hat, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -24,204 +25,79 @@
#include <libmm-glib.h>
#include "mm-charsets.h"
+#include "mm-log.h"
-typedef struct {
- const char *gsm_name;
- const char *other_name;
- const char *iconv_from_name;
- const char *iconv_to_name;
- MMModemCharset charset;
-} CharsetEntry;
-
-static CharsetEntry charset_map[] = {
- { "UTF-8", "UTF8", "UTF-8", "UTF-8//TRANSLIT", MM_MODEM_CHARSET_UTF8 },
- { "UCS2", NULL, "UCS-2BE", "UCS-2BE//TRANSLIT", MM_MODEM_CHARSET_UCS2 },
- { "IRA", "ASCII", "ASCII", "ASCII//TRANSLIT", MM_MODEM_CHARSET_IRA },
- { "GSM", NULL, NULL, NULL, MM_MODEM_CHARSET_GSM },
- { "8859-1", NULL, "ISO8859-1", "ISO8859-1//TRANSLIT", MM_MODEM_CHARSET_8859_1 },
- { "PCCP437", "CP437", "CP437", "CP437//TRANSLIT", MM_MODEM_CHARSET_PCCP437 },
- { "PCDN", "CP850", "CP850", "CP850//TRANSLIT", MM_MODEM_CHARSET_PCDN },
- { "HEX", NULL, NULL, NULL, MM_MODEM_CHARSET_HEX },
- { NULL, NULL, NULL, NULL, MM_MODEM_CHARSET_UNKNOWN }
-};
-
-const char *
-mm_modem_charset_to_string (MMModemCharset charset)
-{
- CharsetEntry *iter = &charset_map[0];
+/* Common fallback character when transliteration is enabled */
+static const gchar *translit_fallback = "?";
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
+/******************************************************************************/
+/* Expected charset settings */
- while (iter->gsm_name) {
- if (iter->charset == charset)
- return iter->gsm_name;
- iter++;
- }
- g_warn_if_reached ();
- return NULL;
-}
+typedef struct {
+ MMModemCharset charset;
+ const gchar *gsm_name;
+ const gchar *other_name;
+ const gchar *iconv_name;
+} CharsetSettings;
+
+static const CharsetSettings charset_settings[] = {
+ { MM_MODEM_CHARSET_UTF8, "UTF-8", "UTF8", "UTF-8" },
+ { MM_MODEM_CHARSET_UCS2, "UCS2", NULL, "UCS-2BE" },
+ { MM_MODEM_CHARSET_IRA, "IRA", "ASCII", "ASCII" },
+ { MM_MODEM_CHARSET_GSM, "GSM", NULL, NULL },
+ { MM_MODEM_CHARSET_8859_1, "8859-1", NULL, "ISO8859-1" },
+ { MM_MODEM_CHARSET_PCCP437, "PCCP437", "CP437", "CP437" },
+ { MM_MODEM_CHARSET_PCDN, "PCDN", "CP850", "CP850" },
+ { MM_MODEM_CHARSET_UTF16, "UTF-16", "UTF16", "UTF-16BE" },
+};
MMModemCharset
-mm_modem_charset_from_string (const char *string)
+mm_modem_charset_from_string (const gchar *string)
{
- CharsetEntry *iter = &charset_map[0];
+ guint i;
g_return_val_if_fail (string != NULL, MM_MODEM_CHARSET_UNKNOWN);
- while (iter->gsm_name) {
- if (strcasestr (string, iter->gsm_name))
- return iter->charset;
- if (iter->other_name && strcasestr (string, iter->other_name))
- return iter->charset;
- iter++;
+ for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) {
+ if (strcasestr (string, charset_settings[i].gsm_name))
+ return charset_settings[i].charset;
+ if (charset_settings[i].other_name && strcasestr (string, charset_settings[i].other_name))
+ return charset_settings[i].charset;
}
return MM_MODEM_CHARSET_UNKNOWN;
}
-static const char *
-charset_iconv_to (MMModemCharset charset)
+static const CharsetSettings *
+lookup_charset_settings (MMModemCharset charset)
{
- CharsetEntry *iter = &charset_map[0];
+ guint i;
g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- while (iter->gsm_name) {
- if (iter->charset == charset)
- return iter->iconv_to_name;
- iter++;
- }
- g_warn_if_reached ();
- return NULL;
-}
-
-static const char *
-charset_iconv_from (MMModemCharset charset)
-{
- CharsetEntry *iter = &charset_map[0];
-
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- while (iter->gsm_name) {
- if (iter->charset == charset)
- return iter->iconv_from_name;
- iter++;
+ for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) {
+ if (charset_settings[i].charset == charset)
+ return &charset_settings[i];
}
g_warn_if_reached ();
return NULL;
}
-gboolean
-mm_modem_charset_byte_array_append (GByteArray *array,
- const char *utf8,
- gboolean quoted,
- MMModemCharset charset)
-{
- const char *iconv_to;
- char *converted;
- GError *error = NULL;
- gsize written = 0;
-
- g_return_val_if_fail (array != NULL, FALSE);
- g_return_val_if_fail (utf8 != NULL, FALSE);
-
- iconv_to = charset_iconv_to (charset);
- g_return_val_if_fail (iconv_to != NULL, FALSE);
-
- converted = g_convert (utf8, -1, iconv_to, "UTF-8", NULL, &written, &error);
- if (!converted) {
- if (error) {
- g_warning ("%s: failed to convert '%s' to %s character set: (%d) %s",
- __func__, utf8, iconv_to,
- error->code, error->message);
- g_error_free (error);
- }
- return FALSE;
- }
-
- if (quoted)
- g_byte_array_append (array, (const guint8 *) "\"", 1);
- g_byte_array_append (array, (const guint8 *) converted, written);
- if (quoted)
- g_byte_array_append (array, (const guint8 *) "\"", 1);
-
- g_free (converted);
- return TRUE;
-}
-
-char *
-mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset)
-{
- char *unconverted, *converted;
- const char *iconv_from;
- gsize unconverted_len = 0;
- GError *error = NULL;
-
- g_return_val_if_fail (src != NULL, NULL);
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- iconv_from = charset_iconv_from (charset);
- g_return_val_if_fail (iconv_from != NULL, FALSE);
-
- unconverted = mm_utils_hexstr2bin (src, &unconverted_len);
- if (!unconverted)
- return NULL;
-
- if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA)
- return unconverted;
-
- converted = g_convert (unconverted, unconverted_len,
- "UTF-8//TRANSLIT", iconv_from,
- NULL, NULL, &error);
- if (!converted || error) {
- g_clear_error (&error);
- converted = NULL;
- }
-
- g_free (unconverted);
-
- return converted;
-}
-
-char *
-mm_modem_charset_utf8_to_hex (const char *src, MMModemCharset charset)
+const gchar *
+mm_modem_charset_to_string (MMModemCharset charset)
{
- gsize converted_len = 0;
- char *converted;
- const char *iconv_to;
- GError *error = NULL;
- gchar *hex;
-
- g_return_val_if_fail (src != NULL, NULL);
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, NULL);
-
- iconv_to = charset_iconv_from (charset);
- g_return_val_if_fail (iconv_to != NULL, FALSE);
-
- if (charset == MM_MODEM_CHARSET_UTF8 || charset == MM_MODEM_CHARSET_IRA)
- return g_strdup (src);
-
- converted = g_convert (src, strlen (src),
- iconv_to, "UTF-8//TRANSLIT",
- NULL, &converted_len, &error);
- if (!converted || error) {
- g_clear_error (&error);
- g_free (converted);
- return NULL;
- }
+ const CharsetSettings *settings;
- /* Get hex representation of the string */
- hex = mm_utils_bin2hexstr ((guint8 *)converted, converted_len);
- g_free (converted);
- return hex;
+ settings = lookup_charset_settings (charset);
+ return settings ? settings->gsm_name : NULL;
}
+/******************************************************************************/
/* GSM 03.38 encoding conversion stuff */
#define GSM_DEF_ALPHABET_SIZE 128
#define GSM_EXT_ALPHABET_SIZE 10
typedef struct GsmUtf8Mapping {
- gchar chars[3];
+ gchar chars[3];
guint8 len;
guint8 gsm; /* only used for extended GSM charset */
} GsmUtf8Mapping;
@@ -305,7 +181,8 @@ static const GsmUtf8Mapping gsm_def_utf8_alphabet[GSM_DEF_ALPHABET_SIZE] = {
};
static guint8
-gsm_def_char_to_utf8 (const guint8 gsm, guint8 out_utf8[2])
+gsm_def_char_to_utf8 (const guint8 gsm,
+ guint8 out_utf8[2])
{
g_return_val_if_fail (gsm < GSM_DEF_ALPHABET_SIZE, 0);
memcpy (&out_utf8[0], &gsm_def_utf8_alphabet[gsm].chars[0], gsm_def_utf8_alphabet[gsm].len);
@@ -313,9 +190,11 @@ gsm_def_char_to_utf8 (const guint8 gsm, guint8 out_utf8[2])
}
static gboolean
-utf8_to_gsm_def_char (const char *utf8, guint32 len, guint8 *out_gsm)
+utf8_to_gsm_def_char (const gchar *utf8,
+ guint32 len,
+ guint8 *out_gsm)
{
- int i;
+ gint i;
if (len > 0 && len < 4) {
for (i = 0; i < GSM_DEF_ALPHABET_SIZE; i++) {
@@ -330,6 +209,22 @@ utf8_to_gsm_def_char (const char *utf8, guint32 len, guint8 *out_gsm)
return FALSE;
}
+static gboolean
+translit_gsm_nul_byte (GByteArray *gsm)
+{
+ guint i;
+ guint n_replaces = 0;
+
+ for (i = 0; i < gsm->len; i++) {
+ if (gsm->data[i] == 0x00) {
+ utf8_to_gsm_def_char (translit_fallback, strlen (translit_fallback), &gsm->data[i]);
+ n_replaces++;
+ }
+ }
+
+ return (n_replaces > 0);
+}
+
#define EONE(a, g) { {a, 0x00, 0x00}, 1, g }
#define ETHR(a, b, c, g) { {a, b, c}, 3, g }
@@ -352,7 +247,8 @@ static const GsmUtf8Mapping gsm_ext_utf8_alphabet[GSM_EXT_ALPHABET_SIZE] = {
#define GSM_ESCAPE_CHAR 0x1b
static guint8
-gsm_ext_char_to_utf8 (const guint8 gsm, guint8 out_utf8[3])
+gsm_ext_char_to_utf8 (const guint8 gsm,
+ guint8 out_utf8[3])
{
int i;
@@ -366,7 +262,9 @@ gsm_ext_char_to_utf8 (const guint8 gsm, guint8 out_utf8[3])
}
static gboolean
-utf8_to_gsm_ext_char (const char *utf8, guint32 len, guint8 *out_gsm)
+utf8_to_gsm_ext_char (const gchar *utf8,
+ guint32 len,
+ guint8 *out_gsm)
{
int i;
@@ -383,11 +281,14 @@ utf8_to_gsm_ext_char (const char *utf8, guint32 len, guint8 *out_gsm)
return FALSE;
}
-guint8 *
-mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len)
+static guint8 *
+charset_gsm_unpacked_to_utf8 (const guint8 *gsm,
+ guint32 len,
+ gboolean translit,
+ GError **error)
{
- int i;
- GByteArray *utf8;
+ g_autoptr(GByteArray) utf8 = NULL;
+ guint i;
g_return_val_if_fail (gsm != NULL, NULL);
g_return_val_if_fail (len < 4096, NULL);
@@ -399,6 +300,28 @@ mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len)
guint8 uchars[4];
guint8 ulen;
+ /*
+ * 0x00 is NULL (when followed only by 0x00 up to the
+ * end of (fixed byte length) message, possibly also up to
+ * FORM FEED. But 0x00 is also the code for COMMERCIAL AT
+ * when some other character (CARRIAGE RETURN if nothing else)
+ * comes after the 0x00.
+ * http://unicode.org/Public/MAPPINGS/ETSI/GSM0338.TXT
+ *
+ * So, if we find a '@' (0x00) and all the next chars after that
+ * are also 0x00, we can consider the string finished already.
+ */
+ if (gsm[i] == 0x00) {
+ gsize j;
+
+ for (j = i + 1; j < len; j++) {
+ if (gsm[j] != 0x00)
+ break;
+ }
+ if (j == len)
+ break;
+ }
+
if (gsm[i] == GSM_ESCAPE_CHAR) {
/* Extended alphabet, decode next char */
ulen = gsm_ext_char_to_utf8 (gsm[i+1], uchars);
@@ -411,25 +334,36 @@ mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len)
if (ulen)
g_byte_array_append (utf8, &uchars[0], ulen);
- else
- g_byte_array_append (utf8, (guint8 *) "?", 1);
+ else if (translit)
+ g_byte_array_append (utf8, (guint8 *) translit_fallback, strlen (translit_fallback));
+ else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid conversion from GSM7");
+ return NULL;
+ }
}
- g_byte_array_append (utf8, (guint8 *) "\0", 1); /* NULL terminator */
- return g_byte_array_free (utf8, FALSE);
+ /* Always make sure returned string is NUL terminated */
+ g_byte_array_append (utf8, (guint8 *) "\0", 1);
+ return g_byte_array_free (g_steal_pointer (&utf8), FALSE);
}
-guint8 *
-mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len)
+static guint8 *
+charset_utf8_to_unpacked_gsm (const gchar *utf8,
+ gboolean translit,
+ guint32 *out_len,
+ GError **error)
{
- GByteArray *gsm;
- const char *c = utf8, *next = c;
- static const guint8 gesc = GSM_ESCAPE_CHAR;
- int i = 0;
-
- g_return_val_if_fail (utf8 != NULL, NULL);
- g_return_val_if_fail (out_len != NULL, NULL);
- g_return_val_if_fail (g_utf8_validate (utf8, -1, NULL), NULL);
+ g_autoptr(GByteArray) gsm = NULL;
+ const gchar *c;
+ const gchar *next;
+ static const guint8 gesc = GSM_ESCAPE_CHAR;
+
+ if (!utf8 || !g_utf8_validate (utf8, -1, NULL)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't convert UTF-8 to GSM: input UTF-8 validation failed");
+ return NULL;
+ }
/* worst case initial length */
gsm = g_byte_array_sized_new (g_utf8_strlen (utf8, -1) * 2 + 1);
@@ -437,10 +371,13 @@ mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len)
if (*utf8 == 0x00) {
/* Zero-length string */
g_byte_array_append (gsm, (guint8 *) "\0", 1);
- *out_len = 0;
- return g_byte_array_free (gsm, FALSE);
+ if (out_len)
+ *out_len = 0;
+ return g_byte_array_free (g_steal_pointer (&gsm), FALSE);
}
+ next = utf8;
+ c = utf8;
while (next && *next) {
guint8 gch = 0x3f; /* 0x3f == '?' */
@@ -451,55 +388,83 @@ mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len)
/* Add the escape char */
g_byte_array_append (gsm, &gesc, 1);
g_byte_array_append (gsm, &gch, 1);
- } else if (utf8_to_gsm_def_char (c, next - c, &gch))
+ } else if (utf8_to_gsm_def_char (c, next - c, &gch)) {
+ g_byte_array_append (gsm, &gch, 1);
+ } else if (translit) {
+ /* add ? */
g_byte_array_append (gsm, &gch, 1);
+ } else {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't convert UTF-8 char to GSM");
+ return NULL;
+ }
c = next;
- i++;
}
- *out_len = gsm->len;
- return g_byte_array_free (gsm, FALSE);
+ /* Output length doesn't consider terminating NUL byte */
+ if (out_len)
+ *out_len = gsm->len;
+
+ /* Always make sure returned string is NUL terminated */
+ g_byte_array_append (gsm, (guint8 *) "\0", 1);
+ return g_byte_array_free (g_steal_pointer (&gsm), FALSE);
}
+/******************************************************************************/
+/* Checks to see whether conversion to a target charset may be done without
+ * any loss. */
+
static gboolean
-gsm_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
+gsm_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
{
guint8 gsm;
- *out_clen = 1;
if (utf8_to_gsm_def_char (utf8, ulen, &gsm))
return TRUE;
- if (utf8_to_gsm_ext_char (utf8, ulen, &gsm)) {
- *out_clen = 2;
+ if (utf8_to_gsm_ext_char (utf8, ulen, &gsm))
return TRUE;
- }
return FALSE;
}
static gboolean
-ira_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
+ira_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
{
- *out_clen = 1;
return (ulen == 1);
}
static gboolean
-ucs2_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
+ucs2_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
{
- *out_clen = 2;
return (c <= 0xFFFF);
}
static gboolean
-iso88591_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
+utf16_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
+{
+ return TRUE;
+}
+
+static gboolean
+iso88591_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
{
- *out_clen = 1;
return (c <= 0xFF);
}
static gboolean
-pccp437_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
+pccp437_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
{
static const gunichar t[] = {
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea,
@@ -518,13 +483,11 @@ pccp437_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2,
0x25a0, 0x00a0
};
- int i;
-
- *out_clen = 1;
+ guint i;
if (c <= 0x7F)
return TRUE;
- for (i = 0; i < sizeof (t) / sizeof (t[0]); i++) {
+ for (i = 0; i < G_N_ELEMENTS (t); i++) {
if (c == t[i])
return TRUE;
}
@@ -532,7 +495,9 @@ pccp437_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
}
static gboolean
-pcdn_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
+pcdn_is_subset (gunichar c,
+ const gchar *utf8,
+ gsize ulen)
{
static const gunichar t[] = {
0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea,
@@ -551,9 +516,7 @@ pcdn_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2,
0x25a0, 0x00a0
};
- int i;
-
- *out_clen = 1;
+ guint i;
if (c <= 0x7F)
return TRUE;
@@ -566,84 +529,75 @@ pcdn_is_subset (gunichar c, const char *utf8, gsize ulen, guint *out_clen)
typedef struct {
MMModemCharset cs;
- gboolean (*func) (gunichar c, const char *utf8, gsize ulen, guint *out_clen);
- guint charsize;
+ gboolean (*func) (gunichar c,
+ const gchar *utf8,
+ gsize ulen);
} SubsetEntry;
-SubsetEntry subset_table[] = {
- { MM_MODEM_CHARSET_GSM, gsm_is_subset },
- { MM_MODEM_CHARSET_IRA, ira_is_subset },
- { MM_MODEM_CHARSET_UCS2, ucs2_is_subset },
+const SubsetEntry subset_table[] = {
+ { MM_MODEM_CHARSET_GSM, gsm_is_subset },
+ { MM_MODEM_CHARSET_IRA, ira_is_subset },
+ { MM_MODEM_CHARSET_UCS2, ucs2_is_subset },
+ { MM_MODEM_CHARSET_UTF16, utf16_is_subset },
{ MM_MODEM_CHARSET_8859_1, iso88591_is_subset },
- { MM_MODEM_CHARSET_PCCP437, pccp437_is_subset },
- { MM_MODEM_CHARSET_PCDN, pcdn_is_subset },
- { MM_MODEM_CHARSET_UNKNOWN, NULL },
+ { MM_MODEM_CHARSET_PCCP437, pccp437_is_subset },
+ { MM_MODEM_CHARSET_PCDN, pcdn_is_subset },
};
-/**
- * mm_charset_get_encoded_len:
- *
- * @utf8: UTF-8 valid string
- * @charset: the #MMModemCharset to check the length of @utf8 in
- * @out_unsupported: on return, number of characters of @utf8 that are not fully
- * representable in @charset
- *
- * Returns: the size in bytes of the string if converted from UTF-8 into @charset.
- **/
-guint
-mm_charset_get_encoded_len (const char *utf8,
- MMModemCharset charset,
- guint *out_unsupported)
+gboolean
+mm_charset_can_convert_to (const gchar *utf8,
+ MMModemCharset charset)
{
- const char *p = utf8, *next;
- guint len = 0, unsupported = 0;
- SubsetEntry *e;
+ const gchar *p;
+ guint i;
- g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, 0);
- g_return_val_if_fail (utf8 != NULL, 0);
+ g_return_val_if_fail (charset != MM_MODEM_CHARSET_UNKNOWN, FALSE);
+ g_return_val_if_fail (utf8 != NULL, FALSE);
if (charset == MM_MODEM_CHARSET_UTF8)
- return strlen (utf8);
+ return TRUE;
/* Find the charset in our subset table */
- for (e = &subset_table[0];
- e->cs != charset && e->cs != MM_MODEM_CHARSET_UNKNOWN;
- e++);
- g_return_val_if_fail (e->cs != MM_MODEM_CHARSET_UNKNOWN, 0);
+ for (i = 0; i < G_N_ELEMENTS (subset_table); i++) {
+ if (subset_table[i].cs == charset)
+ break;
+ }
+ g_return_val_if_fail (i < G_N_ELEMENTS (subset_table), FALSE);
+ p = utf8;
while (*p) {
gunichar c;
const char *end;
- guint clen = 0;
c = g_utf8_get_char_validated (p, -1);
g_return_val_if_fail (c != (gunichar) -1, 0);
- end = next = g_utf8_find_next_char (p, NULL);
+ end = g_utf8_find_next_char (p, NULL);
if (end == NULL) {
- /* Find the end... */
+ /* Find the string terminating NULL */
end = p;
- while (*end++);
+ while (*++end);
}
- if (!e->func (c, p, (end - p), &clen))
- unsupported++;
- len += clen;
- p = next;
+ if (!subset_table[i].func (c, p, (end - p)))
+ return FALSE;
+
+ p = end;
}
- if (out_unsupported)
- *out_unsupported = unsupported;
- return len;
+ return TRUE;
}
+/******************************************************************************/
+/* GSM-7 pack/unpack operations */
+
guint8 *
-gsm_unpack (const guint8 *gsm,
- guint32 num_septets,
- guint8 start_offset, /* in _bits_ */
- guint32 *out_unpacked_len)
+mm_charset_gsm_unpack (const guint8 *gsm,
+ guint32 num_septets,
+ guint8 start_offset, /* in _bits_ */
+ guint32 *out_unpacked_len)
{
GByteArray *unpacked;
- int i;
+ guint i;
unpacked = g_byte_array_sized_new (num_septets + 1);
@@ -673,14 +627,14 @@ gsm_unpack (const guint8 *gsm,
}
guint8 *
-gsm_pack (const guint8 *src,
- guint32 src_len,
- guint8 start_offset,
- guint32 *out_packed_len)
+mm_charset_gsm_pack (const guint8 *src,
+ guint32 src_len,
+ guint8 start_offset,
+ guint32 *out_packed_len)
{
guint8 *packed;
guint octet = 0, lshift, plen;
- int i = 0;
+ guint i = 0;
g_return_val_if_fail (start_offset < 8, NULL);
@@ -708,202 +662,308 @@ gsm_pack (const guint8 *src,
return packed;
}
-/* We do all our best to get the given string, which is possibly given in the
- * specified charset, to UTF8. It may happen that the given string is really
- * the hex representation of the charset-encoded string, so we need to cope with
- * that case. */
-gchar *
-mm_charset_take_and_convert_to_utf8 (gchar *str, MMModemCharset charset)
+/*****************************************************************************/
+/* Main conversion functions */
+
+static guint8 *
+charset_iconv_from_utf8 (const gchar *utf8,
+ const CharsetSettings *settings,
+ gboolean translit,
+ guint *out_size,
+ GError **error)
{
- gchar *utf8 = NULL;
+ g_autoptr(GError) inner_error = NULL;
+ gsize bytes_written = 0;
+ g_autofree guint8 *encoded = NULL;
+
+ encoded = (guint8 *) g_convert (utf8, -1,
+ settings->iconv_name, "UTF-8",
+ NULL, &bytes_written, &inner_error);
+ if (encoded) {
+ if (out_size)
+ *out_size = (guint) bytes_written;
+ return g_steal_pointer (&encoded);
+ }
- if (!str)
+ if (!translit) {
+ g_propagate_error (error, g_steal_pointer (&inner_error));
+ g_prefix_error (error, "Couldn't convert from UTF-8 to %s: ", settings->gsm_name);
return NULL;
-
- switch (charset) {
- case MM_MODEM_CHARSET_UNKNOWN:
- g_warn_if_reached ();
- utf8 = str;
- break;
-
- case MM_MODEM_CHARSET_HEX:
- /* We'll assume that the HEX string is really valid ASCII at the end */
- utf8 = str;
- break;
-
- case MM_MODEM_CHARSET_GSM:
- case MM_MODEM_CHARSET_8859_1:
- case MM_MODEM_CHARSET_PCCP437:
- case MM_MODEM_CHARSET_PCDN: {
- const gchar *iconv_from;
- GError *error = NULL;
-
- iconv_from = charset_iconv_from (charset);
- utf8 = g_convert (str, strlen (str),
- "UTF-8//TRANSLIT", iconv_from,
- NULL, NULL, &error);
- if (!utf8 || error) {
- g_clear_error (&error);
- utf8 = NULL;
- }
-
- g_free (str);
- break;
}
- case MM_MODEM_CHARSET_UCS2: {
- gsize len;
- gboolean possibly_hex = TRUE;
- gsize bread = 0, bwritten = 0;
+ encoded = (guint8 *) g_convert_with_fallback (utf8, -1,
+ settings->iconv_name, "UTF-8", translit_fallback,
+ NULL, &bytes_written, error);
+ if (encoded) {
+ if (out_size)
+ *out_size = (guint) bytes_written;
+ return g_steal_pointer (&encoded);
+ }
- /* If the string comes in hex-UCS-2, len needs to be a multiple of 4 */
- len = strlen (str);
- if ((len < 4) || ((len % 4) != 0))
- possibly_hex = FALSE;
- else {
- const gchar *p = str;
+ g_prefix_error (error, "Couldn't convert from UTF-8 to %s with translit: ", settings->gsm_name);
+ return NULL;
+}
- /* All chars in the string must be hex */
- while (*p && possibly_hex)
- possibly_hex = isxdigit (*p++);
- }
+GByteArray *
+mm_modem_charset_bytearray_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
+{
+ const CharsetSettings *settings;
+ guint8 *encoded = NULL;
+ guint encoded_size = 0;
- /* If hex, then we expect hex-encoded UCS-2 */
- if (possibly_hex) {
- utf8 = mm_modem_charset_hex_to_utf8 (str, charset);
- if (utf8) {
- g_free (str);
- break;
- }
- }
+ settings = lookup_charset_settings (charset);
- /* If not hex, then it might be raw UCS-2 (very unlikely) or ASCII/UTF-8
- * (much more likely). Try to convert to UTF-8 and if that fails, use
- * the partial conversion length to re-convert the part of the string
- * that is UTF-8, if any.
- */
- utf8 = g_convert (str, strlen (str),
- "UTF-8//TRANSLIT", "UTF-8//TRANSLIT",
- &bread, &bwritten, NULL);
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
+ return NULL;
+ }
- /* Valid conversion, or we didn't get enough valid UTF-8 */
- if (utf8 || (bwritten <= 2)) {
- g_free (str);
+ switch (charset) {
+ case MM_MODEM_CHARSET_GSM:
+ encoded = charset_utf8_to_unpacked_gsm (utf8, translit, &encoded_size, error);
break;
- }
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ case MM_MODEM_CHARSET_UTF16:
+ encoded = charset_iconv_from_utf8 (utf8, settings, translit, &encoded_size, error);
+ break;
+ case MM_MODEM_CHARSET_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
- /* Last try; chop off the original string at the conversion failure
- * location and get what we can.
- */
- str[bread] = '\0';
- utf8 = g_convert (str, strlen (str),
- "UTF-8//TRANSLIT", "UTF-8//TRANSLIT",
- NULL, NULL, NULL);
- g_free (str);
- break;
+ return g_byte_array_new_take (encoded, encoded_size);
+}
+
+gchar *
+mm_modem_charset_str_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
+{
+ g_autoptr(GByteArray) bytearray = NULL;
+
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
+ return NULL;
}
- /* If the given charset is ASCII or UTF8, we really expect the final string
- * already here */
- case MM_MODEM_CHARSET_IRA:
- case MM_MODEM_CHARSET_UTF8:
- utf8 = str;
- break;
+ bytearray = mm_modem_charset_bytearray_from_utf8 (utf8, charset, translit, error);
+ if (!bytearray)
+ return NULL;
+
+ switch (charset) {
+ case MM_MODEM_CHARSET_GSM:
+ /* Note: strings encoded in unpacked GSM-7 can be used as plain
+ * strings as long as the string doesn't contain character '@', which
+ * is the one encoded as 0x00. At this point, we perform transliteration
+ * of the NUL bytes in the GSM-7 bytearray, and we fail the operation
+ * if one or more replacements were done and transliteration wasn't
+ * requested */
+ if (translit_gsm_nul_byte (bytearray) && !translit) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert to GSM-7 string: transliteration required for embedded '@'");
+ return NULL;
+ }
+ /* fall through */
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ return (gchar *) g_byte_array_free (g_steal_pointer (&bytearray), FALSE);
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_UTF16:
+ return mm_utils_bin2hexstr (bytearray->data, bytearray->len);
+ default:
+ case MM_MODEM_CHARSET_UNKNOWN:
+ g_assert_not_reached ();
}
+}
- /* Validate UTF-8 always before returning. This result will be exposed in DBus
- * very likely... */
- if (utf8 && !g_utf8_validate (utf8, -1, NULL)) {
- /* Better return NULL than an invalid UTF-8 string */
- g_free (utf8);
- utf8 = NULL;
+static gchar *
+charset_iconv_to_utf8 (const guint8 *data,
+ guint32 len,
+ const CharsetSettings *settings,
+ gboolean translit,
+ GError **error)
+{
+ g_autoptr(GError) inner_error = NULL;
+ g_autofree gchar *utf8 = NULL;
+
+ utf8 = g_convert ((const gchar *) data, len,
+ "UTF-8",
+ settings->iconv_name,
+ NULL, NULL, &inner_error);
+ if (utf8)
+ return g_steal_pointer (&utf8);
+
+ if (!translit) {
+ g_propagate_error (error, g_steal_pointer (&inner_error));
+ g_prefix_error (error, "Couldn't convert from %s to UTF-8: ", settings->gsm_name);
+ return NULL;
}
- return utf8;
+ utf8 = g_convert_with_fallback ((const gchar *) data, len,
+ "UTF-8", settings->iconv_name, translit_fallback,
+ NULL, NULL, error);
+ if (utf8)
+ return g_steal_pointer (&utf8);
+
+ g_prefix_error (error, "Couldn't convert from %s to UTF-8 with translit: ", settings->gsm_name);
+ return NULL;
}
-/* We do all our best to convert the given string, which comes in UTF-8, to the
- * specified charset. It may be that the output string needs to be the hex
- * representation of the charset-encoded string, so we need to cope with that
- * case. */
gchar *
-mm_utf8_take_and_convert_to_charset (gchar *str,
- MMModemCharset charset)
+mm_modem_charset_bytearray_to_utf8 (GByteArray *bytearray,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
{
- gchar *encoded = NULL;
+ const CharsetSettings *settings;
+ g_autofree gchar *utf8 = NULL;
- if (!str)
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
return NULL;
+ }
+
+ settings = lookup_charset_settings (charset);
+
+ switch (charset) {
+ case MM_MODEM_CHARSET_GSM:
+ utf8 = (gchar *) charset_gsm_unpacked_to_utf8 (bytearray->data,
+ bytearray->len,
+ translit,
+ error);
+ break;
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_UTF16:
+ utf8 = charset_iconv_to_utf8 (bytearray->data,
+ bytearray->len,
+ settings,
+ translit,
+ error);
+ break;
+ case MM_MODEM_CHARSET_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
- /* Validate UTF-8 always before converting */
- if (!g_utf8_validate (str, -1, NULL)) {
- /* Better return NULL than an invalid encoded string */
- g_free (str);
+ if (utf8 && g_utf8_validate (utf8, -1, NULL))
+ return g_steal_pointer (&utf8);
+
+ g_prefix_error (error, "Invalid conversion from %s to UTF-8: ", settings->gsm_name);
+ return NULL;
+}
+
+gchar *
+mm_modem_charset_str_to_utf8 (const gchar *str,
+ gssize len,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error)
+{
+ g_autoptr(GByteArray) bytearray = NULL;
+
+ if (charset == MM_MODEM_CHARSET_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot convert from UTF-8: unknown target charset");
return NULL;
}
+ /* Note: if the input string is GSM-7 encoded and it contains the '@'
+ * character, using -1 to indicate string length won't work properly,
+ * as '@' is encoded as 0x00. Whenever possible, if using GSM-7,
+ * give a proper len value or otherwise use the bytearray_to_utf8()
+ * method instead. */
+ if (len < 0)
+ len = strlen (str);
+
switch (charset) {
- case MM_MODEM_CHARSET_UNKNOWN:
- g_warn_if_reached ();
- encoded = str;
- break;
-
- case MM_MODEM_CHARSET_HEX:
- /* FIXME: What encoding is this? */
- g_warn_if_reached ();
- encoded = str;
- break;
-
- case MM_MODEM_CHARSET_GSM:
- case MM_MODEM_CHARSET_8859_1:
- case MM_MODEM_CHARSET_PCCP437:
- case MM_MODEM_CHARSET_PCDN: {
- const gchar *iconv_to;
- GError *error = NULL;
-
- iconv_to = charset_iconv_from (charset);
- encoded = g_convert (str, strlen (str),
- iconv_to, "UTF-8",
- NULL, NULL, &error);
- if (!encoded || error) {
- g_clear_error (&error);
- encoded = NULL;
- }
+ case MM_MODEM_CHARSET_GSM:
+ case MM_MODEM_CHARSET_IRA:
+ case MM_MODEM_CHARSET_8859_1:
+ case MM_MODEM_CHARSET_UTF8:
+ case MM_MODEM_CHARSET_PCCP437:
+ case MM_MODEM_CHARSET_PCDN:
+ bytearray = g_byte_array_sized_new (len);
+ g_byte_array_append (bytearray, (const guint8 *)str, len);
+ break;
+ case MM_MODEM_CHARSET_UCS2:
+ case MM_MODEM_CHARSET_UTF16: {
+ guint8 *bin = NULL;
+ gsize bin_len;
- g_free (str);
- break;
+ bin = (guint8 *) mm_utils_hexstr2bin (str, len, &bin_len, error);
+ if (!bin)
+ return NULL;
+
+ bytearray = g_byte_array_new_take (bin, bin_len);
+ break;
+ }
+ case MM_MODEM_CHARSET_UNKNOWN:
+ default:
+ g_assert_not_reached ();
}
- case MM_MODEM_CHARSET_UCS2: {
- const gchar *iconv_to;
- gsize encoded_len = 0;
- GError *error = NULL;
- gchar *hex;
-
- iconv_to = charset_iconv_from (charset);
- encoded = g_convert (str, strlen (str),
- iconv_to, "UTF-8",
- NULL, &encoded_len, &error);
- if (!encoded || error) {
- g_clear_error (&error);
- encoded = NULL;
+ return mm_modem_charset_bytearray_to_utf8 (bytearray, charset, translit, error);
+}
+
+/******************************************************************************/
+/* Runtime charset support via iconv() */
+
+void
+mm_modem_charsets_init (void)
+{
+ /* As test string, something we can convert to/from all the encodings */
+ static const gchar *default_test_str = "ModemManager";
+ guint i;
+
+ mm_obj_dbg (NULL, "[charsets] detecting platform iconv() support...");
+ for (i = 0; i < G_N_ELEMENTS (charset_settings); i++) {
+ g_autofree guint8 *enc = NULL;
+ guint enc_size;
+ g_autofree gchar *dec = NULL;
+
+ if (!charset_settings[i].iconv_name)
+ continue;
+
+ enc = charset_iconv_from_utf8 (default_test_str,
+ &charset_settings[i],
+ FALSE,
+ &enc_size,
+ NULL);
+ if (!enc) {
+ mm_obj_dbg (NULL, "[charsets] %s: iconv conversion to charset not supported", charset_settings[i].iconv_name);
+ continue;
}
- /* Get hex representation of the string */
- hex = mm_utils_bin2hexstr ((guint8 *)encoded, encoded_len);
- g_free (encoded);
- encoded = hex;
- g_free (str);
- break;
- }
+ dec = charset_iconv_to_utf8 (enc,
+ enc_size,
+ &charset_settings[i],
+ FALSE,
+ NULL);
+ if (!enc) {
+ mm_obj_dbg (NULL, "[charsets] %s: iconv conversion from charset not supported", charset_settings[i].iconv_name);
+ continue;
+ }
- /* If the given charset is ASCII or UTF8, we really expect the final string
- * already here. */
- case MM_MODEM_CHARSET_IRA:
- case MM_MODEM_CHARSET_UTF8:
- encoded = str;
- break;
+ mm_obj_dbg (NULL, "[charsets] %s: iconv conversion to/from charset is supported", charset_settings[i].iconv_name);
}
-
- return encoded;
}
diff --git a/src/mm-charsets.h b/src/mm-charsets.h
index a85f3dc7..3071f6be 100644
--- a/src/mm-charsets.h
+++ b/src/mm-charsets.h
@@ -18,63 +18,98 @@
#include <glib.h>
+/*****************************************************************************************/
+
typedef enum {
- MM_MODEM_CHARSET_UNKNOWN = 0x00000000,
- MM_MODEM_CHARSET_GSM = 0x00000001,
- MM_MODEM_CHARSET_IRA = 0x00000002,
- MM_MODEM_CHARSET_8859_1 = 0x00000004,
- MM_MODEM_CHARSET_UTF8 = 0x00000008,
- MM_MODEM_CHARSET_UCS2 = 0x00000010,
- MM_MODEM_CHARSET_PCCP437 = 0x00000020,
- MM_MODEM_CHARSET_PCDN = 0x00000040,
- MM_MODEM_CHARSET_HEX = 0x00000080
+ MM_MODEM_CHARSET_UNKNOWN = 0,
+ MM_MODEM_CHARSET_GSM = 1 << 0,
+ MM_MODEM_CHARSET_IRA = 1 << 1,
+ MM_MODEM_CHARSET_8859_1 = 1 << 2,
+ MM_MODEM_CHARSET_UTF8 = 1 << 3,
+ MM_MODEM_CHARSET_UCS2 = 1 << 4,
+ MM_MODEM_CHARSET_PCCP437 = 1 << 5,
+ MM_MODEM_CHARSET_PCDN = 1 << 6,
+ MM_MODEM_CHARSET_UTF16 = 1 << 7,
} MMModemCharset;
-const char *mm_modem_charset_to_string (MMModemCharset charset);
+const gchar *mm_modem_charset_to_string (MMModemCharset charset);
+MMModemCharset mm_modem_charset_from_string (const gchar *string);
-MMModemCharset mm_modem_charset_from_string (const char *string);
+/*****************************************************************************************/
-/* Append the given string to the given byte array but re-encode it
- * into the given charset first. The original string is assumed to be
- * UTF-8 encoded.
- */
-gboolean mm_modem_charset_byte_array_append (GByteArray *array,
- const char *utf8,
- gboolean quoted,
- MMModemCharset charset);
+/* Checks whether conversion to the given charset may be done without errors */
+gboolean mm_charset_can_convert_to (const gchar *utf8,
+ MMModemCharset charset);
-/* Take a string in hex representation ("00430052" or "A4BE11" for example)
- * and convert it from the given character set to UTF-8.
- */
-char *mm_modem_charset_hex_to_utf8 (const char *src, MMModemCharset charset);
+guint8 *mm_charset_gsm_unpack (const guint8 *gsm,
+ guint32 num_septets,
+ guint8 start_offset, /* in bits */
+ guint32 *out_unpacked_len);
-/* Take a string in UTF-8 and convert it to the given charset in hex
- * representation.
- */
-char *mm_modem_charset_utf8_to_hex (const char *src, MMModemCharset charset);
+guint8 *mm_charset_gsm_pack (const guint8 *src,
+ guint32 src_len,
+ guint8 start_offset, /* in bits */
+ guint32 *out_packed_len);
-guint8 *mm_charset_utf8_to_unpacked_gsm (const char *utf8, guint32 *out_len);
+/*****************************************************************************************/
-guint8 *mm_charset_gsm_unpacked_to_utf8 (const guint8 *gsm, guint32 len);
+/*
+ * Convert the given UTF-8 encoded string into the given charset.
+ *
+ * The output is given as a bytearray, because the target charset may allow
+ * embedded NUL bytes (e.g. UTF-16).
+ *
+ * The output encoded string is not guaranteed to be NUL-terminated, instead
+ * the bytearray length itself gives the correct string length.
+ */
+GByteArray *mm_modem_charset_bytearray_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
-/* Returns the size in bytes required to hold the UTF-8 string in the given charset */
-guint mm_charset_get_encoded_len (const char *utf8,
- MMModemCharset charset,
- guint *out_unsupported);
+/*
+ * Convert the given UTF-8 encoded string into the given charset.
+ *
+ * The output is given as a C string, and those charsets that allow
+ * embedded NUL bytes (e.g. UTF-16) will be hex-encoded.
+ *
+ * The output encoded string is guaranteed to be NUL-terminated, and so no
+ * explicit output length is returned.
+ */
+gchar *mm_modem_charset_str_from_utf8 (const gchar *utf8,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
-guint8 *gsm_unpack (const guint8 *gsm,
- guint32 num_septets,
- guint8 start_offset, /* in bits */
- guint32 *out_unpacked_len);
+/*
+ * Convert into an UTF-8 encoded string the input byte array, which is
+ * encoded in the given charset.
+ *
+ * The output string is guaranteed to be valid UTF-8 and NUL-terminated.
+ */
+gchar *mm_modem_charset_bytearray_to_utf8 (GByteArray *bytearray,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
-guint8 *gsm_pack (const guint8 *src,
- guint32 src_len,
- guint8 start_offset, /* in bits */
- guint32 *out_packed_len);
+/*
+ * Convert into an UTF-8 encoded string the input string, which is
+ * encoded in the given charset. Those charsets that allow embedded NUL
+ * bytes (e.g. UTF-16) need to be hex-encoded.
+ *
+ * If the input string is NUL-terminated, len may be given as -1; otherwise
+ * len needs to specify the number of valid bytes in the input string.
+ *
+ * The output string is guaranteed to be valid UTF-8 and NUL-terminated.
+ */
+gchar *mm_modem_charset_str_to_utf8 (const gchar *str,
+ gssize len,
+ MMModemCharset charset,
+ gboolean translit,
+ GError **error);
-gchar *mm_charset_take_and_convert_to_utf8 (gchar *str, MMModemCharset charset);
+/*****************************************************************************************/
-gchar *mm_utf8_take_and_convert_to_charset (gchar *str,
- MMModemCharset charset);
+void mm_modem_charsets_init (void);
#endif /* MM_CHARSETS_H */
diff --git a/src/mm-context.c b/src/mm-context.c
index 4f888518..8c341667 100644
--- a/src/mm-context.c
+++ b/src/mm-context.c
@@ -13,27 +13,88 @@
* Copyright (C) 2012 Aleksander Morgado <aleksander@gnu.org>
*/
+#include <config.h>
#include <stdlib.h>
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
#include "mm-context.h"
/*****************************************************************************/
/* Application context */
-static gboolean version_flag;
-static gboolean debug;
-static const gchar *log_level;
-static const gchar *log_file;
-static gboolean show_ts;
-static gboolean rel_ts;
+#if defined WITH_UDEV || defined WITH_QRTR
+# define NO_AUTO_SCAN_OPTION_FLAG 0
+# define NO_AUTO_SCAN_DEFAULT FALSE
+#else
+/* Keep the option when udev and QRTR disabled, just so that the unit test
+ * setup can unconditionally use --no-auto-scan */
+# define NO_AUTO_SCAN_OPTION_FLAG G_OPTION_FLAG_HIDDEN
+# define NO_AUTO_SCAN_DEFAULT TRUE
+#endif
+
+static gboolean help_flag;
+static gboolean version_flag;
+static gboolean debug;
+static MMFilterRule filter_policy = MM_FILTER_POLICY_STRICT;
+static gboolean no_auto_scan = NO_AUTO_SCAN_DEFAULT;
+static const gchar *initial_kernel_events;
+
+static gboolean
+filter_policy_option_arg (const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ if (!g_ascii_strcasecmp (value, "whitelist-only")) {
+ filter_policy = MM_FILTER_POLICY_WHITELIST_ONLY;
+ return TRUE;
+ }
+
+ if (!g_ascii_strcasecmp (value, "strict")) {
+ filter_policy = MM_FILTER_POLICY_STRICT;
+ return TRUE;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid filter policy value given: %s",
+ value);
+ return FALSE;
+}
static const GOptionEntry entries[] = {
- { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag, "Print version", NULL },
- { "debug", 0, 0, G_OPTION_ARG_NONE, &debug, "Run with extended debugging capabilities", NULL },
- { "log-level", 0, 0, G_OPTION_ARG_STRING, &log_level, "Log level: one of [ERR, WARN, INFO, DEBUG]", "INFO" },
- { "log-file", 0, 0, G_OPTION_ARG_STRING, &log_file, "Path to log file", NULL },
- { "timestamps", 0, 0, G_OPTION_ARG_NONE, &show_ts, "Show timestamps in log output", NULL },
- { "relative-timestamps", 0, 0, G_OPTION_ARG_NONE, &rel_ts, "Use relative timestamps (from MM start)", NULL },
+ {
+ "filter-policy", 0, 0, G_OPTION_ARG_CALLBACK, filter_policy_option_arg,
+ "Filter policy: one of WHITELIST-ONLY, STRICT",
+ "[POLICY]"
+ },
+ {
+ "no-auto-scan", 0, NO_AUTO_SCAN_OPTION_FLAG, G_OPTION_ARG_NONE, &no_auto_scan,
+ "Don't auto-scan looking for devices",
+ NULL
+ },
+ {
+ "initial-kernel-events", 0, 0, G_OPTION_ARG_FILENAME, &initial_kernel_events,
+ "Path to initial kernel events file",
+ "[PATH]"
+ },
+ {
+ "debug", 0, 0, G_OPTION_ARG_NONE, &debug,
+ "Run with extended debugging capabilities",
+ NULL
+ },
+ {
+ "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ {
+ "help", 'h', 0, G_OPTION_ARG_NONE, &help_flag,
+ "Show help",
+ NULL
+ },
{ NULL }
};
@@ -44,6 +105,78 @@ mm_context_get_debug (void)
}
const gchar *
+mm_context_get_initial_kernel_events (void)
+{
+ return initial_kernel_events;
+}
+
+gboolean
+mm_context_get_no_auto_scan (void)
+{
+ return no_auto_scan;
+}
+
+MMFilterRule
+mm_context_get_filter_policy (void)
+{
+ return filter_policy;
+}
+
+/*****************************************************************************/
+/* Log context */
+
+static const gchar *log_level;
+static const gchar *log_file;
+static gboolean log_journal;
+static gboolean log_show_ts;
+static gboolean log_rel_ts;
+
+static const GOptionEntry log_entries[] = {
+ {
+ "log-level", 0, 0, G_OPTION_ARG_STRING, &log_level,
+ "Log level: one of ERR, WARN, INFO, DEBUG",
+ "[LEVEL]"
+ },
+ {
+ "log-file", 0, 0, G_OPTION_ARG_FILENAME, &log_file,
+ "Path to log file",
+ "[PATH]"
+ },
+#if defined WITH_SYSTEMD_JOURNAL
+ {
+ "log-journal", 0, 0, G_OPTION_ARG_NONE, &log_journal,
+ "Log to systemd journal",
+ NULL
+ },
+#endif
+ {
+ "log-timestamps", 0, 0, G_OPTION_ARG_NONE, &log_show_ts,
+ "Show timestamps in log output",
+ NULL
+ },
+ {
+ "log-relative-timestamps", 0, 0, G_OPTION_ARG_NONE, &log_rel_ts,
+ "Use relative timestamps (from MM start)",
+ NULL
+ },
+ { NULL }
+};
+
+static GOptionGroup *
+log_get_option_group (void)
+{
+ GOptionGroup *group;
+
+ group = g_option_group_new ("log",
+ "Logging options:",
+ "Show logging options",
+ NULL,
+ NULL);
+ g_option_group_add_entries (group, log_entries);
+ return group;
+}
+
+const gchar *
mm_context_get_log_level (void)
{
return log_level;
@@ -56,30 +189,88 @@ mm_context_get_log_file (void)
}
gboolean
-mm_context_get_timestamps (void)
+mm_context_get_log_journal (void)
{
- return show_ts;
+ return log_journal;
}
gboolean
-mm_context_get_relative_timestamps (void)
+mm_context_get_log_timestamps (void)
{
- return rel_ts;
+ return log_show_ts;
+}
+
+gboolean
+mm_context_get_log_relative_timestamps (void)
+{
+ return log_rel_ts;
}
/*****************************************************************************/
/* Test context */
-static gboolean test_session;
-static gboolean test_no_auto_scan;
-static gboolean test_enable;
-static gchar *test_plugin_dir;
+static gboolean test_session;
+static gboolean test_enable;
+static gchar *test_plugin_dir;
+#if defined WITH_UDEV
+static gboolean test_no_udev;
+#endif
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+static gboolean test_no_suspend_resume;
+static gboolean test_quick_suspend_resume;
+#endif
+#if defined WITH_QRTR
+static gboolean test_no_qrtr;
+#endif
+static gboolean test_multiplex_requested;
static const GOptionEntry test_entries[] = {
- { "test-session", 0, 0, G_OPTION_ARG_NONE, &test_session, "Run in session DBus", NULL },
- { "test-no-auto-scan", 0, 0, G_OPTION_ARG_NONE, &test_no_auto_scan, "Don't auto-scan looking for devices", NULL },
- { "test-enable", 0, 0, G_OPTION_ARG_NONE, &test_enable, "Enable the Test interface in the daemon", NULL },
- { "test-plugin-dir", 0, 0, G_OPTION_ARG_STRING, &test_plugin_dir, "Path to look for plugins", "[PATH]" },
+ {
+ "test-session", 0, 0, G_OPTION_ARG_NONE, &test_session,
+ "Run in session DBus",
+ NULL
+ },
+ {
+ "test-enable", 0, 0, G_OPTION_ARG_NONE, &test_enable,
+ "Enable the Test interface in the daemon",
+ NULL
+ },
+ {
+ "test-plugin-dir", 0, 0, G_OPTION_ARG_FILENAME, &test_plugin_dir,
+ "Path to look for plugins",
+ "[PATH]"
+ },
+#if defined WITH_UDEV
+ {
+ "test-no-udev", 0, 0, G_OPTION_ARG_NONE, &test_no_udev,
+ "Run without udev support even if available",
+ NULL
+ },
+#endif
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+ {
+ "test-no-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_no_suspend_resume,
+ "Disable suspend/resume support at runtime even if available",
+ NULL
+ },
+ {
+ "test-quick-suspend-resume", 0, 0, G_OPTION_ARG_NONE, &test_quick_suspend_resume,
+ "Enable quick suspend/resume support for modems which stay on during host suspension",
+ NULL
+ },
+#endif
+#if defined WITH_QRTR
+ {
+ "test-no-qrtr", 0, 0, G_OPTION_ARG_NONE, &test_no_qrtr,
+ "Run without qrtr support even if available",
+ NULL
+ },
+#endif
+ {
+ "test-multiplex-requested", 0, 0, G_OPTION_ARG_NONE, &test_multiplex_requested,
+ "Default to request multiplex support if no explicitly given",
+ NULL
+ },
{ NULL }
};
@@ -89,7 +280,7 @@ test_get_option_group (void)
GOptionGroup *group;
group = g_option_group_new ("test",
- "Test options",
+ "Test options:",
"Show Test options",
NULL,
NULL);
@@ -104,12 +295,6 @@ mm_context_get_test_session (void)
}
gboolean
-mm_context_get_test_no_auto_scan (void)
-{
- return test_no_auto_scan;
-}
-
-gboolean
mm_context_get_test_enable (void)
{
return test_enable;
@@ -121,19 +306,63 @@ mm_context_get_test_plugin_dir (void)
return test_plugin_dir ? test_plugin_dir : PLUGINDIR;
}
+#if defined WITH_UDEV
+gboolean
+mm_context_get_test_no_udev (void)
+{
+ return test_no_udev;
+}
+#endif
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+gboolean
+mm_context_get_test_no_suspend_resume (void)
+{
+ return test_no_suspend_resume;
+}
+gboolean
+mm_context_get_test_quick_suspend_resume (void)
+{
+ return test_quick_suspend_resume;
+}
+#endif
+
+#if defined WITH_QRTR
+gboolean
+mm_context_get_test_no_qrtr (void)
+{
+ return test_no_qrtr;
+}
+#endif
+
+gboolean
+mm_context_get_test_multiplex_requested (void)
+{
+ return test_multiplex_requested;
+}
+
/*****************************************************************************/
static void
print_version (void)
{
- g_print ("\n"
- "ModemManager " MM_DIST_VERSION "\n"
- "Copyright (2008 - 2014) The ModemManager authors\n"
+ g_print ("ModemManager " MM_DIST_VERSION "\n"
+ "Copyright (C) 2008-2021 The ModemManager authors\n"
"License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
"This is free software: you are free to change and redistribute it.\n"
"There is NO WARRANTY, to the extent permitted by law.\n"
"\n");
- exit (EXIT_SUCCESS);
+}
+
+static void
+print_help (GOptionContext *context)
+{
+ gchar *str;
+
+ /* Always print --help-all */
+ str = g_option_context_get_help (context, FALSE, NULL);
+ g_print ("%s", str);
+ g_free (str);
}
void
@@ -144,26 +373,56 @@ mm_context_init (gint argc,
GOptionContext *ctx;
ctx = g_option_context_new (NULL);
- g_option_context_set_summary (ctx, "DBus system service to communicate with modems.");
+ g_option_context_set_summary (ctx, "DBus system service to control mobile broadband modems.");
g_option_context_add_main_entries (ctx, entries, NULL);
+ g_option_context_add_group (ctx, log_get_option_group ());
g_option_context_add_group (ctx, test_get_option_group ());
+ g_option_context_set_help_enabled (ctx, FALSE);
if (!g_option_context_parse (ctx, &argc, &argv, &error)) {
- g_warning ("%s\n", error->message);
+ g_printerr ("error: %s\n", error->message);
g_error_free (error);
exit (1);
}
+ if (version_flag) {
+ print_version ();
+ g_option_context_free (ctx);
+ exit (EXIT_SUCCESS);
+ }
+
+ if (help_flag) {
+ print_help (ctx);
+ g_option_context_free (ctx);
+ exit (EXIT_SUCCESS);
+ }
+
g_option_context_free (ctx);
/* Additional setup to be done on debug mode */
if (debug) {
log_level = "DEBUG";
- if (!show_ts && !rel_ts)
- show_ts = TRUE;
+ if (!log_show_ts && !log_rel_ts)
+ log_show_ts = TRUE;
}
- /* If just version requested, print and exit */
- if (version_flag)
- print_version ();
+ /* Initial kernel events processing may only be used if autoscan is disabled */
+#if defined WITH_UDEV || defined WITH_QRTR
+ if (!no_auto_scan && initial_kernel_events) {
+ g_printerr ("error: --initial-kernel-events must be used only if --no-auto-scan is also used\n");
+ exit (1);
+ }
+# if defined WITH_UDEV
+ /* Force skipping autoscan if running test without udev */
+ if (test_no_udev)
+ no_auto_scan = TRUE;
+# endif
+# if defined WITH_QRTR
+ /* Force skipping autoscan if running test without qrtr */
+ if (test_no_qrtr)
+ no_auto_scan = TRUE;
+# endif
+#endif
+
+
}
diff --git a/src/mm-context.h b/src/mm-context.h
index 6627a601..fefd574e 100644
--- a/src/mm-context.h
+++ b/src/mm-context.h
@@ -19,23 +19,43 @@
#include <config.h>
#include <glib.h>
+#include "mm-filter.h"
+
#if !defined(MM_DIST_VERSION)
# define MM_DIST_VERSION VERSION
#endif
-void mm_context_init (gint argc,
+void mm_context_init (gint argc,
gchar **argv);
-gboolean mm_context_get_debug (void);
-const gchar *mm_context_get_log_level (void);
-const gchar *mm_context_get_log_file (void);
-gboolean mm_context_get_timestamps (void);
-gboolean mm_context_get_relative_timestamps (void);
+gboolean mm_context_get_debug (void);
+const gchar *mm_context_get_initial_kernel_events (void);
+gboolean mm_context_get_no_auto_scan (void);
+
+/* Filter support */
+MMFilterRule mm_context_get_filter_policy (void);
+
+/* Logging support */
+const gchar *mm_context_get_log_level (void);
+const gchar *mm_context_get_log_file (void);
+gboolean mm_context_get_log_journal (void);
+gboolean mm_context_get_log_timestamps (void);
+gboolean mm_context_get_log_relative_timestamps (void);
/* Testing support */
-gboolean mm_context_get_test_session (void);
-gboolean mm_context_get_test_no_auto_scan (void);
-gboolean mm_context_get_test_enable (void);
-const gchar *mm_context_get_test_plugin_dir (void);
+gboolean mm_context_get_test_session (void);
+gboolean mm_context_get_test_enable (void);
+const gchar *mm_context_get_test_plugin_dir (void);
+#if defined WITH_UDEV
+gboolean mm_context_get_test_no_udev (void);
+#endif
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+gboolean mm_context_get_test_no_suspend_resume (void);
+gboolean mm_context_get_test_quick_suspend_resume (void);
+#endif
+#if defined WITH_QRTR
+gboolean mm_context_get_test_no_qrtr (void);
+#endif
+gboolean mm_context_get_test_multiplex_requested (void);
#endif /* MM_CONTEXT_H */
diff --git a/src/mm-device.c b/src/mm-device.c
index 3e2e46f6..c9fa5d05 100644
--- a/src/mm-device.c
+++ b/src/mm-device.c
@@ -25,18 +25,22 @@
#include "mm-device.h"
#include "mm-plugin.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
-G_DEFINE_TYPE (MMDevice, mm_device, G_TYPE_OBJECT);
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMDevice, mm_device, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
- PROP_PATH,
- PROP_UDEV_DEVICE,
+ PROP_UID,
+ PROP_OBJECT_MANAGER,
PROP_PLUGIN,
PROP_MODEM,
PROP_HOTPLUGGED,
PROP_VIRTUAL,
+ PROP_INHIBITED,
PROP_LAST
};
@@ -53,11 +57,13 @@ struct _MMDevicePrivate {
/* Whether the device is real or virtual */
gboolean virtual;
- /* Device path */
- gchar *path;
+ /* Unique id */
+ gchar *uid;
- /* Parent UDev device */
- GUdevDevice *udev_device;
+ /* The object manager */
+ GDBusObjectManagerServer *object_manager;
+
+ /* If USB, device vid/pid */
guint16 vendor;
guint16 product;
@@ -73,206 +79,112 @@ struct _MMDevicePrivate {
/* The Modem object for this device */
MMBaseModem *modem;
-
- /* When exported, a reference to the object manager */
- GDBusObjectManagerServer *object_manager;
+ gulong modem_valid_id;
/* Whether the device was hot-plugged. */
gboolean hotplugged;
+ /* Whether the device is inhibited. */
+ gboolean inhibited;
+
/* Virtual ports */
gchar **virtual_ports;
+
+ /* Scheduled reprobe */
+ guint reprobe_id;
};
/*****************************************************************************/
static MMPortProbe *
-device_find_probe_with_device (MMDevice *self,
- GUdevDevice *udev_port,
- gboolean lookup_ignored)
+probe_list_lookup_by_device (GList *port_probes,
+ MMKernelDevice *kernel_port)
{
GList *l;
- for (l = self->priv->port_probes; l; l = g_list_next (l)) {
+ for (l = port_probes; l; l = g_list_next (l)) {
MMPortProbe *probe = MM_PORT_PROBE (l->data);
- if (g_str_equal (g_udev_device_get_sysfs_path (mm_port_probe_peek_port (probe)),
- g_udev_device_get_sysfs_path (udev_port)))
+ if (mm_kernel_device_cmp (mm_port_probe_peek_port (probe), kernel_port))
return probe;
}
+ return NULL;
+}
- if (!lookup_ignored)
- return NULL;
+static MMPortProbe *
+probe_list_lookup_by_name (GList *port_probes,
+ const gchar *subsystem,
+ const gchar *name)
+{
+ GList *l;
- for (l = self->priv->ignored_port_probes; l; l = g_list_next (l)) {
- MMPortProbe *probe = MM_PORT_PROBE (l->data);
+ for (l = port_probes; l; l = g_list_next (l)) {
+ MMPortProbe *probe = MM_PORT_PROBE (l->data);
+ MMKernelDevice *probe_device;
- if (g_str_equal (g_udev_device_get_sysfs_path (mm_port_probe_peek_port (probe)),
- g_udev_device_get_sysfs_path (udev_port)))
+ probe_device = mm_port_probe_peek_port (probe);
+ if ((g_strcmp0 (subsystem, mm_kernel_device_get_subsystem (probe_device)) == 0) &&
+ (g_strcmp0 (name, mm_kernel_device_get_name (probe_device)) == 0))
return probe;
}
-
return NULL;
}
-gboolean
-mm_device_owns_port (MMDevice *self,
- GUdevDevice *udev_port)
+static MMPortProbe *
+device_find_probe_with_device (MMDevice *self,
+ MMKernelDevice *kernel_port,
+ gboolean lookup_ignored)
{
- return !!device_find_probe_with_device (self, udev_port, FALSE);
-}
-
-static gboolean
-get_device_ids (GUdevDevice *device,
- guint16 *vendor,
- guint16 *product)
-{
- GUdevDevice *parent = NULL;
- const gchar *vid = NULL, *pid = NULL, *parent_subsys;
- gboolean success = FALSE;
- char *pci_vid = NULL, *pci_pid = NULL;
-
- parent = g_udev_device_get_parent (device);
- if (parent) {
- parent_subsys = g_udev_device_get_subsystem (parent);
- if (parent_subsys) {
- if (g_str_equal (parent_subsys, "bluetooth")) {
- /* Bluetooth devices report the VID/PID of the BT adapter here,
- * which isn't really what we want. Just return null IDs instead.
- */
- success = TRUE;
- goto out;
- } else if (g_str_equal (parent_subsys, "pcmcia")) {
- /* For PCMCIA devices we need to grab the PCMCIA subsystem's
- * manfid and cardid, since any IDs on the tty device itself
- * may be from PCMCIA controller or something else.
- */
- vid = g_udev_device_get_sysfs_attr (parent, "manf_id");
- pid = g_udev_device_get_sysfs_attr (parent, "card_id");
- if (!vid || !pid)
- goto out;
- } else if (g_str_equal (parent_subsys, "platform")) {
- /* Platform devices don't usually have a VID/PID */
- success = TRUE;
- goto out;
- } else if (g_str_has_prefix (parent_subsys, "usb") &&
- (!g_strcmp0 (g_udev_device_get_driver (parent), "qmi_wwan") ||
- !g_strcmp0 (g_udev_device_get_driver (parent), "cdc_mbim"))) {
- /* Need to look for vendor/product in the parent of the QMI/MBIM device */
- GUdevDevice *qmi_parent;
-
- qmi_parent = g_udev_device_get_parent (parent);
- if (qmi_parent) {
- vid = g_udev_device_get_property (qmi_parent, "ID_VENDOR_ID");
- pid = g_udev_device_get_property (qmi_parent, "ID_MODEL_ID");
- g_object_unref (qmi_parent);
- }
- } else if (g_str_equal (parent_subsys, "pci")) {
- const char *pci_id;
-
- /* We can't always rely on the model + vendor showing up on
- * the PCI device's child, so look at the PCI parent. PCI_ID
- * has the format "1931:000C".
- */
- pci_id = g_udev_device_get_property (parent, "PCI_ID");
- if (pci_id && strlen (pci_id) == 9 && pci_id[4] == ':') {
- vid = pci_vid = g_strdup (pci_id);
- pci_vid[4] = '\0';
- pid = pci_pid = g_strdup (pci_id + 5);
- }
- }
- }
- }
-
- if (!vid)
- vid = g_udev_device_get_property (device, "ID_VENDOR_ID");
- if (!vid)
- goto out;
-
- if (strncmp (vid, "0x", 2) == 0)
- vid += 2;
- if (strlen (vid) != 4)
- goto out;
-
- if (vendor) {
- *vendor = (guint16) (mm_utils_hex2byte (vid + 2) & 0xFF);
- *vendor |= (guint16) ((mm_utils_hex2byte (vid) & 0xFF) << 8);
- }
-
- if (!pid)
- pid = g_udev_device_get_property (device, "ID_MODEL_ID");
- if (!pid) {
- *vendor = 0;
- goto out;
- }
+ MMPortProbe *probe;
- if (strncmp (pid, "0x", 2) == 0)
- pid += 2;
- if (strlen (pid) != 4) {
- *vendor = 0;
- goto out;
- }
+ probe = probe_list_lookup_by_device (self->priv->port_probes, kernel_port);
+ if (probe)
+ return probe;
- if (product) {
- *product = (guint16) (mm_utils_hex2byte (pid + 2) & 0xFF);
- *product |= (guint16) ((mm_utils_hex2byte (pid) & 0xFF) << 8);
- }
+ if (!lookup_ignored)
+ return NULL;
- success = TRUE;
+ return probe_list_lookup_by_device (self->priv->ignored_port_probes, kernel_port);
+}
-out:
- if (parent)
- g_object_unref (parent);
- g_free (pci_vid);
- g_free (pci_pid);
- return success;
+gboolean
+mm_device_owns_port (MMDevice *self,
+ MMKernelDevice *kernel_port)
+{
+ return !!device_find_probe_with_device (self, kernel_port, TRUE);
}
-const gchar *
-mm_device_utils_get_port_driver (GUdevDevice *udev_port)
-{
- const gchar *driver, *subsys;
- const char *name = g_udev_device_get_name (udev_port);
-
- driver = g_udev_device_get_driver (udev_port);
- if (!driver) {
- GUdevDevice *parent;
-
- parent = g_udev_device_get_parent (udev_port);
- if (parent)
- driver = g_udev_device_get_driver (parent);
-
- /* Check for bluetooth; it's driver is a bunch of levels up so we
- * just check for the subsystem of the parent being bluetooth.
- */
- if (!driver && parent) {
- subsys = g_udev_device_get_subsystem (parent);
- if (subsys && !strcmp (subsys, "bluetooth"))
- driver = "bluetooth";
- }
+static MMPortProbe *
+device_find_probe_with_name (MMDevice *self,
+ const gchar *subsystem,
+ const gchar *name)
+{
+ MMPortProbe *probe;
- if (parent)
- g_object_unref (parent);
- }
+ probe = probe_list_lookup_by_name (self->priv->port_probes, subsystem, name);
+ if (probe)
+ return probe;
- /* Newer kernels don't set up the rfcomm port parent in sysfs,
- * so we must infer it from the device name.
- */
- if (!driver && strncmp (name, "rfcomm", 6) == 0)
- driver = "bluetooth";
+ return probe_list_lookup_by_name (self->priv->ignored_port_probes, subsystem, name);
+}
- return driver;
+gboolean
+mm_device_owns_port_name (MMDevice *self,
+ const gchar *subsystem,
+ const gchar *name)
+{
+ return !!device_find_probe_with_name (self, subsystem, name);
}
static void
-add_port_driver (MMDevice *self,
- GUdevDevice *udev_port)
+add_port_driver (MMDevice *self,
+ MMKernelDevice *kernel_port)
{
const gchar *driver;
guint n_items;
guint i;
- driver = mm_device_utils_get_port_driver (udev_port);
+ driver = mm_kernel_device_get_driver (kernel_port);
if (!driver)
return;
@@ -290,71 +202,94 @@ add_port_driver (MMDevice *self,
if (!driver)
return;
- self->priv->drivers = g_realloc (self->priv->drivers,
- (n_items + 2) * sizeof (gchar *));
+ self->priv->drivers = g_realloc (self->priv->drivers, (n_items + 2) * sizeof (gchar *));
self->priv->drivers[n_items] = g_strdup (driver);
self->priv->drivers[n_items + 1] = NULL;
}
void
-mm_device_grab_port (MMDevice *self,
- GUdevDevice *udev_port)
+mm_device_grab_port (MMDevice *self,
+ MMKernelDevice *kernel_port)
{
- MMPortProbe *probe;
+ MMPortProbe *probe;
+ MMKernelDevice *lower_port;
+
+ if (mm_device_owns_port (self, kernel_port))
+ return;
- if (mm_device_owns_port (self, udev_port))
+ lower_port = mm_kernel_device_peek_lower_device (kernel_port);
+ if (lower_port) {
+ g_autoptr(GError) error = NULL;
+
+ /* No port probing done, at this point this is not something we require
+ * as all the virtual instantiated ports are net devices. We also avoid
+ * emitting the PORT_GRABBED signal in the MMDevice, because that is
+ * exclusively linked to a port being added to the list of probes, which
+ * we don't do here. */
+ if (self->priv->modem && !mm_base_modem_grab_link_port (self->priv->modem, kernel_port, &error))
+ mm_obj_dbg (self, "fully ignoring link port %s from now on: %s",
+ mm_kernel_device_get_name (kernel_port),
+ error->message);
return;
+ }
/* Get the vendor/product IDs out of the first one that gives us
* some valid value (it seems we may get NULL reported for VID in QMI
* ports, e.g. Huawei E367) */
if (!self->priv->vendor && !self->priv->product) {
- if (!get_device_ids (udev_port,
- &self->priv->vendor,
- &self->priv->product)) {
- mm_dbg ("(%s) could not get vendor/product ID",
- self->priv->path);
- }
+ self->priv->vendor = mm_kernel_device_get_physdev_vid (kernel_port);
+ self->priv->product = mm_kernel_device_get_physdev_pid (kernel_port);
}
/* Add new port driver */
- add_port_driver (self, udev_port);
+ add_port_driver (self, kernel_port);
/* Create and store new port probe */
- probe = mm_port_probe_new (self, udev_port);
+ probe = mm_port_probe_new (self, kernel_port);
self->priv->port_probes = g_list_prepend (self->priv->port_probes, probe);
/* Notify about the grabbed port */
- g_signal_emit (self, signals[SIGNAL_PORT_GRABBED], 0, udev_port);
+ g_signal_emit (self, signals[SIGNAL_PORT_GRABBED], 0, kernel_port);
}
void
-mm_device_release_port (MMDevice *self,
- GUdevDevice *udev_port)
+mm_device_release_port_name (MMDevice *self,
+ const gchar *subsystem,
+ const gchar *name)
{
MMPortProbe *probe;
- probe = device_find_probe_with_device (self, udev_port, TRUE);
+ /* If modem exists, try to remove it as a link port. We also avoid emitting
+ * the PORT_RELEASED signal in this case, as the link ports are not associated
+ * to the port probe list */
+ if (self->priv->modem && mm_base_modem_release_link_port (self->priv->modem, subsystem, name, NULL))
+ return;
+
+ probe = device_find_probe_with_name (self, subsystem, name);
if (probe) {
- /* Found, remove from list and destroy probe */
- self->priv->port_probes = g_list_remove (self->priv->port_probes, probe);
+ /* Found, remove from lists and destroy probe */
+ if (g_list_find (self->priv->port_probes, probe))
+ self->priv->port_probes = g_list_remove (self->priv->port_probes, probe);
+ else if (g_list_find (self->priv->ignored_port_probes, probe))
+ self->priv->ignored_port_probes = g_list_remove (self->priv->ignored_port_probes, probe);
+ else
+ g_assert_not_reached ();
g_signal_emit (self, signals[SIGNAL_PORT_RELEASED], 0, mm_port_probe_peek_port (probe));
g_object_unref (probe);
}
}
void
-mm_device_ignore_port (MMDevice *self,
- GUdevDevice *udev_port)
+mm_device_ignore_port (MMDevice *self,
+ MMKernelDevice *kernel_port)
{
MMPortProbe *probe;
- probe = device_find_probe_with_device (self, udev_port, FALSE);
+ probe = device_find_probe_with_device (self, kernel_port, FALSE);
if (probe) {
/* Found, remove from list and add to the ignored list */
- mm_dbg ("Fully ignoring port '%s/%s' from now on",
- g_udev_device_get_subsystem (udev_port),
- g_udev_device_get_name (udev_port));
+ mm_obj_dbg (self, "fully ignoring port %s from now on",
+ mm_kernel_device_get_name (kernel_port));
self->priv->port_probes = g_list_remove (self->priv->port_probes, probe);
self->priv->ignored_port_probes = g_list_prepend (self->priv->ignored_port_probes, probe);
}
@@ -376,10 +311,7 @@ unexport_modem (MMDevice *self)
g_object_set (self->priv->modem,
MM_BASE_MODEM_CONNECTION, NULL,
NULL);
-
- mm_dbg ("Unexported modem '%s' from path '%s'",
- g_udev_device_get_sysfs_path (self->priv->udev_device),
- path);
+ mm_obj_dbg (self, "unexported modem from path '%s'", path);
g_free (path);
}
}
@@ -390,16 +322,14 @@ static void
export_modem (MMDevice *self)
{
GDBusConnection *connection = NULL;
- static guint32 id = 0;
- gchar *path;
+ gchar *path;
g_assert (MM_IS_BASE_MODEM (self->priv->modem));
g_assert (G_IS_DBUS_OBJECT_MANAGER (self->priv->object_manager));
/* If modem not yet valid (not fully initialized), don't export it */
if (!mm_base_modem_get_valid (self->priv->modem)) {
- mm_dbg ("Modem '%s' not yet fully initialized",
- g_udev_device_get_sysfs_path (self->priv->udev_device));
+ mm_obj_dbg (self, "modem not yet fully initialized");
return;
}
@@ -409,14 +339,13 @@ export_modem (MMDevice *self)
NULL);
if (path) {
g_free (path);
- mm_dbg ("Modem '%s' already exported",
- g_udev_device_get_sysfs_path (self->priv->udev_device));
+ mm_obj_dbg (self, "modem already exported");
return;
}
/* No outstanding port tasks, so if the modem is valid we can export it */
- path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", id++);
+ path = g_strdup_printf (MM_DBUS_MODEM_PREFIX "/%d", mm_base_modem_get_dbus_id (self->priv->modem));
g_object_get (self->priv->object_manager,
"connection", &connection,
NULL);
@@ -429,27 +358,35 @@ export_modem (MMDevice *self)
g_dbus_object_manager_server_export (self->priv->object_manager,
G_DBUS_OBJECT_SKELETON (self->priv->modem));
- mm_dbg ("Exported modem '%s' at path '%s'",
- (self->priv->virtual ?
- self->priv->path :
- g_udev_device_get_sysfs_path (self->priv->udev_device)),
- path);
-
- /* Once connected, dump additional debug info about the modem */
- mm_dbg ("(%s): '%s' modem, VID 0x%04X PID 0x%04X (%s)",
- path,
- mm_base_modem_get_plugin (self->priv->modem),
- (mm_base_modem_get_vendor_id (self->priv->modem) & 0xFFFF),
- (mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF),
- (self->priv->virtual ?
- "virtual" :
- g_udev_device_get_subsystem (self->priv->udev_device)));
+ mm_obj_dbg (self, " exported modem at path '%s'", path);
+ mm_obj_dbg (self, " plugin: %s", mm_base_modem_get_plugin (self->priv->modem));
+ mm_obj_dbg (self, " vid:pid: 0x%04X:0x%04X",
+ (mm_base_modem_get_vendor_id (self->priv->modem) & 0xFFFF),
+ (mm_base_modem_get_product_id (self->priv->modem) & 0xFFFF));
+ if (self->priv->virtual)
+ mm_obj_dbg (self, " virtual");
g_free (path);
}
/*****************************************************************************/
+static void
+clear_modem (MMDevice *self)
+{
+ if (self->priv->modem_valid_id) {
+ g_signal_handler_disconnect (self->priv->modem, self->priv->modem_valid_id);
+ self->priv->modem_valid_id = 0;
+ }
+
+ if (self->priv->modem) {
+ /* Run dispose before unref-ing, in order to cleanup the SIM object,
+ * if any (which also holds a reference to the modem object) */
+ g_object_run_dispose (G_OBJECT (self->priv->modem));
+ g_clear_object (&(self->priv->modem));
+ }
+}
+
void
mm_device_remove_modem (MMDevice *self)
{
@@ -457,16 +394,30 @@ mm_device_remove_modem (MMDevice *self)
return;
unexport_modem (self);
-
- /* Run dispose before unref-ing, in order to cleanup the SIM object,
- * if any (which also holds a reference to the modem object) */
- g_object_run_dispose (G_OBJECT (self->priv->modem));
- g_clear_object (&(self->priv->modem));
- g_clear_object (&(self->priv->object_manager));
+ clear_modem (self);
}
/*****************************************************************************/
+#define REPROBE_SECS 2
+
+static gboolean
+reprobe (MMDevice *self)
+{
+ GError *error = NULL;
+
+ self->priv->reprobe_id = 0;
+
+ mm_obj_dbg (self, "Reprobing modem...");
+ if (!mm_device_create_modem (self, &error)) {
+ mm_obj_warn (self, "could not recreate modem: %s", error->message);
+ g_error_free (error);
+ } else
+ mm_obj_dbg (self, "modem recreated");
+
+ return G_SOURCE_REMOVE;
+}
+
static void
modem_valid (MMBaseModem *modem,
GParamSpec *pspec,
@@ -475,6 +426,8 @@ modem_valid (MMBaseModem *modem,
if (!mm_base_modem_get_valid (modem)) {
/* Modem no longer valid */
mm_device_remove_modem (self);
+ if (mm_base_modem_get_reprobe (modem))
+ self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self);
} else {
/* Modem now valid, export it, but only if we really have it around.
* It may happen that the initialization sequence fails because the
@@ -483,17 +436,21 @@ modem_valid (MMBaseModem *modem,
if (self->priv->modem)
export_modem (self);
else
- mm_dbg ("Not exporting modem; no longer available");
+ mm_obj_dbg (self, "not exporting modem; no longer available");
}
}
gboolean
-mm_device_create_modem (MMDevice *self,
- GDBusObjectManagerServer *object_manager,
- GError **error)
+mm_device_create_modem (MMDevice *self,
+ GError **error)
{
g_assert (self->priv->modem == NULL);
- g_assert (self->priv->object_manager == NULL);
+
+ if (self->priv->inhibited) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Device is inhibited");
+ return FALSE;
+ }
if (!self->priv->virtual) {
if (!self->priv->port_probes) {
@@ -504,9 +461,9 @@ mm_device_create_modem (MMDevice *self,
return FALSE;
}
- mm_info ("Creating modem with plugin '%s' and '%u' ports",
- mm_plugin_get_name (self->priv->plugin),
- g_list_length (self->priv->port_probes));
+ mm_obj_info (self, "creating modem with plugin '%s' and '%u' ports",
+ mm_plugin_get_name (self->priv->plugin),
+ g_list_length (self->priv->port_probes));
} else {
if (!self->priv->virtual_ports) {
g_set_error (error,
@@ -516,22 +473,18 @@ mm_device_create_modem (MMDevice *self,
return FALSE;
}
- mm_info ("Creating virtual modem with plugin '%s' and '%u' ports",
- mm_plugin_get_name (self->priv->plugin),
- g_strv_length (self->priv->virtual_ports));
+ mm_obj_info (self, "creating virtual modem with plugin '%s' and '%u' ports",
+ mm_plugin_get_name (self->priv->plugin),
+ g_strv_length (self->priv->virtual_ports));
}
self->priv->modem = mm_plugin_create_modem (self->priv->plugin, self, error);
- if (self->priv->modem) {
- /* Keep the object manager */
- self->priv->object_manager = g_object_ref (object_manager);
-
+ if (self->priv->modem)
/* We want to get notified when the modem becomes valid/invalid */
- g_signal_connect (self->priv->modem,
- "notify::" MM_BASE_MODEM_VALID,
- G_CALLBACK (modem_valid),
- self);
- }
+ self->priv->modem_valid_id = g_signal_connect (self->priv->modem,
+ "notify::" MM_BASE_MODEM_VALID,
+ G_CALLBACK (modem_valid),
+ self);
return !!self->priv->modem;
}
@@ -539,9 +492,9 @@ mm_device_create_modem (MMDevice *self,
/*****************************************************************************/
const gchar *
-mm_device_get_path (MMDevice *self)
+mm_device_get_uid (MMDevice *self)
{
- return self->priv->path;
+ return self->priv->uid;
}
const gchar **
@@ -562,20 +515,6 @@ mm_device_get_product (MMDevice *self)
return self->priv->product;
}
-GUdevDevice *
-mm_device_peek_udev_device (MMDevice *self)
-{
- g_return_val_if_fail (self->priv->udev_device != NULL, NULL);
- return self->priv->udev_device;
-}
-
-GUdevDevice *
-mm_device_get_udev_device (MMDevice *self)
-{
- g_return_val_if_fail (self->priv->udev_device != NULL, NULL);
- return G_UDEV_DEVICE (g_object_ref (self->priv->udev_device));
-}
-
void
mm_device_set_plugin (MMDevice *self,
GObject *plugin)
@@ -597,7 +536,7 @@ GObject *
mm_device_get_plugin (MMDevice *self)
{
return (self->priv->plugin ?
- g_object_ref (self->priv->plugin) :
+ G_OBJECT (g_object_ref (self->priv->plugin)) :
NULL);
}
@@ -618,23 +557,23 @@ mm_device_get_modem (MMDevice *self)
}
GObject *
-mm_device_peek_port_probe (MMDevice *self,
- GUdevDevice *udev_port)
+mm_device_peek_port_probe (MMDevice *self,
+ MMKernelDevice *kernel_port)
{
MMPortProbe *probe;
- probe = device_find_probe_with_device (self, udev_port, FALSE);
+ probe = device_find_probe_with_device (self, kernel_port, FALSE);
return (probe ? G_OBJECT (probe) : NULL);
}
GObject *
-mm_device_get_port_probe (MMDevice *self,
- GUdevDevice *udev_port)
+mm_device_get_port_probe (MMDevice *self,
+ MMKernelDevice *kernel_port)
{
MMPortProbe *probe;
- probe = device_find_probe_with_device (self, udev_port, FALSE);
- return (probe ? g_object_ref (probe) : NULL);
+ probe = device_find_probe_with_device (self, kernel_port, FALSE);
+ return (probe ? G_OBJECT (g_object_ref (probe)) : NULL);
}
GList *
@@ -646,11 +585,9 @@ mm_device_peek_port_probe_list (MMDevice *self)
GList *
mm_device_get_port_probe_list (MMDevice *self)
{
- GList *copy;
-
- copy = g_list_copy (self->priv->port_probes);
- g_list_foreach (copy, (GFunc)g_object_ref, NULL);
- return copy;
+ return g_list_copy_deep (self->priv->port_probes,
+ (GCopyFunc)g_object_ref,
+ NULL);
}
gboolean
@@ -659,6 +596,84 @@ mm_device_get_hotplugged (MMDevice *self)
return self->priv->hotplugged;
}
+gboolean
+mm_device_get_inhibited (MMDevice *self)
+{
+ return self->priv->inhibited;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_device_inhibit_finish (MMDevice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+inhibit_disable_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMDevice *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ if (!mm_base_modem_disable_finish (modem, res, &error))
+ g_task_return_error (task, error);
+ else {
+ g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
+ mm_device_remove_modem (self);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+void
+mm_device_inhibit (MMDevice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* We want to allow inhibiting only devices that are currently
+ * exported in the bus, because otherwise we may be inhibiting
+ * in the middle of port probing and that may lead to some ports
+ * tracked inside the device object during inhibition and some
+ * other ports tracked in the base manager. So, if the device
+ * does not have a valid modem created and exposed, do not allow
+ * the inhibition. */
+ if (!self->priv->modem || !g_dbus_object_get_object_path (G_DBUS_OBJECT (self->priv->modem))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Modem not exported in the bus");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Flag as inhibited right away */
+ g_assert (!self->priv->inhibited);
+ self->priv->inhibited = TRUE;
+
+ /* Make sure modem is disabled while inhibited */
+ mm_base_modem_disable (self->priv->modem,
+ (GAsyncReadyCallback)inhibit_disable_ready,
+ task);
+}
+
+gboolean
+mm_device_uninhibit (MMDevice *self,
+ GError **error)
+{
+ g_assert (self->priv->inhibited);
+ self->priv->inhibited = FALSE;
+ return mm_device_create_modem (self, error);
+}
+
/*****************************************************************************/
void
@@ -693,29 +708,30 @@ mm_device_is_virtual (MMDevice *self)
/*****************************************************************************/
-MMDevice *
-mm_device_new (GUdevDevice *udev_device,
- gboolean hotplugged)
+static gchar *
+log_object_build_id (MMLogObject *_self)
{
- g_return_val_if_fail (udev_device != NULL, NULL);
+ MMDevice *self;
- return MM_DEVICE (g_object_new (MM_TYPE_DEVICE,
- MM_DEVICE_UDEV_DEVICE, udev_device,
- MM_DEVICE_PATH, g_udev_device_get_sysfs_path (udev_device),
- MM_DEVICE_HOTPLUGGED, hotplugged,
- NULL));
+ self = MM_DEVICE (_self);
+ return g_strdup_printf ("device %s", self->priv->uid);
}
+/*****************************************************************************/
+
MMDevice *
-mm_device_virtual_new (const gchar *path,
- gboolean hotplugged)
+mm_device_new (const gchar *uid,
+ gboolean hotplugged,
+ gboolean virtual,
+ GDBusObjectManagerServer *object_manager)
{
- g_return_val_if_fail (path != NULL, NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
return MM_DEVICE (g_object_new (MM_TYPE_DEVICE,
- MM_DEVICE_PATH, path,
- MM_DEVICE_HOTPLUGGED, hotplugged,
- MM_DEVICE_VIRTUAL, TRUE,
+ MM_DEVICE_UID, uid,
+ MM_DEVICE_HOTPLUGGED, hotplugged,
+ MM_DEVICE_VIRTUAL, virtual,
+ MM_DEVICE_OBJECT_MANAGER, object_manager,
NULL));
}
@@ -723,9 +739,7 @@ static void
mm_device_init (MMDevice *self)
{
/* Initialize private data */
- self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
- MM_TYPE_DEVICE,
- MMDevicePrivate);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_DEVICE, MMDevicePrivate);
}
static void
@@ -737,20 +751,20 @@ set_property (GObject *object,
MMDevice *self = MM_DEVICE (object);
switch (prop_id) {
- case PROP_PATH:
+ case PROP_UID:
/* construct only */
- self->priv->path = g_value_dup_string (value);
+ self->priv->uid = g_value_dup_string (value);
break;
- case PROP_UDEV_DEVICE:
+ case PROP_OBJECT_MANAGER:
/* construct only */
- self->priv->udev_device = g_value_dup_object (value);
+ self->priv->object_manager = g_value_dup_object (value);
break;
case PROP_PLUGIN:
g_clear_object (&(self->priv->plugin));
self->priv->plugin = g_value_dup_object (value);
break;
case PROP_MODEM:
- g_clear_object (&(self->priv->modem));
+ clear_modem (self);
self->priv->modem = g_value_dup_object (value);
break;
case PROP_HOTPLUGGED:
@@ -759,6 +773,9 @@ set_property (GObject *object,
case PROP_VIRTUAL:
self->priv->virtual = g_value_get_boolean (value);
break;
+ case PROP_INHIBITED:
+ self->priv->inhibited = g_value_get_boolean (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -774,8 +791,11 @@ get_property (GObject *object,
MMDevice *self = MM_DEVICE (object);
switch (prop_id) {
- case PROP_UDEV_DEVICE:
- g_value_set_object (value, self->priv->udev_device);
+ case PROP_UID:
+ g_value_set_string (value, self->priv->uid);
+ break;
+ case PROP_OBJECT_MANAGER:
+ g_value_set_object (value, self->priv->object_manager);
break;
case PROP_PLUGIN:
g_value_set_object (value, self->priv->plugin);
@@ -789,6 +809,9 @@ get_property (GObject *object,
case PROP_VIRTUAL:
g_value_set_boolean (value, self->priv->virtual);
break;
+ case PROP_INHIBITED:
+ g_value_set_boolean (value, self->priv->inhibited);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -800,11 +823,18 @@ dispose (GObject *object)
{
MMDevice *self = MM_DEVICE (object);
- g_clear_object (&(self->priv->udev_device));
+ if (self->priv->reprobe_id) {
+ g_source_remove (self->priv->reprobe_id);
+ self->priv->reprobe_id = 0;
+ }
+ g_clear_object (&(self->priv->object_manager));
g_clear_object (&(self->priv->plugin));
- g_list_free_full (self->priv->port_probes, (GDestroyNotify)g_object_unref);
- g_list_free_full (self->priv->ignored_port_probes, (GDestroyNotify)g_object_unref);
- g_clear_object (&(self->priv->modem));
+ g_list_free_full (self->priv->port_probes, g_object_unref);
+ self->priv->port_probes = NULL;
+ g_list_free_full (self->priv->ignored_port_probes, g_object_unref);
+ self->priv->ignored_port_probes = NULL;
+
+ clear_modem (self);
G_OBJECT_CLASS (mm_device_parent_class)->dispose (object);
}
@@ -814,7 +844,7 @@ finalize (GObject *object)
{
MMDevice *self = MM_DEVICE (object);
- g_free (self->priv->path);
+ g_free (self->priv->uid);
g_strfreev (self->priv->drivers);
g_strfreev (self->priv->virtual_ports);
@@ -822,6 +852,12 @@ finalize (GObject *object)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_device_class_init (MMDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -831,24 +867,24 @@ mm_device_class_init (MMDeviceClass *klass)
/* Virtual methods */
object_class->get_property = get_property;
object_class->set_property = set_property;
- object_class->finalize = finalize;
- object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ object_class->dispose = dispose;
- properties[PROP_PATH] =
- g_param_spec_string (MM_DEVICE_PATH,
- "Path",
- "Device path",
+ properties[PROP_UID] =
+ g_param_spec_string (MM_DEVICE_UID,
+ "Unique ID",
+ "Unique device id, e.g. the physical device sysfs path",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
- g_object_class_install_property (object_class, PROP_PATH, properties[PROP_PATH]);
+ g_object_class_install_property (object_class, PROP_UID, properties[PROP_UID]);
- properties[PROP_UDEV_DEVICE] =
- g_param_spec_object (MM_DEVICE_UDEV_DEVICE,
- "UDev Device",
- "UDev device object",
- G_UDEV_TYPE_DEVICE,
+ properties[PROP_OBJECT_MANAGER] =
+ g_param_spec_object (MM_DEVICE_OBJECT_MANAGER,
+ "Object manager",
+ "GDBus object manager server",
+ G_TYPE_DBUS_OBJECT_MANAGER_SERVER,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
- g_object_class_install_property (object_class, PROP_UDEV_DEVICE, properties[PROP_UDEV_DEVICE]);
+ g_object_class_install_property (object_class, PROP_OBJECT_MANAGER, properties[PROP_OBJECT_MANAGER]);
properties[PROP_PLUGIN] =
g_param_spec_object (MM_DEVICE_PLUGIN,
@@ -882,6 +918,14 @@ mm_device_class_init (MMDeviceClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_VIRTUAL, properties[PROP_VIRTUAL]);
+ properties[PROP_INHIBITED] =
+ g_param_spec_boolean (MM_DEVICE_INHIBITED,
+ "Inhibited",
+ "Whether the modem is inhibited",
+ FALSE,
+ G_PARAM_READWRITE);
+ g_object_class_install_property (object_class, PROP_INHIBITED, properties[PROP_INHIBITED]);
+
signals[SIGNAL_PORT_GRABBED] =
g_signal_new (MM_DEVICE_PORT_GRABBED,
G_OBJECT_CLASS_TYPE (object_class),
@@ -889,7 +933,7 @@ mm_device_class_init (MMDeviceClass *klass)
G_STRUCT_OFFSET (MMDeviceClass, port_grabbed),
NULL, NULL,
g_cclosure_marshal_generic,
- G_TYPE_NONE, 1, G_UDEV_TYPE_DEVICE);
+ G_TYPE_NONE, 1, MM_TYPE_KERNEL_DEVICE);
signals[SIGNAL_PORT_RELEASED] =
g_signal_new (MM_DEVICE_PORT_RELEASED,
@@ -898,5 +942,5 @@ mm_device_class_init (MMDeviceClass *klass)
G_STRUCT_OFFSET (MMDeviceClass, port_released),
NULL, NULL,
g_cclosure_marshal_generic,
- G_TYPE_NONE, 1, G_UDEV_TYPE_DEVICE);
+ G_TYPE_NONE, 1, MM_TYPE_KERNEL_DEVICE);
}
diff --git a/src/mm-device.h b/src/mm-device.h
index 2da82223..00d5977a 100644
--- a/src/mm-device.h
+++ b/src/mm-device.h
@@ -19,8 +19,7 @@
#include <glib.h>
#include <glib-object.h>
-#include <gudev/gudev.h>
-
+#include "mm-kernel-device.h"
#include "mm-base-modem.h"
#define MM_TYPE_DEVICE (mm_device_get_type ())
@@ -34,12 +33,13 @@ typedef struct _MMDevice MMDevice;
typedef struct _MMDeviceClass MMDeviceClass;
typedef struct _MMDevicePrivate MMDevicePrivate;
-#define MM_DEVICE_PATH "path"
-#define MM_DEVICE_UDEV_DEVICE "udev-device"
-#define MM_DEVICE_PLUGIN "plugin"
-#define MM_DEVICE_MODEM "modem"
-#define MM_DEVICE_HOTPLUGGED "hotplugged"
-#define MM_DEVICE_VIRTUAL "virtual"
+#define MM_DEVICE_UID "uid"
+#define MM_DEVICE_PLUGIN "plugin"
+#define MM_DEVICE_MODEM "modem"
+#define MM_DEVICE_HOTPLUGGED "hotplugged"
+#define MM_DEVICE_VIRTUAL "virtual"
+#define MM_DEVICE_INHIBITED "inhibited"
+#define MM_DEVICE_OBJECT_MANAGER "object-manager"
#define MM_DEVICE_PORT_GRABBED "port-grabbed"
#define MM_DEVICE_PORT_RELEASED "port-released"
@@ -53,62 +53,71 @@ struct _MMDeviceClass {
GObjectClass parent;
/* signals */
- void (* port_grabbed) (MMDevice *self,
- GUdevDevice *port);
- void (* port_released) (MMDevice *self,
- GUdevDevice *port);
+ void (* port_grabbed) (MMDevice *self,
+ MMKernelDevice *port);
+ void (* port_released) (MMDevice *self,
+ MMKernelDevice *port);
};
GType mm_device_get_type (void);
-
-MMDevice *mm_device_new (GUdevDevice *udev_device,
- gboolean hotplugged);
-
-void mm_device_grab_port (MMDevice *self,
- GUdevDevice *udev_port);
-void mm_device_release_port (MMDevice *self,
- GUdevDevice *udev_port);
-gboolean mm_device_owns_port (MMDevice *self,
- GUdevDevice *udev_port);
-void mm_device_ignore_port (MMDevice *self,
- GUdevDevice *udev_port);
-
-gboolean mm_device_create_modem (MMDevice *self,
- GDBusObjectManagerServer *object_manager,
- GError **error);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMDevice, g_object_unref)
+
+MMDevice *mm_device_new (const gchar *uid,
+ gboolean hotplugged,
+ gboolean virtual,
+ GDBusObjectManagerServer *object_manager);
+
+void mm_device_grab_port (MMDevice *self,
+ MMKernelDevice *kernel_port);
+gboolean mm_device_owns_port (MMDevice *self,
+ MMKernelDevice *kernel_port);
+void mm_device_ignore_port (MMDevice *self,
+ MMKernelDevice *kernel_port);
+
+gboolean mm_device_owns_port_name (MMDevice *self,
+ const gchar *subsystem,
+ const gchar *name);
+void mm_device_release_port_name (MMDevice *self,
+ const gchar *subsystem,
+ const gchar *name);
+
+gboolean mm_device_create_modem (MMDevice *self,
+ GError **error);
void mm_device_remove_modem (MMDevice *self);
-const gchar *mm_device_get_path (MMDevice *self);
-const gchar **mm_device_get_drivers (MMDevice *self);
-guint16 mm_device_get_vendor (MMDevice *self);
-guint16 mm_device_get_product (MMDevice *self);
-GUdevDevice *mm_device_peek_udev_device (MMDevice *self);
-GUdevDevice *mm_device_get_udev_device (MMDevice *self);
-void mm_device_set_plugin (MMDevice *self,
- GObject *plugin);
-GObject *mm_device_peek_plugin (MMDevice *self);
-GObject *mm_device_get_plugin (MMDevice *self);
-MMBaseModem *mm_device_peek_modem (MMDevice *self);
-MMBaseModem *mm_device_get_modem (MMDevice *self);
-
-GObject *mm_device_peek_port_probe (MMDevice *self,
- GUdevDevice *udev_port);
-GObject *mm_device_get_port_probe (MMDevice *self,
- GUdevDevice *udev_port);
-GList *mm_device_peek_port_probe_list (MMDevice *self);
-GList *mm_device_get_port_probe_list (MMDevice *self);
-
-const gchar *mm_device_utils_get_port_driver (GUdevDevice *udev_port);
-
-gboolean mm_device_get_hotplugged (MMDevice *self);
-
+void mm_device_inhibit (MMDevice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_device_inhibit_finish (MMDevice *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_device_uninhibit (MMDevice *self,
+ GError **error);
+
+
+const gchar *mm_device_get_uid (MMDevice *self);
+const gchar **mm_device_get_drivers (MMDevice *self);
+guint16 mm_device_get_vendor (MMDevice *self);
+guint16 mm_device_get_product (MMDevice *self);
+void mm_device_set_plugin (MMDevice *self,
+ GObject *plugin);
+GObject *mm_device_peek_plugin (MMDevice *self);
+GObject *mm_device_get_plugin (MMDevice *self);
+MMBaseModem *mm_device_peek_modem (MMDevice *self);
+MMBaseModem *mm_device_get_modem (MMDevice *self);
+GObject *mm_device_peek_port_probe (MMDevice *self,
+ MMKernelDevice *kernel_port);
+GObject *mm_device_get_port_probe (MMDevice *self,
+ MMKernelDevice *kernel_port);
+GList *mm_device_peek_port_probe_list (MMDevice *self);
+GList *mm_device_get_port_probe_list (MMDevice *self);
+gboolean mm_device_get_hotplugged (MMDevice *self);
+gboolean mm_device_get_inhibited (MMDevice *self);
/* For testing purposes */
-MMDevice *mm_device_virtual_new (const gchar *path,
- gboolean hotplugged);
-void mm_device_virtual_grab_ports (MMDevice *self,
+void mm_device_virtual_grab_ports (MMDevice *self,
const gchar **ports);
-const gchar **mm_device_virtual_peek_ports (MMDevice *self);
-gboolean mm_device_is_virtual (MMDevice *self);
+const gchar **mm_device_virtual_peek_ports (MMDevice *self);
+gboolean mm_device_is_virtual (MMDevice *self);
#endif /* MM_DEVICE_H */
diff --git a/src/mm-error-helpers.c b/src/mm-error-helpers.c
index e922cbbf..574c7978 100644
--- a/src/mm-error-helpers.c
+++ b/src/mm-error-helpers.c
@@ -16,247 +16,407 @@
*/
#include "mm-error-helpers.h"
+#include "mm-log.h"
#include <ctype.h>
-typedef struct {
- guint code;
- const gchar *error; /* lowercase, and stripped of special chars and whitespace */
- const gchar *message;
-} ErrorTable;
-
-/* --- Connection errors --- */
+/******************************************************************************/
-GError *
-mm_connection_error_for_code (MMConnectionError code)
+static gchar *
+normalize_error_string (const gchar *str)
{
- const gchar *msg;
-
- switch (code) {
- case MM_CONNECTION_ERROR_UNKNOWN:
- msg = "Unknown";
- break;
- case MM_CONNECTION_ERROR_NO_CARRIER:
- msg = "No carrier";
- break;
- case MM_CONNECTION_ERROR_NO_DIALTONE:
- msg = "No dialtone";
- break;
- case MM_CONNECTION_ERROR_BUSY:
- msg = "Busy";
- break;
- case MM_CONNECTION_ERROR_NO_ANSWER:
- msg = "No answer";
- break;
-
- default:
- g_debug ("Invalid connection error code: %u", code);
- /* uhm... make something up (yes, ok, lie!). */
- code = MM_CONNECTION_ERROR_NO_CARRIER;
- msg = "No carrier";
+ gchar *buf = NULL;
+ guint i;
+ guint j;
+
+ /* Normalize the error code by stripping whitespace and odd characters */
+ buf = g_strdup (str);
+ for (i = 0, j = 0; str[i]; i++) {
+ if (isalnum (str[i]))
+ buf[j++] = tolower (str[i]);
}
+ buf[j] = '\0';
- return g_error_new_literal (MM_CONNECTION_ERROR, code, msg);
+ return buf;
}
-/* --- Mobile equipment errors --- */
-
-static ErrorTable me_errors[] = {
- { MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE, "phonefailure", "Phone failure" },
- { MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION, "noconnectiontophone", "No connection to phone" },
- { MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED, "phoneadapterlinkreserved", "Phone-adaptor link reserved" },
- { MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED, "operationnotallowed", "Operation not allowed" },
- { MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "operationnotsupported", "Operation not supported" },
- { MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN, "phsimpinrequired", "PH-SIM PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN, "phfsimpinrequired", "PH-FSIM PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK, "phfsimpukrequired", "PH-FSIM PUK required" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, "simnotinserted", "SIM not inserted" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN, "simpinrequired", "SIM PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, "simpukrequired", "SIM PUK required" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE, "simfailure", "SIM failure" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY, "simbusy", "SIM busy" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "simwrong", "SIM wrong" },
- { MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, "incorrectpassword", "Incorrect password" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2, "simpin2required", "SIM PIN2 required" },
- { MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2, "simpuk2required", "SIM PUK2 required" },
- { MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL, "memoryfull", "Memory full" },
- { MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX, "invalidindex", "Invalid index" },
- { MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND, "notfound", "Not found" },
- { MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE, "memoryfailure", "Memory failure" },
- { MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG, "textstringtoolong", "Text string too long" },
- { MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS, "invalidcharactersintextstring", "Invalid characters in text string" },
- { MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG, "dialstringtoolong", "Dial string too long" },
- { MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID, "invalidcharactersindialstring", "Invalid characters in dial string" },
- { MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "nonetworkservice", "No network service" },
- { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "networktimeout", "Network timeout" },
- { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED, "networknotallowedemergencycallsonly", "Network not allowed - emergency calls only" },
- { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN, "networkpersonalizationpinrequired", "Network personalization PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK, "networkpersonalizationpukrequired", "Network personalization PUK required" },
- { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN, "networksubsetpersonalizationpinrequired", "Network subset personalization PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK, "networksubsetpersonalizationpukrequired", "Network subset personalization PUK required" },
- { MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN, "serviceproviderpersonalizationpinrequired", "Service provider personalization PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK, "serviceproviderpersonalizationpukrequired", "Service provider personalization PUK required" },
- { MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN, "corporatepersonalizationpinrequired", "Corporate personalization PIN required" },
- { MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK, "corporatepersonalizationpukrequired", "Corporate personalization PUK required" },
- { MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, "unknownerror", "Unknown error" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS, "illegalms", "Illegal MS" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME, "illegalme", "Illegal ME" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED, "gprsservicesnotallowed", "GPRS services not allowed" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED, "plmnnotallowed", "PLMN not allowed" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED, "locationareanotallowed", "Location area not allowed" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED, "roamingnotallowedinthislocationarea", "Roaming not allowed in this location area" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED, "serviceoperationnotsupported", "Service option not supported" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED, "requestedserviceoptionnotsubscribed", "Requested service option not subscribed" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER, "serviceoptiontemporarilyoutoforder", "Service option temporarily out of order" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN, "unspecifiedgprserror", "Unspecified GPRS error" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_PDP_AUTH_FAILURE, "pdpauthenticationfailure", "PDP authentication failure" },
- { MM_MOBILE_EQUIPMENT_ERROR_GPRS_INVALID_MOBILE_CLASS, "invalidmobileclass", "Invalid mobile class" },
+/******************************************************************************/
+/* Connection errors */
+
+/* Human friendly messages for each error type */
+static const gchar *connection_error_messages[] = {
+ [MM_CONNECTION_ERROR_UNKNOWN] = "Unknown",
+ [MM_CONNECTION_ERROR_NO_CARRIER] = "No carrier",
+ [MM_CONNECTION_ERROR_NO_DIALTONE] = "No dialtone",
+ [MM_CONNECTION_ERROR_BUSY] = "Busy",
+ [MM_CONNECTION_ERROR_NO_ANSWER] = "No answer",
};
GError *
-mm_mobile_equipment_error_for_code (MMMobileEquipmentError code)
+mm_connection_error_for_code (MMConnectionError code,
+ gpointer log_object)
{
- guint i;
-
- /* Look for the code */
- for (i = 0; i < G_N_ELEMENTS (me_errors); i++) {
- if (me_errors[i].code == code)
- return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
- code,
- me_errors[i].message);
+ if (code < G_N_ELEMENTS (connection_error_messages)) {
+ const gchar *error_message;
+
+ error_message = connection_error_messages[code];
+ if (error_message)
+ return g_error_new_literal (MM_CONNECTION_ERROR, code, error_message);
}
- /* Not found? Then, default */
- 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");
+ /* Not found? Then, default to 'no carrier' */
+ mm_obj_dbg (log_object, "unknown connection error: %u", code);
+ return g_error_new (MM_CONNECTION_ERROR,
+ MM_CONNECTION_ERROR_NO_CARRIER,
+ "Unknown connection error: %u", code);
}
-GError *
-mm_mobile_equipment_error_for_string (const gchar *str)
-{
- MMMobileEquipmentError code = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN;
- const gchar *msg = NULL;
- gchar *buf;
- guint i;
- guint j;
+/******************************************************************************/
+/* Mobile equipment errors */
- g_return_val_if_fail (str != NULL, NULL);
+/* Human friendly messages for each error type */
+static const gchar *me_error_messages[] = {
+ [MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE] = "Phone failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION] = "No connection to phone",
+ [MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED] = "Phone-adaptor link reserved",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED] = "Operation not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED] = "Operation not supported",
+ [MM_MOBILE_EQUIPMENT_ERROR_PH_SIM_PIN] = "PH-SIM PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PIN] = "PH-FSIM PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_PH_FSIM_PUK] = "PH-FSIM PUK required",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED] = "SIM not inserted",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN] = "SIM PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK] = "SIM PUK required",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE] = "SIM failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY] = "SIM busy",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG] = "SIM wrong",
+ [MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD] = "Incorrect password",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN2] = "SIM PIN2 required",
+ [MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK2] = "SIM PUK2 required",
+ [MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FULL] = "Memory full",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_INDEX] = "Invalid index",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND] = "Not found",
+ [MM_MOBILE_EQUIPMENT_ERROR_MEMORY_FAILURE] = "Memory failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_TEXT_TOO_LONG] = "Text string too long",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_CHARS] = "Invalid characters in text string",
+ [MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_TOO_LONG] = "Dial string too long",
+ [MM_MOBILE_EQUIPMENT_ERROR_DIAL_STRING_INVALID] = "Invalid characters in dial string",
+ [MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK] = "No network service",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT] = "Network timeout",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED] = "Network not allowed - emergency calls only",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PIN] = "Network personalization PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_PUK] = "Network personalization PUK required",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PIN] = "Network subset personalization PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_SUBSET_PUK] = "Network subset personalization PUK required",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PIN] = "Service provider personalization PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_PUK] = "Service provider personalization PUK required",
+ [MM_MOBILE_EQUIPMENT_ERROR_CORP_PIN] = "Corporate personalization PIN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_CORP_PUK] = "Corporate personalization PUK required",
+ [MM_MOBILE_EQUIPMENT_ERROR_HIDDEN_KEY_REQUIRED] = "Hidden key required",
+ [MM_MOBILE_EQUIPMENT_ERROR_EAP_METHOD_NOT_SUPPORTED] = "EAP method not supported",
+ [MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS] = "Incorrect parameters",
+ [MM_MOBILE_EQUIPMENT_ERROR_COMMAND_DISABLED] = "Command disabled",
+ [MM_MOBILE_EQUIPMENT_ERROR_COMMAND_ABORTED] = "Command aborted",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_ATTACHED_RESTRICTED] = "Not attached] restricted",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_EMERGENCY_ONLY] = "Not allowed] emergency only",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED_RESTRICTED] = "Not allowed] restricted",
+ [MM_MOBILE_EQUIPMENT_ERROR_FIXED_DIAL_NUMBER_ONLY] = "Fixed dial number only",
+ [MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_OUT_OF_SERVICE] = "Temporarily out of service",
+ [MM_MOBILE_EQUIPMENT_ERROR_LANGUAGE_OR_ALPHABET_NOT_SUPPORTED] = "Language or alphabet not supported",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNEXPECTED_DATA_VALUE] = "Unexpected data value",
+ [MM_MOBILE_EQUIPMENT_ERROR_SYSTEM_FAILURE] = "System failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_DATA_MISSING] = "Data missing",
+ [MM_MOBILE_EQUIPMENT_ERROR_CALL_BARRED] = "Call barred",
+ [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_WAITING_INDICATION_SUBSCRIPTION_FAILURE] = "Message waiting indication subscription failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN] = "Unknown error",
+ [MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS] = "IMSI unknown in HLR/HSS",
+ [MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE] = "Illegal MS/UE",
+ [MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR] = "IMSI unknown in VLR",
+ [MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED] = "IMEI not accepted",
+ [MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME] = "Illegal ME",
+ [MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED] = "PS services not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED] = "PS and non-PS services not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK] = "UE identity not derived from network",
+ [MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED] = "Implicitly detached",
+ [MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED] = "PLMN not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED] = "Location/tracking area not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA] = "Roaming not allowed in this location/tracking area",
+ [MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN] = "PS services not allowed in PLMN",
+ [MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA] = "No cells in location/tracking area",
+ [MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE] = "MSC temporarily not reachable",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH] = "Network failure (attach)",
+ [MM_MOBILE_EQUIPMENT_ERROR_CS_DOMAIN_UNAVAILABLE] = "CS domain unavailable",
+ [MM_MOBILE_EQUIPMENT_ERROR_ESM_FAILURE] = "ESM failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_CONGESTION] = "Congestion",
+ [MM_MOBILE_EQUIPMENT_ERROR_MBMS_BEARER_CAPABILITIES_INSUFFICIENT_FOR_SERVICE] = "MBMS bearer capabilities insufficient for service",
+ [MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG] = "Not authorized for CSG",
+ [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES] = "Insufficient resources",
+ [MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN] = "Missing or unknown APN",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE] = "Unknown PDP address or type",
+ [MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED] = "User authentication failed",
+ [MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW] = "Activation rejected by GGSN or GW",
+ [MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED] = "Activation rejected (unspecified)",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED] = "Service option not supported",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED] = "Requested service option not subscribed",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER] = "Service option temporarily out of order",
+ [MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE] = "NSAPI/PTI already in use",
+ [MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION] = "Regular deactivation",
+ [MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED] = "QoS not accepted",
+ [MM_MOBILE_EQUIPMENT_ERROR_CALL_CANNOT_BE_IDENTIFIED] = "Call cannot be identified",
+ [MM_MOBILE_EQUIPMENT_ERROR_CS_SERVICE_TEMPORARILY_UNAVAILABLE] = "CS service temporarily unavailable",
+ [MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED] = "Feature not supported",
+ [MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION] = "Semantic error in TFT operation",
+ [MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION] = "Syntactical error in TFT operation",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT] = "Unknown PDP context",
+ [MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER] = "Semantic error in packet filter",
+ [MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER] = "Syntactical error in packet filter",
+ [MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED] = "PDP context without TFT already activated",
+ [MM_MOBILE_EQUIPMENT_ERROR_MULTICAST_GROUP_MEMBERSHIP_TIMEOUT] = "Multicast group membership timeout",
+ [MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN] = "Unspecified GPRS error",
+ [MM_MOBILE_EQUIPMENT_ERROR_PDP_AUTH_FAILURE] = "PDP authentication failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_MOBILE_CLASS] = "Invalid mobile class",
+ [MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED_LEGACY] = "Last PDN disconnection not allowed (legacy)",
+ [MM_MOBILE_EQUIPMENT_ERROR_LAST_PDN_DISCONNECTION_NOT_ALLOWED] = "Last PDN disconnection not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE] = "Semantically incorrect message",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION] = "Invalid mandatory information",
+ [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED] = "Message type not implemented",
+ [MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR] = "Conditional IE error",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR] = "Unspecified protocol error",
+ [MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING] = "Operator determined barring",
+ [MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED] = "Maximum number of PDP/bearer contexts reached",
+ [MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED] = "Requested APN not supported",
+ [MM_MOBILE_EQUIPMENT_ERROR_REQUEST_REJECTED_BCM_VIOLATION] = "Rejected BCM violation",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_QCI_OR_5QI_VALUE] = "Unsupported QCI/5QI value",
+ [MM_MOBILE_EQUIPMENT_ERROR_USER_DATA_VIA_CONTROL_PLANE_CONGESTED] = "User data via control plane congested",
+ [MM_MOBILE_EQUIPMENT_ERROR_SMS_PROVIDED_VIA_GPRS_IN_ROUTING_AREA] = "SMS provided via GPRS in routing area",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_PTI_VALUE] = "Invalid PTI value",
+ [MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED] = "No bearer activated",
+ [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = "Message not compatible with protocol state",
+ [MM_MOBILE_EQUIPMENT_ERROR_RECOVERY_ON_TIMER_EXPIRY] = "Recovery on timer expiry",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE] = "Invalid transaction ID value",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_AUTHORIZED_IN_PLMN] = "Service option not authorized in PLMN",
+ [MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ACTIVATION] = "Network failure (activation)",
+ [MM_MOBILE_EQUIPMENT_ERROR_REACTIVATION_REQUESTED] = "Reactivation requested",
+ [MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED] = "IPv4 only allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED] = "IPv6 only allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED] = "Single address bearers only allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_COLLISION_WITH_NETWORK_INITIATED_REQUEST] = "Collision with network initiated request",
+ [MM_MOBILE_EQUIPMENT_ERROR_IPV4V6_ONLY_ALLOWED] = "IPv4v6 only allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_NON_IP_ONLY_ALLOWED] = "Non-IP only allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_BEARER_HANDLING_UNSUPPORTED] = "Bearer handling unsupported",
+ [MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE] = "APN restriction incompatible",
+ [MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED] = "Multiple access to PDN connection not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED] = "ESM information not received",
+ [MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT] = "PDN connection nonexistent",
+ [MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED] = "Multiple PDN connection to same APN not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_SEVERE_NETWORK_FAILURE] = "Severe network failure",
+ [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE_AND_DNN] = "Insufficient resources for slice and DNN",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNSUPPORTED_SSC_MODE] = "Unsupported SSC mode",
+ [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES_FOR_SLICE] = "Insufficient resources for slice",
+ [MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = "Message type not compatible with protocol state",
+ [MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED] = "IE not implemented",
+ [MM_MOBILE_EQUIPMENT_ERROR_N1_MODE_NOT_ALLOWED] = "N1 mode not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_RESTRICTED_SERVICE_AREA] = "Restricted service area",
+ [MM_MOBILE_EQUIPMENT_ERROR_LADN_UNAVAILABLE] = "LADN unavailable",
+ [MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_DNN_IN_SLICE] = "Missing or unknown DNN in slice",
+ [MM_MOBILE_EQUIPMENT_ERROR_NGKSI_ALREADY_IN_USE] = "ngKSI already in use",
+ [MM_MOBILE_EQUIPMENT_ERROR_PAYLOAD_NOT_FORWARDED] = "Payload not forwarded",
+ [MM_MOBILE_EQUIPMENT_ERROR_NON_3GPP_ACCESS_TO_5GCN_NOT_ALLOWED] = "Non-3GPP access to 5GCN not allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_SERVING_NETWORK_NOT_AUTHORIZED] = "Serving network not authorized",
+ [MM_MOBILE_EQUIPMENT_ERROR_DNN_NOT_SUPPORTED_IN_SLICE] = "DNN not supported in slice",
+ [MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_USER_PLANE_RESOURCES_FOR_PDU_SESSION] = "Insufficient user plane resources for PDU session",
+ [MM_MOBILE_EQUIPMENT_ERROR_OUT_OF_LADN_SERVICE_AREA] = "Out of LADN service area",
+ [MM_MOBILE_EQUIPMENT_ERROR_PTI_MISMATCH] = "PTI mismatch",
+ [MM_MOBILE_EQUIPMENT_ERROR_MAX_DATA_RATE_FOR_USER_PLANE_INTEGRITY_TOO_LOW] = "Max data rate for user plane integrity too low",
+ [MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_QOS_OPERATION] = "Semantic error in QoS operation",
+ [MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_QOS_OPERATION] = "Syntactical error in QoS operation",
+ [MM_MOBILE_EQUIPMENT_ERROR_INVALID_MAPPED_EPS_BEARER_IDENTITY] = "Invalid mapped EPS bearer identity",
+ [MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_5GCN_REQUIRED] = "Redirection to 5GCN required",
+ [MM_MOBILE_EQUIPMENT_ERROR_REDIRECTION_TO_EPC_REQUIRED] = "Redirection to EPC required",
+ [MM_MOBILE_EQUIPMENT_ERROR_TEMPORARILY_UNAUTHORIZED_FOR_SNPN] = "Temporarily unauthorized for SNPN",
+ [MM_MOBILE_EQUIPMENT_ERROR_PERMANENTLY_UNAUTHORIZED_FOR_SNPN] = "Permanently unauthorized for SNPN",
+ [MM_MOBILE_EQUIPMENT_ERROR_ETHERNET_ONLY_ALLOWED] = "Ethernet only allowed",
+ [MM_MOBILE_EQUIPMENT_ERROR_UNAUTHORIZED_FOR_CAG] = "Unauthorized for CAG",
+ [MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK_SLICES_AVAILABLE] = "No network slices available",
+ [MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED] = "Wireline access area not allowed",
+};
- /* Normalize the error code by stripping whitespace and odd characters */
- buf = g_strdup (str);
- for (i = 0, j = 0; str[i]; i++) {
- if (isalnum (str[i]))
- buf[j++] = tolower (str[i]);
- }
- buf[j] = '\0';
+/* All generic ME errors should be < 255, as those are the only reserved ones in the 3GPP spec */
+G_STATIC_ASSERT (G_N_ELEMENTS (me_error_messages) <= 256);
- /* Look for the string */
- for (i = 0; i < G_N_ELEMENTS (me_errors); i++) {
- if (g_str_equal (me_errors[i].error, buf)) {
- code = me_errors[i].code;
- msg = me_errors[i].message;
- break;
- }
- }
+GError *
+mm_mobile_equipment_error_for_code (MMMobileEquipmentError code,
+ gpointer log_object)
+{
+ if (code < G_N_ELEMENTS (me_error_messages)) {
+ const gchar *error_message;
- /* Not found? Then, default */
- if (!msg) {
- g_debug ("Invalid mobile equipment error string: '%s' (%s)",
- str, buf);
- code = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN;
- msg = "Unknown error";
+ error_message = me_error_messages[code];
+ if (error_message)
+ return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, code, error_message);
}
- g_free (buf);
- return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR, code, msg);
+ /* Not found? Then, default */
+ mm_obj_dbg (log_object, "unknown mobile equipment error: %u", code);
+ return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Unknown mobile equipment error: %u", code);
}
-/* --- Message errors --- */
-
-static ErrorTable msg_errors[] = {
- { MM_MESSAGE_ERROR_ME_FAILURE, "mefailure", "ME failure" },
- { MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED, "smsservicereserved", "SMS service reserved" },
- { MM_MESSAGE_ERROR_NOT_ALLOWED, "operationnotallowed", "Operation not allowed" },
- { MM_MESSAGE_ERROR_NOT_SUPPORTED, "operationnotsupported", "Operation not supported" },
- { MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "invalidpduparameter", "Invalid PDU mode parameter" },
- { MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER, "invalidtextparameter", "Invalid text mode parameter" },
- { MM_MESSAGE_ERROR_SIM_NOT_INSERTED, "simnotinserted", "SIM not inserted" },
- { MM_MESSAGE_ERROR_SIM_PIN, "simpinrequired", "SIM PIN required" },
- { MM_MESSAGE_ERROR_PH_SIM_PIN, "phsimpinrequired", "PH-SIM PIN required" },
- { MM_MESSAGE_ERROR_SIM_FAILURE, "simfailure", "SIM failure" },
- { MM_MESSAGE_ERROR_SIM_BUSY, "simbusy", "SIM busy" },
- { MM_MESSAGE_ERROR_SIM_WRONG, "simwrong", "SIM wrong" },
- { MM_MESSAGE_ERROR_SIM_PUK, "simpukrequired", "SIM PUK required" },
- { MM_MESSAGE_ERROR_SIM_PIN2, "simpin2required", "SIM PIN2 required" },
- { MM_MESSAGE_ERROR_SIM_PUK2, "simpuk2required", "SIM PUK2 required" },
- { MM_MESSAGE_ERROR_MEMORY_FAILURE, "memoryfailure", "Memory failure" },
- { MM_MESSAGE_ERROR_INVALID_INDEX, "invalidindex", "Invalid index" },
- { MM_MESSAGE_ERROR_MEMORY_FULL, "memoryfull", "Memory full" },
- { MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN, "smscaddressunknown", "SMSC address unknown" },
- { MM_MESSAGE_ERROR_NO_NETWORK, "nonetwork", "No network" },
- { MM_MESSAGE_ERROR_NETWORK_TIMEOUT, "networktimeout", "Network timeout" },
- { MM_MESSAGE_ERROR_NO_CNMA_ACK_EXPECTED, "nocnmaackexpected", "No CNMA acknowledgement expected" },
- { MM_MESSAGE_ERROR_UNKNOWN, "unknown", "Unknown" }
+/******************************************************************************/
+/* Message errors
+ *
+ * The message errors are all >300, so we define a common offset for all error
+ * types.
+ */
+
+#define MM_MESSAGE_ERROR_COMMON_OFFSET 300
+
+/* Human friendly messages for each error type */
+static const gchar *msg_error_messages[] = {
+ [MM_MESSAGE_ERROR_ME_FAILURE - MM_MESSAGE_ERROR_COMMON_OFFSET] = "ME failure",
+ [MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SMS service reserved",
+ [MM_MESSAGE_ERROR_NOT_ALLOWED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Operation not allowed",
+ [MM_MESSAGE_ERROR_NOT_SUPPORTED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Operation not supported",
+ [MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Invalid PDU mode parameter",
+ [MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Invalid text mode parameter",
+ [MM_MESSAGE_ERROR_SIM_NOT_INSERTED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM not inserted",
+ [MM_MESSAGE_ERROR_SIM_PIN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PIN required",
+ [MM_MESSAGE_ERROR_PH_SIM_PIN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "PH-SIM PIN required",
+ [MM_MESSAGE_ERROR_SIM_FAILURE - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM failure",
+ [MM_MESSAGE_ERROR_SIM_BUSY - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM busy",
+ [MM_MESSAGE_ERROR_SIM_WRONG - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM wrong",
+ [MM_MESSAGE_ERROR_SIM_PUK - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PUK required",
+ [MM_MESSAGE_ERROR_SIM_PIN2 - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PIN2 required",
+ [MM_MESSAGE_ERROR_SIM_PUK2 - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SIM PUK2 required",
+ [MM_MESSAGE_ERROR_MEMORY_FAILURE - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Memory failure",
+ [MM_MESSAGE_ERROR_INVALID_INDEX - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Invalid index",
+ [MM_MESSAGE_ERROR_MEMORY_FULL - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Memory full",
+ [MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "SMSC address unknown",
+ [MM_MESSAGE_ERROR_NO_NETWORK - MM_MESSAGE_ERROR_COMMON_OFFSET] = "No network",
+ [MM_MESSAGE_ERROR_NETWORK_TIMEOUT - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Network timeout",
+ [MM_MESSAGE_ERROR_NO_CNMA_ACK_EXPECTED - MM_MESSAGE_ERROR_COMMON_OFFSET] = "No CNMA acknowledgement expected",
+ [MM_MESSAGE_ERROR_UNKNOWN - MM_MESSAGE_ERROR_COMMON_OFFSET] = "Unknown",
};
+/* All generic message errors should be <= 500 (500-common=200), as those are the only reserved ones in the 3GPP spec */
+G_STATIC_ASSERT (G_N_ELEMENTS (msg_error_messages) <= 201);
+
GError *
-mm_message_error_for_code (MMMessageError code)
+mm_message_error_for_code (MMMessageError code,
+ gpointer log_object)
{
- guint i;
-
- /* Look for the code */
- for (i = 0; i < G_N_ELEMENTS (msg_errors); i++) {
- if (msg_errors[i].code == code)
- return g_error_new_literal (MM_MESSAGE_ERROR,
- code,
- msg_errors[i].message);
+ if ((code >= MM_MESSAGE_ERROR_COMMON_OFFSET) && ((code - MM_MESSAGE_ERROR_COMMON_OFFSET) < G_N_ELEMENTS (msg_error_messages))) {
+ const gchar *error_message;
+
+ error_message = msg_error_messages[code - MM_MESSAGE_ERROR_COMMON_OFFSET];
+ if (error_message)
+ return g_error_new_literal (MM_MESSAGE_ERROR, code, error_message);
}
/* Not found? Then, default */
- g_debug ("Invalid message error code: %u", (guint)code);
+ mm_obj_dbg (log_object, "unknown message error: %u", code);
return g_error_new (MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_UNKNOWN,
- "Unknown error");
+ "Unknown message error: %u", code);
}
+/******************************************************************************/
+/* Mobile equipment and message errors as string
+ *
+ * The strings given here must be all lowercase, and stripped of special chars
+ * and whitespaces.
+ *
+ * Not all errors are included, only the most generic ones.
+ */
+
+typedef struct {
+ guint error_code;
+ const gchar *error_string;
+} MeErrorString;
+
+static const MeErrorString me_error_strings[] = {
+ { MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE, "phonefailure" },
+ { MM_MOBILE_EQUIPMENT_ERROR_NO_CONNECTION, "noconnection" },
+ { MM_MOBILE_EQUIPMENT_ERROR_LINK_RESERVED, "linkreserved" },
+ { MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED, "operationnotallowed" },
+ { MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED, "operationnotsupported" },
+ { MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED, "simnotinserted" },
+ { MM_MOBILE_EQUIPMENT_ERROR_SIM_PIN, "simpinrequired" },
+ { MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, "simpukrequired" },
+ { MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE, "simfailure" },
+ { MM_MOBILE_EQUIPMENT_ERROR_SIM_BUSY, "simbusy" },
+ { MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, "simwrong" },
+ { MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, "incorrectpassword" },
+ { MM_MOBILE_EQUIPMENT_ERROR_NOT_FOUND, "notfound" },
+ { MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK, "nonetwork" },
+ { MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, "timeout" },
+ { MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PARAMETERS, "incorrectparameters" },
+};
+
GError *
-mm_message_error_for_string (const gchar *str)
+mm_mobile_equipment_error_for_string (const gchar *str,
+ gpointer log_object)
{
- MMMessageError code = MM_MESSAGE_ERROR_UNKNOWN;
- const gchar *msg = NULL;
- gchar *buf;
- guint i;
- guint j;
-
- g_return_val_if_fail (str != NULL, NULL);
+ g_autofree gchar *buf = NULL;
+ guint i;
- /* Normalize the error code by stripping whitespace and odd characters */
- buf = g_strdup (str);
- for (i = 0, j = 0; str[i]; i++) {
- if (isalnum (str[i]))
- buf[j++] = tolower (str[i]);
- }
- buf[j] = '\0';
+ g_assert (str != NULL);
/* Look for the string */
- for (i = 0; i < G_N_ELEMENTS (msg_errors); i++) {
- if (g_str_equal (msg_errors[i].error, buf)) {
- code = msg_errors[i].code;
- msg = msg_errors[i].message;
- break;
- }
+ buf = normalize_error_string (str);
+ for (i = 0; i < G_N_ELEMENTS (me_error_strings); i++) {
+ if (g_str_equal (me_error_strings[i].error_string, buf))
+ return mm_mobile_equipment_error_for_code ((MMMobileEquipmentError)me_error_strings[i].error_code, log_object);
}
- /* Not found? Then, default */
- if (!msg) {
- g_debug ("Invalid message error string: '%s' (%s)",
- str, buf);
- code = MM_MESSAGE_ERROR_UNKNOWN;
- msg = "Unknown error";
+ /* Not found? then, default */
+ mm_obj_dbg (log_object, "unknown mobile equipment error string: '%s' (%s)", str, buf);
+ return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Unknown mobile equipment error string: %s", str);
+}
+
+static const MeErrorString msg_error_strings[] = {
+ { MM_MESSAGE_ERROR_ME_FAILURE, "mefailure" },
+ { MM_MESSAGE_ERROR_SMS_SERVICE_RESERVED, "smsservicereserved" },
+ { MM_MESSAGE_ERROR_NOT_ALLOWED, "operationnotallowed" },
+ { MM_MESSAGE_ERROR_NOT_SUPPORTED, "operationnotsupported" },
+ { MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER, "invalidpduparameter" },
+ { MM_MESSAGE_ERROR_INVALID_TEXT_PARAMETER, "invalidtextparameter" },
+ { MM_MESSAGE_ERROR_SIM_NOT_INSERTED, "simnotinserted" },
+ { MM_MESSAGE_ERROR_SIM_PIN, "simpinrequired" },
+ { MM_MESSAGE_ERROR_SIM_FAILURE, "simfailure" },
+ { MM_MESSAGE_ERROR_SIM_BUSY, "simbusy" },
+ { MM_MESSAGE_ERROR_SIM_WRONG, "simwrong" },
+ { MM_MESSAGE_ERROR_SIM_PUK, "simpukrequired" },
+ { MM_MESSAGE_ERROR_MEMORY_FAILURE, "memoryfailure" },
+ { MM_MESSAGE_ERROR_INVALID_INDEX, "invalidindex" },
+ { MM_MESSAGE_ERROR_MEMORY_FULL, "memoryfull" },
+ { MM_MESSAGE_ERROR_SMSC_ADDRESS_UNKNOWN, "smscaddressunknown" },
+ { MM_MESSAGE_ERROR_NO_NETWORK, "nonetwork" },
+ { MM_MESSAGE_ERROR_NETWORK_TIMEOUT, "networktimeout" },
+};
+
+GError *
+mm_message_error_for_string (const gchar *str,
+ gpointer log_object)
+{
+ g_autofree gchar *buf = NULL;
+ guint i;
+
+ g_assert (str != NULL);
+
+ /* Look for the string */
+ buf = normalize_error_string (str);
+ for (i = 0; i < G_N_ELEMENTS (msg_error_strings); i++) {
+ if (g_str_equal (msg_error_strings[i].error_string, buf))
+ return mm_message_error_for_code ((MMMessageError)msg_error_strings[i].error_code, log_object);
}
- g_free (buf);
- return g_error_new_literal (MM_MESSAGE_ERROR, code, msg);
+ /* Not found? then, default */
+ mm_obj_dbg (log_object, "unknown message error string: '%s' (%s)", str, buf);
+ return g_error_new (MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_UNKNOWN,
+ "Unknown message error string: %s", str);
}
diff --git a/src/mm-error-helpers.h b/src/mm-error-helpers.h
index 379afb24..e99d1662 100644
--- a/src/mm-error-helpers.h
+++ b/src/mm-error-helpers.h
@@ -23,10 +23,10 @@
#include <ModemManager.h>
#include <libmm-glib.h>
-GError *mm_connection_error_for_code (MMConnectionError code);
-GError *mm_mobile_equipment_error_for_code (MMMobileEquipmentError code);
-GError *mm_mobile_equipment_error_for_string (const gchar *str);
-GError *mm_message_error_for_code (MMMessageError code);
-GError *mm_message_error_for_string (const gchar *str);
+GError *mm_connection_error_for_code (MMConnectionError code, gpointer log_object);
+GError *mm_mobile_equipment_error_for_code (MMMobileEquipmentError code, gpointer log_object);
+GError *mm_mobile_equipment_error_for_string (const gchar *str, gpointer log_object);
+GError *mm_message_error_for_code (MMMessageError code, gpointer log_object);
+GError *mm_message_error_for_string (const gchar *str, gpointer log_object);
#endif /* MM_ERROR_HELPERS_H */
diff --git a/src/mm-filter.c b/src/mm-filter.c
new file mode 100644
index 00000000..b7a9d2f8
--- /dev/null
+++ b/src/mm-filter.c
@@ -0,0 +1,550 @@
+/* -*- 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) 2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include <ModemManager.h>
+#include <ModemManager-tags.h>
+
+#include "mm-daemon-enums-types.h"
+#include "mm-filter.h"
+#include "mm-log-object.h"
+
+#define FILTER_PORT_MAYBE_FORBIDDEN "maybe-forbidden"
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMFilter, mm_filter, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+enum {
+ PROP_0,
+ PROP_ENABLED_RULES,
+ LAST_PROP
+};
+
+struct _MMFilterPrivate {
+ MMFilterRule enabled_rules;
+ GList *plugin_whitelist_tags;
+ GArray *plugin_whitelist_vendor_ids;
+ GArray *plugin_whitelist_product_ids;
+};
+
+/*****************************************************************************/
+
+void
+mm_filter_register_plugin_whitelist_tag (MMFilter *self,
+ const gchar *tag)
+{
+ if (!g_list_find_custom (self->priv->plugin_whitelist_tags, tag, (GCompareFunc) g_strcmp0)) {
+ mm_obj_dbg (self, "registered plugin whitelist tag: %s", tag);
+ self->priv->plugin_whitelist_tags = g_list_prepend (self->priv->plugin_whitelist_tags, g_strdup (tag));
+ }
+}
+
+void
+mm_filter_register_plugin_whitelist_vendor_id (MMFilter *self,
+ guint16 vid)
+{
+ guint i;
+
+ if (!self->priv->plugin_whitelist_vendor_ids)
+ self->priv->plugin_whitelist_vendor_ids = g_array_sized_new (FALSE, FALSE, sizeof (guint16), 64);
+
+ for (i = 0; i < self->priv->plugin_whitelist_vendor_ids->len; i++) {
+ guint16 item;
+
+ item = g_array_index (self->priv->plugin_whitelist_vendor_ids, guint16, i);
+ if (item == vid)
+ return;
+ }
+
+ g_array_append_val (self->priv->plugin_whitelist_vendor_ids, vid);
+ mm_obj_dbg (self, "registered plugin whitelist vendor id: %04x", vid);
+}
+
+void
+mm_filter_register_plugin_whitelist_product_id (MMFilter *self,
+ guint16 vid,
+ guint16 pid)
+{
+ mm_uint16_pair new_item;
+ guint i;
+
+ if (!self->priv->plugin_whitelist_product_ids)
+ self->priv->plugin_whitelist_product_ids = g_array_sized_new (FALSE, FALSE, sizeof (mm_uint16_pair), 10);
+
+ for (i = 0; i < self->priv->plugin_whitelist_product_ids->len; i++) {
+ mm_uint16_pair *item;
+
+ item = &g_array_index (self->priv->plugin_whitelist_product_ids, mm_uint16_pair, i);
+ if (item->l == vid && item->r == pid)
+ return;
+ }
+
+ new_item.l = vid;
+ new_item.r = pid;
+ g_array_append_val (self->priv->plugin_whitelist_product_ids, new_item);
+ mm_obj_dbg (self, "registered plugin whitelist product id: %04x:%04x", vid, pid);
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_filter_port (MMFilter *self,
+ MMKernelDevice *port,
+ gboolean manual_scan)
+{
+ const gchar *subsystem;
+ const gchar *name;
+
+ subsystem = mm_kernel_device_get_subsystem (port);
+ name = mm_kernel_device_get_name (port);
+
+ /* If the device is explicitly whitelisted, we process every port. Also
+ * allow specifying this flag per-port instead of for the full device, e.g.
+ * for platform tty ports where there's only one port anyway. */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_EXPLICIT_WHITELIST) &&
+ (mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_PROCESS) ||
+ mm_kernel_device_get_property_as_boolean (port, ID_MM_DEVICE_PROCESS))) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: device is whitelisted", subsystem, name);
+ return TRUE;
+ }
+
+ /* If the device is explicitly blacklisted, we ignore every port. */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_EXPLICIT_BLACKLIST) &&
+ (mm_kernel_device_get_global_property_as_boolean (port, ID_MM_DEVICE_IGNORE))) {
+ mm_obj_dbg (self, "(%s/%s): port filtered: device is blacklisted", subsystem, name);
+ return FALSE;
+ }
+
+ /* If the device is whitelisted by a plugin, we allow it. */
+ if (self->priv->enabled_rules & MM_FILTER_RULE_PLUGIN_WHITELIST) {
+ GList *l;
+ guint16 vid = 0;
+ guint16 pid = 0;
+
+ for (l = self->priv->plugin_whitelist_tags; l; l = g_list_next (l)) {
+ if (mm_kernel_device_get_global_property_as_boolean (port, (const gchar *)(l->data)) ||
+ mm_kernel_device_get_property_as_boolean (port, (const gchar *)(l->data))) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: device is whitelisted by plugin (tag)", subsystem, name);
+ return TRUE;
+ }
+ }
+
+ vid = mm_kernel_device_get_physdev_vid (port);
+ if (vid)
+ pid = mm_kernel_device_get_physdev_pid (port);
+
+ if (vid && pid && self->priv->plugin_whitelist_product_ids) {
+ guint i;
+
+ for (i = 0; i < self->priv->plugin_whitelist_product_ids->len; i++) {
+ mm_uint16_pair *item;
+
+ item = &g_array_index (self->priv->plugin_whitelist_product_ids, mm_uint16_pair, i);
+ if (item->l == vid && item->r == pid) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: device is whitelisted by plugin (vid/pid)", subsystem, name);
+ return TRUE;
+ }
+ }
+ }
+
+ if (vid && self->priv->plugin_whitelist_vendor_ids) {
+ guint i;
+
+ for (i = 0; i < self->priv->plugin_whitelist_vendor_ids->len; i++) {
+ guint16 item;
+
+ item = g_array_index (self->priv->plugin_whitelist_vendor_ids, guint16, i);
+ if (item == vid) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: device is whitelisted by plugin (vid)", subsystem, name);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ /* If this is a QRTR device, we always allow it. This check comes before
+ * checking for VIRTUAL since qrtr devices don't have a sysfs path, and the
+ * check for VIRTUAL will return FALSE. */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_QRTR) &&
+ g_str_equal (subsystem, "qrtr")) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: qrtr device", subsystem, name);
+ return TRUE;
+ }
+
+ /* If this is a virtual device, don't allow it */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_VIRTUAL) &&
+ (!mm_kernel_device_get_physdev_sysfs_path (port))) {
+ mm_obj_dbg (self, "(%s/%s) port filtered: virtual device", subsystem, name);
+ return FALSE;
+ }
+
+ /* If this is a net device, we always allow it */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_NET) &&
+ (g_strcmp0 (subsystem, "net") == 0)) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: net device", subsystem, name);
+ return TRUE;
+ }
+
+ /* If this is a cdc-wdm device, we always allow it */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_USBMISC) &&
+ (g_strcmp0 (subsystem, "usbmisc") == 0)) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: usbmisc device", subsystem, name);
+ return TRUE;
+ }
+
+ /* If this is a rpmsg channel device, we always allow it */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_RPMSG) &&
+ (g_strcmp0 (subsystem, "rpmsg") == 0)) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: rpmsg device", subsystem, name);
+ return TRUE;
+ }
+
+ /* If this is a wwan port/device, we always allow it */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_WWAN) &&
+ (g_strcmp0 (subsystem, "wwan") == 0)) {
+ mm_obj_dbg (self, "(%s/%s) port allowed: wwan device", subsystem, name);
+ return TRUE;
+ }
+
+ /* If this is a tty device, we may allow it */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY) &&
+ (g_strcmp0 (subsystem, "tty") == 0)) {
+ const gchar *physdev_subsystem;
+ const gchar *driver;
+
+ /* Mixed blacklist/whitelist rules */
+
+ /* If the physdev is a 'platform' or 'pnp' device that's not whitelisted, ignore it */
+ physdev_subsystem = mm_kernel_device_get_physdev_subsystem (port);
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_PLATFORM_DRIVER) &&
+ (!g_strcmp0 (physdev_subsystem, "platform") ||
+ !g_strcmp0 (physdev_subsystem, "pci") ||
+ !g_strcmp0 (physdev_subsystem, "pnp") ||
+ !g_strcmp0 (physdev_subsystem, "sdio"))) {
+ mm_obj_dbg (self, "(%s/%s): port filtered: tty platform driver", subsystem, name);
+ return FALSE;
+ }
+
+ /* Whitelist rules last */
+
+ /* If the TTY kernel driver is one expected modem kernel driver, allow it */
+ driver = mm_kernel_device_get_driver (port);
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_DRIVER) &&
+ (!g_strcmp0 (driver, "option1") ||
+ !g_strcmp0 (driver, "qcserial") ||
+ !g_strcmp0 (driver, "qcaux") ||
+ !g_strcmp0 (driver, "nozomi") ||
+ !g_strcmp0 (driver, "sierra"))) {
+ mm_obj_dbg (self, "(%s/%s): port allowed: modem-specific kernel driver detected", subsystem, name);
+ return TRUE;
+ }
+
+ /*
+ * If the TTY kernel driver is cdc-acm and the interface is not
+ * class=2/subclass=2/protocol=[1-6], forbidden.
+ *
+ * Otherwise, we'll require the modem to have more ports other
+ * than the ttyACM one (see mm_filter_device_and_port()), because
+ * there are lots of Arduino devices out there exposing a single
+ * ttyACM port and wrongly claiming AT protocol support...
+ *
+ * Class definitions for Communication Devices 1.2
+ * Communications Interface Class Control Protocol Codes:
+ * 00h | USB specification | No class specific protocol required
+ * 01h | ITU-T V.250 | AT Commands: V.250 etc
+ * 02h | PCCA-101 | AT Commands defined by PCCA-101
+ * 03h | PCCA-101 | AT Commands defined by PCCA-101 & Annex O
+ * 04h | GSM 7.07 | AT Commands defined by GSM 07.07
+ * 05h | 3GPP 27.07 | AT Commands defined by 3GPP 27.007
+ * 06h | C-S0017-0 | AT Commands defined by TIA for CDMA
+ * 07h | USB EEM | Ethernet Emulation Model
+ * 08h-FDh | | RESERVED (future use)
+ * FEh | | External Protocol: Commands defined by Command Set Functional Descriptor
+ * FFh | USB Specification | Vendor-specific
+ */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_ACM_INTERFACE) &&
+ (!g_strcmp0 (driver, "cdc_acm")) &&
+ ((mm_kernel_device_get_interface_class (port) != 2) ||
+ (mm_kernel_device_get_interface_subclass (port) != 2) ||
+ (mm_kernel_device_get_interface_protocol (port) < 1) ||
+ (mm_kernel_device_get_interface_protocol (port) > 6))) {
+ mm_obj_dbg (self, "(%s/%s): port filtered: cdc-acm interface is not AT-capable", subsystem, name);
+ return FALSE;
+ }
+
+ /* Default forbidden? flag the port as maybe-forbidden, and go on */
+ if (self->priv->enabled_rules & MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN) {
+ g_object_set_data (G_OBJECT (port), FILTER_PORT_MAYBE_FORBIDDEN, GUINT_TO_POINTER (TRUE));
+ return TRUE;
+ }
+
+ g_assert_not_reached ();
+ }
+
+ /* Otherwise forbidden */
+ mm_obj_dbg (self, "(%s/%s) port filtered: forbidden port type", subsystem, name);
+ return FALSE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+device_has_net_port (MMDevice *device)
+{
+ GList *l;
+
+ for (l = mm_device_peek_port_probe_list (device); l; l = g_list_next (l)) {
+ if (!g_strcmp0 (mm_port_probe_get_port_subsys (MM_PORT_PROBE (l->data)), "net"))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+device_has_multiple_ports (MMDevice *device)
+{
+ return (g_list_length (mm_device_peek_port_probe_list (device)) > 1);
+}
+
+gboolean
+mm_filter_device_and_port (MMFilter *self,
+ MMDevice *device,
+ MMKernelDevice *port)
+{
+ const gchar *subsystem;
+ const gchar *name;
+ const gchar *driver;
+
+ /* If it wasn't flagged as maybe forbidden, there's nothing to do */
+ if (!GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (port), FILTER_PORT_MAYBE_FORBIDDEN)))
+ return TRUE;
+
+ subsystem = mm_kernel_device_get_subsystem (port);
+ name = mm_kernel_device_get_name (port);
+
+ /* Check whether this device holds a NET port in addition to this TTY */
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_WITH_NET) &&
+ device_has_net_port (device)) {
+ mm_obj_dbg (self, "(%s/%s): port allowed: device also exports a net interface", subsystem, name);
+ return TRUE;
+ }
+
+ /* Check whether this device holds any other port in addition to the ttyACM port */
+ driver = mm_kernel_device_get_driver (port);
+ if ((self->priv->enabled_rules & MM_FILTER_RULE_TTY_ACM_INTERFACE) &&
+ (!g_strcmp0 (driver, "cdc_acm")) &&
+ device_has_multiple_ports (device)) {
+ mm_obj_dbg (self, "(%s/%s): port allowed: device exports multiple interfaces", subsystem, name);
+ return TRUE;
+ }
+
+ mm_obj_dbg (self, "(%s/%s) port filtered: forbidden", subsystem, name);
+ return FALSE;
+}
+
+/*****************************************************************************/
+/* Use filter rule names as environment variables to control them on startup:
+ * - MM_FILTER_RULE_XXX=1 to explicitly enable the rule.
+ * - MM_FILTER_RULE_XXX=0 to explicitly disable the rule.
+ */
+
+static MMFilterRule
+filter_rule_env_process (MMFilterRule enabled_rules)
+{
+ MMFilterRule updated_rules = enabled_rules;
+ GFlagsClass *flags_class;
+ guint i;
+
+ flags_class = g_type_class_ref (MM_TYPE_FILTER_RULE);
+
+ for (i = 0; (1 << i) & MM_FILTER_RULE_ALL; i++) {
+ GFlagsValue *flags_value;
+ const gchar *env_value;
+
+ flags_value = g_flags_get_first_value (flags_class, (1 << i));
+ g_assert (flags_value);
+
+ env_value = g_getenv (flags_value->value_name);
+ if (!env_value)
+ continue;
+
+ if (g_str_equal (env_value, "0"))
+ updated_rules &= ~(1 << i);
+ else if (g_str_equal (env_value, "1"))
+ updated_rules |= (1 << i);
+ }
+
+ g_type_class_unref (flags_class);
+
+ return updated_rules;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_filter_check_rule_enabled (MMFilter *self,
+ MMFilterRule rule)
+{
+ return !!(self->priv->enabled_rules & rule);
+}
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("filter");
+}
+
+/*****************************************************************************/
+
+/* If TTY rule enabled, DEFAULT_FORBIDDEN must be set. */
+#define VALIDATE_RULE_TTY(rules) (!(rules & MM_FILTER_RULE_TTY) || (rules & (MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN)))
+
+MMFilter *
+mm_filter_new (MMFilterRule enabled_rules,
+ GError **error)
+{
+ MMFilter *self;
+ MMFilterRule updated_rules;
+
+ /* The input enabled rules are coming from predefined filter profiles. */
+ g_assert (VALIDATE_RULE_TTY (enabled_rules));
+ updated_rules = filter_rule_env_process (enabled_rules);
+ if (!VALIDATE_RULE_TTY (updated_rules)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid rules after processing envvars");
+ return NULL;
+ }
+
+ self = g_object_new (MM_TYPE_FILTER,
+ MM_FILTER_ENABLED_RULES, updated_rules,
+ NULL);
+
+#define RULE_ENABLED_STR(flag) ((self->priv->enabled_rules & flag) ? "yes" : "no")
+
+ mm_obj_dbg (self, "created");
+ mm_obj_dbg (self, " explicit whitelist: %s", RULE_ENABLED_STR (MM_FILTER_RULE_EXPLICIT_WHITELIST));
+ mm_obj_dbg (self, " explicit blacklist: %s", RULE_ENABLED_STR (MM_FILTER_RULE_EXPLICIT_BLACKLIST));
+ mm_obj_dbg (self, " plugin whitelist: %s", RULE_ENABLED_STR (MM_FILTER_RULE_PLUGIN_WHITELIST));
+ mm_obj_dbg (self, " qrtr devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_QRTR));
+ mm_obj_dbg (self, " virtual devices forbidden: %s", RULE_ENABLED_STR (MM_FILTER_RULE_VIRTUAL));
+ mm_obj_dbg (self, " net devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_NET));
+ mm_obj_dbg (self, " usbmisc devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_USBMISC));
+ mm_obj_dbg (self, " rpmsg devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_RPMSG));
+ mm_obj_dbg (self, " wwan devices allowed: %s", RULE_ENABLED_STR (MM_FILTER_RULE_WWAN));
+ if (self->priv->enabled_rules & MM_FILTER_RULE_TTY) {
+ mm_obj_dbg (self, " tty devices:");
+ mm_obj_dbg (self, " platform driver check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_PLATFORM_DRIVER));
+ mm_obj_dbg (self, " driver check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_DRIVER));
+ mm_obj_dbg (self, " cdc-acm interface check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_ACM_INTERFACE));
+ mm_obj_dbg (self, " with net check: %s", RULE_ENABLED_STR (MM_FILTER_RULE_TTY_WITH_NET));
+ if (self->priv->enabled_rules & MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN)
+ mm_obj_dbg (self, " default: forbidden");
+ else
+ g_assert_not_reached ();
+ } else
+ mm_obj_dbg (self, " tty devices: no");
+
+#undef RULE_ENABLED_STR
+
+ return self;
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMFilter *self = MM_FILTER (object);
+
+ switch (prop_id) {
+ case PROP_ENABLED_RULES:
+ self->priv->enabled_rules = g_value_get_flags (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMFilter *self = MM_FILTER (object);
+
+ switch (prop_id) {
+ case PROP_ENABLED_RULES:
+ g_value_set_flags (value, self->priv->enabled_rules);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+mm_filter_init (MMFilter *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_FILTER, MMFilterPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMFilter *self = MM_FILTER (object);
+
+ g_clear_pointer (&self->priv->plugin_whitelist_vendor_ids, g_array_unref);
+ g_clear_pointer (&self->priv->plugin_whitelist_product_ids, g_array_unref);
+ g_list_free_full (self->priv->plugin_whitelist_tags, g_free);
+
+ G_OBJECT_CLASS (mm_filter_parent_class)->finalize (object);
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
+mm_filter_class_init (MMFilterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMFilterPrivate));
+
+ /* Virtual methods */
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+
+ g_object_class_install_property (
+ object_class, PROP_ENABLED_RULES,
+ g_param_spec_flags (MM_FILTER_ENABLED_RULES,
+ "Enabled rules",
+ "Mask of rules enabled in the filter",
+ MM_TYPE_FILTER_RULE,
+ MM_FILTER_RULE_NONE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/src/mm-filter.h b/src/mm-filter.h
new file mode 100644
index 00000000..6c38568d
--- /dev/null
+++ b/src/mm-filter.h
@@ -0,0 +1,115 @@
+/* -*- 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) 2017 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_FILTER_H
+#define MM_FILTER_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "mm-device.h"
+#include "mm-kernel-device.h"
+
+#define MM_TYPE_FILTER (mm_filter_get_type ())
+#define MM_FILTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_FILTER, MMFilter))
+#define MM_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_FILTER, MMFilterClass))
+#define MM_IS_FILTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_FILTER))
+#define MM_IS_FILTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MM_TYPE_FILTER))
+#define MM_FILTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_FILTER, MMFilterClass))
+
+#define MM_FILTER_ENABLED_RULES "enabled-rules" /* construct-only */
+
+typedef struct _MMFilterPrivate MMFilterPrivate;
+
+typedef struct {
+ GObject parent;
+ MMFilterPrivate *priv;
+} MMFilter;
+
+typedef struct {
+ GObjectClass parent;
+} MMFilterClass;
+
+GType mm_filter_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMFilter, g_object_unref)
+
+typedef enum { /*< underscore_name=mm_filter_rule >*/
+ MM_FILTER_RULE_NONE = 0,
+ MM_FILTER_RULE_EXPLICIT_WHITELIST = 1 << 0,
+ MM_FILTER_RULE_EXPLICIT_BLACKLIST = 1 << 1,
+ MM_FILTER_RULE_PLUGIN_WHITELIST = 1 << 2,
+ MM_FILTER_RULE_QRTR = 1 << 3,
+ MM_FILTER_RULE_VIRTUAL = 1 << 4,
+ MM_FILTER_RULE_NET = 1 << 5,
+ MM_FILTER_RULE_USBMISC = 1 << 6,
+ MM_FILTER_RULE_RPMSG = 1 << 7,
+ MM_FILTER_RULE_WWAN = 1 << 8,
+ MM_FILTER_RULE_TTY = 1 << 9,
+ MM_FILTER_RULE_TTY_PLATFORM_DRIVER = 1 << 10,
+ MM_FILTER_RULE_TTY_DRIVER = 1 << 11,
+ MM_FILTER_RULE_TTY_ACM_INTERFACE = 1 << 12,
+ MM_FILTER_RULE_TTY_WITH_NET = 1 << 13,
+ MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN = 1 << 14,
+} MMFilterRule;
+
+/* This is a stricter policy which will only automatically probe device ports
+ * if they are allowed by any of the automatic whitelist rules. */
+#define MM_FILTER_POLICY_STRICT \
+ (MM_FILTER_RULE_EXPLICIT_WHITELIST | \
+ MM_FILTER_RULE_EXPLICIT_BLACKLIST | \
+ MM_FILTER_RULE_PLUGIN_WHITELIST | \
+ MM_FILTER_RULE_QRTR | \
+ MM_FILTER_RULE_VIRTUAL | \
+ MM_FILTER_RULE_NET | \
+ MM_FILTER_RULE_USBMISC | \
+ MM_FILTER_RULE_RPMSG | \
+ MM_FILTER_RULE_WWAN | \
+ MM_FILTER_RULE_TTY | \
+ MM_FILTER_RULE_TTY_PLATFORM_DRIVER | \
+ MM_FILTER_RULE_TTY_DRIVER | \
+ MM_FILTER_RULE_TTY_ACM_INTERFACE | \
+ MM_FILTER_RULE_TTY_WITH_NET | \
+ MM_FILTER_RULE_TTY_DEFAULT_FORBIDDEN)
+
+/* This policy only allows using device ports explicitly whitelisted via
+ * udev rules. i.e. ModemManager won't do any kind of automatic probing. */
+#define MM_FILTER_POLICY_WHITELIST_ONLY MM_FILTER_RULE_EXPLICIT_WHITELIST
+
+/* The strict policy has all supported rules */
+#define MM_FILTER_RULE_ALL MM_FILTER_POLICY_STRICT
+
+MMFilter *mm_filter_new (MMFilterRule enabled_rules,
+ GError **error);
+
+gboolean mm_filter_port (MMFilter *self,
+ MMKernelDevice *port,
+ gboolean manual_scan);
+
+gboolean mm_filter_device_and_port (MMFilter *self,
+ MMDevice *device,
+ MMKernelDevice *port);
+
+void mm_filter_register_plugin_whitelist_tag (MMFilter *self,
+ const gchar *tag);
+void mm_filter_register_plugin_whitelist_vendor_id (MMFilter *self,
+ guint16 vid);
+void mm_filter_register_plugin_whitelist_product_id (MMFilter *self,
+ guint16 vid,
+ guint16 pid);
+
+gboolean mm_filter_check_rule_enabled (MMFilter *self,
+ MMFilterRule rule);
+
+#endif /* MM_FILTER_H */
diff --git a/src/mm-iface-modem-3gpp-profile-manager.c b/src/mm-iface-modem-3gpp-profile-manager.c
new file mode 100644
index 00000000..088bc6c5
--- /dev/null
+++ b/src/mm-iface-modem-3gpp-profile-manager.c
@@ -0,0 +1,1673 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-3gpp-profile-manager.h"
+#include "mm-base-modem.h"
+#include "mm-log-object.h"
+
+#define SUPPORT_CHECKED_TAG "3gpp-ussd-support-checked-tag"
+#define SUPPORTED_TAG "3gpp-ussd-supported-tag"
+
+static GQuark support_checked_quark;
+static GQuark supported_quark;
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_3gpp_profile_manager_bind_simple_status (MMIfaceModem3gppProfileManager *self,
+ MMSimpleStatus *status)
+{
+ /* Nothing shown in simple status */
+}
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_3gpp_profile_manager_updated (MMIfaceModem3gppProfileManager *self)
+{
+ g_autoptr(MmGdbusModem3gppProfileManagerSkeleton) skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (skeleton)
+ mm_gdbus_modem3gpp_profile_manager_emit_updated (MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (skeleton));
+}
+
+static gboolean
+profile_manager_fail_if_connected_bearer (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GError **error)
+{
+ g_autoptr(MMBearerList) bearer_list = NULL;
+ g_autoptr(MMBaseBearer) bearer = NULL;
+
+ g_assert (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ g_object_get (self, MM_IFACE_MODEM_BEARER_LIST, &bearer_list, NULL);
+ if (bearer_list)
+ bearer = mm_bearer_list_find_by_profile_id (bearer_list, profile_id);
+
+ /* If a bearer is found reporting the profile id we're targeting to use,
+ * it means we have a known connected bearer, and we must abort the
+ * operation right away. */
+ if (bearer) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_CONNECTED,
+ "Cannot use profile %d: found an already connected bearer", profile_id);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Set profile (3GPP profile management interface) */
+
+typedef enum {
+ SET_PROFILE_STEP_FIRST,
+ SET_PROFILE_STEP_CHECK_FORMAT,
+ SET_PROFILE_STEP_LIST_BEFORE,
+ SET_PROFILE_STEP_SELECT_PROFILE,
+ SET_PROFILE_STEP_CHECK_ACTIVATED_PROFILE,
+ SET_PROFILE_STEP_DEACTIVATE_PROFILE,
+ SET_PROFILE_STEP_STORE_PROFILE,
+ SET_PROFILE_STEP_LIST_AFTER,
+ SET_PROFILE_STEP_LAST,
+} SetProfileStep;
+
+typedef struct {
+ SetProfileStep step;
+ MM3gppProfile *requested;
+ gboolean strict;
+ gboolean new_id;
+ gint min_profile_id;
+ gint max_profile_id;
+ GEqualFunc profile_apn_cmp;
+ MM3gppProfileCmpFlags profile_cmp_flags;
+ gint profile_id;
+ GList *before_list;
+ MM3gppProfile *stored;
+} SetProfileContext;
+
+static void
+set_profile_context_free (SetProfileContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->before_list);
+ g_clear_object (&ctx->requested);
+ g_clear_object (&ctx->stored);
+ g_slice_free (SetProfileContext, ctx);
+}
+
+MM3gppProfile *
+mm_iface_modem_3gpp_profile_manager_set_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void set_profile_step (GTask *task);
+
+static void
+profile_manager_get_profile_after_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->stored = mm_iface_modem_3gpp_profile_manager_get_profile_finish (self, res, &error);
+ if (!ctx->stored) {
+ g_prefix_error (&error, "Couldn't validate update of profile '%d': ", ctx->profile_id);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_list_after (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_iface_modem_3gpp_profile_manager_get_profile (
+ self,
+ ctx->profile_id,
+ (GAsyncReadyCallback)profile_manager_get_profile_after_ready,
+ task);
+}
+
+static void
+profile_manager_store_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ GError *error = NULL;
+ gint profile_id;
+
+ ctx = g_task_get_task_data (task);
+
+ profile_id = MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->store_profile_finish (self, res, &error);
+ if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* when creating a new profile with an unbound input profile id, store the
+ * one received after store */
+ if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN)
+ ctx->profile_id = profile_id;
+
+ g_assert (ctx->profile_id == profile_id);
+ mm_obj_dbg (self, "stored profile with id '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_store_profile (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (!ctx->stored);
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->store_profile (
+ self,
+ ctx->requested,
+ (GAsyncReadyCallback) profile_manager_store_profile_ready,
+ task);
+}
+
+static void
+profile_manager_deactivate_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ /* profile deactivation errors aren't fatal per se */
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile_finish (self, res, &error))
+ mm_obj_dbg (self, "couldn't deactivate profile with id '%d': %s", ctx->profile_id, error->message);
+ else
+ mm_obj_dbg (self, "deactivated profile with id '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_deactivate_profile (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile_finish) {
+ mm_obj_dbg (self, "skipping profile deactivation");
+ ctx->step++;
+ set_profile_step (task);
+ return;
+ }
+
+ /* This profile deactivation is EXCLUSIVELY done for those profiles that
+ * are connected in the modem but for which we don't have any connected
+ * bearer tracked. This covers e.g. a clean recovery of a previous daemon
+ * crash, and is now defined as a supported step in the core logic, instead
+ * of doing it differently in the different plugins and protocols. */
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->deactivate_profile (
+ self,
+ ctx->requested,
+ (GAsyncReadyCallback) profile_manager_deactivate_profile_ready,
+ task);
+}
+
+static void
+profile_manager_check_activated_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ gboolean activated = TRUE;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile_finish (self, res, &activated, &error)) {
+ mm_obj_dbg (self, "couldn't check if profile '%d' is activated: %s", ctx->profile_id, error->message);
+ ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE;
+ }
+ else if (activated) {
+ mm_obj_dbg (self, "profile '%d' is activated", ctx->profile_id);
+ ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE;
+ } else {
+ mm_obj_dbg (self, "profile '%d' is not activated", ctx->profile_id);
+ ctx->step = SET_PROFILE_STEP_STORE_PROFILE;
+ }
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_check_activated_profile (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ /* First, a quick check on our own bearer list. If we have a known bearer
+ * connected using the same profile id, we fail the operation right away. */
+ if (!profile_manager_fail_if_connected_bearer (self, ctx->profile_id, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile_finish) {
+ ctx->step = SET_PROFILE_STEP_DEACTIVATE_PROFILE;
+ set_profile_step (task);
+ return;
+ }
+
+ /* Second, an actual query to the modem, in order to trigger the profile
+ * deactivation before we attempt to activate it again */
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_activated_profile (
+ self,
+ ctx->requested,
+ (GAsyncReadyCallback) profile_manager_check_activated_profile_ready,
+ task);
+}
+
+static void
+set_profile_step_select_profile_exact (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) existing = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+
+ /* Look for the exact profile we want to use */
+ existing = mm_3gpp_profile_list_find_by_profile_id (ctx->before_list,
+ ctx->profile_id,
+ &error);
+ if (!existing) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If the profile is 100% equal to what we require, nothing to do */
+ if (mm_3gpp_profile_cmp (existing, ctx->requested, ctx->profile_apn_cmp, ctx->profile_cmp_flags)) {
+ ctx->stored = g_object_ref (existing);
+ mm_obj_dbg (self, "reusing profile '%d'", ctx->profile_id);
+ } else
+ mm_obj_dbg (self, "overwriting profile '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_select_profile_new (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_assert (ctx->strict);
+
+ /* If strict set required, fail if we cannot find an empty profile id */
+ ctx->profile_id = mm_3gpp_profile_list_find_empty (ctx->before_list,
+ ctx->min_profile_id,
+ ctx->max_profile_id,
+ &error);
+ if (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* store profile id in the requested profile */
+ mm_3gpp_profile_set_profile_id (ctx->requested, ctx->profile_id);
+
+ mm_obj_dbg (self, "creating profile '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_select_profile_best (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+ gboolean overwritten = FALSE;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->profile_id == MM_3GPP_PROFILE_ID_UNKNOWN);
+ g_assert (!ctx->strict);
+
+ ctx->profile_id = mm_3gpp_profile_list_find_best (ctx->before_list,
+ ctx->requested,
+ ctx->profile_apn_cmp,
+ ctx->profile_cmp_flags,
+ ctx->min_profile_id,
+ ctx->max_profile_id,
+ self,
+ &ctx->stored,
+ &overwritten);
+
+ /* store profile id in the requested profile */
+ mm_3gpp_profile_set_profile_id (ctx->requested, ctx->profile_id);
+
+ /* If we're reusing an already existing profile, we're done at this
+ * point, no need to create a new one */
+ if (ctx->stored)
+ mm_obj_dbg (self, "reusing profile '%d'", ctx->profile_id);
+ else if (overwritten)
+ mm_obj_dbg (self, "overwriting profile '%d'", ctx->profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+profile_manager_list_profiles_before_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, &ctx->before_list, &error))
+ mm_obj_dbg (self, "failed checking currently defined contexts: %s", error->message);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_list_before (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+
+ self = g_task_get_source_object (task);
+ mm_iface_modem_3gpp_profile_manager_list_profiles (
+ self,
+ (GAsyncReadyCallback)profile_manager_list_profiles_before_ready,
+ task);
+}
+
+static void
+set_profile_check_format_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetProfileContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_format_finish (
+ self, res,
+ &ctx->new_id,
+ &ctx->min_profile_id,
+ &ctx->max_profile_id,
+ &ctx->profile_apn_cmp,
+ &ctx->profile_cmp_flags,
+ NULL)) {
+ ctx->min_profile_id = 1;
+ ctx->max_profile_id = G_MAXINT-1;
+ mm_obj_dbg (self, "unknown context definition format; using defaults: minimum %d, maximum %d",
+ ctx->min_profile_id, ctx->max_profile_id);
+ } else
+ mm_obj_dbg (self, "context definition format: minimum %d, maximum %d",
+ ctx->min_profile_id, ctx->max_profile_id);
+
+ ctx->step++;
+ set_profile_step (task);
+}
+
+static void
+set_profile_step_check_format (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_format (
+ self,
+ mm_3gpp_profile_get_ip_type (ctx->requested),
+ (GAsyncReadyCallback)set_profile_check_format_ready,
+ task);
+}
+
+static void
+set_profile_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ SetProfileContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_PROFILE_STEP_FIRST:
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_CHECK_FORMAT:
+ mm_obj_dbg (self, "set profile state (%d/%d): check format",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_check_format (task);
+ return;
+
+ case SET_PROFILE_STEP_LIST_BEFORE:
+ mm_obj_dbg (self, "set profile state (%d/%d): list before",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_list_before (task);
+ return;
+
+ case SET_PROFILE_STEP_SELECT_PROFILE:
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (exact)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_select_profile_exact (task);
+ return;
+ }
+ if (!ctx->strict) {
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (best)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_select_profile_best (task);
+ return;
+ }
+ if (ctx->new_id) {
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (new)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_select_profile_new (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "set profile state (%d/%d): select profile (none)",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_CHECK_ACTIVATED_PROFILE:
+ /* If the modem/protocol doesn't allow preselecting the profile id of
+ * a new profile we're going to create, then we won't have a profile id
+ * set at this point. If so, just skip this step. */
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "set profile state (%d/%d): check activated profile",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_check_activated_profile (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_DEACTIVATE_PROFILE:
+ /* If the modem/protocol doesn't allow preselecting the profile id of
+ * a new profile we're going to create, then we won't have a profile id
+ * set at this point. If so, just skip this step. */
+ if (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN) {
+ mm_obj_dbg (self, "set profile state (%d/%d): deactivate profile",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_deactivate_profile (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case SET_PROFILE_STEP_STORE_PROFILE:
+ /* if we're reusing an already existing profile, we can jump
+ * to the last step now, there is no need to store any update */
+ if (ctx->stored) {
+ g_assert (ctx->profile_id != MM_3GPP_PROFILE_ID_UNKNOWN);
+ mm_obj_dbg (self, "set profile state (%d/%d): profile already stored",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ ctx->step = SET_PROFILE_STEP_LAST;
+ set_profile_step (task);
+ return;
+ }
+ mm_obj_dbg (self, "set profile state (%d/%d): store profile",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_store_profile (task);
+ return;
+
+ case SET_PROFILE_STEP_LIST_AFTER:
+ mm_obj_dbg (self, "set profile state (%d/%d): list after",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ set_profile_step_list_after (task);
+ return;
+
+ case SET_PROFILE_STEP_LAST:
+ mm_obj_dbg (self, "set profile state (%d/%d): all done",
+ ctx->step, SET_PROFILE_STEP_LAST);
+ g_assert (ctx->stored);
+ g_task_return_pointer (task, g_steal_pointer (&ctx->stored), g_object_unref);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_set_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ gboolean strict,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ SetProfileContext *ctx;
+ MMBearerIpFamily ip_family;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (SetProfileContext);
+ ctx->step = SET_PROFILE_STEP_FIRST;
+ ctx->requested = g_object_ref (requested);
+ ctx->strict = strict;
+ ctx->profile_id = mm_3gpp_profile_get_profile_id (requested);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_profile_context_free);
+
+ /* normalize IP family right away */
+ ip_family = mm_3gpp_profile_get_ip_type (requested);
+ mm_3gpp_normalize_ip_family (&ip_family);
+ mm_3gpp_profile_set_ip_type (requested, ip_family);
+
+ set_profile_step (task);
+}
+
+/*****************************************************************************/
+
+MM3gppProfile *
+mm_iface_modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_3GPP_PROFILE (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static void
+get_profile_list_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GList *profiles = NULL;
+ gint profile_id;
+ MM3gppProfile *profile;
+
+ profile_id = GPOINTER_TO_INT (g_task_get_task_data (task));
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish (self, res, &profiles, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ profile = mm_3gpp_profile_list_find_by_profile_id (profiles, profile_id, &error);
+ mm_3gpp_profile_list_free (profiles);
+
+ if (!profile) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_return_pointer (task, profile, g_object_unref);
+ g_object_unref (task);
+}
+
+static void
+get_profile_single_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MM3gppProfile *profile;
+
+ profile = MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile_finish (self, res, &error);
+ if (!profile)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, profile, g_object_unref);
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->get_profile (self,
+ profile_id,
+ (GAsyncReadyCallback)get_profile_single_ready,
+ task);
+ return;
+ }
+
+ /* If there is no way to query one single profile, query all and filter */
+ g_task_set_task_data (task, GINT_TO_POINTER (profile_id), NULL);
+
+ mm_iface_modem_3gpp_profile_manager_list_profiles (self,
+ (GAsyncReadyCallback)get_profile_list_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ GList *profiles;
+} ListProfilesContext;
+
+static void
+list_profiles_context_free (ListProfilesContext *ctx)
+{
+ mm_3gpp_profile_list_free (ctx->profiles);
+ g_slice_free (ListProfilesContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **out_profiles,
+ GError **error)
+{
+ ListProfilesContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (out_profiles)
+ *out_profiles = g_steal_pointer (&ctx->profiles);
+ return TRUE;
+}
+
+static void
+internal_list_profiles_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ListProfilesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_slice_new0 (ListProfilesContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) list_profiles_context_free);
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish (self, res, &ctx->profiles, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Internal calls to the list profile logic may be performed even if the 3GPP Profile Manager
+ * interface is not exposed in DBus, therefore, make sure this logic exits cleanly if there
+ * is no support for listing profiles */
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Listing profiles is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->list_profiles (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ (GAsyncReadyCallback)internal_list_profiles_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gppProfileManager *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem3gppProfileManager *self;
+} HandleListContext;
+
+static void
+handle_list_context_free (HandleListContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleListContext, ctx);
+}
+
+static void
+list_profiles_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ HandleListContext *ctx)
+{
+ GVariantBuilder builder;
+ GError *error = NULL;
+ GList *profiles = NULL;
+ GList *l;
+
+ if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, &profiles, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_list_context_free (ctx);
+ return;
+ }
+
+ /* Build array of dicts */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+ for (l = profiles; l; l = g_list_next (l)) {
+ g_autoptr(GVariant) dict = NULL;
+
+ dict = mm_3gpp_profile_get_dictionary (MM_3GPP_PROFILE (l->data));
+ g_variant_builder_add_value (&builder, dict);
+ }
+
+ mm_gdbus_modem3gpp_profile_manager_complete_list (ctx->skeleton,
+ ctx->invocation,
+ g_variant_builder_end (&builder));
+ handle_list_context_free (ctx);
+ mm_3gpp_profile_list_free (profiles);
+}
+
+static void
+handle_list_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleListContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_list_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_list_context_free (ctx);
+ return;
+ }
+
+ /* Don't call the class callback directly, use the common helper method
+ * that is also used by other internal operations. */
+ mm_iface_modem_3gpp_profile_manager_list_profiles (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ (GAsyncReadyCallback)list_profiles_ready,
+ ctx);
+}
+
+static gboolean
+handle_list (MmGdbusModem3gppProfileManager *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModem3gppProfileManager *self)
+{
+ HandleListContext *ctx;
+
+ ctx = g_slice_new0 (HandleListContext);
+ 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_list_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gppProfileManager *skeleton;
+ GDBusMethodInvocation *invocation;
+ GVariant *requested_dictionary;
+ MMIfaceModem3gppProfileManager *self;
+} HandleSetContext;
+
+static void
+handle_set_context_free (HandleSetContext *ctx)
+{
+ g_clear_pointer (&ctx->requested_dictionary, g_variant_unref);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetContext, ctx);
+}
+
+static void
+set_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ HandleSetContext *ctx)
+{
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile_stored = NULL;
+
+ profile_stored = mm_iface_modem_3gpp_profile_manager_set_profile_finish (self, res, &error);
+ if (!profile_stored)
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ g_autoptr(GVariant) profile_dictionary = NULL;
+
+ profile_dictionary = mm_3gpp_profile_get_dictionary (profile_stored);
+ mm_gdbus_modem3gpp_profile_manager_complete_set (ctx->skeleton, ctx->invocation, profile_dictionary);
+ }
+ handle_set_context_free (ctx);
+}
+
+static void
+handle_set_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetContext *ctx)
+{
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile_requested = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ if (!ctx->requested_dictionary) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing requested profile settings");
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ profile_requested = mm_3gpp_profile_new_from_dictionary (ctx->requested_dictionary, &error);
+ if (!profile_requested) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_context_free (ctx);
+ return;
+ }
+
+ /* Don't call the class callback directly, use the common helper method
+ * that is also used by other internal operations. */
+ mm_iface_modem_3gpp_profile_manager_set_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ profile_requested,
+ TRUE, /* strict always! */
+ (GAsyncReadyCallback)set_profile_ready,
+ ctx);
+}
+
+static gboolean
+handle_set (MmGdbusModem3gppProfileManager *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *requested_dictionary,
+ MMIfaceModem3gppProfileManager *self)
+{
+ HandleSetContext *ctx;
+
+ ctx = g_slice_new0 (HandleSetContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->requested_dictionary = requested_dictionary ? g_variant_ref (requested_dictionary) : NULL;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gppProfileManager *skeleton;
+ GDBusMethodInvocation *invocation;
+ GVariant *dictionary;
+ MMIfaceModem3gppProfileManager *self;
+} HandleDeleteContext;
+
+static void
+handle_delete_context_free (HandleDeleteContext *ctx)
+{
+ g_clear_pointer (&ctx->dictionary, g_variant_unref);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleDeleteContext, ctx);
+}
+
+static void
+delete_profile_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ HandleDeleteContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem3gpp_profile_manager_complete_delete (ctx->skeleton, ctx->invocation);
+ handle_delete_context_free (ctx);
+}
+
+static void
+handle_delete_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleDeleteContext *ctx)
+{
+ gint profile_id;
+ GError *error = NULL;
+ g_autoptr(MM3gppProfile) profile = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile ||
+ !MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile_finish) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Deleting profiles is not supported");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!ctx->dictionary) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing profile settings");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ profile = mm_3gpp_profile_new_from_dictionary (ctx->dictionary, &error);
+ if (!profile) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ profile_id = mm_3gpp_profile_get_profile_id (profile);
+ if (profile_id == MM_3GPP_PROFILE_ID_UNKNOWN) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Missing 'profile-id' in profile settings");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!profile_manager_fail_if_connected_bearer (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self), profile_id, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->delete_profile (
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER (self),
+ profile,
+ (GAsyncReadyCallback)delete_profile_ready,
+ ctx);
+}
+
+static gboolean
+handle_delete (MmGdbusModem3gppProfileManager *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary,
+ MMIfaceModem3gppProfileManager *self)
+{
+ HandleDeleteContext *ctx;
+
+ ctx = g_slice_new0 (HandleDeleteContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_delete_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct _DisablingContext DisablingContext;
+static void interface_disabling_step (GTask *task);
+
+typedef enum {
+ DISABLING_STEP_FIRST,
+ DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
+ DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
+ DISABLING_STEP_LAST
+} DisablingStep;
+
+struct _DisablingContext {
+ DisablingStep step;
+ MmGdbusModem3gppProfileManager *skeleton;
+};
+
+static void
+disabling_context_free (DisablingContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_slice_free (DisablingContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_disable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't disable unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_disabling_step (task);
+}
+
+static void
+cleanup_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't cleanup unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_disabling_step (task);
+}
+
+static void
+interface_disabling_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case DISABLING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)disable_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)cleanup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_disable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisablingContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (DisablingContext);
+ ctx->step = DISABLING_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ interface_disabling_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct _EnablingContext EnablingContext;
+static void interface_enabling_step (GTask *task);
+
+typedef enum {
+ ENABLING_STEP_FIRST,
+ ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
+ ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
+ ENABLING_STEP_LAST
+} EnablingStep;
+
+struct _EnablingContext {
+ EnablingStep step;
+ MmGdbusModem3gppProfileManager *skeleton;
+};
+
+static void
+enabling_context_free (EnablingContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_slice_free (EnablingContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_enable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+setup_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't setup unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_enabling_step (task);
+}
+
+static void
+enable_unsolicited_events_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "couldn't enable unsolicited profile management events: %s", error->message);
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_enabling_step (task);
+}
+
+static void
+interface_enabling_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ EnablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case ENABLING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)setup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)enable_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_enable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnablingContext *ctx;
+ GTask *task;
+
+ ctx = g_slice_new0 (EnablingContext);
+ ctx->step = ENABLING_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ interface_enabling_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct _InitializationContext InitializationContext;
+static void interface_initialization_step (GTask *task);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CHECK_SUPPORT,
+ INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitializationContext {
+ MmGdbusModem3gppProfileManager *skeleton;
+ InitializationStep step;
+};
+
+static void
+initialization_context_free (InitializationContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_slice_free (InitializationContext, ctx);
+}
+
+gboolean
+mm_iface_modem_3gpp_profile_manager_initialize_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+check_support_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support_finish (self, res, &error)) {
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "profile management support check failed: %s", error->message);
+ }
+ } else {
+ /* profile management is supported! */
+ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE));
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+profile_manager_list_profiles_check_ready (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_3gpp_profile_manager_list_profiles_finish (self, res, NULL, &error))
+ mm_obj_dbg (self, "profile management support check failed: couldn't load profile list: %s", error->message);
+ else {
+ /* profile management is supported! */
+ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (TRUE));
+ }
+
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+interface_initialization_step (GTask *task)
+{
+ MMIfaceModem3gppProfileManager *self;
+ InitializationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ 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));
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_CHECK_SUPPORT:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), support_checked_quark))) {
+ /* Set the checked flag so that we don't run it again */
+ g_object_set_qdata (G_OBJECT (self), support_checked_quark, GUINT_TO_POINTER (TRUE));
+ /* Initially, assume we don't support it */
+ g_object_set_qdata (G_OBJECT (self), supported_quark, GUINT_TO_POINTER (FALSE));
+ if (MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE (self)->check_support (
+ self,
+ (GAsyncReadyCallback)check_support_ready,
+ task);
+ return;
+ }
+
+ /* If there is no implementation to check support, try to query the list
+ * explicitly; it may be the case that there is no other way to check for
+ * support. */
+ mm_iface_modem_3gpp_profile_manager_list_profiles (
+ self,
+ (GAsyncReadyCallback)profile_manager_list_profiles_check_ready,
+ task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self), supported_quark))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Profile management not supported");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_LAST:
+ /* We are done without errors! */
+
+ /* Handle method invocations */
+ g_object_connect (ctx->skeleton,
+ "signal::handle-list", G_CALLBACK (handle_list), self,
+ "signal::handle-set", G_CALLBACK (handle_set), self,
+ "signal::handle-delete", G_CALLBACK (handle_delete), self,
+ NULL);
+
+ /* Finally, export the new interface */
+ mm_gdbus_object_skeleton_set_modem3gpp_profile_manager (MM_GDBUS_OBJECT_SKELETON (self),
+ MM_GDBUS_MODEM3GPP_PROFILE_MANAGER (ctx->skeleton));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_initialize (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationContext *ctx;
+ MmGdbusModem3gppProfileManager *skeleton = NULL;
+ GTask *task;
+
+ /* Did we already create it? */
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ skeleton = mm_gdbus_modem3gpp_profile_manager_skeleton_new ();
+ g_object_set (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, skeleton,
+ NULL);
+ }
+
+ /* Perform async initialization here */
+
+ ctx = g_slice_new0 (InitializationContext);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->skeleton = skeleton;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
+}
+
+void
+mm_iface_modem_3gpp_profile_manager_shutdown (MMIfaceModem3gppProfileManager *self)
+{
+ /* Unexport DBus interface and remove the skeleton */
+ mm_gdbus_object_skeleton_set_modem3gpp_profile_manager (MM_GDBUS_OBJECT_SKELETON (self), NULL);
+ g_object_set (self,
+ MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+iface_modem_3gpp_profile_manager_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_3GPP_PROFILE_MANAGER_DBUS_SKELETON,
+ "3GPP Profile Manager DBus skeleton",
+ "DBus skeleton for the 3GPP Profile Manager interface",
+ MM_GDBUS_TYPE_MODEM3GPP_PROFILE_MANAGER_SKELETON,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+}
+
+GType
+mm_iface_modem_3gpp_profile_manager_get_type (void)
+{
+ static GType iface_modem_3gpp_profile_manager_type = 0;
+
+ if (!G_UNLIKELY (iface_modem_3gpp_profile_manager_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMIfaceModem3gppProfileManager), /* class_size */
+ iface_modem_3gpp_profile_manager_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_modem_3gpp_profile_manager_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMIfaceModem3gppProfileManager",
+ &info, 0);
+
+ g_type_interface_add_prerequisite (iface_modem_3gpp_profile_manager_type, MM_TYPE_IFACE_MODEM_3GPP);
+ }
+
+ return iface_modem_3gpp_profile_manager_type;
+}
diff --git a/src/mm-iface-modem-3gpp-profile-manager.h b/src/mm-iface-modem-3gpp-profile-manager.h
new file mode 100644
index 00000000..dcf97895
--- /dev/null
+++ b/src/mm-iface-modem-3gpp-profile-manager.h
@@ -0,0 +1,248 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Google, Inc.
+ */
+
+#ifndef MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#define MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER (mm_iface_modem_3gpp_profile_manager_get_type ())
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, MMIfaceModem3gppProfileManager))
+#define MM_IS_IFACE_MODEM_3GPP_PROFILE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER))
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, MMIfaceModem3gppProfileManager))
+
+#define MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_DBUS_SKELETON "iface-modem-3gpp-profile-manager-dbus-skeleton"
+
+typedef struct _MMIfaceModem3gppProfileManager MMIfaceModem3gppProfileManager;
+
+struct _MMIfaceModem3gppProfileManager {
+ GTypeInterface g_iface;
+
+ /* Check for profile management support (async) */
+ void (* check_support) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_support_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setup of unsolicited events */
+ void (* setup_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous enabling of unsolicited events */
+ void (* enable_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* enable_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous disabling of unsolicited events */
+ void (* disable_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* disable_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous cleaning up of unsolicited events */
+ void (* cleanup_unsolicited_events) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Get a single profile.
+ * This is completely optional, and should be used by implementations that are able
+ * to query one single profile settings. For all other implementations, it should be
+ * set to NULL so that the generic implementation is used (listing all and lookup by
+ * profile id) */
+ void (* get_profile) (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MM3gppProfile * (* get_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* List */
+ void (* list_profiles) (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* list_profiles_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **profiles,
+ GError **error);
+
+ /* Delete */
+ void (* delete_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* delete_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /*
+ * Check profile format (substep of 'set profiles')
+ *
+ * Before a profile is set, we would like to check the format of the set
+ * operation, if possible.
+ *
+ * Expected outputs:
+ * - new_id: whether new profiles can be created with a specific known id.
+ * - min_profile_id: minimum supported profile id.
+ * - max_profile_id: maximum supported profile id.
+ * - apn_cmp: method to use when comparing APN strings.
+ * - profile_cmp_flags: flags to use when comparing profile objects.
+ *
+ * The check is done per IP family, as the ranges may be different for each.
+ */
+ void (* check_format) (MMIfaceModem3gppProfileManager *self,
+ MMBearerIpFamily family,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_format_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *new_id,
+ gint *min_profile_id,
+ gint *max_profile_id,
+ GEqualFunc *apn_cmp,
+ MM3gppProfileCmpFlags *profile_cmp_flags,
+ GError **error);
+
+ /* Check activated profile (substep of 'set profiles')
+ *
+ * Before a profile is set, we may attempt to deactivate it first, but only
+ * if there is no known bearer using it already and only if this check for
+ * activation really reports the profile being already activated.
+ *
+ * The given profile MUST have profile-id set, so the set_profile()
+ * implementation should only use it once the profile-id is known, never
+ * before.
+ *
+ * This step is optional (method pointers can be initialized to NULL), so
+ * that the deactivate profile step is done always.
+ */
+ void (* check_activated_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_activated_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ gboolean *out_activated,
+ GError **error);
+
+ /* Deactivate profile (substep of 'set profiles')
+ *
+ * Before a profile is set, we may attempt to deactivate it first, but only
+ * if there is no known bearer using it already.
+ *
+ * The given profile MUST have profile-id set, so the set_profile()
+ * implementation should only use it once the profile-id is known, never
+ * before.
+ */
+ void (* deactivate_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* deactivate_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Store profile (substep of 'set profiles') */
+ void (* store_profile) (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gint (* store_profile_finish) (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+};
+
+GType mm_iface_modem_3gpp_profile_manager_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gppProfileManager, g_object_unref)
+
+/* Initialize profile manager interface (async) */
+void mm_iface_modem_3gpp_profile_manager_initialize (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_initialize_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Enable profile manager interface (async) */
+void mm_iface_modem_3gpp_profile_manager_enable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_enable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Disable profile manager interface (async) */
+void mm_iface_modem_3gpp_profile_manager_disable (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_disable_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shutdown profile manager interface */
+void mm_iface_modem_3gpp_profile_manager_shutdown (MMIfaceModem3gppProfileManager *self);
+
+/* Bind properties for simple GetStatus() */
+void mm_iface_modem_3gpp_profile_manager_bind_simple_status (MMIfaceModem3gppProfileManager *self,
+ MMSimpleStatus *status);
+
+/* Helper to emit the Updated signal by implementations */
+void mm_iface_modem_3gpp_profile_manager_updated (MMIfaceModem3gppProfileManager *self);
+
+/* Internal list profile management */
+void mm_iface_modem_3gpp_profile_manager_get_profile (MMIfaceModem3gppProfileManager *self,
+ gint profile_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MM3gppProfile *mm_iface_modem_3gpp_profile_manager_get_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_iface_modem_3gpp_profile_manager_list_profiles (MMIfaceModem3gppProfileManager *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_profile_manager_list_profiles_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GList **profiles,
+ GError **error);
+void mm_iface_modem_3gpp_profile_manager_set_profile (MMIfaceModem3gppProfileManager *self,
+ MM3gppProfile *requested,
+ gboolean strict,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MM3gppProfile *mm_iface_modem_3gpp_profile_manager_set_profile_finish (MMIfaceModem3gppProfileManager *self,
+ GAsyncResult *res,
+ GError **error);
+
+
+#endif /* MM_IFACE_MODEM_3GPP_PROFILE_MANAGER_H */
diff --git a/src/mm-iface-modem-3gpp-ussd.c b/src/mm-iface-modem-3gpp-ussd.c
index db62cbbc..33f49f0e 100644
--- a/src/mm-iface-modem-3gpp-ussd.c
+++ b/src/mm-iface-modem-3gpp-ussd.c
@@ -26,7 +26,7 @@
#include "mm-iface-modem-3gpp-ussd.h"
#include "mm-base-modem.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#define SUPPORT_CHECKED_TAG "3gpp-ussd-support-checked-tag"
#define SUPPORTED_TAG "3gpp-ussd-supported-tag"
@@ -45,57 +45,6 @@ mm_iface_modem_3gpp_ussd_bind_simple_status (MMIfaceModem3gppUssd *self,
/*****************************************************************************/
-static gboolean
-ensure_enabled (MMBaseModem *self,
- GError **error)
-{
- MMModemState modem_state;
-
- modem_state = MM_MODEM_STATE_UNKNOWN;
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &modem_state,
- NULL);
-
- switch (modem_state) {
- case MM_MODEM_STATE_FAILED:
- case MM_MODEM_STATE_UNKNOWN:
- case MM_MODEM_STATE_LOCKED:
- /* We should never have such request (interface wasn't exported yet) */
- g_assert_not_reached ();
- return FALSE;
-
- case MM_MODEM_STATE_INITIALIZING:
- case MM_MODEM_STATE_ENABLING:
- case MM_MODEM_STATE_DISABLED:
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot perform USSD action: "
- "device not enabled yet");
- return FALSE;
-
- case MM_MODEM_STATE_DISABLING:
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot perform USSD action: "
- "currently being disabled");
- return FALSE;
-
- case MM_MODEM_STATE_ENABLED:
- case MM_MODEM_STATE_SEARCHING:
- case MM_MODEM_STATE_REGISTERED:
- case MM_MODEM_STATE_DISCONNECTING:
- case MM_MODEM_STATE_CONNECTING:
- case MM_MODEM_STATE_CONNECTED:
- break;
- }
-
- return TRUE;
-}
-
-/*****************************************************************************/
-
typedef struct {
MmGdbusModem3gppUssd *skeleton;
GDBusMethodInvocation *invocation;
@@ -133,13 +82,19 @@ handle_cancel_auth_ready (MMBaseModem *self,
{
GError *error = NULL;
- if (!mm_base_modem_authorize_finish (self, res, &error) ||
- !ensure_enabled (self, &error)) {
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_cancel_context_free (ctx);
return;
}
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_cancel_context_free (ctx);
+ return;
+ }
+
g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cancel != NULL);
g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cancel_finish != NULL);
@@ -194,15 +149,17 @@ handle_respond_ready (MMIfaceModem3gppUssd *self,
HandleRespondContext *ctx)
{
GError *error = NULL;
- const gchar *reply;
+ gchar *reply;
- reply = MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish (self, res,&error);
+ reply = MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish (self, res, &error);
if (!reply)
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ else {
mm_gdbus_modem3gpp_ussd_complete_respond (ctx->skeleton,
ctx->invocation,
reply);
+ g_free (reply);
+ }
handle_respond_context_free (ctx);
}
@@ -213,13 +170,19 @@ handle_respond_auth_ready (MMBaseModem *self,
{
GError *error = NULL;
- if (!mm_base_modem_authorize_finish (self, res, &error) ||
- !ensure_enabled (self, &error)) {
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_respond_context_free (ctx);
return;
}
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_respond_context_free (ctx);
+ return;
+ }
+
g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send != NULL);
g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish != NULL);
@@ -298,15 +261,17 @@ handle_initiate_ready (MMIfaceModem3gppUssd *self,
HandleInitiateContext *ctx)
{
GError *error = NULL;
- const gchar *reply;
+ gchar *reply;
reply = MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish (self, res, &error);
if (!reply)
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ else {
mm_gdbus_modem3gpp_ussd_complete_initiate (ctx->skeleton,
ctx->invocation,
reply);
+ g_free (reply);
+ }
handle_initiate_context_free (ctx);
}
@@ -317,13 +282,19 @@ handle_initiate_auth_ready (MMBaseModem *self,
{
GError *error = NULL;
- if (!mm_base_modem_authorize_finish (self, res, &error) ||
- !ensure_enabled (self, &error)) {
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_initiate_context_free (ctx);
return;
}
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_initiate_context_free (ctx);
+ return;
+ }
+
g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send != NULL);
g_assert (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->send_finish != NULL);
@@ -398,6 +369,24 @@ mm_iface_modem_3gpp_ussd_decode (MMIfaceModem3gppUssd *self,
/*****************************************************************************/
+MMModem3gppUssdSessionState
+mm_iface_modem_3gpp_ussd_get_state (MMIfaceModem3gppUssd *self)
+{
+ MmGdbusModem3gppUssd *skeleton = NULL;
+ MMModem3gppUssdSessionState state;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_USSD_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!skeleton)
+ return MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN;
+
+ state = (MMModem3gppUssdSessionState) mm_gdbus_modem3gpp_ussd_get_state (skeleton);
+ g_object_unref (skeleton);
+ return state;
+}
+
void
mm_iface_modem_3gpp_ussd_update_state (MMIfaceModem3gppUssd *self,
MMModem3gppUssdSessionState new_state)
@@ -459,28 +448,23 @@ mm_iface_modem_3gpp_ussd_update_network_request (MMIfaceModem3gppUssd *self,
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
- DISABLING_STEP_DISABLE_UNSOLICITED_RESULT_CODES,
- DISABLING_STEP_CLEANUP_UNSOLICITED_RESULT_CODES,
+ DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
+ DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
DISABLING_STEP_LAST
} DisablingStep;
struct _DisablingContext {
- MMIfaceModem3gppUssd *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModem3gppUssd *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -491,80 +475,89 @@ mm_iface_modem_3gpp_ussd_disable_finish (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-disable_unsolicited_result_codes_ready (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- DisablingContext *ctx)
+disable_unsolicited_events_ready (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->disable_unsolicited_result_codes_finish (self,
- res,
- &error);
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Couldn't disable unsolicited result codes: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't disable unsolicited USSD events: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-cleanup_unsolicited_result_codes_ready (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- DisablingContext *ctx)
+cleanup_unsolicited_events_ready (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cleanup_unsolicited_result_codes_finish (self,
- res,
- &error);
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Couldn't cleanup unsolicited result codes: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't cleanup unsolicited USSD events: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModem3gppUssd *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
- case DISABLING_STEP_DISABLE_UNSOLICITED_RESULT_CODES:
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->disable_unsolicited_result_codes (
- ctx->self,
- (GAsyncReadyCallback)disable_unsolicited_result_codes_ready,
- ctx);
+ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)disable_unsolicited_events_ready,
+ task);
return;
- case DISABLING_STEP_CLEANUP_UNSOLICITED_RESULT_CODES:
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->cleanup_unsolicited_result_codes (
- ctx->self,
- (GAsyncReadyCallback)cleanup_unsolicited_result_codes_ready,
- ctx);
+ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)cleanup_unsolicited_events_ready,
+ task);
return;
case DISABLING_STEP_LAST:
/* We are done without errors! */
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
MM_MODEM_3GPP_USSD_SESSION_STATE_UNKNOWN);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disabling_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -576,54 +569,49 @@ mm_iface_modem_3gpp_ussd_disable (MMIfaceModem3gppUssd *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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_3gpp_ussd_disable);
ctx->step = DISABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_3GPP_USSD_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
- ENABLING_STEP_SETUP_UNSOLICITED_RESULT_CODES,
- ENABLING_STEP_ENABLE_UNSOLICITED_RESULT_CODES,
+ ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
+ ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
- MMIfaceModem3gppUssd *self;
EnablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModem3gppUssd *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_free (EnablingContext *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);
@@ -634,80 +622,89 @@ mm_iface_modem_3gpp_ussd_enable_finish (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-setup_unsolicited_result_codes_ready (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- EnablingContext *ctx)
+setup_unsolicited_events_ready (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->setup_unsolicited_result_codes_finish (self,
- res,
- &error);
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Couldn't setup unsolicited result codes: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't setup unsolicited USSD events: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-enable_unsolicited_result_codes_ready (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- EnablingContext *ctx)
+enable_unsolicited_events_ready (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->enable_unsolicited_result_codes_finish (self,
- res,
- &error);
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Couldn't enable unsolicited result codes: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't enable unsolicited USSD events: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModem3gppUssd *self;
+ EnablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
- case ENABLING_STEP_SETUP_UNSOLICITED_RESULT_CODES:
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->setup_unsolicited_result_codes (
- ctx->self,
- (GAsyncReadyCallback)setup_unsolicited_result_codes_ready,
- ctx);
+ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)setup_unsolicited_events_ready,
+ task);
return;
- case ENABLING_STEP_ENABLE_UNSOLICITED_RESULT_CODES:
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->enable_unsolicited_result_codes (
- ctx->self,
- (GAsyncReadyCallback)enable_unsolicited_result_codes_ready,
- ctx);
+ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)enable_unsolicited_events_ready,
+ task);
return;
case ENABLING_STEP_LAST:
/* We are done without errors! */
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- enabling_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -719,33 +716,33 @@ mm_iface_modem_3gpp_ussd_enable (MMIfaceModem3gppUssd *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
ctx = g_new0 (EnablingContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_3gpp_ussd_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_3GPP_USSD_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
@@ -755,18 +752,13 @@ typedef enum {
} InitializationStep;
struct _InitializationContext {
- MMIfaceModem3gppUssd *self;
MmGdbusModem3gppUssd *skeleton;
- GSimpleAsyncResult *result;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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->skeleton);
g_free (ctx);
}
@@ -774,8 +766,9 @@ initialization_context_complete_and_free (InitializationContext *ctx)
static void
check_support_ready (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support_finish (self,
@@ -783,7 +776,7 @@ check_support_ready (MMIfaceModem3gppUssd *self,
&error)) {
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("USSD support check failed: '%s'", error->message);
+ mm_obj_dbg (self, "USSD support check failed: %s", error->message);
g_error_free (error);
}
} else {
@@ -794,13 +787,20 @@ check_support_ready (MMIfaceModem3gppUssd *self,
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModem3gppUssd *self;
+ InitializationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
/* Setup quarks if we didn't do it before */
@@ -811,27 +811,27 @@ interface_initialization_step (InitializationContext *ctx)
supported_quark = (g_quark_from_static_string (
SUPPORTED_TAG));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_CHECK_SUPPORT:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
support_checked_quark))) {
/* Set the checked flag so that we don't run it again */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
support_checked_quark,
GUINT_TO_POINTER (TRUE));
/* Initially, assume we don't support it */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
supported_quark,
GUINT_TO_POINTER (FALSE));
- if (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->check_support &&
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->check_support_finish) {
- MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (ctx->self)->check_support (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_3GPP_USSD_GET_INTERFACE (self)->check_support (
+ self,
(GAsyncReadyCallback)check_support_ready,
- ctx);
+ task);
return;
}
@@ -839,22 +839,22 @@ interface_initialization_step (InitializationContext *ctx)
* support it. */
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
supported_quark))) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "USSD not supported");
- initialization_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "USSD not supported");
+ g_object_unref (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -863,23 +863,26 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-initiate",
G_CALLBACK (handle_initiate),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-respond",
G_CALLBACK (handle_respond),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-cancel",
G_CALLBACK (handle_cancel),
- ctx->self);
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem3gpp_ussd (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem3gpp_ussd (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM3GPP_USSD (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -890,7 +893,7 @@ mm_iface_modem_3gpp_ussd_initialize_finish (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -900,6 +903,7 @@ mm_iface_modem_3gpp_ussd_initialize (MMIfaceModem3gppUssd *self,
{
InitializationContext *ctx;
MmGdbusModem3gppUssd *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -921,15 +925,13 @@ mm_iface_modem_3gpp_ussd_initialize (MMIfaceModem3gppUssd *self,
/* Perform async initialization here */
ctx = g_new0 (InitializationContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_3gpp_ussd_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
diff --git a/src/mm-iface-modem-3gpp-ussd.h b/src/mm-iface-modem-3gpp-ussd.h
index 662d555e..8a6d2f42 100644
--- a/src/mm-iface-modem-3gpp-ussd.h
+++ b/src/mm-iface-modem-3gpp-ussd.h
@@ -40,44 +40,44 @@ struct _MMIfaceModem3gppUssd {
GTypeInterface g_iface;
/* Check for USSD support (async) */
- void (* check_support) (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*check_support_finish) (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error);
-
- /* Asynchronous setup of unsolicited result codes */
- void (*setup_unsolicited_result_codes) (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*setup_unsolicited_result_codes_finish) (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error);
-
- /* Asynchronous enabling of unsolicited result codes */
- void (*enable_unsolicited_result_codes) (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*enable_unsolicited_result_codes_finish) (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error);
-
- /* Asynchronous disabling of unsolicited result codes */
- void (*disable_unsolicited_result_codes) (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*disable_unsolicited_result_codes_finish) (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error);
-
- /* Asynchronous cleaning up of unsolicited result codes */
- void (*cleanup_unsolicited_result_codes) (MMIfaceModem3gppUssd *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*cleanup_unsolicited_result_codes_finish) (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error);
+ void (* check_support) (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_support_finish) (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setup of unsolicited events */
+ void (* setup_unsolicited_events) (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_unsolicited_events_finish) (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous enabling of unsolicited events */
+ void (* enable_unsolicited_events) (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* enable_unsolicited_events_finish) (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous disabling of unsolicited events */
+ void (* disable_unsolicited_events) (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* disable_unsolicited_events_finish) (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous cleaning up of unsolicited events */
+ void (* cleanup_unsolicited_events) (MMIfaceModem3gppUssd *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error);
/* Encode/Decode */
gchar * (*encode) (MMIfaceModem3gppUssd *self,
@@ -93,9 +93,9 @@ struct _MMIfaceModem3gppUssd {
const gchar *command,
GAsyncReadyCallback callback,
gpointer user_data);
- const gchar * (* send_finish) (MMIfaceModem3gppUssd *self,
- GAsyncResult *res,
- GError **error);
+ gchar * (* send_finish) (MMIfaceModem3gppUssd *self,
+ GAsyncResult *res,
+ GError **error);
/* Cancel */
void (* cancel) (MMIfaceModem3gppUssd *self,
@@ -107,6 +107,7 @@ struct _MMIfaceModem3gppUssd {
};
GType mm_iface_modem_3gpp_ussd_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gppUssd, g_object_unref)
/* Initialize USSD interface (async) */
void mm_iface_modem_3gpp_ussd_initialize (MMIfaceModem3gppUssd *self,
@@ -132,6 +133,8 @@ gboolean mm_iface_modem_3gpp_ussd_disable_finish (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
GError **error);
+MMModem3gppUssdSessionState mm_iface_modem_3gpp_ussd_get_state (MMIfaceModem3gppUssd *self);
+
/* Property updaters */
void mm_iface_modem_3gpp_ussd_update_state (MMIfaceModem3gppUssd *self,
MMModem3gppUssdSessionState new_state);
diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c
index 919954c5..1e18b5b7 100644
--- a/src/mm-iface-modem-3gpp.c
+++ b/src/mm-iface-modem-3gpp.c
@@ -28,15 +28,78 @@
#include "mm-error-helpers.h"
#include "mm-log.h"
-#define REGISTRATION_CHECK_TIMEOUT_SEC 30
-
#define SUBSYSTEM_3GPP "3gpp"
-#define REGISTRATION_STATE_CONTEXT_TAG "3gpp-registration-state-context-tag"
-#define REGISTRATION_CHECK_CONTEXT_TAG "3gpp-registration-check-context-tag"
+/* When comparing EPS bearer settings take into account that:
+ * - 'password' may not always be readable.
+ * - 'apn-type' may not always be supported.
+ * - 'profile-id' will not be known in the requested settings
+ * - we ignore settings not applicable to profiles, like 'allow-roaming' or
+ * 'rm-protocol'.
+ * - we apply very loose matching for all fields.
+ */
+#define MM_BEARER_PROPERTIES_CMP_FLAGS_EPS \
+ (MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE | \
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID | \
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD | \
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING | \
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE | \
+ MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL)
-static GQuark registration_state_context_quark;
-static GQuark registration_check_context_quark;
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "iface-modem-3gpp-private-tag"
+static GQuark private_quark;
+
+typedef struct {
+ /* Registration state */
+ MMModem3gppRegistrationState state_cs;
+ MMModem3gppRegistrationState state_ps;
+ MMModem3gppRegistrationState state_eps;
+ MMModem3gppRegistrationState state_5gs;
+ gboolean manual_registration;
+ gchar *manual_registration_operator_id;
+ GCancellable *pending_registration_cancellable;
+ gboolean reloading_registration_info;
+ /* Registration checks */
+ guint check_timeout_source;
+ gboolean check_running;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_free (priv->manual_registration_operator_id);
+ if (priv->pending_registration_cancellable) {
+ g_cancellable_cancel (priv->pending_registration_cancellable);
+ g_object_unref (priv->pending_registration_cancellable);
+ }
+ if (priv->check_timeout_source)
+ g_source_remove (priv->check_timeout_source);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMIfaceModem3gpp *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ priv->state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ priv->state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ priv->state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ priv->state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
/*****************************************************************************/
@@ -64,91 +127,119 @@ 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);
}
/*****************************************************************************/
-typedef struct {
- MMModem3gppRegistrationState cs;
- MMModem3gppRegistrationState ps;
- MMModem3gppRegistrationState eps;
- gboolean manual_registration;
- GCancellable *pending_registration_cancellable;
- gboolean reloading_registration_info;
-} RegistrationStateContext;
+#define REG_STATE_IS_REGISTERED(state) \
+ (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED)
-static void
-registration_state_context_free (RegistrationStateContext *ctx)
-{
- if (ctx->pending_registration_cancellable) {
- g_cancellable_cancel (ctx->pending_registration_cancellable);
- g_object_unref (ctx->pending_registration_cancellable);
- }
- g_slice_free (RegistrationStateContext, ctx);
-}
+#define REG_STATE_IS_UNKNOWN_IDLE_DENIED(state) \
+ (state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || \
+ state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED)
-static RegistrationStateContext *
-get_registration_state_context (MMIfaceModem3gpp *self)
+static MMModem3gppPacketServiceState
+get_consolidated_packet_service_state (MMIfaceModem3gpp *self)
{
- RegistrationStateContext *ctx;
+ Private *priv;
- if (G_UNLIKELY (!registration_state_context_quark))
- registration_state_context_quark = (g_quark_from_static_string (
- REGISTRATION_STATE_CONTEXT_TAG));
+ priv = get_private (self);
- ctx = g_object_get_qdata (G_OBJECT (self), registration_state_context_quark);
- if (!ctx) {
- /* Create context and keep it as object data */
- ctx = g_slice_new0 (RegistrationStateContext);
- ctx->cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- ctx->ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- ctx->eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+ /* If registered in any of PS, EPS or 5GS, then packet service domain is
+ * implicitly attached. */
+ if (REG_STATE_IS_REGISTERED (priv->state_ps) ||
+ REG_STATE_IS_REGISTERED (priv->state_eps) ||
+ REG_STATE_IS_REGISTERED (priv->state_5gs))
+ return MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED;
- g_object_set_qdata_full (
- G_OBJECT (self),
- registration_state_context_quark,
- ctx,
- (GDestroyNotify)registration_state_context_free);
- }
+ if (REG_STATE_IS_REGISTERED (priv->state_cs))
+ return MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED;
- return ctx;
+ return MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN;
}
static MMModem3gppRegistrationState
-get_consolidated_reg_state (RegistrationStateContext *ctx)
+get_consolidated_reg_state (MMIfaceModem3gpp *self)
{
+ Private *priv;
+
+ priv = get_private (self);
+
/* Some devices (Blackberries for example) will respond to +CGREG, but
* return ERROR for +CREG, probably because their firmware is just stupid.
* So here we prefer the +CREG response, but if we never got a successful
* +CREG response, we'll take +CGREG instead.
*/
- if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
- return ctx->cs;
-
- if (ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
- return ctx->ps;
-
- if (ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
- return ctx->eps;
-
- if (ctx->cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
- return ctx->cs;
-
- if (ctx->ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
- return ctx->ps;
+ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
+ return priv->state_cs;
+ if (priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
+ return priv->state_ps;
+ if (priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
+ return priv->state_eps;
+ if (priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
+ return priv->state_5gs;
+
+ /* Searching? */
+ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
+ priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
+ priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ||
+ priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
+ return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING;
+
+ /* If at least one state is DENIED and the others are UNKNOWN or IDLE, use DENIED */
+ if ((priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED ||
+ priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED ||
+ priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED ||
+ priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) &&
+ REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_cs) &&
+ REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_ps) &&
+ REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_eps) &&
+ REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_5gs))
+ return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED;
+
+ /* Emergency services? */
+ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
+ priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
+ priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY ||
+ priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY)
+ return MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY;
+
+ /* Support for additional registration states reported when on LTE/5GNR.
+ *
+ * For example, we may see the modem registered in LTE (EPS==HOME), and we
+ * may get "SMS only" reported for CS.
+ *
+ * We give these states a very low priority w.r.t. the other ones as they
+ * are really likely never used (i.e. we would get as consolidated the LTE
+ * registration state, not the CS fall back state).
+ *
+ * We also warn in that case, because ideally we should always report the
+ * LTE registration state first, not this one.
+ */
+ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY ||
+ priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY ||
+ priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED ||
+ priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED) {
+ mm_obj_warn (self, "3GPP CSFB registration state is consolidated: %s",
+ mm_modem_3gpp_registration_state_get_string (priv->state_cs));
+ return priv->state_cs;
+ }
- if (ctx->eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
- return ctx->eps;
+ /* Idle? */
+ if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
+ priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
+ priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE ||
+ priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
+ return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE;
- return ctx->cs;
+ return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
}
/*****************************************************************************/
@@ -156,30 +247,25 @@ get_consolidated_reg_state (RegistrationStateContext *ctx)
typedef struct {
MMIfaceModem3gpp *self;
MmGdbusModem3gpp *skeleton;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gchar *operator_id;
- GTimer *timer;
- guint max_registration_time;
+ GCancellable *cancellable;
+ gchar *operator_id;
+ GTimer *timer;
+ guint max_registration_time;
} RegisterInNetworkContext;
static void
-register_in_network_context_complete_and_free (RegisterInNetworkContext *ctx)
+register_in_network_context_free (RegisterInNetworkContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
-
if (ctx->timer)
g_timer_destroy (ctx->timer);
if (ctx->cancellable) {
- RegistrationStateContext *registration_state_context;
+ Private *priv;
/* Clear our cancellable if still around */
- registration_state_context = get_registration_state_context (ctx->self);
- if (registration_state_context->pending_registration_cancellable == ctx->cancellable)
- g_clear_object (&registration_state_context->pending_registration_cancellable);
-
+ priv = get_private (ctx->self);
+ if (priv->pending_registration_cancellable == ctx->cancellable)
+ g_clear_object (&priv->pending_registration_cancellable);
g_object_unref (ctx->cancellable);
}
@@ -191,90 +277,82 @@ register_in_network_context_complete_and_free (RegisterInNetworkContext *ctx)
}
static void
-register_in_network_context_failed (RegisterInNetworkContext *ctx,
- GError *error)
+register_in_network_context_complete_failed (GTask *task,
+ GError *error)
{
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
mm_iface_modem_3gpp_update_cs_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
mm_iface_modem_3gpp_update_ps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
mm_iface_modem_3gpp_update_eps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE);
mm_iface_modem_3gpp_update_access_technologies (ctx->self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
- mm_iface_modem_3gpp_update_location (ctx->self, 0, 0);
+ mm_iface_modem_3gpp_update_location (ctx->self, 0, 0, 0);
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
gboolean
-mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- GError **error)
+mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void run_registration_checks_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- RegisterInNetworkContext *ctx);
-
-static gboolean
-run_registration_checks_again (RegisterInNetworkContext *ctx)
-{
- /* Get fresh registration state */
- mm_iface_modem_3gpp_run_registration_checks (
- ctx->self,
- (GAsyncReadyCallback)run_registration_checks_ready,
- ctx);
- return FALSE;
-}
+static gboolean run_registration_checks (GTask *task);
static void
run_registration_checks_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- RegisterInNetworkContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
- RegistrationStateContext *registration_state_context;
- MMModem3gppRegistrationState current_registration_state;
+ RegisterInNetworkContext *ctx;
+ GError *error = NULL;
+ MMModem3gppRegistrationState current_registration_state;
+
+ ctx = g_task_get_task_data (task);
mm_iface_modem_3gpp_run_registration_checks_finish (MM_IFACE_MODEM_3GPP (self), res, &error);
if (error) {
- mm_dbg ("3GPP registration check failed: '%s'", error->message);
- register_in_network_context_failed (ctx, error);
- register_in_network_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "3GPP registration check failed: %s", error->message);
+ register_in_network_context_complete_failed (task, error);
return;
}
- registration_state_context = get_registration_state_context (ctx->self);
- current_registration_state = get_consolidated_reg_state (registration_state_context);
+ current_registration_state = get_consolidated_reg_state (ctx->self);
/* If we got a final state and it's denied, we can assume the registration is
* finished */
if (current_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) {
- mm_dbg ("Registration denied");
- register_in_network_context_failed (
- ctx,
- mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED));
- register_in_network_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "registration denied");
+ register_in_network_context_complete_failed (
+ task,
+ mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED, self));
return;
}
/* If we got registered, end registration checks */
- if (current_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- current_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
- /* Request immediate access tech update */
- mm_iface_modem_refresh_access_technologies (MM_IFACE_MODEM (ctx->self));
- mm_dbg ("Modem is currently registered in a 3GPP network");
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ if (REG_STATE_IS_REGISTERED (current_registration_state)) {
+ /* Request immediate access tech and signal update: we may have changed
+ * from home to roaming or viceversa, both registered states, so there
+ * wouldn't be an explicit refresh triggered from the modem interface as
+ * the modem never got un-registered during the sequence. */
+ mm_iface_modem_refresh_signal (MM_IFACE_MODEM (ctx->self));
+ mm_obj_dbg (self, "currently registered in a 3GPP network");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Don't spend too much time waiting to get registered */
if (g_timer_elapsed (ctx->timer, NULL) > ctx->max_registration_time) {
- mm_dbg ("3GPP registration check timed out");
- register_in_network_context_failed (
- ctx,
- mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT));
- register_in_network_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "3GPP registration check timed out");
+ register_in_network_context_complete_failed (
+ task,
+ mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self));
return;
}
@@ -284,126 +362,143 @@ run_registration_checks_ready (MMIfaceModem3gpp *self,
* This 3s timeout will catch results from automatic registrations as
* well.
*/
- mm_dbg ("Modem not yet registered in a 3GPP network... will recheck soon");
- g_timeout_add_seconds (3, (GSourceFunc)run_registration_checks_again, ctx);
+ mm_obj_dbg (self, "not yet registered in a 3GPP network... will recheck soon");
+ g_timeout_add_seconds (3, (GSourceFunc)run_registration_checks, task);
+}
+
+static gboolean
+run_registration_checks (GTask *task)
+{
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* Get fresh registration state */
+ mm_iface_modem_3gpp_run_registration_checks (
+ ctx->self,
+ (GAsyncReadyCallback)run_registration_checks_ready,
+ task);
+ return G_SOURCE_REMOVE;
}
static void
register_in_network_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- RegisterInNetworkContext *ctx)
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish (self, res, &error)) {
/* Propagate error when trying to lock to network */
- register_in_network_context_failed (ctx, error);
- register_in_network_context_complete_and_free (ctx);
+ register_in_network_context_complete_failed (task, error);
return;
}
/* Now try to gather current registration status until we're registered or
* the time goes off */
- mm_iface_modem_3gpp_run_registration_checks (
- self,
- (GAsyncReadyCallback)run_registration_checks_ready,
- ctx);
+ run_registration_checks (task);
}
void
-mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- guint max_registration_time,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- RegisterInNetworkContext *ctx;
- const gchar *current_operator_code;
- RegistrationStateContext *registration_state_context;
- GError *error = NULL;
+mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ gboolean force_registration,
+ guint max_registration_time,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ RegisterInNetworkContext *ctx;
+ const gchar *current_operator_code;
+ GError *error = NULL;
+ GTask *task;
+ Private *priv;
+ MMModem3gppRegistrationState reg_state;
+
+ priv = get_private (self);
ctx = g_slice_new0 (RegisterInNetworkContext);
ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_3gpp_register_in_network);
ctx->operator_id = (operator_id && operator_id[0]) ? g_strdup (operator_id) : NULL;
ctx->max_registration_time = max_registration_time;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free);
+
g_object_get (self,
MM_IFACE_MODEM_3GPP_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");
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
/* Validate input MCC/MNC */
- if (ctx->operator_id && !mm_3gpp_parse_operator_id (ctx->operator_id, NULL, NULL, &error)) {
+ if (ctx->operator_id && !mm_3gpp_parse_operator_id (ctx->operator_id, NULL, NULL, NULL, &error)) {
g_assert (error != NULL);
- g_simple_async_result_take_error (ctx->result, error);
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- /* Get registration state context */
- registration_state_context = get_registration_state_context (self);
-
/* (Try to) cancel previous registration request */
- if (registration_state_context->pending_registration_cancellable) {
- g_cancellable_cancel (registration_state_context->pending_registration_cancellable);
- g_clear_object (&registration_state_context->pending_registration_cancellable);
+ if (priv->pending_registration_cancellable) {
+ g_cancellable_cancel (priv->pending_registration_cancellable);
+ g_clear_object (&priv->pending_registration_cancellable);
}
current_operator_code = mm_gdbus_modem3gpp_get_operator_code (ctx->skeleton);
+ reg_state = mm_gdbus_modem3gpp_get_registration_state (ctx->skeleton);
/* Manual registration requested? */
if (ctx->operator_id) {
- /* If already registered with the requested operator, we're done */
- if (current_operator_code &&
- g_str_equal (current_operator_code, ctx->operator_id)) {
- registration_state_context->manual_registration = TRUE;
- mm_dbg ("Already registered in selected network '%s'...",
- current_operator_code);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ /* If already registered manually with the requested operator, we're done */
+ if (!force_registration &&
+ (g_strcmp0 (current_operator_code, ctx->operator_id) == 0) &&
+ REG_STATE_IS_REGISTERED (reg_state) &&
+ priv->manual_registration) {
+ mm_obj_dbg (self, "already registered manually in selected network '%s', manual registration not launched...",
+ current_operator_code);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Manual registration to a new operator required */
- mm_dbg ("Launching manual network registration (%s)...",
- ctx->operator_id);
- registration_state_context->manual_registration = TRUE;
+ mm_obj_dbg (self, "launching manual network registration (%s)...", ctx->operator_id);
+ g_free (priv->manual_registration_operator_id);
+ priv->manual_registration_operator_id = g_strdup (ctx->operator_id);
+ priv->manual_registration = TRUE;
}
/* Automatic registration requested? */
else {
/* If the modem is already registered and the last time it was asked
* automatic registration, we're done */
- if (current_operator_code &&
- !registration_state_context->manual_registration) {
- mm_dbg ("Already registered in network '%s',"
- " automatic registration not launched...",
- current_operator_code);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ if (!force_registration &&
+ REG_STATE_IS_REGISTERED (reg_state) &&
+ !priv->manual_registration) {
+ mm_obj_dbg (self, "already registered automatically in network '%s',"
+ " automatic registration not launched...",
+ current_operator_code ? current_operator_code : "unknown");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Automatic registration to a new operator requested */
- mm_dbg ("Launching automatic network registration...");
- registration_state_context->manual_registration = FALSE;
+ mm_obj_dbg (self, "launching automatic network registration...");
+ g_clear_pointer (&priv->manual_registration_operator_id, g_free);
+ priv->manual_registration = FALSE;
}
ctx->cancellable = g_cancellable_new ();
/* Keep an accessible reference to the cancellable, so that we can cancel
* previous request when needed */
- registration_state_context->pending_registration_cancellable =
- g_object_ref (ctx->cancellable);
+ priv->pending_registration_cancellable = g_object_ref (ctx->cancellable);
ctx->timer = g_timer_new ();
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network (
@@ -411,9 +506,42 @@ mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
ctx->operator_id,
ctx->cancellable,
(GAsyncReadyCallback)register_in_network_ready,
- ctx);
+ task);
+}
+
+/*****************************************************************************/
+/* Request to reregister using the last settings */
+
+#define REREGISTER_IN_NETWORK_TIMEOUT 120
+
+gboolean
+mm_iface_modem_3gpp_reregister_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return mm_iface_modem_3gpp_register_in_network_finish (self, res, error);
+}
+
+void
+mm_iface_modem_3gpp_reregister_in_network (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+
+ /* Relaunch registration using the last used settings */
+ priv = get_private (self);
+ mm_iface_modem_3gpp_register_in_network (self,
+ priv->manual_registration_operator_id,
+ TRUE, /* if already registered with same settings, force re-registration */
+ REREGISTER_IN_NETWORK_TIMEOUT,
+ callback,
+ user_data);
}
+
+/*****************************************************************************/
+
typedef struct {
MmGdbusModem3gpp *skeleton;
GDBusMethodInvocation *invocation;
@@ -438,7 +566,7 @@ handle_register_ready (MMIfaceModem3gpp *self,
{
GError *error = NULL;
- if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish (self, res,&error))
+ if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_modem3gpp_complete_register (ctx->skeleton, ctx->invocation);
@@ -489,6 +617,7 @@ handle_register_auth_ready (MMBaseModem *self,
case MM_MODEM_STATE_REGISTERED:
mm_iface_modem_3gpp_register_in_network (MM_IFACE_MODEM_3GPP (self),
ctx->operator_id,
+ FALSE, /* if already registered with same settings, do nothing */
60,
(GAsyncReadyCallback)handle_register_ready,
ctx);
@@ -520,6 +649,9 @@ handle_register_auth_ready (MMBaseModem *self,
"Cannot register modem: "
"modem is connected");
break;
+
+ default:
+ g_assert_not_reached ();
}
handle_register_context_free (ctx);
@@ -692,6 +824,9 @@ handle_scan_auth_ready (MMBaseModem *self,
(GAsyncReadyCallback)handle_scan_ready,
ctx);
return;
+
+ default:
+ g_assert_not_reached ();
}
handle_scan_context_free (ctx);
@@ -719,6 +854,593 @@ handle_scan (MmGdbusModem3gpp *skeleton,
/*****************************************************************************/
+typedef struct {
+ MmGdbusModem3gpp *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem3gpp *self;
+ MMModem3gppEpsUeModeOperation mode;
+} HandleSetEpsUeModeOperationContext;
+
+static void
+handle_set_eps_ue_mode_operation_context_free (HandleSetEpsUeModeOperationContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetEpsUeModeOperationContext, ctx);
+}
+
+static void
+after_set_load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ HandleSetEpsUeModeOperationContext *ctx)
+{
+ MMModem3gppEpsUeModeOperation uemode;
+ GError *error = NULL;
+
+ uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error);
+ if (error) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+ return;
+ }
+
+ if (uemode != ctx->mode) {
+ g_dbus_method_invocation_return_error_literal (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "UE mode of operation for EPS wasn't updated");
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+ return;
+ }
+
+ mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode);
+ mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+}
+
+static void
+handle_set_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ HandleSetEpsUeModeOperationContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+ return;
+ }
+
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation (
+ self,
+ (GAsyncReadyCallback)after_set_load_eps_ue_mode_operation_ready,
+ ctx);
+ return;
+ }
+
+ /* Assume we're ok */
+ mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+}
+
+static void
+handle_set_eps_ue_mode_operation_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetEpsUeModeOperationContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+ return;
+ }
+
+ /* Check if we already are in the requested mode */
+ if (mm_gdbus_modem3gpp_get_eps_ue_mode_operation (ctx->skeleton) == ctx->mode) {
+ /* Nothing to do */
+ mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation);
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+ return;
+ }
+
+ /* If UE mode update is not implemented, report an error */
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation ||
+ !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set UE mode of operation for EPS: operation not supported");
+ handle_set_eps_ue_mode_operation_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation (
+ MM_IFACE_MODEM_3GPP (self),
+ ctx->mode,
+ (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_ready,
+ ctx);
+}
+
+static gboolean
+handle_set_eps_ue_mode_operation (MmGdbusModem3gpp *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint mode,
+ MMIfaceModem3gpp *self)
+{
+ HandleSetEpsUeModeOperationContext *ctx;
+
+ ctx = g_slice_new (HandleSetEpsUeModeOperationContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->mode = mode;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gpp *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem3gpp *self;
+ GVariant *dictionary;
+ MMBearerProperties *config;
+} HandleSetInitialEpsBearerSettingsContext;
+
+static void
+handle_set_initial_eps_bearer_settings_context_free (HandleSetInitialEpsBearerSettingsContext *ctx)
+{
+ g_clear_object (&ctx->config);
+ g_variant_unref (ctx->dictionary);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetInitialEpsBearerSettingsContext, ctx);
+}
+
+static void
+log_initial_eps_bearer_settings (MMIfaceModem3gpp *self,
+ MMBearerProperties *properties)
+{
+ const gchar *apn;
+ MMBearerAllowedAuth allowed_auth;
+ const gchar *user;
+ const gchar *password;
+ MMBearerIpFamily ip_family;
+
+ apn = mm_bearer_properties_get_apn (properties);
+ if (apn)
+ mm_obj_dbg (self, " APN: '%s'", apn);
+
+ allowed_auth = mm_bearer_properties_get_allowed_auth (properties);
+ if (allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
+ g_autofree gchar *allowed_auth_str = NULL;
+
+ allowed_auth_str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
+ mm_obj_dbg (self, " allowed auth: '%s'", allowed_auth_str);
+ }
+
+ user = mm_bearer_properties_get_user (properties);
+ if (user)
+ mm_obj_dbg (self, " user: '%s'", user);
+
+ password = mm_bearer_properties_get_password (properties);
+ if (password)
+ mm_obj_dbg (self, " password: '%s'", password);
+
+ ip_family = mm_bearer_properties_get_ip_type (properties);
+ if (ip_family != MM_BEARER_IP_FAMILY_NONE) {
+ g_autofree gchar *ip_family_str = NULL;
+
+ ip_family_str = mm_bearer_ip_family_build_string_from_mask (ip_family);
+ mm_obj_dbg (self, " ip family: '%s'", ip_family_str);
+ }
+}
+
+static void
+after_set_load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ HandleSetInitialEpsBearerSettingsContext *ctx)
+{
+ GError *error = NULL;
+ MMBearerProperties *new_config;
+
+ new_config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error);
+ if (error) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ return;
+ }
+
+ mm_obj_dbg (self, "Updated initial EPS bearer settings:");
+ log_initial_eps_bearer_settings (self, new_config);
+
+ if (!mm_bearer_properties_cmp (new_config, ctx->config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) {
+ mm_obj_dbg (self, "Requested initial EPS bearer settings:");
+ log_initial_eps_bearer_settings (self, ctx->config);
+ g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Initial EPS bearer settings were not updated");
+ } else {
+ GVariant *dictionary;
+
+ dictionary = mm_bearer_properties_get_dictionary (new_config);
+ mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary);
+ if (dictionary)
+ g_variant_unref (dictionary);
+ mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ g_object_unref (new_config);
+}
+
+static void
+set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ HandleSetInitialEpsBearerSettingsContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ return;
+ }
+
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings (
+ self,
+ (GAsyncReadyCallback)after_set_load_initial_eps_bearer_settings_ready,
+ ctx);
+ return;
+ }
+
+ /* Assume we're ok */
+ mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation);
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+}
+
+static void
+set_initial_eps_bearer_settings_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetInitialEpsBearerSettingsContext *ctx)
+{
+ GError *error = NULL;
+ MMBearerProperties *old_config = NULL;
+ GVariant *old_dictionary;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ return;
+ }
+
+ /* If UE mode update is not implemented, report an error */
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings ||
+ !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set initial EPS bearer settings: operation not supported");
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ return;
+ }
+
+ ctx->config = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error);
+ if (!ctx->config) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ return;
+ }
+
+ old_dictionary = mm_gdbus_modem3gpp_get_initial_eps_bearer_settings (ctx->skeleton);
+ if (old_dictionary)
+ old_config = mm_bearer_properties_new_from_dictionary (old_dictionary, NULL);
+
+ if (old_config && mm_bearer_properties_cmp (ctx->config, old_config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) {
+ mm_obj_dbg (self, "Skipping initial eps bearer configuration. Same configuration provided");
+ mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation);
+ handle_set_initial_eps_bearer_settings_context_free (ctx);
+ } else {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings (
+ MM_IFACE_MODEM_3GPP (self),
+ ctx->config,
+ (GAsyncReadyCallback)set_initial_eps_bearer_settings_ready,
+ ctx);
+ }
+
+ g_clear_object (&old_config);
+}
+
+static gboolean
+handle_set_initial_eps_bearer_settings (MmGdbusModem3gpp *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary,
+ MMIfaceModem3gpp *self)
+{
+ HandleSetInitialEpsBearerSettingsContext *ctx;
+
+ ctx = g_slice_new0 (HandleSetInitialEpsBearerSettingsContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)set_initial_eps_bearer_settings_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModem3gpp *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem3gpp *self;
+ GVariant *dictionary;
+ MMModem3gppFacility facility;
+ guint8 slot;
+ gchar *control_key;
+} HandleDisableFacilityLockContext;
+
+static void
+handle_disable_facility_lock_context_free (HandleDisableFacilityLockContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_variant_unref (ctx->dictionary);
+ g_free (ctx->control_key);
+ g_free (ctx);
+}
+
+static void
+update_lock_info_ready (MMIfaceModem *modem,
+ GAsyncResult *res,
+ HandleDisableFacilityLockContext *ctx)
+{
+ GError *error = NULL;
+
+ mm_iface_modem_update_lock_info_finish (modem, res, &error);
+ if (error) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_disable_facility_lock_context_free (ctx);
+ return;
+ }
+
+ mm_gdbus_modem3gpp_complete_disable_facility_lock (ctx->skeleton, ctx->invocation);
+ handle_disable_facility_lock_context_free (ctx);
+}
+
+static void
+handle_disable_facility_lock_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ HandleDisableFacilityLockContext *ctx)
+{
+ MMModem3gppFacility facilities;
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_disable_facility_lock_context_free (ctx);
+ return;
+ }
+
+ /* Update the Enabled Facility Locks property in the DBus interface */
+ facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (ctx->skeleton);
+ facilities &= ~ctx->facility;
+ mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities);
+
+ /* Recheck lock status after unlock code has been sent */
+ mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self),
+ MM_MODEM_LOCK_UNKNOWN, /* ask */
+ (GAsyncReadyCallback)update_lock_info_ready,
+ ctx);
+}
+
+static void
+disable_facility_lock_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleDisableFacilityLockContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_disable_facility_lock_context_free (ctx);
+ return;
+ }
+
+ /* If disable facility locks is not implemented, report an error */
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock ||
+ !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot disable facility locks: operation not supported");
+ handle_disable_facility_lock_context_free (ctx);
+ return;
+ }
+
+ /* Parse properties dictionary */
+ if (!g_variant_is_of_type (ctx->dictionary, G_VARIANT_TYPE ("(us)"))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot disable facility locks: invalid parameters");
+ handle_disable_facility_lock_context_free (ctx);
+ return;
+ }
+
+ /* Only modems with single slot or single configuration for all slots are supported */
+ ctx->slot = 1;
+
+ g_variant_get (ctx->dictionary, "(us)", &ctx->facility, &ctx->control_key);
+
+ /* Only four facility locks can be disabled:
+ * - MM_MODEM_3GPP_FACILITY_NET_PERS (network personalization)
+ * - MM_MODEM_3GPP_FACILITY_NET_SUB_PERS (network subset personalization)
+ * - MM_MODEM_3GPP_FACILITY_PROVIDER_PERS (service provider personalization)
+ * - MM_MODEM_3GPP_FACILITY_CORP_PERS (corporate personalization)
+ */
+ if (ctx->facility != (ctx->facility & (MM_MODEM_3GPP_FACILITY_NET_PERS |
+ MM_MODEM_3GPP_FACILITY_NET_SUB_PERS |
+ MM_MODEM_3GPP_FACILITY_PROVIDER_PERS |
+ MM_MODEM_3GPP_FACILITY_CORP_PERS))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Invalid type of facility lock to disable or empty key");
+ handle_disable_facility_lock_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock (
+ MM_IFACE_MODEM_3GPP (self),
+ ctx->facility,
+ ctx->slot,
+ ctx->control_key,
+ (GAsyncReadyCallback)handle_disable_facility_lock_ready,
+ ctx);
+}
+
+static gboolean
+handle_disable_facility_lock (MmGdbusModem3gpp *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary,
+ MMIfaceModem3gpp *self)
+{
+ HandleDisableFacilityLockContext *ctx;
+
+ ctx = g_new0 (HandleDisableFacilityLockContext, 1);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)disable_facility_lock_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Set Packet Service State */
+
+typedef struct {
+ MMIfaceModem3gpp *self;
+ MmGdbusModem3gpp *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMModem3gppPacketServiceState packet_service_state;
+} HandlePacketServiceStateContext;
+
+static void
+handle_set_packet_service_state_context_free (HandlePacketServiceStateContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->self);
+ g_slice_free (HandlePacketServiceStateContext,ctx);
+}
+
+static void
+set_packet_service_state_ready(MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ HandlePacketServiceStateContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem3gpp_complete_set_packet_service_state (ctx->skeleton, ctx->invocation);
+ handle_set_packet_service_state_context_free (ctx);
+}
+
+static void
+set_packet_service_state_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandlePacketServiceStateContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_packet_service_state_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_set_packet_service_state_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->set_packet_service_state ||
+ !MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->set_packet_service_state_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Explicit packet service attach/detach operation not supported");
+ handle_set_packet_service_state_context_free (ctx);
+ return;
+ }
+
+ if ((ctx->packet_service_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) &&
+ (ctx->packet_service_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED)) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid packet service state requested");
+ handle_set_packet_service_state_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state (ctx->self,
+ ctx->packet_service_state,
+ (GAsyncReadyCallback)set_packet_service_state_ready,
+ ctx);
+}
+
+static gboolean
+handle_set_packet_service_state (MmGdbusModem3gpp *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMModem3gppPacketServiceState packet_service_state,
+ MMIfaceModem3gpp *self)
+{
+ HandlePacketServiceStateContext *ctx;
+
+ ctx = g_slice_new (HandlePacketServiceStateContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->packet_service_state = packet_service_state;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)set_packet_service_state_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
gboolean
mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
@@ -733,27 +1455,31 @@ mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- gboolean cs_supported = FALSE;
- gboolean ps_supported = FALSE;
- gboolean eps_supported = FALSE;
+ gboolean is_cs_supported = FALSE;
+ gboolean is_ps_supported = FALSE;
+ gboolean is_eps_supported = FALSE;
+ gboolean is_5gs_supported = FALSE;
g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks != NULL);
g_object_get (self,
- MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &cs_supported,
- MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &ps_supported,
- MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
+ MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &is_cs_supported,
+ MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &is_ps_supported,
+ MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &is_eps_supported,
+ MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, &is_5gs_supported,
NULL);
- mm_dbg ("Running registration checks (CS: '%s', PS: '%s', EPS: '%s')",
- cs_supported ? "yes" : "no",
- ps_supported ? "yes" : "no",
- eps_supported ? "yes" : "no");
+ mm_obj_dbg (self, "running registration checks (CS: '%s', PS: '%s', EPS: '%s', 5GS: '%s')",
+ is_cs_supported ? "yes" : "no",
+ is_ps_supported ? "yes" : "no",
+ is_eps_supported ? "yes" : "no",
+ is_5gs_supported ? "yes" : "no");
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks (self,
- cs_supported,
- ps_supported,
- eps_supported,
+ is_cs_supported,
+ is_ps_supported,
+ is_eps_supported,
+ is_5gs_supported,
callback,
user_data);
}
@@ -761,22 +1487,16 @@ mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
/*****************************************************************************/
typedef struct {
- MMIfaceModem3gpp *self;
MmGdbusModem3gpp *skeleton;
- GSimpleAsyncResult *result;
gboolean operator_code_loaded;
gboolean operator_name_loaded;
- gboolean subscription_state_loaded;
} ReloadCurrentRegistrationInfoContext;
static void
-reload_current_registration_info_context_complete_and_free (ReloadCurrentRegistrationInfoContext *ctx)
+reload_current_registration_info_context_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 (ReloadCurrentRegistrationInfoContext, ctx);
}
@@ -785,22 +1505,25 @@ mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *s
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void reload_current_registration_info_context_step (ReloadCurrentRegistrationInfoContext *ctx);
+static void reload_current_registration_info_context_step (GTask *task);
static void
load_operator_name_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- ReloadCurrentRegistrationInfoContext *ctx)
+ GTask *task)
{
+ ReloadCurrentRegistrationInfoContext *ctx;
GError *error = NULL;
gchar *str;
+ ctx = g_task_get_task_data (task);
+
str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish (self, res, &error);
if (error) {
- mm_warn ("Couldn't load Operator Name: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load operator name: %s", error->message);
g_error_free (error);
}
@@ -809,123 +1532,72 @@ load_operator_name_ready (MMIfaceModem3gpp *self,
g_free (str);
ctx->operator_name_loaded = TRUE;
- reload_current_registration_info_context_step (ctx);
-}
-
-static gboolean
-parse_mcc_mnc (const gchar *mccmnc,
- guint *mcc_out,
- guint *mnc_out)
-{
- guint mccmnc_len;
- gchar mcc[4] = { 0, 0, 0, 0 };
- gchar mnc[4] = { 0, 0, 0, 0 };
-
- mccmnc_len = (mccmnc ? strlen (mccmnc) : 0);
- if (mccmnc_len != 5 &&
- mccmnc_len != 6) {
- mm_dbg ("Unexpected MCC/MNC string '%s'", mccmnc);
- return FALSE;
- }
-
- memcpy (mcc, mccmnc, 3);
- /* Not all modems report 6-digit MNCs */
- memcpy (mnc, mccmnc + 3, 2);
- if (mccmnc_len == 6)
- mnc[2] = mccmnc[5];
-
- *mcc_out = atoi (mcc);
- *mnc_out = atoi (mnc);
-
- return TRUE;
+ reload_current_registration_info_context_step (task);
}
static void
load_operator_code_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- ReloadCurrentRegistrationInfoContext *ctx)
+ GTask *task)
{
+ ReloadCurrentRegistrationInfoContext *ctx;
GError *error = NULL;
gchar *str;
+ ctx = g_task_get_task_data (task);
+
str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish (self, res, &error);
if (error) {
- mm_warn ("Couldn't load Operator Code: '%s'", error->message);
- g_error_free (error);
+ mm_obj_warn (self, "couldn't load operator code: %s", error->message);
+ } else if (!mm_3gpp_parse_operator_id (str, NULL, NULL, NULL, &error)) {
+ mm_obj_dbg (self, "unexpected operator code string '%s': %s", str, error->message);
+ g_clear_pointer (&str, g_free);
}
+ g_clear_error (&error);
if (ctx->skeleton)
mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, str);
/* If we also implement the location interface, update the 3GPP location */
- if (str && MM_IS_IFACE_MODEM_LOCATION (self)) {
- guint mcc = 0;
- guint mnc = 0;
+ if (str && MM_IS_IFACE_MODEM_LOCATION (self))
+ mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), str);
- if (parse_mcc_mnc (str, &mcc, &mnc))
- mm_iface_modem_location_3gpp_update_mcc_mnc (MM_IFACE_MODEM_LOCATION (self), mcc, mnc);
- }
g_free (str);
ctx->operator_code_loaded = TRUE;
- reload_current_registration_info_context_step (ctx);
+ reload_current_registration_info_context_step (task);
}
static void
-load_subscription_state_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- ReloadCurrentRegistrationInfoContext *ctx)
+reload_current_registration_info_context_step (GTask *task)
{
- 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);
-}
+ MMIfaceModem3gpp *self;
+ ReloadCurrentRegistrationInfoContext *ctx;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
-static void
-reload_current_registration_info_context_step (ReloadCurrentRegistrationInfoContext *ctx)
-{
if (!ctx->operator_code_loaded) {
/* Launch operator code update */
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_operator_code (
- ctx->self,
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code (
+ self,
(GAsyncReadyCallback)load_operator_code_ready,
- ctx);
+ task);
return;
}
if (!ctx->operator_name_loaded) {
/* Launch operator name update */
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_operator_name (
- ctx->self,
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name (
+ self,
(GAsyncReadyCallback)load_operator_name_ready,
- ctx);
- return;
- }
-
- 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);
+ task);
return;
}
/* If all are loaded, all done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- reload_current_registration_info_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -934,23 +1606,22 @@ mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self,
gpointer user_data)
{
ReloadCurrentRegistrationInfoContext *ctx;
+ GTask *task;
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_registration_info);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)reload_current_registration_info_context_free);
g_object_get (self,
MM_IFACE_MODEM_3GPP_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");
- reload_current_registration_info_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
@@ -959,7 +1630,7 @@ mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self,
if (ctx->operator_code_loaded) {
mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, NULL);
if (MM_IS_IFACE_MODEM_LOCATION (self))
- mm_iface_modem_location_3gpp_update_mcc_mnc (MM_IFACE_MODEM_LOCATION (self), 0, 0);
+ mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL);
}
ctx->operator_name_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name &&
@@ -967,12 +1638,7 @@ mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self,
if (ctx->operator_name_loaded)
mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, NULL);
- 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);
+ reload_current_registration_info_context_step (task);
}
void
@@ -989,33 +1655,19 @@ mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self)
mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL);
mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL);
if (MM_IS_IFACE_MODEM_LOCATION (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);
+ mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL);
}
/*****************************************************************************/
void
-mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
- MMModemAccessTechnology access_tech)
+mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
+ MMModemAccessTechnology access_tech)
{
- MMModem3gppRegistrationState state;
- RegistrationStateContext *ctx;
+ Private *priv;
+ MMModem3gppRegistrationState state;
- ctx = get_registration_state_context (self);
- g_assert (ctx);
+ priv = get_private (self);
g_object_get (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state,
@@ -1023,9 +1675,7 @@ mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
/* Even if registration state didn't change, report access technology,
* but only if something valid to report */
- if (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING ||
- ctx->reloading_registration_info) {
+ if (REG_STATE_IS_REGISTERED (state) || priv->reloading_registration_info) {
if (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN)
mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
access_tech,
@@ -1038,10 +1688,14 @@ mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self,
void
mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self,
- gulong location_area_code,
- gulong cell_id)
+ gulong location_area_code,
+ gulong tracking_area_code,
+ gulong cell_id)
{
- MMModem3gppRegistrationState state;
+ Private *priv;
+ MMModem3gppRegistrationState state;
+
+ priv = get_private (self);
if (!MM_IS_IFACE_MODEM_LOCATION (self))
return;
@@ -1051,13 +1705,15 @@ mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self,
NULL);
/* Even if registration state didn't change, report access technology or
- * location updates, but only if something valid to report */
- if (state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
- if (location_area_code > 0 && cell_id > 0)
- mm_iface_modem_location_3gpp_update_lac_ci (MM_IFACE_MODEM_LOCATION (self),
- location_area_code,
- cell_id);
+ * location updates, but only if something valid to report. For the case
+ * where we're registering (loading current registration info after a state
+ * change to registered), we also allow LAC/CID updates. */
+ if (REG_STATE_IS_REGISTERED (state) || priv->reloading_registration_info) {
+ if (location_area_code || tracking_area_code || cell_id)
+ mm_iface_modem_location_3gpp_update_lac_tac_ci (MM_IFACE_MODEM_LOCATION (self),
+ location_area_code,
+ tracking_area_code,
+ cell_id);
} else
mm_iface_modem_location_3gpp_clear (MM_IFACE_MODEM_LOCATION (self));
}
@@ -1065,18 +1721,43 @@ mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self,
/*****************************************************************************/
static void
+update_packet_service_state (MMIfaceModem3gpp *self,
+ MMModem3gppPacketServiceState state)
+{
+ g_autoptr(MmGdbusModem3gppSkeleton) skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton)
+ mm_gdbus_modem3gpp_set_packet_service_state (MM_GDBUS_MODEM3GPP (skeleton), state);
+}
+
+/*****************************************************************************/
+
+static void
update_registration_reload_current_registration_info_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- gpointer user_data)
+ GAsyncResult *res,
+ gpointer user_data)
{
- MMModem3gppRegistrationState new_state;
- RegistrationStateContext *ctx;
+ Private *priv;
+ MMModem3gppRegistrationState new_state;
+
+ priv = get_private (self);
new_state = GPOINTER_TO_UINT (user_data);
- mm_info ("Modem %s: 3GPP Registration state changed (registering -> %s)",
- g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
- mm_modem_3gpp_registration_state_get_string (new_state));
+ mm_obj_info (self, "3GPP registration state changed (registering -> %s)",
+ mm_modem_3gpp_registration_state_get_string (new_state));
+ mm_obj_dbg (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'",
+ mm_modem_3gpp_registration_state_get_string (priv->state_cs),
+ mm_modem_3gpp_registration_state_get_string (priv->state_ps),
+ mm_modem_3gpp_registration_state_get_string (priv->state_eps),
+ mm_modem_3gpp_registration_state_get_string (priv->state_5gs),
+ mm_modem_3gpp_registration_state_get_string (new_state));
+
+ /* Packet service state refresh */
+ update_packet_service_state (self, get_consolidated_packet_service_state (self));
/* The property in the interface is bound to the property
* in the skeleton, so just updating here is enough */
@@ -1089,25 +1770,19 @@ update_registration_reload_current_registration_info_ready (MMIfaceModem3gpp *se
MM_MODEM_STATE_REGISTERED,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
- ctx = get_registration_state_context (self);
- ctx->reloading_registration_info = FALSE;
+ priv->reloading_registration_info = FALSE;
}
static void
-update_non_registered_state (MMIfaceModem3gpp *self,
- MMModem3gppRegistrationState old_state,
- MMModem3gppRegistrationState new_state)
+update_non_registered_state (MMIfaceModem3gpp *self,
+ MMModem3gppRegistrationState old_state,
+ MMModem3gppRegistrationState new_state)
{
/* 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);
+ /* Packet service detached */
+ update_packet_service_state (self, MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED);
/* The property in the interface is bound to the property
* in the skeleton, so just updating here is enough */
@@ -1125,37 +1800,51 @@ update_non_registered_state (MMIfaceModem3gpp *self,
}
static void
-update_registration_state (MMIfaceModem3gpp *self,
- MMModem3gppRegistrationState new_state,
- gboolean deferrable)
+update_registration_state (MMIfaceModem3gpp *self,
+ MMModem3gppRegistrationState new_state,
+ gboolean deferrable)
{
- MMModem3gppRegistrationState old_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- RegistrationStateContext *ctx;
+ Private *priv;
+ MMModem3gppRegistrationState old_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
+
+ priv = get_private (self);
g_object_get (self,
MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &old_state,
NULL);
- ctx = get_registration_state_context (self);
- g_assert (ctx);
-
/* Only set new state if different */
if (new_state == old_state)
return;
- if (new_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME ||
- new_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
+ if (REG_STATE_IS_REGISTERED (new_state)) {
+ MMModemState modem_state;
+
/* If already reloading registration info, skip it */
- if (ctx->reloading_registration_info)
+ if (priv->reloading_registration_info)
+ return;
+
+ /* If the modem isn't already enabled, this registration state update
+ * could be due to a previously scheduled initial registration check
+ * when the modem was being enabled. We need to ignore it as otherwise
+ * it may cause an incorrect transition of the registration state and
+ * modem state when the modem is being disabled or still going through
+ * enable steps */
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (modem_state < MM_MODEM_STATE_ENABLED) {
+ mm_obj_dbg (self, "3GPP registration state change ignored as modem isn't enabled");
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));
+ mm_obj_info (self, "3GPP registration state changed (%s -> registering)",
+ mm_modem_3gpp_registration_state_get_string (old_state));
/* Reload current registration info. ONLY update the state to REGISTERED
* after having loaded operator code/name/subscription state */
- ctx->reloading_registration_info = TRUE;
+ priv->reloading_registration_info = TRUE;
mm_iface_modem_3gpp_reload_current_registration_info (
self,
(GAsyncReadyCallback)update_registration_reload_current_registration_info_ready,
@@ -1163,20 +1852,25 @@ update_registration_state (MMIfaceModem3gpp *self,
return;
}
- mm_info ("Modem %s: 3GPP Registration state changed (%s -> %s)",
- g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
- mm_modem_3gpp_registration_state_get_string (old_state),
- mm_modem_3gpp_registration_state_get_string (new_state));
+ mm_obj_info (self, "3GPP registration state changed (%s -> %s)",
+ mm_modem_3gpp_registration_state_get_string (old_state),
+ mm_modem_3gpp_registration_state_get_string (new_state));
+ mm_obj_dbg (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'",
+ mm_modem_3gpp_registration_state_get_string (priv->state_cs),
+ mm_modem_3gpp_registration_state_get_string (priv->state_ps),
+ mm_modem_3gpp_registration_state_get_string (priv->state_eps),
+ mm_modem_3gpp_registration_state_get_string (priv->state_5gs),
+ mm_modem_3gpp_registration_state_get_string (new_state));
update_non_registered_state (self, old_state, new_state);
}
void
-mm_iface_modem_3gpp_update_cs_registration_state (MMIfaceModem3gpp *self,
- MMModem3gppRegistrationState state)
+mm_iface_modem_3gpp_update_cs_registration_state (MMIfaceModem3gpp *self,
+ MMModem3gppRegistrationState state)
{
- RegistrationStateContext *ctx;
- gboolean supported = FALSE;
+ Private *priv;
+ gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &supported,
@@ -1185,17 +1879,17 @@ mm_iface_modem_3gpp_update_cs_registration_state (MMIfaceModem3gpp *self,
if (!supported)
return;
- ctx = get_registration_state_context (self);
- ctx->cs = state;
- update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
+ priv = get_private (self);
+ priv->state_cs = state;
+ update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
void
-mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self,
- MMModem3gppRegistrationState state)
+mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self,
+ MMModem3gppRegistrationState state)
{
- RegistrationStateContext *ctx;
- gboolean supported = FALSE;
+ Private *priv;
+ gboolean supported = FALSE;
g_object_get (self,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &supported,
@@ -1204,16 +1898,16 @@ mm_iface_modem_3gpp_update_ps_registration_state (MMIfaceModem3gpp *self,
if (!supported)
return;
- ctx = get_registration_state_context (self);
- ctx->ps = state;
- update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
+ priv = get_private (self);
+ priv->state_ps = state;
+ update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
void
-mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
- MMModem3gppRegistrationState state)
+mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
+ MMModem3gppRegistrationState state)
{
- RegistrationStateContext *ctx;
+ Private *priv;
gboolean supported = FALSE;
g_object_get (self,
@@ -1223,127 +1917,224 @@ mm_iface_modem_3gpp_update_eps_registration_state (MMIfaceModem3gpp *self,
if (!supported)
return;
- ctx = get_registration_state_context (self);
- ctx->eps = state;
- update_registration_state (self, get_consolidated_reg_state (ctx), TRUE);
+ priv = get_private (self);
+ priv->state_eps = state;
+ update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
void
-mm_iface_modem_3gpp_update_subscription_state (MMIfaceModem3gpp *self,
- MMModem3gppSubscriptionState state)
+mm_iface_modem_3gpp_update_5gs_registration_state (MMIfaceModem3gpp *self,
+ MMModem3gppRegistrationState state)
{
- MmGdbusModem3gpp *skeleton = NULL;
+ Private *priv;
+ gboolean supported = FALSE;
g_object_get (self,
- MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+ MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, &supported,
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);
- }
+
+ if (!supported)
+ return;
+
+ priv = get_private (self);
+ priv->state_5gs = state;
+ update_registration_state (self, get_consolidated_reg_state (self), TRUE);
}
/*****************************************************************************/
+/* Periodic registration checks */
-typedef struct {
- guint timeout_source;
- gboolean running;
-} RegistrationCheckContext;
-
-static void
-registration_check_context_free (RegistrationCheckContext *ctx)
-{
- if (ctx->timeout_source)
- g_source_remove (ctx->timeout_source);
- g_free (ctx);
-}
+#define REGISTRATION_CHECK_TIMEOUT_SEC 30
static void
periodic_registration_checks_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res)
+ GAsyncResult *res)
{
- RegistrationCheckContext *ctx;
- GError *error = NULL;
+ Private *priv;
+ GError *error = NULL;
+
+ priv = get_private (self);
mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error);
if (error) {
- mm_dbg ("Couldn't refresh 3GPP registration status: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't refresh 3GPP registration status: %s", error->message);
g_error_free (error);
}
- /* Remove the running tag */
- ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark);
- if (ctx)
- ctx->running = FALSE;
+ priv->check_running = FALSE;
}
static gboolean
periodic_registration_check (MMIfaceModem3gpp *self)
{
- RegistrationCheckContext *ctx;
+ Private *priv;
+
+ priv = get_private (self);
/* Only launch a new one if not one running already */
- ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark);
- if (!ctx->running) {
- ctx->running = TRUE;
+ if (!priv->check_running) {
+ priv->check_running = TRUE;
mm_iface_modem_3gpp_run_registration_checks (
self,
(GAsyncReadyCallback)periodic_registration_checks_ready,
NULL);
}
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static void
periodic_registration_check_disable (MMIfaceModem3gpp *self)
{
- if (G_UNLIKELY (!registration_check_context_quark))
- registration_check_context_quark = (g_quark_from_static_string (
- REGISTRATION_CHECK_CONTEXT_TAG));
+ Private *priv;
- /* Overwriting the data will free the previous context */
- g_object_set_qdata (G_OBJECT (self),
- registration_check_context_quark,
- NULL);
+ priv = get_private (self);
- mm_dbg ("Periodic 3GPP registration checks disabled");
+ /* Do nothing if already disabled */
+ if (!priv->check_timeout_source)
+ return;
+
+ g_source_remove (priv->check_timeout_source);
+ priv->check_timeout_source = 0;
+
+ mm_obj_dbg (self, "periodic 3GPP registration checks disabled");
}
static void
periodic_registration_check_enable (MMIfaceModem3gpp *self)
{
- RegistrationCheckContext *ctx;
-
- if (G_UNLIKELY (!registration_check_context_quark))
- registration_check_context_quark = (g_quark_from_static_string (
- REGISTRATION_CHECK_CONTEXT_TAG));
+ Private *priv;
- ctx = g_object_get_qdata (G_OBJECT (self), registration_check_context_quark);
+ priv = get_private (self);
- /* If context is already there, we're already enabled */
- if (ctx)
+ /* Do nothing if already enabled */
+ if (priv->check_timeout_source)
return;
/* Create context and keep it as object data */
- mm_dbg ("Periodic 3GPP registration checks enabled");
- ctx = g_new0 (RegistrationCheckContext, 1);
- ctx->timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC,
- (GSourceFunc)periodic_registration_check,
- self);
- g_object_set_qdata_full (G_OBJECT (self),
- registration_check_context_quark,
- ctx,
- (GDestroyNotify)registration_check_context_free);
+ mm_obj_dbg (self, "periodic 3GPP registration checks enabled");
+ priv->check_timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC,
+ (GSourceFunc)periodic_registration_check,
+ self);
+}
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_3gpp_update_pco_list (MMIfaceModem3gpp *self,
+ const GList *pco_list)
+{
+ MmGdbusModem3gpp *skeleton = NULL;
+ GVariantBuilder builder;
+ GVariant *variant;
+ const GList *iter;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ubay)"));
+ for (iter = pco_list; iter; iter = g_list_next (iter)) {
+ g_variant_builder_add_value (&builder,
+ mm_pco_to_variant (MM_PCO (iter->data)));
+ }
+ variant = g_variant_ref_sink (g_variant_builder_end (&builder));
+ mm_gdbus_modem3gpp_set_pco (skeleton, variant);
+ g_variant_unref (variant);
+ g_object_unref (skeleton);
+}
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_3gpp_update_initial_eps_bearer (MMIfaceModem3gpp *self,
+ MMBearerProperties *properties)
+{
+ MmGdbusModem3gpp *skeleton = NULL;
+ MMBaseBearer *old_bearer = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton,
+ MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, &old_bearer,
+ NULL);
+ g_assert (skeleton);
+
+ /* skip update? */
+ if ((!old_bearer && !properties) ||
+ (old_bearer && properties &&
+ mm_bearer_properties_cmp (properties,
+ mm_base_bearer_peek_config (MM_BASE_BEARER (old_bearer)),
+ MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)))
+ goto out;
+
+ if (properties) {
+ MMBaseBearer *new_bearer;
+
+ mm_obj_dbg (self, "updating initial EPS bearer...");
+ g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer);
+ new_bearer = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer (self, properties);
+ g_object_set (self,
+ MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, new_bearer,
+ NULL);
+ mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, mm_base_bearer_get_path (new_bearer));
+ g_object_unref (new_bearer);
+ } else {
+ mm_obj_dbg (self, "clearing initial EPS bearer...");
+ g_object_set (self,
+ MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, NULL,
+ NULL);
+ mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL);
+ }
+
+out:
+ g_clear_object (&old_bearer);
+ g_object_unref (skeleton);
+}
+
+static void
+reload_initial_eps_bearer_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res)
+{
+ g_autoptr(MMBearerProperties) properties = NULL;
+ g_autoptr(GError) error = NULL;
+
+ properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error);
+ if (!properties) {
+ mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message);
+ return;
+ }
+
+ mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties);
+}
+
+void
+mm_iface_modem_3gpp_reload_initial_eps_bearer (MMIfaceModem3gpp *self)
+{
+ gboolean eps_supported = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
+ NULL);
+
+ if (eps_supported &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer (
+ self,
+ (GAsyncReadyCallback)reload_initial_eps_bearer_ready,
+ NULL);
+ }
}
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
+ DISABLING_STEP_INITIAL_EPS_BEARER,
DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS,
DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS,
DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS,
@@ -1354,18 +2145,13 @@ typedef enum {
} DisablingStep;
struct _DisablingContext {
- MMIfaceModem3gpp *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModem *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -1376,7 +2162,7 @@ mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
#undef VOID_REPLY_READY_FN
@@ -1384,19 +2170,21 @@ mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self,
static void \
NAME##_ready (MMIfaceModem3gpp *self, \
GAsyncResult *res, \
- DisablingContext *ctx) \
+ GTask *task) \
{ \
+ DisablingContext *ctx; \
GError *error = NULL; \
\
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->NAME##_finish (self, res, &error); \
if (error) { \
- mm_dbg ("Couldn't %s: '%s'", DISPLAY, error->message); \
+ mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
+ ctx = g_task_get_task_data (task); \
ctx->step++; \
- interface_disabling_step (ctx); \
+ interface_disabling_step (task); \
}
VOID_REPLY_READY_FN (cleanup_unsolicited_events,
@@ -1409,93 +2197,106 @@ VOID_REPLY_READY_FN (disable_unsolicited_registration_events,
"disable unsolicited registration events")
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModem3gpp *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_INITIAL_EPS_BEARER:
+ mm_iface_modem_3gpp_update_initial_eps_bearer (self, NULL);
+ ctx->step++;
+ /* fall through */
case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS:
/* Disable periodic registration checks, if they were set */
- periodic_registration_check_disable (ctx->self);
- /* Fall down to next step */
+ periodic_registration_check_disable (self);
ctx->step++;
+ /* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS: {
gboolean cs_supported = FALSE;
gboolean ps_supported = FALSE;
gboolean eps_supported = FALSE;
- g_object_get (ctx->self,
+ g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &cs_supported,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &ps_supported,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->disable_unsolicited_registration_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->disable_unsolicited_registration_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->disable_unsolicited_registration_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events (
+ self,
cs_supported,
ps_supported,
eps_supported,
(GAsyncReadyCallback)disable_unsolicited_registration_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->cleanup_unsolicited_registration_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->cleanup_unsolicited_registration_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->cleanup_unsolicited_registration_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events (
+ self,
(GAsyncReadyCallback)cleanup_unsolicited_registration_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->disable_unsolicited_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->disable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_REGISTRATION_STATE:
- update_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, FALSE);
- mm_iface_modem_3gpp_update_access_technologies (ctx->self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
- mm_iface_modem_3gpp_update_location (ctx->self, 0, 0);
- /* Fall down to next step */
+ update_registration_state (self, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, FALSE);
+ mm_iface_modem_3gpp_update_access_technologies (self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
+ mm_iface_modem_3gpp_update_location (self, 0, 0, 0);
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
g_assert_not_reached ();
@@ -1507,33 +2308,33 @@ mm_iface_modem_3gpp_disable (MMIfaceModem3gpp *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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_3gpp_disable);
ctx->step = DISABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_3GPP_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
@@ -1541,249 +2342,268 @@ typedef enum {
ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS,
ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS,
- ENABLING_STEP_RUN_REGISTRATION_CHECKS,
+ ENABLING_STEP_INITIAL_EPS_BEARER,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
- MMIfaceModem3gpp *self;
EnablingStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModem3gpp *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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_3gpp_enable_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Setting up unsolicited events failed: '%s'", error->message);
+ mm_obj_dbg (self, "setting up unsolicited events failed: %s", error->message);
g_error_free (error);
/* If we get an error setting up unsolicited events, don't even bother trying to
* enable them. */
ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS + 1;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
return;
}
/* Go on to next step */
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Enabling unsolicited events failed: '%s'", error->message);
+ mm_obj_dbg (self, "enabling unsolicited events failed: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
setup_unsolicited_registration_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Setting up unsolicited registration events failed: '%s'", error->message);
+ mm_obj_dbg (self, "setting up unsolicited registration events failed: %s", error->message);
g_error_free (error);
/* If we get an error setting up unsolicited events, don't even bother trying to
* enable them. */
ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS + 1;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
/* If error, setup periodic registration checks */
- periodic_registration_check_enable (ctx->self);
+ periodic_registration_check_enable (self);
return;
}
/* Go on to next step */
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
enable_unsolicited_registration_events_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Enabling unsolicited registration events failed: '%s'", error->message);
+ mm_obj_dbg (self, "enabling unsolicited registration events failed: %s", error->message);
g_error_free (error);
/* If error, setup periodic registration checks */
- periodic_registration_check_enable (ctx->self);
+ periodic_registration_check_enable (self);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-run_all_registration_checks_ready (MMIfaceModem3gpp *self,
- GAsyncResult *res,
- EnablingContext *ctx)
+load_initial_eps_bearer_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ MMBearerProperties *properties;
+ EnablingContext *ctx;
+ GError *error = NULL;
- mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error);
- if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
- return;
+ ctx = g_task_get_task_data (task);
+
+ properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error);
+ if (!properties) {
+ mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message);
+ g_error_free (error);
+ goto out;
}
+ mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties);
+ g_object_unref (properties);
+
+out:
/* Go on to next step */
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModem3gpp *self;
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->setup_unsolicited_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->setup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->enable_unsolicited_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->enable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->setup_unsolicited_registration_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->setup_unsolicited_registration_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->setup_unsolicited_registration_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events (
+ self,
(GAsyncReadyCallback)setup_unsolicited_registration_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS: {
gboolean cs_supported = FALSE;
gboolean ps_supported = FALSE;
gboolean eps_supported = FALSE;
- g_object_get (ctx->self,
+ g_object_get (self,
MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, &cs_supported,
MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, &ps_supported,
MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
NULL);
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->enable_unsolicited_registration_events &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->enable_unsolicited_registration_events_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->enable_unsolicited_registration_events (
- ctx->self,
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events (
+ self,
cs_supported,
ps_supported,
eps_supported,
(GAsyncReadyCallback)enable_unsolicited_registration_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall through */
- case ENABLING_STEP_RUN_REGISTRATION_CHECKS:
- mm_iface_modem_3gpp_run_registration_checks (
- ctx->self,
- (GAsyncReadyCallback)run_all_registration_checks_ready,
- ctx);
- return;
+ case ENABLING_STEP_INITIAL_EPS_BEARER: {
+ gboolean eps_supported = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
+ NULL);
+
+ if (eps_supported &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer (
+ self,
+ (GAsyncReadyCallback)load_initial_eps_bearer_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ } /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1796,75 +2616,222 @@ mm_iface_modem_3gpp_enable (MMIfaceModem3gpp *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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_3gpp_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_3GPP_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef struct _SyncingContext SyncingContext;
+static void interface_syncing_step (GTask *task);
+
+typedef enum {
+ SYNCING_STEP_FIRST,
+ SYNCING_STEP_REFRESH_3GPP_REGISTRATION,
+ SYNCING_STEP_REFRESH_EPS_BEARER,
+ SYNCING_STEP_LAST
+} SyncingStep;
+
+struct _SyncingContext {
+ SyncingStep step;
+};
+
+gboolean
+mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sync_eps_bearer_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ g_autoptr(MMBearerProperties) properties = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error);
+ if (!properties)
+ mm_obj_dbg (self, "couldn't refresh initial EPS bearer status: %s", error->message);
+ else
+ mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+sync_eps_bearer (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ gboolean eps_supported = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, &eps_supported,
+ NULL);
+
+ /* Refresh EPS bearer if supported */
+ if (eps_supported &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer (
+ self,
+ callback,
+ task);
+ return;
+ }
+
+ /* If EPS is unsupported, just go to the next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+sync_registration_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ g_autoptr (GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error))
+ mm_obj_dbg (self, "couldn't synchronize 3GPP registration: %s", error->message);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_syncing_step(task);
+}
+
+static void
+interface_syncing_step (GTask *task)
+{
+ MMIfaceModem3gpp *self;
+ SyncingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SYNCING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_REFRESH_3GPP_REGISTRATION:
+ /*
+ * Refresh registration info to verify that the modem is still registered.
+ * Wait until registration checks are complete before going to the next step.
+ */
+ mm_iface_modem_3gpp_run_registration_checks (
+ self,
+ (GAsyncReadyCallback)sync_registration_ready,
+ task);
+ return;
+
+ case SYNCING_STEP_REFRESH_EPS_BEARER:
+ /*
+ * Refresh EPS bearer and wait until complete.
+ * We want to make sure that the modem is fully enabled again
+ * when we refresh the mobile data connection bearers.
+ */
+ sync_eps_bearer (
+ self,
+ (GAsyncReadyCallback)sync_eps_bearer_ready,
+ task);
+ return;
+
+ case SYNCING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncingContext *ctx;
+ GTask *task;
+
+ /* Create SyncingContext */
+ ctx = g_new0 (SyncingContext, 1);
+ ctx->step = SYNCING_STEP_FIRST;
+
+ /* Create sync steps task and execute it */
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
+ interface_syncing_step (task);
+}
+
+#endif
+
+/*****************************************************************************/
+
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
- INITIALIZATION_STEP_IMEI,
INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS,
+ INITIALIZATION_STEP_TEST_LOCKED,
+ INITIALIZATION_STEP_IMEI,
+ INITIALIZATION_STEP_EPS_UE_MODE_OPERATION,
+ INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS,
+ INITIALIZATION_STEP_CONNECT_SIGNALS,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
- MMIfaceModem3gpp *self;
MmGdbusModem3gpp *skeleton;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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
sim_pin_lock_enabled_cb (MMBaseSim *self,
gboolean enabled,
@@ -1882,18 +2849,75 @@ sim_pin_lock_enabled_cb (MMBaseSim *self,
}
static void
+load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ MMBearerProperties *config;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error);
+ if (!config) {
+ mm_obj_warn (self, "couldn't load initial EPS bearer settings: %s", error->message);
+ g_error_free (error);
+ } else {
+ GVariant *dictionary;
+
+ dictionary = mm_bearer_properties_get_dictionary (config);
+ mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary);
+ g_object_unref (config);
+ if (dictionary)
+ g_variant_unref (dictionary);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ MMModem3gppEpsUeModeOperation uemode;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error);
+ mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode);
+
+ if (error) {
+ mm_obj_warn (self, "couldn't load UE mode of operation for EPS: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
load_enabled_facility_locks_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
MMModem3gppFacility facilities;
+ ctx = g_task_get_task_data (task);
+
facilities = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish (self, res, &error);
mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities);
if (error) {
- mm_warn ("couldn't load facility locks: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load facility locks: %s", error->message);
g_error_free (error);
} else {
MMBaseSim *sim = NULL;
@@ -1914,92 +2938,166 @@ load_enabled_facility_locks_ready (MMIfaceModem3gpp *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_imei_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
gchar *imei;
+ ctx = g_task_get_task_data (task);
+
imei = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish (self, res, &error);
mm_gdbus_modem3gpp_set_imei (ctx->skeleton, imei);
g_free (imei);
if (error) {
- mm_warn ("couldn't load IMEI: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load IMEI: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModem3gpp *self;
+ InitializationContext *ctx;
+ MMModemState modem_state;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS:
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks (
+ self,
+ (GAsyncReadyCallback)load_enabled_facility_locks_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_TEST_LOCKED:
+ modem_state = MM_MODEM_STATE_UNKNOWN;
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &modem_state,
+ NULL);
+ if (modem_state == MM_MODEM_STATE_LOCKED) {
+ /* Skip some steps and export the interface if modem is locked */
+ ctx->step = INITIALIZATION_STEP_LAST;
+ interface_initialization_step (task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_IMEI:
/* IMEI value is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have it loaded,
* don't try to load it again. */
if (!mm_gdbus_modem3gpp_get_imei (ctx->skeleton) &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_imei &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_imei_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_imei (
- ctx->self,
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei (
+ self,
(GAsyncReadyCallback)load_imei_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
-
- case INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS:
- if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_enabled_facility_locks &&
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_enabled_facility_locks_finish) {
- MM_IFACE_MODEM_3GPP_GET_INTERFACE (ctx->self)->load_enabled_facility_locks (
- ctx->self,
- (GAsyncReadyCallback)load_enabled_facility_locks_ready,
- ctx);
+ /* fall through */
+
+ case INITIALIZATION_STEP_EPS_UE_MODE_OPERATION:
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation (
+ self,
+ (GAsyncReadyCallback)load_eps_ue_mode_operation_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS:
+ if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings &&
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) {
+ MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings (
+ self,
+ (GAsyncReadyCallback)load_initial_eps_bearer_settings_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
- case INITIALIZATION_STEP_LAST:
+ case INITIALIZATION_STEP_CONNECT_SIGNALS:
/* We are done without errors! */
/* Handle method invocations */
g_signal_connect (ctx->skeleton,
"handle-register",
G_CALLBACK (handle_register),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-scan",
G_CALLBACK (handle_scan),
- ctx->self);
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-set-eps-ue-mode-operation",
+ G_CALLBACK (handle_set_eps_ue_mode_operation),
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-set-initial-eps-bearer-settings",
+ G_CALLBACK (handle_set_initial_eps_bearer_settings),
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-set-packet-service-state",
+ G_CALLBACK (handle_set_packet_service_state),
+ self);
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_LAST:
+ /* Always connect the signal to unlock modem */
+ g_signal_connect (ctx->skeleton,
+ "handle-disable-facility-lock",
+ G_CALLBACK (handle_disable_facility_lock),
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM3GPP (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -2010,7 +3108,7 @@ mm_iface_modem_3gpp_initialize_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -2021,6 +3119,7 @@ mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self,
{
MmGdbusModem3gpp *skeleton = NULL;
InitializationContext *ctx;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -2035,6 +3134,8 @@ mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self,
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);
+ mm_gdbus_modem3gpp_set_pco (skeleton, NULL);
+ mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL);
/* Bind our RegistrationState property */
g_object_bind_property (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE,
@@ -2044,42 +3145,22 @@ mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self,
g_object_set (self,
MM_IFACE_MODEM_3GPP_DBUS_SKELETON, skeleton,
NULL);
-
- /* If the modem is *only* LTE, we assume that CS network is not
- * supported */
- if (mm_iface_modem_is_3gpp_lte_only (MM_IFACE_MODEM (self))) {
- mm_dbg ("Modem is LTE-only, assuming CS network is not supported");
- g_object_set (self,
- MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
- NULL);
- }
}
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_3gpp_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
/* Perform async initialization here */
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
void
mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self)
{
- /* Remove RegistrationCheckContext object to make sure any pending
- * invocation of periodic_registration_check is cancelled before the
- * DBus skeleton is removed. */
- if (G_LIKELY (registration_check_context_quark))
- g_object_set_qdata (G_OBJECT (self),
- registration_check_context_quark,
- NULL);
-
/* Unexport DBus interface and remove the skeleton */
mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self), NULL);
g_object_set (self,
@@ -2141,6 +3222,14 @@ iface_modem_3gpp_init (gpointer g_iface)
g_object_interface_install_property
(g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED,
+ "5GS network supported",
+ "Whether the modem works in the 5GS network",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
g_param_spec_flags (MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS,
"Ignored locks",
"Ignored facility locks",
@@ -2148,6 +3237,14 @@ iface_modem_3gpp_init (gpointer g_iface)
MM_MODEM_3GPP_FACILITY_NONE,
G_PARAM_READWRITE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_object (MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER,
+ "Initial EPS bearer",
+ "Initial EPS bearer setup during registration",
+ MM_TYPE_BASE_BEARER,
+ G_PARAM_READWRITE));
+
initialized = TRUE;
}
diff --git a/src/mm-iface-modem-3gpp.h b/src/mm-iface-modem-3gpp.h
index 6237b8aa..b0403ea8 100644
--- a/src/mm-iface-modem-3gpp.h
+++ b/src/mm-iface-modem-3gpp.h
@@ -21,6 +21,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-base-bearer.h"
#include "mm-port-serial-at.h"
#define MM_TYPE_IFACE_MODEM_3GPP (mm_iface_modem_3gpp_get_type ())
@@ -33,7 +34,9 @@
#define MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED "iface-modem-3gpp-cs-network-supported"
#define MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED "iface-modem-3gpp-ps-network-supported"
#define MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED "iface-modem-3gpp-eps-network-supported"
+#define MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED "iface-modem-3gpp-5gs-network-supported"
#define MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS "iface-modem-3gpp-ignored-facility-locks"
+#define MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER "iface-modem-3gpp-initial-eps-bearer"
#define MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK \
(MM_MODEM_ACCESS_TECHNOLOGY_GSM | \
@@ -45,7 +48,8 @@
MM_MODEM_ACCESS_TECHNOLOGY_HSUPA | \
MM_MODEM_ACCESS_TECHNOLOGY_HSPA | \
MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS | \
- MM_MODEM_ACCESS_TECHNOLOGY_LTE)
+ MM_MODEM_ACCESS_TECHNOLOGY_LTE | \
+ MM_MODEM_ACCESS_TECHNOLOGY_5GNR)
typedef struct _MMIfaceModem3gpp MMIfaceModem3gpp;
@@ -68,6 +72,14 @@ struct _MMIfaceModem3gpp {
GAsyncResult *res,
GError **error);
+ /* Loading of the UE mode of operation for EPS property */
+ void (* load_eps_ue_mode_operation) (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMModem3gppEpsUeModeOperation (* load_eps_ue_mode_operation_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Asynchronous setting up unsolicited events */
void (*setup_unsolicited_events) (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
@@ -137,13 +149,34 @@ struct _MMIfaceModem3gpp {
GAsyncResult *res,
GError **error);
- /* Run CS/PS/EPS registration state checks..
+ /* Asynchronous initial default EPS bearer loading */
+ void (*load_initial_eps_bearer) (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMBearerProperties * (*load_initial_eps_bearer_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous initial default EPS bearer settings loading */
+ void (*load_initial_eps_bearer_settings) (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMBearerProperties * (*load_initial_eps_bearer_settings_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Create initial default EPS bearer object */
+ MMBaseBearer * (*create_initial_eps_bearer) (MMIfaceModem3gpp *self,
+ MMBearerProperties *properties);
+
+ /* Run CS/PS/EPS/5GS registration state checks..
* Note that no registration state is returned, implementations should call
* mm_iface_modem_3gpp_update_registration_state(). */
void (* run_registration_checks) (MMIfaceModem3gpp *self,
- gboolean cs_supported,
- gboolean ps_supported,
- gboolean eps_supported,
+ gboolean is_cs_supported,
+ gboolean is_ps_supported,
+ gboolean is_eps_supported,
+ gboolean is_5gs_supported,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (*run_registration_checks_finish) (MMIfaceModem3gpp *self,
@@ -176,14 +209,6 @@ 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,
@@ -191,9 +216,48 @@ struct _MMIfaceModem3gpp {
GList * (*scan_networks_finish) (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error);
+
+ /* Set UE mode of operation for EPS */
+ void (* set_eps_ue_mode_operation) (MMIfaceModem3gpp *self,
+ MMModem3gppEpsUeModeOperation mode,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* set_eps_ue_mode_operation_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Set initial EPS bearer settings */
+ void (* set_initial_eps_bearer_settings) (MMIfaceModem3gpp *self,
+ MMBearerProperties *properties,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* set_initial_eps_bearer_settings_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Remove modem personalization */
+ void (* disable_facility_lock) (MMIfaceModem3gpp *self,
+ MMModem3gppFacility facility,
+ guint8 slot,
+ const gchar *control_key,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* disable_facility_lock_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Set Packet service */
+ void (*set_packet_service_state) (MMIfaceModem3gpp *self,
+ MMModem3gppPacketServiceState state,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*set_packet_service_state_finish) (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
};
GType mm_iface_modem_3gpp_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem3gpp, g_object_unref)
/* Initialize Modem 3GPP interface (async) */
void mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self,
@@ -221,6 +285,18 @@ gboolean mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error);
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+/* Sync 3GPP interface (async) */
+void mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
+
/* Shutdown Modem 3GPP interface */
void mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self);
@@ -234,13 +310,21 @@ 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_5gs_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,
gulong location_area_code,
+ gulong tracking_area_code,
gulong cell_id);
+void mm_iface_modem_3gpp_update_pco_list (MMIfaceModem3gpp *self,
+ const GList *pco_list);
+void mm_iface_modem_3gpp_update_initial_eps_bearer (MMIfaceModem3gpp *self,
+ MMBearerProperties *properties);
+void mm_iface_modem_3gpp_reload_initial_eps_bearer (MMIfaceModem3gpp *self);
/* Run all registration checks */
void mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self,
@@ -260,14 +344,23 @@ gboolean mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceMod
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,
- GAsyncResult *res,
- GError **error);
-void mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
- const gchar *operator_id,
- guint max_registration_time,
- GAsyncReadyCallback callback,
- gpointer user_data);
+void mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ gboolean force_registration,
+ guint max_registration_time,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Allow re-registering in the network with last settings */
+void mm_iface_modem_3gpp_reregister_in_network (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_3gpp_reregister_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
/* Bind properties for simple GetStatus() */
void mm_iface_modem_3gpp_bind_simple_status (MMIfaceModem3gpp *self,
diff --git a/src/mm-iface-modem-cdma.c b/src/mm-iface-modem-cdma.c
index 853f1dbc..877afe4e 100644
--- a/src/mm-iface-modem-cdma.c
+++ b/src/mm-iface-modem-cdma.c
@@ -23,16 +23,43 @@
#include "mm-iface-modem-cdma.h"
#include "mm-base-modem.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
-
-#define REGISTRATION_CHECK_TIMEOUT_SEC 30
+#include "mm-log-object.h"
#define SUBSYSTEM_CDMA1X "cdma1x"
#define SUBSYSTEM_EVDO "evdo"
-#define REGISTRATION_CHECK_CONTEXT_TAG "cdma-registration-check-context-tag"
+/*****************************************************************************/
+/* Private data context */
-static GQuark registration_check_context_quark;
+#define PRIVATE_TAG "iface-modem-cdma-private-tag"
+static GQuark private_quark;
+
+typedef struct {
+ gboolean activation_ongoing;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMIfaceModemCdma *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
/*****************************************************************************/
@@ -91,7 +118,11 @@ handle_activate_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
HandleActivateContext *ctx)
{
- GError *error = NULL;
+ Private *priv;
+ GError *error = NULL;
+
+ priv = get_private (self);
+ priv->activation_ongoing = FALSE;
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_finish (self, res,&error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -106,8 +137,11 @@ handle_activate_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleActivateContext *ctx)
{
- MMModemState modem_state;
- GError *error = NULL;
+ Private *priv;
+ MMModemState modem_state;
+ GError *error = NULL;
+
+ priv = get_private (MM_IFACE_MODEM_CDMA (self));
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -115,9 +149,19 @@ handle_activate_auth_ready (MMBaseModem *self,
return;
}
+ /* Fail if we have already an activation ongoing */
+ if (priv->activation_ongoing) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "An activation operation is already in progress");
+ handle_activate_context_free (ctx);
+ return;
+ }
+
/* If we're already activated, nothing to do */
if (mm_gdbus_modem_cdma_get_activation_state (ctx->skeleton) == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED) {
- mm_dbg ("Modem is already activated");
+ mm_obj_dbg (self, "already activated");
mm_gdbus_modem_cdma_complete_activate (ctx->skeleton, ctx->invocation);
handle_activate_context_free (ctx);
return;
@@ -135,6 +179,17 @@ handle_activate_auth_ready (MMBaseModem *self,
return;
}
+ /* Error if carrier code is empty */
+ if (!ctx->carrier || !ctx->carrier[0]) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot perform OTA activation: "
+ "invalid empty carrier code");
+ handle_activate_context_free (ctx);
+ return;
+ }
+
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
@@ -160,6 +215,7 @@ handle_activate_auth_ready (MMBaseModem *self,
case MM_MODEM_STATE_ENABLED:
case MM_MODEM_STATE_SEARCHING:
case MM_MODEM_STATE_REGISTERED:
+ priv->activation_ongoing = TRUE;
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate (
MM_IFACE_MODEM_CDMA (self),
ctx->carrier,
@@ -193,6 +249,9 @@ handle_activate_auth_ready (MMBaseModem *self,
"Cannot perform OTA activation: "
"modem is connected");
break;
+
+ default:
+ g_assert_not_reached ();
}
handle_activate_context_free (ctx);
@@ -245,7 +304,11 @@ handle_activate_manual_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
HandleActivateManualContext *ctx)
{
- GError *error = NULL;
+ Private *priv;
+ GError *error = NULL;
+
+ priv = get_private (self);
+ priv->activation_ongoing = FALSE;
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual_finish (self, res,&error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -261,8 +324,11 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
HandleActivateManualContext *ctx)
{
MMCdmaManualActivationProperties *properties;
- MMModemState modem_state;
- GError *error = NULL;
+ Private *priv;
+ MMModemState modem_state;
+ GError *error = NULL;
+
+ priv = get_private (MM_IFACE_MODEM_CDMA (self));
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -270,9 +336,19 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
return;
}
+ /* Fail if we have already an activation ongoing */
+ if (priv->activation_ongoing) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "An activation operation is already in progress");
+ handle_activate_manual_context_free (ctx);
+ return;
+ }
+
/* If we're already activated, nothing to do */
if (mm_gdbus_modem_cdma_get_activation_state (ctx->skeleton) == MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED) {
- mm_dbg ("Modem is already activated");
+ mm_obj_dbg (self, "already activated");
mm_gdbus_modem_cdma_complete_activate_manual (ctx->skeleton, ctx->invocation);
handle_activate_manual_context_free (ctx);
return;
@@ -322,6 +398,7 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
case MM_MODEM_STATE_ENABLED:
case MM_MODEM_STATE_SEARCHING:
case MM_MODEM_STATE_REGISTERED:
+ priv->activation_ongoing = TRUE;
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->activate_manual (
MM_IFACE_MODEM_CDMA (self),
properties,
@@ -356,6 +433,9 @@ handle_activate_manual_auth_ready (MMBaseModem *self,
"Cannot perform manual activation: "
"modem is connected");
break;
+
+ default:
+ g_assert_not_reached ();
}
g_object_unref (properties);
@@ -396,22 +476,21 @@ mm_iface_modem_cdma_register_in_network_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
register_in_network_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->register_in_network_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -420,23 +499,26 @@ mm_iface_modem_cdma_register_in_network (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_cdma_register_in_network);
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->register_in_network (
self,
max_registration_time,
(GAsyncReadyCallback)register_in_network_ready,
- result);
+ task);
}
/*****************************************************************************/
+#define REGISTRATION_CHECK_TIMEOUT_SEC 30
+#define REGISTRATION_CHECK_CONTEXT_TAG "cdma-registration-check-context-tag"
+
+static GQuark registration_check_context_quark;
+
typedef struct _RunRegistrationChecksContext RunRegistrationChecksContext;
-static void registration_check_step (RunRegistrationChecksContext *ctx);
+static void registration_check_step (GTask *task);
typedef enum {
REGISTRATION_CHECK_STEP_FIRST,
@@ -456,8 +538,6 @@ typedef enum {
} RegistrationCheckStep;
struct _RunRegistrationChecksContext {
- MMIfaceModemCdma *self;
- GSimpleAsyncResult *result;
RegistrationCheckStep step;
MMModemCdmaRegistrationState cdma1x_state;
MMModemCdmaRegistrationState evdo_state;
@@ -483,30 +563,24 @@ struct _RunRegistrationChecksContext {
guint cdma1x_nid;
};
-static void
-run_registration_checks_context_complete_and_free (RunRegistrationChecksContext *ctx)
-{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
-}
-
gboolean
mm_iface_modem_cdma_run_registration_checks_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_registration_checks_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks_finish (
self,
res,
@@ -517,56 +591,62 @@ setup_registration_checks_ready (MMIfaceModemCdma *self,
&ctx->skip_detailed_registration_state,
&error)) {
/* Make it fatal */
- g_simple_async_result_take_error (ctx->result, error);
- run_registration_checks_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
ctx->step++;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
get_call_manager_state_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state_finish (
self,
res,
&ctx->call_manager_system_mode,
&ctx->call_manager_operating_mode,
&error)) {
- mm_dbg ("Could not get call manager state: %s", error->message);
+ mm_obj_dbg (self, "could not get call manager state: %s", error->message);
g_error_free (error);
/* Fallback to AT-based check */
ctx->step = REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS;
- registration_check_step (ctx);
+ registration_check_step (task);
return;
}
/* If no CDMA service, just finish checks */
if (ctx->call_manager_operating_mode != QCDM_CMD_CM_SUBSYS_STATE_INFO_OPERATING_MODE_ONLINE) {
ctx->step = REGISTRATION_CHECK_STEP_LAST;
- registration_check_step (ctx);
+ registration_check_step (task);
return;
}
/* Go on to next step */
ctx->step++;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
get_hdr_state_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state_finish (
self,
res,
@@ -574,26 +654,32 @@ get_hdr_state_ready (MMIfaceModemCdma *self,
&ctx->hdr_session_state,
&ctx->hdr_almp_state,
&error)) {
- mm_dbg ("Could not get HDR state: %s", error->message);
+ mm_obj_dbg (self, "could not get HDR state: %s", error->message);
g_error_free (error);
/* Fallback to AT-based check */
ctx->step = REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS;
- registration_check_step (ctx);
+ registration_check_step (task);
return;
}
/* Go on to next step */
ctx->step++;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
-parse_qcdm_results (RunRegistrationChecksContext *ctx)
+parse_qcdm_results (GTask *task)
{
- mm_dbg ("QCDM CM System Mode: %d", ctx->call_manager_system_mode);
- mm_dbg ("QCDM HDR Hybrid Mode: %d", ctx->hdr_hybrid_mode);
- mm_dbg ("QCDM HDR Session State: %d", ctx->hdr_session_state);
- mm_dbg ("QCDM HDR ALMP State: %d", ctx->hdr_almp_state);
+ MMIfaceModemCdma *self;
+ RunRegistrationChecksContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ mm_obj_dbg (self, "QCDM CM System Mode: %d", ctx->call_manager_system_mode);
+ mm_obj_dbg (self, "QCDM HDR Hybrid Mode: %d", ctx->hdr_hybrid_mode);
+ mm_obj_dbg (self, "QCDM HDR Session State: %d", ctx->hdr_session_state);
+ mm_obj_dbg (self, "QCDM HDR ALMP State: %d", ctx->hdr_almp_state);
/* Set QCDM-obtained registration info */
switch (ctx->call_manager_system_mode) {
@@ -623,45 +709,51 @@ parse_qcdm_results (RunRegistrationChecksContext *ctx)
/* If no CDMA service, just finish checks */
ctx->step = REGISTRATION_CHECK_STEP_LAST;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
get_service_status_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
GError *error = NULL;
gboolean has_service = FALSE;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status_finish (self,
res,
&has_service,
&error)) {
- mm_warn ("Could not get service status: %s", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- run_registration_checks_context_complete_and_free (ctx);
+ mm_obj_warn (self, "could not get service status: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!has_service) {
/* There is no CDMA service at all, end registration checks */
- mm_dbg ("No CDMA service found");
+ mm_obj_dbg (self, "no CDMA service found");
ctx->step = REGISTRATION_CHECK_STEP_LAST;
} else
/* If we do have service, go on to next step */
ctx->step++;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
get_cdma1x_serving_system_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
/* Note: used for *both* AT and QCDM serving system checks */
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish (
@@ -676,9 +768,9 @@ get_cdma1x_serving_system_ready (MMIfaceModemCdma *self,
if (!g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NO_NETWORK)) {
- mm_warn ("Could not get serving system: %s", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- run_registration_checks_context_complete_and_free (ctx);
+ mm_obj_warn (self, "could not get serving system: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -690,17 +782,23 @@ get_cdma1x_serving_system_ready (MMIfaceModemCdma *self,
/* Go on to next step */
ctx->step++;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
-parse_at_results (RunRegistrationChecksContext *ctx)
+parse_at_results (GTask *task)
{
+ MMIfaceModemCdma *self;
+ RunRegistrationChecksContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* 99999 means unknown/no service */
if (ctx->cdma1x_sid == MM_MODEM_CDMA_SID_UNKNOWN &&
ctx->cdma1x_nid == MM_MODEM_CDMA_NID_UNKNOWN) {
/* Not registered in CDMA network, end registration checks */
- mm_dbg ("Not registered in any CDMA network");
+ mm_obj_dbg (self, "no registered in any CDMA network");
ctx->step = REGISTRATION_CHECK_STEP_LAST;
} else {
/* We're registered on the CDMA 1x network (at least) */
@@ -709,18 +807,21 @@ parse_at_results (RunRegistrationChecksContext *ctx)
ctx->step = REGISTRATION_CHECK_STEP_DETAILED_REGISTRATION_STATE;
}
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
get_detailed_registration_state_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RunRegistrationChecksContext *ctx)
+ GTask *task)
{
+ RunRegistrationChecksContext *ctx;
GError *error = NULL;
MMModemCdmaRegistrationState detailed_cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
MMModemCdmaRegistrationState detailed_evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state_finish (
self,
res,
@@ -729,7 +830,7 @@ get_detailed_registration_state_ready (MMIfaceModemCdma *self,
&error)) {
/* This error is NOT fatal. If we get an error here, we'll just fallback
* to the non-detailed values we already got. */
- mm_dbg ("Could not get more detailed registration state: %s", error->message);
+ mm_obj_dbg (self, "could not get more detailed registration state: %s", error->message);
} else {
ctx->cdma1x_state = detailed_cdma1x_state;
ctx->evdo_state = detailed_evdo_state;
@@ -737,105 +838,110 @@ get_detailed_registration_state_ready (MMIfaceModemCdma *self,
/* Go on to next step */
ctx->step++;
- registration_check_step (ctx);
+ registration_check_step (task);
}
static void
-registration_check_step (RunRegistrationChecksContext *ctx)
+registration_check_step (GTask *task)
{
+ MMIfaceModemCdma *self;
+ RunRegistrationChecksContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case REGISTRATION_CHECK_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_SETUP_REGISTRATION_CHECKS:
/* Allow implementations to run an initial setup check. This setup allows
* to specify which of the next steps will be completely skipped. Useful
* when implementations have a best get_detailed_registration_state()
* so that they just need that to be run. */
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->setup_registration_checks &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->setup_registration_checks_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->setup_registration_checks (
- ctx->self,
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_registration_checks (
+ self,
(GAsyncReadyCallback)setup_registration_checks_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_QCDM_CALL_MANAGER_STATE:
- mm_dbg ("Starting QCDM-based registration checks");
+ mm_obj_dbg (self, "starting QCDM-based registration checks...");
if (!ctx->skip_qcdm_call_manager_step &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_call_manager_state &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_call_manager_state_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state_finish) {
/* Start by trying to get the call manager state. */
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_call_manager_state (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_call_manager_state (
+ self,
(GAsyncReadyCallback)get_call_manager_state_ready,
- ctx);
+ task);
return;
}
/* Fallback to AT-based check */
- mm_dbg (" Skipping all QCDM-based checks and falling back to AT-based checks");
+ mm_obj_dbg (self, " skipping all QCDM-based checks and falling back to AT-based checks");
ctx->step = REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS;
- registration_check_step (ctx);
+ registration_check_step (task);
return;
case REGISTRATION_CHECK_STEP_QCDM_HDR_STATE:
if (ctx->evdo_supported &&
!ctx->skip_qcdm_hdr_step &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_hdr_state &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_hdr_state_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state_finish) {
/* Get HDR (EVDO) state. */
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_hdr_state (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_hdr_state (
+ self,
(GAsyncReadyCallback)get_hdr_state_ready,
- ctx);
+ task);
return;
}
- mm_dbg (" Skipping HDR check");
- /* Fall down to next step */
+ mm_obj_dbg (self, " skipping HDR check");
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_QCDM_CDMA1X_SERVING_SYSTEM:
/* We only care about SID/NID here; nothing to do with registration
* state.
*/
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system (
- ctx->self,
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system (
+ self,
(GAsyncReadyCallback)get_cdma1x_serving_system_ready,
- ctx);
+ task);
return;
}
- mm_dbg (" Skipping CDMA1x Serving System check");
- /* Fall down to next step */
+ mm_obj_dbg (self, " skipping CDMA1x serving system check");
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_QCDM_LAST:
/* When we get all QCDM results, parse them */
- parse_qcdm_results (ctx);
+ parse_qcdm_results (task);
return;
case REGISTRATION_CHECK_STEP_AT_CDMA_SERVICE_STATUS:
- mm_dbg ("Starting AT-based registration checks");
-
+ mm_obj_dbg (self, "starting AT-based registration checks");
/* If we don't have means to get service status, just assume we do have
* CDMA service and keep on */
if (!ctx->skip_at_cdma_service_status_step &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_service_status &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_service_status_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_service_status (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_service_status (
+ self,
(GAsyncReadyCallback)get_service_status_ready,
- ctx);
+ task);
return;
}
- mm_dbg (" Skipping CDMA service status check, assuming with service");
- /* Fall down to next step */
+ mm_obj_dbg (self, " skipping CDMA service status check, assuming with service");
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_AT_CDMA1X_SERVING_SYSTEM:
/* Now that we have some sort of service, check if the the device is
@@ -849,59 +955,62 @@ registration_check_step (RunRegistrationChecksContext *ctx)
* themselves; if they do, they'll set these callbacks to NULL..
*/
if (!ctx->skip_at_cdma1x_serving_system_step &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_cdma1x_serving_system (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_cdma1x_serving_system (
+ self,
(GAsyncReadyCallback)get_cdma1x_serving_system_ready,
- ctx);
+ task);
return;
}
- mm_dbg (" Skipping CDMA1x Serving System check");
- /* Fall down to next step */
+ mm_obj_dbg (self, " skipping CDMA1x Serving System check");
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_AT_LAST:
/* When we get all AT results, parse them */
- parse_at_results (ctx);
+ parse_at_results (task);
return;
case REGISTRATION_CHECK_STEP_DETAILED_REGISTRATION_STATE:
- mm_dbg ("Starting detailed registration state check");
+ mm_obj_dbg (self, "starting detailed registration state check");
/* We let classes implementing this interface to look for more detailed
* registration info. */
if (!ctx->skip_detailed_registration_state &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_detailed_registration_state &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_detailed_registration_state_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state_finish) {
/* We pass the CDMA1x/EVDO registration states we got up to now.
* If the implementation can't improve the detail, it must either
* return the values it already got as input, or issue an error,
* and we'll assume it couldn't get any better value. */
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->get_detailed_registration_state (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->get_detailed_registration_state (
+ self,
ctx->cdma1x_state,
ctx->evdo_state,
(GAsyncReadyCallback)get_detailed_registration_state_ready,
- ctx);
+ task);
return;
}
- mm_dbg (" Skipping detailed registration state check");
- /* Fall down to next step */
+ mm_obj_dbg (self, " skipping detailed registration state check");
ctx->step++;
+ /* fall through */
case REGISTRATION_CHECK_STEP_LAST:
/* We are done without errors! */
- mm_dbg ("All CDMA registration state checks done");
- mm_iface_modem_cdma_update_cdma1x_registration_state (ctx->self,
+ mm_obj_dbg (self, "all CDMA registration state checks done");
+ mm_iface_modem_cdma_update_cdma1x_registration_state (self,
ctx->cdma1x_state,
ctx->cdma1x_sid,
ctx->cdma1x_nid);
- mm_iface_modem_cdma_update_evdo_registration_state (ctx->self,
+ mm_iface_modem_cdma_update_evdo_registration_state (self,
ctx->evdo_state);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- run_registration_checks_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -910,16 +1019,15 @@ registration_check_step (RunRegistrationChecksContext *ctx)
static void
custom_run_registration_checks_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
if (!MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks_finish (self, res, &error))
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -927,23 +1035,20 @@ mm_iface_modem_cdma_run_registration_checks (MMIfaceModemCdma *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
gboolean cdma1x_supported;
gboolean evdo_supported;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_cdma_run_registration_checks);
+ task = g_task_new (self, NULL, callback, user_data);
g_object_get (self,
MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, &evdo_supported,
MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, &cdma1x_supported,
NULL);
- mm_dbg ("Running registration checks (CDMA1x: '%s', EV-DO: '%s')",
- cdma1x_supported ? "yes" : "no",
- evdo_supported ? "yes" : "no");
+ mm_obj_dbg (self, "running registration checks (CDMA1x: '%s', EV-DO: '%s')",
+ cdma1x_supported ? "yes" : "no",
+ evdo_supported ? "yes" : "no");
if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks &&
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->run_registration_checks_finish) {
@@ -967,13 +1072,11 @@ mm_iface_modem_cdma_run_registration_checks (MMIfaceModemCdma *self,
cdma1x_supported,
evdo_supported,
(GAsyncReadyCallback)custom_run_registration_checks_ready,
- result);
+ task);
} else {
RunRegistrationChecksContext *ctx;
ctx = g_new0 (RunRegistrationChecksContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = result;
ctx->cdma1x_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
ctx->evdo_state = MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN;
ctx->call_manager_system_mode = QCDM_CMD_CM_SUBSYS_STATE_INFO_SYSTEM_MODE_NO_SERVICE;
@@ -983,7 +1086,9 @@ mm_iface_modem_cdma_run_registration_checks (MMIfaceModemCdma *self,
ctx->evdo_supported = evdo_supported;
ctx->cdma1x_supported = cdma1x_supported;
- registration_check_step (ctx);
+ g_task_set_task_data (task, ctx, g_free);
+
+ registration_check_step (task);
}
}
@@ -1055,6 +1160,9 @@ mm_iface_modem_cdma_update_evdo_registration_state (MMIfaceModemCdma *self,
MM_MODEM_STATE_ENABLED,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
break;
+ default:
+ g_assert_not_reached ();
+ break;
}
}
@@ -1106,6 +1214,9 @@ mm_iface_modem_cdma_update_cdma1x_registration_state (MMIfaceModemCdma *self,
MM_MODEM_STATE_ENABLED,
MM_MODEM_STATE_CHANGE_REASON_UNKNOWN);
break;
+ default:
+ g_assert_not_reached ();
+ break;
}
}
@@ -1136,7 +1247,7 @@ periodic_registration_checks_ready (MMIfaceModemCdma *self,
mm_iface_modem_cdma_run_registration_checks_finish (self, res, &error);
if (error) {
- mm_dbg ("Couldn't refresh CDMA registration status: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't refresh CDMA registration status: %s", error->message);
g_error_free (error);
}
@@ -1160,7 +1271,7 @@ periodic_registration_check (MMIfaceModemCdma *self)
(GAsyncReadyCallback)periodic_registration_checks_ready,
NULL);
}
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
static void
@@ -1175,7 +1286,7 @@ periodic_registration_check_disable (MMIfaceModemCdma *self)
registration_check_context_quark,
NULL);
- mm_dbg ("Periodic CDMA registration checks disabled");
+ mm_obj_dbg (self, "periodic CDMA registration checks disabled");
}
static void
@@ -1194,7 +1305,7 @@ periodic_registration_check_enable (MMIfaceModemCdma *self)
return;
/* Create context and keep it as object data */
- mm_dbg ("Periodic CDMA registration checks enabled");
+ mm_obj_dbg (self, "periodic CDMA registration checks enabled");
ctx = g_new0 (RegistrationCheckContext, 1);
ctx->timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC,
(GSourceFunc)periodic_registration_check,
@@ -1231,11 +1342,11 @@ mm_iface_modem_cdma_update_activation_state (MMIfaceModemCdma *self,
return;
if (activation_error) {
- mm_dbg ("Activation failed: %s", activation_error->message);
+ mm_obj_dbg (self, "activation failed: %s", activation_error->message);
if (activation_error->domain == MM_CDMA_ACTIVATION_ERROR)
error = activation_error->code;
else {
- mm_warn ("Error given is not an activation error");
+ mm_obj_warn (self, "error given is not an activation error");
error = MM_CDMA_ACTIVATION_ERROR_UNKNOWN;
}
}
@@ -1257,7 +1368,7 @@ mm_iface_modem_cdma_update_activation_state (MMIfaceModemCdma *self,
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
@@ -1268,18 +1379,13 @@ typedef enum {
} DisablingStep;
struct _DisablingContext {
- MMIfaceModemCdma *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModemCdma *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -1290,87 +1396,100 @@ mm_iface_modem_cdma_disable_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disable_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
if (error) {
- mm_dbg ("Couldn't disable unsolicited events: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't disable unsolicited events: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
cleanup_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
if (error) {
- mm_dbg ("Couldn't cleanup unsolicited events: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't cleanup unsolicited events: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModemCdma *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS:
- periodic_registration_check_disable (ctx->self);
- /* Fall down to next step */
+ periodic_registration_check_disable (self);
ctx->step++;
+ /* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->disable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1382,193 +1501,163 @@ mm_iface_modem_cdma_disable (MMIfaceModemCdma *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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_cdma_disable);
ctx->step = DISABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+
+ g_object_get (self,
MM_IFACE_MODEM_CDMA_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
- ENABLING_STEP_RUN_REGISTRATION_CHECKS,
ENABLING_STEP_PERIODIC_REGISTRATION_CHECKS,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
- MMIfaceModemCdma *self;
EnablingStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModemCdma *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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_cdma_enable_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Setting up unsolicited events failed: '%s'", error->message);
+ mm_obj_dbg (self, "setting up unsolicited events failed: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error);
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Enabling unsolicited events failed: '%s'", error->message);
+ mm_obj_dbg (self, "enabling unsolicited events failed: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-run_registration_checks_ready (MMIfaceModemCdma *self,
- GAsyncResult *res,
- EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
- GError *error = NULL;
+ MMIfaceModemCdma *self;
+ EnablingContext *ctx;
- if (!mm_iface_modem_cdma_run_registration_checks_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
+ /* Don't run new steps if we're cancelled */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
}
- /* Go on to 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;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->setup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
- if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->enable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
-
- case ENABLING_STEP_RUN_REGISTRATION_CHECKS:
- mm_iface_modem_cdma_run_registration_checks (ctx->self,
- (GAsyncReadyCallback)run_registration_checks_ready,
- ctx);
- return;
+ /* fall through */
case ENABLING_STEP_PERIODIC_REGISTRATION_CHECKS:
- periodic_registration_check_enable (ctx->self);
- /* Fall down to next step */
+ periodic_registration_check_enable (self);
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1581,34 +1670,33 @@ mm_iface_modem_cdma_enable (MMIfaceModemCdma *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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_cdma_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_CDMA_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
@@ -1619,60 +1707,42 @@ typedef enum {
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemCdma *self;
MmGdbusModemCdma *skeleton;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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;
-}
-
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
static void \
load_##NAME##_ready (MMIfaceModemCdma *self, \
GAsyncResult *res, \
- InitializationContext *ctx) \
+ GTask *task) \
{ \
+ InitializationContext *ctx; \
GError *error = NULL; \
gchar *val; \
\
+ ctx = g_task_get_task_data (task); \
+ \
val = MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_modem_cdma_set_##NAME (ctx->skeleton, val); \
g_free (val); \
\
if (error) { \
- mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
+ mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
ctx->step++; \
- interface_initialization_step (ctx); \
+ interface_initialization_step (task); \
}
STR_REPLY_READY_FN (meid, "MEID")
@@ -1681,83 +1751,94 @@ STR_REPLY_READY_FN (esn, "ESN")
static void
load_activation_state_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
MMModemCdmaActivationState state;
+ ctx = g_task_get_task_data (task);
+
state = MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state_finish (self, res, &error);
mm_gdbus_modem_cdma_set_activation_state (ctx->skeleton, state);
if (error) {
- mm_warn ("couldn't load activation state: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load activation state: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModemCdma *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_MEID:
/* MEID value is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have it loaded,
* don't try to load it again. */
if (!mm_gdbus_modem_cdma_get_meid (ctx->skeleton) &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_meid &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_meid_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_meid (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_meid &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_meid_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_meid (
+ self,
(GAsyncReadyCallback)load_meid_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_ESN:
/* ESN value is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have it loaded,
* don't try to load it again. */
if (!mm_gdbus_modem_cdma_get_esn (ctx->skeleton) &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_esn &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_esn_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_esn (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_esn &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_esn_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_esn (
+ self,
(GAsyncReadyCallback)load_esn_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_ACTIVATION_STATE:
/* Initial activation state is meant to be loaded only once during the
* whole lifetime of the modem. Therefore, if we already have it loaded,
* don't try to load it again. */
if (mm_gdbus_modem_cdma_get_activation_state (ctx->skeleton) == MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_activation_state &&
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_activation_state_finish) {
- MM_IFACE_MODEM_CDMA_GET_INTERFACE (ctx->self)->load_activation_state (
- ctx->self,
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state &&
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state_finish) {
+ MM_IFACE_MODEM_CDMA_GET_INTERFACE (self)->load_activation_state (
+ self,
(GAsyncReadyCallback)load_activation_state_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -1766,19 +1847,22 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-activate",
G_CALLBACK (handle_activate),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-activate-manual",
G_CALLBACK (handle_activate_manual),
- ctx->self);
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_cdma (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_cdma (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_CDMA (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1789,7 +1873,7 @@ mm_iface_modem_cdma_initialize_finish (MMIfaceModemCdma *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -1800,6 +1884,7 @@ mm_iface_modem_cdma_initialize (MMIfaceModemCdma *self,
{
InitializationContext *ctx;
MmGdbusModemCdma *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -1831,16 +1916,13 @@ mm_iface_modem_cdma_initialize (MMIfaceModemCdma *self,
/* 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_cdma_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
diff --git a/src/mm-iface-modem-cdma.h b/src/mm-iface-modem-cdma.h
index 9e817dde..7501d897 100644
--- a/src/mm-iface-modem-cdma.h
+++ b/src/mm-iface-modem-cdma.h
@@ -229,6 +229,7 @@ struct _MMIfaceModemCdma {
};
GType mm_iface_modem_cdma_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemCdma, g_object_unref)
/* Initialize CDMA interface (async) */
void mm_iface_modem_cdma_initialize (MMIfaceModemCdma *self,
diff --git a/src/mm-iface-modem-firmware.c b/src/mm-iface-modem-firmware.c
index 9d41f223..1b4a07ad 100644
--- a/src/mm-iface-modem-firmware.c
+++ b/src/mm-iface-modem-firmware.c
@@ -19,13 +19,14 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-firmware.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
-#define SUPPORT_CHECKED_TAG "firmware-support-checked-tag"
-#define SUPPORTED_TAG "firmware-supported-tag"
-
-static GQuark support_checked_quark;
-static GQuark supported_quark;
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+#if defined WITH_MBIM
+# include "mm-broadband-modem-mbim.h"
+#endif
/*****************************************************************************/
@@ -50,7 +51,7 @@ static void
handle_list_context_free (HandleListContext *ctx)
{
if (ctx->list)
- g_list_free_full (ctx->list, (GDestroyNotify)g_object_unref);
+ g_list_free_full (ctx->list, g_object_unref);
if (ctx->current)
g_object_unref (ctx->current);
g_object_unref (ctx->skeleton);
@@ -68,20 +69,27 @@ load_current_ready (MMIfaceModemFirmware *self,
GList *l;
GError *error = NULL;
- /* reported current may be NULL and we don't treat it as error */
ctx->current = MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current_finish (self, res, &error);
- if (error) {
- g_dbus_method_invocation_take_error (ctx->invocation, error);
- handle_list_context_free (ctx);
- return;
+ if (!ctx->current) {
+ /* Not found isn't fatal */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_list_context_free (ctx);
+ return;
+ }
+ mm_obj_dbg (self, "couldn't load current firmware image: %s", error->message);
+ g_clear_error (&error);
}
/* Build array of dicts */
- g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY);
- for (l = ctx->list; l; l = g_list_next (l))
- g_variant_builder_add_value (
- &builder,
- mm_firmware_properties_get_dictionary (MM_FIRMWARE_PROPERTIES (l->data)));
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}"));
+ for (l = ctx->list; l; l = g_list_next (l)) {
+ GVariant *dict;
+
+ dict = mm_firmware_properties_get_dictionary (MM_FIRMWARE_PROPERTIES (l->data));
+ g_variant_builder_add_value (&builder, dict);
+ g_variant_unref (dict);
+ }
mm_gdbus_modem_firmware_complete_list (
ctx->skeleton,
@@ -99,10 +107,15 @@ load_list_ready (MMIfaceModemFirmware *self,
GError *error = NULL;
ctx->list = MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list_finish (self, res, &error);
- if (error) {
- g_dbus_method_invocation_take_error (ctx->invocation, error);
- handle_list_context_free (ctx);
- return;
+ if (!ctx->list) {
+ /* Not found isn't fatal */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_list_context_free (ctx);
+ return;
+ }
+ mm_obj_dbg (self, "couldn't load firmware image list: %s", error->message);
+ g_clear_error (&error);
}
MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current (MM_IFACE_MODEM_FIRMWARE (self),
@@ -123,6 +136,18 @@ list_auth_ready (MMBaseModem *self,
return;
}
+ if (!MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list ||
+ !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list_finish ||
+ !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current ||
+ !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot list firmware: operation not supported");
+ handle_list_context_free (ctx);
+ return;
+ }
+
MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list (MM_IFACE_MODEM_FIRMWARE (self),
(GAsyncReadyCallback)load_list_ready,
ctx);
@@ -135,19 +160,14 @@ handle_list (MmGdbusModemFirmware *skeleton,
{
HandleListContext *ctx;
- g_assert (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list != NULL);
- g_assert (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_list_finish != NULL);
- g_assert (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current != NULL);
- g_assert (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_current_finish != NULL);
-
- ctx = g_slice_new (HandleListContext);
+ ctx = g_slice_new0 (HandleListContext);
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,
+ MM_AUTHORIZATION_FIRMWARE,
(GAsyncReadyCallback)list_auth_ready,
ctx);
@@ -201,6 +221,17 @@ select_auth_ready (MMBaseModem *self,
return;
}
+
+ if (!MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current ||
+ !MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot select firmware: operation not supported");
+ handle_select_context_free (ctx);
+ return;
+ }
+
MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current (MM_IFACE_MODEM_FIRMWARE (self),
ctx->name,
(GAsyncReadyCallback)change_current_ready,
@@ -215,9 +246,6 @@ handle_select (MmGdbusModemFirmware *skeleton,
{
HandleSelectContext *ctx;
- g_assert (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current != NULL);
- g_assert (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->change_current_finish != NULL);
-
ctx = g_slice_new (HandleSelectContext);
ctx->skeleton = g_object_ref (skeleton);
ctx->invocation = g_object_ref (invocation);
@@ -226,7 +254,7 @@ handle_select (MmGdbusModemFirmware *skeleton,
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
- MM_AUTHORIZATION_DEVICE_CONTROL,
+ MM_AUTHORIZATION_FIRMWARE,
(GAsyncReadyCallback)select_auth_ready,
ctx);
@@ -236,176 +264,260 @@ handle_select (MmGdbusModemFirmware *skeleton,
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
- INITIALIZATION_STEP_CHECK_SUPPORT,
- INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
+ INITIALIZATION_STEP_UPDATE_SETTINGS,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemFirmware *self;
MmGdbusModemFirmware *skeleton;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
- InitializationStep step;
+ InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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);
}
+gboolean
+mm_iface_modem_firmware_initialize_finish (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+add_generic_version (MMBaseModem *self,
+ MMFirmwareUpdateSettings *update_settings,
+ GError **error)
+{
+ const gchar *firmware_revision;
+ const gchar *carrier_revision = NULL;
+ g_autofree gchar *combined = NULL;
+ gboolean ignore_carrier = FALSE;
+
+ firmware_revision = mm_iface_modem_get_revision (MM_IFACE_MODEM (self));
+ if (!firmware_revision) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Unknown revision");
+ return FALSE;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, &ignore_carrier,
+ NULL);
+
+ if (!ignore_carrier)
+ mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self), NULL, &carrier_revision);
+
+ if (!carrier_revision) {
+ mm_firmware_update_settings_set_version (update_settings, firmware_revision);
+ return TRUE;
+ }
+
+ combined = g_strdup_printf ("%s - %s", firmware_revision, carrier_revision);
+ mm_firmware_update_settings_set_version (update_settings, combined);
+ return TRUE;
+}
+
static gboolean
-initialization_context_complete_and_free_if_cancelled (InitializationContext *ctx)
+add_generic_device_ids (MMBaseModem *self,
+ MMFirmwareUpdateSettings *update_settings,
+ GError **error)
{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
+ static const gchar *supported_subsystems[] = { "USB", "PCI" };
+ guint16 vid;
+ guint16 pid;
+ guint16 rid;
+ MMPort *primary = NULL;
+ const gchar *subsystem;
+ const gchar *carrier_config = NULL;
+ g_autoptr(GPtrArray) ids = NULL;
+ guint i;
+ gboolean ignore_carrier = FALSE;
+
+ vid = mm_base_modem_get_vendor_id (self);
+ pid = mm_base_modem_get_product_id (self);
+
+#if defined WITH_QMI
+ if (MM_IS_BROADBAND_MODEM_QMI (self))
+ primary = MM_PORT (mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self)));
+#endif
+#if defined WITH_MBIM
+ if (!primary && MM_IS_BROADBAND_MODEM_MBIM (self))
+ primary = MM_PORT (mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)));
+#endif
+ if (!primary)
+ primary = MM_PORT (mm_base_modem_peek_port_primary (self));
+ g_assert (primary != NULL);
+ rid = mm_kernel_device_get_physdev_revision (mm_port_peek_kernel_device (primary));
+
+
+ subsystem = mm_kernel_device_get_physdev_subsystem (mm_port_peek_kernel_device (primary));
+ if (!subsystem) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown device subsystem");
return FALSE;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (supported_subsystems); i++) {
+ if (g_ascii_strcasecmp (supported_subsystems[i], subsystem) == 0)
+ break;
+ }
+ if (i == G_N_ELEMENTS (supported_subsystems)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported subsystem: %s", subsystem);
+ return FALSE;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, &ignore_carrier,
+ NULL);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Interface initialization cancelled");
- initialization_context_complete_and_free (ctx);
+ if (!ignore_carrier)
+ mm_iface_modem_get_carrier_config (MM_IFACE_MODEM (self), &carrier_config, NULL);
+
+ ids = g_ptr_array_new_with_free_func (g_free);
+ if (carrier_config) {
+ g_autofree gchar *carrier = NULL;
+
+ carrier = g_ascii_strup (carrier_config, -1);
+ g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X&PID_%04X&REV_%04X&CARRIER_%s",
+ supported_subsystems[i], vid, pid, rid, carrier));
+ }
+ g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X&PID_%04X&REV_%04X",
+ supported_subsystems[i], vid, pid, rid));
+ g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X&PID_%04X",
+ supported_subsystems[i], vid, pid));
+ g_ptr_array_add (ids, g_strdup_printf ("%s\\VID_%04X",
+ supported_subsystems[i], vid));
+ g_ptr_array_add (ids, NULL);
+
+ mm_firmware_update_settings_set_device_ids (update_settings, (const gchar **)ids->pdata);
return TRUE;
}
static void
-check_support_ready (MMIfaceModemFirmware *self,
- GAsyncResult *res,
- InitializationContext *ctx)
+load_update_settings_ready (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ InitializationContext *ctx;
+ MMFirmwareUpdateSettings *update_settings;
+ GError *error = NULL;
+ GVariant *variant = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ update_settings = MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings_finish (self, res, &error);
+ if (!update_settings) {
+ mm_obj_dbg (self, "couldn't load update settings: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
- if (!MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->check_support_finish (self,
- res,
- &error)) {
- if (error) {
- /* This error shouldn't be treated as critical */
- mm_dbg ("Firmware support check failed: '%s'", error->message);
- g_error_free (error);
- }
- } else {
- /* Firmware is supported! */
- g_object_set_qdata (G_OBJECT (self),
- supported_quark,
- GUINT_TO_POINTER (TRUE));
+ /* If the plugin didn't specify custom device ids, add the default ones ourselves */
+ if (!mm_firmware_update_settings_get_device_ids (update_settings) &&
+ !add_generic_device_ids (MM_BASE_MODEM (self), update_settings, &error)) {
+ mm_obj_warn (self, "couldn't build device ids: %s", error->message);
+ g_error_free (error);
+ g_clear_object (&update_settings);
+ goto out;
+ }
+
+ /* If the plugin didn't specify custom version, add the default one ourselves */
+ if (!mm_firmware_update_settings_get_version (update_settings) &&
+ !add_generic_version (MM_BASE_MODEM (self), update_settings, &error)) {
+ mm_obj_warn (self, "couldn't set version: %s", error->message);
+ g_error_free (error);
+ g_clear_object (&update_settings);
+ goto out;
+ }
+
+out:
+ if (update_settings) {
+ variant = mm_firmware_update_settings_get_variant (update_settings);
+ g_object_unref (update_settings);
}
+ mm_gdbus_modem_firmware_set_update_settings (ctx->skeleton, variant);
+ if (variant)
+ g_variant_unref (variant);
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModemFirmware *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
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_FIRMWARE_GET_INTERFACE (ctx->self)->check_support &&
- MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (ctx->self)->check_support_finish) {
- MM_IFACE_MODEM_FIRMWARE_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,
- "Firmware not supported");
- initialization_context_complete_and_free (ctx);
+ /* fall through */
+
+ case INITIALIZATION_STEP_UPDATE_SETTINGS:
+ if (MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings &&
+ MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings_finish) {
+ MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE (self)->load_update_settings (
+ self,
+ (GAsyncReadyCallback)load_update_settings_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
/* Handle method invocations */
- g_signal_connect (ctx->skeleton,
- "handle-list",
- G_CALLBACK (handle_list),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-select",
- G_CALLBACK (handle_select),
- ctx->self);
+ g_object_connect (ctx->skeleton,
+ "signal::handle-list", G_CALLBACK (handle_list), self,
+ "signal::handle-select", G_CALLBACK (handle_select), self,
+ NULL);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_firmware (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_firmware (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_FIRMWARE (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
}
-gboolean
-mm_iface_modem_firmware_initialize_finish (MMIfaceModemFirmware *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
void
mm_iface_modem_firmware_initialize (MMIfaceModemFirmware *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
InitializationContext *ctx;
- MmGdbusModemFirmware *skeleton = NULL;
+ MmGdbusModemFirmware *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -419,18 +531,14 @@ mm_iface_modem_firmware_initialize (MMIfaceModemFirmware *self,
}
/* 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_firmware_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
@@ -464,6 +572,14 @@ iface_modem_firmware_init (gpointer g_iface)
MM_GDBUS_TYPE_MODEM_FIRMWARE_SKELETON,
G_PARAM_READWRITE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER,
+ "Ignore carrier info in firmware details",
+ "Whether carrier info (version, name) should be ignored when showing the firmware details",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
initialized = TRUE;
}
diff --git a/src/mm-iface-modem-firmware.h b/src/mm-iface-modem-firmware.h
index b26cfa73..365bfb15 100644
--- a/src/mm-iface-modem-firmware.h
+++ b/src/mm-iface-modem-firmware.h
@@ -27,20 +27,21 @@
#define MM_IS_IFACE_MODEM_FIRMWARE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_FIRMWARE))
#define MM_IFACE_MODEM_FIRMWARE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_FIRMWARE, MMIfaceModemFirmware))
-#define MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON "iface-modem-firmware-dbus-skeleton"
+#define MM_IFACE_MODEM_FIRMWARE_DBUS_SKELETON "iface-modem-firmware-dbus-skeleton"
+#define MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER "iface-modem-firmware-ignore-carrier"
typedef struct _MMIfaceModemFirmware MMIfaceModemFirmware;
struct _MMIfaceModemFirmware {
GTypeInterface g_iface;
- /* Check for Firmware support (async) */
- void (* check_support) (MMIfaceModemFirmware *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (* check_support_finish) (MMIfaceModemFirmware *self,
- GAsyncResult *res,
- GError **error);
+ /* Get update settings (async) */
+ void (* load_update_settings) (MMIfaceModemFirmware *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMFirmwareUpdateSettings * (* load_update_settings_finish) (MMIfaceModemFirmware *self,
+ GAsyncResult *res,
+ GError **error);
/* Get Firmware list (async) */
void (* load_list) (MMIfaceModemFirmware *self,
@@ -69,6 +70,7 @@ struct _MMIfaceModemFirmware {
};
GType mm_iface_modem_firmware_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemFirmware, g_object_unref)
/* Initialize Firmware interface (async) */
void mm_iface_modem_firmware_initialize (MMIfaceModemFirmware *self,
diff --git a/src/mm-iface-modem-location.c b/src/mm-iface-modem-location.c
index 90a9b94e..da16a6dd 100644
--- a/src/mm-iface-modem-location.c
+++ b/src/mm-iface-modem-location.c
@@ -11,7 +11,8 @@
* GNU General Public License for more details:
*
* Copyright (C) 2012 Google, Inc.
- * Copyright (C) 2012 Lanedo GmbH <aleksander@lanedo.com>
+ * Copyright (C) 2012 Lanedo GmbH
+ * Copyright (C) 2012-2019 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <ModemManager.h>
@@ -20,7 +21,8 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-location.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
+#include "mm-modem-helpers.h"
#define MM_LOCATION_GPS_REFRESH_TIME_SECS 30
@@ -126,23 +128,30 @@ build_location_dictionary (GVariant *previous,
while (g_variant_iter_next (&iter, "{uv}", &source, &value)) {
switch (source) {
case MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI:
+ g_assert (!location_3gpp_value);
location_3gpp_value = value;
break;
case MM_MODEM_LOCATION_SOURCE_GPS_NMEA:
+ g_assert (!location_gps_nmea_value);
location_gps_nmea_value = value;
break;
case MM_MODEM_LOCATION_SOURCE_GPS_RAW:
+ g_assert (!location_gps_raw_value);
location_gps_raw_value = value;
break;
case MM_MODEM_LOCATION_SOURCE_CDMA_BS:
+ g_assert (!location_cdma_bs_value);
location_cdma_bs_value = value;
break;
case MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED:
g_assert_not_reached ();
- case MM_MODEM_LOCATION_SOURCE_AGPS:
+ case MM_MODEM_LOCATION_SOURCE_AGPS_MSA:
+ g_assert_not_reached ();
+ case MM_MODEM_LOCATION_SOURCE_AGPS_MSB:
g_assert_not_reached ();
default:
g_warn_if_reached ();
+ g_variant_unref (value);
break;
}
}
@@ -159,10 +168,12 @@ build_location_dictionary (GVariant *previous,
}
if (location_3gpp_value) {
+ g_assert (!g_variant_is_floating (location_3gpp_value));
g_variant_builder_add (&builder,
"{uv}",
MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI,
location_3gpp_value);
+ g_variant_unref (location_3gpp_value);
}
/* If a new one given, use it */
@@ -172,11 +183,14 @@ build_location_dictionary (GVariant *previous,
location_gps_nmea_value = mm_location_gps_nmea_get_string_variant (location_gps_nmea);
}
- if (location_gps_nmea_value)
+ if (location_gps_nmea_value) {
+ g_assert (!g_variant_is_floating (location_gps_nmea_value));
g_variant_builder_add (&builder,
"{uv}",
MM_MODEM_LOCATION_SOURCE_GPS_NMEA,
location_gps_nmea_value);
+ g_variant_unref (location_gps_nmea_value);
+ }
/* If a new one given, use it */
if (location_gps_raw) {
@@ -185,11 +199,14 @@ build_location_dictionary (GVariant *previous,
location_gps_raw_value = mm_location_gps_raw_get_dictionary (location_gps_raw);
}
- if (location_gps_raw_value)
+ if (location_gps_raw_value) {
+ g_assert (!g_variant_is_floating (location_gps_raw_value));
g_variant_builder_add (&builder,
"{uv}",
MM_MODEM_LOCATION_SOURCE_GPS_RAW,
location_gps_raw_value);
+ g_variant_unref (location_gps_raw_value);
+ }
/* If a new one given, use it */
if (location_cdma_bs) {
@@ -198,11 +215,14 @@ build_location_dictionary (GVariant *previous,
location_cdma_bs_value = mm_location_cdma_bs_get_dictionary (location_cdma_bs);
}
- if (location_cdma_bs_value)
+ if (location_cdma_bs_value) {
+ g_assert (!g_variant_is_floating (location_cdma_bs_value));
g_variant_builder_add (&builder,
"{uv}",
MM_MODEM_LOCATION_SOURCE_CDMA_BS,
location_cdma_bs_value);
+ g_variant_unref (location_cdma_bs_value);
+ }
return g_variant_builder_end (&builder);
}
@@ -215,11 +235,7 @@ notify_gps_location_update (MMIfaceModemLocation *self,
MMLocationGpsNmea *location_gps_nmea,
MMLocationGpsRaw *location_gps_raw)
{
- const gchar *dbus_path;
-
- dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
- mm_info ("Modem %s: GPS location updated",
- dbus_path);
+ mm_obj_dbg (self, "GPS location updated");
/* We only update the property if we are supposed to signal
* location */
@@ -233,14 +249,14 @@ notify_gps_location_update (MMIfaceModemLocation *self,
NULL));
}
-void
-mm_iface_modem_location_gps_update (MMIfaceModemLocation *self,
- const gchar *nmea_trace)
+static void
+location_gps_update_nmea (MMIfaceModemLocation *self,
+ const gchar *nmea_trace)
{
MmGdbusModemLocation *skeleton;
- LocationContext *ctx;
- gboolean update_nmea = FALSE;
- gboolean update_raw = FALSE;
+ LocationContext *ctx;
+ gboolean update_nmea = FALSE;
+ gboolean update_raw = FALSE;
ctx = get_location_context (self);
g_object_get (self,
@@ -253,7 +269,7 @@ mm_iface_modem_location_gps_update (MMIfaceModemLocation *self,
g_assert (ctx->location_gps_nmea != NULL);
if (mm_location_gps_nmea_add_trace (ctx->location_gps_nmea, nmea_trace) &&
(ctx->location_gps_nmea_last_time == 0 ||
- time (NULL) - ctx->location_gps_nmea_last_time >= MM_LOCATION_GPS_REFRESH_TIME_SECS)) {
+ time (NULL) - ctx->location_gps_nmea_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) {
ctx->location_gps_nmea_last_time = time (NULL);
update_nmea = TRUE;
}
@@ -263,7 +279,7 @@ mm_iface_modem_location_gps_update (MMIfaceModemLocation *self,
g_assert (ctx->location_gps_raw != NULL);
if (mm_location_gps_raw_add_trace (ctx->location_gps_raw, nmea_trace) &&
(ctx->location_gps_raw_last_time == 0 ||
- time (NULL) - ctx->location_gps_raw_last_time >= MM_LOCATION_GPS_REFRESH_TIME_SECS)) {
+ time (NULL) - ctx->location_gps_raw_last_time >= (glong)mm_gdbus_modem_location_get_gps_refresh_rate (skeleton))) {
ctx->location_gps_raw_last_time = time (NULL);
update_raw = TRUE;
}
@@ -278,6 +294,50 @@ mm_iface_modem_location_gps_update (MMIfaceModemLocation *self,
g_object_unref (skeleton);
}
+void
+mm_iface_modem_location_gps_update (MMIfaceModemLocation *self,
+ const gchar *nmea_trace)
+{
+ /* Helper to debug GPS location related issues. Don't depend on a real GPS
+ * fix for debugging, just use some random values to update */
+#if 0
+ {
+ const gchar *prefix = NULL;
+ const gchar *lat = NULL;
+
+ /* lat N/S just to test which one is used */
+ if (g_str_has_prefix (nmea_trace, "$GPGGA")) {
+ prefix = "GPGGA";
+ lat = "S";
+ } else if (g_str_has_prefix (nmea_trace, "$GNGGA")) {
+ prefix = "GNGGA";
+ lat = "N";
+ }
+
+ if (prefix && lat) {
+ g_autoptr(GString) str = NULL;
+ g_autoptr(GDateTime) now = NULL;
+
+ mm_obj_dbg (self, "GGA trace detected: %s", nmea_trace);
+
+ now = g_date_time_new_now_utc ();
+ str = g_string_new ("");
+ g_string_append_printf (str,
+ "$%s,%02u%02u%02u,4807.038,%s,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47",
+ prefix,
+ g_date_time_get_hour (now),
+ g_date_time_get_minute (now),
+ g_date_time_get_second (now),
+ lat);
+ location_gps_update_nmea (self, str->str);
+ return;
+ }
+ }
+#endif
+
+ location_gps_update_nmea (self, nmea_trace);
+}
+
/*****************************************************************************/
static void
@@ -285,16 +345,15 @@ notify_3gpp_location_update (MMIfaceModemLocation *self,
MmGdbusModemLocation *skeleton,
MMLocation3gpp *location_3gpp)
{
- const gchar *dbus_path;
+ const gchar *operator_code;
- dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
- mm_info ("Modem %s: 3GPP location updated "
- "(MCC: '%u', MNC: '%u', Location area code: '%lX', Cell ID: '%lX')",
- dbus_path,
- mm_location_3gpp_get_mobile_country_code (location_3gpp),
- mm_location_3gpp_get_mobile_network_code (location_3gpp),
- mm_location_3gpp_get_location_area_code (location_3gpp),
- mm_location_3gpp_get_cell_id (location_3gpp));
+ operator_code = mm_location_3gpp_get_operator_code (location_3gpp);
+ mm_obj_dbg (self, "3GPP location updated "
+ "(MCCMNC: '%s', location area code: '%04lX', tracking area code: '%06lX', cell ID: '%08lX')",
+ operator_code ? operator_code : "<none>",
+ mm_location_3gpp_get_location_area_code (location_3gpp),
+ mm_location_3gpp_get_tracking_area_code (location_3gpp),
+ mm_location_3gpp_get_cell_id (location_3gpp));
/* We only update the property if we are supposed to signal
* location */
@@ -308,9 +367,8 @@ notify_3gpp_location_update (MMIfaceModemLocation *self,
}
void
-mm_iface_modem_location_3gpp_update_mcc_mnc (MMIfaceModemLocation *self,
- guint mobile_country_code,
- guint mobile_network_code)
+mm_iface_modem_location_3gpp_update_operator_code (MMIfaceModemLocation *self,
+ const gchar *operator_code)
{
MmGdbusModemLocation *skeleton;
LocationContext *ctx;
@@ -326,10 +384,8 @@ mm_iface_modem_location_3gpp_update_mcc_mnc (MMIfaceModemLocation *self,
guint changed = 0;
g_assert (ctx->location_3gpp != NULL);
- changed += mm_location_3gpp_set_mobile_country_code (ctx->location_3gpp,
- mobile_country_code);
- changed += mm_location_3gpp_set_mobile_network_code (ctx->location_3gpp,
- mobile_network_code);
+ changed += mm_location_3gpp_set_operator_code (ctx->location_3gpp,
+ operator_code);
if (changed)
notify_3gpp_location_update (self, skeleton, ctx->location_3gpp);
}
@@ -338,33 +394,71 @@ mm_iface_modem_location_3gpp_update_mcc_mnc (MMIfaceModemLocation *self,
}
void
-mm_iface_modem_location_3gpp_update_lac_ci (MMIfaceModemLocation *self,
- gulong location_area_code,
- gulong cell_id)
+mm_iface_modem_location_3gpp_update_lac_tac_ci (MMIfaceModemLocation *self,
+ gulong location_area_code,
+ gulong tracking_area_code,
+ gulong cell_id)
{
- MmGdbusModemLocation *skeleton;
- LocationContext *ctx;
+ g_autoptr(MmGdbusModemLocationSkeleton) skeleton = NULL;
+ LocationContext *ctx;
+ guint changed = 0;
+ gulong old_location_area_code;
+ gulong old_tracking_area_code;
+ gulong old_cell_id;
- ctx = get_location_context (self);
g_object_get (self,
MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &skeleton,
NULL);
- if (!skeleton)
+ if (!skeleton || !(mm_gdbus_modem_location_get_enabled (MM_GDBUS_MODEM_LOCATION (skeleton)) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI))
return;
- if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) {
- guint changed = 0;
+ ctx = get_location_context (self);
+ g_assert (ctx->location_3gpp != NULL);
+
+ old_location_area_code = mm_location_3gpp_get_location_area_code (ctx->location_3gpp);
+ old_tracking_area_code = mm_location_3gpp_get_tracking_area_code (ctx->location_3gpp);
+ old_cell_id = mm_location_3gpp_get_cell_id (ctx->location_3gpp);
+
+ /* Update LAC if given, and clear TAC unless a TAC is also given */
+ if (location_area_code) {
+ if (old_location_area_code != location_area_code) {
+ mm_obj_dbg (self, "3GPP location area code updated: '%04lX->%04lX'", old_location_area_code, location_area_code);
+ mm_location_3gpp_set_location_area_code (ctx->location_3gpp, location_area_code);
+ changed++;
+ }
+ if (!tracking_area_code) {
+ if (old_tracking_area_code != 0) {
+ mm_obj_dbg (self, "3GPP tracking area code cleared: '%06lX->%06lX'", old_tracking_area_code, tracking_area_code);
+ mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, 0);
+ changed++;
+ }
+ }
+ }
+ /* Update TAC if given, and clear LAC unless a LAC is also given */
+ if (tracking_area_code) {
+ if (old_tracking_area_code != tracking_area_code) {
+ mm_obj_dbg (self, "3GPP tracking area code updated: '%06lX->%06lX'", old_tracking_area_code, tracking_area_code);
+ mm_location_3gpp_set_tracking_area_code (ctx->location_3gpp, tracking_area_code);
+ changed++;
+ }
+ if (!location_area_code) {
+ if (old_location_area_code != 0) {
+ mm_obj_dbg (self, "3GPP location area code cleared: '%04lX->%04lX'", old_location_area_code, location_area_code);
+ mm_location_3gpp_set_location_area_code (ctx->location_3gpp, 0);
+ changed++;
+ }
+ }
+ }
- g_assert (ctx->location_3gpp != NULL);
- changed += mm_location_3gpp_set_location_area_code (ctx->location_3gpp,
- location_area_code);
- changed += mm_location_3gpp_set_cell_id (ctx->location_3gpp,
- cell_id);
- if (changed)
- notify_3gpp_location_update (self, skeleton, ctx->location_3gpp);
+ /* Cell ID only updated if given. It is assumed that if LAC or TAC are given, CID is also given */
+ if (cell_id && (old_cell_id != cell_id)) {
+ mm_obj_dbg (self, "3GPP cell id updated: '%08lX->%08lX'", old_cell_id, cell_id);
+ mm_location_3gpp_set_cell_id (ctx->location_3gpp, cell_id);
+ changed++;
}
- g_object_unref (skeleton);
+ if (changed)
+ notify_3gpp_location_update (self, MM_GDBUS_MODEM_LOCATION (skeleton), ctx->location_3gpp);
}
void
@@ -381,14 +475,8 @@ mm_iface_modem_location_3gpp_clear (MMIfaceModemLocation *self)
return;
if (mm_gdbus_modem_location_get_enabled (skeleton) & MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI) {
- guint changed = 0;
-
g_assert (ctx->location_3gpp != NULL);
- changed += mm_location_3gpp_set_location_area_code (ctx->location_3gpp, 0);
- changed += mm_location_3gpp_set_cell_id (ctx->location_3gpp, 0);
- changed += mm_location_3gpp_set_mobile_country_code (ctx->location_3gpp, 0);
- changed += mm_location_3gpp_set_mobile_network_code (ctx->location_3gpp, 0);
- if (changed)
+ if (mm_location_3gpp_reset (ctx->location_3gpp))
notify_3gpp_location_update (self, skeleton, ctx->location_3gpp);
}
@@ -402,14 +490,9 @@ notify_cdma_bs_location_update (MMIfaceModemLocation *self,
MmGdbusModemLocation *skeleton,
MMLocationCdmaBs *location_cdma_bs)
{
- const gchar *dbus_path;
-
- dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
- mm_info ("Modem %s: CDMA BS location updated "
- "(Longitude: '%lf', Latitude: '%lf')",
- dbus_path,
- mm_location_cdma_bs_get_longitude (location_cdma_bs),
- mm_location_cdma_bs_get_latitude (location_cdma_bs));
+ mm_obj_dbg (self, "CDMA base station location updated (longitude: '%lf', latitude: '%lf')",
+ mm_location_cdma_bs_get_longitude (location_cdma_bs),
+ mm_location_cdma_bs_get_latitude (location_cdma_bs));
/* We only update the property if we are supposed to signal
* location */
@@ -510,7 +593,9 @@ update_location_source_status (MMIfaceModemLocation *self,
g_clear_object (&ctx->location_cdma_bs);
break;
case MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED:
- case MM_MODEM_LOCATION_SOURCE_AGPS:
+ case MM_MODEM_LOCATION_SOURCE_AGPS_MSA:
+ case MM_MODEM_LOCATION_SOURCE_AGPS_MSB:
+ case MM_MODEM_LOCATION_SOURCE_NONE:
/* Nothing to setup in the context */
default:
break;
@@ -524,24 +609,19 @@ update_location_source_status (MMIfaceModemLocation *self,
/*****************************************************************************/
typedef struct {
- MMIfaceModemLocation *self;
MmGdbusModemLocation *skeleton;
- GSimpleAsyncResult *result;
MMModemLocationSource to_enable;
MMModemLocationSource to_disable;
MMModemLocationSource current;
} SetupGatheringContext;
-static void setup_gathering_step (SetupGatheringContext *ctx);
+static void setup_gathering_step (GTask *task);
static void
-setup_gathering_context_complete_and_free (SetupGatheringContext *ctx)
+setup_gathering_context_free (SetupGatheringContext *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_free (ctx);
}
@@ -550,76 +630,88 @@ setup_gathering_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enable_location_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- SetupGatheringContext *ctx)
+ GTask *task)
{
+ SetupGatheringContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering_finish (self, res, &error)) {
gchar *str;
- update_location_source_status (ctx->self, ctx->current, FALSE);
+ update_location_source_status (self, ctx->current, FALSE);
str = mm_modem_location_source_build_string_from_mask (ctx->current);
g_prefix_error (&error,
"Couldn't enable location '%s' gathering: ",
str);
- g_simple_async_result_take_error (ctx->result, error);
- setup_gathering_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_free (str);
return;
}
/* Keep on with next ones... */
ctx->current = ctx->current << 1;
- setup_gathering_step (ctx);
+ setup_gathering_step (task);
}
static void
disable_location_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- SetupGatheringContext *ctx)
+ GTask *task)
{
+ SetupGatheringContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering_finish (self, res, &error)) {
gchar *str;
/* Back to enabled then */
- update_location_source_status (ctx->self, ctx->current, TRUE);
+ update_location_source_status (self, ctx->current, TRUE);
str = mm_modem_location_source_build_string_from_mask (ctx->current);
g_prefix_error (&error,
"Couldn't disable location '%s' gathering: ",
str);
- g_simple_async_result_take_error (ctx->result, error);
- setup_gathering_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_free (str);
return;
}
/* Keep on with next ones... */
ctx->current = ctx->current << 1;
- setup_gathering_step (ctx);
+ setup_gathering_step (task);
}
static void
-setup_gathering_step (SetupGatheringContext *ctx)
+setup_gathering_step (GTask *task)
{
+ MMIfaceModemLocation *self;
+ SetupGatheringContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Are we done? */
if (ctx->to_enable == MM_MODEM_LOCATION_SOURCE_NONE &&
ctx->to_disable == MM_MODEM_LOCATION_SOURCE_NONE) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- setup_gathering_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- while (ctx->current <= MM_MODEM_LOCATION_SOURCE_AGPS) {
+ while (ctx->current <= MM_MODEM_LOCATION_SOURCE_LAST) {
gchar *source_str;
if (ctx->to_enable & ctx->current) {
@@ -630,41 +722,41 @@ setup_gathering_step (SetupGatheringContext *ctx)
* specific actions to enable the gathering, so that we are
* able to get location updates while the gathering gets
* enabled. */
- update_location_source_status (ctx->self, ctx->current, TRUE);
+ update_location_source_status (self, ctx->current, TRUE);
/* Plugins can run custom actions to enable location gathering */
- if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->enable_location_gathering &&
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->enable_location_gathering_finish) {
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->enable_location_gathering (
- MM_IFACE_MODEM_LOCATION (ctx->self),
+ if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering_finish) {
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->enable_location_gathering (
+ MM_IFACE_MODEM_LOCATION (self),
ctx->current,
(GAsyncReadyCallback)enable_location_gathering_ready,
- ctx);
+ task);
return;
}
source_str = mm_modem_location_source_build_string_from_mask (ctx->current);
- mm_dbg ("Enabled location '%s' gathering...", source_str);
+ mm_obj_dbg (self, "enabled location '%s' gathering...", source_str);
g_free (source_str);
} else if (ctx->to_disable & ctx->current) {
/* Remove from mask */
ctx->to_disable &= ~ctx->current;
- update_location_source_status (ctx->self, ctx->current, FALSE);
+ update_location_source_status (self, ctx->current, FALSE);
/* Plugins can run custom actions to disable location gathering */
- if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->disable_location_gathering &&
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->disable_location_gathering_finish) {
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->disable_location_gathering (
- MM_IFACE_MODEM_LOCATION (ctx->self),
+ if (MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering_finish) {
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->disable_location_gathering (
+ MM_IFACE_MODEM_LOCATION (self),
ctx->current,
(GAsyncReadyCallback)disable_location_gathering_ready,
- ctx);
+ task);
return;
}
source_str = mm_modem_location_source_build_string_from_mask (ctx->current);
- mm_dbg ("Disabled location '%s' gathering...", source_str);
+ mm_obj_dbg (self, "disabled location '%s' gathering...", source_str);
g_free (source_str);
}
@@ -675,7 +767,7 @@ setup_gathering_step (SetupGatheringContext *ctx)
/* We just need to finish now */
g_assert (ctx->to_enable == MM_MODEM_LOCATION_SOURCE_NONE);
g_assert (ctx->to_disable == MM_MODEM_LOCATION_SOURCE_NONE);
- setup_gathering_step (ctx);
+ setup_gathering_step (task);
}
static void
@@ -685,25 +777,27 @@ setup_gathering (MMIfaceModemLocation *self,
gpointer user_data)
{
SetupGatheringContext *ctx;
+ GTask *task;
MMModemLocationSource currently_enabled;
MMModemLocationSource source;
gchar *str;
+ gboolean allow_gps_unmanaged_always = FALSE;
ctx = g_new (SetupGatheringContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- setup_gathering);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)setup_gathering_context_free);
+
g_object_get (self,
- MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton,
+ MM_IFACE_MODEM_LOCATION_DBUS_SKELETON, &ctx->skeleton,
+ MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS, &allow_gps_unmanaged_always,
NULL);
if (!ctx->skeleton) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get interface skeleton");
- setup_gathering_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
@@ -715,8 +809,8 @@ setup_gathering (MMIfaceModemLocation *self,
ctx->to_disable = MM_MODEM_LOCATION_SOURCE_NONE;
/* Loop through all known bits in the bitmask to enable/disable specific location sources */
- for (source = MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI;
- source <= MM_MODEM_LOCATION_SOURCE_AGPS;
+ for (source = MM_MODEM_LOCATION_SOURCE_FIRST;
+ source <= MM_MODEM_LOCATION_SOURCE_LAST;
source = source << 1) {
/* skip unsupported sources */
if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & source))
@@ -727,7 +821,7 @@ setup_gathering (MMIfaceModemLocation *self,
if (mask & source) {
/* Source set in mask, need to enable if disabled */
if (currently_enabled & source)
- mm_dbg ("Location '%s' gathering is already enabled...", str);
+ mm_obj_dbg (self, "location '%s' gathering is already enabled...", str);
else
ctx->to_enable |= source;
} else {
@@ -735,43 +829,61 @@ setup_gathering (MMIfaceModemLocation *self,
if (currently_enabled & source)
ctx->to_disable |= source;
else
- mm_dbg ("Location '%s' gathering is already disabled...", str);
+ mm_obj_dbg (self, "location '%s' gathering is already disabled...", str);
}
g_free (str);
}
/* When standard GPS retrieval (RAW/NMEA) is enabled, we cannot enable the
- * UNMANAGED setup, and viceversa. */
- if ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED &&
- currently_enabled & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) ||
- (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) &&
- currently_enabled & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED) ||
- (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) &&
- ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot have both unmanaged GPS and raw/nmea GPS enabled at the same time");
- setup_gathering_context_complete_and_free (ctx);
+ * UNMANAGED setup, and viceversa, unless explicitly allowed to do so by the
+ * plugin implementation (e.g. if the RAW/NMEA sources don't use the same TTY
+ * as the GPS UNMANAGED setup. */
+ if (!allow_gps_unmanaged_always &&
+ ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED &&
+ currently_enabled & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) ||
+ (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) &&
+ currently_enabled & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED) ||
+ (ctx->to_enable & (MM_MODEM_LOCATION_SOURCE_GPS_RAW | MM_MODEM_LOCATION_SOURCE_GPS_NMEA) &&
+ ctx->to_enable & MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED))) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot have both unmanaged GPS and raw/nmea GPS enabled at the same time");
+ g_object_unref (task);
+ return;
+ }
+
+ /* MSA A-GPS and MSB A-GPS cannot be set at the same time */
+ if ((ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSA &&
+ currently_enabled & MM_MODEM_LOCATION_SOURCE_AGPS_MSB) ||
+ (ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSB &&
+ currently_enabled & MM_MODEM_LOCATION_SOURCE_AGPS_MSA) ||
+ (ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSA &&
+ ctx->to_enable & MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot have both MSA A-GPS and MSB A-GPS enabled at the same time");
+ g_object_unref (task);
return;
}
if (ctx->to_enable != MM_MODEM_LOCATION_SOURCE_NONE) {
str = mm_modem_location_source_build_string_from_mask (ctx->to_enable);
- mm_dbg ("Need to enable the following location sources: '%s'", str);
+ mm_obj_dbg (self, "need to enable the following location sources: '%s'", str);
g_free (str);
}
if (ctx->to_disable != MM_MODEM_LOCATION_SOURCE_NONE) {
str = mm_modem_location_source_build_string_from_mask (ctx->to_disable);
- mm_dbg ("Need to disable the following location sources: '%s'", str);
+ mm_obj_dbg (self, "need to disable the following location sources: '%s'", str);
g_free (str);
}
/* Start enabling/disabling location sources */
- ctx->current = MM_MODEM_LOCATION_SOURCE_3GPP_LAC_CI;
- setup_gathering_step (ctx);
+ ctx->current = MM_MODEM_LOCATION_SOURCE_FIRST;
+ setup_gathering_step (task);
}
/*****************************************************************************/
@@ -856,8 +968,8 @@ handle_setup_auth_ready (MMBaseModem *self,
/* Enable/disable location signaling */
location_ctx = get_location_context (ctx->self);
if (mm_gdbus_modem_location_get_signals_location (ctx->skeleton) != ctx->signal_location) {
- mm_dbg ("%s location signaling",
- ctx->signal_location ? "Enabling" : "Disabling");
+ mm_obj_dbg (self, "%s location signaling",
+ ctx->signal_location ? "enabling" : "disabling");
mm_gdbus_modem_location_set_signals_location (ctx->skeleton,
ctx->signal_location);
if (ctx->signal_location)
@@ -875,7 +987,7 @@ handle_setup_auth_ready (MMBaseModem *self,
}
str = mm_modem_location_source_build_string_from_mask (ctx->sources);
- mm_dbg ("Setting up location sources: '%s'", str);
+ mm_obj_dbg (self, "setting up location sources: '%s'", str);
g_free (str);
/* Go on to enable or disable the requested sources */
@@ -974,7 +1086,7 @@ handle_set_supl_server_auth_ready (MMBaseModem *self,
}
/* If A-GPS is NOT supported, set error */
- if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & MM_MODEM_LOCATION_SOURCE_AGPS)) {
+ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA | MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
@@ -983,6 +1095,13 @@ handle_set_supl_server_auth_ready (MMBaseModem *self,
return;
}
+ /* Validate SUPL address string: either FQDN:PORT or IP:PORT */
+ if (!mm_parse_supl_address (ctx->supl, NULL, NULL, NULL, &error)) {
+ g_dbus_method_invocation_return_gerror (ctx->invocation, error);
+ handle_set_supl_server_context_free (ctx);
+ return;
+ }
+
/* Check if plugin implements it */
if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server ||
!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->set_supl_server_finish) {
@@ -1026,6 +1145,193 @@ handle_set_supl_server (MmGdbusModemLocation *skeleton,
/*****************************************************************************/
typedef struct {
+ MmGdbusModemLocation *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemLocation *self;
+ GVariant *datav;
+} HandleInjectAssistanceDataContext;
+
+static void
+handle_inject_assistance_data_context_free (HandleInjectAssistanceDataContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_variant_unref (ctx->datav);
+ g_slice_free (HandleInjectAssistanceDataContext, ctx);
+}
+
+static void
+inject_assistance_data_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ HandleInjectAssistanceDataContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem_location_complete_inject_assistance_data (ctx->skeleton, ctx->invocation);
+
+ handle_inject_assistance_data_context_free (ctx);
+}
+
+static void
+handle_inject_assistance_data_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleInjectAssistanceDataContext *ctx)
+{
+ GError *error = NULL;
+ const guint8 *data;
+ gsize data_size;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_inject_assistance_data_context_free (ctx);
+ return;
+ }
+
+ /* If the type is NOT supported, set error */
+ if (mm_gdbus_modem_location_get_supported_assistance_data (ctx->skeleton) == MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot inject assistance data: ununsupported");
+ handle_inject_assistance_data_context_free (ctx);
+ return;
+ }
+
+ /* Check if plugin implements it */
+ if (!MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data ||
+ !MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot inject assistance data: not implemented");
+ handle_inject_assistance_data_context_free (ctx);
+ return;
+ }
+
+ data = (const guint8 *) g_variant_get_fixed_array (ctx->datav, &data_size, sizeof (guint8));
+
+ /* Request to inject assistance data */
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->inject_assistance_data (ctx->self,
+ data,
+ data_size,
+ (GAsyncReadyCallback)inject_assistance_data_ready,
+ ctx);
+}
+
+static gboolean
+handle_inject_assistance_data (MmGdbusModemLocation *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *datav,
+ MMIfaceModemLocation *self)
+{
+ HandleInjectAssistanceDataContext *ctx;
+
+ ctx = g_slice_new (HandleInjectAssistanceDataContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->datav = g_variant_ref (datav);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_inject_assistance_data_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemLocation *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemLocation *self;
+ guint rate;
+} HandleSetGpsRefreshRateContext;
+
+static void
+handle_set_gps_refresh_rate_context_free (HandleSetGpsRefreshRateContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetGpsRefreshRateContext, ctx);
+}
+
+static void
+handle_set_gps_refresh_rate_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetGpsRefreshRateContext *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_set_gps_refresh_rate_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 set SUPL server: "
+ "device not yet enabled");
+ handle_set_gps_refresh_rate_context_free (ctx);
+ return;
+ }
+
+ /* If GPS is NOT supported, set error */
+ if (!(mm_gdbus_modem_location_get_capabilities (ctx->skeleton) & ((MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_NMEA)))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set GPS refresh rate: GPS not supported");
+ handle_set_gps_refresh_rate_context_free (ctx);
+ return;
+ }
+
+ /* Set the new rate in the interface */
+ mm_gdbus_modem_location_set_gps_refresh_rate (ctx->skeleton, ctx->rate);
+ mm_gdbus_modem_location_complete_set_gps_refresh_rate (ctx->skeleton, ctx->invocation);
+ handle_set_gps_refresh_rate_context_free (ctx);
+}
+
+static gboolean
+handle_set_gps_refresh_rate (MmGdbusModemLocation *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint rate,
+ MMIfaceModemLocation *self)
+{
+ HandleSetGpsRefreshRateContext *ctx;
+
+ ctx = g_slice_new (HandleSetGpsRefreshRateContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->rate = rate;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_gps_refresh_rate_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
MmGdbusModemLocation *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModemLocation *self;
@@ -1104,7 +1410,7 @@ handle_get_location (MmGdbusModemLocation *skeleton,
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
@@ -1113,18 +1419,13 @@ typedef enum {
} DisablingStep;
struct _DisablingContext {
- MMIfaceModemLocation *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModemLocation *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -1135,48 +1436,59 @@ mm_iface_modem_location_disable_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disabling_location_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
if (!setup_gathering_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- disabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModemLocation *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_DISABLE_GATHERING:
- setup_gathering (ctx->self,
+ setup_gathering (self,
MM_MODEM_LOCATION_SOURCE_NONE,
(GAsyncReadyCallback)disabling_location_gathering_ready,
- ctx);
+ task);
return;
case DISABLING_STEP_LAST:
/* We are done without errors! */
- clear_location_context (ctx->self);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disabling_context_complete_and_free (ctx);
+ clear_location_context (self);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1188,33 +1500,33 @@ mm_iface_modem_location_disable (MMIfaceModemLocation *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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_location_disable);
ctx->step = DISABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_LOCATION_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
@@ -1223,76 +1535,65 @@ typedef enum {
} EnablingStep;
struct _EnablingContext {
- MMIfaceModemLocation *self;
EnablingStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModemLocation *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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_location_enable_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enabling_location_gathering_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
if (!setup_gathering_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModemLocation *self;
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_ENABLE_GATHERING: {
MMModemLocationSource default_sources;
@@ -1302,20 +1603,24 @@ interface_enabling_step (EnablingContext *ctx)
default_sources &= ~(MM_MODEM_LOCATION_SOURCE_GPS_RAW |
MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
MM_MODEM_LOCATION_SOURCE_GPS_UNMANAGED |
- MM_MODEM_LOCATION_SOURCE_AGPS);
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB);
- setup_gathering (ctx->self,
+ setup_gathering (self,
default_sources,
(GAsyncReadyCallback)enabling_location_gathering_ready,
- ctx);
+ task);
return;
}
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1328,88 +1633,121 @@ mm_iface_modem_location_enable (MMIfaceModemLocation *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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_location_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_LOCATION_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_CAPABILITIES,
INITIALIZATION_STEP_VALIDATE_CAPABILITIES,
INITIALIZATION_STEP_SUPL_SERVER,
+ INITIALIZATION_STEP_SUPPORTED_ASSISTANCE_DATA,
+ INITIALIZATION_STEP_ASSISTANCE_DATA_SERVERS,
+ INITIALIZATION_STEP_GPS_REFRESH_RATE,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemLocation *self;
MmGdbusModemLocation *skeleton;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
InitializationStep step;
MMModemLocationSource capabilities;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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)
+static void
+load_assistance_data_servers_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
{
- if (!g_cancellable_is_cancelled (ctx->cancellable))
- return FALSE;
+ GError *error = NULL;
+ InitializationContext *ctx;
+ gchar **servers;
- 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;
+ ctx = g_task_get_task_data (task);
+
+ servers = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load assistance data servers: %s", error->message);
+ g_error_free (error);
+ }
+
+ mm_gdbus_modem_location_set_assistance_data_servers (ctx->skeleton, (const gchar *const *)servers);
+ g_strfreev (servers);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+load_supported_assistance_data_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMModemLocationAssistanceDataType mask;
+ InitializationContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ mask = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load supported assistance data types: %s", error->message);
+ g_error_free (error);
+ }
+
+ mm_gdbus_modem_location_set_supported_assistance_data (ctx->skeleton, (guint32) mask);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
}
static void
load_supl_server_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
GError *error = NULL;
gchar *supl;
+ InitializationContext *ctx;
+
+ ctx = g_task_get_task_data (task);
supl = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server_finish (self, res, &error);
if (error) {
- mm_warn ("couldn't load SUPL server: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load SUPL server: %s", error->message);
g_error_free (error);
}
@@ -1418,19 +1756,22 @@ load_supl_server_ready (MMIfaceModemLocation *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_capabilities_ready (MMIfaceModemLocation *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
GError *error = NULL;
+ InitializationContext *ctx;
+
+ ctx = g_task_get_task_data (task);
ctx->capabilities = MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities_finish (self, res, &error);
if (error) {
- mm_warn ("couldn't load location capabilities: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load location capabilities: %s", error->message);
g_error_free (error);
}
@@ -1438,64 +1779,114 @@ load_capabilities_ready (MMIfaceModemLocation *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModemLocation *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_CAPABILITIES:
/* Location capabilities value is meant to be loaded only once during
* the whole lifetime of the modem. Therefore, if we already have it
* loaded, don't try to load it again. */
if (!mm_gdbus_modem_location_get_capabilities (ctx->skeleton) &&
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->load_capabilities &&
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->load_capabilities_finish) {
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->load_capabilities (
- ctx->self,
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities_finish) {
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_capabilities (
+ self,
(GAsyncReadyCallback)load_capabilities_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_VALIDATE_CAPABILITIES:
/* If the modem doesn't support any location capabilities, we won't export
* the interface. We just report an UNSUPPORTED error. */
if (ctx->capabilities == MM_MODEM_LOCATION_SOURCE_NONE) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "The modem doesn't have location capabilities");
- initialization_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "The modem doesn't have location capabilities");
+ g_object_unref (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_SUPL_SERVER:
/* If the modem supports A-GPS, load SUPL server */
- if (ctx->capabilities & MM_MODEM_LOCATION_SOURCE_AGPS &&
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->load_supl_server &&
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->load_supl_server_finish) {
- MM_IFACE_MODEM_LOCATION_GET_INTERFACE (ctx->self)->load_supl_server (
- ctx->self,
+ if ((ctx->capabilities & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB)) &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server_finish) {
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supl_server (
+ self,
(GAsyncReadyCallback)load_supl_server_ready,
- ctx);
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_SUPPORTED_ASSISTANCE_DATA:
+ /* If the modem supports any GPS-related technology, check assistance data types supported */
+ if ((ctx->capabilities & (MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_NMEA)) &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data_finish) {
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_supported_assistance_data (
+ self,
+ (GAsyncReadyCallback)load_supported_assistance_data_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_ASSISTANCE_DATA_SERVERS:
+ /* If any assistance data supported, load servers */
+ if ((mm_gdbus_modem_location_get_supported_assistance_data (ctx->skeleton) != MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE) &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers &&
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers_finish) {
+ MM_IFACE_MODEM_LOCATION_GET_INTERFACE (self)->load_assistance_data_servers (
+ self,
+ (GAsyncReadyCallback)load_assistance_data_servers_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_GPS_REFRESH_RATE:
+ /* If we have GPS capabilities, expose the GPS refresh rate */
+ if (ctx->capabilities & ((MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_GPS_NMEA)))
+ /* Set the default rate in the interface */
+ mm_gdbus_modem_location_set_gps_refresh_rate (ctx->skeleton, MM_LOCATION_GPS_REFRESH_TIME_SECS);
+
+ ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -1504,23 +1895,34 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-setup",
G_CALLBACK (handle_setup),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-set-supl-server",
G_CALLBACK (handle_set_supl_server),
- ctx->self);
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-inject-assistance-data",
+ G_CALLBACK (handle_inject_assistance_data),
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-set-gps-refresh-rate",
+ G_CALLBACK (handle_set_gps_refresh_rate),
+ self);
g_signal_connect (ctx->skeleton,
"handle-get-location",
G_CALLBACK (handle_get_location),
- ctx->self);
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_location (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_location (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_LOCATION (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1531,7 +1933,7 @@ mm_iface_modem_location_initialize_finish (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -1542,6 +1944,7 @@ mm_iface_modem_location_initialize (MMIfaceModemLocation *self,
{
InitializationContext *ctx;
MmGdbusModemLocation *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -1552,6 +1955,7 @@ mm_iface_modem_location_initialize (MMIfaceModemLocation *self,
/* Set all initial property defaults */
mm_gdbus_modem_location_set_capabilities (skeleton, MM_MODEM_LOCATION_SOURCE_NONE);
+ mm_gdbus_modem_location_set_supported_assistance_data (skeleton, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
mm_gdbus_modem_location_set_enabled (skeleton, MM_MODEM_LOCATION_SOURCE_NONE);
mm_gdbus_modem_location_set_signals_location (skeleton, FALSE);
mm_gdbus_modem_location_set_location (skeleton,
@@ -1565,17 +1969,14 @@ mm_iface_modem_location_initialize (MMIfaceModemLocation *self,
/* Perform async initialization here */
ctx = g_new0 (InitializationContext, 1);
- ctx->self = g_object_ref (self);
ctx->capabilities = MM_MODEM_LOCATION_SOURCE_NONE;
- ctx->cancellable = g_object_ref (cancellable);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_location_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
@@ -1607,6 +2008,14 @@ iface_modem_location_init (gpointer g_iface)
MM_GDBUS_TYPE_MODEM_LOCATION_SKELETON,
G_PARAM_READWRITE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS,
+ "Allow unmanaged GPS always",
+ "Whether to always allow GPS unmanaged, even when raw/nmea GPS sources are enabled",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
initialized = TRUE;
}
diff --git a/src/mm-iface-modem-location.h b/src/mm-iface-modem-location.h
index 96fe9b3b..c0a7fbd9 100644
--- a/src/mm-iface-modem-location.h
+++ b/src/mm-iface-modem-location.h
@@ -27,7 +27,8 @@
#define MM_IS_IFACE_MODEM_LOCATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_LOCATION))
#define MM_IFACE_MODEM_LOCATION_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_LOCATION, MMIfaceModemLocation))
-#define MM_IFACE_MODEM_LOCATION_DBUS_SKELETON "iface-modem-location-dbus-skeleton"
+#define MM_IFACE_MODEM_LOCATION_DBUS_SKELETON "iface-modem-location-dbus-skeleton"
+#define MM_IFACE_MODEM_LOCATION_ALLOW_GPS_UNMANAGED_ALWAYS "iface-modem-location-allow-gps-unmanaged-always"
typedef struct _MMIfaceModemLocation MMIfaceModemLocation;
@@ -50,6 +51,22 @@ struct _MMIfaceModemLocation {
GAsyncResult *res,
GError **error);
+ /* Loading of the AssistanceDataServers property */
+ void (* load_assistance_data_servers) (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gchar ** (* load_assistance_data_servers_finish) (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Loading of the SupportedAssistanceData property */
+ void (* load_supported_assistance_data) (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ MMModemLocationAssistanceDataType (* load_supported_assistance_data_finish) (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Enable location gathering (async) */
void (* enable_location_gathering) (MMIfaceModemLocation *self,
MMModemLocationSource source,
@@ -76,9 +93,20 @@ struct _MMIfaceModemLocation {
gboolean (*set_supl_server_finish) (MMIfaceModemLocation *self,
GAsyncResult *res,
GError **error);
+
+ /* Inject assistance data (async) */
+ void (* inject_assistance_data) (MMIfaceModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*inject_assistance_data_finish) (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
};
GType mm_iface_modem_location_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemLocation, g_object_unref)
/* Initialize Location interface (async) */
void mm_iface_modem_location_initialize (MMIfaceModemLocation *self,
@@ -111,12 +139,12 @@ void mm_iface_modem_location_shutdown (MMIfaceModemLocation *self);
/* Update 3GPP (LAC/CI) location */
void mm_iface_modem_location_3gpp_clear (MMIfaceModemLocation *self);
-void mm_iface_modem_location_3gpp_update_mcc_mnc (MMIfaceModemLocation *self,
- guint mobile_country_code,
- guint mobile_network_code);
-void mm_iface_modem_location_3gpp_update_lac_ci (MMIfaceModemLocation *self,
- gulong location_area_code,
- gulong cell_id);
+void mm_iface_modem_location_3gpp_update_operator_code (MMIfaceModemLocation *self,
+ const gchar *operator_code);
+void mm_iface_modem_location_3gpp_update_lac_tac_ci (MMIfaceModemLocation *self,
+ gulong location_area_code,
+ gulong tracking_area_code,
+ gulong cell_id);
/* Update GPS location */
void mm_iface_modem_location_gps_update (MMIfaceModemLocation *self,
diff --git a/src/mm-iface-modem-messaging.c b/src/mm-iface-modem-messaging.c
index d2ef6e86..f9c2c98b 100644
--- a/src/mm-iface-modem-messaging.c
+++ b/src/mm-iface-modem-messaging.c
@@ -20,7 +20,7 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
#include "mm-sms-list.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#define SUPPORT_CHECKED_TAG "messaging-support-checked-tag"
#define SUPPORTED_TAG "messaging-supported-tag"
@@ -410,29 +410,27 @@ handle_list (MmGdbusModemMessaging *skeleton,
gboolean
mm_iface_modem_messaging_take_part (MMIfaceModemMessaging *self,
- MMSmsPart *sms_part,
- MMSmsState state,
- MMSmsStorage storage)
+ MMSmsPart *sms_part,
+ MMSmsState state,
+ MMSmsStorage storage)
{
- MMSmsList *list = NULL;
- GError *error = NULL;
- gboolean added;
+ g_autoptr(MMSmsList) list = NULL;
+ g_autoptr(GError) error = NULL;
+ gboolean added = FALSE;
g_object_get (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, &list,
NULL);
- if (!list)
- return FALSE;
- added = mm_sms_list_take_part (list, sms_part, state, storage, &error);
- if (!added) {
- mm_dbg ("Couldn't take part in SMS list: '%s'", error->message);
- g_error_free (error);
+ if (list) {
+ added = mm_sms_list_take_part (list, sms_part, state, storage, &error);
+ if (!added)
+ mm_obj_dbg (self, "couldn't take part in SMS list: %s", error->message);
+ }
- /* If part wasn't taken, we need to free the part ourselves */
+ /* If part wasn't taken, we need to free the part ourselves */
+ if (!added)
mm_sms_part_free (sms_part);
- }
- g_object_unref (list);
return added;
}
@@ -498,27 +496,25 @@ update_message_list (MmGdbusModemMessaging *skeleton,
paths = mm_sms_list_get_paths (list);
mm_gdbus_modem_messaging_set_messages (skeleton, (const gchar *const *)paths);
g_strfreev (paths);
+
+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
}
static void
-sms_added (MMSmsList *list,
- const gchar *sms_path,
- gboolean received,
+sms_added (MMSmsList *list,
+ const gchar *sms_path,
+ gboolean received,
MmGdbusModemMessaging *skeleton)
{
- 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);
}
static void
-sms_deleted (MMSmsList *list,
- const gchar *sms_path,
+sms_deleted (MMSmsList *list,
+ const gchar *sms_path,
MmGdbusModemMessaging *skeleton)
{
- mm_dbg ("Deleted SMS at '%s'", sms_path);
update_message_list (skeleton, list);
mm_gdbus_modem_messaging_emit_deleted (skeleton, sms_path);
}
@@ -526,7 +522,7 @@ sms_deleted (MMSmsList *list,
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
@@ -536,18 +532,13 @@ typedef enum {
} DisablingStep;
struct _DisablingContext {
- MMIfaceModemMessaging *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModemMessaging *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -558,91 +549,104 @@ mm_iface_modem_messaging_disable_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disable_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
cleanup_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModemMessaging *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
/* Allow cleaning up unsolicited events */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->disable_unsolicited_events &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->disable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
/* Allow cleaning up unsolicited events */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_LAST:
/* Clear SMS list */
- g_object_set (ctx->self,
+ g_object_set (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, NULL,
NULL);
/* We are done without errors! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- disabling_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -654,33 +658,33 @@ mm_iface_modem_messaging_disable (MMIfaceModemMessaging *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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_messaging_disable);
ctx->step = DISABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_MESSAGING_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
@@ -693,118 +697,108 @@ typedef enum {
} EnablingStep;
struct _EnablingContext {
- MMIfaceModemMessaging *self;
EnablingStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModemMessaging *skeleton;
guint mem1_storage_index;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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_messaging_enable_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_sms_format_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
-static void load_initial_sms_parts_from_storages (EnablingContext *ctx);
+static void load_initial_sms_parts_from_storages (GTask *task);
static void
load_initial_sms_parts_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish (self, res, &error);
if (error) {
StorageContext *storage_ctx;
- storage_ctx = get_storage_context (ctx->self);
- mm_dbg ("Couldn't load SMS parts from storage '%s': '%s'",
- mm_sms_storage_get_string (g_array_index (storage_ctx->supported_mem1,
- MMSmsStorage,
- ctx->mem1_storage_index)),
- error->message);
+ storage_ctx = get_storage_context (self);
+ mm_obj_dbg (self, "couldn't load SMS parts from storage '%s': %s",
+ mm_sms_storage_get_string (g_array_index (storage_ctx->supported_mem1,
+ MMSmsStorage,
+ ctx->mem1_storage_index)),
+ error->message);
g_error_free (error);
}
/* Go on with the storage iteration */
ctx->mem1_storage_index++;
- load_initial_sms_parts_from_storages (ctx);
+ load_initial_sms_parts_from_storages (task);
}
static void
set_default_storage_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish (self, res, &error)) {
- mm_dbg ("Couldn't set default storage: '%s'", error->message);
+ mm_obj_warn (self, "could not set default storage: %s", error->message);
g_error_free (error);
}
/* Go on with next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-load_initial_sms_parts_from_storages (EnablingContext *ctx)
+load_initial_sms_parts_from_storages (GTask *task)
{
+ MMIfaceModemMessaging *self;
+ EnablingContext *ctx;
gboolean all_loaded = FALSE;
StorageContext *storage_ctx;
- storage_ctx = get_storage_context (ctx->self);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ storage_ctx = get_storage_context (self);
if (!storage_ctx->supported_mem1 || ctx->mem1_storage_index >= storage_ctx->supported_mem1->len)
all_loaded = TRUE;
@@ -822,55 +816,58 @@ load_initial_sms_parts_from_storages (EnablingContext *ctx)
if (all_loaded) {
/* Go on with next step */
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
return;
}
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_initial_sms_parts (
- ctx->self,
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts (
+ self,
g_array_index (storage_ctx->supported_mem1,
MMSmsStorage,
ctx->mem1_storage_index),
(GAsyncReadyCallback)load_initial_sms_parts_ready,
- ctx);
- return;
+ task);
}
static void
setup_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_MESSAGING_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
/* Not critical! */
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) {
- mm_dbg ("Couldn't enable unsolicited events: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message);
g_error_free (error);
}
/* Go on with next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static MMSmsStorage
@@ -898,19 +895,48 @@ get_best_initial_default_sms_storage (MMIfaceModemMessaging *self)
return default_storages_preference[i];
}
+static MMSmsStorage
+get_single_default_sms_storage (MMIfaceModemMessaging *self)
+{
+ StorageContext *storage_ctx;
+
+ storage_ctx = get_storage_context (self);
+
+ /* If there is one single storage supported for storing and receiving, just
+ * use that one. */
+ if (storage_ctx->supported_mem2 && storage_ctx->supported_mem2->len == 1 &&
+ storage_ctx->supported_mem3 && storage_ctx->supported_mem3->len == 1) {
+ MMSmsStorage storing_default;
+
+ storing_default = g_array_index (storage_ctx->supported_mem2, MMSmsStorage, 0);
+ if (storing_default == g_array_index (storage_ctx->supported_mem3, MMSmsStorage, 0))
+ return storing_default;
+ }
+
+ return MM_SMS_STORAGE_UNKNOWN;
+}
+
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModemMessaging *self;
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST: {
MMSmsList *list;
- list = mm_sms_list_new (MM_BASE_MODEM (ctx->self));
- g_object_set (ctx->self,
+ list = mm_sms_list_new (MM_BASE_MODEM (self));
+ g_object_set (self,
MM_IFACE_MODEM_MESSAGING_SMS_LIST, list,
NULL);
@@ -926,91 +952,95 @@ interface_enabling_step (EnablingContext *ctx)
g_object_unref (list);
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall through */
case ENABLING_STEP_SETUP_SMS_FORMAT:
/* Allow setting SMS format to use */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_sms_format &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_sms_format_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_sms_format (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_sms_format (
+ self,
(GAsyncReadyCallback)setup_sms_format_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
- case ENABLING_STEP_STORAGE_DEFAULTS:
- /* Set storage defaults */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->set_default_storage &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->set_default_storage_finish) {
- MMSmsStorage default_storage;
-
- default_storage = get_best_initial_default_sms_storage (ctx->self);
-
- /* Already bound to the 'default-storage' property in the skeleton */
- g_object_set (ctx->self,
- MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, default_storage,
- NULL);
-
- if (default_storage != MM_SMS_STORAGE_UNKNOWN) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->set_default_storage (
- ctx->self,
- default_storage,
- (GAsyncReadyCallback)set_default_storage_ready,
- ctx);
- return;
- }
+ case ENABLING_STEP_STORAGE_DEFAULTS: {
+ MMSmsStorage default_storage;
+
+ /* Is there only one single storage supported? if so, we don't care if
+ * setting default storage is implemented or not. */
+ default_storage = get_single_default_sms_storage (self);
+ if (default_storage == MM_SMS_STORAGE_UNKNOWN)
+ default_storage = get_best_initial_default_sms_storage (self);
- mm_info ("Cannot set default storage, none of the suggested ones supported");
+ /* Already bound to the 'default-storage' property in the skeleton */
+ g_object_set (self,
+ MM_IFACE_MODEM_MESSAGING_SMS_DEFAULT_STORAGE, default_storage,
+ NULL);
+
+ if (default_storage == MM_SMS_STORAGE_UNKNOWN)
+ mm_obj_warn (self, "cannot set default storage, none of the suggested ones supported");
+ else if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->set_default_storage (
+ self,
+ default_storage,
+ (GAsyncReadyCallback)set_default_storage_ready,
+ task);
+ return;
}
- /* Fall down to next step */
+
ctx->step++;
+ } /* fall through */
case ENABLING_STEP_LOAD_INITIAL_SMS_PARTS:
/* Allow loading the initial list of SMS parts */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_initial_sms_parts &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_initial_sms_parts_finish) {
- load_initial_sms_parts_from_storages (ctx);
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_initial_sms_parts_finish) {
+ load_initial_sms_parts_from_storages (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
/* Allow setting up unsolicited events */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_unsolicited_events &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->setup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
/* Allow setting up unsolicited events */
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->enable_unsolicited_events &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->enable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1023,76 +1053,55 @@ mm_iface_modem_messaging_enable (MMIfaceModemMessaging *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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_messaging_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_MESSAGING_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_CHECK_SUPPORT,
INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES,
+ INITIALIZATION_STEP_INIT_CURRENT_STORAGES,
INITIALIZATION_STEP_LAST
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemMessaging *self;
MmGdbusModemMessaging *skeleton;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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
skip_unknown_storages (GArray *mem)
{
@@ -1111,11 +1120,13 @@ skip_unknown_storages (GArray *mem)
static void
load_supported_storages_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
StorageContext *storage_ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
storage_ctx = get_storage_context (self);
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish (
self,
@@ -1124,7 +1135,7 @@ load_supported_storages_ready (MMIfaceModemMessaging *self,
&storage_ctx->supported_mem2,
&storage_ctx->supported_mem3,
&error)) {
- mm_dbg ("Couldn't load supported storages: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't load supported storages: %s", error->message);
g_error_free (error);
} else {
gchar *mem1;
@@ -1138,17 +1149,17 @@ load_supported_storages_ready (MMIfaceModemMessaging *self,
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,
+ mem1 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem1->data,
storage_ctx->supported_mem1->len);
- mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem2->data,
+ mem2 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem2->data,
storage_ctx->supported_mem2->len);
- mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)storage_ctx->supported_mem3->data,
+ mem3 = mm_common_build_sms_storages_string ((MMSmsStorage *)(gpointer)storage_ctx->supported_mem3->data,
storage_ctx->supported_mem3->len);
- mm_dbg ("Supported storages loaded:");
- mm_dbg (" mem1 (list/read/delete) storages: '%s'", mem1);
- mm_dbg (" mem2 (write/send) storages: '%s'", mem2);
- mm_dbg (" mem3 (reception) storages: '%s'", mem3);
+ mm_obj_dbg (self, "supported storages loaded:");
+ mm_obj_dbg (self, " mem1 (list/read/delete) storages: '%s'", mem1);
+ mm_obj_dbg (self, " mem2 (write/send) storages: '%s'", mem2);
+ mm_obj_dbg (self, " mem3 (reception) storages: '%s'", mem3);
g_free (mem1);
g_free (mem2);
g_free (mem3);
@@ -1182,14 +1193,15 @@ load_supported_storages_ready (MMIfaceModemMessaging *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
check_support_ready (MMIfaceModemMessaging *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish (self,
@@ -1197,7 +1209,7 @@ check_support_ready (MMIfaceModemMessaging *self,
&error)) {
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Messaging support check failed: '%s'", error->message);
+ mm_obj_dbg (self, "messaging support check failed: %s", error->message);
g_error_free (error);
}
} else {
@@ -1208,16 +1220,48 @@ check_support_ready (MMIfaceModemMessaging *self,
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+init_current_storages_ready (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages_finish (
+ self,
+ res,
+ &error)) {
+ mm_obj_dbg (self, "couldn't initialize current storages: %s", error->message);
+ g_error_free (error);
+ } else
+ mm_obj_dbg (self, "current storages initialized");
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+interface_initialization_step (GTask *task)
+{
+ MMIfaceModemMessaging *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
@@ -1229,60 +1273,72 @@ interface_initialization_step (InitializationContext *ctx)
supported_quark = (g_quark_from_static_string (
SUPPORTED_TAG));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_CHECK_SUPPORT:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
support_checked_quark))) {
/* Set the checked flag so that we don't run it again */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
support_checked_quark,
GUINT_TO_POINTER (TRUE));
/* Initially, assume we don't support it */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
supported_quark,
GUINT_TO_POINTER (FALSE));
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->check_support &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->check_support_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->check_support (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->check_support (
+ self,
(GAsyncReadyCallback)check_support_ready,
- ctx);
+ task);
return;
}
/* If there is no implementation to check support, assume we DON'T
* support it. */
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
supported_quark))) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Messaging not supported");
- initialization_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Messaging not supported");
+ g_object_unref (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LOAD_SUPPORTED_STORAGES:
- if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_supported_storages &&
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_supported_storages_finish) {
- MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (ctx->self)->load_supported_storages (
- ctx->self,
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->load_supported_storages (
+ self,
(GAsyncReadyCallback)load_supported_storages_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_INIT_CURRENT_STORAGES:
+ if (MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages &&
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages_finish) {
+ MM_IFACE_MODEM_MESSAGING_GET_INTERFACE (self)->init_current_storages (
+ self,
+ (GAsyncReadyCallback)init_current_storages_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -1291,23 +1347,26 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-create",
G_CALLBACK (handle_create),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-delete",
G_CALLBACK (handle_delete),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-list",
G_CALLBACK (handle_list),
- ctx->self);
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_messaging (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_MESSAGING (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1318,7 +1377,7 @@ mm_iface_modem_messaging_initialize_finish (MMIfaceModemMessaging *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -1329,6 +1388,7 @@ mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self,
{
InitializationContext *ctx;
MmGdbusModemMessaging *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -1351,16 +1411,13 @@ mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self,
/* 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_messaging_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
diff --git a/src/mm-iface-modem-messaging.h b/src/mm-iface-modem-messaging.h
index c27e100c..64dfb2b1 100644
--- a/src/mm-iface-modem-messaging.h
+++ b/src/mm-iface-modem-messaging.h
@@ -62,6 +62,13 @@ struct _MMIfaceModemMessaging {
GArray **mem2,
GArray **mem3,
GError **error);
+ /* Initializes the state of the storages */
+ void (* init_current_storages) (MMIfaceModemMessaging *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*init_current_storages_finish) (MMIfaceModemMessaging *self,
+ GAsyncResult *res,
+ GError **error);
/* Set default storage (async) */
void (* set_default_storage) (MMIfaceModemMessaging *self,
@@ -127,6 +134,7 @@ struct _MMIfaceModemMessaging {
};
GType mm_iface_modem_messaging_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemMessaging, g_object_unref)
/* Initialize Messaging interface (async) */
void mm_iface_modem_messaging_initialize (MMIfaceModemMessaging *self,
diff --git a/src/mm-iface-modem-oma.c b/src/mm-iface-modem-oma.c
index b8c48417..cc796b88 100644
--- a/src/mm-iface-modem-oma.c
+++ b/src/mm-iface-modem-oma.c
@@ -19,7 +19,7 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-oma.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#define SUPPORT_CHECKED_TAG "oma-support-checked-tag"
#define SUPPORTED_TAG "oma-supported-tag"
@@ -124,10 +124,9 @@ mm_iface_modem_oma_update_session_state (MMIfaceModemOma *self,
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));
+ mm_obj_info (self, "OMA session state changed (%s -> %s)",
+ 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
@@ -220,7 +219,7 @@ handle_setup_auth_ready (MMBaseModem *self,
}
str = mm_oma_feature_build_string_from_mask (ctx->features);
- mm_dbg ("Setting up OMA features: '%s'", str);
+ mm_obj_dbg (self, "setting up OMA features: '%s'", str);
g_free (str);
MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->setup (
@@ -342,8 +341,8 @@ handle_start_client_initiated_session_auth_ready (MMBaseModem *self,
return;
}
- mm_dbg ("Starting client-initiated OMA session (%s)",
- mm_oma_session_type_get_string (ctx->session_type));
+ mm_obj_dbg (self, "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,
@@ -501,10 +500,10 @@ handle_accept_network_initiated_session_auth_ready (MMBaseModem *self,
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_obj_dbg (self, "%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,
@@ -615,7 +614,7 @@ handle_cancel_session_auth_ready (MMBaseModem *self,
return;
}
- mm_dbg ("Cancelling OMA session");
+ mm_obj_dbg (self, "cancelling OMA session");
MM_IFACE_MODEM_OMA_GET_INTERFACE (ctx->self)->cancel_session (
ctx->self,
(GAsyncReadyCallback)cancel_session_ready,
@@ -645,7 +644,7 @@ handle_cancel_session (MmGdbusModemOma *skeleton,
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
@@ -655,18 +654,13 @@ typedef enum {
} DisablingStep;
struct _DisablingContext {
- MMIfaceModemOma *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModemOma *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -677,86 +671,99 @@ 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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disable_unsolicited_events_ready (MMIfaceModemOma *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
cleanup_unsolicited_events_ready (MMIfaceModemOma *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModemOma *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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,
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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,
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -768,33 +775,33 @@ mm_iface_modem_oma_disable (MMIfaceModemOma *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
@@ -805,162 +812,159 @@ typedef enum {
} EnablingStep;
struct _EnablingContext {
- MMIfaceModemOma *self;
EnablingStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModemOma *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
load_features_ready (MMIfaceModemOma *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Update in the interface */
mm_gdbus_modem_oma_set_features (ctx->skeleton, features);
/* Go on to next step */
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
setup_unsolicited_events_ready (MMIfaceModemOma *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModemOma *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ 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);
+ mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message);
g_error_free (error);
}
/* Go on with next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModemOma *self;
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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,
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->load_features (
+ self,
(GAsyncReadyCallback)load_features_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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,
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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,
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -973,34 +977,33 @@ mm_iface_modem_oma_enable (MMIfaceModemOma *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
@@ -1010,49 +1013,29 @@ typedef enum {
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemOma *self;
MmGdbusModemOma *skeleton;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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)
+ GTask *task)
{
+ 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);
+ mm_obj_dbg (self, "OMA support check failed: %s", error->message);
g_error_free (error);
}
} else {
@@ -1063,16 +1046,25 @@ check_support_ready (MMIfaceModemOma *self,
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModemOma *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
@@ -1084,48 +1076,48 @@ interface_initialization_step (InitializationContext *ctx)
supported_quark = (g_quark_from_static_string (
SUPPORTED_TAG));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_CHECK_SUPPORT:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
support_checked_quark))) {
/* Set the checked flag so that we don't run it again */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
support_checked_quark,
GUINT_TO_POINTER (TRUE));
/* Initially, assume we don't support it */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (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,
+ if (MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_OMA_GET_INTERFACE (self)->check_support (
+ self,
(GAsyncReadyCallback)check_support_ready,
- ctx);
+ task);
return;
}
/* If there is no implementation to check support, assume we DON'T
* support it. */
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "OMA not supported");
+ g_object_unref (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -1134,27 +1126,30 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-setup",
G_CALLBACK (handle_setup),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-start-client-initiated-session",
G_CALLBACK (handle_start_client_initiated_session),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-accept-network-initiated-session",
G_CALLBACK (handle_accept_network_initiated_session),
- ctx->self);
+ self);
g_signal_connect (ctx->skeleton,
"handle-cancel-session",
G_CALLBACK (handle_cancel_session),
- ctx->self);
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_oma (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_OMA (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -1165,7 +1160,7 @@ 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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -1176,6 +1171,7 @@ mm_iface_modem_oma_initialize (MMIfaceModemOma *self,
{
InitializationContext *ctx;
MmGdbusModemOma *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -1197,16 +1193,13 @@ mm_iface_modem_oma_initialize (MMIfaceModemOma *self,
/* 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);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
diff --git a/src/mm-iface-modem-oma.h b/src/mm-iface-modem-oma.h
index 94b16e1d..8117ebe6 100644
--- a/src/mm-iface-modem-oma.h
+++ b/src/mm-iface-modem-oma.h
@@ -120,6 +120,7 @@ struct _MMIfaceModemOma {
};
GType mm_iface_modem_oma_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemOma, g_object_unref)
/* Initialize Oma interface (async) */
void mm_iface_modem_oma_initialize (MMIfaceModemOma *self,
diff --git a/src/mm-iface-modem-sar.c b/src/mm-iface-modem-sar.c
new file mode 100644
index 00000000..2e92af1a
--- /dev/null
+++ b/src/mm-iface-modem-sar.c
@@ -0,0 +1,570 @@
+/* -*- 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) 2021 Fibocom Wireless Inc.
+ */
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-sar.h"
+#include "mm-log-object.h"
+
+#define SUPPORT_CHECKED_TAG "sar-support-checked-tag"
+#define SUPPORTED_TAG "sar-supported-tag"
+
+static GQuark support_checked_quark;
+static GQuark supported_quark;
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_sar_bind_simple_status (MMIfaceModemSar *self,
+ MMSimpleStatus *status)
+{
+}
+
+gboolean
+mm_iface_modem_get_sar_state (MMIfaceModemSar *self)
+{
+ MmGdbusModemSar *skeleton = NULL;
+ gboolean state;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!skeleton)
+ return FALSE;
+
+ state = mm_gdbus_modem_sar_get_state (skeleton);
+ g_object_unref (skeleton);
+ return state;
+}
+
+guint
+mm_iface_modem_sar_get_power_level (MMIfaceModemSar *self)
+{
+ MmGdbusModemSar *skeleton = NULL;
+ guint level;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!skeleton)
+ return 0;
+
+ level = mm_gdbus_modem_sar_get_power_level (skeleton);
+ g_object_unref (skeleton);
+ return level;
+}
+
+/*****************************************************************************/
+/* Handle Enable() */
+
+typedef struct {
+ MmGdbusModemSar *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemSar *self;
+ gboolean enable;
+} HandleEnableContext;
+
+static void
+handle_enable_context_free (HandleEnableContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleEnableContext, ctx);
+}
+
+static void
+enable_ready (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ HandleEnableContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish (self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* Update current features in the interface */
+ mm_gdbus_modem_sar_set_state (ctx->skeleton, ctx->enable);
+ mm_gdbus_modem_sar_complete_enable (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_enable_context_free (ctx);
+}
+
+static void
+handle_enable_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleEnableContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_enable_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable ||
+ !MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot setup SAR: "
+ "operation not supported");
+ handle_enable_context_free (ctx);
+ return;
+ }
+
+ mm_obj_dbg (self, "%s SAR...", ctx->enable ? "Enabling" : "Disabling");
+
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable (ctx->self,
+ ctx->enable,
+ (GAsyncReadyCallback)enable_ready,
+ ctx);
+}
+
+static gboolean
+handle_enable (MmGdbusModemSar *skeleton,
+ GDBusMethodInvocation *invocation,
+ gboolean enable,
+ MMIfaceModemSar *self)
+{
+ HandleEnableContext *ctx;
+
+ ctx = g_slice_new0 (HandleEnableContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->enable = enable;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_enable_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Handle Set Power Level() */
+
+typedef struct {
+ MmGdbusModemSar *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemSar *self;
+ guint power_level;
+} HandleSetPowerLevelContext;
+
+static void
+handle_set_power_level_context_free (HandleSetPowerLevelContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleSetPowerLevelContext, ctx);
+}
+
+static void
+set_power_level_ready (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ HandleSetPowerLevelContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->enable_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ } else {
+ mm_gdbus_modem_sar_set_power_level (ctx->skeleton, ctx->power_level);
+ mm_gdbus_modem_sar_complete_set_power_level (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_set_power_level_context_free (ctx);
+}
+
+static void
+handle_set_power_level_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetPowerLevelContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_power_level_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level ||
+ !MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot set SAR power level: "
+ "operation not supported");
+ handle_set_power_level_context_free (ctx);
+ return;
+ }
+
+ mm_obj_dbg (self, "Set SAR power level to: '%d'", ctx->power_level);
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (ctx->self)->set_power_level (
+ ctx->self,
+ ctx->power_level,
+ (GAsyncReadyCallback)set_power_level_ready,
+ ctx);
+}
+
+static gboolean
+handle_set_power_level (MmGdbusModemSar *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint level,
+ MMIfaceModemSar *self)
+{
+ HandleSetPowerLevelContext *ctx;
+
+ ctx = g_slice_new0 (HandleSetPowerLevelContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->power_level = level;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_power_level_auth_ready,
+ ctx);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct _InitializationContext InitializationContext;
+static void interface_initialization_step (GTask *task);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CHECK_SUPPORT,
+ INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
+ INITIALIZATION_STEP_LOAD_STATE,
+ INITIALIZATION_STEP_LOAD_POWER_LEVEL,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitializationContext {
+ MmGdbusModemSar *skeleton;
+ InitializationStep step;
+};
+
+static void
+initialization_context_free (InitializationContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+gboolean
+mm_iface_modem_sar_initialize_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+load_power_level_ready (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+ guint level;
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level_finish (self, res, &level, &error)) {
+ mm_obj_dbg (self, "SAR load power level failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+ mm_gdbus_modem_sar_set_power_level (ctx->skeleton, level);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+load_state_ready (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ gboolean state;
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state_finish (self, res, &state, &error)) {
+ mm_obj_dbg (self, "SAR load state failed: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+ mm_gdbus_modem_sar_set_state (ctx->skeleton, state);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+check_support_ready (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support_finish (self, res, &error)) {
+ if (error) {
+ /* This error shouldn't be treated as critical */
+ mm_obj_dbg (self, "SAR support check failed: %s", error->message);
+ g_error_free (error);
+ }
+ } else {
+ /* SAR is supported! */
+ g_object_set_qdata (G_OBJECT (self),
+ supported_quark,
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+interface_initialization_step (GTask *task)
+{
+ MMIfaceModemSar *self;
+ InitializationContext *ctx;
+
+ /* Don't run new steps if we're cancelled */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ 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));
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_CHECK_SUPPORT:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
+ support_checked_quark))) {
+ /* Set the checked flag so that we don't run it again */
+ g_object_set_qdata (G_OBJECT (self),
+ support_checked_quark,
+ GUINT_TO_POINTER (TRUE));
+ /* Initially, assume we don't support it */
+ g_object_set_qdata (G_OBJECT (self),
+ supported_quark,
+ GUINT_TO_POINTER (FALSE));
+
+ if (MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->check_support (
+ self,
+ (GAsyncReadyCallback)check_support_ready,
+ task);
+ return;
+ }
+
+ /* If there is no implementation to check support, assume we DON'T
+ * support it. */
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
+ supported_quark))) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "SAR not supported");
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_LOAD_STATE:
+ if (MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state &&
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state_finish) {
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_state (
+ self,
+ (GAsyncReadyCallback)load_state_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_LOAD_POWER_LEVEL:
+ if (MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level &&
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level_finish) {
+ MM_IFACE_MODEM_SAR_GET_INTERFACE (self)->load_power_level (
+ self,
+ (GAsyncReadyCallback)load_power_level_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_LAST:
+ /* We are done without errors! */
+
+ /* Handle method invocations */
+ g_signal_connect (ctx->skeleton,
+ "handle-enable",
+ G_CALLBACK (handle_enable),
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-set-power-level",
+ G_CALLBACK (handle_set_power_level),
+ self);
+
+ /* Finally, export the new interface */
+ mm_gdbus_object_skeleton_set_modem_sar (MM_GDBUS_OBJECT_SKELETON (self),
+ MM_GDBUS_MODEM_SAR (ctx->skeleton));
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_sar_initialize (MMIfaceModemSar *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationContext *ctx;
+ MmGdbusModemSar *skeleton = NULL;
+ GTask *task;
+
+ /* Did we already create it? */
+ g_object_get (self,
+ MM_IFACE_MODEM_SAR_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!skeleton) {
+ skeleton = mm_gdbus_modem_sar_skeleton_new ();
+ g_object_set (self,
+ MM_IFACE_MODEM_SAR_DBUS_SKELETON, skeleton,
+ NULL);
+ }
+
+ /* Perform async initialization here */
+ ctx = g_new0 (InitializationContext, 1);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->skeleton = skeleton;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
+}
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_sar_shutdown (MMIfaceModemSar *self)
+{
+ /* Unexport DBus interface and remove the skeleton */
+ mm_gdbus_object_skeleton_set_modem_sar (MM_GDBUS_OBJECT_SKELETON (self), NULL);
+ g_object_set (self,
+ MM_IFACE_MODEM_SAR_DBUS_SKELETON, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+iface_modem_sar_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_SAR_DBUS_SKELETON,
+ "SAR DBus skeleton",
+ "DBus skeleton for the SAR interface",
+ MM_GDBUS_TYPE_MODEM_SAR_SKELETON,
+ G_PARAM_READWRITE));
+ initialized = TRUE;
+}
+
+GType
+mm_iface_modem_sar_get_type (void)
+{
+ static GType iface_modem_sar_type = 0;
+
+ if (!G_UNLIKELY (iface_modem_sar_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMIfaceModemSar), /* class_size */
+ iface_modem_sar_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_modem_sar_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMIfaceModemSar",
+ &info,
+ 0);
+
+ g_type_interface_add_prerequisite (iface_modem_sar_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return iface_modem_sar_type;
+}
diff --git a/src/mm-iface-modem-sar.h b/src/mm-iface-modem-sar.h
new file mode 100644
index 00000000..2dba7003
--- /dev/null
+++ b/src/mm-iface-modem-sar.h
@@ -0,0 +1,102 @@
+/* -*- 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) 2021 Fibocom Wireless Inc.
+ */
+
+#ifndef MM_IFACE_MODEM_SAR_H
+#define MM_IFACE_MODEM_SAR_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#define MM_TYPE_IFACE_MODEM_SAR (mm_iface_modem_sar_get_type ())
+#define MM_IFACE_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_SAR, MMIfaceModemSar))
+#define MM_IS_IFACE_MODEM_SAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_SAR))
+#define MM_IFACE_MODEM_SAR_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_SAR, MMIfaceModemSar))
+
+#define MM_IFACE_MODEM_SAR_DBUS_SKELETON "iface-modem-sar-dbus-skeleton"
+
+typedef struct _MMIfaceModemSar MMIfaceModemSar;
+
+struct _MMIfaceModemSar {
+ GTypeInterface g_iface;
+
+ /* Check for SAR support (async) */
+ void (* check_support) (MMIfaceModemSar *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* check_support_finish) (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error);
+ /* Enable SAR (async) */
+ void (* enable) (MMIfaceModemSar *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* enable_finish) (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error);
+ /* Get SAR state (async) */
+ void (* load_state) (MMIfaceModemSar *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* load_state_finish) (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ gboolean *out_state,
+ GError **error);
+
+ /* Get power level (async) */
+ void (* load_power_level) (MMIfaceModemSar *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* load_power_level_finish) (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ guint *out_power_level,
+ GError **error);
+
+ /* Set power level (async) */
+ void (* set_power_level) (MMIfaceModemSar *self,
+ guint power_level,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* set_power_level_finish) (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error);
+};
+
+GType mm_iface_modem_sar_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemSar, g_object_unref)
+
+/* Initialize Sar interface (async) */
+void mm_iface_modem_sar_initialize (MMIfaceModemSar *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_sar_initialize_finish (MMIfaceModemSar *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shutdown Sar interface */
+void mm_iface_modem_sar_shutdown (MMIfaceModemSar *self);
+
+/* Bind properties for simple GetStatus() */
+void mm_iface_modem_sar_bind_simple_status (MMIfaceModemSar*self,
+ MMSimpleStatus *status);
+
+gboolean mm_iface_modem_get_sar_state (MMIfaceModemSar *self);
+guint mm_iface_modem_sar_get_power_level (MMIfaceModemSar *self);
+
+#endif /* MM_IFACE_MODEM_SAR_H */
diff --git a/src/mm-iface-modem-signal.c b/src/mm-iface-modem-signal.c
index d28c2ad9..d2040390 100644
--- a/src/mm-iface-modem-signal.c
+++ b/src/mm-iface-modem-signal.c
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
*/
#include <ModemManager.h>
@@ -19,227 +20,250 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-signal.h"
-#include "mm-log.h"
+#include "mm-log-object.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;
/*****************************************************************************/
+/* Private data context */
-void
-mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self,
- MMSimpleStatus *status)
-{
-}
-
-/*****************************************************************************/
+#define PRIVATE_TAG "signal-private-tag"
+static GQuark private_quark;
typedef struct {
- guint rate;
- guint timeout_source;
-} RefreshContext;
+ /* polling-based reporting enabled */
+ guint rate;
+ guint timeout_source;
+ /* threshold-based reporting enabled */
+ guint rssi_threshold;
+ gboolean error_rate_threshold;
+} Private;
static void
-refresh_context_free (RefreshContext *ctx)
+private_free (Private *priv)
{
- if (ctx->timeout_source)
- g_source_remove (ctx->timeout_source);
- g_slice_free (RefreshContext, ctx);
+ if (priv->timeout_source)
+ g_source_remove (priv->timeout_source);
+ g_slice_free (Private, priv);
}
-static void
-clear_values (MMIfaceModemSignal *self)
+static Private *
+get_private (MMIfaceModemSignal *self)
{
- MmGdbusModemSignal *skeleton;
+ Private *priv;
- g_object_get (self,
- MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton,
- NULL);
- if (!skeleton)
- return;
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
- 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);
+ return priv;
}
-static void
-load_values_ready (MMIfaceModemSignal *self,
- GAsyncResult *res)
+/*****************************************************************************/
+
+void
+mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self,
+ MMSimpleStatus *status)
{
- GVariant *dictionary;
- GError *error = NULL;
- MMSignal *cdma = NULL;
- MMSignal *evdo = NULL;
- MMSignal *gsm = NULL;
- MMSignal *umts = NULL;
- MMSignal *lte = NULL;
- MmGdbusModemSignal *skeleton;
+}
- if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->load_values_finish (
- self,
- res,
- &cdma,
- &evdo,
- &gsm,
- &umts,
- &lte,
- &error)) {
- mm_warn ("Couldn't load extended signal information: %s", error->message);
- g_error_free (error);
- clear_values (self);
- return;
- }
+/*****************************************************************************/
+
+static void
+internal_signal_update (MMIfaceModemSignal *self,
+ MMSignal *cdma,
+ MMSignal *evdo,
+ MMSignal *gsm,
+ MMSignal *umts,
+ MMSignal *lte,
+ MMSignal *nr5g)
+{
+ g_autoptr(GVariant) dict_cdma = NULL;
+ g_autoptr(GVariant) dict_evdo = NULL;
+ g_autoptr(GVariant) dict_gsm = NULL;
+ g_autoptr(GVariant) dict_umts = NULL;
+ g_autoptr(GVariant) dict_lte = NULL;
+ g_autoptr(GVariant) dict_nr5g = NULL;
+ g_autoptr(MmGdbusModemSignalSkeleton) skeleton = NULL;
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");
+ mm_obj_warn (self, "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);
+ mm_obj_dbg (self, "cdma extended signal information updated");
+ dict_cdma = mm_signal_get_dictionary (cdma);
}
+ mm_gdbus_modem_signal_set_cdma (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_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);
+ mm_obj_dbg (self, "evdo extended signal information updated");
+ dict_evdo = mm_signal_get_dictionary (evdo);
}
+ mm_gdbus_modem_signal_set_evdo (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_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);
+ mm_obj_dbg (self, "gsm extended signal information updated");
+ dict_gsm = mm_signal_get_dictionary (gsm);
}
+ mm_gdbus_modem_signal_set_gsm (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_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);
+ mm_obj_dbg (self, "umts extended signal information updated");
+ dict_umts = mm_signal_get_dictionary (umts);
}
+ mm_gdbus_modem_signal_set_umts (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_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);
+ mm_obj_dbg (self, "lte extended signal information updated");
+ dict_lte = mm_signal_get_dictionary (lte);
+ }
+ mm_gdbus_modem_signal_set_lte (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_lte);
+
+ if (nr5g) {
+ mm_obj_dbg (self, "5gnr extended signal information updated");
+ dict_nr5g = mm_signal_get_dictionary (nr5g);
}
+ mm_gdbus_modem_signal_set_nr5g (MM_GDBUS_MODEM_SIGNAL (skeleton), dict_nr5g);
/* Flush right away */
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+}
+
+void
+mm_iface_modem_signal_update (MMIfaceModemSignal *self,
+ MMSignal *cdma,
+ MMSignal *evdo,
+ MMSignal *gsm,
+ MMSignal *umts,
+ MMSignal *lte,
+ MMSignal *nr5g)
+{
+ Private *priv;
+
+ priv = get_private (self);
+ if (!priv->rate && !priv->rssi_threshold && !priv->error_rate_threshold) {
+ mm_obj_dbg (self, "skipping extended signal information update...");
+ return;
+ }
+
+ internal_signal_update (self, cdma, evdo, gsm, umts, lte, nr5g);
+}
+
+/*****************************************************************************/
+
+static void
+check_interface_reset (MMIfaceModemSignal *self)
+{
+ Private *priv;
+
+ priv = get_private (self);
+
+ if (!priv->rate && !priv->rssi_threshold && !priv->error_rate_threshold) {
+ mm_obj_dbg (self, "reseting extended signal information...");
+ mm_iface_modem_signal_update (self, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+}
+
+/*****************************************************************************/
+
+static void
+load_values_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res)
+{
+ g_autoptr(GError) error = NULL;
+ g_autoptr(MMSignal) cdma = NULL;
+ g_autoptr(MMSignal) evdo = NULL;
+ g_autoptr(MMSignal) gsm = NULL;
+ g_autoptr(MMSignal) umts = NULL;
+ g_autoptr(MMSignal) lte = NULL;
+ g_autoptr(MMSignal) nr5g = NULL;
+
+ if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->load_values_finish (
+ self,
+ res,
+ &cdma,
+ &evdo,
+ &gsm,
+ &umts,
+ &lte,
+ &nr5g,
+ &error)) {
+ mm_obj_warn (self, "couldn't reload extended signal information: %s", error->message);
+ return;
+ }
- g_object_unref (skeleton);
+ mm_iface_modem_signal_update (self, cdma, evdo, gsm, umts, lte, nr5g);
}
static gboolean
-refresh_context_cb (MMIfaceModemSignal *self)
+polling_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);
+ return G_SOURCE_CONTINUE;
}
static gboolean
-setup_refresh_context (MMIfaceModemSignal *self,
- gboolean update_rate,
- guint new_rate,
- GError **error)
+polling_restart (MMIfaceModemSignal *self,
+ guint rate,
+ GError **error)
{
- MmGdbusModemSignal *skeleton;
- RefreshContext *ctx;
- MMModemState modem_state;
+ Private *priv;
- if (G_UNLIKELY (!refresh_context_quark))
- refresh_context_quark = g_quark_from_static_string (REFRESH_CONTEXT_TAG);
+ priv = get_private (self);
- 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;
- }
+ /* Update the rate in the interface if it changed */
+ if (priv->rate != rate) {
+ g_autoptr(MmGdbusModemSignalSkeleton) skeleton = NULL;
- /* 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);
+ g_object_get (self,
+ MM_IFACE_MODEM_SIGNAL_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ return FALSE;
+ }
+ mm_gdbus_modem_signal_set_rate (MM_GDBUS_MODEM_SIGNAL (skeleton), rate);
+ priv->rate = rate;
}
- /* We're enabling, compare to old rate */
- if (ctx->rate == new_rate) {
- /* Already there */
+ /* Polling disabled by user? */
+ if (rate == 0) {
+ mm_obj_dbg (self, "extended signal information polling disabled (rate: 0 seconds)");
+ if (priv->timeout_source) {
+ g_source_remove (priv->timeout_source);
+ priv->timeout_source = 0;
+ }
+ check_interface_reset (self);
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);
+ /* Restart polling */
+ mm_obj_dbg (self, "extended signal information reporting enabled (rate: %u seconds)", rate);
+ if (priv->timeout_source)
+ g_source_remove (priv->timeout_source);
+ priv->timeout_source = g_timeout_add_seconds (rate, (GSourceFunc) polling_context_cb, self);
/* Also launch right away */
- refresh_context_cb (self);
-
+ polling_context_cb (self);
return TRUE;
}
@@ -247,9 +271,9 @@ setup_refresh_context (MMIfaceModemSignal *self,
typedef struct {
GDBusMethodInvocation *invocation;
- MmGdbusModemSignal *skeleton;
- MMIfaceModemSignal *self;
- guint rate;
+ MmGdbusModemSignal *skeleton;
+ MMIfaceModemSignal *self;
+ guint rate;
} HandleSetupContext;
static void
@@ -268,9 +292,20 @@ handle_setup_auth_ready (MMBaseModem *self,
{
GError *error = NULL;
- if (!mm_base_modem_authorize_finish (self, res, &error))
+ 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))
+ handle_setup_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_setup_context_free (ctx);
+ return;
+ }
+
+ if (!polling_restart (ctx->self, ctx->rate, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
mm_gdbus_modem_signal_complete_setup (ctx->skeleton, ctx->invocation);
@@ -278,10 +313,10 @@ handle_setup_auth_ready (MMBaseModem *self,
}
static gboolean
-handle_setup (MmGdbusModemSignal *skeleton,
+handle_setup (MmGdbusModemSignal *skeleton,
GDBusMethodInvocation *invocation,
- guint rate,
- MMIfaceModemSignal *self)
+ guint rate,
+ MMIfaceModemSignal *self)
{
HandleSetupContext *ctx;
@@ -301,70 +336,198 @@ handle_setup (MmGdbusModemSignal *skeleton,
/*****************************************************************************/
+typedef struct {
+ GDBusMethodInvocation *invocation;
+ MmGdbusModemSignal *skeleton;
+ MMIfaceModemSignal *self;
+ GVariant *settings;
+ guint32 rssi_threshold;
+ gboolean error_rate_threshold;
+} HandleSetupThresholdsContext;
+
+static void
+handle_setup_thresholds_context_free (HandleSetupThresholdsContext *ctx)
+{
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->self);
+ if (ctx->settings)
+ g_variant_unref (ctx->settings);
+ g_slice_free (HandleSetupThresholdsContext, ctx);
+}
+
+static void
+setup_thresholds_ready (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ HandleSetupThresholdsContext *ctx)
+{
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_IFACE_MODEM_SIGNAL (self));
+
+ if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->setup_thresholds_finish (ctx->self, res, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else {
+ /* Update the properties with the latest threshold setting */
+ mm_gdbus_modem_signal_set_rssi_threshold (ctx->skeleton, ctx->rssi_threshold);
+ mm_gdbus_modem_signal_set_error_rate_threshold (ctx->skeleton, ctx->error_rate_threshold);
+ priv->rssi_threshold = ctx->rssi_threshold;
+ priv->error_rate_threshold = ctx->error_rate_threshold;
+ check_interface_reset (self);
+
+ mm_gdbus_modem_signal_complete_setup_thresholds (ctx->skeleton, ctx->invocation);
+ }
+
+ handle_setup_thresholds_context_free (ctx);
+}
+
+static void
+handle_setup_thresholds_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetupThresholdsContext *ctx)
+{
+ g_autoptr(MMSignalThresholdProperties) properties = NULL;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_IFACE_MODEM_SIGNAL (self));
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_setup_thresholds_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->setup_thresholds ||
+ !MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->setup_thresholds_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot setup thresholds: operation not supported");
+ handle_setup_thresholds_context_free (ctx);
+ return;
+ }
+
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
+ ctx->invocation,
+ MM_MODEM_STATE_ENABLED)) {
+ handle_setup_thresholds_context_free (ctx);
+ return;
+ }
+
+ properties = mm_signal_threshold_properties_new_from_dictionary (ctx->settings, &error);
+ if (!properties) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_setup_thresholds_context_free (ctx);
+ return;
+ }
+ ctx->rssi_threshold = mm_signal_threshold_properties_get_rssi (properties);
+ ctx->error_rate_threshold = mm_signal_threshold_properties_get_error_rate (properties);
+
+ /* Already there? */
+ if ((ctx->rssi_threshold == mm_gdbus_modem_signal_get_rssi_threshold (ctx->skeleton)) &&
+ (ctx->error_rate_threshold == mm_gdbus_modem_signal_get_error_rate_threshold (ctx->skeleton))) {
+ mm_gdbus_modem_signal_complete_setup_thresholds (ctx->skeleton, ctx->invocation);
+ handle_setup_thresholds_context_free (ctx);
+ return;
+ }
+
+ /* Same settings? */
+ if ((ctx->rssi_threshold == priv->rssi_threshold) &&
+ (ctx->error_rate_threshold == priv->error_rate_threshold)) {
+ mm_gdbus_modem_signal_complete_setup_thresholds (ctx->skeleton, ctx->invocation);
+ handle_setup_thresholds_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (ctx->self)->setup_thresholds (
+ ctx->self,
+ ctx->rssi_threshold,
+ ctx->error_rate_threshold,
+ (GAsyncReadyCallback)setup_thresholds_ready,
+ ctx);
+}
+
+static gboolean
+handle_setup_thresholds (MmGdbusModemSignal *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *settings,
+ MMIfaceModemSignal *self)
+{
+ HandleSetupThresholdsContext *ctx;
+
+ ctx = g_slice_new0 (HandleSetupThresholdsContext);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->self = g_object_ref (self);
+ ctx->settings = g_variant_ref (settings);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_setup_thresholds_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
gboolean
-mm_iface_modem_signal_disable_finish (MMIfaceModemSignal *self,
- GAsyncResult *res,
- GError **error)
+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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
-mm_iface_modem_signal_disable (MMIfaceModemSignal *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_iface_modem_signal_disable (MMIfaceModemSignal *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_signal_disable);
+ mm_iface_modem_signal_update (self, NULL, NULL, NULL, NULL, NULL, NULL);
- 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);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
gboolean
-mm_iface_modem_signal_enable_finish (MMIfaceModemSignal *self,
- GAsyncResult *res,
- GError **error)
+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);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
-mm_iface_modem_signal_enable (MMIfaceModemSignal *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_iface_modem_signal_enable (MMIfaceModemSignal *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- GSimpleAsyncResult *result;
- GError *error = NULL;
+ GTask *task;
+ GError *error = NULL;
+ Private *priv;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_signal_enable);
+ priv = get_private (self);
+ task = g_task_new (self, cancellable, callback, user_data);
- if (!setup_refresh_context (self, FALSE, 0, &error))
- g_simple_async_result_take_error (result, error);
+ /* same rate as we had before */
+ if (!polling_restart (self, priv->rate, &error))
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
-
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
@@ -374,58 +537,37 @@ typedef enum {
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemSignal *self;
MmGdbusModemSignal *skeleton;
- GCancellable *cancellable;
- GSimpleAsyncResult *result;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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;
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
check_support_ready (MMIfaceModemSignal *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ 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);
+ mm_obj_dbg (self, "extended signal support check failed: %s", error->message);
g_error_free (error);
}
} else {
@@ -436,16 +578,25 @@ check_support_ready (MMIfaceModemSignal *self,
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModemSignal *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
@@ -457,48 +608,48 @@ interface_initialization_step (InitializationContext *ctx)
supported_quark = (g_quark_from_static_string (
SUPPORTED_TAG));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_CHECK_SUPPORT:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
support_checked_quark))) {
/* Set the checked flag so that we don't run it again */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
support_checked_quark,
GUINT_TO_POINTER (TRUE));
/* Initially, assume we don't support it */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (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,
+ if (MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_SIGNAL_GET_INTERFACE (self)->check_support (
+ self,
(GAsyncReadyCallback)check_support_ready,
- ctx);
+ task);
return;
}
/* If there is no implementation to check support, assume we DON'T
* support it. */
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Extended Signal information not supported");
+ g_object_unref (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -507,14 +658,21 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-setup",
G_CALLBACK (handle_setup),
- ctx->self);
+ self);
+ g_signal_connect (ctx->skeleton,
+ "handle-setup-thresholds",
+ G_CALLBACK (handle_setup_thresholds),
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_signal (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_signal (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_SIGNAL (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -528,6 +686,7 @@ mm_iface_modem_signal_initialize (MMIfaceModemSignal *self,
{
InitializationContext *ctx;
MmGdbusModemSignal *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -535,7 +694,6 @@ mm_iface_modem_signal_initialize (MMIfaceModemSignal *self,
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);
@@ -544,16 +702,13 @@ mm_iface_modem_signal_initialize (MMIfaceModemSignal *self,
/* 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);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
diff --git a/src/mm-iface-modem-signal.h b/src/mm-iface-modem-signal.h
index 1d49e5f3..d359659d 100644
--- a/src/mm-iface-modem-signal.h
+++ b/src/mm-iface-modem-signal.h
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
+ * Copyright (C) 2021 Intel Corporation
*/
#ifndef MM_IFACE_MODEM_SIGNAL_H
@@ -54,10 +55,23 @@ struct _MMIfaceModemSignal {
MMSignal **gsm,
MMSignal **umts,
MMSignal **lte,
+ MMSignal **nr5g,
GError **error);
+
+ /* Setup thresholds */
+ void (* setup_thresholds) (MMIfaceModemSignal *self,
+ guint32 rssi_threshold,
+ gboolean error_rate_threshold,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_thresholds_finish) (MMIfaceModemSignal *self,
+ GAsyncResult *res,
+ GError **error);
+
};
GType mm_iface_modem_signal_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemSignal, g_object_unref)
/* Initialize Signal interface (async) */
void mm_iface_modem_signal_initialize (MMIfaceModemSignal *self,
@@ -92,4 +106,13 @@ void mm_iface_modem_signal_shutdown (MMIfaceModemSignal *self);
void mm_iface_modem_signal_bind_simple_status (MMIfaceModemSignal *self,
MMSimpleStatus *status);
+/* Allow signal quality updates via indications */
+void mm_iface_modem_signal_update (MMIfaceModemSignal *self,
+ MMSignal *cdma,
+ MMSignal *evdo,
+ MMSignal *gsm,
+ MMSignal *umts,
+ MMSignal *lte,
+ MMSignal *nr5g);
+
#endif /* MM_IFACE_MODEM_SIGNAL_H */
diff --git a/src/mm-iface-modem-simple.c b/src/mm-iface-modem-simple.c
index ec1825e5..8a30a316 100644
--- a/src/mm-iface-modem-simple.c
+++ b/src/mm-iface-modem-simple.c
@@ -26,14 +26,46 @@
#include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-cdma.h"
#include "mm-iface-modem-simple.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "iface-modem-simple-private-tag"
+static GQuark private_quark;
+
+typedef struct {
+ GCancellable *ongoing_connect;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_clear_object (&priv->ongoing_connect);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMIfaceModemSimple *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
/*****************************************************************************/
/* Register in either a CDMA or a 3GPP network (or both) */
typedef struct {
- GSimpleAsyncResult *result;
- MMIfaceModemSimple *self;
gchar *operator_id;
guint remaining_tries_cdma;
guint remaining_tries_3gpp;
@@ -41,12 +73,9 @@ typedef struct {
} RegisterInNetworkContext;
static void
-register_in_network_context_complete_and_free (RegisterInNetworkContext *ctx)
+register_in_network_context_free (RegisterInNetworkContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
g_free (ctx->operator_id);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -55,77 +84,88 @@ register_in_3gpp_or_cdma_network_finish (MMIfaceModemSimple *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void check_next_registration (RegisterInNetworkContext *ctx);
+static void check_next_registration (GTask *task);
static void
register_in_cdma_network_ready (MMIfaceModemCdma *self,
GAsyncResult *res,
- RegisterInNetworkContext *ctx)
+ GTask *task)
{
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
ctx->remaining_tries_cdma--;
- if (!mm_iface_modem_cdma_register_in_network_finish (
- MM_IFACE_MODEM_CDMA (self), res, NULL)) {
+ if (!mm_iface_modem_cdma_register_in_network_finish (self, res, NULL)) {
/* Retry check */
- check_next_registration (ctx);
+ check_next_registration (task);
return;
}
/* Registered we are! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
register_in_3gpp_network_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
- RegisterInNetworkContext *ctx)
+ GTask *task)
{
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
ctx->remaining_tries_3gpp--;
- if (!mm_iface_modem_3gpp_register_in_network_finish (
- MM_IFACE_MODEM_3GPP (self), res, NULL)) {
+ if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, NULL)) {
/* Retry check */
- check_next_registration (ctx);
+ check_next_registration (task);
return;
}
/* Registered we are! */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-check_next_registration (RegisterInNetworkContext *ctx)
+check_next_registration (GTask *task)
{
+ RegisterInNetworkContext *ctx;
+ MMIfaceModemSimple *self;
+
+ self = MM_IFACE_MODEM_SIMPLE (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+
if (ctx->remaining_tries_cdma > ctx->remaining_tries_3gpp &&
ctx->remaining_tries_cdma > 0) {
mm_iface_modem_cdma_register_in_network (
- MM_IFACE_MODEM_CDMA (ctx->self),
+ MM_IFACE_MODEM_CDMA (self),
ctx->max_try_time,
(GAsyncReadyCallback)register_in_cdma_network_ready,
- ctx);
+ task);
return;
}
if (ctx->remaining_tries_3gpp > 0) {
mm_iface_modem_3gpp_register_in_network (
- MM_IFACE_MODEM_3GPP (ctx->self),
+ MM_IFACE_MODEM_3GPP (self),
ctx->operator_id,
+ FALSE, /* if already registered with same settings, do nothing */
ctx->max_try_time,
(GAsyncReadyCallback)register_in_3gpp_network_ready,
- ctx);
+ task);
return;
}
/* No more tries of anything */
- g_simple_async_result_take_error (
- ctx->result,
- mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT));
- register_in_network_context_complete_and_free (ctx);
+ g_task_return_error (
+ task,
+ mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self));
+ g_object_unref (task);
}
static void
@@ -135,23 +175,19 @@ register_in_3gpp_or_cdma_network (MMIfaceModemSimple *self,
gpointer user_data)
{
RegisterInNetworkContext *ctx;
+ GTask *task;
ctx = g_new0 (RegisterInNetworkContext, 1);
- ctx->self = g_object_ref (self);
ctx->operator_id = g_strdup (operator_id);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- register_in_3gpp_or_cdma_network);
/* 3GPP-only modems... */
- if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (ctx->self))) {
+ if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (self))) {
ctx->max_try_time = 60;
ctx->remaining_tries_cdma = 0;
ctx->remaining_tries_3gpp = 1;
}
/* CDMA-only modems... */
- else if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (ctx->self))) {
+ else if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (self))) {
ctx->max_try_time = 60;
ctx->remaining_tries_cdma = 1;
ctx->remaining_tries_3gpp = 0;
@@ -163,7 +199,12 @@ register_in_3gpp_or_cdma_network (MMIfaceModemSimple *self,
ctx->remaining_tries_3gpp = 6;
}
- check_next_registration (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task,
+ ctx,
+ (GDestroyNotify)register_in_network_context_free);
+
+ check_next_registration (task);
}
/*****************************************************************************/
@@ -185,6 +226,7 @@ typedef struct {
GDBusMethodInvocation *invocation;
MMIfaceModemSimple *self;
ConnectionStep step;
+ MMBearerList *bearer_list;
/* Expected input properties */
GVariant *dictionary;
@@ -192,16 +234,38 @@ typedef struct {
/* Results to set */
MMBaseBearer *bearer;
+
+ /* Cancellation */
+ GCancellable *cancellable;
} ConnectionContext;
static void
+cleanup_cancellation (ConnectionContext *ctx)
+{
+ Private *priv;
+
+ /* The ongoing connect cancellable and the one in the connection context
+ * must be the same, as they're set together, so if the one in the
+ * context doesn't exist, do nothing. */
+ if (!ctx->cancellable)
+ return;
+
+ /* If the ongoing connect cancellable is cancelled via the Simple.Disconnect
+ * method, it won't exist in the private struct, so don't assume they
+ * both exist. */
+ priv = get_private (ctx->self);
+ g_clear_object (&priv->ongoing_connect);
+ g_clear_object (&ctx->cancellable);
+}
+
+static void
connection_context_free (ConnectionContext *ctx)
{
+ cleanup_cancellation (ctx);
+ g_clear_object (&ctx->properties);
+ g_clear_object (&ctx->bearer);
+ g_clear_object (&ctx->bearer_list);
g_variant_unref (ctx->dictionary);
- if (ctx->properties)
- g_object_unref (ctx->properties);
- if (ctx->bearer)
- g_object_unref (ctx->bearer);
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
@@ -218,7 +282,7 @@ connect_bearer_ready (MMBaseBearer *bearer,
GError *error = NULL;
if (!mm_base_bearer_connect_finish (bearer, res, &error)) {
- mm_dbg ("Couldn't connect bearer: '%s'", error->message);
+ mm_obj_dbg (ctx->self, "couldn't connect bearer: %s", error->message);
g_dbus_method_invocation_take_error (ctx->invocation, error);
connection_context_free (ctx);
return;
@@ -370,10 +434,11 @@ update_lock_info_ready (MMIfaceModem *self,
}
/* If we are already unlocked, go on to next step. Note that we do also
- * allow SIM-PIN2, as we don't need to unlock that in order to get
+ * allow SIM-PIN2/SIM-PUK2, as we don't need to unlock that in order to get
* connected. */
if (lock == MM_MODEM_LOCK_NONE ||
- lock == MM_MODEM_LOCK_SIM_PIN2) {
+ lock == MM_MODEM_LOCK_SIM_PIN2 ||
+ lock == MM_MODEM_LOCK_SIM_PUK2) {
ctx->step++;
connection_step (ctx);
return;
@@ -414,33 +479,56 @@ update_lock_info_ready (MMIfaceModem *self,
g_object_unref (sim);
}
-typedef struct {
- MMBaseBearer *found;
-} BearerListFindContext;
+static gboolean
+completed_if_cancelled (ConnectionContext *ctx)
+{
+ /* Do nothing if not cancelled */
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ /* Otherwise cancellable is valid and it's cancelled */
+ g_dbus_method_invocation_return_error (
+ ctx->invocation, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ "Connection attempt cancelled");
+ connection_context_free (ctx);
+ return TRUE;
+}
-static void
-bearer_list_find_disconnected (MMBaseBearer *bearer,
- BearerListFindContext *ctx)
+static gboolean
+setup_cancellation (ConnectionContext *ctx,
+ GError **error)
{
- /* If already marked one to remove, do nothing */
- if (ctx->found)
- return;
+ Private *priv;
+
+ /* Only one connection attempt by Simple.Connect() may be handled at a time */
+ priv = get_private (ctx->self);
+ if (priv->ongoing_connect) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "Connection request forbidden: operation already in progress");
+ return FALSE;
+ }
- if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_DISCONNECTED)
- ctx->found = g_object_ref (bearer);
+ g_assert (!ctx->cancellable);
+ ctx->cancellable = g_cancellable_new ();
+ priv->ongoing_connect = g_object_ref (ctx->cancellable);
+ return TRUE;
}
static void
connection_step (ConnectionContext *ctx)
{
+ /* Early abort if operation is cancelled */
+ if (completed_if_cancelled (ctx))
+ return;
+
switch (ctx->step) {
case CONNECTION_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case CONNECTION_STEP_UNLOCK_CHECK:
- mm_info ("Simple connect state (%d/%d): Unlock check",
- ctx->step, CONNECTION_STEP_LAST);
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): unlock check",
+ ctx->step, CONNECTION_STEP_LAST);
mm_iface_modem_update_lock_info (MM_IFACE_MODEM (ctx->self),
MM_MODEM_LOCK_UNKNOWN, /* ask */
(GAsyncReadyCallback)update_lock_info_ready,
@@ -448,8 +536,8 @@ connection_step (ConnectionContext *ctx)
return;
case CONNECTION_STEP_WAIT_FOR_INITIALIZED:
- mm_info ("Simple connect state (%d/%d): Wait to get fully initialized",
- ctx->step, CONNECTION_STEP_LAST);
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): wait to get fully initialized",
+ ctx->step, CONNECTION_STEP_LAST);
mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self),
MM_MODEM_STATE_DISABLED, /* disabled == initialized */
(GAsyncReadyCallback)wait_for_initialized_ready,
@@ -457,16 +545,16 @@ connection_step (ConnectionContext *ctx)
return;
case CONNECTION_STEP_ENABLE:
- mm_info ("Simple connect state (%d/%d): Enable",
- ctx->step, CONNECTION_STEP_LAST);
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): enable",
+ ctx->step, CONNECTION_STEP_LAST);
mm_base_modem_enable (MM_BASE_MODEM (ctx->self),
(GAsyncReadyCallback)enable_ready,
ctx);
return;
case CONNECTION_STEP_WAIT_FOR_ENABLED:
- mm_info ("Simple connect state (%d/%d): Wait to get fully enabled",
- ctx->step, CONNECTION_STEP_LAST);
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): wait to get fully enabled",
+ ctx->step, CONNECTION_STEP_LAST);
mm_iface_modem_wait_for_final_state (MM_IFACE_MODEM (ctx->self),
MM_MODEM_STATE_UNKNOWN, /* just a final state */
(GAsyncReadyCallback)wait_for_enabled_ready,
@@ -474,9 +562,8 @@ connection_step (ConnectionContext *ctx)
return;
case CONNECTION_STEP_REGISTER:
- mm_info ("Simple connect state (%d/%d): Register",
- ctx->step, CONNECTION_STEP_LAST);
-
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): register",
+ ctx->step, CONNECTION_STEP_LAST);
if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) ||
mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
/* 3GPP or CDMA registration */
@@ -489,98 +576,41 @@ connection_step (ConnectionContext *ctx)
}
/* If not 3GPP and not CDMA, this will possibly be a POTS modem,
- * which won't require any specific registration anywhere.
- * So, fall down to next step */
+ * which won't require any specific registration anywhere. */
ctx->step++;
+ /* fall through */
case CONNECTION_STEP_BEARER: {
- MMBearerList *list = NULL;
- MMBearerProperties *bearer_properties;
+ g_autoptr(MMBearerProperties) bearer_properties = NULL;
- mm_info ("Simple connect state (%d/%d): Bearer",
- ctx->step, CONNECTION_STEP_LAST);
-
- g_object_get (ctx->self,
- MM_IFACE_MODEM_BEARER_LIST, &list,
- NULL);
- if (!list) {
- g_dbus_method_invocation_return_error (
- ctx->invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't get the bearer list");
- connection_context_free (ctx);
- return;
- }
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): bearer",
+ ctx->step, CONNECTION_STEP_LAST);
bearer_properties = mm_simple_connect_properties_get_bearer_properties (ctx->properties);
/* Check if the bearer we want to create is already in the list */
- ctx->bearer = mm_bearer_list_find (list, bearer_properties);
+ ctx->bearer = mm_bearer_list_find_by_properties (ctx->bearer_list, bearer_properties);
if (!ctx->bearer) {
- mm_dbg ("Creating new bearer...");
- /* If we don't have enough space to create the bearer, try to remove
- * a disconnected bearer first. */
- if (mm_bearer_list_get_max (list) == mm_bearer_list_get_count (list)) {
- BearerListFindContext foreach_ctx;
-
- foreach_ctx.found = NULL;
- mm_bearer_list_foreach (list,
- (MMBearerListForeachFunc)bearer_list_find_disconnected,
- &foreach_ctx);
-
- /* Found a disconnected bearer, remove it */
- if (foreach_ctx.found) {
- GError *error = NULL;
-
- if (!mm_bearer_list_delete_bearer (list,
- mm_base_bearer_get_path (foreach_ctx.found),
- &error)) {
- mm_dbg ("Couldn't delete disconnected bearer at '%s': '%s'",
- mm_base_bearer_get_path (foreach_ctx.found),
- error->message);
- g_error_free (error);
- } else
- mm_dbg ("Deleted disconnected bearer at '%s'",
- mm_base_bearer_get_path (foreach_ctx.found));
- g_object_unref (foreach_ctx.found);
- }
-
- /* Re-check space, and if we still are in max, return an error */
- if (mm_bearer_list_get_max (list) == mm_bearer_list_get_count (list)) {
- g_dbus_method_invocation_return_error (
- ctx->invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_TOO_MANY,
- "Cannot create new bearer: all existing bearers are connected");
- connection_context_free (ctx);
- g_object_unref (list);
- g_object_unref (bearer_properties);
- return;
- }
- }
-
+ mm_obj_dbg (ctx->self, "creating new bearer...");
mm_iface_modem_create_bearer (MM_IFACE_MODEM (ctx->self),
bearer_properties,
(GAsyncReadyCallback)create_bearer_ready,
ctx);
-
- g_object_unref (list);
- g_object_unref (bearer_properties);
return;
}
- mm_dbg ("Using already existing bearer at '%s'...",
- mm_base_bearer_get_path (ctx->bearer));
- g_object_unref (list);
- g_object_unref (bearer_properties);
- /* Fall down to next step */
+ mm_obj_dbg (ctx->self, "Using already existing bearer at '%s'...",
+ mm_base_bearer_get_path (ctx->bearer));
ctx->step++;
- }
+ } /* fall through */
case CONNECTION_STEP_CONNECT:
- mm_info ("Simple connect state (%d/%d): Connect",
- ctx->step, CONNECTION_STEP_LAST);
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): connect",
+ ctx->step, CONNECTION_STEP_LAST);
+
+ /* At this point, we can cleanup the cancellation point in the Simple interface,
+ * because the bearer connection has its own cancellation setup. */
+ cleanup_cancellation (ctx);
/* Wait... if we're already using an existing bearer, we need to check if it is
* already connected; and if so, just don't do anything else */
@@ -591,15 +621,15 @@ connection_step (ConnectionContext *ctx)
return;
}
- mm_dbg ("Bearer at '%s' is already connected...",
- mm_base_bearer_get_path (ctx->bearer));
+ mm_obj_dbg (ctx->self, "bearer at '%s' is already connected...",
+ mm_base_bearer_get_path (ctx->bearer));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case CONNECTION_STEP_LAST:
- mm_info ("Simple connect state (%d/%d): All done",
- ctx->step, CONNECTION_STEP_LAST);
+ mm_obj_info (ctx->self, "simple connect state (%d/%d): all done",
+ ctx->step, CONNECTION_STEP_LAST);
/* All done, yey! */
mm_gdbus_modem_simple_complete_connect (
ctx->skeleton,
@@ -607,6 +637,9 @@ connection_step (ConnectionContext *ctx)
mm_base_bearer_get_path (ctx->bearer));
connection_context_free (ctx);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -633,50 +666,80 @@ connect_auth_ready (MMBaseModem *self,
return;
}
+ if (!setup_cancellation (ctx, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ connection_context_free (ctx);
+ return;
+ }
+
/* We may be able to skip some steps, so check that before doing anything */
g_object_get (self,
MM_IFACE_MODEM_STATE, &current,
+ MM_IFACE_MODEM_BEARER_LIST, &ctx->bearer_list,
NULL);
+ if (!ctx->bearer_list) {
+ g_dbus_method_invocation_return_error (
+ ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't get the bearer list");
+ connection_context_free (ctx);
+ return;
+ }
- mm_info ("Simple connect started...");
+ mm_obj_info (self, "simple connect started...");
/* Log about all the parameters being used for the simple connect */
{
- MMBearerAllowedAuth allowed_auth;
- gchar *str;
- MMBearerIpFamily ip_family;
+ MMBearerMultiplexSupport multiplex;
+ MMBearerAllowedAuth allowed_auth;
+ MMBearerApnType apn_type;
+ gchar *str;
+ MMBearerIpFamily ip_family;
+ gint profile_id;
#define VALIDATE_UNSPECIFIED(str) (str ? str : "unspecified")
- mm_dbg (" PIN: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_pin (ctx->properties)));
-
- mm_dbg (" Operator ID: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_operator_id (ctx->properties)));
-
- mm_dbg (" Allowed roaming: %s", mm_simple_connect_properties_get_allow_roaming (ctx->properties) ? "yes" : "no");
-
- mm_dbg (" APN: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_apn (ctx->properties)));
+ profile_id = mm_simple_connect_properties_get_profile_id (ctx->properties);
+ if (profile_id != MM_3GPP_PROFILE_ID_UNKNOWN)
+ mm_obj_dbg (self, " profile ID: %d", profile_id);
+ else
+ mm_obj_dbg (self, " profile ID: %s", VALIDATE_UNSPECIFIED (NULL));
+
+ mm_obj_dbg (self, " PIN: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_pin (ctx->properties)));
+ mm_obj_dbg (self, " operator ID: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_operator_id (ctx->properties)));
+ mm_obj_dbg (self, " allowed roaming: %s", mm_simple_connect_properties_get_allow_roaming (ctx->properties) ? "yes" : "no");
+ mm_obj_dbg (self, " APN: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_apn (ctx->properties)));
+
+ apn_type = mm_simple_connect_properties_get_apn_type (ctx->properties);
+ if (apn_type != MM_BEARER_APN_TYPE_NONE) {
+ str = mm_bearer_apn_type_build_string_from_mask (apn_type);
+ mm_obj_dbg (self, " APN type: %s", str);
+ g_free (str);
+ } else
+ mm_obj_dbg (self, " APN type: %s", VALIDATE_UNSPECIFIED (NULL));
ip_family = mm_simple_connect_properties_get_ip_type (ctx->properties);
if (ip_family != MM_BEARER_IP_FAMILY_NONE) {
str = mm_bearer_ip_family_build_string_from_mask (ip_family);
- mm_dbg (" IP family: %s", str);
+ mm_obj_dbg (self, " IP family: %s", str);
g_free (str);
} else
- mm_dbg (" IP family: %s", VALIDATE_UNSPECIFIED (NULL));
+ mm_obj_dbg (self, " IP family: %s", VALIDATE_UNSPECIFIED (NULL));
allowed_auth = mm_simple_connect_properties_get_allowed_auth (ctx->properties);
if (allowed_auth != MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
str = mm_bearer_allowed_auth_build_string_from_mask (allowed_auth);
- mm_dbg (" Allowed authentication: %s", str);
+ mm_obj_dbg (self, " allowed authentication: %s", str);
g_free (str);
} else
- mm_dbg (" Allowed authentication: %s", VALIDATE_UNSPECIFIED (NULL));
+ mm_obj_dbg (self, " allowed authentication: %s", VALIDATE_UNSPECIFIED (NULL));
- mm_dbg (" User: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_user (ctx->properties)));
+ mm_obj_dbg (self, " user: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_user (ctx->properties)));
+ mm_obj_dbg (self, " password: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_password (ctx->properties)));
- mm_dbg (" Password: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_password (ctx->properties)));
-
- mm_dbg (" Number: %s", VALIDATE_UNSPECIFIED (mm_simple_connect_properties_get_number (ctx->properties)));
+ multiplex = mm_simple_connect_properties_get_multiplex (ctx->properties);
+ mm_obj_dbg (self, " multiplex: %s", VALIDATE_UNSPECIFIED (multiplex != MM_BEARER_MULTIPLEX_SUPPORT_UNKNOWN ?
+ mm_bearer_multiplex_support_get_string (multiplex) :
+ NULL));
#undef VALIDATE_UNSPECIFIED
}
@@ -714,6 +777,10 @@ connect_auth_ready (MMBaseModem *self,
case MM_MODEM_STATE_CONNECTED:
ctx->step = CONNECTION_STEP_ENABLE + 1;
break;
+
+ default:
+ g_assert_not_reached ();
+ break;
}
connection_step (ctx);
}
@@ -732,6 +799,8 @@ handle_connect (MmGdbusModemSimple *skeleton,
ctx->self = g_object_ref (self);
ctx->dictionary = g_variant_ref (dictionary);
+ mm_obj_dbg (self, "user request to connect modem");
+
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
MM_AUTHORIZATION_DEVICE_CONTROL,
@@ -760,7 +829,7 @@ disconnection_context_free (DisconnectionContext *ctx)
g_free (ctx->bearer_path);
if (ctx->current)
g_object_unref (ctx->current);
- g_list_free_full (ctx->bearers, (GDestroyNotify) g_object_unref);
+ g_list_free_full (ctx->bearers, g_object_unref);
g_free (ctx);
}
@@ -820,6 +889,7 @@ disconnect_auth_ready (MMBaseModem *self,
{
GError *error = NULL;
MMBearerList *list = NULL;
+ Private *priv;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -827,6 +897,14 @@ disconnect_auth_ready (MMBaseModem *self,
return;
}
+ /* If not disconnecting a specific bearer, also cancel any ongoing
+ * connection attempt. */
+ priv = get_private (MM_IFACE_MODEM_SIMPLE (self));
+ if (!ctx->bearer_path && priv->ongoing_connect) {
+ g_cancellable_cancel (priv->ongoing_connect);
+ g_clear_object (&priv->ongoing_connect);
+ }
+
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
@@ -874,11 +952,18 @@ handle_disconnect (MmGdbusModemSimple *skeleton,
ctx->self = g_object_ref (self);
ctx->invocation = g_object_ref (invocation);
- if (bearer_path &&
- bearer_path[0] == '/' &&
- bearer_path[1]) {
- ctx->bearer_path = g_strdup (ctx->bearer_path);
- }
+ /* The Disconnect() method expects a valid object path given in bearer path,
+ * it cannot be NULL, so we assume we get '/' when we're asked to disconnect
+ * all connected bearers, as that is what mm_modem_simple_disconnect() does
+ * when a NULL bearer path is given to that method.
+ *
+ * We will detect the '/' string and set the bearer path as NULL in the
+ * context if so, and otherwise use the given input string as path */
+ if (g_strcmp0 (bearer_path, "/") != 0) {
+ mm_obj_dbg (self, "user request to disconnect modem (bearer '%s')", bearer_path);
+ ctx->bearer_path = g_strdup (bearer_path);
+ } else
+ mm_obj_dbg (self, "user request to disconnect modem (all bearers)");
mm_base_modem_authorize (MM_BASE_MODEM (self),
invocation,
diff --git a/src/mm-iface-modem-simple.h b/src/mm-iface-modem-simple.h
index 5b9b8af6..1aca5de1 100644
--- a/src/mm-iface-modem-simple.h
+++ b/src/mm-iface-modem-simple.h
@@ -34,6 +34,7 @@ struct _MMIfaceModemSimple {
};
GType mm_iface_modem_simple_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemSimple, g_object_unref)
/* Initialize Modem Simple interface */
void mm_iface_modem_simple_initialize (MMIfaceModemSimple *self);
diff --git a/src/mm-iface-modem-time.c b/src/mm-iface-modem-time.c
index 1a6ab213..0d4f3764 100644
--- a/src/mm-iface-modem-time.c
+++ b/src/mm-iface-modem-time.c
@@ -19,18 +19,15 @@
#include "mm-iface-modem.h"
#include "mm-iface-modem-time.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
-#define SUPPORT_CHECKED_TAG "time-support-checked-tag"
-#define SUPPORTED_TAG "time-supported-tag"
-#define NETWORK_TIMEZONE_CANCELLABLE_TAG "time-network-timezone-cancellable"
+#define SUPPORT_CHECKED_TAG "time-support-checked-tag"
+#define SUPPORTED_TAG "time-supported-tag"
+#define NETWORK_TIMEZONE_CONTEXT_TAG "time-network-timezone-context"
static GQuark support_checked_quark;
static GQuark supported_quark;
-static GQuark network_timezone_cancellable_quark;
-
-#define TIMEZONE_POLL_INTERVAL_SEC 5
-#define TIMEZONE_POLL_RETRIES 6
+static GQuark network_timezone_context_quark;
/*****************************************************************************/
@@ -58,42 +55,34 @@ handle_get_network_time_context_free (HandleGetNetworkTimeContext *ctx)
}
static void
-load_network_time_ready (MMIfaceModemTime *self,
- GAsyncResult *res,
+load_network_time_ready (MMIfaceModemTime *self,
+ GAsyncResult *res,
HandleGetNetworkTimeContext *ctx)
{
gchar *time_str;
GError *error = NULL;
- time_str = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time_finish (self,
- res,
- &error);
+ time_str = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time_finish (self, res, &error);
if (error)
g_dbus_method_invocation_take_error (ctx->invocation, error);
else
- mm_gdbus_modem_time_complete_get_network_time (ctx->skeleton,
- ctx->invocation,
- time_str);
+ mm_gdbus_modem_time_complete_get_network_time (ctx->skeleton, ctx->invocation, time_str);
g_free (time_str);
handle_get_network_time_context_free (ctx);
}
-static gboolean
-handle_get_network_time (MmGdbusModemTime *skeleton,
- GDBusMethodInvocation *invocation,
- MMIfaceModemTime *self)
+static void
+handle_get_network_time_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleGetNetworkTimeContext *ctx)
{
- HandleGetNetworkTimeContext *ctx;
- MMModemState state;
+ MMModemState state;
+ GError *error = NULL;
- if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time ||
- !MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time_finish) {
- g_dbus_method_invocation_return_error (invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot load network time: "
- "operation not supported");
- return TRUE;
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_get_network_time_context_free (ctx);
+ return;
}
state = MM_MODEM_STATE_UNKNOWN;
@@ -102,77 +91,83 @@ handle_get_network_time (MmGdbusModemTime *skeleton,
NULL);
/* If we're not yet registered, we cannot get the network time */
if (state < MM_MODEM_STATE_REGISTERED) {
- g_dbus_method_invocation_return_error (invocation,
+ g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
"Cannot load network time: "
"not registered yet");
- return TRUE;
+ handle_get_network_time_context_free (ctx);
+ return;
}
+ if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time ||
+ !MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot load network time: "
+ "operation not supported");
+ handle_get_network_time_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time (
+ ctx->self,
+ (GAsyncReadyCallback)load_network_time_ready,
+ ctx);
+}
+
+static gboolean
+handle_get_network_time (MmGdbusModemTime *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemTime *self)
+{
+ HandleGetNetworkTimeContext *ctx;
+
ctx = g_new (HandleGetNetworkTimeContext, 1);
ctx->invocation = g_object_ref (invocation);
ctx->skeleton = g_object_ref (skeleton);
ctx->self = g_object_ref (self);
- MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_time (
- self,
- (GAsyncReadyCallback)load_network_time_ready,
- ctx);
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_TIME,
+ (GAsyncReadyCallback)handle_get_network_time_auth_ready,
+ ctx);
return TRUE;
}
/*****************************************************************************/
+/* Network timezone loading */
+
+/*
+ * As soon as we're registered in a network, we try to check timezone
+ * information up to NETWORK_TIMEZONE_POLL_RETRIES times. As soon as one of
+ * the retries succeeds, we stop polling as we don't expect the timezone
+ * information to change while registered in the same network.
+ */
+#define NETWORK_TIMEZONE_POLL_INTERVAL_SEC 5
+#define NETWORK_TIMEZONE_POLL_RETRIES 6
typedef struct {
- MMIfaceModemTime *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- gulong cancelled_id;
gulong state_changed_id;
+ MMModemState state;
guint network_timezone_poll_id;
guint network_timezone_poll_retries;
-} UpdateNetworkTimezoneContext;
-
-static gboolean timezone_poll_cb (UpdateNetworkTimezoneContext *ctx);
+} NetworkTimezoneContext;
static void
-update_network_timezone_context_complete_and_free (UpdateNetworkTimezoneContext *ctx)
+network_timezone_context_free (NetworkTimezoneContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->self);
+ /* Note: no need to remove signal connection here, we have already done it
+ * in stop_network_timezone() when the logic is disabled (or will be done
+ * automatically when the last modem object reference is dropped) */
+ if (ctx->network_timezone_poll_id)
+ g_source_remove (ctx->network_timezone_poll_id);
g_free (ctx);
}
-static gboolean
-update_network_timezone_finish (MMIfaceModemTime *self,
- GAsyncResult *res,
- GError **error)
-{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-cancelled (GCancellable *cancellable,
- UpdateNetworkTimezoneContext *ctx)
-{
- /* If waiting to get registered, disconnect signal */
- if (ctx->state_changed_id)
- g_signal_handler_disconnect (ctx->self,
- ctx->state_changed_id);
-
- /* If waiting in the timeout loop, remove the timeout */
- else if (ctx->network_timezone_poll_id)
- g_source_remove (ctx->network_timezone_poll_id);
-
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Network timezone loading cancelled");
- update_network_timezone_context_complete_and_free (ctx);
-}
+static gboolean network_timezone_poll_cb (MMIfaceModemTime *self);
static void
update_network_timezone_dictionary (MMIfaceModemTime *self,
@@ -197,171 +192,163 @@ update_network_timezone_dictionary (MMIfaceModemTime *self,
static void
load_network_timezone_ready (MMIfaceModemTime *self,
- GAsyncResult *res,
- UpdateNetworkTimezoneContext *ctx)
+ GAsyncResult *res)
{
- GError *error = NULL;
+ g_autoptr(GError) error = NULL;
MMNetworkTimezone *tz;
- if (g_cancellable_is_cancelled (ctx->cancellable)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Finished network timezone loading, "
- "but cancelled meanwhile");
- update_network_timezone_context_complete_and_free (ctx);
- return;
- }
-
/* Finish the async operation */
- tz = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish (self,
- res,
- &error);
- if (error) {
+ tz = MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish (self, res, &error);
+ if (!tz) {
+ NetworkTimezoneContext *ctx;
+
+ mm_obj_dbg (self, "couldn't load network timezone: %s", error->message);
+
+ /* Note: may be NULL if the polling has been removed while processing the async operation */
+ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark);
+ if (!ctx)
+ return;
+
/* Retry? */
ctx->network_timezone_poll_retries--;
- /* Fatal if no more retries, or if specific error is not RETRY */
+ /* If no more retries, we don't do anything else */
if (ctx->network_timezone_poll_retries == 0 ||
- !g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_RETRY)) {
- g_simple_async_result_take_error (ctx->result, error);
- update_network_timezone_context_complete_and_free (ctx);
+ !g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
+ mm_obj_warn (self, "couldn't load network timezone from the current network");
return;
}
- /* Otherwise, reconnect cancellable and relaunch timeout to query a bit
- * later */
- ctx->cancelled_id = g_cancellable_connect (ctx->cancellable,
- G_CALLBACK (cancelled),
- ctx,
- NULL);
- ctx->network_timezone_poll_id = g_timeout_add_seconds (TIMEZONE_POLL_INTERVAL_SEC,
- (GSourceFunc)timezone_poll_cb,
- ctx);
-
- g_error_free (error);
+ /* Otherwise, relaunch timeout to query a bit later */
+ ctx->network_timezone_poll_id = g_timeout_add_seconds (NETWORK_TIMEZONE_POLL_INTERVAL_SEC,
+ (GSourceFunc)network_timezone_poll_cb,
+ self);
return;
}
/* Got final result properly, update the property in the skeleton */
- update_network_timezone_dictionary (ctx->self, tz);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- update_network_timezone_context_complete_and_free (ctx);
+ update_network_timezone_dictionary (self, tz);
g_object_unref (tz);
}
static gboolean
-timezone_poll_cb (UpdateNetworkTimezoneContext *ctx)
+network_timezone_poll_cb (MMIfaceModemTime *self)
{
- ctx->network_timezone_poll_id = 0;
+ NetworkTimezoneContext *ctx;
- /* Before we launch the async loading of the network timezone,
- * we disconnect the cancellable signal. We don't want to get
- * signaled while waiting to finish this async method, we'll
- * check the cancellable afterwards instead. */
- g_cancellable_disconnect (ctx->cancellable,
- ctx->cancelled_id);
- ctx->cancelled_id = 0;
+ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark);
+ ctx->network_timezone_poll_id = 0;
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->load_network_timezone (
- ctx->self,
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone (
+ self,
(GAsyncReadyCallback)load_network_timezone_ready,
- ctx);
+ NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
-start_timezone_poll (UpdateNetworkTimezoneContext *ctx)
+start_network_timezone_poll (MMIfaceModemTime *self)
{
- /* Setup loop to query current timezone, don't do it right away.
- * Note that we're passing the context reference to the loop. */
- ctx->network_timezone_poll_retries = TIMEZONE_POLL_RETRIES;
- ctx->network_timezone_poll_id = g_timeout_add_seconds (TIMEZONE_POLL_INTERVAL_SEC,
- (GSourceFunc)timezone_poll_cb,
- ctx);
+ NetworkTimezoneContext *ctx;
+
+ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark);
+
+ mm_obj_dbg (self, "network timezone polling started");
+ ctx->network_timezone_poll_retries = NETWORK_TIMEZONE_POLL_RETRIES;
+ ctx->network_timezone_poll_id = g_timeout_add_seconds (NETWORK_TIMEZONE_POLL_INTERVAL_SEC, (GSourceFunc)network_timezone_poll_cb, self);
}
static void
-state_changed (MMIfaceModemTime *self,
- GParamSpec *spec,
- UpdateNetworkTimezoneContext *ctx)
+stop_network_timezone_poll (MMIfaceModemTime *self)
{
- MMModemState state = MM_MODEM_STATE_UNKNOWN;
+ NetworkTimezoneContext *ctx;
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &state,
- NULL);
+ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark);
- /* We're waiting to get registered */
- if (state < MM_MODEM_STATE_REGISTERED)
+ if (ctx->network_timezone_poll_id) {
+ mm_obj_dbg (self, "network timezone polling stopped");
+ g_source_remove (ctx->network_timezone_poll_id);
+ ctx->network_timezone_poll_id = 0;
+ }
+}
+
+static void
+network_timezone_state_changed (MMIfaceModemTime *self)
+{
+ NetworkTimezoneContext *ctx;
+ MMModemState old_state;
+
+ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark);
+ old_state = ctx->state;
+
+ g_object_get (self, MM_IFACE_MODEM_STATE, &ctx->state, NULL);
+
+ /* If going from unregistered to registered, start polling */
+ if (ctx->state >= MM_MODEM_STATE_REGISTERED && old_state < MM_MODEM_STATE_REGISTERED) {
+ start_network_timezone_poll (self);
return;
+ }
- /* Got registered, disconnect signal */
- if (ctx->state_changed_id) {
- g_signal_handler_disconnect (self,
- ctx->state_changed_id);
- ctx->state_changed_id = 0;
+ /* If going from registered to unregistered, stop polling */
+ if (ctx->state < MM_MODEM_STATE_REGISTERED && old_state >= MM_MODEM_STATE_REGISTERED) {
+ stop_network_timezone_poll (self);
+ return;
}
+}
- /* Once we know we're registered, start timezone poll */
- start_timezone_poll (ctx);
+static void
+stop_network_timezone (MMIfaceModemTime *self)
+{
+ NetworkTimezoneContext *ctx;
+
+ ctx = (NetworkTimezoneContext *) g_object_get_qdata (G_OBJECT (self), network_timezone_context_quark);
+ if (ctx) {
+ /* Remove signal connection and then trigger context free */
+ if (ctx->state_changed_id) {
+ g_signal_handler_disconnect (self, ctx->state_changed_id);
+ ctx->state_changed_id = 0;
+ }
+ g_object_set_qdata (G_OBJECT (self), network_timezone_context_quark, NULL);
+ }
}
static void
-update_network_timezone (MMIfaceModemTime *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+start_network_timezone (MMIfaceModemTime *self)
{
- UpdateNetworkTimezoneContext *ctx;
- MMModemState state = MM_MODEM_STATE_UNKNOWN;
+ NetworkTimezoneContext *ctx;
/* If loading network timezone not supported, just finish here */
if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone ||
!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->load_network_timezone_finish) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Loading network timezone is not supported");
+ mm_obj_dbg (self, "loading network timezone is not supported");
return;
}
- ctx = g_new0 (UpdateNetworkTimezoneContext, 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,
- update_network_timezone);
-
- /* Note: we don't expect to get cancelled by any other thread, so no
- * need to check if we're cancelled just after connecting to the
- * cancelled signal */
- ctx->cancelled_id = g_cancellable_connect (ctx->cancellable,
- G_CALLBACK (cancelled),
- ctx,
- NULL);
-
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &state,
- NULL);
-
- /* Already registered? */
- if (state >= MM_MODEM_STATE_REGISTERED) {
- /* Once we know we're registered, start timezone poll */
- start_timezone_poll (ctx);
- } else {
- /* Want to get notified when modem state changes */
- ctx->state_changed_id = g_signal_connect (ctx->self,
- "notify::" MM_IFACE_MODEM_STATE,
- G_CALLBACK (state_changed),
- ctx);
- }
+ if (G_UNLIKELY (!network_timezone_context_quark))
+ network_timezone_context_quark = (g_quark_from_static_string (NETWORK_TIMEZONE_CONTEXT_TAG));
+
+ /* Cleanup context properly if it already exists, including the signal handler */
+ stop_network_timezone (self);
+
+ ctx = g_new0 (NetworkTimezoneContext, 1);
+ g_object_set_qdata_full (G_OBJECT (self),
+ network_timezone_context_quark,
+ ctx,
+ (GDestroyNotify)network_timezone_context_free);
+
+ /* Want to get notified when modem state changes to enable/disable
+ * the polling logic. This signal is connected as long as the network timezone
+ * logic is enabled. */
+ g_object_get (self, MM_IFACE_MODEM_STATE, &ctx->state, NULL);
+ ctx->state_changed_id = g_signal_connect (self,
+ "notify::" MM_IFACE_MODEM_STATE,
+ G_CALLBACK (network_timezone_state_changed),
+ NULL);
+
+ /* If we're registered already, start timezone poll */
+ if (ctx->state >= MM_MODEM_STATE_REGISTERED)
+ start_network_timezone_poll (self);
}
/*****************************************************************************/
@@ -384,10 +371,31 @@ mm_iface_modem_time_update_network_time (MMIfaceModemTime *self,
g_object_unref (skeleton);
}
+void
+mm_iface_modem_time_update_network_timezone (MMIfaceModemTime *self,
+ MMNetworkTimezone *tz)
+{
+ MmGdbusModemTime *skeleton;
+ GVariant *dictionary;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_TIME_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ dictionary = mm_network_timezone_get_dictionary (tz);
+ mm_gdbus_modem_time_set_network_timezone (skeleton, dictionary);
+ if (dictionary)
+ g_variant_unref (dictionary);
+
+ g_object_unref (skeleton);
+}
+
/*****************************************************************************/
typedef struct _DisablingContext DisablingContext;
-static void interface_disabling_step (DisablingContext *ctx);
+static void interface_disabling_step (GTask *task);
typedef enum {
DISABLING_STEP_FIRST,
@@ -398,18 +406,13 @@ typedef enum {
} DisablingStep;
struct _DisablingContext {
- MMIfaceModemTime *self;
DisablingStep step;
- GSimpleAsyncResult *result;
MmGdbusModemTime *skeleton;
};
static void
-disabling_context_complete_and_free (DisablingContext *ctx)
+disabling_context_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);
@@ -420,106 +423,105 @@ mm_iface_modem_time_disable_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
disable_unsolicited_events_ready (MMIfaceModemTime *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_TIME_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
cleanup_unsolicited_events_ready (MMIfaceModemTime *self,
GAsyncResult *res,
- DisablingContext *ctx)
+ GTask *task)
{
+ DisablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_TIME_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
static void
-interface_disabling_step (DisablingContext *ctx)
+interface_disabling_step (GTask *task)
{
+ MMIfaceModemTime *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case DISABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
- case DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE: {
- if (G_LIKELY (network_timezone_cancellable_quark)) {
- GCancellable *cancellable = NULL;
-
- cancellable = g_object_get_qdata (G_OBJECT (ctx->self),
- network_timezone_cancellable_quark);
-
- /* If network timezone loading is currently running, abort it */
- if (cancellable) {
- g_cancellable_cancel (cancellable);
- g_object_set_qdata (G_OBJECT (ctx->self),
- network_timezone_cancellable_quark,
- NULL);
- }
- }
-
- /* Fall down to next step */
+ case DISABLING_STEP_CANCEL_NETWORK_TIMEZONE_UPDATE:
+ /* Stop and cleanup context */
+ stop_network_timezone (self);
ctx->step++;
- }
+ /* fall through */
case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
/* Allow cleaning up unsolicited events */
- if (MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->disable_unsolicited_events &&
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->disable_unsolicited_events_finish) {
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->disable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
(GAsyncReadyCallback)disable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
/* Allow cleaning up unsolicited events */
- if (MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events &&
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events_finish) {
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->cleanup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
(GAsyncReadyCallback)cleanup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -531,33 +533,33 @@ mm_iface_modem_time_disable (MMIfaceModemTime *self,
gpointer user_data)
{
DisablingContext *ctx;
+ GTask *task;
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_time_disable);
ctx->step = DISABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_TIME_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_disabling_step (ctx);
+ interface_disabling_step (task);
}
/*****************************************************************************/
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
@@ -568,173 +570,127 @@ typedef enum {
} EnablingStep;
struct _EnablingContext {
- MMIfaceModemTime *self;
EnablingStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModemTime *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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_time_enable_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
-}
-
-static void
-update_network_timezone_ready (MMIfaceModemTime *self,
- GAsyncResult *res)
-{
- GError *error = NULL;
-
- if (!update_network_timezone_finish (self, res, &error)) {
- if (!g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED))
- mm_dbg ("Couldn't update network timezone: '%s'", error->message);
- g_error_free (error);
- }
-
- /* Cleanup our cancellable in the context */
- g_object_set_qdata (G_OBJECT (self),
- network_timezone_cancellable_quark,
- NULL);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
setup_unsolicited_events_ready (MMIfaceModemTime *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
MM_IFACE_MODEM_TIME_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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
enable_unsolicited_events_ready (MMIfaceModemTime *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
/* Not critical! */
if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) {
- mm_dbg ("Couldn't enable unsolicited events: '%s'", error->message);
+ mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message);
g_error_free (error);
}
/* Go on with next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModemTime *self;
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
- case ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL: {
- GCancellable *cancellable;
-
- /* We'll create a cancellable which is valid as long as we're updating
- * network timezone, and we set it as context */
- cancellable = g_cancellable_new ();
- if (G_UNLIKELY (!network_timezone_cancellable_quark))
- network_timezone_cancellable_quark = (g_quark_from_static_string (
- NETWORK_TIMEZONE_CANCELLABLE_TAG));
- g_object_set_qdata_full (G_OBJECT (ctx->self),
- network_timezone_cancellable_quark,
- cancellable,
- (GDestroyNotify)g_object_unref);
-
- update_network_timezone (ctx->self,
- cancellable,
- (GAsyncReadyCallback)update_network_timezone_ready,
- NULL);
-
- /* NOTE!!!! We'll leave the timezone network update operation
- * running, we don't wait for it to finish */
-
- /* Fall down to next step */
+ case ENABLING_STEP_SETUP_NETWORK_TIMEZONE_RETRIEVAL:
+ /* We start it and schedule it to run asynchronously */
+ start_network_timezone (self);
ctx->step++;
- }
+ /* fall through */
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
/* Allow setting up unsolicited events */
- if (MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->setup_unsolicited_events &&
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->setup_unsolicited_events_finish) {
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->setup_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
(GAsyncReadyCallback)setup_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
/* Allow setting up unsolicited events */
- if (MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->enable_unsolicited_events &&
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->enable_unsolicited_events_finish) {
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->enable_unsolicited_events (
- ctx->self,
+ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
(GAsyncReadyCallback)enable_unsolicited_events_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -747,34 +703,111 @@ mm_iface_modem_time_enable (MMIfaceModemTime *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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_time_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_TIME_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef struct _SyncingContext SyncingContext;
+static void interface_syncing_step (GTask *task);
+
+typedef enum {
+ SYNCING_STEP_FIRST,
+ SYNCING_STEP_REFRESH_NETWORK_TIMEZONE,
+ SYNCING_STEP_LAST
+} SyncingStep;
+
+struct _SyncingContext {
+ SyncingStep step;
+};
+
+gboolean
+mm_iface_modem_time_sync_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+interface_syncing_step (GTask *task)
+{
+ MMIfaceModemTime *self;
+ SyncingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SYNCING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_REFRESH_NETWORK_TIMEZONE:
+ /* We start it and schedule it to run asynchronously */
+ start_network_timezone (self);
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_time_sync (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncingContext *ctx;
+ GTask *task;
+
+ /* Create SyncingContext */
+ ctx = g_new0 (SyncingContext, 1);
+ ctx->step = SYNCING_STEP_FIRST;
+
+ /* Create sync steps task and execute it */
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
+ interface_syncing_step (task);
+}
+
+#endif
+
+/*****************************************************************************/
+
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
@@ -784,43 +817,23 @@ typedef enum {
} InitializationStep;
struct _InitializationContext {
- MMIfaceModemTime *self;
MmGdbusModemTime *skeleton;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
InitializationStep step;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_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 (MMIfaceModemTime *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support_finish (self,
@@ -828,7 +841,7 @@ check_support_ready (MMIfaceModemTime *self,
&error)) {
if (error) {
/* This error shouldn't be treated as critical */
- mm_dbg ("Time support check failed: '%s'", error->message);
+ mm_obj_dbg (self, "time support check failed: %s", error->message);
g_error_free (error);
}
} else {
@@ -839,16 +852,25 @@ check_support_ready (MMIfaceModemTime *self,
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModemTime *self;
+ InitializationContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
@@ -860,48 +882,48 @@ interface_initialization_step (InitializationContext *ctx)
supported_quark = (g_quark_from_static_string (
SUPPORTED_TAG));
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_CHECK_SUPPORT:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
support_checked_quark))) {
/* Set the checked flag so that we don't run it again */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
support_checked_quark,
GUINT_TO_POINTER (TRUE));
/* Initially, assume we don't support it */
- g_object_set_qdata (G_OBJECT (ctx->self),
+ g_object_set_qdata (G_OBJECT (self),
supported_quark,
GUINT_TO_POINTER (FALSE));
- if (MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->check_support &&
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->check_support_finish) {
- MM_IFACE_MODEM_TIME_GET_INTERFACE (ctx->self)->check_support (
- ctx->self,
+ if (MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_TIME_GET_INTERFACE (self)->check_support (
+ self,
(GAsyncReadyCallback)check_support_ready,
- ctx);
+ task);
return;
}
/* If there is no implementation to check support, assume we DON'T
* support it. */
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
- if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (ctx->self),
+ if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
supported_quark))) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Time not supported");
- initialization_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Time not supported");
+ g_object_unref (task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall through */
case INITIALIZATION_STEP_LAST:
/* We are done without errors! */
@@ -910,15 +932,18 @@ interface_initialization_step (InitializationContext *ctx)
g_signal_connect (ctx->skeleton,
"handle-get-network-time",
G_CALLBACK (handle_get_network_time),
- ctx->self);
+ self);
/* Finally, export the new interface */
- mm_gdbus_object_skeleton_set_modem_time (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ mm_gdbus_object_skeleton_set_modem_time (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM_TIME (ctx->skeleton));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- initialization_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -929,7 +954,7 @@ mm_iface_modem_time_initialize_finish (MMIfaceModemTime *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -940,6 +965,7 @@ mm_iface_modem_time_initialize (MMIfaceModemTime *self,
{
InitializationContext *ctx;
MmGdbusModemTime *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -955,16 +981,13 @@ mm_iface_modem_time_initialize (MMIfaceModemTime *self,
/* 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_time_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
diff --git a/src/mm-iface-modem-time.h b/src/mm-iface-modem-time.h
index 7712e4cf..a3ac7074 100644
--- a/src/mm-iface-modem-time.h
+++ b/src/mm-iface-modem-time.h
@@ -94,6 +94,7 @@ struct _MMIfaceModemTime {
};
GType mm_iface_modem_time_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemTime, g_object_unref)
/* Initialize Time interface (async) */
void mm_iface_modem_time_initialize (MMIfaceModemTime *self,
@@ -124,13 +125,27 @@ gboolean mm_iface_modem_time_disable_finish (MMIfaceModemTime *self,
/* Shutdown Time interface */
void mm_iface_modem_time_shutdown (MMIfaceModemTime *self);
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+/* Sync Time interface (async) */
+void mm_iface_modem_time_sync (MMIfaceModemTime *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_time_sync_finish (MMIfaceModemTime *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
+
/* Bind properties for simple GetStatus() */
void mm_iface_modem_time_bind_simple_status (MMIfaceModemTime *self,
MMSimpleStatus *status);
/* Implementations of the unsolicited events handling should call this method
* to notify about the updated time */
-void mm_iface_modem_time_update_network_time (MMIfaceModemTime *self,
- const gchar *network_time);
+void mm_iface_modem_time_update_network_time (MMIfaceModemTime *self,
+ const gchar *network_time);
+void mm_iface_modem_time_update_network_timezone (MMIfaceModemTime *self,
+ MMNetworkTimezone *tz);
#endif /* MM_IFACE_MODEM_TIME_H */
diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c
new file mode 100644
index 00000000..4f3b30fc
--- /dev/null
+++ b/src/mm-iface-modem-voice.c
@@ -0,0 +1,3217 @@
+/* -*- 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) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-voice.h"
+#include "mm-call-list.h"
+#include "mm-log-object.h"
+
+#define CALL_LIST_POLLING_CONTEXT_TAG "voice-call-list-polling-context-tag"
+#define IN_CALL_EVENT_CONTEXT_TAG "voice-in-call-event-context-tag"
+
+static GQuark call_list_polling_context_quark;
+static GQuark in_call_event_context_quark;
+
+/*****************************************************************************/
+
+void
+mm_iface_modem_voice_bind_simple_status (MMIfaceModemVoice *self,
+ MMSimpleStatus *status)
+{
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_iface_modem_voice_authorize_outgoing_call (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GError **error)
+{
+ MmGdbusModemVoice *skeleton = NULL;
+ MMBaseSim *sim = NULL;
+ gboolean emergency_only = FALSE;
+ gboolean call_allowed = FALSE;
+ GError *inner_error = NULL;
+ guint i;
+ const gchar *number;
+
+ static const gchar *always_valid_emergency_numbers[] = { "112", "911" };
+ static const gchar *no_sim_valid_emergency_numbers[] = { "000", "08", "110", "999", "118", "119" };
+
+ g_assert (mm_base_call_get_direction (call) == MM_CALL_DIRECTION_OUTGOING);
+ number = mm_base_call_get_number (call);
+ g_assert (number);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!skeleton) {
+ g_set_error (&inner_error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "voice operations unsupported");
+ goto out;
+ }
+
+ g_object_get (skeleton,
+ "emergency-only", &emergency_only,
+ NULL);
+
+ /* Identification of emergency numbers. 3GPP TS 22.101
+ *
+ * a) 112 and 911 shall always be available.
+ * b) Any emergency call number stored on a SIM/USIM when the SIM/USIM is
+ * present.
+ * c) 000, 08, 110, 999, 118 and 119 when a SIM/USIM is not present.
+ * d) Additional emergency call numbers that may have been downloaded by
+ * the serving network when the SIM/USIM is present.
+ *
+ * In ModemManager we're not flagging any call as being "emergency" or
+ * "normal", but we can right away limit non-emergency calls if we're in
+ * "emergency-only" mode.
+ */
+
+ /* If we're not in emergency mode, the call (emergency or normal) is always allowed */
+ if (!emergency_only) {
+ mm_obj_dbg (self, "voice call to %s allowed", number);
+ call_allowed = TRUE;
+ goto out;
+ }
+
+ for (i = 0; i < G_N_ELEMENTS (always_valid_emergency_numbers); i++) {
+ if (g_strcmp0 (number, always_valid_emergency_numbers[i]) == 0) {
+ mm_obj_dbg (self, "voice call to %s allowed: emergency call number always valid", number);
+ call_allowed = TRUE;
+ goto out;
+ }
+ }
+
+ /* Check if we have a SIM */
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM, &sim,
+ NULL);
+ if (!sim) {
+ /* If no SIM available, some additional numbers may be valid emergency numbers */
+ for (i = 0; i < G_N_ELEMENTS (no_sim_valid_emergency_numbers); i++) {
+ if (g_strcmp0 (number, no_sim_valid_emergency_numbers[i]) == 0) {
+ mm_obj_dbg (self, "voice call to %s allowed: emergency call number valid when no SIM", number);
+ call_allowed = TRUE;
+ goto out;
+ }
+ }
+
+ mm_obj_dbg (self, "voice call to %s NOT allowed: not a valid emergency call number when no SIM", number);
+ goto out;
+ }
+
+ /* Check if the number is programmed in EF_ECC */
+ if (mm_base_sim_is_emergency_number (sim, number)) {
+ mm_obj_dbg (self, "voice call to %s allowed: emergency call number programmed in the SIM", number);
+ call_allowed = TRUE;
+ } else
+ mm_obj_dbg (self, "voice call to %s NOT allowed: not a valid emergency call number programmed in the SIM", number);
+
+ out:
+
+ if (inner_error)
+ g_propagate_error (error, inner_error);
+ else if (!call_allowed)
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNAUTHORIZED,
+ "only emergency calls allowed");
+
+ g_clear_object (&skeleton);
+ g_clear_object (&sim);
+ return call_allowed;
+}
+
+/*****************************************************************************/
+
+/* new calls will inherit audio settings if the modem is already in-call state */
+static void update_audio_settings_in_call (MMIfaceModemVoice *self,
+ MMBaseCall *call);
+
+static MMBaseCall *
+create_incoming_call (MMIfaceModemVoice *self,
+ const gchar *number)
+{
+ MMBaseCall *call;
+
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call != NULL);
+
+ call = MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call (self, MM_CALL_DIRECTION_INCOMING, number);
+ update_audio_settings_in_call (self, call);
+ return call;
+}
+
+static MMBaseCall *
+create_outgoing_call_from_properties (MMIfaceModemVoice *self,
+ MMCallProperties *properties,
+ GError **error)
+{
+ MMBaseCall *call;
+ const gchar *number;
+
+ /* Don't create CALL from properties if either number is missing */
+ number = mm_call_properties_get_number (properties) ;
+ if (!number) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot create call: mandatory parameter 'number' is missing");
+ return NULL;
+ }
+
+ /* Create a call object as defined by the interface */
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call != NULL);
+ call = MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->create_call (self, MM_CALL_DIRECTION_OUTGOING, number);
+ update_audio_settings_in_call (self, call);
+ return call;
+}
+
+/*****************************************************************************/
+/* Common helper to match call info against a known call object */
+
+static gboolean
+match_single_call_info (MMIfaceModemVoice *self,
+ const MMCallInfo *call_info,
+ MMBaseCall *call)
+{
+ MMCallState state;
+ MMCallDirection direction;
+ const gchar *number;
+ guint idx;
+ gboolean match_direction_and_state = FALSE;
+ gboolean match_number = FALSE;
+ gboolean match_index = FALSE;
+ gboolean match_terminated = FALSE;
+
+ /* try to look for a matching call by direction/number/index */
+ state = mm_base_call_get_state (call);
+ direction = mm_base_call_get_direction (call);
+ number = mm_base_call_get_number (call);
+ idx = mm_base_call_get_index (call);
+
+ /* Match index */
+ if (call_info->index && (call_info->index == idx))
+ match_index = TRUE;
+
+ /* Match direction and state.
+ * We cannot apply this match if both call info and call have an index set
+ * and they're different already. */
+ if ((call_info->direction == direction) &&
+ (call_info->state == state) &&
+ (!call_info->index || !idx || match_index))
+ match_direction_and_state = TRUE;
+
+ /* Match number */
+ if (call_info->number && number &&
+ g_strcmp0 (call_info->number, number) == 0)
+ match_number = TRUE;
+
+ /* Match special terminated event.
+ * We cannot apply this match if the call is part of a multiparty
+ * call, because we don't know which of the calls in the multiparty
+ * is the one that finished. Must rely on other reports that do
+ * provide call index. */
+ if ((call_info->state == MM_CALL_STATE_TERMINATED) &&
+ (call_info->direction == MM_CALL_DIRECTION_UNKNOWN) &&
+ !call_info->index &&
+ !call_info->number &&
+ !mm_base_call_get_multiparty (call))
+ match_terminated = TRUE;
+
+ /* If no clear match, nothing to do */
+ if (!match_direction_and_state &&
+ !match_number &&
+ !match_index &&
+ !match_terminated)
+ return FALSE;
+
+ mm_obj_dbg (self, "call info matched (matched direction/state %s, matched number %s"
+ ", matched index %s, matched terminated %s) with call at '%s'",
+ match_direction_and_state ? "yes" : "no",
+ match_number ? "yes" : "no",
+ match_index ? "yes" : "no",
+ match_terminated ? "yes" : "no",
+ mm_base_call_get_path (call));
+
+ /* Early detect if a known incoming call that was created
+ * from a plain CRING URC (i.e. without caller number)
+ * needs to have the number provided.
+ */
+ if (call_info->number && !number) {
+ mm_obj_dbg (self, " number set: %s", call_info->number);
+ mm_base_call_set_number (call, call_info->number);
+ }
+
+ /* Early detect if a known incoming/outgoing call does
+ * not have a known call index yet.
+ */
+ if (call_info->index && !idx) {
+ mm_obj_dbg (self, " index set: %u", call_info->index);
+ mm_base_call_set_index (call, call_info->index);
+ }
+
+ /* Update state if it changed */
+ if (call_info->state != state) {
+ mm_obj_dbg (self, " state updated: %s", mm_call_state_get_string (call_info->state));
+ mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_UNKNOWN);
+ }
+
+ /* refresh if incoming and new state is not terminated */
+ if ((call_info->state != MM_CALL_STATE_TERMINATED) &&
+ (direction == MM_CALL_DIRECTION_INCOMING)) {
+ mm_obj_dbg (self, " incoming refreshed");
+ mm_base_call_incoming_refresh (call);
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMIfaceModemVoice *self;
+ const MMCallInfo *call_info;
+} ReportCallForeachContext;
+
+static void
+report_call_foreach (MMBaseCall *call,
+ ReportCallForeachContext *ctx)
+{
+ /* Do nothing if already matched */
+ if (!ctx->call_info)
+ return;
+
+ /* fully ignore already terminated calls */
+ if (mm_base_call_get_state (call) == MM_CALL_STATE_TERMINATED)
+ return;
+
+ /* Reset call info in context if the call info matches an existing call */
+ if (match_single_call_info (ctx->self, ctx->call_info, call))
+ ctx->call_info = NULL;
+}
+
+void
+mm_iface_modem_voice_report_call (MMIfaceModemVoice *self,
+ const MMCallInfo *call_info)
+{
+ ReportCallForeachContext ctx = { 0 };
+ MMBaseCall *call = NULL;
+ MMCallList *list = NULL;
+
+ /* When reporting single call, the only mandatory parameter is the state:
+ * - index is optional (e.g. unavailable when receiving +CLIP URCs)
+ * - number is optional (e.g. unavailable when receiving +CRING URCs)
+ * - direction is optional (e.g. unavailable when receiving some vendor-specific URCs)
+ */
+ g_assert (call_info->state != MM_CALL_STATE_UNKNOWN);
+
+ /* Early debugging of the call state update */
+ mm_obj_dbg (self, "call at index %u: direction %s, state %s, number %s",
+ call_info->index,
+ mm_call_direction_get_string (call_info->direction),
+ mm_call_state_get_string (call_info->state),
+ call_info->number ? call_info->number : "n/a");
+
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+
+ if (!list) {
+ mm_obj_warn (self, "cannot process call state update: missing call list");
+ return;
+ }
+
+ /* Iterate over all known calls and try to match a known one */
+ ctx.self = self;
+ ctx.call_info = call_info;
+ mm_call_list_foreach (list, (MMCallListForeachFunc)report_call_foreach, &ctx);
+
+ /* If call info matched with an existing one, the context call info would have been reseted */
+ if (!ctx.call_info)
+ goto out;
+
+ /* If call info didn't match with any known call, it may be because we're being
+ * reported a NEW incoming call. If that's not the case, we'll ignore the report. */
+ if ((call_info->direction != MM_CALL_DIRECTION_INCOMING) ||
+ ((call_info->state != MM_CALL_STATE_WAITING) && (call_info->state != MM_CALL_STATE_RINGING_IN))) {
+ mm_obj_dbg (self, "unhandled call state update reported: direction: %s, state %s",
+ mm_call_direction_get_string (call_info->direction),
+ mm_call_state_get_string (call_info->state));
+ goto out;
+ }
+
+ mm_obj_dbg (self, "creating new incoming call...");
+ call = create_incoming_call (self, call_info->number);
+
+ /* Set the state */
+ mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_INCOMING_NEW);
+
+ /* Set the index, if known */
+ if (call_info->index)
+ mm_base_call_set_index (call, call_info->index);
+
+ /* Start its validity timeout */
+ mm_base_call_incoming_refresh (call);
+
+ /* Only export once properly created */
+ mm_base_call_export (call);
+ mm_call_list_add_call (list, call);
+ g_object_unref (call);
+
+ out:
+ g_object_unref (list);
+}
+
+/*****************************************************************************/
+/* Full current call list reporting
+ *
+ * This method receives as input a list with all the currently active calls,
+ * including the specific state they're in.
+ *
+ * This method should:
+ * - Check whether we're reporting a new call (i.e. not in our internal call
+ * list yet). We'll create a new call object if so.
+ * - Check whether any of the known calls has changed state, and if so,
+ * update it.
+ * - Check whether any of the known calls is NOT given in the input list of
+ * call infos, which would mean the call is terminated.
+ */
+
+typedef struct {
+ MMIfaceModemVoice *self;
+ GList *call_info_list;
+} ReportAllCallsForeachContext;
+
+static void
+report_all_calls_foreach (MMBaseCall *call,
+ ReportAllCallsForeachContext *ctx)
+{
+ MMCallState state;
+ GList *l;
+
+ /* fully ignore already terminated calls */
+ state = mm_base_call_get_state (call);
+ if (state == MM_CALL_STATE_TERMINATED)
+ return;
+
+ /* Iterate over the call info list */
+ for (l = ctx->call_info_list; l; l = g_list_next (l)) {
+ MMCallInfo *call_info = (MMCallInfo *)(l->data);
+
+ /* if match found, delete item from list and halt iteration right away */
+ if (match_single_call_info (ctx->self, call_info, call)) {
+ ctx->call_info_list = g_list_delete_link (ctx->call_info_list, l);
+ return;
+ }
+ }
+
+ /* not found in list! this call is now terminated */
+ mm_obj_dbg (ctx->self, "call '%s' with direction %s, state %s, number '%s', index %u"
+ " not found in list, terminating",
+ mm_base_call_get_path (call),
+ mm_call_direction_get_string (mm_base_call_get_direction (call)),
+ mm_call_state_get_string (state),
+ mm_base_call_get_number (call),
+ mm_base_call_get_index (call));
+ mm_base_call_change_state (call, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_UNKNOWN);
+}
+
+void
+mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self,
+ GList *call_info_list)
+{
+ ReportAllCallsForeachContext ctx = { 0 };
+ MMCallList *list = NULL;
+ GList *l;
+
+ /* Early debugging of the full list of calls */
+ mm_obj_dbg (self, "reported %u ongoing calls", g_list_length (call_info_list));
+ for (l = call_info_list; l; l = g_list_next (l)) {
+ MMCallInfo *call_info = (MMCallInfo *)(l->data);
+
+ /* When reporting full list of calls, index and state are mandatory */
+ g_assert (call_info->index != 0);
+ g_assert (call_info->state != MM_CALL_STATE_UNKNOWN);
+
+ mm_obj_dbg (self, "call at index %u: direction %s, state %s, number %s",
+ call_info->index,
+ mm_call_direction_get_string (call_info->direction),
+ mm_call_state_get_string (call_info->state),
+ call_info->number ? call_info->number : "n/a");
+ }
+
+ /* Retrieve list of known calls */
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ mm_obj_warn (self, "cannot report all calls: missing call list");
+ return;
+ }
+
+ /* Iterate over all the calls already known to us.
+ * Whenever a known call is updated, it will be removed from the call info list */
+ ctx.self = self;
+ ctx.call_info_list = g_list_copy (call_info_list);
+ mm_call_list_foreach (list, (MMCallListForeachFunc)report_all_calls_foreach, &ctx);
+
+ /* Once processed, the call info list will have all calls that were unknown to
+ * us, i.e. the new calls to create. We really only expect new incoming calls, so
+ * we'll warn if we get any outgoing call reported here. */
+ for (l = ctx.call_info_list; l; l = g_list_next (l)) {
+ MMCallInfo *call_info = (MMCallInfo *)(l->data);
+
+ /* Ignore unknown terminated calls, because these be due to an already
+ * processed event. */
+ if (call_info->state == MM_CALL_STATE_TERMINATED)
+ continue;
+
+ if (call_info->direction == MM_CALL_DIRECTION_OUTGOING) {
+ mm_obj_warn (self, "unexpected outgoing call to number '%s' reported in call list: state %s",
+ call_info->number ? call_info->number : "n/a",
+ mm_call_state_get_string (call_info->state));
+ continue;
+ }
+
+ if (call_info->direction == MM_CALL_DIRECTION_INCOMING) {
+ MMBaseCall *call;
+
+ /* We only expect either RINGING-IN or WAITING states */
+ if ((call_info->state != MM_CALL_STATE_RINGING_IN) &&
+ (call_info->state != MM_CALL_STATE_WAITING)) {
+ mm_obj_warn (self, "unexpected incoming call to number '%s' reported in call list: state %s",
+ call_info->number ? call_info->number : "n/a",
+ mm_call_state_get_string (call_info->state));
+ continue;
+ }
+
+ mm_obj_dbg (self, "creating new incoming call...");
+ call = create_incoming_call (self, call_info->number);
+
+ /* Set the state and the index */
+ mm_base_call_change_state (call, call_info->state, MM_CALL_STATE_REASON_INCOMING_NEW);
+ mm_base_call_set_index (call, call_info->index);
+
+ /* Start its validity timeout */
+ mm_base_call_incoming_refresh (call);
+
+ /* Only export once properly created */
+ mm_base_call_export (call);
+ mm_call_list_add_call (list, call);
+ g_object_unref (call);
+ continue;
+ }
+
+ mm_obj_warn (self, "unexpected call to number '%s' reported in call list: state %s, direction unknown",
+ call_info->number ? call_info->number : "n/a",
+ mm_call_state_get_string (call_info->state));
+ }
+ g_list_free (ctx.call_info_list);
+ g_object_unref (list);
+}
+
+/*****************************************************************************/
+/* Incoming DTMF reception, not associated to a specific call */
+
+typedef struct {
+ guint index;
+ const gchar *dtmf;
+} ReceivedDtmfContext;
+
+static void
+received_dtmf_foreach (MMBaseCall *call,
+ ReceivedDtmfContext *ctx)
+{
+ if ((!ctx->index || (ctx->index == mm_base_call_get_index (call))) &&
+ (mm_base_call_get_state (call) == MM_CALL_STATE_ACTIVE))
+ mm_base_call_received_dtmf (call, ctx->dtmf);
+}
+
+void
+mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self,
+ guint index,
+ const gchar *dtmf)
+{
+ MMCallList *list = NULL;
+ ReceivedDtmfContext ctx = {
+ .index = index,
+ .dtmf = dtmf
+ };
+
+ /* Retrieve list of known calls */
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ mm_obj_warn (self, "cannot report received DTMF: missing call list");
+ return;
+ }
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc)received_dtmf_foreach, &ctx);
+ g_object_unref (list);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ gchar *path;
+} HandleDeleteContext;
+
+static void
+handle_delete_context_free (HandleDeleteContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_free (ctx->path);
+ g_free (ctx);
+}
+
+static void
+handle_delete_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleDeleteContext *ctx)
+{
+ MMCallList *list = NULL;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot delete call: missing call list");
+ handle_delete_context_free (ctx);
+ return;
+ }
+
+ if (!mm_call_list_delete_call (list, ctx->path, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem_voice_complete_delete_call (ctx->skeleton, ctx->invocation);
+
+ handle_delete_context_free (ctx);
+ g_object_unref (list);
+}
+
+static gboolean
+handle_delete (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ const gchar *path,
+ MMIfaceModemVoice *self)
+{
+ HandleDeleteContext *ctx;
+
+ ctx = g_new (HandleDeleteContext, 1);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->path = g_strdup (path);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_delete_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ GVariant *dictionary;
+} HandleCreateContext;
+
+static void
+handle_create_context_free (HandleCreateContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_variant_unref (ctx->dictionary);
+ g_free (ctx);
+}
+
+static void
+handle_create_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleCreateContext *ctx)
+{
+ MMCallList *list = NULL;
+ GError *error = NULL;
+ MMCallProperties *properties;
+ MMBaseCall *call;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_create_context_free (ctx);
+ return;
+ }
+
+ /* Parse input properties */
+ properties = mm_call_properties_new_from_dictionary (ctx->dictionary, &error);
+ if (!properties) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_create_context_free (ctx);
+ return;
+ }
+
+ call = create_outgoing_call_from_properties (MM_IFACE_MODEM_VOICE (self), properties, &error);
+ if (!call) {
+ g_object_unref (properties);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_create_context_free (ctx);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_object_unref (properties);
+ g_object_unref (call);
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot create CALL: missing CALL list");
+ handle_create_context_free (ctx);
+ return;
+ }
+
+ /* Only export once properly created */
+ mm_base_call_export (call);
+ mm_call_list_add_call (list, call);
+
+ /* Complete the DBus call */
+ mm_gdbus_modem_voice_complete_create_call (ctx->skeleton,
+ ctx->invocation,
+ mm_base_call_get_path (call));
+ g_object_unref (call);
+
+ g_object_unref (properties);
+ g_object_unref (list);
+
+ handle_create_context_free (ctx);
+}
+
+static gboolean
+handle_create (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ GVariant *dictionary,
+ MMIfaceModemVoice *self)
+{
+ HandleCreateContext *ctx;
+
+ ctx = g_new (HandleCreateContext, 1);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->dictionary = g_variant_ref (dictionary);
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_create_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static gboolean
+handle_list (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemVoice *self)
+{
+ GStrv paths;
+ MMCallList *list = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_dbus_method_invocation_return_error (invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot list CALL: missing CALL list");
+ return TRUE;
+ }
+
+ paths = mm_call_list_get_paths (list);
+ mm_gdbus_modem_voice_complete_list_calls (skeleton,
+ invocation,
+ (const gchar *const *)paths);
+ g_strfreev (paths);
+ g_object_unref (list);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ GList *active_calls;
+ MMBaseCall *next_call;
+} HandleHoldAndAcceptContext;
+
+static void
+handle_hold_and_accept_context_free (HandleHoldAndAcceptContext *ctx)
+{
+ g_list_free_full (ctx->active_calls, g_object_unref);
+ g_clear_object (&ctx->next_call);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleHoldAndAcceptContext, ctx);
+}
+
+static void
+hold_and_accept_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ HandleHoldAndAcceptContext *ctx)
+{
+ GError *error = NULL;
+ GList *l;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hold_and_accept_context_free (ctx);
+ return;
+ }
+
+ for (l = ctx->active_calls; l; l = g_list_next (l))
+ mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
+ if (ctx->next_call)
+ mm_base_call_change_state (ctx->next_call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED);
+
+ mm_gdbus_modem_voice_complete_hold_and_accept (ctx->skeleton, ctx->invocation);
+ handle_hold_and_accept_context_free (ctx);
+}
+
+static void
+prepare_hold_and_accept_foreach (MMBaseCall *call,
+ HandleHoldAndAcceptContext *ctx)
+{
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_ACTIVE:
+ ctx->active_calls = g_list_append (ctx->active_calls, g_object_ref (call));
+ break;
+ case MM_CALL_STATE_WAITING:
+ g_clear_object (&ctx->next_call);
+ ctx->next_call = g_object_ref (call);
+ break;
+ case MM_CALL_STATE_HELD:
+ if (!ctx->next_call)
+ ctx->next_call = g_object_ref (call);
+ break;
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+static void
+handle_hold_and_accept_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleHoldAndAcceptContext *ctx)
+{
+ GError *error = NULL;
+ MMCallList *list = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hold_and_accept_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot hold and accept: unsupported");
+ handle_hold_and_accept_context_free (ctx);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot hold and accept: missing call list");
+ handle_hold_and_accept_context_free (ctx);
+ return;
+ }
+ mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hold_and_accept_foreach, ctx);
+ g_object_unref (list);
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept (MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)hold_and_accept_ready,
+ ctx);
+}
+
+static gboolean
+handle_hold_and_accept (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemVoice *self)
+{
+ HandleHoldAndAcceptContext *ctx;
+
+ ctx = g_slice_new0 (HandleHoldAndAcceptContext);
+ 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_VOICE,
+ (GAsyncReadyCallback)handle_hold_and_accept_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ GList *active_calls;
+ MMBaseCall *next_call;
+} HandleHangupAndAcceptContext;
+
+static void
+handle_hangup_and_accept_context_free (HandleHangupAndAcceptContext *ctx)
+{
+ g_list_free_full (ctx->active_calls, g_object_unref);
+ g_clear_object (&ctx->next_call);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleHangupAndAcceptContext, ctx);
+}
+
+static void
+hangup_and_accept_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ HandleHangupAndAcceptContext *ctx)
+{
+ GError *error = NULL;
+ GList *l;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hangup_and_accept_context_free (ctx);
+ return;
+ }
+
+ for (l = ctx->active_calls; l; l = g_list_next (l))
+ mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED);
+ if (ctx->next_call)
+ mm_base_call_change_state (ctx->next_call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED);
+
+ mm_gdbus_modem_voice_complete_hangup_and_accept (ctx->skeleton, ctx->invocation);
+ handle_hangup_and_accept_context_free (ctx);
+}
+
+static void
+prepare_hangup_and_accept_foreach (MMBaseCall *call,
+ HandleHangupAndAcceptContext *ctx)
+{
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_ACTIVE:
+ ctx->active_calls = g_list_append (ctx->active_calls, g_object_ref (call));
+ break;
+ case MM_CALL_STATE_WAITING:
+ g_clear_object (&ctx->next_call);
+ ctx->next_call = g_object_ref (call);
+ break;
+ case MM_CALL_STATE_HELD:
+ if (!ctx->next_call)
+ ctx->next_call = g_object_ref (call);
+ break;
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+static void
+handle_hangup_and_accept_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleHangupAndAcceptContext *ctx)
+{
+ GError *error = NULL;
+ MMCallList *list = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hangup_and_accept_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot hangup and accept: unsupported");
+ handle_hangup_and_accept_context_free (ctx);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot hangup and accept: missing call list");
+ handle_hangup_and_accept_context_free (ctx);
+ return;
+ }
+ mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hangup_and_accept_foreach, ctx);
+ g_object_unref (list);
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_and_accept (MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)hangup_and_accept_ready,
+ ctx);
+}
+
+static gboolean
+handle_hangup_and_accept (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemVoice *self)
+{
+ HandleHangupAndAcceptContext *ctx;
+
+ ctx = g_slice_new0 (HandleHangupAndAcceptContext);
+ 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_VOICE,
+ (GAsyncReadyCallback)handle_hangup_and_accept_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ GList *calls;
+} HandleHangupAllContext;
+
+static void
+handle_hangup_all_context_free (HandleHangupAllContext *ctx)
+{
+ g_list_free_full (ctx->calls, g_object_unref);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleHangupAllContext, ctx);
+}
+
+static void
+hangup_all_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ HandleHangupAllContext *ctx)
+{
+ GError *error = NULL;
+ GList *l;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hangup_all_context_free (ctx);
+ return;
+ }
+
+ for (l = ctx->calls; l; l = g_list_next (l))
+ mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TERMINATED);
+
+ mm_gdbus_modem_voice_complete_hangup_all (ctx->skeleton, ctx->invocation);
+ handle_hangup_all_context_free (ctx);
+}
+
+static void
+prepare_hangup_all_foreach (MMBaseCall *call,
+ HandleHangupAllContext *ctx)
+{
+ /* The implementation of this operation will usually be done with +CHUP, and we
+ * know that +CHUP is implemented in different ways by different manufacturers.
+ *
+ * The 3GPP TS27.007 spec for +CHUP states that the "Execution command causes
+ * the TA to hangup the current call of the MT." This sentence leaves a bit of open
+ * interpretation to the implementors, because a current call can be considered only
+ * the active ones, or otherwise any call (active, held or waiting).
+ *
+ * And so, the u-blox TOBY-L4 takes one interpretation and "In case of multiple
+ * calls, all active calls will be released, while waiting and held calls are not".
+ *
+ * And the Cinterion PLS-8 takes a different interpretation and cancels all calls,
+ * including the waiting and held ones.
+ *
+ * In this logic, we're going to terminate exclusively the ACTIVE calls only, and we
+ * will leave the possible termination of waiting/held calls to be reported via
+ * call state updates, e.g. +CLCC polling or other plugin-specific method. In the
+ * case of the Cinterion PLS-8, we'll detect the termination of the waiting and
+ * held calls via ^SLCC URCs.
+ */
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_ACTIVE:
+ ctx->calls = g_list_append (ctx->calls, g_object_ref (call));
+ break;
+ case MM_CALL_STATE_WAITING:
+ case MM_CALL_STATE_HELD:
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+static void
+handle_hangup_all_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleHangupAllContext *ctx)
+{
+ GError *error = NULL;
+ MMCallList *list = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_hangup_all_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot hangup all: unsupported");
+ handle_hangup_all_context_free (ctx);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot hangup all: missing call list");
+ handle_hangup_all_context_free (ctx);
+ return;
+ }
+ mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hangup_all_foreach, ctx);
+ g_object_unref (list);
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all (MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)hangup_all_ready,
+ ctx);
+}
+
+static gboolean
+handle_hangup_all (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemVoice *self)
+{
+ HandleHangupAllContext *ctx;
+
+ ctx = g_slice_new0 (HandleHangupAllContext);
+ 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_VOICE,
+ (GAsyncReadyCallback)handle_hangup_all_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ GList *calls;
+} HandleTransferContext;
+
+static void
+handle_transfer_context_free (HandleTransferContext *ctx)
+{
+ g_list_free_full (ctx->calls, g_object_unref);
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleTransferContext, ctx);
+}
+
+static void
+transfer_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ HandleTransferContext *ctx)
+{
+ GError *error = NULL;
+ GList *l;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_transfer_context_free (ctx);
+ return;
+ }
+
+ for (l = ctx->calls; l; l = g_list_next (l))
+ mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TRANSFERRED);
+
+ mm_gdbus_modem_voice_complete_transfer (ctx->skeleton, ctx->invocation);
+ handle_transfer_context_free (ctx);
+}
+
+static void
+prepare_transfer_foreach (MMBaseCall *call,
+ HandleTransferContext *ctx)
+{
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_ACTIVE:
+ case MM_CALL_STATE_HELD:
+ ctx->calls = g_list_append (ctx->calls, g_object_ref (call));
+ break;
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_WAITING:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+static void
+handle_transfer_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleTransferContext *ctx)
+{
+ GError *error = NULL;
+ MMCallList *list = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_transfer_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot transfer: unsupported");
+ handle_transfer_context_free (ctx);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot transfer: missing call list");
+ handle_transfer_context_free (ctx);
+ return;
+ }
+ mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_transfer_foreach, ctx);
+ g_object_unref (list);
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer (MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)transfer_ready,
+ ctx);
+}
+
+static gboolean
+handle_transfer (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemVoice *self)
+{
+ HandleTransferContext *ctx;
+
+ ctx = g_slice_new0 (HandleTransferContext);
+ 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_VOICE,
+ (GAsyncReadyCallback)handle_transfer_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ gboolean enable;
+} HandleCallWaitingSetupContext;
+
+static void
+handle_call_waiting_setup_context_free (HandleCallWaitingSetupContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleCallWaitingSetupContext, ctx);
+}
+
+static void
+call_waiting_setup_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ HandleCallWaitingSetupContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_call_waiting_setup_context_free (ctx);
+ return;
+ }
+
+ mm_gdbus_modem_voice_complete_call_waiting_setup (ctx->skeleton, ctx->invocation);
+ handle_call_waiting_setup_context_free (ctx);
+}
+
+static void
+handle_call_waiting_setup_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleCallWaitingSetupContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_call_waiting_setup_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot setup call waiting: unsupported");
+ handle_call_waiting_setup_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup (MM_IFACE_MODEM_VOICE (self),
+ ctx->enable,
+ (GAsyncReadyCallback)call_waiting_setup_ready,
+ ctx);
+}
+
+static gboolean
+handle_call_waiting_setup (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ gboolean enable,
+ MMIfaceModemVoice *self)
+{
+ HandleCallWaitingSetupContext *ctx;
+
+ ctx = g_slice_new0 (HandleCallWaitingSetupContext);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->enable = enable;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_call_waiting_setup_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MmGdbusModemVoice *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModemVoice *self;
+ gboolean enable;
+} HandleCallWaitingQueryContext;
+
+static void
+handle_call_waiting_query_context_free (HandleCallWaitingQueryContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleCallWaitingQueryContext, ctx);
+}
+
+static void
+call_waiting_query_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ HandleCallWaitingQueryContext *ctx)
+{
+ GError *error = NULL;
+ gboolean status = FALSE;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query_finish (self, res, &status, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_call_waiting_query_context_free (ctx);
+ return;
+ }
+
+ mm_gdbus_modem_voice_complete_call_waiting_query (ctx->skeleton, ctx->invocation, status);
+ handle_call_waiting_query_context_free (ctx);
+}
+
+static void
+handle_call_waiting_query_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleCallWaitingQueryContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_call_waiting_query_context_free (ctx);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot query call waiting: unsupported");
+ handle_call_waiting_query_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query (MM_IFACE_MODEM_VOICE (self),
+ (GAsyncReadyCallback)call_waiting_query_ready,
+ ctx);
+}
+
+static gboolean
+handle_call_waiting_query (MmGdbusModemVoice *skeleton,
+ GDBusMethodInvocation *invocation,
+ MMIfaceModemVoice *self)
+{
+ HandleCallWaitingQueryContext *ctx;
+
+ ctx = g_slice_new0 (HandleCallWaitingQueryContext);
+ 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_VOICE,
+ (GAsyncReadyCallback)handle_call_waiting_query_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* Leave one of the calls from the multiparty call */
+
+typedef struct {
+ MMBaseCall *call;
+ GList *other_calls;
+} LeaveMultipartyContext;
+
+static void
+leave_multiparty_context_free (LeaveMultipartyContext *ctx)
+{
+ g_list_free_full (ctx->other_calls, g_object_unref);
+ g_object_unref (ctx->call);
+ g_slice_free (LeaveMultipartyContext, ctx);
+}
+
+static void
+prepare_leave_multiparty_foreach (MMBaseCall *call,
+ LeaveMultipartyContext *ctx)
+{
+ /* ignore call that is leaving */
+ if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0))
+ return;
+
+ /* ignore non-multiparty calls */
+ if (!mm_base_call_get_multiparty (call))
+ return;
+
+ /* ignore calls not currently ongoing */
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_ACTIVE:
+ case MM_CALL_STATE_HELD:
+ ctx->other_calls = g_list_append (ctx->other_calls, g_object_ref (call));
+ break;
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_WAITING:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+gboolean
+mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+leave_multiparty_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ LeaveMultipartyContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* If there is only one remaining call that was part of the multiparty, consider that
+ * one also no longer part of any multiparty, and put it on hold right away */
+ if (g_list_length (ctx->other_calls) == 1) {
+ mm_base_call_set_multiparty (MM_BASE_CALL (ctx->other_calls->data), FALSE);
+ mm_base_call_change_state (MM_BASE_CALL (ctx->other_calls->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
+ }
+ /* If there are still more than one calls in the multiparty, just change state of all
+ * of them. */
+ else {
+ GList *l;
+
+ for (l = ctx->other_calls; l; l = g_list_next (l))
+ mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
+ }
+
+ /* The call that left would now be active */
+ mm_base_call_set_multiparty (ctx->call, FALSE);
+ mm_base_call_change_state (ctx->call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ LeaveMultipartyContext *ctx;
+ MMCallList *list = NULL;
+ MMCallState call_state;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* validate multiparty status */
+ if (!mm_base_call_get_multiparty (call)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "this call is not part of a multiparty call");
+ g_object_unref (task);
+ return;
+ }
+ /* validate call state */
+ call_state = mm_base_call_get_state (call);
+ if ((call_state != MM_CALL_STATE_ACTIVE) && (call_state != MM_CALL_STATE_HELD)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "invalid call state (%s): must be either active or held",
+ mm_call_state_get_string (call_state));
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot leave multiparty: unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Cannot leave multiparty: missing call list");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (LeaveMultipartyContext);
+ ctx->call = g_object_ref (call);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) leave_multiparty_context_free);
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_leave_multiparty_foreach, ctx);
+ g_object_unref (list);
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty (self,
+ call,
+ (GAsyncReadyCallback)leave_multiparty_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Join calls into a multiparty call */
+
+typedef struct {
+ MMBaseCall *call;
+ GList *all_calls;
+ gboolean added;
+} JoinMultipartyContext;
+
+static void
+join_multiparty_context_free (JoinMultipartyContext *ctx)
+{
+ g_list_free_full (ctx->all_calls, g_object_unref);
+ g_object_unref (ctx->call);
+ g_slice_free (JoinMultipartyContext, ctx);
+}
+
+static void
+prepare_join_multiparty_foreach (MMBaseCall *call,
+ JoinMultipartyContext *ctx)
+{
+ /* always add call that is being added */
+ if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0))
+ ctx->added = TRUE;
+
+ /* ignore calls not currently ongoing */
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_ACTIVE:
+ case MM_CALL_STATE_HELD:
+ ctx->all_calls = g_list_append (ctx->all_calls, g_object_ref (call));
+ break;
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_WAITING:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+gboolean
+mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+join_multiparty_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ JoinMultipartyContext *ctx;
+ GList *l;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (l = ctx->all_calls; l; l = g_list_next (l)) {
+ mm_base_call_set_multiparty (MM_BASE_CALL (l->data), TRUE);
+ mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ JoinMultipartyContext *ctx;
+ MMCallList *list = NULL;
+ MMCallState call_state;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* validate multiparty status */
+ if (mm_base_call_get_multiparty (call)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "this call is already part of a multiparty call");
+ g_object_unref (task);
+ return;
+ }
+ /* validate call state */
+ call_state = mm_base_call_get_state (call);
+ if (call_state != MM_CALL_STATE_HELD) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "invalid call state (%s): must be held",
+ mm_call_state_get_string (call_state));
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty ||
+ !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot join multiparty: unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Cannot join multiparty: missing call list");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (JoinMultipartyContext);
+ ctx->call = g_object_ref (call);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) join_multiparty_context_free);
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_join_multiparty_foreach, ctx);
+ g_object_unref (list);
+
+ /* our logic makes sure that we would be adding the incoming call into the multiparty call */
+ g_assert (ctx->added);
+
+ /* NOTE: we do not give the call we want to join, because the join operation acts on all
+ * active/held calls. */
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty (self,
+ (GAsyncReadyCallback)join_multiparty_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* In-call setup operation
+ *
+ * It will setup URC handlers for all in-call URCs, and also setup the audio
+ * channel if the plugin requires to do so.
+ */
+
+typedef enum {
+ IN_CALL_SETUP_STEP_FIRST,
+ IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS,
+ IN_CALL_SETUP_STEP_AUDIO_CHANNEL,
+ IN_CALL_SETUP_STEP_LAST,
+} InCallSetupStep;
+
+typedef struct {
+ InCallSetupStep step;
+ MMPort *audio_port;
+ MMCallAudioFormat *audio_format;
+} InCallSetupContext;
+
+static void
+in_call_setup_context_free (InCallSetupContext *ctx)
+{
+ g_clear_object (&ctx->audio_port);
+ g_clear_object (&ctx->audio_format);
+ g_slice_free (InCallSetupContext, ctx);
+}
+
+static gboolean
+in_call_setup_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ MMPort **audio_port, /* optional */
+ MMCallAudioFormat **audio_format, /* optional */
+ GError **error)
+{
+ InCallSetupContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (audio_port) {
+ *audio_port = ctx->audio_port;
+ ctx->audio_port = NULL;
+ }
+ if (audio_format) {
+ *audio_format = ctx->audio_format;
+ ctx->audio_format = NULL;
+ }
+
+ return TRUE;
+}
+
+static void in_call_setup_context_step (GTask *task);
+
+static void
+setup_in_call_audio_channel_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InCallSetupContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel_finish (self,
+ res,
+ &ctx->audio_port,
+ &ctx->audio_format,
+ &error)) {
+ mm_obj_warn (self, "couldn't setup in-call audio channel: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ ctx->step++;
+ in_call_setup_context_step (task);
+}
+
+static void
+setup_in_call_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InCallSetupContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't setup in-call unsolicited events: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ ctx->step++;
+ in_call_setup_context_step (task);
+}
+
+static void
+in_call_setup_context_step (GTask *task)
+{
+ MMIfaceModemVoice *self;
+ InCallSetupContext *ctx;
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case IN_CALL_SETUP_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+ case IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_unsolicited_events (
+ self,
+ (GAsyncReadyCallback) setup_in_call_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+ case IN_CALL_SETUP_STEP_AUDIO_CHANNEL:
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_in_call_audio_channel (
+ self,
+ (GAsyncReadyCallback) setup_in_call_audio_channel_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+ case IN_CALL_SETUP_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+in_call_setup (MMIfaceModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ InCallSetupContext *ctx;
+
+ ctx = g_slice_new0 (InCallSetupContext);
+ ctx->step = IN_CALL_SETUP_STEP_FIRST;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) in_call_setup_context_free);
+
+ in_call_setup_context_step (task);
+}
+
+/*****************************************************************************/
+/* In-call cleanup operation
+ *
+ * It will cleanup audio channel settings and remove all in-call URC handlers.
+ */
+
+typedef enum {
+ IN_CALL_CLEANUP_STEP_FIRST,
+ IN_CALL_CLEANUP_STEP_AUDIO_CHANNEL,
+ IN_CALL_CLEANUP_STEP_UNSOLICITED_EVENTS,
+ IN_CALL_CLEANUP_STEP_LAST,
+} InCallCleanupStep;
+
+typedef struct {
+ InCallCleanupStep step;
+} InCallCleanupContext;
+
+static gboolean
+in_call_cleanup_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void in_call_cleanup_context_step (GTask *task);
+
+static void
+cleanup_in_call_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InCallCleanupContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't cleanup in-call unsolicited events: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ ctx->step++;
+ in_call_cleanup_context_step (task);
+}
+
+static void
+cleanup_in_call_audio_channel_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InCallCleanupContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't cleanup in-call audio channel: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ ctx->step++;
+ in_call_cleanup_context_step (task);
+}
+
+static void
+in_call_cleanup_context_step (GTask *task)
+{
+ MMIfaceModemVoice *self;
+ InCallCleanupContext *ctx;
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case IN_CALL_CLEANUP_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+ case IN_CALL_CLEANUP_STEP_AUDIO_CHANNEL:
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_audio_channel (
+ self,
+ (GAsyncReadyCallback) cleanup_in_call_audio_channel_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+ case IN_CALL_CLEANUP_STEP_UNSOLICITED_EVENTS:
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_in_call_unsolicited_events (
+ self,
+ (GAsyncReadyCallback) cleanup_in_call_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+ case IN_CALL_CLEANUP_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+in_call_cleanup (MMIfaceModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ InCallCleanupContext *ctx;
+
+ ctx = g_new0 (InCallCleanupContext, 1);
+ ctx->step = IN_CALL_CLEANUP_STEP_FIRST;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ in_call_cleanup_context_step (task);
+}
+
+/*****************************************************************************/
+/* In-call event handling logic
+ *
+ * This procedure will run a in-call setup async function whenever we detect
+ * that there is at least one call that is ongoing. This setup function will
+ * try to setup in-call unsolicited events as well as any audio channel
+ * requirements.
+ *
+ * The procedure will run a in-call cleanup async function whenever we detect
+ * that there are no longer any ongoing calls. The cleanup function will
+ * cleanup the audio channel and remove the in-call unsolicited event handlers.
+ */
+
+typedef struct {
+ guint check_id;
+ GCancellable *setup_cancellable;
+ GCancellable *cleanup_cancellable;
+ gboolean in_call_state;
+ MMPort *audio_port;
+ MMCallAudioFormat *audio_format;
+} InCallEventContext;
+
+static void
+in_call_event_context_free (InCallEventContext *ctx)
+{
+ if (ctx->check_id)
+ g_source_remove (ctx->check_id);
+ if (ctx->cleanup_cancellable) {
+ g_cancellable_cancel (ctx->cleanup_cancellable);
+ g_clear_object (&ctx->cleanup_cancellable);
+ }
+ if (ctx->setup_cancellable) {
+ g_cancellable_cancel (ctx->setup_cancellable);
+ g_clear_object (&ctx->setup_cancellable);
+ }
+ g_clear_object (&ctx->audio_port);
+ g_clear_object (&ctx->audio_format);
+ g_slice_free (InCallEventContext, ctx);
+}
+
+static InCallEventContext *
+get_in_call_event_context (MMIfaceModemVoice *self)
+{
+ InCallEventContext *ctx;
+
+ if (G_UNLIKELY (!in_call_event_context_quark))
+ in_call_event_context_quark = g_quark_from_static_string (IN_CALL_EVENT_CONTEXT_TAG);
+
+ ctx = g_object_get_qdata (G_OBJECT (self), in_call_event_context_quark);
+ if (!ctx) {
+ /* Create context and keep it as object data */
+ ctx = g_slice_new0 (InCallEventContext);
+ g_object_set_qdata_full (
+ G_OBJECT (self),
+ in_call_event_context_quark,
+ ctx,
+ (GDestroyNotify)in_call_event_context_free);
+ }
+
+ return ctx;
+}
+
+static void
+call_list_foreach_audio_settings (MMBaseCall *call,
+ InCallEventContext *ctx)
+{
+ if (mm_base_call_get_state (call) != MM_CALL_STATE_TERMINATED)
+ return;
+ mm_base_call_change_audio_settings (call, ctx->audio_port, ctx->audio_format);
+}
+
+static void
+update_audio_settings_in_ongoing_calls (MMIfaceModemVoice *self)
+{
+ MMCallList *list = NULL;
+ InCallEventContext *ctx;
+
+ ctx = get_in_call_event_context (self);
+
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ mm_obj_warn (self, "cannot update audio settings in active calls: missing internal call list");
+ return;
+ }
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_audio_settings, ctx);
+ g_clear_object (&list);
+}
+
+static void
+update_audio_settings_in_call (MMIfaceModemVoice *self,
+ MMBaseCall *call)
+{
+ InCallEventContext *ctx;
+
+ ctx = get_in_call_event_context (self);
+ mm_base_call_change_audio_settings (call, ctx->audio_port, ctx->audio_format);
+}
+
+static void
+call_list_foreach_count_in_call (MMBaseCall *call,
+ gpointer user_data)
+{
+ guint *n_calls_in_call = (guint *)user_data;
+
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_HELD:
+ case MM_CALL_STATE_ACTIVE:
+ *n_calls_in_call = *n_calls_in_call + 1;
+ break;
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_WAITING:
+ /* NOTE: ringing-in and waiting calls are NOT yet in-call, e.g. there must
+ * be no audio settings enabled and we must not enable in-call URC handling
+ * yet. */
+ case MM_CALL_STATE_UNKNOWN:
+ case MM_CALL_STATE_TERMINATED:
+ default:
+ break;
+ }
+}
+
+static void
+in_call_cleanup_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ InCallEventContext *ctx;
+
+ ctx = get_in_call_event_context (self);
+
+ if (!in_call_cleanup_finish (self, res, &error)) {
+ /* ignore cancelled operations */
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ mm_obj_warn (self, "cannot cleanup in-call modem state: %s", error->message);
+ g_clear_error (&error);
+ } else {
+ mm_obj_dbg (self, "modem is no longer in-call state");
+ ctx->in_call_state = FALSE;
+ g_clear_object (&ctx->audio_port);
+ g_clear_object (&ctx->audio_format);
+ }
+
+ g_clear_object (&ctx->cleanup_cancellable);
+}
+
+static void
+in_call_setup_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ InCallEventContext *ctx;
+
+ ctx = get_in_call_event_context (self);
+
+ if (!in_call_setup_finish (self, res, &ctx->audio_port, &ctx->audio_format, &error)) {
+ /* ignore cancelled operations */
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ mm_obj_warn (self, "cannot setup in-call modem state: %s", error->message);
+ g_clear_error (&error);
+ } else {
+ mm_obj_dbg (self, "modem is now in-call state");
+ ctx->in_call_state = TRUE;
+ update_audio_settings_in_ongoing_calls (self);
+ }
+
+ g_clear_object (&ctx->setup_cancellable);
+}
+
+static gboolean
+call_list_check_in_call_events (MMIfaceModemVoice *self)
+{
+ InCallEventContext *ctx;
+ MMCallList *list = NULL;
+ guint n_calls_in_call = 0;
+
+ ctx = get_in_call_event_context (self);
+ ctx->check_id = 0;
+
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+ if (!list) {
+ mm_obj_warn (self, "cannot update in-call state: missing internal call list");
+ goto out;
+ }
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_count_in_call, &n_calls_in_call);
+
+ /* Need to setup in-call events? */
+ if (n_calls_in_call > 0 && !ctx->in_call_state) {
+ /* if setup already ongoing, do nothing */
+ if (ctx->setup_cancellable)
+ goto out;
+
+ /* cancel ongoing cleanup if any */
+ if (ctx->cleanup_cancellable) {
+ g_cancellable_cancel (ctx->cleanup_cancellable);
+ g_clear_object (&ctx->cleanup_cancellable);
+ }
+
+ /* run setup */
+ mm_obj_dbg (self, "setting up in-call state...");
+ ctx->setup_cancellable = g_cancellable_new ();
+ in_call_setup (self, ctx->setup_cancellable, (GAsyncReadyCallback) in_call_setup_ready, NULL);
+ goto out;
+ }
+
+ /* Need to cleanup in-call events? */
+ if (n_calls_in_call == 0 && ctx->in_call_state) {
+ /* if cleanup already ongoing, do nothing */
+ if (ctx->cleanup_cancellable)
+ goto out;
+
+ /* cancel ongoing setup if any */
+ if (ctx->setup_cancellable) {
+ g_cancellable_cancel (ctx->setup_cancellable);
+ g_clear_object (&ctx->setup_cancellable);
+ }
+
+ /* run cleanup */
+ mm_obj_dbg (self, "cleaning up in-call state...");
+ ctx->cleanup_cancellable = g_cancellable_new ();
+ in_call_cleanup (self, ctx->cleanup_cancellable, (GAsyncReadyCallback) in_call_cleanup_ready, NULL);
+ goto out;
+ }
+
+ out:
+ g_clear_object (&list);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+call_state_changed (MMIfaceModemVoice *self)
+{
+ InCallEventContext *ctx;
+
+ ctx = get_in_call_event_context (self);
+ if (ctx->check_id)
+ return;
+
+ /* Process check for in-call events in an idle, so that we can combine
+ * together in the same check multiple call state updates happening
+ * at the same time for different calls (e.g. when swapping active/held
+ * calls). */
+ ctx->check_id = g_idle_add ((GSourceFunc)call_list_check_in_call_events, self);
+}
+
+static void
+setup_in_call_event_handling (MMCallList *call_list,
+ const gchar *call_path_added,
+ MMIfaceModemVoice *self)
+{
+ MMBaseCall *call;
+
+ call = mm_call_list_get_call (call_list, call_path_added);
+ g_assert (call);
+
+ g_signal_connect_swapped (call,
+ "state-changed",
+ G_CALLBACK (call_state_changed),
+ self);
+}
+
+/*****************************************************************************/
+/* Call list polling logic
+ *
+ * The call list polling is exclusively used to detect detailed call state
+ * updates while a call is being established. Therefore, if there is no call
+ * being established (i.e. all terminated, unknown or active), then there is
+ * no polling to do.
+ *
+ * Any time we add a new call to the list, we'll setup polling if it's not
+ * already running, and the polling logic itself will decide when the polling
+ * should stop.
+ */
+
+#define CALL_LIST_POLLING_TIMEOUT_SECS 2
+
+typedef struct {
+ guint polling_id;
+ gboolean polling_ongoing;
+} CallListPollingContext;
+
+static void
+call_list_polling_context_free (CallListPollingContext *ctx)
+{
+ if (ctx->polling_id)
+ g_source_remove (ctx->polling_id);
+ g_slice_free (CallListPollingContext, ctx);
+}
+
+static CallListPollingContext *
+get_call_list_polling_context (MMIfaceModemVoice *self)
+{
+ CallListPollingContext *ctx;
+
+ if (G_UNLIKELY (!call_list_polling_context_quark))
+ call_list_polling_context_quark = (g_quark_from_static_string (
+ CALL_LIST_POLLING_CONTEXT_TAG));
+
+ ctx = g_object_get_qdata (G_OBJECT (self), call_list_polling_context_quark);
+ if (!ctx) {
+ /* Create context and keep it as object data */
+ ctx = g_slice_new0 (CallListPollingContext);
+
+ g_object_set_qdata_full (
+ G_OBJECT (self),
+ call_list_polling_context_quark,
+ ctx,
+ (GDestroyNotify)call_list_polling_context_free);
+ }
+
+ return ctx;
+}
+
+static gboolean call_list_poll (MMIfaceModemVoice *self);
+
+static void
+load_call_list_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res)
+{
+ CallListPollingContext *ctx;
+ GList *call_info_list = NULL;
+ GError *error = NULL;
+
+ ctx = get_call_list_polling_context (self);
+ ctx->polling_ongoing = FALSE;
+
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish);
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish (self, res, &call_info_list, &error)) {
+ mm_obj_warn (self, "couldn't load call list: %s", error->message);
+ g_error_free (error);
+ } else {
+ /* Always report the list even if NULL (it would mean no ongoing calls) */
+ mm_iface_modem_voice_report_all_calls (self, call_info_list);
+ mm_3gpp_call_info_list_free (call_info_list);
+ }
+
+ /* setup the polling again, but only if it hasn't been done already while
+ * we reported calls (e.g. a new incoming call may have been detected that
+ * also triggers the poll setup) */
+ if (!ctx->polling_id)
+ ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS,
+ (GSourceFunc) call_list_poll,
+ self);
+}
+
+static void
+call_list_foreach_count_establishing (MMBaseCall *call,
+ gpointer user_data)
+{
+ guint *n_calls_establishing = (guint *)user_data;
+
+ switch (mm_base_call_get_state (call)) {
+ case MM_CALL_STATE_DIALING:
+ case MM_CALL_STATE_RINGING_OUT:
+ case MM_CALL_STATE_RINGING_IN:
+ case MM_CALL_STATE_HELD:
+ case MM_CALL_STATE_WAITING:
+ *n_calls_establishing = *n_calls_establishing + 1;
+ break;
+ case MM_CALL_STATE_ACTIVE:
+ case MM_CALL_STATE_TERMINATED:
+ case MM_CALL_STATE_UNKNOWN:
+ default:
+ break;
+ }
+}
+
+static gboolean
+call_list_poll (MMIfaceModemVoice *self)
+{
+ CallListPollingContext *ctx;
+ MMCallList *list = NULL;
+ guint n_calls_establishing = 0;
+
+ ctx = get_call_list_polling_context (self);
+ ctx->polling_id = 0;
+
+ g_object_get (MM_BASE_MODEM (self),
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+
+ if (!list) {
+ mm_obj_warn (self, "Cannot poll call list: missing internal call list");
+ goto out;
+ }
+
+ mm_call_list_foreach (list, (MMCallListForeachFunc) call_list_foreach_count_establishing, &n_calls_establishing);
+
+ /* If there is at least ONE call being established, we need the call list */
+ if (n_calls_establishing > 0) {
+ mm_obj_dbg (self, "%u calls being established: call list polling required", n_calls_establishing);
+ ctx->polling_ongoing = TRUE;
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list);
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list (self,
+ (GAsyncReadyCallback)load_call_list_ready,
+ NULL);
+ } else
+ mm_obj_dbg (self, "no calls being established: call list polling stopped");
+
+out:
+ g_clear_object (&list);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+setup_call_list_polling (MMCallList *call_list,
+ const gchar *call_path_added,
+ MMIfaceModemVoice *self)
+{
+ CallListPollingContext *ctx;
+
+ ctx = get_call_list_polling_context (self);
+
+ if (!ctx->polling_id && !ctx->polling_ongoing)
+ ctx->polling_id = g_timeout_add_seconds (CALL_LIST_POLLING_TIMEOUT_SECS,
+ (GSourceFunc) call_list_poll,
+ self);
+}
+
+/*****************************************************************************/
+/* Call list reload */
+
+gboolean
+mm_iface_modem_voice_reload_all_calls_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+reload_all_calls_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GList *call_info_list = NULL;
+ GError *error = NULL;
+
+ g_assert (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish);
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish (self, res, &call_info_list, &error)) {
+ mm_obj_warn (self, "couldn't reload call list: %s", error->message);
+
+ g_task_return_error (task, error);
+ } else {
+ /* Always report the list even if NULL (it would mean no ongoing calls) */
+ mm_iface_modem_voice_report_all_calls (self, call_info_list);
+ mm_3gpp_call_info_list_free (call_info_list);
+
+ g_task_return_boolean (task, TRUE);
+ }
+
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_voice_reload_all_calls (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list (self,
+ (GAsyncReadyCallback)reload_all_calls_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+static void
+update_call_list (MmGdbusModemVoice *skeleton,
+ MMCallList *list)
+{
+ gchar **paths;
+
+ paths = mm_call_list_get_paths (list);
+ mm_gdbus_modem_voice_set_calls (skeleton, (const gchar *const *)paths);
+ g_strfreev (paths);
+
+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+}
+
+static void
+call_added (MMCallList *list,
+ const gchar *call_path,
+ MmGdbusModemVoice *skeleton)
+{
+ update_call_list (skeleton, list);
+ mm_gdbus_modem_voice_emit_call_added (skeleton, call_path);
+}
+
+static void
+call_deleted (MMCallList *list,
+ const gchar *call_path,
+ MmGdbusModemVoice *skeleton)
+{
+ update_call_list (skeleton, list);
+ mm_gdbus_modem_voice_emit_call_deleted (skeleton, call_path);
+}
+
+/*****************************************************************************/
+
+typedef struct _DisablingContext DisablingContext;
+static void interface_disabling_step (GTask *task);
+
+typedef enum {
+ DISABLING_STEP_FIRST,
+ DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS,
+ DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS,
+ DISABLING_STEP_LAST
+} DisablingStep;
+
+struct _DisablingContext {
+ DisablingStep step;
+ MmGdbusModemVoice *skeleton;
+};
+
+static void
+disabling_context_free (DisablingContext *ctx)
+{
+ if (ctx->skeleton)
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+gboolean
+mm_iface_modem_voice_disable_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+disable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisablingContext *ctx;
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_disabling_step (task);
+}
+
+static void
+cleanup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ DisablingContext *ctx;
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_disabling_step (task);
+}
+
+static void
+interface_disabling_step (GTask *task)
+{
+ MMIfaceModemVoice *self;
+ DisablingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case DISABLING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS:
+ /* Allow cleaning up unsolicited events */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->disable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)disable_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS:
+ /* Allow cleaning up unsolicited events */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)cleanup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case DISABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_voice_disable (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DisablingContext *ctx;
+ GTask *task;
+
+ ctx = g_new0 (DisablingContext, 1);
+ ctx->step = DISABLING_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ interface_disabling_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct _EnablingContext EnablingContext;
+static void interface_enabling_step (GTask *task);
+
+typedef enum {
+ ENABLING_STEP_FIRST,
+ ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
+ ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
+ ENABLING_STEP_LAST
+} EnablingStep;
+
+struct _EnablingContext {
+ EnablingStep step;
+ MmGdbusModemVoice *skeleton;
+ guint mem1_storage_index;
+};
+
+static void
+enabling_context_free (EnablingContext *ctx)
+{
+ if (ctx->skeleton)
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+gboolean
+mm_iface_modem_voice_enable_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+setup_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_enabling_step (task);
+}
+
+static void
+enable_unsolicited_events_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ EnablingContext *ctx;
+ GError *error = NULL;
+
+ /* Not critical! */
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't enable unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Go on with next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_enabling_step (task);
+}
+
+static void
+interface_enabling_step (GTask *task)
+{
+ MMIfaceModemVoice *self;
+ EnablingContext *ctx;
+
+ /* Don't run new steps if we're cancelled */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case ENABLING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
+ /* Allow setting up unsolicited events to get notified of incoming calls */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)setup_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
+ /* Allow setting up unsolicited events to get notified of incoming calls */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)enable_unsolicited_events_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case ENABLING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_voice_enable (MMIfaceModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ EnablingContext *ctx;
+ GTask *task;
+
+ ctx = g_new0 (EnablingContext, 1);
+ ctx->step = ENABLING_STEP_FIRST;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ interface_enabling_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct _InitializationContext InitializationContext;
+static void interface_initialization_step (GTask *task);
+
+typedef enum {
+ INITIALIZATION_STEP_FIRST,
+ INITIALIZATION_STEP_CHECK_SUPPORT,
+ INITIALIZATION_STEP_SETUP_CALL_LIST,
+ INITIALIZATION_STEP_LAST
+} InitializationStep;
+
+struct _InitializationContext {
+ MmGdbusModemVoice *skeleton;
+ InitializationStep step;
+};
+
+static void
+initialization_context_free (InitializationContext *ctx)
+{
+ g_object_unref (ctx->skeleton);
+ g_free (ctx);
+}
+
+static void
+check_support_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support_finish (self, res, &error)) {
+ if (error) {
+ mm_obj_dbg (self, "voice support check failed: %s", error->message);
+ g_error_free (error);
+ }
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Voice not supported");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx = g_task_get_task_data (task);
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+interface_initialization_step (GTask *task)
+{
+ MMIfaceModemVoice *self;
+ InitializationContext *ctx;
+
+ /* Don't run new steps if we're cancelled */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case INITIALIZATION_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case INITIALIZATION_STEP_CHECK_SUPPORT:
+ /* Always check voice support when we run initialization, because
+ * the support may be different before and after SIM-PIN unlock. */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support_finish) {
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->check_support (
+ self,
+ (GAsyncReadyCallback)check_support_ready,
+ task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Voice not supported");
+ g_object_unref (task);
+ return;
+
+ case INITIALIZATION_STEP_SETUP_CALL_LIST: {
+ MMCallList *list = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
+ NULL);
+
+ /* Create a new call list if not already available (this initialization
+ * may be called multiple times) */
+ if (!list) {
+ list = mm_call_list_new (MM_BASE_MODEM (self));
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_CALL_LIST, list,
+ NULL);
+
+ /* Connect to list's signals */
+ g_signal_connect (list,
+ MM_CALL_ADDED,
+ G_CALLBACK (call_added),
+ ctx->skeleton);
+ g_signal_connect (list,
+ MM_CALL_DELETED,
+ G_CALLBACK (call_deleted),
+ ctx->skeleton);
+
+ /* Setup monitoring for in-call event handling */
+ g_signal_connect (list,
+ MM_CALL_ADDED,
+ G_CALLBACK (setup_in_call_event_handling),
+ self);
+ }
+
+ /* Unless we're told not to, setup call list polling logic */
+ if (MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list &&
+ MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->load_call_list_finish) {
+ gboolean periodic_call_list_check_disabled = FALSE;
+
+ /* Cleanup any previously configured handler, before checking if we need to
+ * add a new one, because the PERIODIC_CALL_LIST_CHECK_DISABLED flag may
+ * change before and after SIM-PIN unlock */
+ g_signal_handlers_disconnect_by_func (list, G_CALLBACK (setup_call_list_polling), self);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED, &periodic_call_list_check_disabled,
+ NULL);
+ if (!periodic_call_list_check_disabled) {
+ mm_obj_dbg (self, "periodic call list polling will be used if supported");
+ g_signal_connect (list,
+ MM_CALL_ADDED,
+ G_CALLBACK (setup_call_list_polling),
+ self);
+ }
+ }
+ g_object_unref (list);
+
+ ctx->step++;
+ } /* fall through */
+
+ case INITIALIZATION_STEP_LAST:
+ /* Setup all method handlers */
+ g_object_connect (ctx->skeleton,
+ "signal::handle-create-call", G_CALLBACK (handle_create), self,
+ "signal::handle-delete-call", G_CALLBACK (handle_delete), self,
+ "signal::handle-list-calls", G_CALLBACK (handle_list), self,
+ "signal::handle-hangup-and-accept", G_CALLBACK (handle_hangup_and_accept), self,
+ "signal::handle-hold-and-accept", G_CALLBACK (handle_hold_and_accept), self,
+ "signal::handle-hangup-all", G_CALLBACK (handle_hangup_all), self,
+ "signal::handle-transfer", G_CALLBACK (handle_transfer), self,
+ "signal::handle-call-waiting-setup", G_CALLBACK (handle_call_waiting_setup), self,
+ "signal::handle-call-waiting-query", G_CALLBACK (handle_call_waiting_query), self,
+ NULL);
+
+ /* Finally, export the new interface */
+ mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self),
+ MM_GDBUS_MODEM_VOICE (ctx->skeleton));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+gboolean
+mm_iface_modem_voice_initialize_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+modem_state_to_emergency_only (GBinding *binding,
+ const GValue *from_value,
+ GValue *to_value)
+{
+ MMModemState state;
+
+ /* If the modem is REGISTERED, we allow any kind of call, otherwise
+ * only emergency calls */
+ state = g_value_get_enum (from_value);
+ g_value_set_boolean (to_value, (state < MM_MODEM_STATE_REGISTERED));
+ return TRUE;
+}
+
+void
+mm_iface_modem_voice_initialize (MMIfaceModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializationContext *ctx;
+ MmGdbusModemVoice *skeleton = NULL;
+ GTask *task;
+
+ /* Did we already create it? */
+ g_object_get (self,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton) {
+ skeleton = mm_gdbus_modem_voice_skeleton_new ();
+
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON, skeleton,
+ NULL);
+
+ g_object_bind_property_full (self, MM_IFACE_MODEM_STATE,
+ skeleton, "emergency-only",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ (GBindingTransformFunc) modem_state_to_emergency_only,
+ NULL, NULL, NULL);
+ }
+
+ /* Perform async initialization here */
+
+ ctx = g_new0 (InitializationContext, 1);
+ ctx->step = INITIALIZATION_STEP_FIRST;
+ ctx->skeleton = skeleton;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
+}
+
+void
+mm_iface_modem_voice_shutdown (MMIfaceModemVoice *self)
+{
+ /* Unexport DBus interface and remove the skeleton */
+ mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self), NULL);
+ g_object_set (self,
+ MM_IFACE_MODEM_VOICE_DBUS_SKELETON, NULL,
+ NULL);
+}
+
+/*****************************************************************************/
+
+static void
+iface_modem_voice_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_VOICE_DBUS_SKELETON,
+ "Voice DBus skeleton",
+ "DBus skeleton for the Voice interface",
+ MM_GDBUS_TYPE_MODEM_VOICE_SKELETON,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_object (MM_IFACE_MODEM_VOICE_CALL_LIST,
+ "CALL list",
+ "List of CALL objects managed in the interface",
+ MM_TYPE_CALL_LIST,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED,
+ "Periodic call list checks disabled",
+ "Whether periodic call list check are disabled.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED,
+ "Reload call list on call update",
+ "Ignore call updates and forcefully reload all calls.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ initialized = TRUE;
+}
+
+GType
+mm_iface_modem_voice_get_type (void)
+{
+ static GType iface_modem_voice_type = 0;
+
+ if (!G_UNLIKELY (iface_modem_voice_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMIfaceModemVoice), /* class_size */
+ iface_modem_voice_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ iface_modem_voice_type = g_type_register_static (G_TYPE_INTERFACE,
+ "MMIfaceModemVoice",
+ &info,
+ 0);
+
+ g_type_interface_add_prerequisite (iface_modem_voice_type, MM_TYPE_IFACE_MODEM);
+ }
+
+ return iface_modem_voice_type;
+}
diff --git a/src/mm-iface-modem-voice.h b/src/mm-iface-modem-voice.h
new file mode 100644
index 00000000..f3164d64
--- /dev/null
+++ b/src/mm-iface-modem-voice.h
@@ -0,0 +1,282 @@
+/* -*- 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) 2015 Marco Bascetta <marco.bascetta@sadel.it>
+ * Copyright (C) 2019 Purism SPC
+ */
+
+#ifndef MM_IFACE_MODEM_VOICE_H
+#define MM_IFACE_MODEM_VOICE_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include "mm-base-call.h"
+
+#define MM_TYPE_IFACE_MODEM_VOICE (mm_iface_modem_voice_get_type ())
+#define MM_IFACE_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM_VOICE, MMIfaceModemVoice))
+#define MM_IS_IFACE_MODEM_VOICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM_VOICE))
+#define MM_IFACE_MODEM_VOICE_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM_VOICE, MMIfaceModemVoice))
+
+#define MM_IFACE_MODEM_VOICE_DBUS_SKELETON "iface-modem-voice-dbus-skeleton"
+#define MM_IFACE_MODEM_VOICE_CALL_LIST "iface-modem-voice-call-list"
+#define MM_IFACE_MODEM_VOICE_PERIODIC_CALL_LIST_CHECK_DISABLED "iface-modem-voice-periodic-call-list-check-disabled"
+#define MM_IFACE_MODEM_VOICE_INDICATION_CALL_LIST_RELOAD_ENABLED "iface-modem-voice-indication-call-list-reload-enabled"
+
+typedef struct _MMIfaceModemVoice MMIfaceModemVoice;
+
+struct _MMIfaceModemVoice {
+ GTypeInterface g_iface;
+
+ /* Check for Voice support (async) */
+ void (* check_support) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*check_support_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setting up unsolicited CALL reception events */
+ void (*setup_unsolicited_events) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*setup_unsolicited_events_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous cleaning up of unsolicited CALL reception events */
+ void (*cleanup_unsolicited_events) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*cleanup_unsolicited_events_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous enabling unsolicited CALL reception events */
+ void (* enable_unsolicited_events) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* enable_unsolicited_events_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous disabling unsolicited CALL reception events */
+ void (* disable_unsolicited_events) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* disable_unsolicited_events_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setup of in-call unsolicited events */
+ void (* setup_in_call_unsolicited_events) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_in_call_unsolicited_events_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous cleanup of in-call unsolicited events */
+ void (* cleanup_in_call_unsolicited_events) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cleanup_in_call_unsolicited_events_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Asynchronous setup of in-call audio channel */
+ void (* setup_in_call_audio_channel) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_in_call_audio_channel_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ MMPort **audio_port, /* optional */
+ MMCallAudioFormat **audio_format, /* optional */
+ GError **error);
+
+ /* Asynchronous cleanup of in-call audio channel */
+ void (* cleanup_in_call_audio_channel) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* cleanup_in_call_audio_channel_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Load full list of calls (MMCallInfo list) */
+ void (* load_call_list) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* load_call_list_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GList **call_info_list,
+ GError **error);
+
+ /* Create call objects */
+ MMBaseCall * (* create_call) (MMIfaceModemVoice *self,
+ MMCallDirection direction,
+ const gchar *number);
+
+ /* Hold and accept */
+ void (* hold_and_accept) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* hold_and_accept_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Hangup and accept */
+ void (* hangup_and_accept) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* hangup_and_accept_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Hangup all */
+ void (* hangup_all) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* hangup_all_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Join multiparty */
+ void (* join_multiparty) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* join_multiparty_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Leave multiparty */
+ void (* leave_multiparty) (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* leave_multiparty_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Transfer */
+ void (* transfer) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* transfer_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Call waiting setup */
+ void (* call_waiting_setup) (MMIfaceModemVoice *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* call_waiting_setup_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Call waiting query */
+ void (* call_waiting_query) (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* call_waiting_query_finish) (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ gboolean *status,
+ GError **error);
+};
+
+GType mm_iface_modem_voice_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModemVoice, g_object_unref)
+
+/* Initialize Voice interface (async) */
+void mm_iface_modem_voice_initialize (MMIfaceModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_voice_initialize_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Enable Voice interface (async) */
+void mm_iface_modem_voice_enable (MMIfaceModemVoice *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_voice_enable_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Disable Voice interface (async) */
+void mm_iface_modem_voice_disable (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_voice_disable_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shutdown Voice interface */
+void mm_iface_modem_voice_shutdown (MMIfaceModemVoice *self);
+
+/* Bind properties for simple GetStatus() */
+void mm_iface_modem_voice_bind_simple_status (MMIfaceModemVoice *self,
+ MMSimpleStatus *status);
+
+/* Single call info reporting */
+void mm_iface_modem_voice_report_call (MMIfaceModemVoice *self,
+ const MMCallInfo *call_info);
+
+/* Full current call list reporting (MMCallInfo list) */
+void mm_iface_modem_voice_report_all_calls (MMIfaceModemVoice *self,
+ GList *call_info_list);
+
+/* Full reload of call list (async) */
+void mm_iface_modem_voice_reload_all_calls (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_voice_reload_all_calls_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Report an incoming DTMF received */
+void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self,
+ guint index,
+ const gchar *dtmf);
+
+/* Authorize outgoing call based on modem status and ECC list */
+gboolean mm_iface_modem_voice_authorize_outgoing_call (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GError **error);
+
+/* Join/Leave multiparty calls
+ *
+ * These actions are provided in the Call API, but implemented in the
+ * modem Voice interface because they really affect multiple calls at
+ * the same time.
+ */
+void mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self,
+ MMBaseCall *call,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_IFACE_MODEM_VOICE_H */
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 7c31f9a9..3806661c 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -13,39 +13,195 @@
* Copyright (C) 2011 Google, Inc.
*/
-
#include <ModemManager.h>
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-modem-helpers.h"
#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-cdma.h"
#include "mm-base-modem.h"
#include "mm-base-modem-at.h"
#include "mm-base-sim.h"
#include "mm-bearer-list.h"
-#include "mm-log.h"
+#include "mm-private-boxed-types.h"
+#include "mm-log-object.h"
#include "mm-context.h"
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+#if defined WITH_MBIM
+# include "mm-broadband-modem-mbim.h"
+#endif
+
+#define SIGNAL_QUALITY_RECENT_TIMEOUT_SEC 60
-#define SIGNAL_QUALITY_RECENT_TIMEOUT_SEC 60
-#define SIGNAL_QUALITY_INITIAL_CHECK_TIMEOUT_SEC 3
-#define SIGNAL_QUALITY_CHECK_TIMEOUT_SEC 30
-#define ACCESS_TECHNOLOGIES_CHECK_TIMEOUT_SEC 30
+#define SIGNAL_CHECK_INITIAL_RETRIES 5
+#define SIGNAL_CHECK_INITIAL_TIMEOUT_SEC 3
+#define SIGNAL_CHECK_TIMEOUT_SEC 30
-#define STATE_UPDATE_CONTEXT_TAG "state-update-context-tag"
-#define SIGNAL_QUALITY_UPDATE_CONTEXT_TAG "signal-quality-update-context-tag"
-#define SIGNAL_QUALITY_CHECK_CONTEXT_TAG "signal-quality-check-context-tag"
-#define ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG "access-technologies-check-context-tag"
-#define RESTART_INITIALIZE_IDLE_TAG "restart-initialize-tag"
+#define STATE_UPDATE_CONTEXT_TAG "state-update-context-tag"
+#define SIGNAL_QUALITY_UPDATE_CONTEXT_TAG "signal-quality-update-context-tag"
+#define SIGNAL_CHECK_CONTEXT_TAG "signal-check-context-tag"
+#define RESTART_INITIALIZE_IDLE_TAG "restart-initialize-tag"
static GQuark state_update_context_quark;
static GQuark signal_quality_update_context_quark;
-static GQuark signal_quality_check_context_quark;
-static GQuark access_technologies_check_context_quark;
+static GQuark signal_check_context_quark;
static GQuark restart_initialize_idle_quark;
/*****************************************************************************/
+gboolean
+mm_iface_modem_check_for_sim_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+explicit_check_for_sim_swap_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) {
+ mm_obj_warn (self, "SIM swap check failed: %s", error->message);
+ g_task_return_error (task, error);
+ } else {
+ mm_obj_dbg (self, "SIM swap check completed");
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+void
+mm_iface_modem_check_for_sim_swap (MMIfaceModem *self,
+ guint slot_index,
+ const gchar *iccid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Slot index 0 is used when slot is not known, assumingly on a modem with just one SIM slot */
+ if (slot_index != 0) {
+ MmGdbusModem *skeleton;
+ guint primary_slot;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!skeleton) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ primary_slot = mm_gdbus_modem_get_primary_sim_slot (MM_GDBUS_MODEM (skeleton));
+ g_object_unref (skeleton);
+
+ /* Check that it's really the primary slot whose iccid has changed */
+ if (primary_slot && primary_slot != slot_index) {
+ mm_obj_dbg (self, "checking for SIM swap ignored: status changed in slot %u, but primary is %u", slot_index, primary_slot);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) {
+ mm_obj_dbg (self, "start checking for SIM swap in slot %u", slot_index);
+ MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap (
+ self,
+ iccid,
+ (GAsyncReadyCallback)explicit_check_for_sim_swap_ready,
+ task);
+ return;
+ }
+
+ g_task_return_boolean (task, FALSE);
+ g_object_unref (task);
+}
+
+static void
+sim_slot_free (MMBaseSim *sim)
+{
+ if (sim)
+ g_object_unref (sim);
+}
+
+void
+mm_iface_modem_modify_sim (MMIfaceModem *self,
+ guint slot_index,
+ MMBaseSim *new_sim)
+{
+ g_autoptr(MmGdbusModemSkeleton) skeleton = NULL;
+ g_autoptr(GPtrArray) sim_slots_old = NULL;
+ g_autoptr(GPtrArray) sim_slots_new = NULL;
+ guint i;
+ GPtrArray *sim_slot_paths_array;
+ g_auto(GStrv) sim_slot_paths = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM_SLOTS, &sim_slots_old,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (!sim_slots_old) {
+ mm_obj_warn (self, "Failed to process SIM hot swap: couldn't load current list of SIM slots");
+ return;
+ }
+
+ if (!skeleton) {
+ mm_obj_warn (self, "Failed to process SIM hot swap: interface skeleton not available");
+ return;
+ }
+
+ sim_slot_paths_array = g_ptr_array_new ();
+ sim_slots_new = g_ptr_array_new_with_free_func ((GDestroyNotify) sim_slot_free);
+ for (i = 0; i < sim_slots_old->len; i++) {
+ MMBaseSim *sim;
+ const gchar *sim_path = NULL;
+
+ if (i == slot_index)
+ sim = new_sim;
+ else
+ sim = MM_BASE_SIM (g_ptr_array_index (sim_slots_old, i));
+
+ if (sim) {
+ g_ptr_array_add (sim_slots_new, g_object_ref (sim));
+ sim_path = mm_base_sim_get_path (sim);
+ } else
+ g_ptr_array_add (sim_slots_new, NULL);
+
+ if (sim_path)
+ g_ptr_array_add (sim_slot_paths_array, g_strdup (sim_path));
+ else
+ g_ptr_array_add (sim_slot_paths_array, g_strdup ("/"));
+ }
+ g_ptr_array_add (sim_slot_paths_array, NULL);
+ sim_slot_paths = (GStrv) g_ptr_array_free (sim_slot_paths_array, FALSE);
+
+ g_object_set (self,
+ MM_IFACE_MODEM_SIM_SLOTS,
+ sim_slots_new,
+ NULL);
+ mm_gdbus_modem_set_sim_slots (MM_GDBUS_MODEM (skeleton), (const gchar *const *) sim_slot_paths);
+}
+
+/*****************************************************************************/
+
void
mm_iface_modem_bind_simple_status (MMIfaceModem *self,
MMSimpleStatus *status)
@@ -88,27 +244,45 @@ mm_iface_modem_bind_simple_status (MMIfaceModem *self,
state == MM_MODEM_STATE_CONNECTING)
typedef struct {
- MMIfaceModem *self;
MMModemState final_state;
- GSimpleAsyncResult *result;
gulong state_changed_id;
guint state_changed_wait_id;
} WaitForFinalStateContext;
static void
-wait_for_final_state_context_complete_and_free (WaitForFinalStateContext *ctx)
+wait_for_final_state_context_complete (GTask *task,
+ MMModemState state,
+ GError *error)
{
- /* The callback associated with 'ctx->result' may update the modem state.
+ MMIfaceModem *self;
+ WaitForFinalStateContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* The callback associated with 'task' 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);
+ * 'task' in order to prevent state_changed from being invoked, which
+ * invokes wait_for_final_state_context_complete again. */
+ if (ctx->state_changed_id) {
+ /* may be automatically disconnected during dispose */
+ if (g_signal_handler_is_connected (self, ctx->state_changed_id))
+ g_signal_handler_disconnect (self, ctx->state_changed_id);
+ ctx->state_changed_id = 0;
+ }
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_source_remove (ctx->state_changed_wait_id);
- g_object_unref (ctx->self);
- g_slice_free (WaitForFinalStateContext, ctx);
+ /* Remove any outstanding timeout on waiting for state change. */
+ if (ctx->state_changed_wait_id) {
+ g_source_remove (ctx->state_changed_wait_id);
+ ctx->state_changed_wait_id = 0;
+ }
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, state);
+
+ g_object_unref (task);
}
MMModemState
@@ -116,29 +290,35 @@ mm_iface_modem_wait_for_final_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return MM_MODEM_STATE_UNKNOWN;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemState)GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_STATE_UNKNOWN;
+ }
+ return (MMModemState)value;
}
static gboolean
-state_changed_wait_expired (WaitForFinalStateContext *ctx)
+state_changed_wait_expired (GTask *task)
{
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_RETRY,
- "Too much time waiting to get to a final state");
- wait_for_final_state_context_complete_and_free (ctx);
- return FALSE;
+ GError *error;
+
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Too much time waiting to get to a final state");
+ wait_for_final_state_context_complete (task, MM_MODEM_STATE_UNKNOWN, error);
+ return G_SOURCE_REMOVE;
}
static void
state_changed (MMIfaceModem *self,
GParamSpec *spec,
- WaitForFinalStateContext *ctx)
+ GTask *task)
{
+ WaitForFinalStateContext *ctx;
MMModemState state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
@@ -149,6 +329,8 @@ state_changed (MMIfaceModem *self,
if (MODEM_STATE_IS_INTERMEDIATE (state))
return;
+ ctx = g_task_get_task_data (task);
+
/* If we want a specific final state and this is not the one we were
* looking for, then skip */
if (ctx->final_state != MM_MODEM_STATE_UNKNOWN &&
@@ -157,8 +339,7 @@ state_changed (MMIfaceModem *self,
return;
/* Done! */
- g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (state), NULL);
- wait_for_final_state_context_complete_and_free (ctx);
+ wait_for_final_state_context_complete (task, state, NULL);
}
void
@@ -169,12 +350,9 @@ mm_iface_modem_wait_for_final_state (MMIfaceModem *self,
{
MMModemState state = MM_MODEM_STATE_UNKNOWN;
WaitForFinalStateContext *ctx;
- GSimpleAsyncResult *result;
+ GTask *task;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_wait_for_final_state);
+ task = g_task_new (self, NULL, callback, user_data);
g_object_get (self,
MM_IFACE_MODEM_STATE, &state,
@@ -185,91 +363,124 @@ mm_iface_modem_wait_for_final_state (MMIfaceModem *self,
/* Is this the state we actually wanted? */
if (final_state == MM_MODEM_STATE_UNKNOWN ||
(state != MM_MODEM_STATE_UNKNOWN && state == final_state)) {
- g_simple_async_result_set_op_res_gpointer (result, GUINT_TO_POINTER (state), NULL);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_int (task, state);
+ g_object_unref (task);
return;
}
/* Otherwise, we'll need to wait for the exact one we want */
}
- ctx = g_slice_new0 (WaitForFinalStateContext);
- ctx->result = result;
- ctx->self = g_object_ref (self);
+ ctx = g_new0 (WaitForFinalStateContext, 1);
ctx->final_state = final_state;
+ g_task_set_task_data (task, ctx, g_free);
+
/* Want to get notified when modem state changes */
- ctx->state_changed_id = g_signal_connect (ctx->self,
+ ctx->state_changed_id = g_signal_connect (self,
"notify::" MM_IFACE_MODEM_STATE,
G_CALLBACK (state_changed),
- ctx);
+ task);
/* But we don't want to wait forever */
ctx->state_changed_wait_id = g_timeout_add_seconds (10,
(GSourceFunc)state_changed_wait_expired,
- ctx);
+ task);
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_iface_modem_abort_invocation_if_state_not_reached (MMIfaceModem *self,
+ GDBusMethodInvocation *invocation,
+ MMModemState minimum_required)
+{
+ MMModemState state = MM_MODEM_STATE_UNKNOWN;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_STATE, &state,
+ NULL);
+
+ if (state >= minimum_required)
+ return FALSE;
+
+ g_dbus_method_invocation_return_error (invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "modem in %s state",
+ mm_modem_state_get_string (state));
+ return TRUE;
}
/*****************************************************************************/
/* Helper method to load unlock required, considering retries */
-#define MAX_RETRIES 6
+/* If a SIM is known to exist, for e.g. if it was created during load_sim_slots,
+ * persist a few more times before giving up on the SIM to be ready. There
+ * are modems on which the SIM takes more than 15s to be ready, luckily,
+ * they happen to be QMI modems where the SIM's iccid in load_sim_slots
+ * lets us know that there is a sim */
+#define MAX_UNLOCK_REQUIRED_RETRIES_NO_SIM 6
+#define MAX_UNLOCK_REQUIRED_RETRIES_SIM_EXISTS 30
+
+/* Time between retries */
+#define UNLOAD_REQUIRED_RETRY_TIMEOUT_SECS 2
typedef struct {
- MMIfaceModem *self;
- GSimpleAsyncResult *result;
guint retries;
- guint pin_check_timeout_id;
+ guint max_retries;
+ guint 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)
+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;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCK_UNKNOWN;
+ }
+ return (MMModemLock)value;
}
-static void internal_load_unlock_required_context_step (InternalLoadUnlockRequiredContext *ctx);
+static void internal_load_unlock_required_context_step (GTask *task);
static gboolean
-load_unlock_required_again (InternalLoadUnlockRequiredContext *ctx)
+load_unlock_required_again (GTask *task)
{
- ctx->pin_check_timeout_id = 0;
+ InternalLoadUnlockRequiredContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->timeout_id = 0;
/* Retry the step */
- internal_load_unlock_required_context_step (ctx);
- return FALSE;
+ internal_load_unlock_required_context_step (task);
+ return G_SOURCE_REMOVE;
}
static void
load_unlock_required_ready (MMIfaceModem *self,
GAsyncResult *res,
- InternalLoadUnlockRequiredContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
- MMModemLock lock;
+ InternalLoadUnlockRequiredContext *ctx;
+ g_autoptr(GError) error = NULL;
+ MMModemLock lock;
+
+ ctx = g_task_get_task_data (task);
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);
+ mm_obj_dbg (self, "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_IO_ERROR,
+ G_IO_ERROR_CANCELLED) ||
g_error_matches (error,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
@@ -279,75 +490,90 @@ load_unlock_required_ready (MMIfaceModem *self,
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);
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
return;
}
/* For the remaining ones, retry if possible */
- if (ctx->retries < MAX_RETRIES) {
+ if (ctx->retries < ctx->max_retries) {
ctx->retries++;
- mm_dbg ("Retrying (%u) unlock required check", ctx->retries);
+ mm_obj_dbg (self, "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);
+ g_assert (ctx->timeout_id == 0);
+ ctx->timeout_id = g_timeout_add_seconds (UNLOAD_REQUIRED_RETRY_TIMEOUT_SECS,
+ (GSourceFunc)load_unlock_required_again,
+ task);
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);
+ g_task_return_new_error (task,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE,
+ "Couldn't get SIM lock status after %u retries",
+ ctx->retries);
+ g_object_unref (task);
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);
+ g_task_return_int (task, lock);
+ g_object_unref (task);
}
static void
-internal_load_unlock_required_context_step (InternalLoadUnlockRequiredContext *ctx)
+internal_load_unlock_required_context_step (GTask *task)
{
- 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);
+ MMIfaceModem *self;
+ InternalLoadUnlockRequiredContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->timeout_id == 0);
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required (
+ self,
+ (ctx->retries >= ctx->max_retries), /* last_attempt? */
+ (GAsyncReadyCallback) load_unlock_required_ready,
+ task);
+}
+
+static guint
+load_unlock_required_max_retries (MMIfaceModem *self)
+{
+ g_autoptr(MMBaseSim) sim = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM, &sim,
+ NULL);
+
+ return (sim ? MAX_UNLOCK_REQUIRED_RETRIES_SIM_EXISTS : MAX_UNLOCK_REQUIRED_RETRIES_NO_SIM);
}
static void
-internal_load_unlock_required (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+internal_load_unlock_required (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
InternalLoadUnlockRequiredContext *ctx;
+ GTask *task;
- 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);
+ ctx = g_new0 (InternalLoadUnlockRequiredContext, 1);
+ ctx->max_retries = load_unlock_required_max_retries (self);
- if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required ||
- !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, g_free);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required ||
+ !MM_IFACE_MODEM_GET_INTERFACE (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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
- internal_load_unlock_required_context_step (ctx);
+ internal_load_unlock_required_context_step (task);
}
/*****************************************************************************/
@@ -371,11 +597,12 @@ bearer_list_updated (MMBearerList *bearer_list,
g_strfreev (paths);
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+ g_object_unref (skeleton);
}
/*****************************************************************************/
-static MMModemState get_current_consolidated_state (MMIfaceModem *self, MMModemState modem_state);
+static MMModemState get_consolidated_subsystem_state (MMIfaceModem *self);
typedef struct {
MMBaseBearer *self;
@@ -440,8 +667,10 @@ bearer_status_changed (MMBaseBearer *bearer,
new_state = MM_MODEM_STATE_DISCONNECTING;
break;
case MM_BEARER_STATUS_DISCONNECTED:
- new_state = get_current_consolidated_state (self, MM_MODEM_STATE_UNKNOWN);
+ new_state = get_consolidated_subsystem_state (self);
break;
+ default:
+ g_assert_not_reached ();
}
mm_iface_modem_update_state (self,
@@ -453,17 +682,12 @@ bearer_status_changed (MMBaseBearer *bearer,
}
typedef struct {
- MMIfaceModem *self;
MMBearerList *list;
- GSimpleAsyncResult *result;
} CreateBearerContext;
static void
-create_bearer_context_complete_and_free (CreateBearerContext *ctx)
+create_bearer_context_free (CreateBearerContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
if (ctx->list)
g_object_unref (ctx->list);
g_slice_free (CreateBearerContext, ctx);
@@ -474,30 +698,30 @@ mm_iface_modem_create_bearer_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
create_bearer_ready (MMIfaceModem *self,
GAsyncResult *res,
- CreateBearerContext *ctx)
+ GTask *task)
{
+ CreateBearerContext *ctx;
MMBaseBearer *bearer;
GError *error = NULL;
bearer = MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer_finish (self, res, &error);
if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- create_bearer_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
if (!mm_bearer_list_add_bearer (ctx->list, bearer, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- create_bearer_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
g_object_unref (bearer);
return;
}
@@ -508,8 +732,8 @@ create_bearer_ready (MMIfaceModem *self,
"notify::" MM_BASE_BEARER_STATUS,
(GCallback)bearer_status_changed,
self);
- g_simple_async_result_set_op_res_gpointer (ctx->result, bearer, g_object_unref);
- create_bearer_context_complete_and_free (ctx);
+ g_task_return_pointer (task, bearer, g_object_unref);
+ g_object_unref (task);
}
void
@@ -519,34 +743,23 @@ mm_iface_modem_create_bearer (MMIfaceModem *self,
gpointer user_data)
{
CreateBearerContext *ctx;
+ GTask *task;
ctx = g_slice_new (CreateBearerContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_create_bearer);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)create_bearer_context_free);
+
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &ctx->list,
NULL);
if (!ctx->list) {
- g_simple_async_result_set_error (
- ctx->result,
+ g_task_return_new_error (
+ task,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Cannot add new bearer: bearer list not found");
- create_bearer_context_complete_and_free (ctx);
- return;
- }
-
- if (mm_bearer_list_get_count (ctx->list) == mm_bearer_list_get_max (ctx->list)) {
- g_simple_async_result_set_error (
- ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_TOO_MANY,
- "Cannot add new bearer: already reached maximum (%u)",
- mm_bearer_list_get_count (ctx->list));
- create_bearer_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
@@ -554,7 +767,7 @@ mm_iface_modem_create_bearer (MMIfaceModem *self,
self,
properties,
(GAsyncReadyCallback)create_bearer_ready,
- ctx);
+ task);
}
typedef struct {
@@ -609,6 +822,11 @@ handle_create_bearer_auth_ready (MMBaseModem *self,
return;
}
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) {
+ handle_create_bearer_context_free (ctx);
+ return;
+ }
+
properties = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error);
if (!properties) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
@@ -698,6 +916,7 @@ handle_command_auth_ready (MMBaseModem *self,
return;
}
+#if ! defined WITH_AT_COMMAND_VIA_DBUS
/* If we are not in Debug mode, report an error */
if (!mm_context_get_debug ()) {
g_dbus_method_invocation_return_error (ctx->invocation,
@@ -708,6 +927,7 @@ handle_command_auth_ready (MMBaseModem *self,
handle_command_context_free (ctx);
return;
}
+#endif
/* If command is not implemented, report an error */
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->command ||
@@ -760,11 +980,14 @@ typedef struct {
MMIfaceModem *self;
MMBearerList *list;
gchar *bearer_path;
+ MMBaseBearer *bearer;
} HandleDeleteBearerContext;
static void
handle_delete_bearer_context_free (HandleDeleteBearerContext *ctx)
{
+ if (ctx->bearer)
+ g_object_unref (ctx->bearer);
g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation);
g_object_unref (ctx->self);
@@ -775,6 +998,26 @@ handle_delete_bearer_context_free (HandleDeleteBearerContext *ctx)
}
static void
+delete_bearer_disconnect_ready (MMBaseBearer *bearer,
+ GAsyncResult *res,
+ HandleDeleteBearerContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!mm_base_bearer_disconnect_finish (bearer, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_delete_bearer_context_free (ctx);
+ return;
+ }
+
+ if (!mm_bearer_list_delete_bearer (ctx->list, ctx->bearer_path, &error))
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ else
+ mm_gdbus_modem_complete_delete_bearer (ctx->skeleton, ctx->invocation);
+ handle_delete_bearer_context_free (ctx);
+}
+
+static void
handle_delete_bearer_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleDeleteBearerContext *ctx)
@@ -787,14 +1030,35 @@ handle_delete_bearer_auth_ready (MMBaseModem *self,
return;
}
- if (!ctx->list)
- mm_gdbus_modem_complete_delete_bearer (ctx->skeleton, ctx->invocation);
- else if (!mm_bearer_list_delete_bearer (ctx->list, ctx->bearer_path, &error))
- g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
- mm_gdbus_modem_complete_delete_bearer (ctx->skeleton, ctx->invocation);
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) {
+ handle_delete_bearer_context_free (ctx);
+ return;
+ }
- handle_delete_bearer_context_free (ctx);
+ if (!g_str_has_prefix (ctx->bearer_path, MM_DBUS_BEARER_PREFIX)) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot delete bearer: invalid path '%s'",
+ ctx->bearer_path);
+ handle_delete_bearer_context_free (ctx);
+ return;
+ }
+
+ ctx->bearer = mm_bearer_list_find_by_path (ctx->list, ctx->bearer_path);
+ if (!ctx->bearer) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot delete bearer: no bearer found with path '%s'",
+ ctx->bearer_path);
+ handle_delete_bearer_context_free (ctx);
+ return;
+ }
+
+ mm_base_bearer_disconnect (ctx->bearer,
+ (GAsyncReadyCallback)delete_bearer_disconnect_ready,
+ ctx);
}
static gboolean
@@ -832,6 +1096,9 @@ handle_list_bearers (MmGdbusModem *skeleton,
GStrv paths;
MMBearerList *list = NULL;
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (self, invocation, MM_MODEM_STATE_LOCKED))
+ return TRUE;
+
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
@@ -855,6 +1122,117 @@ handle_list_bearers (MmGdbusModem *skeleton,
/*****************************************************************************/
+typedef struct {
+ MmGdbusModem *skeleton;
+ GDBusMethodInvocation *invocation;
+ MMIfaceModem *self;
+ guint requested_sim_slot;
+} HandleSetPrimarySimSlotContext;
+
+static void
+handle_set_primary_sim_slot_context_free (HandleSetPrimarySimSlotContext *ctx)
+{
+ g_clear_object (&ctx->skeleton);
+ g_clear_object (&ctx->invocation);
+ g_clear_object (&ctx->self);
+ g_free (ctx);
+}
+
+static void
+set_primary_sim_slot_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ HandleSetPrimarySimSlotContext *ctx)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish (self, res, &error)) {
+ /* If the implementation returns EXISTS, we're already in the requested SIM slot,
+ * so we can safely return a success on the operation and skip the reprobing */
+ if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS)) {
+ mm_obj_warn (self, "couldn't process primary SIM update request: %s", error->message);
+ g_dbus_method_invocation_take_error (ctx->invocation, g_steal_pointer (&error));
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+ mm_obj_dbg (self, "ignoring SIM update request: %s", error->message);
+ } else {
+ /* Notify about the SIM swap, which will disable and reprobe the device.
+ * There is no need to update the PrimarySimSlot property, as this value will be
+ * reloaded automatically during the reprobe. */
+ mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
+ }
+
+ mm_gdbus_modem_complete_set_primary_sim_slot (ctx->skeleton, ctx->invocation);
+ handle_set_primary_sim_slot_context_free (ctx);
+}
+
+static void
+handle_set_primary_sim_slot_auth_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ HandleSetPrimarySimSlotContext *ctx)
+{
+ GError *error = NULL;
+ const gchar *const *sim_slot_paths;
+
+ if (!mm_base_modem_authorize_finish (self, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+
+ /* If SIM switching is not implemented, report an error */
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot ||
+ !MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot switch sim: "
+ "operation not supported");
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+
+ /* Validate SIM slot number */
+ sim_slot_paths = mm_gdbus_modem_get_sim_slots (ctx->skeleton);
+ if (!sim_slot_paths || (ctx->requested_sim_slot > g_strv_length ((gchar **)sim_slot_paths))) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot switch sim: requested SIM slot number is out of bounds");
+ handle_set_primary_sim_slot_context_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_GET_INTERFACE (self)->set_primary_sim_slot (MM_IFACE_MODEM (self),
+ ctx->requested_sim_slot,
+ (GAsyncReadyCallback)set_primary_sim_slot_ready,
+ ctx);
+}
+
+static gboolean
+handle_set_primary_sim_slot (MmGdbusModem *skeleton,
+ GDBusMethodInvocation *invocation,
+ guint sim_slot,
+ MMIfaceModem *self)
+{
+ HandleSetPrimarySimSlotContext *ctx;
+
+ ctx = g_new0 (HandleSetPrimarySimSlotContext, 1);
+ ctx->skeleton = g_object_ref (skeleton);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->self = g_object_ref (self);
+ ctx->requested_sim_slot = sim_slot;
+
+ mm_base_modem_authorize (MM_BASE_MODEM (self),
+ invocation,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
+ (GAsyncReadyCallback)handle_set_primary_sim_slot_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
void
mm_iface_modem_update_access_technologies (MMIfaceModem *self,
MMModemAccessTechnology new_access_tech,
@@ -888,10 +1266,9 @@ mm_iface_modem_update_access_technologies (MMIfaceModem *self,
/* Log */
old_access_tech_string = mm_modem_access_technology_build_string_from_mask (old_access_tech);
new_access_tech_string = mm_modem_access_technology_build_string_from_mask (built_access_tech);
- mm_dbg ("Modem %s: access technology changed (%s -> %s)",
- g_dbus_object_get_object_path (G_DBUS_OBJECT (self)),
- old_access_tech_string,
- new_access_tech_string);
+ mm_obj_dbg (self, "access technology changed (%s -> %s)",
+ old_access_tech_string,
+ new_access_tech_string);
g_free (old_access_tech_string);
g_free (new_access_tech_string);
}
@@ -902,152 +1279,6 @@ mm_iface_modem_update_access_technologies (MMIfaceModem *self,
/*****************************************************************************/
typedef struct {
- guint timeout_source;
- gboolean running;
-} AccessTechnologiesCheckContext;
-
-static void
-access_technologies_check_context_free (AccessTechnologiesCheckContext *ctx)
-{
- if (ctx->timeout_source)
- g_source_remove (ctx->timeout_source);
- g_free (ctx);
-}
-
-static void
-access_technologies_check_ready (MMIfaceModem *self,
- GAsyncResult *res)
-{
- GError *error = NULL;
- MMModemAccessTechnology access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- guint mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
- AccessTechnologiesCheckContext *ctx;
-
- if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish (
- self,
- res,
- &access_technologies,
- &mask,
- &error)) {
- /* Ignore issues when the operation is unsupported, don't even log */
- if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED))
- mm_dbg ("Couldn't refresh access technologies: '%s'", error->message);
- g_error_free (error);
- } else
- mm_iface_modem_update_access_technologies (self, access_technologies, mask);
-
- /* Remove the running tag. Note that the context may have been removed by
- * mm_iface_modem_shutdown when this function is invoked as a callback of
- * load_access_technologies. */
- ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
- if (ctx)
- ctx->running = FALSE;
-}
-
-static gboolean
-periodic_access_technologies_check (MMIfaceModem *self)
-{
- AccessTechnologiesCheckContext *ctx;
-
- ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
-
- /* Only launch a new one if not one running already OR if the last one run
- * was more than 15s ago. */
- if (!ctx->running) {
- ctx->running = TRUE;
- MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies (
- self,
- (GAsyncReadyCallback)access_technologies_check_ready,
- NULL);
- }
-
- return TRUE;
-}
-
-void
-mm_iface_modem_refresh_access_technologies (MMIfaceModem *self)
-{
- AccessTechnologiesCheckContext *ctx;
-
- if (G_UNLIKELY (!access_technologies_check_context_quark))
- access_technologies_check_context_quark = (g_quark_from_static_string (
- ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG));
-
- ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
- if (!ctx)
- return;
-
- /* Re-set timeout */
- if (ctx->timeout_source)
- g_source_remove (ctx->timeout_source);
- ctx->timeout_source = g_timeout_add_seconds (ACCESS_TECHNOLOGIES_CHECK_TIMEOUT_SEC,
- (GSourceFunc)periodic_access_technologies_check,
- self);
-
- /* Get first access technology value */
- periodic_access_technologies_check (self);
-}
-
-static void
-periodic_access_technologies_check_disable (MMIfaceModem *self)
-{
- if (G_UNLIKELY (!access_technologies_check_context_quark))
- access_technologies_check_context_quark = (g_quark_from_static_string (
- ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG));
-
- /* Clear access technology */
- mm_iface_modem_update_access_technologies (self,
- MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
- MM_MODEM_ACCESS_TECHNOLOGY_ANY);
-
- /* Overwriting the data will free the previous context */
- g_object_set_qdata (G_OBJECT (self),
- access_technologies_check_context_quark,
- NULL);
-
- mm_dbg ("Periodic access technology checks disabled");
-}
-
-static void
-periodic_access_technologies_check_enable (MMIfaceModem *self)
-{
- AccessTechnologiesCheckContext *ctx;
-
- if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies ||
- !MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish) {
- /* If loading access technology not supported, don't even bother setting up
- * a timeout */
- return;
- }
-
- if (G_UNLIKELY (!access_technologies_check_context_quark))
- access_technologies_check_context_quark = (g_quark_from_static_string (
- ACCESS_TECHNOLOGIES_CHECK_CONTEXT_TAG));
-
- ctx = g_object_get_qdata (G_OBJECT (self), access_technologies_check_context_quark);
-
- /* If context is already there, we're already enabled */
- if (ctx) {
- periodic_access_technologies_check (self);
- return;
- }
-
- /* Create context and keep it as object data */
- mm_dbg ("Periodic access technology checks enabled");
- ctx = g_new0 (AccessTechnologiesCheckContext, 1);
- g_object_set_qdata_full (G_OBJECT (self),
- access_technologies_check_context_quark,
- ctx,
- (GDestroyNotify)access_technologies_check_context_free);
-
- /* Get first and setup timeout */
- mm_iface_modem_refresh_access_technologies (self);
-}
-
-/*****************************************************************************/
-
-typedef struct {
- time_t last_update;
guint recent_timeout_source;
} SignalQualityUpdateContext;
@@ -1059,20 +1290,6 @@ signal_quality_update_context_free (SignalQualityUpdateContext *ctx)
g_free (ctx);
}
-static time_t
-get_last_signal_quality_update_time (MMIfaceModem *self)
-{
- SignalQualityUpdateContext *ctx;
-
- if (G_UNLIKELY (!signal_quality_update_context_quark))
- signal_quality_update_context_quark = (g_quark_from_static_string (
- SIGNAL_QUALITY_UPDATE_CONTEXT_TAG));
-
- ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_update_context_quark);
-
- return (ctx ? ctx->last_update : 0);
-}
-
static gboolean
expire_signal_quality (MMIfaceModem *self)
{
@@ -1096,9 +1313,8 @@ expire_signal_quality (MMIfaceModem *self)
/* If value is already not recent, we're done */
if (recent) {
- mm_dbg ("Signal quality value not updated in %us, "
- "marking as not being recent",
- SIGNAL_QUALITY_RECENT_TIMEOUT_SEC);
+ mm_obj_dbg (self, "signal quality value not updated in %us, marking as not being recent",
+ SIGNAL_QUALITY_RECENT_TIMEOUT_SEC);
mm_gdbus_modem_set_signal_quality (skeleton,
g_variant_new ("(ub)",
signal_quality,
@@ -1111,7 +1327,7 @@ expire_signal_quality (MMIfaceModem *self)
/* Remove source id */
ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_update_context_quark);
ctx->recent_timeout_source = 0;
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -1121,7 +1337,6 @@ update_signal_quality (MMIfaceModem *self,
{
SignalQualityUpdateContext *ctx;
MmGdbusModem *skeleton = NULL;
- const gchar *dbus_path;
g_object_get (self,
MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
@@ -1146,9 +1361,6 @@ update_signal_quality (MMIfaceModem *self,
(GDestroyNotify)signal_quality_update_context_free);
}
- /* Keep current timestamp */
- ctx->last_update = time (NULL);
-
/* Note: we always set the new value, even if the signal quality level
* is the same, in order to provide an up to date 'recent' flag.
* The only exception being if 'expire' is FALSE; in that case we assume
@@ -1159,10 +1371,7 @@ update_signal_quality (MMIfaceModem *self,
signal_quality,
expire));
- dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
- mm_dbg ("Modem %s: signal quality updated (%u)",
- dbus_path,
- signal_quality);
+ mm_obj_dbg (self, "signal quality updated (%u)", signal_quality);
/* Remove any previous expiration refresh timeout */
if (ctx->recent_timeout_source) {
@@ -1188,142 +1397,357 @@ mm_iface_modem_update_signal_quality (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Signal info (quality and access technology) polling */
+
+typedef enum {
+ SIGNAL_CHECK_STEP_NONE,
+ SIGNAL_CHECK_STEP_FIRST,
+ SIGNAL_CHECK_STEP_SIGNAL_QUALITY,
+ SIGNAL_CHECK_STEP_ACCESS_TECHNOLOGIES,
+ SIGNAL_CHECK_STEP_LAST,
+} SignalCheckStep;
typedef struct {
- guint interval;
- guint initial_retries;
- guint timeout_source;
- gboolean running;
-} SignalQualityCheckContext;
+ gboolean enabled;
+ guint timeout_source;
+
+ /* We first attempt an initial loading, and once it's done we
+ * setup polling */
+ guint initial_retries;
+ gboolean initial_check_done;
+
+ /* Values polled in this iteration */
+ guint signal_quality;
+ MMModemAccessTechnology access_technologies;
+ guint access_technologies_mask;
+
+ /* If both signal and access tech polling are either unsupported
+ * or disabled, we'll automatically stop polling */
+ gboolean signal_quality_polling_supported;
+ gboolean signal_quality_polling_disabled;
+ gboolean access_technology_polling_supported;
+ gboolean access_technology_polling_disabled;
+
+ /* Steps triggered when polling active */
+ SignalCheckStep running_step;
+} SignalCheckContext;
static void
-signal_quality_check_context_free (SignalQualityCheckContext *ctx)
+signal_check_context_free (SignalCheckContext *ctx)
{
if (ctx->timeout_source)
g_source_remove (ctx->timeout_source);
- g_free (ctx);
+ g_slice_free (SignalCheckContext, ctx);
+}
+
+static SignalCheckContext *
+get_signal_check_context (MMIfaceModem *self)
+{
+ SignalCheckContext *ctx;
+
+ if (G_UNLIKELY (!signal_check_context_quark))
+ signal_check_context_quark = (g_quark_from_static_string (
+ SIGNAL_CHECK_CONTEXT_TAG));
+
+ ctx = g_object_get_qdata (G_OBJECT (self), signal_check_context_quark);
+ if (!ctx) {
+ /* Create context and attach it to the object */
+ ctx = g_slice_new0 (SignalCheckContext);
+ ctx->running_step = SIGNAL_CHECK_STEP_NONE;
+
+ /* Initially assume supported if load_access_technologies() is
+ * implemented. If the plugin reports an UNSUPPORTED error we'll clear
+ * this flag and no longer poll. */
+ ctx->access_technology_polling_supported = (MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish);
+
+ /* Initially assume supported if load_signal_quality() is
+ * implemented. If the plugin reports an UNSUPPORTED error we'll clear
+ * this flag and no longer poll. */
+ ctx->signal_quality_polling_supported = (MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish);
+
+ /* Get plugin-specific setup for the polling logic */
+ g_object_get (self,
+ MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED, &ctx->signal_quality_polling_disabled,
+ MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED, &ctx->access_technology_polling_disabled,
+ NULL);
+
+ g_object_set_qdata_full (G_OBJECT (self), signal_check_context_quark,
+ ctx, (GDestroyNotify) signal_check_context_free);
+ }
+
+ g_assert (ctx);
+ return ctx;
}
-static gboolean periodic_signal_quality_check (MMIfaceModem *self);
+static void periodic_signal_check_disable (MMIfaceModem *self,
+ gboolean clear);
+static gboolean periodic_signal_check_cb (MMIfaceModem *self);
+static void periodic_signal_check_step (MMIfaceModem *self);
+
+static void
+access_technologies_check_ready (MMIfaceModem *self,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+ SignalCheckContext *ctx;
+
+ ctx = get_signal_check_context (self);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies_finish (
+ self,
+ res,
+ &ctx->access_technologies,
+ &ctx->access_technologies_mask,
+ &error)) {
+ /* Did the plugin report that polling access technology is unsupported? */
+ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_dbg (self, "polling to refresh access technologies is unsupported");
+ ctx->access_technology_polling_supported = FALSE;
+ }
+ /* Ignore logging any message if the error is in 'in-progress' */
+ else if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS))
+ mm_obj_dbg (self, "couldn't refresh access technologies: %s", error->message);
+ g_error_free (error);
+ }
+ /* We may have been disabled while this command was running. */
+ else if (ctx->enabled)
+ mm_iface_modem_update_access_technologies (self, ctx->access_technologies, ctx->access_technologies_mask);
+
+ /* Go on */
+ ctx->running_step++;
+ periodic_signal_check_step (self);
+}
static void
signal_quality_check_ready (MMIfaceModem *self,
GAsyncResult *res)
{
- GError *error = NULL;
- guint signal_quality;
- SignalQualityCheckContext *ctx;
+ GError *error = NULL;
+ SignalCheckContext *ctx;
+
+ ctx = get_signal_check_context (self);
- signal_quality = MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish (self,
- res,
- &error);
+ ctx->signal_quality = MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish (self, res, &error);
if (error) {
- mm_dbg ("Couldn't refresh signal quality: '%s'", error->message);
+ /* Did the plugin report that polling signal quality is unsupported? */
+ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ mm_obj_dbg (self, "polling to refresh signal quality is unsupported");
+ ctx->signal_quality_polling_supported = FALSE;
+ }
+ /* Ignore logging any message if the error is in 'in-progress' */
+ else if (!g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS))
+ mm_obj_dbg (self, "couldn't refresh signal quality: %s", error->message);
g_error_free (error);
- } else
- update_signal_quality (self, signal_quality, TRUE);
-
- /* Remove the running tag. Note that the context may have been removed by
- * mm_iface_modem_shutdown when this function is invoked as a callback of
- * load_signal_quality. */
- ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_check_context_quark);
- if (ctx) {
- if (ctx->interval == SIGNAL_QUALITY_INITIAL_CHECK_TIMEOUT_SEC &&
- (signal_quality != 0 || --ctx->initial_retries == 0)) {
- ctx->interval = SIGNAL_QUALITY_CHECK_TIMEOUT_SEC;
- if (ctx->timeout_source) {
- mm_dbg ("Periodic signal quality checks rescheduled (interval = %ds)", ctx->interval);
- g_source_remove(ctx->timeout_source);
- ctx->timeout_source = g_timeout_add_seconds (ctx->interval,
- (GSourceFunc)periodic_signal_quality_check,
- self);
- }
+ }
+ /* We may have been disabled while this command was running. */
+ else if (ctx->enabled)
+ update_signal_quality (self, ctx->signal_quality, TRUE);
+
+ /* Go on */
+ ctx->running_step++;
+ periodic_signal_check_step (self);
+}
+
+static void
+periodic_signal_check_step (MMIfaceModem *self)
+{
+ SignalCheckContext *ctx;
+
+ ctx = get_signal_check_context (self);
+
+ switch (ctx->running_step) {
+ case SIGNAL_CHECK_STEP_NONE:
+ g_assert_not_reached ();
+
+ case SIGNAL_CHECK_STEP_FIRST:
+ ctx->running_step++;
+ /* fall-through */
+
+ case SIGNAL_CHECK_STEP_SIGNAL_QUALITY:
+ if (ctx->enabled && ctx->signal_quality_polling_supported &&
+ (!ctx->initial_check_done || !ctx->signal_quality_polling_disabled)) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality (
+ self, (GAsyncReadyCallback)signal_quality_check_ready, NULL);
+ return;
+ }
+ ctx->running_step++;
+ /* fall-through */
+
+ case SIGNAL_CHECK_STEP_ACCESS_TECHNOLOGIES:
+ if (ctx->enabled && ctx->access_technology_polling_supported &&
+ (!ctx->initial_check_done || !ctx->access_technology_polling_disabled)) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_access_technologies (
+ self, (GAsyncReadyCallback)access_technologies_check_ready, NULL);
+ return;
+ }
+ ctx->running_step++;
+ /* fall-through */
+
+ case SIGNAL_CHECK_STEP_LAST:
+ /* Flag as sequence finished */
+ ctx->running_step = SIGNAL_CHECK_STEP_NONE;
+
+ /* If we have been disabled while we were running the steps, we don't
+ * do anything else. */
+ if (!ctx->enabled) {
+ mm_obj_dbg (self, "periodic signal quality and access technology checks not rescheduled: disabled");
+ return;
}
- ctx->running = FALSE;
+
+ /* Schedule when we poll next time.
+ * Initially we poll at a higher frequency until we get valid signal
+ * quality and access technology values. As soon as we get them, OR if
+ * we made too many retries at a high frequency, we fallback to the
+ * slower polling. */
+ if (!ctx->initial_check_done) {
+ gboolean signal_quality_ready;
+ gboolean access_technology_ready;
+
+ /* Signal quality is ready if unsupported or if we got a valid
+ * value reported */
+ signal_quality_ready = (!ctx->signal_quality_polling_supported || (ctx->signal_quality != 0));
+
+ /* Access technology is ready if unsupported or if we got a valid
+ * value reported */
+ access_technology_ready = (!ctx->access_technology_polling_supported ||
+ ((ctx->access_technologies & ctx->access_technologies_mask) != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN));
+
+ ctx->initial_check_done = ((signal_quality_ready && access_technology_ready) || (--ctx->initial_retries == 0));
+ }
+
+ /* After running the initial check, if both signal quality and access tech
+ * loading are either disabled or unsupported, we'll stop polling completely,
+ * because they may be loaded asynchronously by unsolicited messages */
+ if (ctx->initial_check_done &&
+ (!ctx->signal_quality_polling_supported || ctx->signal_quality_polling_disabled) &&
+ (!ctx->access_technology_polling_supported || ctx->access_technology_polling_disabled)) {
+ mm_obj_dbg (self, "periodic signal quality and access technology checks not rescheduled: unneeded or unsupported");
+ periodic_signal_check_disable (self, FALSE);
+ return;
+ }
+
+ mm_obj_dbg (self, "periodic signal quality and access technology checks scheduled");
+ g_assert (!ctx->timeout_source);
+ ctx->timeout_source = g_timeout_add_seconds (ctx->initial_check_done ? SIGNAL_CHECK_TIMEOUT_SEC : SIGNAL_CHECK_INITIAL_TIMEOUT_SEC,
+ (GSourceFunc) periodic_signal_check_cb,
+ self);
+ return;
+
+ default:
+ g_assert_not_reached ();
}
}
static gboolean
-periodic_signal_quality_check (MMIfaceModem *self)
+periodic_signal_check_cb (MMIfaceModem *self)
{
- SignalQualityCheckContext *ctx;
+ SignalCheckContext *ctx;
- ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_check_context_quark);
+ ctx = get_signal_check_context (self);
+ g_assert (ctx->enabled);
- /* Only launch a new one if not one running already OR if the last one run
- * was more than 15s ago. */
- if (!ctx->running ||
- (time (NULL) - get_last_signal_quality_update_time (self) > (ctx->interval / 2))) {
- ctx->running = TRUE;
- MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality (
- self,
- (GAsyncReadyCallback)signal_quality_check_ready,
- NULL);
- }
+ /* Start the sequence */
+ ctx->running_step = SIGNAL_CHECK_STEP_FIRST;
+ ctx->signal_quality = 0;
+ ctx->access_technologies = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ ctx->access_technologies_mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
+ periodic_signal_check_step (self);
- return TRUE;
+ /* Remove the timeout and clear the source id */
+ if (ctx->timeout_source)
+ ctx->timeout_source = 0;
+ return G_SOURCE_REMOVE;
}
-static void
-periodic_signal_quality_check_disable (MMIfaceModem *self)
+void
+mm_iface_modem_refresh_signal (MMIfaceModem *self)
{
- if (G_UNLIKELY (!signal_quality_check_context_quark))
- signal_quality_check_context_quark = (g_quark_from_static_string (
- SIGNAL_QUALITY_CHECK_CONTEXT_TAG));
+ SignalCheckContext *ctx;
+
+ ctx = get_signal_check_context (self);
+
+ /* Don't refresh polling if we're not enabled */
+ if (!ctx->enabled) {
+ mm_obj_dbg (self, "periodic signal check refresh ignored: checks not enabled");
+ return;
+ }
+
+ /* Don't refresh if we're already doing it */
+ if (ctx->running_step != SIGNAL_CHECK_STEP_NONE) {
+ mm_obj_dbg (self, "periodic signal check refresh ignored: check already running");
+ return;
+ }
+
+ mm_obj_dbg (self, "periodic signal check refresh requested");
- /* Clear signal quality */
- update_signal_quality (self, 0, FALSE);
+ /* Remove the scheduled timeout as we're going to refresh
+ * right away */
+ if (ctx->timeout_source) {
+ g_source_remove (ctx->timeout_source);
+ ctx->timeout_source = 0;
+ }
- /* Overwriting the data will free the previous context */
- g_object_set_qdata (G_OBJECT (self),
- signal_quality_check_context_quark,
- NULL);
+ /* Reset refresh rate and initial retries when we're asked to refresh signal
+ * so that we poll at a higher frequency */
+ ctx->initial_retries = SIGNAL_CHECK_INITIAL_RETRIES;
+ ctx->initial_check_done = FALSE;
- mm_dbg ("Periodic signal quality checks disabled");
+ /* Start sequence */
+ periodic_signal_check_cb (self);
}
static void
-periodic_signal_quality_check_enable (MMIfaceModem *self)
+periodic_signal_check_disable (MMIfaceModem *self,
+ gboolean clear)
{
- SignalQualityCheckContext *ctx;
+ SignalCheckContext *ctx;
- if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality ||
- !MM_IFACE_MODEM_GET_INTERFACE (self)->load_signal_quality_finish) {
- /* If loading signal quality not supported, don't even bother setting up
- * a timeout */
+ ctx = get_signal_check_context (self);
+ if (!ctx->enabled)
return;
+
+ /* Clear access technology and signal quality */
+ if (clear) {
+ update_signal_quality (self, 0, FALSE);
+ mm_iface_modem_update_access_technologies (self,
+ MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN,
+ MM_MODEM_ACCESS_TECHNOLOGY_ANY);
}
- if (G_UNLIKELY (!signal_quality_check_context_quark))
- signal_quality_check_context_quark = (g_quark_from_static_string (
- SIGNAL_QUALITY_CHECK_CONTEXT_TAG));
+ /* Remove scheduled timeout */
+ if (ctx->timeout_source) {
+ g_source_remove (ctx->timeout_source);
+ ctx->timeout_source = 0;
+ }
+
+ ctx->enabled = FALSE;
+ mm_obj_dbg (self, "periodic signal checks disabled");
+}
+
+static void
+periodic_signal_check_enable (MMIfaceModem *self)
+{
+ SignalCheckContext *ctx;
- ctx = g_object_get_qdata (G_OBJECT (self), signal_quality_check_context_quark);
+ ctx = get_signal_check_context (self);
- /* If context is already there, we're already enabled */
- if (ctx) {
- periodic_signal_quality_check (self);
+ /* If polling access technology and signal quality not supported, don't even
+ * bother trying. */
+ if (!ctx->signal_quality_polling_supported && !ctx->access_technology_polling_supported) {
+ mm_obj_dbg (self, "not enabling periodic signal checks: unsupported");
return;
}
- /* Create context and keep it as object data */
- ctx = g_new0 (SignalQualityCheckContext, 1);
- /* Schedule the signal quality check using a shorter period, up to 5
- * periods, initially until a non-zero signal quality value is obtained
- * and then switch back to the normal period. */
- ctx->interval = SIGNAL_QUALITY_INITIAL_CHECK_TIMEOUT_SEC;
- ctx->initial_retries = 5;
- mm_dbg ("Periodic signal quality checks enabled (interval = %ds)", ctx->interval);
- ctx->timeout_source = g_timeout_add_seconds (ctx->interval,
- (GSourceFunc)periodic_signal_quality_check,
- self);
- g_object_set_qdata_full (G_OBJECT (self),
- signal_quality_check_context_quark,
- ctx,
- (GDestroyNotify)signal_quality_check_context_free);
+ /* Log and flag as enabled */
+ if (!ctx->enabled) {
+ mm_obj_dbg (self, "periodic signal checks enabled");
+ ctx->enabled = TRUE;
+ }
- /* Get first signal quality value */
- periodic_signal_quality_check (self);
+ /* And refresh, which will trigger the first check at high frequency */
+ mm_iface_modem_refresh_signal (self);
}
/*****************************************************************************/
@@ -1362,7 +1786,8 @@ __iface_modem_update_state_internal (MMIfaceModem *self,
/* While connected we don't want registration status changes to change
* the modem's state away from CONNECTED. */
- if ((new_state == MM_MODEM_STATE_SEARCHING ||
+ if ((new_state == MM_MODEM_STATE_ENABLED ||
+ new_state == MM_MODEM_STATE_SEARCHING ||
new_state == MM_MODEM_STATE_REGISTERED) &&
bearer_list &&
old_state > MM_MODEM_STATE_REGISTERED) {
@@ -1378,18 +1803,13 @@ __iface_modem_update_state_internal (MMIfaceModem *self,
/* Enabled may really be searching or registered */
if (new_state == MM_MODEM_STATE_ENABLED)
- new_state = get_current_consolidated_state (self, new_state);
+ new_state = get_consolidated_subsystem_state (self);
/* Update state only if different */
if (new_state != old_state) {
- const gchar *dbus_path;
-
- dbus_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (self));
- mm_info ("Modem%s%s: state changed (%s -> %s)",
- dbus_path ? " " : "",
- dbus_path ? dbus_path : "",
- mm_modem_state_get_string (old_state),
- mm_modem_state_get_string (new_state));
+ mm_obj_info (self, "state changed (%s -> %s)",
+ mm_modem_state_get_string (old_state),
+ mm_modem_state_get_string (new_state));
/* The property in the interface is bound to the property
* in the skeleton, so just updating here is enough */
@@ -1413,20 +1833,14 @@ __iface_modem_update_state_internal (MMIfaceModem *self,
reason);
}
- /* If we go to registered state (from unregistered), setup signal
- * quality and access technologies periodic retrieval */
- if (new_state == MM_MODEM_STATE_REGISTERED &&
- old_state < MM_MODEM_STATE_REGISTERED) {
- periodic_signal_quality_check_enable (self);
- periodic_access_technologies_check_enable (self);
- }
+ /* If we go to a registered/connected state (from unregistered), setup
+ * signal quality and access technologies periodic retrieval */
+ if (new_state >= MM_MODEM_STATE_REGISTERED && old_state < MM_MODEM_STATE_REGISTERED)
+ periodic_signal_check_enable (self);
/* If we go from a registered/connected state to unregistered,
* cleanup signal quality retrieval */
- else if (old_state >= MM_MODEM_STATE_REGISTERED &&
- new_state < MM_MODEM_STATE_REGISTERED) {
- periodic_signal_quality_check_disable (self);
- periodic_access_technologies_check_disable (self);
- }
+ else if (old_state >= MM_MODEM_STATE_REGISTERED && new_state < MM_MODEM_STATE_REGISTERED)
+ periodic_signal_check_disable (self, TRUE);
}
if (skeleton)
@@ -1478,9 +1892,12 @@ subsystem_state_array_free (GArray *array)
}
static MMModemState
-get_current_consolidated_state (MMIfaceModem *self, MMModemState modem_state)
+get_consolidated_subsystem_state (MMIfaceModem *self)
{
- MMModemState consolidated = modem_state;
+ /* Use as initial state ENABLED, which is the minimum one expected. Do not
+ * use the old modem state as initial state, as that would disallow reporting
+ * e.g. ENABLED if the old state was REGISTERED (as ENABLED < REGISTERED). */
+ MMModemState consolidated = MM_MODEM_STATE_ENABLED;
GArray *subsystem_states;
if (G_UNLIKELY (!state_update_context_quark))
@@ -1557,14 +1974,13 @@ get_updated_consolidated_state (MMIfaceModem *self,
if (i == subsystem_states->len) {
SubsystemState s;
- mm_dbg ("Will start keeping track of state for subsystem '%s'",
- subsystem);
+ mm_obj_dbg (self, "will start keeping track of state for subsystem '%s'", subsystem);
s.subsystem = g_strdup (subsystem);
s.state = subsystem_state;
g_array_append_val (subsystem_states, s);
}
- return get_current_consolidated_state (self, modem_state);
+ return get_consolidated_subsystem_state (self);
}
void
@@ -1646,6 +2062,11 @@ handle_enable_auth_ready (MMBaseModem *self,
return;
}
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_LOCKED)) {
+ handle_enable_context_free (ctx);
+ return;
+ }
+
if (ctx->enable)
mm_base_modem_enable (self,
(GAsyncReadyCallback)enable_ready,
@@ -1742,14 +2163,15 @@ handle_set_power_state_auth_ready (MMBaseModem *self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
- /* Going into LOW or ON only allowed in disabled state */
+ /* Going into LOW or ON only allowed in disabled and failed states */
if ((ctx->power_state == MM_MODEM_POWER_STATE_LOW ||
ctx->power_state == MM_MODEM_POWER_STATE_ON) &&
+ modem_state != MM_MODEM_STATE_FAILED &&
modem_state != MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
- "Cannot set power state: not in disabled state");
+ "Cannot set power state: not in disabled or failed state");
handle_set_power_state_context_free (ctx);
return;
}
@@ -1966,7 +2388,12 @@ handle_factory_reset (MmGdbusModem *skeleton,
}
/*****************************************************************************/
-/* Current capabilities setting */
+/* Current capabilities setting
+ *
+ * Setting capabilities allowed also in FAILED state. Just imagine a
+ * 3GPP+3GPP2 modem in 3GPP-only mode without SIM, we should allow
+ * changing caps to 3GPP2, which doesn't require SIM
+ */
typedef struct {
MmGdbusModem *skeleton;
@@ -1993,8 +2420,12 @@ set_current_capabilities_ready (MMIfaceModem *self,
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ else {
+ /* Capabilities updated: explicitly refresh signal and access technology */
+ mm_iface_modem_refresh_signal (self);
mm_gdbus_modem_complete_set_current_capabilities (ctx->skeleton, ctx->invocation);
+ }
+
handle_set_current_capabilities_context_free (ctx);
}
@@ -2069,7 +2500,7 @@ handle_set_current_capabilities_auth_ready (MMBaseModem *self,
}
capabilities_string = mm_modem_capability_build_string_from_mask (ctx->capabilities);
- mm_dbg ("Setting new list of capabilities: '%s'", capabilities_string);
+ mm_obj_dbg (self, "setting new list of capabilities: %s", capabilities_string);
g_free (capabilities_string);
MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_capabilities (
@@ -2104,23 +2535,25 @@ handle_set_current_capabilities (MmGdbusModem *skeleton,
/*****************************************************************************/
/* Current bands setting */
+#define AFTER_SET_LOAD_CURRENT_BANDS_RETRIES 5
+#define AFTER_SET_LOAD_CURRENT_BANDS_TIMEOUT_SECS 1
+
typedef struct {
- MMIfaceModem *self;
MmGdbusModem *skeleton;
- GSimpleAsyncResult *result;
- GArray *bands_array;
+ GArray *bands_array;
+ GArray *supported_bands_array; /* when ANY requested */
+ guint retries;
} SetCurrentBandsContext;
static void
-set_current_bands_context_complete_and_free (SetCurrentBandsContext *ctx)
+set_current_bands_context_free (SetCurrentBandsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
if (ctx->bands_array)
g_array_unref (ctx->bands_array);
+ if (ctx->supported_bands_array)
+ g_array_unref (ctx->supported_bands_array);
g_slice_free (SetCurrentBandsContext, ctx);
}
@@ -2129,37 +2562,151 @@ mm_iface_modem_set_current_bands_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_current_bands_complete_with_defaults (GTask *task)
+{
+ SetCurrentBandsContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ /* Never show just 'any' in the interface */
+ if (ctx->bands_array->len == 1 && g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ g_assert (ctx->supported_bands_array);
+ g_array_unref (ctx->bands_array);
+ ctx->bands_array = g_array_ref (ctx->supported_bands_array);
+ }
+
+ mm_common_bands_garray_sort (ctx->bands_array);
+ mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (ctx->bands_array));
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void set_current_bands_reload_schedule (GTask *task);
+
+static void
+after_set_load_current_bands_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentBandsContext *ctx;
+ GArray *current_bands;
+ GError *error = NULL;
+ GArray *requested_bands = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ current_bands = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error);
+ if (!current_bands) {
+ /* If we can retry, do it */
+ if (ctx->retries > 0) {
+ mm_obj_dbg (self, "couldn't load current bands: %s (will retry)", error->message);
+ g_clear_error (&error);
+ set_current_bands_reload_schedule (task);
+ goto out;
+ }
+
+ /* Errors when reloading bands won't be critical */
+ mm_obj_warn (self, "couldn't load current bands: %s", error->message);
+ g_clear_error (&error);
+ set_current_bands_complete_with_defaults (task);
+ goto out;
+ }
+
+ if ((ctx->bands_array->len == 1) && (g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY))
+ requested_bands = g_array_ref (ctx->supported_bands_array);
+ else
+ requested_bands = g_array_ref (ctx->bands_array);
+
+ /* Compare arrays */
+ if (!mm_common_bands_garray_cmp (current_bands, requested_bands)) {
+ gchar *requested_str;
+ gchar *current_str;
+
+ /* If we can retry, do it */
+ if (ctx->retries > 0) {
+ mm_obj_dbg (self, "reloaded current bands different to the requested ones (will retry)");
+ set_current_bands_reload_schedule (task);
+ goto out;
+ }
+
+ requested_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)requested_bands->data, requested_bands->len);
+ current_str = mm_common_build_bands_string ((const MMModemBand *)(gconstpointer)current_bands->data, current_bands->len);
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "reloaded current bands (%s) different to the requested ones (%s)",
+ current_str, requested_str);
+ g_free (requested_str);
+ g_free (current_str);
+ }
+
+ /* Store as current the last loaded ones and set operation result */
+ mm_common_bands_garray_sort (current_bands);
+ mm_gdbus_modem_set_current_bands (ctx->skeleton, mm_common_bands_garray_to_variant (current_bands));
+
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ out:
+ g_array_unref (requested_bands);
+ g_array_unref (current_bands);
+}
+
+static gboolean
+set_current_bands_reload (GTask *task)
+{
+ MMIfaceModem *self;
+ SetCurrentBandsContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->retries > 0);
+ ctx->retries--;
+
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands (
+ self,
+ (GAsyncReadyCallback)after_set_load_current_bands_ready,
+ task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+set_current_bands_reload_schedule (GTask *task)
+{
+ g_timeout_add_seconds (AFTER_SET_LOAD_CURRENT_BANDS_TIMEOUT_SECS,
+ (GSourceFunc) set_current_bands_reload,
+ task);
}
static void
set_current_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
- SetCurrentBandsContext *ctx)
+ GTask *task)
{
GError *error = NULL;
- if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish (self, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
- else {
- /* Never show just 'any' in the interface */
- if (ctx->bands_array->len == 1 &&
- g_array_index (ctx->bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
- GArray *supported_bands;
-
- supported_bands = (mm_common_bands_variant_to_garray (
- mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
- mm_gdbus_modem_set_current_bands (ctx->skeleton,
- mm_common_bands_garray_to_variant (supported_bands));
- g_array_unref (supported_bands);
- } else
- mm_gdbus_modem_set_current_bands (ctx->skeleton,
- mm_common_bands_garray_to_variant (ctx->bands_array));
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish) {
+ set_current_bands_reload (task);
+ return;
}
- set_current_bands_context_complete_and_free (ctx);
+ /* Default to the ones we requested */
+ set_current_bands_complete_with_defaults (task);
}
static gboolean
@@ -2203,7 +2750,7 @@ validate_bands (const GArray *supported_bands_array,
gchar *supported_bands_str;
supported_bands_str = (mm_common_build_bands_string (
- (const MMModemBand *)supported_bands_array->data,
+ (const MMModemBand *)(gconstpointer)supported_bands_array->data,
supported_bands_array->len));
g_set_error (error,
MM_CORE_ERROR,
@@ -2227,48 +2774,48 @@ mm_iface_modem_set_current_bands (MMIfaceModem *self,
gpointer user_data)
{
SetCurrentBandsContext *ctx;
- GArray *supported_bands_array;
GArray *current_bands_array;
GError *error = NULL;
gchar *bands_string;
+ GTask *task;
/* If setting allowed bands is not implemented, report an error */
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands_finish) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting allowed bands not supported");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ mm_iface_modem_set_current_bands,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Setting allowed bands not supported");
return;
}
/* Setup context */
ctx = g_slice_new0 (SetCurrentBandsContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_set_current_bands);
+ ctx->retries = AFTER_SET_LOAD_CURRENT_BANDS_RETRIES;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_bands_context_free);
+
g_object_get (self,
MM_IFACE_MODEM_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");
- set_current_bands_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array->data,
- bands_array->len);
+ bands_string = mm_common_build_bands_string ((const MMModemBand *)(gpointer)bands_array->data, bands_array->len);
/* Get list of supported bands */
- supported_bands_array = (mm_common_bands_variant_to_garray (
- mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
+ ctx->supported_bands_array = (mm_common_bands_variant_to_garray (
+ mm_gdbus_modem_get_supported_bands (ctx->skeleton)));
/* Set ctx->bands_array to target list of bands before comparing with current list
* of bands. If input list of bands contains only ANY, target list of bands is set
@@ -2277,8 +2824,8 @@ mm_iface_modem_set_current_bands (MMIfaceModem *self,
g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
guint i;
- for (i = 0; i < supported_bands_array->len; i++) {
- MMModemBand band = g_array_index (supported_bands_array, MMModemBand, i);
+ for (i = 0; i < ctx->supported_bands_array->len; i++) {
+ MMModemBand band = g_array_index (ctx->supported_bands_array, MMModemBand, i);
if (band != MM_MODEM_BAND_ANY &&
band != MM_MODEM_BAND_UNKNOWN) {
@@ -2286,7 +2833,7 @@ mm_iface_modem_set_current_bands (MMIfaceModem *self,
ctx->bands_array = g_array_sized_new (FALSE,
FALSE,
sizeof (MMModemBand),
- supported_bands_array->len);
+ ctx->supported_bands_array->len);
g_array_append_val (ctx->bands_array, band);
}
}
@@ -2299,13 +2846,11 @@ mm_iface_modem_set_current_bands (MMIfaceModem *self,
current_bands_array = (mm_common_bands_variant_to_garray (
mm_gdbus_modem_get_current_bands (ctx->skeleton)));
if (mm_common_bands_garray_cmp (ctx->bands_array, current_bands_array)) {
- mm_dbg ("Requested list of bands (%s) is equal to the current ones, skipping re-set",
- bands_string);
+ mm_obj_dbg (self, "requested list of bands (%s) is equal to the current ones, skipping re-set", bands_string);
g_free (bands_string);
- g_array_unref (supported_bands_array);
g_array_unref (current_bands_array);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_bands_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -2317,27 +2862,24 @@ mm_iface_modem_set_current_bands (MMIfaceModem *self,
}
/* Validate input list of bands */
- if (!validate_bands (supported_bands_array,
+ if (!validate_bands (ctx->supported_bands_array,
ctx->bands_array,
&error)) {
- mm_dbg ("Requested list of bands (%s) cannot be handled",
- bands_string);
+ mm_obj_dbg (self, "requested list of bands (%s) cannot be handled", bands_string);
g_free (bands_string);
- g_array_unref (supported_bands_array);
g_array_unref (current_bands_array);
- g_simple_async_result_take_error (ctx->result, error);
- set_current_bands_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- mm_dbg ("Setting new list of bands: '%s'", bands_string);
+ mm_obj_dbg (self, "setting new list of bands: %s", bands_string);
MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_bands (
self,
ctx->bands_array,
(GAsyncReadyCallback)set_current_bands_ready,
- ctx);
+ task);
- g_array_unref (supported_bands_array);
g_array_unref (current_bands_array);
g_free (bands_string);
}
@@ -2368,8 +2910,11 @@ handle_set_current_bands_ready (MMIfaceModem *self,
if (!mm_iface_modem_set_current_bands_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ else {
+ /* Bands updated: explicitly refresh signal and access technology */
+ mm_iface_modem_refresh_signal (self);
mm_gdbus_modem_complete_set_current_bands (ctx->skeleton, ctx->invocation);
+ }
handle_set_current_bands_context_free (ctx);
}
@@ -2380,7 +2925,6 @@ handle_set_current_bands_auth_ready (MMBaseModem *self,
HandleSetCurrentBandsContext *ctx)
{
GArray *bands_array;
- MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
@@ -2389,17 +2933,7 @@ handle_set_current_bands_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 set current bands: "
- "not initialized/unlocked yet");
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_DISABLED)) {
handle_set_current_bands_context_free (ctx);
return;
}
@@ -2437,23 +2971,22 @@ handle_set_current_bands (MmGdbusModem *skeleton,
/*****************************************************************************/
/* Set current modes */
+#define AFTER_SET_LOAD_CURRENT_MODES_RETRIES 5
+#define AFTER_SET_LOAD_CURRENT_MODES_TIMEOUT_SECS 1
+
typedef struct {
- MMIfaceModem *self;
MmGdbusModem *skeleton;
- GSimpleAsyncResult *result;
- MMModemMode allowed;
- MMModemMode preferred;
+ MMModemMode allowed;
+ MMModemMode preferred;
+ guint retries;
} SetCurrentModesContext;
static void
-set_current_modes_context_complete_and_free (SetCurrentModesContext *ctx)
+set_current_modes_context_free (SetCurrentModesContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
- g_free (ctx);
+ g_slice_free (SetCurrentModesContext, ctx);
}
gboolean
@@ -2461,66 +2994,146 @@ mm_iface_modem_set_current_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
+static void set_current_modes_reload_schedule (GTask *task);
+
static void
after_set_load_current_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
+ SetCurrentModesContext *ctx;
MMModemMode allowed = MM_MODEM_MODE_NONE;
MMModemMode preferred = MM_MODEM_MODE_NONE;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish (self,
res,
&allowed,
&preferred,
&error)) {
+ /* If we can retry, do it */
+ if (ctx->retries > 0) {
+ mm_obj_dbg (self, "couldn't load current allowed/preferred modes: %s", error->message);
+ g_error_free (error);
+ set_current_modes_reload_schedule (task);
+ return;
+ }
+
/* Errors when getting allowed/preferred won't be critical */
- mm_warn ("couldn't load current allowed/preferred modes: '%s'", error->message);
- g_error_free (error);
+ mm_obj_warn (self, "couldn't load current allowed/preferred modes: %s", error->message);
+ g_clear_error (&error);
/* If errors getting allowed modes, default to the ones we asked for */
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", ctx->allowed, ctx->preferred));
- } else
- mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred));
+ goto out;
+ }
+
+ /* Store as current the last loaded ones and set operation result */
+ mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred));
+
+ /* Compare modes. If the requested one was ANY, we won't consider an error if the
+ * result differs. */
+ if (((allowed != ctx->allowed) || (preferred != ctx->preferred)) && (ctx->allowed != MM_MODEM_MODE_ANY)) {
+ gchar *requested_allowed_str;
+ gchar *requested_preferred_str;
+ gchar *current_allowed_str;
+ gchar *current_preferred_str;
+
+ /* If we can retry, do it */
+ if (ctx->retries > 0) {
+ mm_obj_dbg (self, "reloaded current modes different to the requested ones (will retry)");
+ set_current_modes_reload_schedule (task);
+ return;
+ }
+
+ requested_allowed_str = mm_modem_mode_build_string_from_mask (ctx->allowed);
+ requested_preferred_str = mm_modem_mode_build_string_from_mask (ctx->preferred);
+ current_allowed_str = mm_modem_mode_build_string_from_mask (allowed);
+ current_preferred_str = mm_modem_mode_build_string_from_mask (preferred);
+
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "reloaded modes (allowed '%s' and preferred '%s') different "
+ "to the requested ones (allowed '%s' and preferred '%s')",
+ requested_allowed_str, requested_preferred_str,
+ current_allowed_str, current_preferred_str);
+
+ g_free (requested_allowed_str);
+ g_free (requested_preferred_str);
+ g_free (current_allowed_str);
+ g_free (current_preferred_str);
+ }
+
+out:
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
- /* Done */
- set_current_modes_context_complete_and_free (ctx);
+static gboolean
+set_current_modes_reload (GTask *task)
+{
+ MMIfaceModem *self;
+ SetCurrentModesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->retries > 0);
+ ctx->retries--;
+
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes (
+ self,
+ (GAsyncReadyCallback)after_set_load_current_modes_ready,
+ task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+set_current_modes_reload_schedule (GTask *task)
+{
+ g_timeout_add_seconds (AFTER_SET_LOAD_CURRENT_MODES_TIMEOUT_SECS,
+ (GSourceFunc) set_current_modes_reload,
+ task);
}
static void
set_current_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- SetCurrentModesContext *ctx)
+ GTask *task)
{
+ SetCurrentModesContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_modes &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_modes_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_modes (
- ctx->self,
- (GAsyncReadyCallback)after_set_load_current_modes_ready,
- ctx);
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish) {
+ set_current_modes_reload (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
/* Default to the ones we requested */
mm_gdbus_modem_set_current_modes (ctx->skeleton,
g_variant_new ("(uu)",
ctx->allowed,
ctx->preferred));
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -2535,37 +3148,39 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
MMModemMode current_allowed = MM_MODEM_MODE_ANY;
MMModemMode current_preferred = MM_MODEM_MODE_NONE;
guint i;
+ GTask *task;
/* If setting allowed modes is not implemented, report an error */
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes ||
!MM_IFACE_MODEM_GET_INTERFACE (self)->set_current_modes_finish) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting allowed modes not supported");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ mm_iface_modem_set_current_modes,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Setting allowed modes not supported");
return;
}
/* Setup context */
- ctx = g_new0 (SetCurrentModesContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_set_current_modes);
+ ctx = g_slice_new0 (SetCurrentModesContext);
+ ctx->retries = AFTER_SET_LOAD_CURRENT_MODES_RETRIES;
ctx->allowed = allowed;
ctx->preferred = preferred;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free);
+
g_object_get (self,
MM_IFACE_MODEM_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");
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
@@ -2575,12 +3190,12 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
/* 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,
- MM_CORE_ERROR_UNSUPPORTED,
- "Cannot change modes: only one combination supported");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Cannot change modes: only one combination supported");
+ g_object_unref (task);
g_array_unref (supported);
- set_current_modes_context_complete_and_free (ctx);
return;
}
@@ -2604,12 +3219,12 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
}
if (!matched) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "The given combination of allowed and preferred modes is not supported");
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "The given combination of allowed and preferred modes is not supported");
+ g_object_unref (task);
g_array_unref (supported);
- set_current_modes_context_complete_and_free (ctx);
return;
}
}
@@ -2623,8 +3238,8 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
&current_preferred);
if (current_allowed == allowed &&
current_preferred == preferred) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_current_modes_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
@@ -2635,16 +3250,15 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
preferred_str = mm_modem_mode_build_string_from_mask (preferred);
allowed_str = mm_modem_mode_build_string_from_mask (allowed);
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Preferred mode (%s) is not allowed (%s)",
- preferred_str,
- allowed_str);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Preferred mode (%s) is not allowed (%s)",
+ preferred_str,
+ allowed_str);
+ g_object_unref (task);
g_free (preferred_str);
g_free (allowed_str);
-
- set_current_modes_context_complete_and_free (ctx);
return;
}
@@ -2654,7 +3268,7 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
allowed,
preferred,
(GAsyncReadyCallback)set_current_modes_ready,
- ctx);
+ task);
}
typedef struct {
@@ -2683,8 +3297,11 @@ handle_set_current_modes_ready (MMIfaceModem *self,
if (!mm_iface_modem_set_current_modes_finish (self, res, &error))
g_dbus_method_invocation_take_error (ctx->invocation, error);
- else
+ else {
+ /* Modes updated: explicitly refresh signal and access technology */
+ mm_iface_modem_refresh_signal (self);
mm_gdbus_modem_complete_set_current_modes (ctx->skeleton, ctx->invocation);
+ }
handle_set_current_modes_context_free (ctx);
}
@@ -2694,7 +3311,6 @@ handle_set_current_modes_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleSetCurrentModesContext *ctx)
{
- MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
@@ -2703,17 +3319,7 @@ handle_set_current_modes_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 set current modes: "
- "not initialized/unlocked yet");
+ if (mm_iface_modem_abort_invocation_if_state_not_reached (ctx->self, ctx->invocation, MM_MODEM_STATE_DISABLED)) {
handle_set_current_modes_context_free (ctx);
return;
}
@@ -2761,7 +3367,7 @@ reinitialize_ready (MMBaseModem *self,
mm_base_modem_initialize_finish (self, res, &error);
if (error) {
- mm_warn ("Modem reinitialization failed: '%s'", error->message);
+ mm_obj_warn (self, "reinitialization failed: %s", error->message);
g_error_free (error);
}
}
@@ -2775,7 +3381,7 @@ restart_initialize_idle (MMIfaceModem *self)
mm_base_modem_initialize (MM_BASE_MODEM (self),
(GAsyncReadyCallback) reinitialize_ready,
NULL);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static void
@@ -2793,6 +3399,8 @@ set_lock_status (MMIfaceModem *self,
old_lock = mm_gdbus_modem_get_unlock_required (skeleton);
mm_gdbus_modem_set_unlock_required (skeleton, lock);
+ if (lock == MM_MODEM_LOCK_UNKNOWN)
+ mm_gdbus_modem_set_unlock_retries (skeleton, 0);
/* We don't care about SIM-PIN2/SIM-PUK2 since the device is
* operational without it. */
@@ -2829,12 +3437,50 @@ set_lock_status (MMIfaceModem *self,
}
}
-static void
-update_unlock_retries (MMIfaceModem *self,
- MMUnlockRetries *unlock_retries)
+MMModemLock
+mm_iface_modem_get_unlock_required (MMIfaceModem *self)
+{
+ MmGdbusModem *skeleton = NULL;
+ MMModemLock lock;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton) {
+ lock = mm_gdbus_modem_get_unlock_required (skeleton);
+ g_object_unref (skeleton);
+ } else
+ lock = MM_MODEM_LOCK_UNKNOWN;
+
+ return lock;
+}
+
+MMUnlockRetries *
+mm_iface_modem_get_unlock_retries (MMIfaceModem *self)
+{
+ MmGdbusModem *skeleton = NULL;
+ MMUnlockRetries *unlock_retries;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton) {
+ GVariant *dictionary;
+
+ dictionary = mm_gdbus_modem_get_unlock_retries (skeleton);
+ unlock_retries = mm_unlock_retries_new_from_dictionary (dictionary);
+ g_object_unref (skeleton);
+ } else
+ unlock_retries = mm_unlock_retries_new ();
+
+ return unlock_retries;
+}
+
+void
+mm_iface_modem_update_unlock_retries (MMIfaceModem *self,
+ MMUnlockRetries *unlock_retries)
{
MmGdbusModem *skeleton = NULL;
- GError *error = NULL;
GVariant *previous_dictionary;
MMUnlockRetries *previous_unlock_retries;
@@ -2847,18 +3493,13 @@ update_unlock_retries (MMIfaceModem *self,
previous_dictionary = mm_gdbus_modem_get_unlock_retries (skeleton);
previous_unlock_retries = mm_unlock_retries_new_from_dictionary (previous_dictionary);
- if (error) {
- mm_warn ("Couldn't build previous unlock retries: '%s'", error->message);
- g_error_free (error);
- } else {
- /* If they are different, update */
- if (!mm_unlock_retries_cmp (unlock_retries, previous_unlock_retries)) {
- GVariant *new_dictionary;
+ /* If they are different, update */
+ if (!mm_unlock_retries_cmp (unlock_retries, previous_unlock_retries)) {
+ GVariant *new_dictionary;
- new_dictionary = mm_unlock_retries_get_dictionary (unlock_retries);
- mm_gdbus_modem_set_unlock_retries (skeleton, new_dictionary);
- g_variant_unref (new_dictionary);
- }
+ new_dictionary = mm_unlock_retries_get_dictionary (unlock_retries);
+ mm_gdbus_modem_set_unlock_retries (skeleton, new_dictionary);
+ g_variant_unref (new_dictionary);
}
g_object_unref (previous_unlock_retries);
@@ -2868,28 +3509,23 @@ update_unlock_retries (MMIfaceModem *self,
typedef enum {
UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST = 0,
UPDATE_LOCK_INFO_CONTEXT_STEP_LOCK,
- UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES,
UPDATE_LOCK_INFO_CONTEXT_STEP_AFTER_UNLOCK,
+ UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES,
UPDATE_LOCK_INFO_CONTEXT_STEP_LAST
} UpdateLockInfoContextStep;
typedef struct {
- MMIfaceModem *self;
UpdateLockInfoContextStep step;
- GSimpleAsyncResult *result;
MmGdbusModem *skeleton;
MMModemLock lock;
GError *saved_error;
} UpdateLockInfoContext;
static void
-update_lock_info_context_complete_and_free (UpdateLockInfoContext *ctx)
+update_lock_info_context_free (UpdateLockInfoContext *ctx)
{
g_assert (ctx->saved_error == NULL);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_slice_free (UpdateLockInfoContext, ctx);
@@ -2900,61 +3536,73 @@ mm_iface_modem_update_lock_info_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;
+ GError *inner_error = NULL;
+ gssize value;
- return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCK_UNKNOWN;
+ }
+ return (MMModemLock)value;
}
-static void update_lock_info_context_step (UpdateLockInfoContext *ctx);
+static void update_lock_info_context_step (GTask *task);
static void
load_unlock_retries_ready (MMIfaceModem *self,
GAsyncResult *res,
- UpdateLockInfoContext *ctx)
+ GTask *task)
{
+ UpdateLockInfoContext *ctx;
GError *error = NULL;
MMUnlockRetries *unlock_retries;
unlock_retries = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish (self, res, &error);
if (!unlock_retries) {
- mm_warn ("Couldn't load unlock retries: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load unlock retries: %s", error->message);
g_error_free (error);
} else {
/* Update the dictionary in the DBus interface */
- update_unlock_retries (self, unlock_retries);
+ mm_iface_modem_update_unlock_retries (self, unlock_retries);
g_object_unref (unlock_retries);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- update_lock_info_context_step (ctx);
+ update_lock_info_context_step (task);
}
static void
modem_after_sim_unlock_ready (MMIfaceModem *self,
GAsyncResult *res,
- UpdateLockInfoContext *ctx)
+ GTask *task)
{
+ UpdateLockInfoContext *ctx;
GError *error = NULL;
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish (self, res, &error)) {
- mm_warn ("After SIM unlock failed setup: '%s'", error->message);
+ mm_obj_warn (self, "after SIM unlock failed: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- update_lock_info_context_step (ctx);
+ update_lock_info_context_step (task);
}
static void
internal_load_unlock_required_ready (MMIfaceModem *self,
GAsyncResult *res,
- UpdateLockInfoContext *ctx)
+ GTask *task)
{
+ UpdateLockInfoContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
ctx->lock = internal_load_unlock_required_finish (self, res, &error);
if (error) {
/* Treat several SIM related, serial and other core errors as critical
@@ -2962,11 +3610,11 @@ internal_load_unlock_required_ready (MMIfaceModem *self,
* state. */
if (error->domain == MM_SERIAL_ERROR ||
g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED)) {
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED)) {
ctx->saved_error = error;
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
- update_lock_info_context_step (ctx);
+ update_lock_info_context_step (task);
return;
}
@@ -2983,16 +3631,16 @@ internal_load_unlock_required_ready (MMIfaceModem *self,
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);
+ update_lock_info_context_step (task);
return;
}
/* For mixed 3GPP+3GPP2 devices, skip SIM errors */
- mm_dbg ("Skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
+ mm_obj_dbg (self, "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);
+ mm_obj_dbg (self, "couldn't check if unlock required: %s", error->message);
g_error_free (error);
ctx->lock = MM_MODEM_LOCK_UNKNOWN;
}
@@ -3000,12 +3648,18 @@ internal_load_unlock_required_ready (MMIfaceModem *self,
/* Go on to next step */
ctx->step++;
- update_lock_info_context_step (ctx);
+ update_lock_info_context_step (task);
}
static void
-update_lock_info_context_step (UpdateLockInfoContext *ctx)
+update_lock_info_context_step (GTask *task)
{
+ MMIfaceModem *self;
+ UpdateLockInfoContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
switch (ctx->step) {
case UPDATE_LOCK_INFO_CONTEXT_STEP_FIRST:
/* We need the skeleton around */
@@ -3014,90 +3668,79 @@ update_lock_info_context_step (UpdateLockInfoContext *ctx)
MM_CORE_ERROR_FAILED,
"Couldn't get interface skeleton");
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
- update_lock_info_context_step (ctx);
+ update_lock_info_context_step (task);
return;
}
-
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case UPDATE_LOCK_INFO_CONTEXT_STEP_LOCK:
/* 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) {
- internal_load_unlock_required (
- ctx->self,
- (GAsyncReadyCallback)internal_load_unlock_required_ready,
- ctx);
- return;
- }
-
- /* Just assume that no lock is required */
- ctx->lock = MM_MODEM_LOCK_NONE;
- }
-
- /* Fall down to next step */
- ctx->step++;
-
- case UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES:
- /* Load unlock retries if possible */
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_retries &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_retries_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_retries (
- ctx->self,
- (GAsyncReadyCallback)load_unlock_retries_ready,
- ctx);
+ internal_load_unlock_required (
+ self,
+ (GAsyncReadyCallback)internal_load_unlock_required_ready,
+ task);
return;
}
-
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case UPDATE_LOCK_INFO_CONTEXT_STEP_AFTER_UNLOCK:
/* If we get that no lock is required, run the after SIM unlock step
* in order to wait for the SIM to get ready. Skip waiting on
* CDMA-only modems where we don't support a SIM. */
- if (!mm_iface_modem_is_cdma_only (ctx->self) &&
+ if (!mm_iface_modem_is_cdma_only (self) &&
(ctx->lock == MM_MODEM_LOCK_NONE ||
ctx->lock == MM_MODEM_LOCK_SIM_PIN2 ||
ctx->lock == MM_MODEM_LOCK_SIM_PUK2)) {
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_sim_unlock != NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_sim_unlock_finish != NULL) {
- mm_dbg ("SIM is ready, running after SIM unlock step...");
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_sim_unlock (
- ctx->self,
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock != NULL &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock_finish != NULL) {
+ mm_obj_dbg (self, "SIM is ready, running after SIM unlock step...");
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_sim_unlock (
+ self,
(GAsyncReadyCallback)modem_after_sim_unlock_ready,
- ctx);
+ task);
return;
}
/* If no way to run after SIM unlock step, we're done */
- mm_dbg ("SIM is ready, and no need for the after SIM unlock step...");
+ mm_obj_dbg (self, "SIM is ready, and no need for the after SIM unlock step...");
}
+ ctx->step++;
+ /* fall-through */
- /* Fall down to next step */
+ case UPDATE_LOCK_INFO_CONTEXT_STEP_RETRIES:
+ /* Load unlock retries if possible */
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_retries (
+ self,
+ (GAsyncReadyCallback)load_unlock_retries_ready,
+ task);
+ return;
+ }
ctx->step++;
+ /* fall-through */
case UPDATE_LOCK_INFO_CONTEXT_STEP_LAST:
if (ctx->saved_error) {
+ set_lock_status (self, ctx->skeleton, MM_MODEM_LOCK_UNKNOWN);
/* Return saved error */
- g_simple_async_result_take_error (ctx->result, ctx->saved_error);
+ g_task_return_error (task, ctx->saved_error);
ctx->saved_error = NULL;
} else {
/* Update lock status and modem status if needed */
- set_lock_status (ctx->self, ctx->skeleton, ctx->lock);
-
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (ctx->lock),
- NULL);
+ set_lock_status (self, ctx->skeleton, ctx->lock);
+ g_task_return_int (task, ctx->lock);
}
- update_lock_info_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
default:
- g_assert_not_reached();
+ g_assert_not_reached ();
}
}
@@ -3108,307 +3751,306 @@ mm_iface_modem_update_lock_info (MMIfaceModem *self,
gpointer user_data)
{
UpdateLockInfoContext *ctx;
+ GTask *task;
ctx = g_slice_new0 (UpdateLockInfoContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_update_lock_info);
- g_object_get (ctx->self,
- MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
- NULL);
/* If the given lock is known, we will avoid re-asking for it */
ctx->lock = known_lock;
- update_lock_info_context_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)update_lock_info_context_free);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton,
+ NULL);
+
+ if (!ctx->skeleton) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
+ return;
+ }
+
+ update_lock_info_context_step (task);
}
/*****************************************************************************/
/* Set power state sequence */
+typedef enum {
+ SET_POWER_STATE_STEP_FIRST,
+ SET_POWER_STATE_STEP_LOAD,
+ SET_POWER_STATE_STEP_CHECK,
+ SET_POWER_STATE_STEP_UPDATE,
+ SET_POWER_STATE_STEP_FCC_UNLOCK,
+ SET_POWER_STATE_STEP_AFTER_UPDATE,
+ SET_POWER_STATE_STEP_LAST,
+} SetPowerStateStep;
+
typedef struct {
- MMIfaceModem *self;
- GSimpleAsyncResult *result;
- MmGdbusModem *skeleton;
- MMModemPowerState power_state;
- MMModemPowerState previous_cached_power_state;
- MMModemPowerState previous_real_power_state;
+ SetPowerStateStep step;
+ MmGdbusModem *skeleton;
+ GError *saved_error;
+ gboolean fcc_unlock_attempted;
+ MMModemPowerState requested_power_state;
+ MMModemPowerState previous_cached_power_state;
+ MMModemPowerState previous_real_power_state;
+
+ void (*requested_power_setup) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*requested_power_setup_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
} SetPowerStateContext;
static void
-set_power_state_context_complete_and_free (SetPowerStateContext *ctx)
+set_power_state_context_free (SetPowerStateContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->self);
- g_object_unref (ctx->result);
+ g_assert (!ctx->saved_error);
if (ctx->skeleton)
g_object_unref (ctx->skeleton);
g_slice_free (SetPowerStateContext, ctx);
}
gboolean
-mm_iface_modem_set_power_state_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+mm_iface_modem_set_power_state_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
+static void set_power_state_step (GTask *task);
+
static void
modem_after_power_up_ready (MMIfaceModem *self,
GAsyncResult *res,
- SetPowerStateContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ SetPowerStateContext *ctx;
- MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish (self, res, &error);
- if (error)
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_power_state_context_complete_and_free (ctx);
+ ctx = g_task_get_task_data (task);
+ g_assert (!ctx->saved_error);
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish (self, res, &ctx->saved_error);
+ if (ctx->saved_error)
+ mm_obj_dbg (self, "failure running after power up step: %s", ctx->saved_error->message);
+
+ ctx->step++;
+ set_power_state_step (task);
}
static void
-modem_power_up_ready (MMIfaceModem *self,
- GAsyncResult *res,
- SetPowerStateContext *ctx)
+fcc_unlock_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
-
- MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish (self, res, &error);
- if (error) {
- /* If the real and cached ones are different, set the real one */
- if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
- g_simple_async_result_take_error (ctx->result, error);
- set_power_state_context_complete_and_free (ctx);
- return;
- }
-
- mm_dbg ("Modem set in full-power mode...");
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state);
+ SetPowerStateContext *ctx;
+ g_autoptr(GError) error = NULL;
- /* If we have something to do just after power-up, do it */
- if (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up &&
- MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up (
- self,
- (GAsyncReadyCallback)modem_after_power_up_ready,
- ctx);
- return;
- }
+ ctx = g_task_get_task_data (task);
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock_finish (self, res, &error))
+ mm_obj_dbg (self, "couldn't run FCC unlock: %s", error->message);
- /* Otherwise, we're done */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_power_state_context_complete_and_free (ctx);
+ /* always retry, even on reported error */
+ ctx->step = SET_POWER_STATE_STEP_UPDATE;
+ set_power_state_step (task);
}
static void
-modem_power_down_ready (MMIfaceModem *self,
- GAsyncResult *res,
- SetPowerStateContext *ctx)
+requested_power_setup_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ SetPowerStateContext *ctx;
- MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (self, res, &error);
- if (error) {
- /* If the real and cached ones are different, set the real one */
- if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
- g_simple_async_result_take_error (ctx->result, error);
- } else {
- mm_dbg ("Modem set in low-power mode...");
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- }
+ ctx = g_task_get_task_data (task);
+ g_assert (!ctx->saved_error);
+ if (!ctx->requested_power_setup_finish (self, res, &ctx->saved_error))
+ mm_obj_dbg (self, "couldn't update power state: %s", ctx->saved_error->message);
- set_power_state_context_complete_and_free (ctx);
+ ctx->step++;
+ set_power_state_step (task);
}
static void
-modem_power_off_ready (MMIfaceModem *self,
- GAsyncResult *res,
- SetPowerStateContext *ctx)
+set_power_state_load_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- GError *error = NULL;
+ SetPowerStateContext *ctx;
+ g_autoptr(GError) error = NULL;
- MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off_finish (self, res, &error);
+ ctx = g_task_get_task_data (task);
+
+ ctx->previous_real_power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (self, res, &error);
if (error) {
- /* If the real and cached ones are different, set the real one */
- if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
- g_simple_async_result_take_error (ctx->result, error);
- } else {
- mm_info ("Modem powered off... may no longer be accessible");
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ mm_obj_dbg (self, "couldn't reload current power state: %s", error->message);
+ /* Default to the cached one */
+ ctx->previous_real_power_state = ctx->previous_cached_power_state;
}
- set_power_state_context_complete_and_free (ctx);
+ ctx->step++;
+ set_power_state_step (task);
}
static void
-set_power_state (SetPowerStateContext *ctx)
-{
- /* Already done if we're in the desired power state */
- if (ctx->previous_real_power_state == ctx->power_state) {
- mm_dbg ("No need to change power state: already in '%s' power state",
- mm_modem_power_state_get_string (ctx->power_state));
- /* If the real and cached ones are different, set the real one */
- if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
- mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- set_power_state_context_complete_and_free (ctx);
- return;
- }
+set_power_state_step (GTask *task)
+{
+ MMIfaceModem *self;
+ SetPowerStateContext *ctx;
- /* Don't allow trying to recover from a power off */
- if (ctx->previous_real_power_state == MM_MODEM_POWER_STATE_OFF) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot recover from a power off");
- set_power_state_context_complete_and_free (ctx);
- return;
- }
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- /* Supported transitions:
- * UNKNOWN|LOW --> ON
- * ON --> LOW
- * ON|LOW --> OFF
- */
+ switch (ctx->step) {
+ case SET_POWER_STATE_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
- /* Fully powering off the modem? */
- if (ctx->power_state == MM_MODEM_POWER_STATE_OFF) {
- /* Error if unsupported */
- if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_off ||
- !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_off_finish) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Powering off is not supported by this modem");
- set_power_state_context_complete_and_free (ctx);
+ case SET_POWER_STATE_STEP_LOAD:
+ /* We cannot really rely on the power state value that we had cached before,
+ * as the real power status of the modem may also be changed by rfkill. So,
+ * before updating the current power state, re-check which is the real power
+ * state. */
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (self, (GAsyncReadyCallback)set_power_state_load_ready, task);
+ return;
+ }
+ /* If there is no way to load power state, just keep on assuming the cached
+ * one is also the real one */
+ ctx->previous_real_power_state = ctx->previous_cached_power_state;
+ ctx->step++;
+ /* fall-through */
+
+ case SET_POWER_STATE_STEP_CHECK:
+ /* Already done if we're in the desired power state */
+ if (ctx->previous_real_power_state == ctx->requested_power_state) {
+ mm_obj_dbg (self, "no need to change power state: already '%s'",
+ mm_modem_power_state_get_string (ctx->requested_power_state));
+ ctx->step = SET_POWER_STATE_STEP_LAST;
+ set_power_state_step (task);
return;
}
+ ctx->step++;
+ /* fall-through */
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_off (
- MM_IFACE_MODEM (ctx->self),
- (GAsyncReadyCallback)modem_power_off_ready,
- ctx);
- return;
- }
+ case SET_POWER_STATE_STEP_UPDATE:
+ mm_obj_dbg (self, "updating power state: '%s'...", mm_modem_power_state_get_string (ctx->requested_power_state));
- /* Going into low power mode? */
- if (ctx->power_state == MM_MODEM_POWER_STATE_LOW) {
/* Error if unsupported */
- if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down ||
- !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down_finish) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Going into low-power mode is not supported by this modem");
- set_power_state_context_complete_and_free (ctx);
+ if (!ctx->requested_power_setup || !ctx->requested_power_setup_finish) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Requested power transition is not supported by this modem");
+ g_object_unref (task);
return;
}
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down (
- MM_IFACE_MODEM (ctx->self),
- (GAsyncReadyCallback)modem_power_down_ready,
- ctx);
+ ctx->requested_power_setup (self, (GAsyncReadyCallback)requested_power_setup_ready, task);
return;
- }
- /* Going out of low power mode? */
- if (ctx->power_state == MM_MODEM_POWER_STATE_ON) {
- /* Error if unsupported */
- if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up ||
- !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up_finish) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Going into full-power mode is not supported by this modem");
- set_power_state_context_complete_and_free (ctx);
+ case SET_POWER_STATE_STEP_FCC_UNLOCK:
+ /* The FCC unlock operation and update retry should only be done
+ * if requested by the implementation with MM_CORE_ERROR_RETRY. */
+ if ((ctx->requested_power_state == MM_MODEM_POWER_STATE_ON) &&
+ ctx->saved_error &&
+ g_error_matches (ctx->saved_error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY) &&
+ !ctx->fcc_unlock_attempted &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock_finish) {
+ mm_obj_dbg (self, "attempting fcc unlock...");
+ ctx->fcc_unlock_attempted = TRUE;
+ g_clear_error (&ctx->saved_error);
+ MM_IFACE_MODEM_GET_INTERFACE (self)->fcc_unlock (self, (GAsyncReadyCallback)fcc_unlock_ready, task);
return;
}
+ ctx->step++;
+ /* fall-through */
+
+ case SET_POWER_STATE_STEP_AFTER_UPDATE:
+ if ((ctx->requested_power_state == MM_MODEM_POWER_STATE_ON) &&
+ !ctx->saved_error &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish) {
+ mm_obj_dbg (self, "running after power up operation...");
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up (self, (GAsyncReadyCallback)modem_after_power_up_ready, task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up (
- MM_IFACE_MODEM (ctx->self),
- (GAsyncReadyCallback)modem_power_up_ready,
- ctx);
+ case SET_POWER_STATE_STEP_LAST:
+ if (ctx->saved_error) {
+ /* If the real and cached ones are different, set the real one */
+ if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
+ mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
+ g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
+ } else {
+ mm_obj_info (self, "power state updated: %s", mm_modem_power_state_get_string (ctx->requested_power_state));
+ mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->requested_power_state);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
return;
- }
-
- g_assert_not_reached ();
-}
-
-static void
-set_power_state_load_ready (MMIfaceModem *self,
- GAsyncResult *res,
- SetPowerStateContext *ctx)
-{
- GError *error = NULL;
- ctx->previous_real_power_state = MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish (self, res, &error);
- if (error) {
- g_debug ("Couldn't reload current power state: %s", error->message);
- g_error_free (error);
- /* Default to the cached one */
- ctx->previous_real_power_state = ctx->previous_cached_power_state;
+ default:
+ g_assert_not_reached ();
}
-
- /* And keep on */
- set_power_state (ctx);
}
void
-mm_iface_modem_set_power_state (MMIfaceModem *self,
- MMModemPowerState power_state,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_iface_modem_set_power_state (MMIfaceModem *self,
+ MMModemPowerState power_state,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
SetPowerStateContext *ctx;
+ GTask *task;
ctx = g_slice_new0 (SetPowerStateContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_set_power_state);
- ctx->power_state = power_state;
- g_object_get (ctx->self,
+ ctx->step = SET_POWER_STATE_STEP_FIRST;
+ ctx->requested_power_state = power_state;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_power_state_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_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");
- set_power_state_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
-
ctx->previous_cached_power_state = mm_gdbus_modem_get_power_state (ctx->skeleton);
- /* We cannot really rely on the power state value that we had cached before,
- * as the real power status of the modem may also be changed by rfkill. So,
- * before updating the current power state, re-check which is the real power
- * state. */
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state (
- ctx->self,
- (GAsyncReadyCallback)set_power_state_load_ready,
- ctx);
- return;
+ /* Setup requested operation */
+ switch (ctx->requested_power_state) {
+ case MM_MODEM_POWER_STATE_OFF:
+ ctx->requested_power_setup = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off;
+ ctx->requested_power_setup_finish = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off_finish;
+ break;
+ case MM_MODEM_POWER_STATE_LOW:
+ ctx->requested_power_setup = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down;
+ ctx->requested_power_setup_finish = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish;
+ break;
+ case MM_MODEM_POWER_STATE_ON:
+ ctx->requested_power_setup = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up;
+ ctx->requested_power_setup_finish = MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish;
+ break;
+ case MM_MODEM_POWER_STATE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
}
- /* If there is no way to load power state, just keep on assuming the cached
- * one is also the real one */
- ctx->previous_real_power_state = ctx->previous_cached_power_state;
- set_power_state (ctx);
+ set_power_state_step (task);
}
/*****************************************************************************/
@@ -3416,10 +4058,10 @@ mm_iface_modem_set_power_state (MMIfaceModem *self,
gboolean
mm_iface_modem_disable_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -3427,152 +4069,107 @@ mm_iface_modem_disable (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *result;
+ GTask *task;
/* Just complete, nothing to do */
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_iface_modem_disable);
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
/*****************************************************************************/
/* MODEM ENABLING */
typedef struct _EnablingContext EnablingContext;
-static void interface_enabling_step (EnablingContext *ctx);
+static void interface_enabling_step (GTask *task);
typedef enum {
ENABLING_STEP_FIRST,
ENABLING_STEP_SET_POWER_STATE,
+ ENABLING_STEP_CHECK_FOR_SIM_SWAP,
ENABLING_STEP_FLOW_CONTROL,
- ENABLING_STEP_SUPPORTED_CHARSETS,
- ENABLING_STEP_CHARSET,
ENABLING_STEP_LAST
} EnablingStep;
struct _EnablingContext {
- MMIfaceModem *self;
EnablingStep step;
- MMModemCharset supported_charsets;
- const MMModemCharset *current_charset;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModem *skeleton;
};
static void
-enabling_context_complete_and_free (EnablingContext *ctx)
+enabling_context_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_enable_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
enabling_set_power_state_ready (MMIfaceModem *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
if (!mm_iface_modem_set_power_state_finish (self, res, &error)) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-setup_flow_control_ready (MMIfaceModem *self,
+check_for_sim_swap_ready (MMIfaceModem *self,
GAsyncResult *res,
- EnablingContext *ctx)
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
- MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish (self, res, &error);
- if (error) {
- g_simple_async_result_take_error (ctx->result, error);
- enabling_context_complete_and_free (ctx);
- return;
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish (self, res, &error)) {
+ mm_obj_warn (self, "failed to check if SIM was swapped: %s", error->message);
+ g_error_free (error);
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static void
-load_supported_charsets_ready (MMIfaceModem *self,
- GAsyncResult *res,
- EnablingContext *ctx)
+setup_flow_control_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
+ EnablingContext *ctx;
GError *error = NULL;
- ctx->supported_charsets =
- MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error);
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish (self, res, &error);
if (error) {
- mm_warn ("couldn't load Supported Charsets: '%s'", error->message);
- g_error_free (error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
/* Go on to next step */
+ ctx = g_task_get_task_data (task);
ctx->step++;
- interface_enabling_step (ctx);
-}
-
-static void
-setup_charset_ready (MMIfaceModem *self,
- GAsyncResult *res,
- EnablingContext *ctx)
-{
- GError *error = NULL;
-
- if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) {
- mm_dbg ("couldn't set charset '%s': '%s'",
- mm_modem_charset_to_string (*ctx->current_charset),
- error->message);
- g_error_free (error);
-
- /* Will retry step with some other charset type */
- } else
- /* Done, Go on to next step */
- ctx->step++;
-
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
static const MMModemCharset best_charsets[] = {
@@ -3585,94 +4182,65 @@ static const MMModemCharset best_charsets[] = {
};
static void
-interface_enabling_step (EnablingContext *ctx)
+interface_enabling_step (GTask *task)
{
+ MMIfaceModem *self;
+ EnablingContext *ctx;
+
/* Don't run new steps if we're cancelled */
- if (enabling_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
return;
+ }
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
switch (ctx->step) {
case ENABLING_STEP_FIRST:
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case ENABLING_STEP_SET_POWER_STATE:
- mm_iface_modem_set_power_state (ctx->self,
+ mm_iface_modem_set_power_state (self,
MM_MODEM_POWER_STATE_ON,
(GAsyncReadyCallback)enabling_set_power_state_ready,
- ctx);
+ task);
return;
- case ENABLING_STEP_FLOW_CONTROL:
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control (
- ctx->self,
- (GAsyncReadyCallback)setup_flow_control_ready,
- ctx);
+ case ENABLING_STEP_CHECK_FOR_SIM_SWAP:
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->check_for_sim_swap (
+ self,
+ NULL,
+ (GAsyncReadyCallback)check_for_sim_swap_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
- case ENABLING_STEP_SUPPORTED_CHARSETS:
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_charsets &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_charsets_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_charsets (
- ctx->self,
- (GAsyncReadyCallback)load_supported_charsets_ready,
- ctx);
- return;
- }
- /* Fall down to next step */
- ctx->step++;
-
- case ENABLING_STEP_CHARSET:
- /* Only try to set charsets if we were able to load supported ones */
- if (ctx->supported_charsets > 0 &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_charset &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_charset_finish) {
- gboolean next_to_try = FALSE;
-
- while (!next_to_try) {
- if (!ctx->current_charset)
- /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
- ctx->current_charset = &best_charsets[0];
- else
- /* Try with the next one */
- ctx->current_charset++;
-
- if (*ctx->current_charset == MM_MODEM_CHARSET_UNKNOWN)
- break;
-
- if (ctx->supported_charsets & (*ctx->current_charset))
- next_to_try = TRUE;
- }
-
- if (next_to_try) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_charset (
- ctx->self,
- *ctx->current_charset,
- (GAsyncReadyCallback)setup_charset_ready,
- ctx);
- return;
- }
-
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Failed to find a usable modem character set");
- enabling_context_complete_and_free (ctx);
+ case ENABLING_STEP_FLOW_CONTROL:
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_flow_control (
+ self,
+ (GAsyncReadyCallback)setup_flow_control_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
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);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -3685,52 +4253,255 @@ mm_iface_modem_enable (MMIfaceModem *self,
gpointer user_data)
{
EnablingContext *ctx;
+ GTask *task;
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_enable);
ctx->step = ENABLING_STEP_FIRST;
- g_object_get (ctx->self,
+
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
+
+ g_object_get (self,
MM_IFACE_MODEM_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);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't get interface skeleton");
+ g_object_unref (task);
return;
}
- interface_enabling_step (ctx);
+ interface_enabling_step (task);
}
/*****************************************************************************/
+/* MODEM SYNCHRONIZATION */
+
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+typedef struct _SyncingContext SyncingContext;
+static void interface_syncing_step (GTask *task);
+
+typedef enum {
+ SYNCING_STEP_FIRST,
+ SYNCING_STEP_DETECT_SIM_SWAP,
+ SYNCING_STEP_REFRESH_SIM_LOCK,
+ SYNCING_STEP_REFRESH_SIGNAL_STRENGTH,
+ SYNCING_STEP_REFRESH_BEARERS,
+ SYNCING_STEP_LAST
+} SyncingStep;
+
+struct _SyncingContext {
+ SyncingStep step;
+};
+
+gboolean
+mm_iface_modem_sync_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sync_sim_lock_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ g_autoptr (GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error))
+ mm_obj_warn (self, "checking sim lock status failed: %s", error->message);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+sync_detect_sim_swap_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SyncingContext *ctx;
+ g_autoptr (GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_iface_modem_check_for_sim_swap_finish (self, res, &error))
+ mm_obj_warn (self, "checking sim swap failed: %s", error->message);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+sync_all_bearers_ready (MMBearerList *bearer_list,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMIfaceModem *self;
+ SyncingContext *ctx;
+ g_autoptr (GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_bearer_list_sync_all_bearers_finish (bearer_list, res, &error))
+ mm_obj_warn (self, "synchronizing all bearer status failed: %s", error->message);
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_syncing_step (task);
+}
+
+static void
+reload_bearers (GTask *task)
+{
+ MMIfaceModem *self;
+ SyncingContext *ctx;
+ g_autoptr(MMBearerList) bearer_list = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_object_get (self,
+ MM_IFACE_MODEM_BEARER_LIST, &bearer_list,
+ NULL);
+
+ if (!bearer_list) {
+ /* Go on to next step */
+ ctx->step++;
+ interface_syncing_step (task);
+ return;
+ }
+
+ mm_bearer_list_sync_all_bearers (bearer_list,
+ (GAsyncReadyCallback)sync_all_bearers_ready,
+ task);
+}
+
+static void
+interface_syncing_step (GTask *task)
+{
+ MMIfaceModem *self;
+ SyncingContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SYNCING_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_DETECT_SIM_SWAP:
+ /*
+ * Detect possible SIM swaps.
+ * Checking lock status in all cases after possible SIM swaps are detected.
+ */
+ mm_iface_modem_check_for_sim_swap (
+ self,
+ 0,
+ NULL,
+ (GAsyncReadyCallback)sync_detect_sim_swap_ready,
+ task);
+ return;
+
+ case SYNCING_STEP_REFRESH_SIM_LOCK:
+ /*
+ * Refresh SIM lock status and wait until complete.
+ */
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required (
+ self,
+ FALSE,
+ (GAsyncReadyCallback)sync_sim_lock_ready,
+ task);
+ return;
+
+ case SYNCING_STEP_REFRESH_SIGNAL_STRENGTH:
+ /*
+ * Restart the signal strength and access technologies refresh sequence.
+ */
+ mm_iface_modem_refresh_signal (self);
+ ctx->step++;
+ /* fall through */
+
+ case SYNCING_STEP_REFRESH_BEARERS:
+ /*
+ * Refresh bearers.
+ */
+ reload_bearers (task);
+ return;
+
+ case SYNCING_STEP_LAST:
+ /* We are done without errors! */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+void
+mm_iface_modem_sync (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SyncingContext *ctx;
+ GTask *task;
+
+ /* Create SyncingContext */
+ ctx = g_new0 (SyncingContext, 1);
+ ctx->step = SYNCING_STEP_FIRST;
+
+ /* Create sync steps task and execute it */
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
+ interface_syncing_step (task);
+}
+
+#endif
+
+/*****************************************************************************/
/* MODEM INITIALIZATION */
typedef struct _InitializationContext InitializationContext;
-static void interface_initialization_step (InitializationContext *ctx);
+static void interface_initialization_step (GTask *task);
typedef enum {
INITIALIZATION_STEP_FIRST,
INITIALIZATION_STEP_CURRENT_CAPABILITIES,
INITIALIZATION_STEP_SUPPORTED_CAPABILITIES,
+ INITIALIZATION_STEP_SUPPORTED_CHARSETS,
+ INITIALIZATION_STEP_CHARSET,
INITIALIZATION_STEP_BEARERS,
INITIALIZATION_STEP_MANUFACTURER,
INITIALIZATION_STEP_MODEL,
INITIALIZATION_STEP_REVISION,
+ INITIALIZATION_STEP_CARRIER_CONFIG,
+ INITIALIZATION_STEP_HARDWARE_REVISION,
INITIALIZATION_STEP_EQUIPMENT_ID,
INITIALIZATION_STEP_DEVICE_ID,
INITIALIZATION_STEP_SUPPORTED_MODES,
INITIALIZATION_STEP_SUPPORTED_BANDS,
INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES,
INITIALIZATION_STEP_POWER_STATE,
+ INITIALIZATION_STEP_SIM_HOT_SWAP,
+ INITIALIZATION_STEP_SIM_SLOTS,
INITIALIZATION_STEP_UNLOCK_REQUIRED,
INITIALIZATION_STEP_SIM,
+ INITIALIZATION_STEP_SETUP_CARRIER_CONFIG,
INITIALIZATION_STEP_OWN_NUMBERS,
INITIALIZATION_STEP_CURRENT_MODES,
INITIALIZATION_STEP_CURRENT_BANDS,
@@ -3738,68 +4509,46 @@ typedef enum {
} InitializationStep;
struct _InitializationContext {
- MMIfaceModem *self;
InitializationStep step;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
MmGdbusModem *skeleton;
+ MMModemCharset supported_charsets;
+ const MMModemCharset *current_charset;
GError *fatal_error;
};
static void
-initialization_context_complete_and_free (InitializationContext *ctx)
+initialization_context_free (InitializationContext *ctx)
{
g_assert (ctx->fatal_error == NULL);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->self);
- g_object_unref (ctx->result);
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;
-
- /* 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,
- "Interface initialization cancelled");
- initialization_context_complete_and_free (ctx);
- return TRUE;
-}
-
#undef STR_REPLY_READY_FN
#define STR_REPLY_READY_FN(NAME,DISPLAY) \
static void \
load_##NAME##_ready (MMIfaceModem *self, \
GAsyncResult *res, \
- InitializationContext *ctx) \
+ GTask *task) \
{ \
+ InitializationContext *ctx; \
GError *error = NULL; \
gchar *val; \
\
+ ctx = g_task_get_task_data (task); \
+ \
val = MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error); \
mm_gdbus_modem_set_##NAME (ctx->skeleton, val); \
g_free (val); \
\
if (error) { \
- mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
+ mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
ctx->step++; \
- interface_initialization_step (ctx); \
+ interface_initialization_step (task); \
}
#undef UINT_REPLY_READY_FN
@@ -3807,31 +4556,37 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct
static void \
load_##NAME##_ready (MMIfaceModem *self, \
GAsyncResult *res, \
- InitializationContext *ctx) \
+ GTask *task) \
{ \
+ InitializationContext *ctx; \
GError *error = NULL; \
\
+ ctx = g_task_get_task_data (task); \
+ \
mm_gdbus_modem_set_##NAME ( \
ctx->skeleton, \
MM_IFACE_MODEM_GET_INTERFACE (self)->load_##NAME##_finish (self, res, &error)); \
\
if (error) { \
- mm_warn ("couldn't load %s: '%s'", DISPLAY, error->message); \
+ mm_obj_warn (self, "couldn't load %s: %s", DISPLAY, error->message); \
g_error_free (error); \
} \
\
/* Go on to next step */ \
ctx->step++; \
- interface_initialization_step (ctx); \
+ interface_initialization_step (task); \
}
static void
current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
internal_load_unlock_required_finish (self, res, &error);
if (error) {
/* These SIM errors indicate that there is NO valid SIM available. So,
@@ -3847,10 +4602,9 @@ current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
MMModemCapability caps;
- mm_dbg ("Multimode device without SIM, no 3GPP capabilities");
+ mm_obj_dbg (self, "multimode device without SIM, no 3GPP capabilities");
caps = mm_gdbus_modem_get_current_capabilities (ctx->skeleton);
- caps &= ~MM_MODEM_CAPABILITY_GSM_UMTS;
- caps &= ~MM_MODEM_CAPABILITY_LTE;
+ caps &= ~MM_MODEM_CAPABILITY_3GPP;
/* CDMA-EVDO must still be around */
g_assert (caps & MM_MODEM_CAPABILITY_CDMA_EVDO);
@@ -3862,27 +4616,60 @@ current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
/* Keep on */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_current_capabilities_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
MMModemCapability caps;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
caps = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities_finish (self, res, &error);
if (error) {
g_propagate_error (&ctx->fatal_error, error);
g_prefix_error (&ctx->fatal_error, "couldn't load current capabilities: ");
/* Jump to the last step */
ctx->step = INITIALIZATION_STEP_LAST;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
return;
}
+ /* By default CS/PS/CDMA1X/EVDO network registration checks are the only
+ * ones enabled, so fix them up based on capabilities, enabling EPS or 5GS
+ * checks if required, and disabling CS/PS/CDMA1X/EVDO if required. */
+ if (caps & MM_MODEM_CAPABILITY_LTE) {
+ mm_obj_dbg (self, "setting EPS network as supported");
+ g_object_set (G_OBJECT (self),
+ MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, TRUE,
+ NULL);
+ }
+ if (caps & MM_MODEM_CAPABILITY_5GNR) {
+ mm_obj_dbg (self, "setting 5GS network as supported");
+ g_object_set (G_OBJECT (self),
+ MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, TRUE,
+ NULL);
+ }
+ if (!(caps & MM_MODEM_CAPABILITY_CDMA_EVDO)) {
+ mm_obj_dbg (self, "setting CDMA1x/EVDO networks as unsupported");
+ g_object_set (G_OBJECT (self),
+ MM_IFACE_MODEM_CDMA_CDMA1X_NETWORK_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_CDMA_EVDO_NETWORK_SUPPORTED, FALSE,
+ NULL);
+ }
+ if (!(caps & MM_MODEM_CAPABILITY_GSM_UMTS)) {
+ mm_obj_dbg (self, "setting CS/PS networks as unsupported");
+ g_object_set (G_OBJECT (self),
+ MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, FALSE,
+ MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, FALSE,
+ NULL);
+ }
+
/* Update current caps right away, even if we may fix them during the
* multimode device check. No big deal in updating them twice, as we're not
* exposed in DBus yet. */
@@ -3890,35 +4677,37 @@ 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_dbg ("Checking if multimode device has a SIM...");
+ if ((caps & MM_MODEM_CAPABILITY_CDMA_EVDO) && (caps & MM_MODEM_CAPABILITY_3GPP)) {
+ mm_obj_dbg (self, "checking if multimode device has a SIM...");
internal_load_unlock_required (
- ctx->self,
+ self,
(GAsyncReadyCallback)current_capabilities_internal_load_unlock_required_ready,
- ctx);
+ task);
return;
}
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_supported_capabilities_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GArray *supported_capabilities;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
supported_capabilities = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities_finish (self, res, &error);
if (error) {
g_propagate_error (&ctx->fatal_error, error);
g_prefix_error (&ctx->fatal_error, "couldn't load supported capabilities: ");
/* Jump to the last step */
ctx->step = INITIALIZATION_STEP_LAST;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
return;
}
@@ -3928,23 +4717,73 @@ load_supported_capabilities_ready (MMIfaceModem *self,
g_array_unref (supported_capabilities);
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
-STR_REPLY_READY_FN (manufacturer, "Manufacturer")
-STR_REPLY_READY_FN (model, "Model")
-STR_REPLY_READY_FN (revision, "Revision")
-STR_REPLY_READY_FN (equipment_identifier, "Equipment Identifier")
-STR_REPLY_READY_FN (device_identifier, "Device Identifier")
+STR_REPLY_READY_FN (manufacturer, "manufacturer")
+STR_REPLY_READY_FN (model, "model")
+STR_REPLY_READY_FN (revision, "revision")
+STR_REPLY_READY_FN (hardware_revision, "hardware revision")
+STR_REPLY_READY_FN (equipment_identifier, "equipment identifier")
+STR_REPLY_READY_FN (device_identifier, "device identifier")
+
+static void
+load_supported_charsets_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->supported_charsets =
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't load supported charsets: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+setup_charset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish (self, res, &error)) {
+ mm_obj_dbg (self, "couldn't set charset '%s': %s",
+ mm_modem_charset_to_string (*ctx->current_charset),
+ error->message);
+ g_error_free (error);
+
+ /* Will retry step with some other charset type */
+ } else
+ /* Done, Go on to next step */
+ ctx->step++;
+
+ interface_initialization_step (task);
+}
static void
load_supported_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
GArray *modes_array;
+ ctx = g_task_get_task_data (task);
+
modes_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish (self, res, &error);
if (modes_array != NULL) {
mm_gdbus_modem_set_supported_modes (ctx->skeleton,
@@ -3953,71 +4792,173 @@ load_supported_modes_ready (MMIfaceModem *self,
}
if (error) {
- mm_warn ("couldn't load Supported Modes: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load supported modes: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_supported_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
GArray *bands_array;
- bands_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish (self, res, &error);
+ ctx = g_task_get_task_data (task);
+ bands_array = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish (self, res, &error);
if (bands_array) {
+ mm_common_bands_garray_sort (bands_array);
mm_gdbus_modem_set_supported_bands (ctx->skeleton,
mm_common_bands_garray_to_variant (bands_array));
g_array_unref (bands_array);
}
if (error) {
- mm_warn ("couldn't load Supported Bands: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load supported bands: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_supported_ip_families_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
MMBearerIpFamily ip_families;
+ ctx = g_task_get_task_data (task);
+
ip_families = MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families_finish (self, res, &error);
if (ip_families != MM_BEARER_IP_FAMILY_NONE)
mm_gdbus_modem_set_supported_ip_families (ctx->skeleton, ip_families);
if (error) {
- mm_warn ("couldn't load Supported IP families: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load supported IP families: %s", error->message);
g_error_free (error);
}
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
-UINT_REPLY_READY_FN (power_state, "Power State")
+UINT_REPLY_READY_FN (power_state, "power state")
+
+static void
+setup_sim_hot_swap_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish (self, res, &error);
+ if (error)
+ mm_obj_warn (self, "SIM hot swap setup failed: %s", error->message);
+ else {
+ mm_obj_dbg (self, "SIM hot swap setup succeeded");
+ g_object_set (self,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, TRUE,
+ NULL);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+load_sim_slots_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ g_autoptr(GPtrArray) sim_slots = NULL;
+ g_autoptr(GError) error = NULL;
+ guint primary_sim_slot = 0;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots_finish (self,
+ res,
+ &sim_slots,
+ &primary_sim_slot,
+ &error))
+ mm_obj_warn (self, "couldn't query SIM slots: %s", error->message);
+
+ if (sim_slots) {
+ MMBaseSim *primary_sim = NULL;
+ GPtrArray *sim_slot_paths_array;
+ g_auto(GStrv) sim_slot_paths = NULL;
+ guint i;
+
+ g_assert (primary_sim_slot);
+ g_assert_cmpuint (primary_sim_slot, <=, sim_slots->len);
+
+ sim_slot_paths_array = g_ptr_array_new ();
+ for (i = 0; i < sim_slots->len; i++) {
+ MMBaseSim *sim;
+ const gchar *sim_path;
+
+ sim = MM_BASE_SIM (g_ptr_array_index (sim_slots, i));
+ if (!sim) {
+ g_ptr_array_add (sim_slot_paths_array, g_strdup ("/"));
+ continue;
+ }
+
+ sim_path = mm_base_sim_get_path (sim);
+ g_ptr_array_add (sim_slot_paths_array, g_strdup (sim_path));
+ }
+ g_ptr_array_add (sim_slot_paths_array, NULL);
+ sim_slot_paths = (GStrv) g_ptr_array_free (sim_slot_paths_array, FALSE);
+
+ mm_gdbus_modem_set_sim_slots (ctx->skeleton, (const gchar *const *)sim_slot_paths);
+ mm_gdbus_modem_set_primary_sim_slot (ctx->skeleton, primary_sim_slot);
+
+ /* If loading SIM slots is supported, we also expose already the primary active SIM object */
+ if (primary_sim_slot) {
+ primary_sim = g_ptr_array_index (sim_slots, primary_sim_slot - 1);
+ if (primary_sim)
+ g_object_bind_property (primary_sim, MM_BASE_SIM_PATH,
+ ctx->skeleton, "sim",
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
+ }
+ g_object_set (self,
+ MM_IFACE_MODEM_SIM, primary_sim,
+ MM_IFACE_MODEM_SIM_SLOTS, sim_slots,
+ NULL);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
static void
modem_update_lock_info_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
/* NOTE: we already propagated the lock state, no need to do it again */
mm_iface_modem_update_lock_info_finish (self, res, &ctx->fatal_error);
if (ctx->fatal_error) {
@@ -4029,22 +4970,27 @@ modem_update_lock_info_ready (MMIfaceModem *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
sim_new_ready (GAsyncInitable *initable,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ MMIfaceModem *self;
+ InitializationContext *ctx;
MMBaseSim *sim;
GError *error = NULL;
- sim = MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim_finish (ctx->self, res, &error);
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ sim = MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim_finish (self, res, &error);
if (error) {
- mm_warn ("couldn't create SIM: '%s'", error->message);
- g_simple_async_result_take_error (ctx->result, error);
- initialization_context_complete_and_free (ctx);
+ mm_obj_warn (self, "couldn't create SIM: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -4055,7 +5001,7 @@ sim_new_ready (GAsyncInitable *initable,
ctx->skeleton, "sim",
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
- g_object_set (ctx->self,
+ g_object_set (self,
MM_IFACE_MODEM_SIM, sim,
NULL);
g_object_unref (sim);
@@ -4063,25 +5009,77 @@ sim_new_ready (GAsyncInitable *initable,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
sim_reinit_ready (MMBaseSim *sim,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMIfaceModem *self;
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
if (!mm_base_sim_initialize_finish (sim, res, &error)) {
- mm_warn ("SIM re-initialization failed: '%s'",
- error ? error->message : "Unknown error");
+ mm_obj_warn (self, "SIM re-initialization failed: %s",
+ error ? error->message : "Unknown error");
g_clear_error (&error);
}
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
+}
+
+static void
+setup_carrier_config_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config_finish (self, res, &error)) {
+ mm_obj_warn (self, "couldn't setup carrier config: %s", error->message);
+ g_error_free (error);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
+}
+
+static void
+load_carrier_config_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializationContext *ctx;
+ GError *error = NULL;
+ gchar *name = NULL;
+ gchar *revision = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish (self, res, &name, &revision, &error)) {
+ mm_obj_warn (self, "couldn't load carrier config: %s", error->message);
+ g_error_free (error);
+ } else {
+ mm_gdbus_modem_set_carrier_configuration (ctx->skeleton, name);
+ mm_gdbus_modem_set_carrier_configuration_revision (ctx->skeleton, revision);
+ g_free (name);
+ g_free (revision);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ interface_initialization_step (task);
}
void
@@ -4102,14 +5100,17 @@ mm_iface_modem_update_own_numbers (MMIfaceModem *self,
static void
load_own_numbers_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GError *error = NULL;
GStrv str_list;
+ ctx = g_task_get_task_data (task);
+
str_list = MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish (self, res, &error);
if (error) {
- mm_warn ("couldn't load list of Own Numbers: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load list of own numbers: %s", error->message);
g_error_free (error);
}
@@ -4120,46 +5121,52 @@ load_own_numbers_ready (MMIfaceModem *self,
/* Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_current_modes_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
MMModemMode allowed = MM_MODEM_MODE_NONE;
MMModemMode preferred = MM_MODEM_MODE_NONE;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
if (!MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish (self,
res,
&allowed,
&preferred,
&error)) {
/* Errors when getting allowed/preferred won't be critical */
- mm_warn ("couldn't load current allowed/preferred modes: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load current allowed/preferred modes: %s", error->message);
g_error_free (error);
} else
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", allowed, preferred));
/* Done, Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
load_current_bands_ready (MMIfaceModem *self,
GAsyncResult *res,
- InitializationContext *ctx)
+ GTask *task)
{
+ InitializationContext *ctx;
GArray *current_bands;
GError *error = NULL;
+ ctx = g_task_get_task_data (task);
+
current_bands = MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish (self, res, &error);
if (!current_bands) {
/* Errors when getting current bands won't be critical */
- mm_warn ("couldn't load current Bands: '%s'", error->message);
+ mm_obj_warn (self, "couldn't load current bands: %s", error->message);
g_error_free (error);
} else {
GArray *filtered_bands;
@@ -4174,6 +5181,7 @@ load_current_bands_ready (MMIfaceModem *self,
g_array_unref (supported_bands);
if (filtered_bands) {
+ mm_common_bands_garray_sort (filtered_bands);
mm_gdbus_modem_set_current_bands (ctx->skeleton,
mm_common_bands_garray_to_variant (filtered_bands));
g_array_unref (filtered_bands);
@@ -4182,15 +5190,28 @@ load_current_bands_ready (MMIfaceModem *self,
/* Done, Go on to next step */
ctx->step++;
- interface_initialization_step (ctx);
+ interface_initialization_step (task);
}
static void
-interface_initialization_step (InitializationContext *ctx)
+interface_initialization_step (GTask *task)
{
+ MMIfaceModem *self;
+ InitializationContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Don't run new steps if we're cancelled */
- if (initialization_context_complete_and_free_if_cancelled (ctx))
+ if (g_task_return_error_if_cancelled (task)) {
+ /* 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_object_unref (task);
return;
+ }
switch (ctx->step) {
case INITIALIZATION_STEP_FIRST:
@@ -4198,7 +5219,7 @@ interface_initialization_step (InitializationContext *ctx)
if (!mm_gdbus_modem_get_device (ctx->skeleton)) {
gchar *device;
- g_object_get (ctx->self,
+ g_object_get (self,
MM_BASE_MODEM_DEVICE, &device,
NULL);
mm_gdbus_modem_set_device (ctx->skeleton, device);
@@ -4208,7 +5229,7 @@ interface_initialization_step (InitializationContext *ctx)
if (!mm_gdbus_modem_get_drivers (ctx->skeleton)) {
gchar **drivers;
- g_object_get (ctx->self,
+ g_object_get (self,
MM_BASE_MODEM_DRIVERS, &drivers,
NULL);
mm_gdbus_modem_set_drivers (ctx->skeleton, (const gchar * const *)drivers);
@@ -4218,7 +5239,7 @@ interface_initialization_step (InitializationContext *ctx)
if (!mm_gdbus_modem_get_plugin (ctx->skeleton)) {
gchar *plugin;
- g_object_get (ctx->self,
+ g_object_get (self,
MM_BASE_MODEM_PLUGIN, &plugin,
NULL);
mm_gdbus_modem_set_plugin (ctx->skeleton, plugin);
@@ -4229,14 +5250,15 @@ interface_initialization_step (InitializationContext *ctx)
MMPort *primary = NULL;
#if defined WITH_QMI
- primary = MM_PORT (mm_base_modem_peek_port_qmi (MM_BASE_MODEM (ctx->self)));
+ if (MM_IS_BROADBAND_MODEM_QMI (self))
+ primary = MM_PORT (mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (self)));
#endif
#if defined WITH_MBIM
- if (!primary)
- primary = MM_PORT (mm_base_modem_peek_port_mbim (MM_BASE_MODEM (ctx->self)));
+ if (!primary && MM_IS_BROADBAND_MODEM_MBIM (self))
+ primary = MM_PORT (mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (self)));
#endif
if (!primary)
- primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (ctx->self)));
+ primary = MM_PORT (mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)));
g_assert (primary != NULL);
mm_gdbus_modem_set_primary_port (ctx->skeleton, mm_port_get_device (primary));
@@ -4246,12 +5268,12 @@ interface_initialization_step (InitializationContext *ctx)
MMModemPortInfo *port_infos;
guint n_port_infos;
- port_infos = mm_base_modem_get_port_infos (MM_BASE_MODEM (ctx->self), &n_port_infos);
+ port_infos = mm_base_modem_get_port_infos (MM_BASE_MODEM (self), &n_port_infos);
mm_gdbus_modem_set_ports (ctx->skeleton, mm_common_ports_array_to_variant (port_infos, n_port_infos));
mm_modem_port_info_array_free (port_infos, n_port_infos);
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_CURRENT_CAPABILITIES:
/* Current capabilities may change during runtime, i.e. if new firmware reloaded; but we'll
@@ -4259,16 +5281,16 @@ interface_initialization_step (InitializationContext *ctx)
* reloaded. So if we're asked to re-initialize, if we already have current capabilities loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_current_capabilities (ctx->skeleton) == MM_MODEM_CAPABILITY_NONE &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_capabilities &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_capabilities_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_capabilities (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_capabilities (
+ self,
(GAsyncReadyCallback)load_current_capabilities_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_SUPPORTED_CAPABILITIES: {
GArray *supported_capabilities;
@@ -4283,12 +5305,12 @@ interface_initialization_step (InitializationContext *ctx)
g_array_index (supported_capabilities, MMModemCapability, 0) == MM_MODEM_CAPABILITY_NONE) {
MMModemCapability current;
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_capabilities &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_capabilities_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_capabilities (
- ctx->self,
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_capabilities (
+ self,
(GAsyncReadyCallback)load_supported_capabilities_ready,
- ctx);
+ task);
g_array_unref (supported_capabilities);
return;
}
@@ -4304,136 +5326,207 @@ interface_initialization_step (InitializationContext *ctx)
}
g_array_unref (supported_capabilities);
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall-through */
+
+ case INITIALIZATION_STEP_SUPPORTED_CHARSETS:
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_charsets (
+ self,
+ (GAsyncReadyCallback)load_supported_charsets_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case INITIALIZATION_STEP_CHARSET:
+ /* Only try to set charsets if we were able to load supported ones */
+ if (ctx->supported_charsets > 0 &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset_finish) {
+ gboolean next_to_try = FALSE;
+
+ while (!next_to_try) {
+ if (!ctx->current_charset)
+ /* Switch the device's charset; we prefer UTF-8, but UCS2 will do too */
+ ctx->current_charset = &best_charsets[0];
+ else
+ /* Try with the next one */
+ ctx->current_charset++;
+
+ if (*ctx->current_charset == MM_MODEM_CHARSET_UNKNOWN)
+ break;
+
+ if (ctx->supported_charsets & (*ctx->current_charset))
+ next_to_try = TRUE;
+ }
+
+ if (next_to_try) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_charset (
+ self,
+ *ctx->current_charset,
+ (GAsyncReadyCallback)setup_charset_ready,
+ task);
+ return;
+ }
+
+ mm_obj_warn (self, "Failed to find usable modem character set, let it to UNKNOWN");
+ }
+ ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_BEARERS: {
- MMBearerList *list = NULL;
+ g_autoptr(MMBearerList) list = NULL;
/* Bearers setup is meant to be loaded only once during the whole
- * lifetime of the modem. The list may have been created by the object
- * implementing the interface; if so use it. */
- g_object_get (ctx->self,
+ * lifetime of the modem, so check if it exists; and if it doesn't,
+ * create it right away. */
+ g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (!list) {
- guint n;
-
- /* The maximum number of available/connected modems is guessed from
- * the size of the data ports list. */
- n = g_list_length (mm_base_modem_peek_data_ports (MM_BASE_MODEM (ctx->self)));
- mm_dbg ("Modem allows up to %u bearers", n);
-
- /* Create new default list */
- list = mm_bearer_list_new (n, n);
+ list = MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer_list (self);
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);
- }
+ self);
- if (mm_gdbus_modem_get_max_bearers (ctx->skeleton) == 0)
- mm_gdbus_modem_set_max_bearers (
- ctx->skeleton,
- mm_bearer_list_get_max (list));
- if (mm_gdbus_modem_get_max_active_bearers (ctx->skeleton) == 0)
mm_gdbus_modem_set_max_active_bearers (
ctx->skeleton,
mm_bearer_list_get_max_active (list));
- g_object_unref (list);
+ mm_gdbus_modem_set_max_active_multiplexed_bearers (
+ ctx->skeleton,
+ mm_bearer_list_get_max_active_multiplexed (list));
+
+ /* MaxBearers set equal to MaxActiveBearers */
+ mm_gdbus_modem_set_max_bearers (
+ ctx->skeleton,
+ mm_gdbus_modem_get_max_active_bearers (ctx->skeleton));
- /* Fall down to next step */
+ g_object_set (self,
+ MM_IFACE_MODEM_BEARER_LIST, list,
+ NULL);
+ }
ctx->step++;
- }
+ } /* fall-through */
case INITIALIZATION_STEP_MANUFACTURER:
/* Manufacturer is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_manufacturer (ctx->skeleton) == NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_manufacturer &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_manufacturer_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_manufacturer (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_manufacturer (
+ self,
(GAsyncReadyCallback)load_manufacturer_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_MODEL:
/* Model is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_model (ctx->skeleton) == NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_model &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_model_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_model (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_model &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_model_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_model (
+ self,
(GAsyncReadyCallback)load_model_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_REVISION:
/* Revision is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_revision (ctx->skeleton) == NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_revision &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_revision_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_revision (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_revision (
+ self,
(GAsyncReadyCallback)load_revision_ready,
- ctx);
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case INITIALIZATION_STEP_CARRIER_CONFIG:
+ /* Current carrier config is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_modem_get_carrier_configuration (ctx->skeleton) == NULL &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_carrier_config (self,
+ (GAsyncReadyCallback)load_carrier_config_ready,
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
+
+ case INITIALIZATION_STEP_HARDWARE_REVISION:
+ /* HardwareRevision is meant to be loaded only once during the whole
+ * lifetime of the modem. Therefore, if we already have them loaded,
+ * don't try to load them again. */
+ if (mm_gdbus_modem_get_hardware_revision (ctx->skeleton) == NULL &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_hardware_revision (
+ self,
+ (GAsyncReadyCallback)load_hardware_revision_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_EQUIPMENT_ID:
/* Equipment ID is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_equipment_identifier (ctx->skeleton) == NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_equipment_identifier &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_equipment_identifier_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_equipment_identifier (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_equipment_identifier (
+ self,
(GAsyncReadyCallback)load_equipment_identifier_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_DEVICE_ID:
/* Device ID is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_device_identifier (ctx->skeleton) == NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_device_identifier &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_device_identifier_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_device_identifier (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_device_identifier (
+ self,
(GAsyncReadyCallback)load_device_identifier_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_SUPPORTED_MODES:
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_modes != NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_modes_finish != NULL) {
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes != NULL &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes_finish != NULL) {
GArray *supported_modes;
MMModemModeCombination *mode = NULL;
@@ -4447,18 +5540,18 @@ interface_initialization_step (InitializationContext *ctx)
mode = &g_array_index (supported_modes, MMModemModeCombination, 0);
if (supported_modes->len == 0 ||
(mode && mode->allowed == MM_MODEM_MODE_ANY && mode->preferred == MM_MODEM_MODE_NONE)) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_modes (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_modes (
+ self,
(GAsyncReadyCallback)load_supported_modes_ready,
- ctx);
+ task);
g_array_unref (supported_modes);
return;
}
g_array_unref (supported_modes);
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_SUPPORTED_BANDS: {
GArray *supported_bands;
@@ -4471,12 +5564,12 @@ interface_initialization_step (InitializationContext *ctx)
* don't try to load them again. */
if (supported_bands->len == 0 ||
g_array_index (supported_bands, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN) {
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_bands &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_bands_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_bands (
- ctx->self,
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_bands (
+ self,
(GAsyncReadyCallback)load_supported_bands_ready,
- ctx);
+ task);
g_array_unref (supported_bands);
return;
}
@@ -4487,73 +5580,106 @@ interface_initialization_step (InitializationContext *ctx)
}
g_array_unref (supported_bands);
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall-through */
case INITIALIZATION_STEP_SUPPORTED_IP_FAMILIES:
/* Supported ip_families are meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_ip_families != NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_ip_families_finish != NULL &&
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families != NULL &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families_finish != NULL &&
mm_gdbus_modem_get_supported_ip_families (ctx->skeleton) == MM_BEARER_IP_FAMILY_NONE) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_supported_ip_families (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_supported_ip_families (
+ self,
(GAsyncReadyCallback)load_supported_ip_families_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_POWER_STATE:
/* Initial power state is meant to be loaded only once. Therefore, if we
* already have it loaded, don't try to load it again. */
if (mm_gdbus_modem_get_power_state (ctx->skeleton) == MM_MODEM_POWER_STATE_UNKNOWN) {
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state (
- ctx->self,
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_power_state (
+ self,
(GAsyncReadyCallback)load_power_state_ready,
- ctx);
+ task);
return;
}
/* We don't know how to load current power state; assume ON */
mm_gdbus_modem_set_power_state (ctx->skeleton, MM_MODEM_POWER_STATE_ON);
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
+
+ case INITIALIZATION_STEP_SIM_HOT_SWAP: {
+ gboolean sim_hot_swap_configured = FALSE;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED, &sim_hot_swap_configured,
+ NULL);
+ if (!sim_hot_swap_configured &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_sim_hot_swap (
+ MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback) setup_sim_hot_swap_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ } /* fall-through */
+
+ case INITIALIZATION_STEP_SIM_SLOTS:
+ /* If the modem doesn't need any SIM (not implemented by plugin, or not
+ * needed in CDMA-only modems), or if we don't know how to query
+ * for SIM slots */
+ if (!mm_gdbus_modem_get_sim_slots (ctx->skeleton) &&
+ !mm_iface_modem_is_cdma_only (self) &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_sim_slots (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)load_sim_slots_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_UNLOCK_REQUIRED:
/* Only check unlock required if we were previously not unlocked */
if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE) {
- mm_iface_modem_update_lock_info (ctx->self,
+ mm_iface_modem_update_lock_info (self,
MM_MODEM_LOCK_UNKNOWN, /* ask */
(GAsyncReadyCallback)modem_update_lock_info_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_SIM:
/* If the modem doesn't need any SIM (not implemented by plugin, or not
* needed in CDMA-only modems) */
- if (!mm_iface_modem_is_cdma_only (ctx->self) &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim_finish) {
+ if (!mm_iface_modem_is_cdma_only (self) &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim_finish) {
MMBaseSim *sim = NULL;
- g_object_get (ctx->self,
+ g_object_get (self,
MM_IFACE_MODEM_SIM, &sim,
NULL);
if (!sim) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->create_sim (
- MM_IFACE_MODEM (ctx->self),
+ MM_IFACE_MODEM_GET_INTERFACE (self)->create_sim (
+ MM_IFACE_MODEM (self),
(GAsyncReadyCallback)sim_new_ready,
- ctx);
+ task);
return;
}
@@ -4561,30 +5687,73 @@ 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_base_sim_initialize (sim,
- ctx->cancellable,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)sim_reinit_ready,
- ctx);
+ task);
g_object_unref (sim);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
+
+ case INITIALIZATION_STEP_SETUP_CARRIER_CONFIG:
+ /* Setup and perform automatic carrier config switching as soon as the
+ * SIM initialization has been performed, only applicable if there is
+ * actually a SIM found with a valid IMSI read */
+ if (!mm_iface_modem_is_cdma_only (self) &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config_finish) {
+ MMBaseSim *sim = NULL;
+ gchar *carrier_config_mapping = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_SIM, &sim,
+ MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING, &carrier_config_mapping,
+ NULL);
+
+ /* If we have a SIM object, and carrier config switching is supported,
+ * validate whether we're already using the best config or not. */
+ if (!sim)
+ mm_obj_dbg (self, "not setting up carrier config: SIM not found");
+ else if (!carrier_config_mapping)
+ mm_obj_dbg (self, "not setting up carrier config: mapping file not configured");
+ else {
+ const gchar *imsi;
+
+ imsi = mm_gdbus_sim_get_imsi (MM_GDBUS_SIM (sim));
+ if (imsi) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->setup_carrier_config (self,
+ imsi,
+ carrier_config_mapping,
+ (GAsyncReadyCallback)setup_carrier_config_ready,
+ task);
+ g_object_unref (sim);
+ g_free (carrier_config_mapping);
+ return;
+ }
+ mm_obj_warn (self, "couldn't setup carrier config: unknown IMSI");
+ }
+ g_clear_object (&sim);
+ g_free (carrier_config_mapping);
+ }
+ ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_OWN_NUMBERS:
/* Own numbers is meant to be loaded only once during the whole
* lifetime of the modem. Therefore, if we already have them loaded,
* don't try to load them again. */
if (mm_gdbus_modem_get_own_numbers (ctx->skeleton) == NULL &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_own_numbers &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_own_numbers_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_own_numbers (
- ctx->self,
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_own_numbers (
+ self,
(GAsyncReadyCallback)load_own_numbers_ready,
- ctx);
+ task);
return;
}
- /* Fall down to next step */
ctx->step++;
+ /* fall-through */
case INITIALIZATION_STEP_CURRENT_MODES: {
MMModemMode allowed = MM_MODEM_MODE_ANY;
@@ -4609,12 +5778,12 @@ interface_initialization_step (InitializationContext *ctx)
supported_mode = &g_array_index (supported, MMModemModeCombination, 0);
mm_gdbus_modem_set_current_modes (ctx->skeleton, g_variant_new ("(uu)", supported_mode->allowed, supported_mode->preferred));
- } else if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_modes &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_modes_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_modes (
- ctx->self,
+ } else if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_modes (
+ self,
(GAsyncReadyCallback)load_current_modes_ready,
- ctx);
+ task);
if (supported)
g_array_unref (supported);
return;
@@ -4624,9 +5793,8 @@ interface_initialization_step (InitializationContext *ctx)
g_array_unref (supported);
}
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall-through */
case INITIALIZATION_STEP_CURRENT_BANDS: {
GArray *current;
@@ -4637,12 +5805,12 @@ interface_initialization_step (InitializationContext *ctx)
/* Current bands are only meant to be loaded once, so if we have them
* loaded already, just skip re-loading */
if (!current || (current->len == 1 && g_array_index (current, MMModemBand, 0) == MM_MODEM_BAND_UNKNOWN)) {
- if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_bands &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_bands_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_current_bands (
- ctx->self,
+ if (MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands &&
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands_finish) {
+ MM_IFACE_MODEM_GET_INTERFACE (self)->load_current_bands (
+ self,
(GAsyncReadyCallback)load_current_bands_ready,
- ctx);
+ task);
if (current)
g_array_unref (current);
return;
@@ -4655,83 +5823,43 @@ interface_initialization_step (InitializationContext *ctx)
if (current)
g_array_unref (current);
- /* Fall down to next step */
ctx->step++;
- }
+ } /* fall-through */
case INITIALIZATION_STEP_LAST:
- /* Setting capabilities allowed also in FAILED state. Just imagine a
- * 3GPP+3GPP2 modem in 3GPP-only mode without SIM, we should allow
- * changing caps to 3GPP2, which doesn't require SIM */
- g_signal_connect (ctx->skeleton,
- "handle-set-current-capabilities",
- G_CALLBACK (handle_set_current_capabilities),
- ctx->self);
- /* Allow setting the power state to OFF even when the modem is in the
- * FAILED state as this operation does not necessarily depend on the
- * presence of a SIM. handle_set_power_state_auth_ready already ensures
- * that the power state can only be set to OFF when the modem is in the
- * FAILED state. */
- g_signal_connect (ctx->skeleton,
- "handle-set-power-state",
- G_CALLBACK (handle_set_power_state),
- 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);
- ctx->fatal_error = NULL;
- } else {
- /* We are done without errors!
- * Handle method invocations */
- g_signal_connect (ctx->skeleton,
- "handle-create-bearer",
- G_CALLBACK (handle_create_bearer),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-command",
- G_CALLBACK (handle_command),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-delete-bearer",
- G_CALLBACK (handle_delete_bearer),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-list-bearers",
- G_CALLBACK (handle_list_bearers),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-enable",
- G_CALLBACK (handle_enable),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-set-current-bands",
- G_CALLBACK (handle_set_current_bands),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-set-current-modes",
- G_CALLBACK (handle_set_current_modes),
- ctx->self);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- }
+ /* Setup all method handlers */
+ g_object_connect (ctx->skeleton,
+ "signal::handle-set-current-capabilities", G_CALLBACK (handle_set_current_capabilities), self,
+ "signal::handle-set-power-state", G_CALLBACK (handle_set_power_state), self,
+ "signal::handle-reset", G_CALLBACK (handle_reset), self,
+ "signal::handle-factory-reset", G_CALLBACK (handle_factory_reset), self,
+ "signal::handle-create-bearer", G_CALLBACK (handle_create_bearer), self,
+ "signal::handle-command", G_CALLBACK (handle_command), self,
+ "signal::handle-delete-bearer", G_CALLBACK (handle_delete_bearer), self,
+ "signal::handle-list-bearers", G_CALLBACK (handle_list_bearers), self,
+ "signal::handle-enable", G_CALLBACK (handle_enable), self,
+ "signal::handle-set-current-bands", G_CALLBACK (handle_set_current_bands), self,
+ "signal::handle-set-current-modes", G_CALLBACK (handle_set_current_modes), self,
+ "signal::handle-set-primary-sim-slot", G_CALLBACK (handle_set_primary_sim_slot), self,
+ NULL);
/* Finally, export the new interface, even if we got errors, but only if not
* done already */
- if (!mm_gdbus_object_peek_modem (MM_GDBUS_OBJECT (ctx->self)))
- mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (ctx->self),
+ if (!mm_gdbus_object_peek_modem (MM_GDBUS_OBJECT (self)))
+ mm_gdbus_object_skeleton_set_modem (MM_GDBUS_OBJECT_SKELETON (self),
MM_GDBUS_MODEM (ctx->skeleton));
- initialization_context_complete_and_free (ctx);
+
+ if (ctx->fatal_error) {
+ g_task_return_error (task, ctx->fatal_error);
+ ctx->fatal_error = NULL;
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_object_unref (task);
return;
+
+ default:
+ break;
}
g_assert_not_reached ();
@@ -4742,7 +5870,7 @@ mm_iface_modem_initialize_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
void
@@ -4753,6 +5881,7 @@ mm_iface_modem_initialize (MMIfaceModem *self,
{
InitializationContext *ctx;
MmGdbusModem *skeleton = NULL;
+ GTask *task;
/* Did we already create it? */
g_object_get (self,
@@ -4800,29 +5929,21 @@ mm_iface_modem_initialize (MMIfaceModem *self,
/* 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_initialize);
ctx->step = INITIALIZATION_STEP_FIRST;
ctx->skeleton = skeleton;
- interface_initialization_step (ctx);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
+
+ interface_initialization_step (task);
}
void
mm_iface_modem_shutdown (MMIfaceModem *self)
{
- /* Remove SignalQualityCheckContext object to make sure any pending
- * invocation of periodic_signal_quality_check is cancelled before
- * SignalQualityUpdateContext is removed (as signal_quality_check_ready may
- * call update_signal_quality). */
- if (G_LIKELY (signal_quality_check_context_quark))
- g_object_set_qdata (G_OBJECT (self),
- signal_quality_check_context_quark,
- NULL);
+ /* Make sure signal polling is disabled. No real need to clear values, as
+ * we're shutting down the interface anyway. */
+ periodic_signal_check_disable (self, FALSE);
/* Remove SignalQualityUpdateContext object to make sure any pending
* invocation of expire_signal_quality is canceled before the DBus skeleton
@@ -4832,14 +5953,6 @@ mm_iface_modem_shutdown (MMIfaceModem *self)
signal_quality_update_context_quark,
NULL);
- /* Remove AccessTechnologiesCheckContext object to make sure any pending
- * invocation of periodic_access_technologies_check is canceled before the
- * DBus skeleton is removed. */
- if (G_LIKELY (access_technologies_check_context_quark))
- g_object_set_qdata (G_OBJECT (self),
- access_technologies_check_context_quark,
- NULL);
-
/* Remove running restart initialization idle, if any */
if (G_LIKELY (restart_initialize_idle_quark))
g_object_set_qdata (G_OBJECT (self),
@@ -4973,6 +6086,22 @@ mm_iface_modem_is_4g_only (MMIfaceModem *self)
FALSE);
}
+gboolean
+mm_iface_modem_is_5g (MMIfaceModem *self)
+{
+ return find_supported_mode (self, MM_MODEM_MODE_5G, NULL);
+}
+
+gboolean
+mm_iface_modem_is_5g_only (MMIfaceModem *self)
+{
+ gboolean only;
+
+ return (find_supported_mode (self, MM_MODEM_MODE_5G, &only) ?
+ only :
+ FALSE);
+}
+
/*****************************************************************************/
MMModemCapability
@@ -5002,40 +6131,34 @@ mm_iface_modem_is_3gpp (MMIfaceModem *self)
gboolean
mm_iface_modem_is_3gpp_lte (MMIfaceModem *self)
{
- return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_3GPP_LTE);
+ return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_LTE);
}
gboolean
-mm_iface_modem_is_cdma (MMIfaceModem *self)
+mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self)
{
- return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO);
+ return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_5GNR);
}
gboolean
-mm_iface_modem_is_3gpp_only (MMIfaceModem *self)
+mm_iface_modem_is_cdma (MMIfaceModem *self)
{
- MMModemCapability capabilities;
-
- capabilities = mm_iface_modem_get_current_capabilities (self);
- return !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities);
+ return (mm_iface_modem_get_current_capabilities (self) & MM_MODEM_CAPABILITY_CDMA_EVDO);
}
gboolean
-mm_iface_modem_is_3gpp_lte_only (MMIfaceModem *self)
+mm_iface_modem_is_3gpp_only (MMIfaceModem *self)
{
MMModemCapability capabilities;
capabilities = mm_iface_modem_get_current_capabilities (self);
- return !((MM_MODEM_CAPABILITY_3GPP_LTE ^ capabilities) & capabilities);
+ return (capabilities & MM_MODEM_CAPABILITY_3GPP) && !((MM_MODEM_CAPABILITY_3GPP ^ capabilities) & capabilities);
}
gboolean
mm_iface_modem_is_cdma_only (MMIfaceModem *self)
{
- MMModemCapability capabilities;
-
- capabilities = mm_iface_modem_get_current_capabilities (self);
- return !((MM_MODEM_CAPABILITY_CDMA_EVDO ^ capabilities) & capabilities);
+ return (mm_iface_modem_get_current_capabilities (self) == MM_MODEM_CAPABILITY_CDMA_EVDO);
}
/*****************************************************************************/
@@ -5058,6 +6181,45 @@ mm_iface_modem_get_model (MMIfaceModem *self)
return model;
}
+const gchar *
+mm_iface_modem_get_revision (MMIfaceModem *self)
+{
+ const gchar *revision = NULL;
+ MmGdbusModem *skeleton;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (skeleton) {
+ revision = mm_gdbus_modem_get_revision (skeleton);
+ g_object_unref (skeleton);
+ }
+
+ return revision;
+}
+
+gboolean
+mm_iface_modem_get_carrier_config (MMIfaceModem *self,
+ const gchar **name,
+ const gchar **revision)
+{
+ MmGdbusModem *skeleton;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return FALSE;
+
+ if (name)
+ *name = mm_gdbus_modem_get_carrier_configuration (skeleton);
+ if (revision)
+ *revision = mm_gdbus_modem_get_carrier_configuration_revision (skeleton);
+ g_object_unref (skeleton);
+ return TRUE;
+}
+
/*****************************************************************************/
static void
@@ -5087,6 +6249,14 @@ iface_modem_init (gpointer g_iface)
g_object_interface_install_property
(g_iface,
+ g_param_spec_boxed (MM_IFACE_MODEM_SIM_SLOTS,
+ "SIM slots",
+ "SIM objects in SIM slots",
+ MM_TYPE_OBJECT_ARRAY,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
g_param_spec_enum (MM_IFACE_MODEM_STATE,
"State",
"State of the modem",
@@ -5102,6 +6272,46 @@ iface_modem_init (gpointer g_iface)
MM_TYPE_BEARER_LIST,
G_PARAM_READWRITE));
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED,
+ "Sim Hot Swap Supported",
+ "Whether the modem supports sim hot swap or not.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED,
+ "Sim Hot Swap Configured",
+ "Whether the sim hot swap support is configured correctly.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED,
+ "Periodic signal quality check disabled",
+ "Whether periodic signal quality check is disabled.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_boolean (MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED,
+ "Periodic access technology check disabled",
+ "Whether periodic access technology check is disabled.",
+ FALSE,
+ G_PARAM_READWRITE));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING,
+ "Carrier config mapping table",
+ "Path to the file including the carrier mapping for the module",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
initialized = TRUE;
}
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index 74ea9f92..34f3297b 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -26,16 +26,23 @@
#include "mm-port-serial-at.h"
#include "mm-base-bearer.h"
#include "mm-base-sim.h"
+#include "mm-bearer-list.h"
#define MM_TYPE_IFACE_MODEM (mm_iface_modem_get_type ())
#define MM_IFACE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_IFACE_MODEM, MMIfaceModem))
#define MM_IS_IFACE_MODEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_IFACE_MODEM))
#define MM_IFACE_MODEM_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_IFACE_MODEM, MMIfaceModem))
-#define MM_IFACE_MODEM_DBUS_SKELETON "iface-modem-dbus-skeleton"
-#define MM_IFACE_MODEM_STATE "iface-modem-state"
-#define MM_IFACE_MODEM_SIM "iface-modem-sim"
-#define MM_IFACE_MODEM_BEARER_LIST "iface-modem-bearer-list"
+#define MM_IFACE_MODEM_DBUS_SKELETON "iface-modem-dbus-skeleton"
+#define MM_IFACE_MODEM_STATE "iface-modem-state"
+#define MM_IFACE_MODEM_SIM "iface-modem-sim"
+#define MM_IFACE_MODEM_SIM_SLOTS "iface-modem-sim-slots"
+#define MM_IFACE_MODEM_BEARER_LIST "iface-modem-bearer-list"
+#define MM_IFACE_MODEM_SIM_HOT_SWAP_SUPPORTED "iface-modem-sim-hot-swap-supported"
+#define MM_IFACE_MODEM_SIM_HOT_SWAP_CONFIGURED "iface-modem-sim-hot-swap-configured"
+#define MM_IFACE_MODEM_CARRIER_CONFIG_MAPPING "iface-modem-carrier-config-mapping"
+#define MM_IFACE_MODEM_PERIODIC_SIGNAL_CHECK_DISABLED "iface-modem-periodic-signal-check-disabled"
+#define MM_IFACE_MODEM_PERIODIC_ACCESS_TECH_CHECK_DISABLED "iface-modem-periodic-access-tech-check-disabled"
typedef struct _MMIfaceModem MMIfaceModem;
@@ -82,6 +89,14 @@ struct _MMIfaceModem {
GAsyncResult *res,
GError **error);
+ /* Loading of the HardwareRevision property */
+ void (*load_hardware_revision) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gchar * (*load_hardware_revision_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Loading of the EquipmentIdentifier property */
void (*load_equipment_identifier) (MMIfaceModem *self,
GAsyncReadyCallback callback,
@@ -108,6 +123,7 @@ struct _MMIfaceModem {
/* Loading of the UnlockRequired property */
void (*load_unlock_required) (MMIfaceModem *self,
+ gboolean last_attempt,
GAsyncReadyCallback callback,
gpointer user_data);
MMModemLock (*load_unlock_required_finish) (MMIfaceModem *self,
@@ -254,6 +270,14 @@ struct _MMIfaceModem {
GAsyncResult *res,
GError **error);
+ /* Asynchronous FCC unlock operation */
+ void (* fcc_unlock) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* fcc_unlock_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Asynchronous modem power-up operation */
void (*modem_power_up) (MMIfaceModem *self,
GAsyncReadyCallback callback,
@@ -271,6 +295,17 @@ struct _MMIfaceModem {
GAsyncResult *res,
GError **error);
+ /* Asynchronous check to see if the SIM was swapped.
+ * Useful for when the modem changes power states since we might
+ * not get the relevant notifications from the modem. */
+ void (*check_for_sim_swap) (MMIfaceModem *self,
+ const gchar *iccid,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*check_for_sim_swap_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Asynchronous flow control setup */
void (*setup_flow_control) (MMIfaceModem *self,
GAsyncReadyCallback callback,
@@ -319,6 +354,25 @@ struct _MMIfaceModem {
GAsyncResult *res,
GError **error);
+ /* Create SIMs in all SIM slots */
+ void (* load_sim_slots) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* load_sim_slots_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GPtrArray **sim_slots,
+ guint *primary_sim_slot,
+ GError **error);
+
+ /* Set primary SIM slot */
+ void (* set_primary_sim_slot) (MMIfaceModem *self,
+ guint sim_slot,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* set_primary_sim_slot_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Create bearer */
void (*create_bearer) (MMIfaceModem *self,
MMBearerProperties *properties,
@@ -327,9 +381,42 @@ struct _MMIfaceModem {
MMBaseBearer * (*create_bearer_finish) (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
+
+ /* Create new bearer list object */
+ MMBearerList * (* create_bearer_list) (MMIfaceModem *self);
+
+ /* Setup SIM hot swap */
+ void (*setup_sim_hot_swap) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+ gboolean (*setup_sim_hot_swap_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+ /* Load carrier config */
+ void (* load_carrier_config) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* load_carrier_config_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ gchar **carrier_config_name,
+ gchar **carrier_config_revision,
+ GError **error);
+
+ /* Setup carrier config based on IMSI */
+ void (* setup_carrier_config) (MMIfaceModem *self,
+ const gchar *imsi,
+ const gchar *carrier_config_mapping,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* setup_carrier_config_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
};
GType mm_iface_modem_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMIfaceModem, g_object_unref)
/* Helpers to query access technologies */
MMModemAccessTechnology mm_iface_modem_get_access_technologies (MMIfaceModem *self);
@@ -339,7 +426,7 @@ MMModemCapability mm_iface_modem_get_current_capabilities (MMIfaceModem *self);
gboolean mm_iface_modem_is_3gpp (MMIfaceModem *self);
gboolean mm_iface_modem_is_3gpp_only (MMIfaceModem *self);
gboolean mm_iface_modem_is_3gpp_lte (MMIfaceModem *self);
-gboolean mm_iface_modem_is_3gpp_lte_only (MMIfaceModem *self);
+gboolean mm_iface_modem_is_3gpp_5gnr (MMIfaceModem *self);
gboolean mm_iface_modem_is_cdma (MMIfaceModem *self);
gboolean mm_iface_modem_is_cdma_only (MMIfaceModem *self);
@@ -350,9 +437,15 @@ gboolean mm_iface_modem_is_3g (MMIfaceModem *self);
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);
+gboolean mm_iface_modem_is_5g (MMIfaceModem *self);
+gboolean mm_iface_modem_is_5g_only (MMIfaceModem *self);
-/* Helper to query model */
-const gchar *mm_iface_modem_get_model (MMIfaceModem *self);
+/* Helpers to query properties */
+const gchar *mm_iface_modem_get_model (MMIfaceModem *self);
+const gchar *mm_iface_modem_get_revision (MMIfaceModem *self);
+gboolean mm_iface_modem_get_carrier_config (MMIfaceModem *self,
+ const gchar **name,
+ const gchar **revision);
/* Initialize Modem interface (async) */
void mm_iface_modem_initialize (MMIfaceModem *self,
@@ -380,6 +473,27 @@ gboolean mm_iface_modem_disable_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
+/* Shutdown Modem interface */
+void mm_iface_modem_shutdown (MMIfaceModem *self);
+
+/* Helper to return an error when the modem is in failed state and so it
+ * cannot process a given method invocation
+ */
+gboolean mm_iface_modem_abort_invocation_if_state_not_reached (MMIfaceModem *self,
+ GDBusMethodInvocation *invocation,
+ MMModemState minimum_required);
+#if defined WITH_SYSTEMD_SUSPEND_RESUME
+
+/* Sync Modem interface (async) */
+void mm_iface_modem_sync (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_sync_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif
+
/* Allow setting power state */
void mm_iface_modem_set_power_state (MMIfaceModem *self,
MMModemPowerState power_state,
@@ -389,9 +503,6 @@ gboolean mm_iface_modem_set_power_state_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
-/* Shutdown Modem interface */
-void mm_iface_modem_shutdown (MMIfaceModem *self);
-
/* Request lock info update.
* It will not only return the lock status, but also set the property values
* in the DBus interface. If 'known_lock' is given, that lock status will be
@@ -404,6 +515,12 @@ MMModemLock mm_iface_modem_update_lock_info_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error);
+MMModemLock mm_iface_modem_get_unlock_required (MMIfaceModem *self);
+MMUnlockRetries *mm_iface_modem_get_unlock_retries (MMIfaceModem *self);
+
+void mm_iface_modem_update_unlock_retries (MMIfaceModem *self,
+ MMUnlockRetries *unlock_retries);
+
/* Request signal quality check update.
* It will not only return the signal quality status, but also set the property
* values in the DBus interface. */
@@ -435,13 +552,13 @@ void mm_iface_modem_update_access_technologies (MMIfaceModem *self,
MMModemAccessTechnology access_tech,
guint32 mask);
-/* Allow requesting to refresh access tech */
-void mm_iface_modem_refresh_access_technologies (MMIfaceModem *self);
-
/* Allow updating signal quality */
void mm_iface_modem_update_signal_quality (MMIfaceModem *self,
guint signal_quality);
+/* Allow requesting to refresh signal via polling */
+void mm_iface_modem_refresh_signal (MMIfaceModem *self);
+
/* Allow setting allowed modes */
void mm_iface_modem_set_current_modes (MMIfaceModem *self,
MMModemMode allowed,
@@ -482,4 +599,18 @@ MMModemState mm_iface_modem_wait_for_final_state_finish (MMIfaceModem *self,
void mm_iface_modem_bind_simple_status (MMIfaceModem *self,
MMSimpleStatus *status);
+/* Check if the SIM or eSIM profile has changed */
+void mm_iface_modem_check_for_sim_swap (MMIfaceModem *self,
+ guint slot_index,
+ const gchar *iccid,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_iface_modem_check_for_sim_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_iface_modem_modify_sim (MMIfaceModem *self,
+ guint slot_index,
+ MMBaseSim *new_sim);
+
#endif /* MM_IFACE_MODEM_H */
diff --git a/src/mm-log-object.c b/src/mm-log-object.c
new file mode 100644
index 00000000..ed41355f
--- /dev/null
+++ b/src/mm-log-object.c
@@ -0,0 +1,89 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "mm-log-object.h"
+
+G_DEFINE_INTERFACE (MMLogObject, mm_log_object, G_TYPE_OBJECT)
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "log-object"
+static GQuark private_quark;
+
+typedef struct {
+ gchar *owner_id;
+ gchar *id;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ g_free (priv->owner_id);
+ g_free (priv->id);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMLogObject *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+const gchar *
+mm_log_object_get_id (MMLogObject *self)
+{
+ Private *priv;
+
+ priv = get_private (self);
+ if (!priv->id) {
+ gchar *self_id;
+
+ self_id = MM_LOG_OBJECT_GET_IFACE (self)->build_id (self);
+ if (priv->owner_id) {
+ priv->id = g_strdup_printf ("%s/%s", priv->owner_id, self_id);
+ g_free (self_id);
+ } else
+ priv->id = self_id;
+ }
+ return priv->id;
+}
+
+void
+mm_log_object_set_owner_id (MMLogObject *self,
+ const gchar *owner_id)
+{
+ Private *priv;
+
+ priv = get_private (self);
+ g_free (priv->owner_id);
+ priv->owner_id = g_strdup (owner_id);
+}
+
+static void
+mm_log_object_default_init (MMLogObjectInterface *iface)
+{
+}
diff --git a/src/mm-log-object.h b/src/mm-log-object.h
new file mode 100644
index 00000000..21ffc05e
--- /dev/null
+++ b/src/mm-log-object.h
@@ -0,0 +1,38 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_LOG_OBJECT_H
+#define MM_LOG_OBJECT_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-log.h"
+
+#define MM_TYPE_LOG_OBJECT mm_log_object_get_type ()
+G_DECLARE_INTERFACE (MMLogObject, mm_log_object, MM, LOG_OBJECT, GObject)
+
+struct _MMLogObjectInterface
+{
+ GTypeInterface g_iface;
+
+ gchar * (* build_id) (MMLogObject *self);
+};
+
+const gchar *mm_log_object_get_id (MMLogObject *self);
+void mm_log_object_set_owner_id (MMLogObject *self,
+ const gchar *owner_id);
+
+#endif /* MM_LOG_OBJECT_H */
diff --git a/src/mm-log-test.h b/src/mm-log-test.h
new file mode 100644
index 00000000..22493d6d
--- /dev/null
+++ b/src/mm-log-test.h
@@ -0,0 +1,46 @@
+/* -*- 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) 2020 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_LOG_TEST_H
+#define MM_LOG_TEST_H
+
+#include <glib.h>
+#include "mm-log.h"
+
+/* This is a common logging method to be used by all test applications */
+
+void
+_mm_log (gpointer obj,
+ const gchar *module,
+ const gchar *loc,
+ const gchar *func,
+ guint32 level,
+ const gchar *fmt,
+ ...)
+{
+ va_list args;
+ gchar *msg;
+
+ if (!g_test_verbose ())
+ return;
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("%s\n", msg);
+ g_free (msg);
+}
+
+#endif /* MM_LOG_TEST_H */
diff --git a/src/mm-log.c b/src/mm-log.c
index da424eed..22a39012 100644
--- a/src/mm-log.c
+++ b/src/mm-log.c
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011-2020 Red Hat, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#define _GNU_SOURCE
@@ -35,7 +36,13 @@
#include <libmbim-glib.h>
#endif
+#if defined WITH_SYSTEMD_JOURNAL
+#define SD_JOURNAL_SUPPRESS_LOCATION
+#include <systemd/sd-journal.h>
+#endif
+
#include "mm-log.h"
+#include "mm-log-object.h"
enum {
TS_FLAG_NONE = 0,
@@ -44,10 +51,16 @@ enum {
};
static gboolean ts_flags = TS_FLAG_NONE;
-static guint32 log_level = LOGL_INFO | LOGL_WARN | LOGL_ERR;
+static guint32 log_level = MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR;
static GTimeVal rel_start = { 0, 0 };
static int logfd = -1;
-static gboolean func_loc = FALSE;
+static gboolean append_log_level_text = TRUE;
+
+static void (*log_backend) (const char *loc,
+ const char *func,
+ int syslog_level,
+ const char *message,
+ size_t length);
typedef struct {
guint32 num;
@@ -55,27 +68,155 @@ typedef struct {
} LogDesc;
static const LogDesc level_descs[] = {
- { LOGL_ERR, "ERR" },
- { LOGL_WARN | LOGL_ERR, "WARN" },
- { LOGL_INFO | LOGL_WARN | LOGL_ERR, "INFO" },
- { LOGL_DEBUG | LOGL_INFO | LOGL_WARN | LOGL_ERR, "DEBUG" },
+ { MM_LOG_LEVEL_ERR, "ERR" },
+ { MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "WARN" },
+ { MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "INFO" },
+ { MM_LOG_LEVEL_DEBUG | MM_LOG_LEVEL_INFO | MM_LOG_LEVEL_WARN | MM_LOG_LEVEL_ERR, "DEBUG" },
{ 0, NULL }
};
static GString *msgbuf = NULL;
-static volatile gsize msgbuf_once = 0;
+static gsize msgbuf_once = 0;
+
+static int
+mm_to_syslog_priority (MMLogLevel level)
+{
+ switch (level) {
+ case MM_LOG_LEVEL_DEBUG:
+ return LOG_DEBUG;
+ case MM_LOG_LEVEL_WARN:
+ return LOG_WARNING;
+ case MM_LOG_LEVEL_INFO:
+ return LOG_INFO;
+ case MM_LOG_LEVEL_ERR:
+ return LOG_ERR;
+ default:
+ break;
+ }
+ g_assert_not_reached ();
+ return 0;
+}
+
+static int
+glib_to_syslog_priority (GLogLevelFlags level)
+{
+ /* if the log was flagged as fatal (e.g. G_DEBUG=fatal-warnings), ignore
+ * the fatal flag for logging purposes */
+ if (level & G_LOG_FLAG_FATAL)
+ level &= ~G_LOG_FLAG_FATAL;
+
+ switch (level) {
+ case G_LOG_LEVEL_ERROR:
+ return LOG_CRIT;
+ case G_LOG_LEVEL_CRITICAL:
+ return LOG_ERR;
+ case G_LOG_LEVEL_WARNING:
+ return LOG_WARNING;
+ case G_LOG_LEVEL_MESSAGE:
+ return LOG_NOTICE;
+ case G_LOG_LEVEL_INFO:
+ return LOG_INFO;
+ case G_LOG_LEVEL_DEBUG:
+ return LOG_DEBUG;
+ case G_LOG_LEVEL_MASK:
+ case G_LOG_FLAG_FATAL:
+ case G_LOG_FLAG_RECURSION:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static const char *
+log_level_description (MMLogLevel level)
+{
+ switch (level) {
+ case MM_LOG_LEVEL_DEBUG:
+ return "<debug>";
+ case MM_LOG_LEVEL_WARN:
+ return "<warn> ";
+ case MM_LOG_LEVEL_INFO:
+ return "<info> ";
+ case MM_LOG_LEVEL_ERR:
+ return "<error>";
+ default:
+ break;
+ }
+ g_assert_not_reached ();
+ return NULL;
+}
+
+static void
+log_backend_file (const char *loc,
+ const char *func,
+ int syslog_level,
+ const char *message,
+ size_t length)
+{
+ ssize_t ign;
+ ign = write (logfd, message, length);
+ if (ign) {} /* whatever; really shut up about unused result */
+
+ fsync (logfd); /* Make sure output is dumped to disk immediately */
+}
+
+static void
+log_backend_syslog (const char *loc,
+ const char *func,
+ int syslog_level,
+ const char *message,
+ size_t length)
+{
+ syslog (syslog_level, "%s", message);
+}
+
+#if defined WITH_SYSTEMD_JOURNAL
+static void
+log_backend_systemd_journal (const char *loc,
+ const char *func,
+ int syslog_level,
+ const char *message,
+ size_t length)
+{
+ const char *line;
+ size_t file_length;
+
+ if (loc == NULL) {
+ sd_journal_send ("MESSAGE=%s", message,
+ "PRIORITY=%d", syslog_level,
+ NULL);
+ return;
+ }
+
+ line = strstr (loc, ":");
+ if (line) {
+ file_length = line - loc;
+ line++;
+ } else {
+ /* This is not supposed to happen but we must be prepared for this */
+ line = loc;
+ file_length = 0;
+ }
+
+ sd_journal_send ("MESSAGE=%s", message,
+ "PRIORITY=%d", syslog_level,
+ "CODE_FUNC=%s", func,
+ "CODE_FILE=%.*s", file_length, loc,
+ "CODE_LINE=%s", line,
+ NULL);
+}
+#endif
void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
+_mm_log (gpointer obj,
+ const gchar *module,
+ const gchar *loc,
+ const gchar *func,
+ MMLogLevel level,
+ const gchar *fmt,
...)
{
va_list args;
GTimeVal tv;
- int syslog_priority = LOG_INFO;
- ssize_t ign;
if (!(log_level & level))
return;
@@ -86,18 +227,8 @@ _mm_log (const char *loc,
} else
g_string_truncate (msgbuf, 0);
- if ((log_level & LOGL_DEBUG) && (level == LOGL_DEBUG))
- g_string_append (msgbuf, "<debug> ");
- else if ((log_level & LOGL_INFO) && (level == LOGL_INFO))
- g_string_append (msgbuf, "<info> ");
- else if ((log_level & LOGL_WARN) && (level == LOGL_WARN)) {
- g_string_append (msgbuf, "<warn> ");
- syslog_priority = LOG_WARNING;
- } else if ((log_level & LOGL_ERR) && (level == LOGL_ERR)) {
- g_string_append (msgbuf, "<error> ");
- syslog_priority = LOG_ERR;
- } else
- return;
+ if (append_log_level_text)
+ g_string_append_printf (msgbuf, "%s ", log_level_description (level));
if (ts_flags == TS_FLAG_WALL) {
g_get_current_time (&tv);
@@ -117,8 +248,14 @@ _mm_log (const char *loc,
g_string_append_printf (msgbuf, "[%06ld.%06ld] ", secs, usecs);
}
- if (func_loc && log_level & LOGL_DEBUG)
- g_string_append_printf (msgbuf, "[%s] %s(): ", loc, func);
+#if defined MM_LOG_FUNC_LOC
+ g_string_append_printf (msgbuf, "[%s] %s(): ", loc, func);
+#endif
+
+ if (obj)
+ g_string_append_printf (msgbuf, "[%s] ", mm_log_object_get_id (MM_LOG_OBJECT (obj)));
+ if (module)
+ g_string_append_printf (msgbuf, "(%s) ", module);
va_start (args, fmt);
g_string_append_vprintf (msgbuf, fmt, args);
@@ -126,14 +263,7 @@ _mm_log (const char *loc,
g_string_append_c (msgbuf, '\n');
- if (logfd < 0)
- syslog (syslog_priority, "%s", msgbuf->str);
- else {
- ign = write (logfd, msgbuf->str, msgbuf->len);
- if (ign) {} /* whatever; really shut up about unused result */
-
- fsync (logfd); /* Make sure output is dumped to disk immediately */
- }
+ log_backend (loc, func, mm_to_syslog_priority (level), msgbuf->str, msgbuf->len);
}
static void
@@ -142,37 +272,7 @@ log_handler (const gchar *log_domain,
const gchar *message,
gpointer ignored)
{
- int syslog_priority;
- ssize_t ign;
-
- switch (level) {
- case G_LOG_LEVEL_ERROR:
- syslog_priority = LOG_CRIT;
- break;
- case G_LOG_LEVEL_CRITICAL:
- syslog_priority = LOG_ERR;
- break;
- case G_LOG_LEVEL_WARNING:
- syslog_priority = LOG_WARNING;
- break;
- case G_LOG_LEVEL_MESSAGE:
- syslog_priority = LOG_NOTICE;
- break;
- case G_LOG_LEVEL_DEBUG:
- syslog_priority = LOG_DEBUG;
- break;
- case G_LOG_LEVEL_INFO:
- default:
- syslog_priority = LOG_INFO;
- break;
- }
-
- if (logfd < 0)
- syslog (syslog_priority, "%s", message);
- else {
- ign = write (logfd, message, strlen (message));
- if (ign) {} /* whatever; really shut up about unused result */
- }
+ log_backend (NULL, NULL, glib_to_syslog_priority (level), message, strlen (message));
}
gboolean
@@ -194,11 +294,11 @@ mm_log_set_level (const char *level, GError **error)
"Unknown log level '%s'", level);
#if defined WITH_QMI
- qmi_utils_set_traces_enabled (log_level & LOGL_DEBUG ? TRUE : FALSE);
+ qmi_utils_set_traces_enabled (log_level & MM_LOG_LEVEL_DEBUG ? TRUE : FALSE);
#endif
#if defined WITH_MBIM
- mbim_utils_set_traces_enabled (log_level & LOGL_DEBUG ? TRUE : FALSE);
+ mbim_utils_set_traces_enabled (log_level & MM_LOG_LEVEL_DEBUG ? TRUE : FALSE);
#endif
return found;
@@ -207,17 +307,15 @@ mm_log_set_level (const char *level, GError **error)
gboolean
mm_log_setup (const char *level,
const char *log_file,
+ gboolean log_journal,
gboolean show_timestamps,
gboolean rel_timestamps,
- gboolean debug_func_loc,
GError **error)
{
/* levels */
if (level && strlen (level) && !mm_log_set_level (level, error))
return FALSE;
- func_loc = debug_func_loc;
-
if (show_timestamps)
ts_flags = TS_FLAG_WALL;
else if (rel_timestamps)
@@ -226,9 +324,16 @@ mm_log_setup (const char *level,
/* Grab start time for relative timestamps */
g_get_current_time (&rel_start);
- if (log_file == NULL)
+#if defined WITH_SYSTEMD_JOURNAL
+ if (log_journal) {
+ log_backend = log_backend_systemd_journal;
+ append_log_level_text = FALSE;
+ } else
+#endif
+ if (log_file == NULL) {
openlog (G_LOG_DOMAIN, LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON);
- else {
+ log_backend = log_backend_syslog;
+ } else {
logfd = open (log_file,
O_CREAT | O_APPEND | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
@@ -238,6 +343,7 @@ mm_log_setup (const char *level,
errno, strerror (errno));
return FALSE;
}
+ log_backend = log_backend_file;
}
g_log_set_handler (G_LOG_DOMAIN,
@@ -252,6 +358,13 @@ mm_log_setup (const char *level,
NULL);
#endif
+#if defined WITH_QRTR
+ g_log_set_handler ("Qrtr",
+ G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
+ log_handler,
+ NULL);
+#endif
+
#if defined WITH_MBIM
g_log_set_handler ("Mbim",
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
diff --git a/src/mm-log.h b/src/mm-log.h
index 762f9751..d0b5c607 100644
--- a/src/mm-log.h
+++ b/src/mm-log.h
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011-2020 Red Hat, Inc.
+ * Copyright (C) 2020 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_LOG_H
@@ -19,41 +20,46 @@
#include <glib.h>
/* Log levels */
-enum {
- LOGL_ERR = 0x00000001,
- LOGL_WARN = 0x00000002,
- LOGL_INFO = 0x00000004,
- LOGL_DEBUG = 0x00000008
-};
+typedef enum {
+ MM_LOG_LEVEL_ERR = 0x00000001,
+ MM_LOG_LEVEL_WARN = 0x00000002,
+ MM_LOG_LEVEL_INFO = 0x00000004,
+ MM_LOG_LEVEL_DEBUG = 0x00000008
+} MMLogLevel;
-#define mm_err(...) \
- _mm_log (G_STRLOC, G_STRFUNC, LOGL_ERR, ## __VA_ARGS__ )
+#if !defined MM_MODULE_NAME
+# define MM_MODULE_NAME (const gchar *)NULL
+#endif
-#define mm_warn(...) \
- _mm_log (G_STRLOC, G_STRFUNC, LOGL_WARN, ## __VA_ARGS__ )
+#define mm_obj_err(obj, ...) _mm_log (obj, MM_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_ERR, ## __VA_ARGS__ )
+#define mm_obj_warn(obj, ...) _mm_log (obj, MM_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_WARN, ## __VA_ARGS__ )
+#define mm_obj_info(obj, ...) _mm_log (obj, MM_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_INFO, ## __VA_ARGS__ )
+#define mm_obj_dbg(obj, ...) _mm_log (obj, MM_MODULE_NAME, G_STRLOC, G_STRFUNC, MM_LOG_LEVEL_DEBUG, ## __VA_ARGS__ )
-#define mm_info(...) \
- _mm_log (G_STRLOC, G_STRFUNC, LOGL_INFO, ## __VA_ARGS__ )
+/* only allow using non-object logging API if explicitly requested
+ * (e.g. in the main daemon source) */
+#if defined MM_LOG_NO_OBJECT
+# define mm_err(...) mm_obj_err (NULL, ## __VA_ARGS__ )
+# define mm_warn(...) mm_obj_warn (NULL, ## __VA_ARGS__ )
+# define mm_info(...) mm_obj_info (NULL, ## __VA_ARGS__ )
+# define mm_dbg(...) mm_obj_dbg (NULL, ## __VA_ARGS__ )
+#endif
-#define mm_dbg(...) \
- _mm_log (G_STRLOC, G_STRFUNC, LOGL_DEBUG, ## __VA_ARGS__ )
-
-#define mm_log(level, ...) \
- _mm_log (G_STRLOC, G_STRFUNC, level, ## __VA_ARGS__ )
-
-void _mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...) __attribute__((__format__ (__printf__, 4, 5)));
+void _mm_log (gpointer obj,
+ const gchar *module,
+ const gchar *loc,
+ const gchar *func,
+ MMLogLevel level,
+ const gchar *fmt,
+ ...) __attribute__((__format__ (__printf__, 6, 7)));
gboolean mm_log_set_level (const char *level, GError **error);
gboolean mm_log_setup (const char *level,
const char *log_file,
+ gboolean log_journal,
gboolean show_ts,
gboolean rel_ts,
- gboolean debug_func_loc,
GError **error);
void mm_log_shutdown (void);
diff --git a/src/mm-modem-helpers-mbim.c b/src/mm-modem-helpers-mbim.c
index 36d55023..c31310ae 100644
--- a/src/mm-modem-helpers-mbim.c
+++ b/src/mm-modem-helpers-mbim.c
@@ -10,14 +10,50 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@aleksander.es>
*/
#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"
+#include "mm-error-helpers.h"
+#include "mm-log-object.h"
+
+#include <string.h>
+
+/*****************************************************************************/
+
+MMModemCapability
+mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class,
+ MbimDataClass caps_data_class,
+ const gchar *caps_custom_data_class)
+{
+ MMModemCapability mask = 0;
+
+ if (caps_cellular_class & MBIM_CELLULAR_CLASS_GSM)
+ mask |= MM_MODEM_CAPABILITY_GSM_UMTS;
+
+#if 0 /* Disable until we add MBIM CDMA support */
+ if (caps_cellular_class & MBIM_CELLULAR_CLASS_CDMA)
+ mask |= MM_MODEM_CAPABILITY_CDMA_EVDO;
+#endif
+
+ if (caps_data_class & MBIM_DATA_CLASS_LTE)
+ mask |= MM_MODEM_CAPABILITY_LTE;
+
+ /* e.g. Gosuncn GM800 reports MBIM custom data class "5G/TDS" */
+ if ((caps_data_class & MBIM_DATA_CLASS_CUSTOM) && caps_custom_data_class) {
+ if (strstr (caps_custom_data_class, "5G"))
+ mask |= MM_MODEM_CAPABILITY_5GNR;
+ }
+
+ /* Support for devices with Microsoft extensions */
+ if (caps_data_class & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA))
+ mask |= MM_MODEM_CAPABILITY_5GNR;
+
+ return mask;
+}
/*****************************************************************************/
@@ -25,9 +61,6 @@ MMModemLock
mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type)
{
switch (pin_type) {
- case MBIM_PIN_TYPE_UNKNOWN:
- case MBIM_PIN_TYPE_CUSTOM:
- break;
case MBIM_PIN_TYPE_PIN1:
return MM_MODEM_LOCK_SIM_PIN;
case MBIM_PIN_TYPE_PIN2:
@@ -60,6 +93,10 @@ mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type)
return MM_MODEM_LOCK_PH_SP_PIN;
case MBIM_PIN_TYPE_CORPORATE_PUK:
return MM_MODEM_LOCK_PH_CORP_PUK;
+ case MBIM_PIN_TYPE_UNKNOWN:
+ case MBIM_PIN_TYPE_CUSTOM:
+ default:
+ break;
}
return MM_MODEM_LOCK_UNKNOWN;
@@ -90,6 +127,80 @@ mm_modem_3gpp_registration_state_from_mbim_register_state (MbimRegisterState sta
/*****************************************************************************/
+MMModemMode
+mm_modem_mode_from_mbim_data_class (MbimDataClass data_class)
+{
+ MMModemMode mask = MM_MODEM_MODE_NONE;
+
+ /* 3GPP... */
+ if (data_class & (MBIM_DATA_CLASS_GPRS |
+ MBIM_DATA_CLASS_EDGE))
+ mask |= MM_MODEM_MODE_2G;
+ if (data_class & (MBIM_DATA_CLASS_UMTS |
+ MBIM_DATA_CLASS_HSDPA |
+ MBIM_DATA_CLASS_HSUPA))
+ mask |= MM_MODEM_MODE_3G;
+ if (data_class & MBIM_DATA_CLASS_LTE)
+ mask |= MM_MODEM_MODE_4G;
+ if (data_class & (MBIM_DATA_CLASS_5G_NSA |
+ MBIM_DATA_CLASS_5G_SA))
+ mask |= MM_MODEM_MODE_5G;
+
+ /* 3GPP2... */
+ if (data_class & MBIM_DATA_CLASS_1XRTT)
+ mask |= MM_MODEM_MODE_2G;
+ if (data_class & (MBIM_DATA_CLASS_1XEVDO |
+ MBIM_DATA_CLASS_1XEVDO_REVA |
+ MBIM_DATA_CLASS_1XEVDV |
+ MBIM_DATA_CLASS_3XRTT |
+ MBIM_DATA_CLASS_1XEVDO_REVB))
+ mask |= MM_MODEM_MODE_3G;
+ if (data_class & MBIM_DATA_CLASS_UMB)
+ mask |= MM_MODEM_MODE_4G;
+
+ return mask;
+}
+
+MbimDataClass
+mm_mbim_data_class_from_modem_mode (MMModemMode modem_mode,
+ gboolean is_3gpp,
+ gboolean is_cdma)
+{
+ MbimDataClass mask = 0;
+
+ /* 3GPP... */
+ if (is_3gpp) {
+ if (modem_mode & MM_MODEM_MODE_2G)
+ mask |= (MBIM_DATA_CLASS_GPRS |
+ MBIM_DATA_CLASS_EDGE);
+ if (modem_mode & MM_MODEM_MODE_3G)
+ mask |= (MBIM_DATA_CLASS_UMTS |
+ MBIM_DATA_CLASS_HSDPA |
+ MBIM_DATA_CLASS_HSUPA);
+ if (modem_mode & MM_MODEM_MODE_4G)
+ mask |= MBIM_DATA_CLASS_LTE;
+ if (modem_mode & MM_MODEM_MODE_5G)
+ mask |= (MBIM_DATA_CLASS_5G_NSA |
+ MBIM_DATA_CLASS_5G_SA);
+ }
+
+ /* 3GPP2... */
+ if (is_cdma) {
+ if (modem_mode & MM_MODEM_MODE_2G)
+ mask |= MBIM_DATA_CLASS_1XRTT;
+ if (modem_mode & MM_MODEM_MODE_3G)
+ mask |= (MBIM_DATA_CLASS_1XEVDO |
+ MBIM_DATA_CLASS_1XEVDO_REVA |
+ MBIM_DATA_CLASS_1XEVDV |
+ MBIM_DATA_CLASS_3XRTT |
+ MBIM_DATA_CLASS_1XEVDO_REVB);
+ if (modem_mode & MM_MODEM_MODE_4G)
+ mask |= MBIM_DATA_CLASS_UMB;
+ }
+
+ return mask;
+}
+
MMModemAccessTechnology
mm_modem_access_technology_from_mbim_data_class (MbimDataClass data_class)
{
@@ -107,6 +218,11 @@ mm_modem_access_technology_from_mbim_data_class (MbimDataClass data_class)
mask |= MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
if (data_class & MBIM_DATA_CLASS_LTE)
mask |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ if (data_class & MBIM_DATA_CLASS_5G_NSA)
+ mask |= (MM_MODEM_ACCESS_TECHNOLOGY_LTE | MM_MODEM_ACCESS_TECHNOLOGY_5GNR);
+ if (data_class & MBIM_DATA_CLASS_5G_SA)
+ mask |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+
if (data_class & MBIM_DATA_CLASS_1XRTT)
mask |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT;
if (data_class & MBIM_DATA_CLASS_1XEVDO)
@@ -175,92 +291,380 @@ mm_3gpp_network_info_list_from_mbim_providers (const MbimProvider *const *provid
/*****************************************************************************/
+MbimPinType
+mbim_pin_type_from_mm_modem_3gpp_facility (MMModem3gppFacility facility)
+{
+ switch (facility) {
+ case MM_MODEM_3GPP_FACILITY_NET_PERS:
+ return MBIM_PIN_TYPE_NETWORK_PIN;
+ case MM_MODEM_3GPP_FACILITY_NET_SUB_PERS:
+ return MBIM_PIN_TYPE_NETWORK_SUBSET_PIN;
+ case MM_MODEM_3GPP_FACILITY_PROVIDER_PERS:
+ return MBIM_PIN_TYPE_SERVICE_PROVIDER_PIN;
+ case MM_MODEM_3GPP_FACILITY_CORP_PERS:
+ return MBIM_PIN_TYPE_CORPORATE_PIN;
+ case MM_MODEM_3GPP_FACILITY_SIM:
+ return MBIM_PIN_TYPE_PIN1;
+ case MM_MODEM_3GPP_FACILITY_FIXED_DIALING:
+ return MBIM_PIN_TYPE_PIN2;
+ case MM_MODEM_3GPP_FACILITY_PH_SIM:
+ return MBIM_PIN_TYPE_DEVICE_SIM_PIN;
+ case MM_MODEM_3GPP_FACILITY_PH_FSIM:
+ return MBIM_PIN_TYPE_DEVICE_FIRST_SIM_PIN;
+ case MM_MODEM_3GPP_FACILITY_NONE:
+ default:
+ return MBIM_PIN_TYPE_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+
+static const MMMobileEquipmentError mbim_nw_errors[] = {
+ [MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR] = MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_HSS,
+ [MBIM_NW_ERROR_ILLEGAL_MS] = MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_UE,
+ [MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR] = MM_MOBILE_EQUIPMENT_ERROR_IMSI_UNKNOWN_IN_VLR,
+ [MBIM_NW_ERROR_ILLEGAL_ME] = MM_MOBILE_EQUIPMENT_ERROR_ILLEGAL_ME,
+ [MBIM_NW_ERROR_GPRS_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED,
+ [MBIM_NW_ERROR_GPRS_AND_NON_GPRS_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_PS_AND_NON_PS_SERVICES_NOT_ALLOWED,
+ [MBIM_NW_ERROR_PLMN_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_PLMN_NOT_ALLOWED,
+ [MBIM_NW_ERROR_LOCATION_AREA_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_AREA_NOT_ALLOWED,
+ [MBIM_NW_ERROR_ROAMING_NOT_ALLOWED_IN_LOCATION_AREA] = MM_MOBILE_EQUIPMENT_ERROR_ROAMING_NOT_ALLOWED_IN_AREA,
+ [MBIM_NW_ERROR_GPRS_NOT_ALLOWED_IN_PLMN] = MM_MOBILE_EQUIPMENT_ERROR_PS_SERVICES_NOT_ALLOWED_IN_PLMN,
+ [MBIM_NW_ERROR_NO_CELLS_IN_LOCATION_AREA] = MM_MOBILE_EQUIPMENT_ERROR_NO_CELLS_IN_AREA,
+ [MBIM_NW_ERROR_NETWORK_FAILURE] = MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH,
+ [MBIM_NW_ERROR_CONGESTION] = MM_MOBILE_EQUIPMENT_ERROR_CONGESTION,
+ [MBIM_NW_ERROR_GSM_AUTHENTICATION_UNACCEPTABLE] = MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED,
+ [MBIM_NW_ERROR_NOT_AUTHORIZED_FOR_CSG] = MM_MOBILE_EQUIPMENT_ERROR_NOT_AUTHORIZED_FOR_CSG,
+ [MBIM_NW_ERROR_INSUFFICIENT_RESOURCES] = MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES,
+ [MBIM_NW_ERROR_MISSING_OR_UNKNOWN_APN] = MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN,
+ [MBIM_NW_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE,
+ [MBIM_NW_ERROR_USER_AUTHENTICATION_FAILED] = MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED,
+ [MBIM_NW_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW,
+ [MBIM_NW_ERROR_ACTIVATION_REJECTED_UNSPECIFIED] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED,
+ [MBIM_NW_ERROR_SERVICE_OPTION_NOT_SUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED,
+ [MBIM_NW_ERROR_REQUESTED_SERVICE_OPTION_NOT_SUBSCRIBED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED,
+ [MBIM_NW_ERROR_SERVICE_OPTION_TEMPORARILY_OUT_OF_ORDER] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER,
+ [MBIM_NW_ERROR_MAXIMUM_NUMBER_OF_PDP_CONTEXTS_REACHED] = MM_MOBILE_EQUIPMENT_ERROR_MAXIMUM_NUMBER_OF_BEARERS_REACHED,
+ [MBIM_NW_ERROR_REQUESTED_APN_NOT_SUPPORTED_IN_CURRENT_RAT_AND_PLMN] = MM_MOBILE_EQUIPMENT_ERROR_REQUESTED_APN_NOT_SUPPORTED,
+ [MBIM_NW_ERROR_SEMANTICALLY_INCORRECT_MESSAGE] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE,
+ [MBIM_NW_ERROR_PROTOCOL_ERROR_UNSPECIFIED] = MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR,
+ [MBIM_NW_ERROR_IMEI_NOT_ACCEPTED] = MM_MOBILE_EQUIPMENT_ERROR_IMEI_NOT_ACCEPTED,
+ [MBIM_NW_ERROR_MS_IDENTITY_NOT_DERIVED_BY_NETWORK] = MM_MOBILE_EQUIPMENT_ERROR_UE_IDENTITY_NOT_DERIVED_FROM_NETWORK,
+ [MBIM_NW_ERROR_IMPLICITLY_DETACHED] = MM_MOBILE_EQUIPMENT_ERROR_IMPLICITLY_DETACHED,
+ [MBIM_NW_ERROR_MSC_TEMPORARILY_NOT_REACHABLE] = MM_MOBILE_EQUIPMENT_ERROR_MSC_TEMPORARILY_NOT_REACHABLE,
+ [MBIM_NW_ERROR_NO_PDP_CONTEXT_ACTIVATED] = MM_MOBILE_EQUIPMENT_ERROR_NO_BEARER_ACTIVATED,
+ [MBIM_NW_ERROR_PDP_TYPE_IPV4_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED,
+ [MBIM_NW_ERROR_PDP_TYPE_IPV6_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED,
+ [MBIM_NW_ERROR_INVALID_MANDATORY_INFORMATION] = MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION,
+ [MBIM_NW_ERROR_MESSAGE_TYPE_NON_EXISTENT_OR_NOT_IMPLEMENTED] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED,
+ [MBIM_NW_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE,
+ [MBIM_NW_ERROR_INFORMATION_ELEMENT_NON_EXISTENT_OR_NOT_IMPLEMENTED] = MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED,
+ [MBIM_NW_ERROR_CONDITIONAL_IE_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR,
+ [MBIM_NW_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE,
+ [MBIM_NW_ERROR_APN_RESTRICTION_VALUE_INCOMPATIBLE_WITH_ACTIVE_PDP_CONTEXT] = MM_MOBILE_EQUIPMENT_ERROR_APN_RESTRICTION_INCOMPATIBLE,
+ [MBIM_NW_ERROR_MULTIPLE_ACCESSES_TO_A_PDN_CONNECTION_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_ACCESS_TO_PDN_CONNECTION_NOT_ALLOWED,
+ [MBIM_NW_ERROR_NONE] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ /* known unmapped errors */
+ /* MBIM_NW_ERROR_MAC_FAILURE */
+ /* MBIM_NW_ERROR_SYNCH_FAILURE */
+};
+
GError *
-mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error)
+mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error,
+ gpointer log_object)
{
- switch (nw_error) {
- case MBIM_NW_ERROR_IMSI_UNKNOWN_IN_HLR:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_HLR,
- "IMSI unknown in HLR");
- case MBIM_NW_ERROR_ILLEGAL_MS:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_MS,
- "Illegal MS");
- case MBIM_NW_ERROR_IMSI_UNKNOWN_IN_VLR:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_IMSI_UNKNOWN_IN_VLR,
- "IMSI unknown in VLR");
- case MBIM_NW_ERROR_ILLEGAL_ME:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_ILLEGAL_ME,
- "Illegal ME");
- case MBIM_NW_ERROR_GPRS_NOT_ALLOWED:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED,
- "GPRS not allowed");
- case MBIM_NW_ERROR_GPRS_AND_NON_GPRS_NOT_ALLOWED:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED,
- "GPRS and non-GPRS not allowed");
- case MBIM_NW_ERROR_PLMN_NOT_ALLOWED:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_PLMN_NOT_ALLOWED,
- "PLMN not allowed");
- case MBIM_NW_ERROR_LOCATION_AREA_NOT_ALLOWED:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_LOCATION_NOT_ALLOWED,
- "Location area not allowed");
- case MBIM_NW_ERROR_ROAMING_NOT_ALLOWED_IN_LOCATION_AREA:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_ROAMING_NOT_ALLOWED,
- "Roaming not allowed in location area");
- case MBIM_NW_ERROR_GPRS_NOT_ALLOWED_IN_PLMN:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_NOT_ALLOWED,
- "GPRS not allowed in PLMN");
- case MBIM_NW_ERROR_NO_CELLS_IN_LOCATION_AREA:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_NO_CELLS_IN_LOCATION_AREA,
- "No cells in location area");
- case MBIM_NW_ERROR_NETWORK_FAILURE:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_NETWORK_FAILURE,
- "Network failure");
- case MBIM_NW_ERROR_CONGESTION:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_CONGESTION,
- "Congestion");
- case MBIM_NW_ERROR_GSM_AUTHENTICATION_UNACCEPTABLE:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED,
- "GSM authentication unacceptable");
- case MBIM_NW_ERROR_NOT_AUTHORIZED_FOR_CSG:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_USER_AUTHENTICATION_FAILED,
- "Not authorized for this CSG");
- case MBIM_NW_ERROR_MISSING_OR_UNKNOWN_APN:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_MISSING_OR_UNKNOWN_APN,
- "Missing or unknown APN");
- case MBIM_NW_ERROR_SERVICE_OPTION_NOT_SUPPORTED:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUPPORTED,
- "Service option not supported");
- case MBIM_NW_ERROR_REQUESTED_SERVICE_OPTION_NOT_SUBSCRIBED:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_NOT_SUBSCRIBED,
- "Requested service option not subscribed");
- case MBIM_NW_ERROR_SERVICE_OPTION_TEMPORARILY_OUT_OF_ORDER:
+ MMMobileEquipmentError error_code;
+ const gchar *msg;
+
+ /* convert to mobile equipment error */
+ error_code = mbim_nw_errors[nw_error];
+ if (error_code)
+ return mm_mobile_equipment_error_for_code (error_code, log_object);
+
+ /* provide a nicer error message on unmapped errors */
+ msg = mbim_nw_error_get_string (nw_error);
+ if (msg)
return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_SERVICE_OPTION_OUT_OF_ORDER,
- "Service option temporarily out of order");
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Unsupported error (%u): %s",
+ nw_error, msg);
+
+ /* fallback */
+ return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Unknown error");
+}
+
+/*****************************************************************************/
+
+MMBearerAllowedAuth
+mm_bearer_allowed_auth_from_mbim_auth_protocol (MbimAuthProtocol auth_protocol)
+{
+ switch (auth_protocol) {
+ case MBIM_AUTH_PROTOCOL_NONE:
+ return MM_BEARER_ALLOWED_AUTH_NONE;
+ case MBIM_AUTH_PROTOCOL_PAP:
+ return MM_BEARER_ALLOWED_AUTH_PAP;
+ case MBIM_AUTH_PROTOCOL_CHAP:
+ return MM_BEARER_ALLOWED_AUTH_CHAP;
+ case MBIM_AUTH_PROTOCOL_MSCHAPV2:
+ return MM_BEARER_ALLOWED_AUTH_MSCHAPV2;
default:
- return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_GPRS_UNKNOWN,
- "Unknown error (%u)",
- nw_error);
+ return MM_BEARER_ALLOWED_AUTH_UNKNOWN;
+ }
+}
+
+MbimAuthProtocol
+mm_bearer_allowed_auth_to_mbim_auth_protocol (MMBearerAllowedAuth bearer_auth,
+ gpointer log_object,
+ GError **error)
+{
+ gchar *str;
+
+ /* NOTE: the input is a BITMASK, so we try to find a "best match" */
+
+ if (bearer_auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
+ mm_obj_dbg (log_object, "using default (CHAP) authentication method");
+ return MBIM_AUTH_PROTOCOL_CHAP;
+ }
+ if (bearer_auth & MM_BEARER_ALLOWED_AUTH_CHAP)
+ return MBIM_AUTH_PROTOCOL_CHAP;
+ if (bearer_auth & MM_BEARER_ALLOWED_AUTH_PAP)
+ return MBIM_AUTH_PROTOCOL_PAP;
+ if (bearer_auth & MM_BEARER_ALLOWED_AUTH_MSCHAPV2)
+ return MBIM_AUTH_PROTOCOL_MSCHAPV2;
+ if (bearer_auth & MM_BEARER_ALLOWED_AUTH_NONE)
+ return MBIM_AUTH_PROTOCOL_NONE;
+
+ str = mm_bearer_allowed_auth_build_string_from_mask (bearer_auth);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported authentication methods (%s)",
+ str);
+ g_free (str);
+ return MBIM_AUTH_PROTOCOL_NONE;
+}
+
+/*****************************************************************************/
+
+MMBearerApnType
+mm_bearer_apn_type_from_mbim_context_type (MbimContextType context_type)
+{
+ switch (context_type) {
+ case MBIM_CONTEXT_TYPE_INTERNET:
+ return MM_BEARER_APN_TYPE_DEFAULT;
+ case MBIM_CONTEXT_TYPE_VPN:
+ return MM_BEARER_APN_TYPE_PRIVATE;
+ case MBIM_CONTEXT_TYPE_VOICE:
+ return MM_BEARER_APN_TYPE_VOICE;
+ case MBIM_CONTEXT_TYPE_VIDEO_SHARE:
+ return MM_BEARER_APN_TYPE_VIDEO_SHARE;
+ case MBIM_CONTEXT_TYPE_PURCHASE:
+ return MM_BEARER_APN_TYPE_PURCHASE;
+ case MBIM_CONTEXT_TYPE_IMS:
+ return MM_BEARER_APN_TYPE_IMS;
+ case MBIM_CONTEXT_TYPE_MMS:
+ return MM_BEARER_APN_TYPE_MMS;
+ case MBIM_CONTEXT_TYPE_LOCAL:
+ return MM_BEARER_APN_TYPE_LOCAL;
+ case MBIM_CONTEXT_TYPE_ADMIN:
+ return MM_BEARER_APN_TYPE_MANAGEMENT;
+ case MBIM_CONTEXT_TYPE_APP:
+ return MM_BEARER_APN_TYPE_APP;
+ case MBIM_CONTEXT_TYPE_XCAP:
+ return MM_BEARER_APN_TYPE_XCAP;
+ case MBIM_CONTEXT_TYPE_TETHERING:
+ return MM_BEARER_APN_TYPE_TETHERING;
+ case MBIM_CONTEXT_TYPE_EMERGENCY_CALLING:
+ return MM_BEARER_APN_TYPE_EMERGENCY;
+ /* some types unused right now */
+ case MBIM_CONTEXT_TYPE_INVALID:
+ case MBIM_CONTEXT_TYPE_NONE:
+ default:
+ return MM_BEARER_APN_TYPE_NONE;
+ }
+}
+
+MbimContextType
+mm_bearer_apn_type_to_mbim_context_type (MMBearerApnType apn_type,
+ gpointer log_object,
+ GError **error)
+{
+ g_autofree gchar *str = NULL;
+
+ /* NOTE: the input is a BITMASK, so we try to find a "best match" */
+
+ if (apn_type == MM_BEARER_APN_TYPE_NONE) {
+ mm_obj_dbg (log_object, "using default (internet) APN type");
+ return MBIM_CONTEXT_TYPE_INTERNET;
+ }
+
+ if (apn_type & MM_BEARER_APN_TYPE_DEFAULT)
+ return MBIM_CONTEXT_TYPE_INTERNET;
+ if (apn_type & MM_BEARER_APN_TYPE_IMS)
+ return MBIM_CONTEXT_TYPE_IMS;
+ if (apn_type & MM_BEARER_APN_TYPE_MMS)
+ return MBIM_CONTEXT_TYPE_MMS;
+ if (apn_type &MM_BEARER_APN_TYPE_MANAGEMENT)
+ return MBIM_CONTEXT_TYPE_ADMIN;
+ if (apn_type & MM_BEARER_APN_TYPE_VOICE)
+ return MBIM_CONTEXT_TYPE_VOICE;
+ if (apn_type & MM_BEARER_APN_TYPE_PRIVATE)
+ return MBIM_CONTEXT_TYPE_VPN;
+ if (apn_type & MM_BEARER_APN_TYPE_PURCHASE)
+ return MBIM_CONTEXT_TYPE_PURCHASE;
+ if (apn_type & MM_BEARER_APN_TYPE_VIDEO_SHARE)
+ return MBIM_CONTEXT_TYPE_VIDEO_SHARE;
+ if (apn_type & MM_BEARER_APN_TYPE_LOCAL)
+ return MBIM_CONTEXT_TYPE_LOCAL;
+ if (apn_type & MM_BEARER_APN_TYPE_APP)
+ return MBIM_CONTEXT_TYPE_APP;
+ if (apn_type & MM_BEARER_APN_TYPE_XCAP)
+ return MBIM_CONTEXT_TYPE_XCAP;
+ if (apn_type & MM_BEARER_APN_TYPE_TETHERING)
+ return MBIM_CONTEXT_TYPE_TETHERING;
+
+ str = mm_bearer_apn_type_build_string_from_mask (apn_type);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported APN types (%s)",
+ str);
+ return MBIM_CONTEXT_TYPE_NONE;
+}
+
+/*****************************************************************************/
+
+MMBearerIpFamily
+mm_bearer_ip_family_from_mbim_context_ip_type (MbimContextIpType ip_type)
+{
+ switch (ip_type) {
+ case MBIM_CONTEXT_IP_TYPE_IPV4:
+ return MM_BEARER_IP_FAMILY_IPV4;
+ case MBIM_CONTEXT_IP_TYPE_IPV6:
+ return MM_BEARER_IP_FAMILY_IPV6;
+ case MBIM_CONTEXT_IP_TYPE_IPV4V6:
+ return MM_BEARER_IP_FAMILY_IPV4V6;
+ case MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6:
+ return MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6;
+ case MBIM_CONTEXT_IP_TYPE_DEFAULT:
+ default:
+ return MM_BEARER_IP_FAMILY_NONE;
+ }
+}
+
+MbimContextIpType
+mm_bearer_ip_family_to_mbim_context_ip_type (MMBearerIpFamily ip_family,
+ GError **error)
+{
+ gchar *str;
+
+ /* NOTE: the input is a BITMASK, so we try to find a "best match" */
+
+ switch ((guint)ip_family) {
+ case MM_BEARER_IP_FAMILY_IPV4:
+ return MBIM_CONTEXT_IP_TYPE_IPV4;
+ case MM_BEARER_IP_FAMILY_IPV6:
+ return MBIM_CONTEXT_IP_TYPE_IPV6;
+ case MM_BEARER_IP_FAMILY_IPV4V6:
+ return MBIM_CONTEXT_IP_TYPE_IPV4V6;
+ case (MM_BEARER_IP_FAMILY_IPV4 | MM_BEARER_IP_FAMILY_IPV6):
+ return MBIM_CONTEXT_IP_TYPE_IPV4_AND_IPV6;
+ case MM_BEARER_IP_FAMILY_NONE:
+ case MM_BEARER_IP_FAMILY_ANY:
+ /* A valid default IP family should have been specified */
+ g_assert_not_reached ();
+ default:
+ break;
+ }
+
+ str = mm_bearer_ip_family_build_string_from_mask (ip_family);
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported IP type configuration: '%s'",
+ str);
+ g_free (str);
+ return MBIM_CONTEXT_IP_TYPE_DEFAULT;
+}
+
+/*****************************************************************************/
+
+/* index in the array is the code point (8 possible values), and the actual
+ * value is the lower limit of the error rate range. */
+static const gdouble bit_error_rate_ranges[] = { 0.00, 0.20, 0.40, 0.80, 1.60, 3.20, 6.40, 12.80 };
+static const gdouble frame_error_rate_ranges[] = { 0.00, 0.01, 0.10, 0.50, 1.00, 2.00, 4.00, 8.00 };
+
+gboolean
+mm_signal_error_rate_percentage_from_coded_value (guint coded_value,
+ gdouble *out_percentage,
+ gboolean is_gsm,
+ GError **error)
+{
+ if ((is_gsm && (coded_value >= G_N_ELEMENTS (bit_error_rate_ranges))) ||
+ (!is_gsm && (coded_value >= G_N_ELEMENTS (frame_error_rate_ranges)))) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "error rate coded value out of range: %u", coded_value);
+ return FALSE;
+ }
+
+ *out_percentage = (is_gsm ? bit_error_rate_ranges[coded_value] : frame_error_rate_ranges[coded_value]);
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_signal_rssi_from_coded_value (guint coded_value,
+ gdouble *out_rssi,
+ GError **error)
+{
+ /* expected values between 0 and 31 */
+ if (coded_value > 31) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "rssi coded value out of range: %u", coded_value);
+ return FALSE;
+ }
+
+ *out_rssi = (gdouble)coded_value - 113;
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_signal_rsrp_from_coded_value (guint coded_value,
+ gdouble *out_rsrp,
+ GError **error)
+{
+ /* expected values between 0 and 126 */
+ if (coded_value > 126) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "rsrp coded value out of range: %u", coded_value);
+ return FALSE;
+ }
+
+ *out_rsrp = (gdouble)coded_value - 156;
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_signal_snr_from_coded_value (guint coded_value,
+ gdouble *out_snr,
+ GError **error)
+{
+ /* expected values between 0 and 126 */
+ if (coded_value > 127) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "snr coded value out of range: %u", coded_value);
+ return FALSE;
}
+
+ *out_snr = ((gdouble)coded_value)/2 - 23;
+ return TRUE;
}
/*****************************************************************************/
@@ -277,7 +681,206 @@ mm_sms_state_from_mbim_message_status (MbimSmsStatus status)
return MM_SMS_STATE_STORED;
case MBIM_SMS_STATUS_SENT:
return MM_SMS_STATE_SENT;
+ default:
+ break;
}
return MM_SMS_STATE_UNKNOWN;
}
+
+/*****************************************************************************/
+
+guint
+mm_signal_quality_from_mbim_signal_state (guint rssi,
+ MbimRsrpSnrInfoArray *rsrp_snr,
+ guint32 rsrp_snr_count,
+ gpointer log_object)
+{
+ guint quality;
+
+ /* When MBIMEx is enabled we may get RSSI unset, but per access technology
+ * RSRP available. When more than one access technology in use (e.g. 4G+5G in
+ * 5G NSA), take the highest RSRP value reported. */
+ if (rssi == 99 && rsrp_snr && rsrp_snr_count) {
+ guint i;
+ gint max_rsrp = G_MININT;
+
+ for (i = 0; i < rsrp_snr_count; i++) {
+ MbimRsrpSnrInfo *info;
+
+ info = rsrp_snr[i];
+ /* scale the value to dBm */
+ if (info->rsrp < 127) {
+ gint rsrp;
+
+ rsrp = -157 + info->rsrp;
+ if (rsrp > max_rsrp)
+ max_rsrp = rsrp;
+ }
+ }
+ quality = MM_RSRP_TO_QUALITY (max_rsrp);
+ mm_obj_dbg (log_object, "signal state update: %ddBm --> %u%%", max_rsrp, quality);
+ } else {
+ /* Normalize the quality. 99 means unknown, we default it to 0 */
+ quality = MM_CLAMP_HIGH (rssi == 99 ? 0 : rssi, 31) * 100 / 31;
+ mm_obj_dbg (log_object, "signal state update: %u --> %u%%", rssi, quality);
+ }
+
+ return quality;
+}
+
+static MMSignal **
+select_mbim_signal_with_data_class (MbimDataClass data_class,
+ MMSignal **cdma,
+ MMSignal **evdo,
+ MMSignal **gsm,
+ MMSignal **umts,
+ MMSignal **lte,
+ MMSignal **nr5g)
+{
+ switch (data_class) {
+ case MBIM_DATA_CLASS_5G_NSA:
+ case MBIM_DATA_CLASS_5G_SA:
+ return nr5g;
+ case MBIM_DATA_CLASS_LTE:
+ return lte;
+ case MBIM_DATA_CLASS_UMTS:
+ case MBIM_DATA_CLASS_HSDPA:
+ case MBIM_DATA_CLASS_HSUPA:
+ return umts;
+ case MBIM_DATA_CLASS_GPRS:
+ case MBIM_DATA_CLASS_EDGE:
+ return gsm;
+ case MBIM_DATA_CLASS_1XEVDO:
+ case MBIM_DATA_CLASS_1XEVDO_REVA:
+ case MBIM_DATA_CLASS_1XEVDV:
+ case MBIM_DATA_CLASS_3XRTT:
+ case MBIM_DATA_CLASS_1XEVDO_REVB:
+ return evdo;
+ case MBIM_DATA_CLASS_1XRTT:
+ return cdma;
+ case MBIM_DATA_CLASS_UMB:
+ case MBIM_DATA_CLASS_CUSTOM:
+ default:
+ return NULL;
+ }
+}
+
+gboolean
+mm_signal_from_mbim_signal_state (MbimDataClass data_class,
+ guint coded_rssi,
+ guint coded_error_rate,
+ MbimRsrpSnrInfoArray *rsrp_snr,
+ guint32 rsrp_snr_count,
+ gpointer log_object,
+ MMSignal **out_cdma,
+ MMSignal **out_evdo,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ MMSignal **out_nr5g)
+{
+ MMSignal **tmp;
+ MMSignal **last_updated = NULL;
+ guint n_out_updated = 0;
+
+ *out_cdma = NULL;
+ *out_evdo = NULL;
+ *out_gsm = NULL;
+ *out_umts = NULL;
+ *out_lte = NULL;
+ *out_nr5g = NULL;
+
+ /* When MBIMEx v2.0 is available, we get LTE+5GNR information reported
+ * in the RSRP/SNR list of items. */
+ if (rsrp_snr && rsrp_snr_count) {
+ guint i;
+
+ for (i = 0; i < rsrp_snr_count; i++) {
+ MbimRsrpSnrInfo *info;
+
+ info = rsrp_snr[i];
+
+ tmp = select_mbim_signal_with_data_class (info->system_type,
+ out_cdma, out_evdo,
+ out_gsm, out_umts, out_lte, out_nr5g);
+ if (!tmp || ((info->rsrp == 0xFFFFFFFF) && (info->snr == 0xFFFFFFFF)))
+ continue;
+
+ last_updated = tmp;
+ n_out_updated++;
+
+ *tmp = mm_signal_new ();
+
+ mm_signal_set_rsrp (*tmp, MM_SIGNAL_UNKNOWN);
+ if (info->rsrp != 0xFFFFFFFF) {
+ g_autoptr(GError) error = NULL;
+ gdouble rsrp;
+
+ if (!mm_signal_rsrp_from_coded_value (info->rsrp, &rsrp, &error))
+ mm_obj_dbg (log_object, "couldn't convert RSRP coded value '%u': %s", info->rsrp, error->message);
+ else
+ mm_signal_set_rsrp (*tmp, rsrp);
+ }
+
+ mm_signal_set_snr (*tmp, MM_SIGNAL_UNKNOWN);
+ if (info->snr != 0xFFFFFFFF) {
+ g_autoptr(GError) error = NULL;
+ gdouble snr;
+
+ if (!mm_signal_snr_from_coded_value (info->snr, &snr, &error))
+ mm_obj_dbg (log_object, "couldn't convert SNR coded value '%u': %s", info->snr, error->message);
+ else
+ mm_signal_set_snr (*tmp, snr);
+ }
+ }
+ }
+
+ /* The MBIM v1.0 details (RSSI, error rate) will only be set if
+ * the target access technology is known without any doubt.
+ * E.g. if we are in 5GNSA (4G+5G), we will only set the fields
+ * if one of them has valid values. If both have valid values,
+ * we'll skip updating RSSI and error rate, as we wouldn't know
+ * to which of them applies. */
+ if (n_out_updated > 1)
+ return TRUE;
+
+ if (n_out_updated == 0) {
+ tmp = select_mbim_signal_with_data_class (data_class,
+ out_cdma, out_evdo,
+ out_gsm, out_umts, out_lte, out_nr5g);
+ if (!tmp)
+ return FALSE;
+ *tmp = mm_signal_new ();
+ } else {
+ tmp = last_updated;
+ g_assert (tmp && *tmp);
+ }
+
+ mm_signal_set_error_rate (*tmp, MM_SIGNAL_UNKNOWN);
+ if (coded_error_rate != 99) {
+ g_autoptr(GError) error = NULL;
+ gdouble error_rate;
+
+ if (!mm_signal_error_rate_percentage_from_coded_value (coded_error_rate,
+ &error_rate,
+ data_class == (MBIM_DATA_CLASS_GPRS | MBIM_DATA_CLASS_EDGE),
+ &error))
+ mm_obj_dbg (log_object, "couldn't convert error rate coded value '%u': %s", coded_error_rate, error->message);
+ else
+ mm_signal_set_error_rate (*tmp, error_rate);
+ }
+
+ mm_signal_set_rssi (*tmp, MM_SIGNAL_UNKNOWN);
+ if (coded_rssi != 99) {
+ g_autoptr(GError) error = NULL;
+ gdouble rssi;
+
+ if (!mm_signal_rssi_from_coded_value (coded_rssi, &rssi, &error))
+ mm_obj_dbg (log_object, "couldn't convert RSSI coded value '%u': %s", coded_rssi, error->message);
+ else
+ mm_signal_set_rssi (*tmp, rssi);
+ }
+
+ return TRUE;
+}
diff --git a/src/mm-modem-helpers-mbim.h b/src/mm-modem-helpers-mbim.h
index 67b6b57f..2c3d2bae 100644
--- a/src/mm-modem-helpers-mbim.h
+++ b/src/mm-modem-helpers-mbim.h
@@ -19,26 +19,91 @@
#include <config.h>
#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
#include <libmbim-glib.h>
/*****************************************************************************/
/* MBIM/BasicConnect to MM translations */
+MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class,
+ MbimDataClass caps_data_class,
+ const gchar *caps_custom_data_class);
+
MMModemLock mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type);
MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_mbim_register_state (MbimRegisterState state);
+MMModemMode mm_modem_mode_from_mbim_data_class (MbimDataClass data_class);
+
+MbimDataClass mm_mbim_data_class_from_modem_mode (MMModemMode modem_mode,
+ gboolean is_3gpp,
+ gboolean is_cdma);
+
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);
+MbimPinType mbim_pin_type_from_mm_modem_3gpp_facility (MMModem3gppFacility facility);
+
+GError *mm_mobile_equipment_error_from_mbim_nw_error (MbimNwError nw_error,
+ gpointer log_object);
+
+MMBearerAllowedAuth mm_bearer_allowed_auth_from_mbim_auth_protocol (MbimAuthProtocol auth_protocol);
+MbimAuthProtocol mm_bearer_allowed_auth_to_mbim_auth_protocol (MMBearerAllowedAuth bearer_auth,
+ gpointer log_object,
+ GError **error);
+MMBearerIpFamily mm_bearer_ip_family_from_mbim_context_ip_type (MbimContextIpType ip_type);
+MbimContextIpType mm_bearer_ip_family_to_mbim_context_ip_type (MMBearerIpFamily ip_family,
+ GError **error);
+MMBearerApnType mm_bearer_apn_type_from_mbim_context_type (MbimContextType context_type);
+MbimContextType mm_bearer_apn_type_to_mbim_context_type (MMBearerApnType apn_type,
+ gpointer log_object,
+ GError **error);
+
+gboolean mm_signal_error_rate_percentage_from_coded_value (guint coded_value,
+ gdouble *out_percentage,
+ gboolean is_gsm,
+ GError **error);
+
+gboolean mm_signal_rssi_from_coded_value (guint coded_value,
+ gdouble *out_rssi,
+ GError **error);
+
+gboolean mm_signal_rsrp_from_coded_value (guint coded_value,
+ gdouble *out_rsrp,
+ GError **error);
+
+gboolean mm_signal_snr_from_coded_value (guint coded_value,
+ gdouble *out_snr,
+ GError **error);
/*****************************************************************************/
/* MBIM/SMS to MM translations */
MMSmsState mm_sms_state_from_mbim_message_status (MbimSmsStatus status);
+/*****************************************************************************/
+
+guint mm_signal_quality_from_mbim_signal_state (guint rssi,
+ MbimRsrpSnrInfoArray *rsrp_snr,
+ guint32 rsrp_snr_count,
+ gpointer log_object);
+
+gboolean mm_signal_from_mbim_signal_state (MbimDataClass data_class,
+ guint coded_rssi,
+ guint coded_error_rate,
+ MbimRsrpSnrInfoArray *rsrp_snr,
+ guint32 rsrp_snr_count,
+ gpointer log_object,
+ MMSignal **out_cdma,
+ MMSignal **out_evdo,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ MMSignal **out_nr5g);
+
#endif /* MM_MODEM_HELPERS_MBIM_H */
diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c
index c673697f..7b71a785 100644
--- a/src/mm-modem-helpers-qmi.c
+++ b/src/mm-modem-helpers-qmi.c
@@ -10,17 +10,26 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2012-2018 Google, Inc.
+ * Copyright (C) 2018 Aleksander Morgado <aleksander@aleksander.es>
*/
+#include <string.h>
+
+#include <ModemManager.h>
+#include <mm-errors-types.h>
+
#include "mm-modem-helpers-qmi.h"
+#include "mm-modem-helpers.h"
+#include "mm-error-helpers.h"
#include "mm-enums-types.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
/*****************************************************************************/
MMModemCapability
-mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network)
+mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network,
+ gpointer log_object)
{
switch (network) {
case QMI_DMS_RADIO_INTERFACE_CDMA20001X:
@@ -33,9 +42,12 @@ mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network)
return MM_MODEM_CAPABILITY_GSM_UMTS;
case QMI_DMS_RADIO_INTERFACE_LTE:
return MM_MODEM_CAPABILITY_LTE;
+ case QMI_DMS_RADIO_INTERFACE_TDS:
+ return MM_MODEM_CAPABILITY_TDS;
+ case QMI_DMS_RADIO_INTERFACE_5GNR:
+ return MM_MODEM_CAPABILITY_5GNR;
default:
- mm_warn ("Unhandled QMI radio interface (%u)",
- (guint)network);
+ mm_obj_warn (log_object, "unhandled QMI radio interface '%u'", (guint)network);
return MM_MODEM_CAPABILITY_NONE;
}
}
@@ -43,7 +55,8 @@ mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network)
/*****************************************************************************/
MMModemMode
-mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network)
+mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network,
+ gpointer log_object)
{
switch (network) {
case QMI_DMS_RADIO_INTERFACE_CDMA20001X:
@@ -56,9 +69,11 @@ mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network)
return MM_MODEM_MODE_3G;
case QMI_DMS_RADIO_INTERFACE_LTE:
return MM_MODEM_MODE_4G;
+ case QMI_DMS_RADIO_INTERFACE_5GNR:
+ return MM_MODEM_MODE_5G;
+ case QMI_DMS_RADIO_INTERFACE_TDS:
default:
- mm_warn ("Unhandled QMI radio interface (%u)",
- (guint)network);
+ mm_obj_warn (log_object, "unhandled QMI radio interface '%u'", (guint)network);
return MM_MODEM_MODE_NONE;
}
}
@@ -98,6 +113,32 @@ mm_modem_lock_from_qmi_uim_pin_status (QmiDmsUimPinStatus status,
/*****************************************************************************/
+gboolean
+mm_pin_enabled_from_qmi_uim_pin_status (QmiDmsUimPinStatus status)
+{
+ switch (status) {
+ case QMI_DMS_UIM_PIN_STATUS_ENABLED_NOT_VERIFIED:
+ case QMI_DMS_UIM_PIN_STATUS_ENABLED_VERIFIED:
+ case QMI_DMS_UIM_PIN_STATUS_BLOCKED:
+ case QMI_DMS_UIM_PIN_STATUS_PERMANENTLY_BLOCKED:
+ case QMI_DMS_UIM_PIN_STATUS_UNBLOCKED:
+ case QMI_DMS_UIM_PIN_STATUS_CHANGED:
+ /* assume the PIN to be enabled then */
+ return TRUE;
+
+ case QMI_DMS_UIM_PIN_STATUS_DISABLED:
+ case QMI_DMS_UIM_PIN_STATUS_NOT_INITIALIZED:
+ /* assume the PIN to be disabled then */
+ return FALSE;
+
+ default:
+ /* by default assume disabled */
+ return FALSE;
+ }
+}
+
+/*****************************************************************************/
+
QmiDmsUimFacility
mm_3gpp_facility_to_qmi_uim_facility (MMModem3gppFacility mm)
{
@@ -105,19 +146,18 @@ mm_3gpp_facility_to_qmi_uim_facility (MMModem3gppFacility mm)
case MM_MODEM_3GPP_FACILITY_PH_SIM:
/* Not really sure about this one; it may be PH_FSIM? */
return QMI_DMS_UIM_FACILITY_PF;
-
case MM_MODEM_3GPP_FACILITY_NET_PERS:
return QMI_DMS_UIM_FACILITY_PN;
-
case MM_MODEM_3GPP_FACILITY_NET_SUB_PERS:
return QMI_DMS_UIM_FACILITY_PU;
-
case MM_MODEM_3GPP_FACILITY_PROVIDER_PERS:
return QMI_DMS_UIM_FACILITY_PP;
-
case MM_MODEM_3GPP_FACILITY_CORP_PERS:
return QMI_DMS_UIM_FACILITY_PC;
-
+ case MM_MODEM_3GPP_FACILITY_NONE:
+ case MM_MODEM_3GPP_FACILITY_SIM:
+ case MM_MODEM_3GPP_FACILITY_FIXED_DIALING:
+ case MM_MODEM_3GPP_FACILITY_PH_FSIM:
default:
/* Never try to ask for a facility we cannot translate */
g_assert_not_reached ();
@@ -135,26 +175,26 @@ static const DmsBandsMap dms_bands_map [] = {
/* CDMA bands */
{
(QMI_DMS_BAND_CAPABILITY_BC_0_A_SYSTEM | QMI_DMS_BAND_CAPABILITY_BC_0_B_SYSTEM),
- MM_MODEM_BAND_CDMA_BC0_CELLULAR_800
+ MM_MODEM_BAND_CDMA_BC0
},
- { QMI_DMS_BAND_CAPABILITY_BC_1_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC1_PCS_1900 },
- { QMI_DMS_BAND_CAPABILITY_BC_2, MM_MODEM_BAND_CDMA_BC2_TACS },
- { QMI_DMS_BAND_CAPABILITY_BC_3_A_SYSTEM, MM_MODEM_BAND_CDMA_BC3_JTACS },
- { QMI_DMS_BAND_CAPABILITY_BC_4_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS },
- { QMI_DMS_BAND_CAPABILITY_BC_5_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC5_NMT450 },
- { QMI_DMS_BAND_CAPABILITY_BC_6, MM_MODEM_BAND_CDMA_BC6_IMT2000 },
- { QMI_DMS_BAND_CAPABILITY_BC_7, MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 },
- { QMI_DMS_BAND_CAPABILITY_BC_8, MM_MODEM_BAND_CDMA_BC8_1800 },
- { QMI_DMS_BAND_CAPABILITY_BC_9, MM_MODEM_BAND_CDMA_BC9_900 },
- { QMI_DMS_BAND_CAPABILITY_BC_10, MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 },
- { QMI_DMS_BAND_CAPABILITY_BC_11, MM_MODEM_BAND_CDMA_BC11_PAMR_400 },
- { QMI_DMS_BAND_CAPABILITY_BC_12, MM_MODEM_BAND_CDMA_BC12_PAMR_800 },
- { QMI_DMS_BAND_CAPABILITY_BC_14, MM_MODEM_BAND_CDMA_BC14_PCS2_1900 },
- { QMI_DMS_BAND_CAPABILITY_BC_15, MM_MODEM_BAND_CDMA_BC15_AWS },
- { QMI_DMS_BAND_CAPABILITY_BC_16, MM_MODEM_BAND_CDMA_BC16_US_2500 },
- { QMI_DMS_BAND_CAPABILITY_BC_17, MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 },
- { QMI_DMS_BAND_CAPABILITY_BC_18, MM_MODEM_BAND_CDMA_BC18_US_PS_700 },
- { QMI_DMS_BAND_CAPABILITY_BC_19, MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 },
+ { QMI_DMS_BAND_CAPABILITY_BC_1_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC1 },
+ { QMI_DMS_BAND_CAPABILITY_BC_2, MM_MODEM_BAND_CDMA_BC2 },
+ { QMI_DMS_BAND_CAPABILITY_BC_3_A_SYSTEM, MM_MODEM_BAND_CDMA_BC3 },
+ { QMI_DMS_BAND_CAPABILITY_BC_4_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC4 },
+ { QMI_DMS_BAND_CAPABILITY_BC_5_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC5 },
+ { QMI_DMS_BAND_CAPABILITY_BC_6, MM_MODEM_BAND_CDMA_BC6 },
+ { QMI_DMS_BAND_CAPABILITY_BC_7, MM_MODEM_BAND_CDMA_BC7 },
+ { QMI_DMS_BAND_CAPABILITY_BC_8, MM_MODEM_BAND_CDMA_BC8 },
+ { QMI_DMS_BAND_CAPABILITY_BC_9, MM_MODEM_BAND_CDMA_BC9 },
+ { QMI_DMS_BAND_CAPABILITY_BC_10, MM_MODEM_BAND_CDMA_BC10 },
+ { QMI_DMS_BAND_CAPABILITY_BC_11, MM_MODEM_BAND_CDMA_BC11 },
+ { QMI_DMS_BAND_CAPABILITY_BC_12, MM_MODEM_BAND_CDMA_BC12 },
+ { QMI_DMS_BAND_CAPABILITY_BC_14, MM_MODEM_BAND_CDMA_BC14 },
+ { QMI_DMS_BAND_CAPABILITY_BC_15, MM_MODEM_BAND_CDMA_BC15 },
+ { QMI_DMS_BAND_CAPABILITY_BC_16, MM_MODEM_BAND_CDMA_BC16 },
+ { QMI_DMS_BAND_CAPABILITY_BC_17, MM_MODEM_BAND_CDMA_BC17 },
+ { QMI_DMS_BAND_CAPABILITY_BC_18, MM_MODEM_BAND_CDMA_BC18 },
+ { QMI_DMS_BAND_CAPABILITY_BC_19, MM_MODEM_BAND_CDMA_BC19 },
/* GSM bands */
{ QMI_DMS_BAND_CAPABILITY_GSM_DCS_1800, MM_MODEM_BAND_DCS },
@@ -164,37 +204,28 @@ static const DmsBandsMap dms_bands_map [] = {
},
{ QMI_DMS_BAND_CAPABILITY_GSM_PCS_1900, MM_MODEM_BAND_PCS },
{ QMI_DMS_BAND_CAPABILITY_GSM_850, MM_MODEM_BAND_G850 },
+ { QMI_DMS_BAND_CAPABILITY_GSM_450, MM_MODEM_BAND_G450 },
+ { QMI_DMS_BAND_CAPABILITY_GSM_480, MM_MODEM_BAND_G480 },
+ { QMI_DMS_BAND_CAPABILITY_GSM_750, MM_MODEM_BAND_G750 },
/* UMTS/WCDMA bands */
- { QMI_DMS_BAND_CAPABILITY_WCDMA_2100, MM_MODEM_BAND_U2100 },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_DCS_1800, MM_MODEM_BAND_U1800 },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_PCS_1900, MM_MODEM_BAND_U1900 },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_1700_US, MM_MODEM_BAND_U17IV },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_800, MM_MODEM_BAND_U800 },
- {
- (QMI_DMS_BAND_CAPABILITY_WCDMA_850_US | QMI_DMS_BAND_CAPABILITY_WCDMA_850_JAPAN),
- MM_MODEM_BAND_U850
- },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_900, MM_MODEM_BAND_U900 },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_1700_JAPAN, MM_MODEM_BAND_U17IX },
- { QMI_DMS_BAND_CAPABILITY_WCDMA_2600, MM_MODEM_BAND_U2600 }
-
- /* NOTE. The following bands were unmatched:
- *
- * - QMI_DMS_BAND_CAPABILITY_GSM_900_PRIMARY
- * - QMI_DMS_BAND_CAPABILITY_GSM_450
- * - QMI_DMS_BAND_CAPABILITY_GSM_480
- * - QMI_DMS_BAND_CAPABILITY_GSM_750
- * - QMI_DMS_BAND_CAPABILITY_GSM_900_RAILWAILS
- * - QMI_DMS_BAND_CAPABILITY_WCDMA_1500
- * - MM_MODEM_BAND_CDMA_BC13_IMT2000_2500
- * - MM_MODEM_BAND_U1900
- */
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_2100, MM_MODEM_BAND_UTRAN_1 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_DCS_1800, MM_MODEM_BAND_UTRAN_3 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_PCS_1900, MM_MODEM_BAND_UTRAN_2 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_1700_US, MM_MODEM_BAND_UTRAN_4 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_800, MM_MODEM_BAND_UTRAN_6 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_850_US, MM_MODEM_BAND_UTRAN_5 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_900, MM_MODEM_BAND_UTRAN_8 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_1700_JAPAN, MM_MODEM_BAND_UTRAN_9 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_2600, MM_MODEM_BAND_UTRAN_7 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_1500, MM_MODEM_BAND_UTRAN_11 },
+ { QMI_DMS_BAND_CAPABILITY_WCDMA_850_JAPAN, MM_MODEM_BAND_UTRAN_19 },
};
static void
-dms_add_qmi_bands (GArray *mm_bands,
- QmiDmsBandCapability qmi_bands)
+dms_add_qmi_bands (GArray *mm_bands,
+ QmiDmsBandCapability qmi_bands,
+ gpointer log_object)
{
static QmiDmsBandCapability qmi_bands_expected = 0;
QmiDmsBandCapability not_expected;
@@ -212,11 +243,10 @@ dms_add_qmi_bands (GArray *mm_bands,
/* Log about the bands that cannot be represented in ModemManager */
not_expected = ((qmi_bands_expected ^ qmi_bands) & qmi_bands);
if (not_expected) {
- gchar *aux;
+ g_autofree gchar *aux = NULL;
aux = qmi_dms_band_capability_build_string_from_mask (not_expected);
- mm_dbg ("Cannot add the following bands: '%s'", aux);
- g_free (aux);
+ mm_obj_dbg (log_object, "cannot add the following bands: '%s'", aux);
}
/* And add the expected ones */
@@ -232,46 +262,45 @@ typedef struct {
} DmsLteBandsMap;
static const DmsLteBandsMap dms_lte_bands_map [] = {
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_1, MM_MODEM_BAND_EUTRAN_I },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_2, MM_MODEM_BAND_EUTRAN_II },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_3, MM_MODEM_BAND_EUTRAN_III },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_4, MM_MODEM_BAND_EUTRAN_IV },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_5, MM_MODEM_BAND_EUTRAN_V },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_6, MM_MODEM_BAND_EUTRAN_VI },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_7, MM_MODEM_BAND_EUTRAN_VII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_8, MM_MODEM_BAND_EUTRAN_VIII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_9, MM_MODEM_BAND_EUTRAN_IX },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_10, MM_MODEM_BAND_EUTRAN_X },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_11, MM_MODEM_BAND_EUTRAN_XI },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_12, MM_MODEM_BAND_EUTRAN_XII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_13, MM_MODEM_BAND_EUTRAN_XIII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_14, MM_MODEM_BAND_EUTRAN_XIV },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_17, MM_MODEM_BAND_EUTRAN_XVII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_18, MM_MODEM_BAND_EUTRAN_XVIII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_19, MM_MODEM_BAND_EUTRAN_XIX },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_20, MM_MODEM_BAND_EUTRAN_XX },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_21, MM_MODEM_BAND_EUTRAN_XXI },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_24, MM_MODEM_BAND_EUTRAN_XXIV },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_25, MM_MODEM_BAND_EUTRAN_XXV },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_33, MM_MODEM_BAND_EUTRAN_XXXIII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_34, MM_MODEM_BAND_EUTRAN_XXXIV },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_35, MM_MODEM_BAND_EUTRAN_XXXV },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_36, MM_MODEM_BAND_EUTRAN_XXXVI },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_37, MM_MODEM_BAND_EUTRAN_XXXVII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_38, MM_MODEM_BAND_EUTRAN_XXXVIII },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_39, MM_MODEM_BAND_EUTRAN_XXXIX },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_40, MM_MODEM_BAND_EUTRAN_XL },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_41, MM_MODEM_BAND_EUTRAN_XLI },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_42, MM_MODEM_BAND_EUTRAN_XLI },
- { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_43, MM_MODEM_BAND_EUTRAN_XLIII }
-
- /* NOTE. The following bands were unmatched:
- *
- * - MM_MODEM_BAND_EUTRAN_XXII
- * - MM_MODEM_BAND_EUTRAN_XXIII
- * - MM_MODEM_BAND_EUTRAN_XXVI
- * - MM_MODEM_BAND_EUTRAN_XLIV
- */
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_1, MM_MODEM_BAND_EUTRAN_1 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_2, MM_MODEM_BAND_EUTRAN_2 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_3, MM_MODEM_BAND_EUTRAN_3 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_4, MM_MODEM_BAND_EUTRAN_4 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_5, MM_MODEM_BAND_EUTRAN_5 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_6, MM_MODEM_BAND_EUTRAN_6 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_7, MM_MODEM_BAND_EUTRAN_7 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_8, MM_MODEM_BAND_EUTRAN_8 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_9, MM_MODEM_BAND_EUTRAN_9 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_10, MM_MODEM_BAND_EUTRAN_10 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_11, MM_MODEM_BAND_EUTRAN_11 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_12, MM_MODEM_BAND_EUTRAN_12 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_13, MM_MODEM_BAND_EUTRAN_13 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_14, MM_MODEM_BAND_EUTRAN_14 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_17, MM_MODEM_BAND_EUTRAN_17 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_18, MM_MODEM_BAND_EUTRAN_18 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_19, MM_MODEM_BAND_EUTRAN_19 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_20, MM_MODEM_BAND_EUTRAN_20 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_21, MM_MODEM_BAND_EUTRAN_21 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_24, MM_MODEM_BAND_EUTRAN_24 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_25, MM_MODEM_BAND_EUTRAN_25 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_26, MM_MODEM_BAND_EUTRAN_26 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_27, MM_MODEM_BAND_EUTRAN_27 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_28, MM_MODEM_BAND_EUTRAN_28 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_29, MM_MODEM_BAND_EUTRAN_29 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_30, MM_MODEM_BAND_EUTRAN_30 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_31, MM_MODEM_BAND_EUTRAN_31 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_32, MM_MODEM_BAND_EUTRAN_32 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_33, MM_MODEM_BAND_EUTRAN_33 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_34, MM_MODEM_BAND_EUTRAN_34 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_35, MM_MODEM_BAND_EUTRAN_35 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_36, MM_MODEM_BAND_EUTRAN_36 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_37, MM_MODEM_BAND_EUTRAN_37 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_38, MM_MODEM_BAND_EUTRAN_38 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_39, MM_MODEM_BAND_EUTRAN_39 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_40, MM_MODEM_BAND_EUTRAN_40 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_41, MM_MODEM_BAND_EUTRAN_41 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_42, MM_MODEM_BAND_EUTRAN_42 },
+ { QMI_DMS_LTE_BAND_CAPABILITY_EUTRAN_43, MM_MODEM_BAND_EUTRAN_43 }
};
static void
@@ -290,15 +319,53 @@ dms_add_qmi_lte_bands (GArray *mm_bands,
}
}
+static void
+dms_add_extended_qmi_lte_bands (GArray *mm_bands,
+ GArray *extended_qmi_bands,
+ gpointer log_object)
+{
+ guint i;
+
+ g_assert (mm_bands != NULL);
+
+ if (!extended_qmi_bands)
+ return;
+
+ for (i = 0; i < extended_qmi_bands->len; i++) {
+ guint16 val;
+
+ val = g_array_index (extended_qmi_bands, guint16, i);
+
+ /* MM_MODEM_BAND_EUTRAN_1 = 31,
+ * ...
+ * MM_MODEM_BAND_EUTRAN_71 = 101
+ */
+ if (val < 1 || val > 71)
+ mm_obj_dbg (log_object, "unexpected LTE band supported by module: EUTRAN %u", val);
+ else {
+ MMModemBand band;
+
+ band = (MMModemBand)(val + MM_MODEM_BAND_EUTRAN_1 - 1);
+ g_array_append_val (mm_bands, band);
+ }
+ }
+}
+
GArray *
-mm_modem_bands_from_qmi_band_capabilities (QmiDmsBandCapability qmi_bands,
- QmiDmsLteBandCapability qmi_lte_bands)
+mm_modem_bands_from_qmi_band_capabilities (QmiDmsBandCapability qmi_bands,
+ QmiDmsLteBandCapability qmi_lte_bands,
+ GArray *extended_qmi_lte_bands,
+ gpointer log_object)
{
GArray *mm_bands;
mm_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
- dms_add_qmi_bands (mm_bands, qmi_bands);
- dms_add_qmi_lte_bands (mm_bands, qmi_lte_bands);
+ dms_add_qmi_bands (mm_bands, qmi_bands, log_object);
+
+ if (extended_qmi_lte_bands)
+ dms_add_extended_qmi_lte_bands (mm_bands, extended_qmi_lte_bands, log_object);
+ else
+ dms_add_qmi_lte_bands (mm_bands, qmi_lte_bands);
return mm_bands;
}
@@ -314,26 +381,26 @@ static const NasBandsMap nas_bands_map [] = {
/* CDMA bands */
{
(QMI_NAS_BAND_PREFERENCE_BC_0_A_SYSTEM | QMI_NAS_BAND_PREFERENCE_BC_0_B_SYSTEM),
- MM_MODEM_BAND_CDMA_BC0_CELLULAR_800
+ MM_MODEM_BAND_CDMA_BC0
},
- { QMI_NAS_BAND_PREFERENCE_BC_1_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC1_PCS_1900 },
- { QMI_NAS_BAND_PREFERENCE_BC_2, MM_MODEM_BAND_CDMA_BC2_TACS },
- { QMI_NAS_BAND_PREFERENCE_BC_3_A_SYSTEM, MM_MODEM_BAND_CDMA_BC3_JTACS },
- { QMI_NAS_BAND_PREFERENCE_BC_4_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS },
- { QMI_NAS_BAND_PREFERENCE_BC_5_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC5_NMT450 },
- { QMI_NAS_BAND_PREFERENCE_BC_6, MM_MODEM_BAND_CDMA_BC6_IMT2000 },
- { QMI_NAS_BAND_PREFERENCE_BC_7, MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 },
- { QMI_NAS_BAND_PREFERENCE_BC_8, MM_MODEM_BAND_CDMA_BC8_1800 },
- { QMI_NAS_BAND_PREFERENCE_BC_9, MM_MODEM_BAND_CDMA_BC9_900 },
- { QMI_NAS_BAND_PREFERENCE_BC_10, MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 },
- { QMI_NAS_BAND_PREFERENCE_BC_11, MM_MODEM_BAND_CDMA_BC11_PAMR_400 },
- { QMI_NAS_BAND_PREFERENCE_BC_12, MM_MODEM_BAND_CDMA_BC12_PAMR_800 },
- { QMI_NAS_BAND_PREFERENCE_BC_14, MM_MODEM_BAND_CDMA_BC14_PCS2_1900 },
- { QMI_NAS_BAND_PREFERENCE_BC_15, MM_MODEM_BAND_CDMA_BC15_AWS },
- { QMI_NAS_BAND_PREFERENCE_BC_16, MM_MODEM_BAND_CDMA_BC16_US_2500 },
- { QMI_NAS_BAND_PREFERENCE_BC_17, MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 },
- { QMI_NAS_BAND_PREFERENCE_BC_18, MM_MODEM_BAND_CDMA_BC18_US_PS_700 },
- { QMI_NAS_BAND_PREFERENCE_BC_19, MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 },
+ { QMI_NAS_BAND_PREFERENCE_BC_1_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC1 },
+ { QMI_NAS_BAND_PREFERENCE_BC_2, MM_MODEM_BAND_CDMA_BC2 },
+ { QMI_NAS_BAND_PREFERENCE_BC_3_A_SYSTEM, MM_MODEM_BAND_CDMA_BC3 },
+ { QMI_NAS_BAND_PREFERENCE_BC_4_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC4 },
+ { QMI_NAS_BAND_PREFERENCE_BC_5_ALL_BLOCKS, MM_MODEM_BAND_CDMA_BC5 },
+ { QMI_NAS_BAND_PREFERENCE_BC_6, MM_MODEM_BAND_CDMA_BC6 },
+ { QMI_NAS_BAND_PREFERENCE_BC_7, MM_MODEM_BAND_CDMA_BC7 },
+ { QMI_NAS_BAND_PREFERENCE_BC_8, MM_MODEM_BAND_CDMA_BC8 },
+ { QMI_NAS_BAND_PREFERENCE_BC_9, MM_MODEM_BAND_CDMA_BC9 },
+ { QMI_NAS_BAND_PREFERENCE_BC_10, MM_MODEM_BAND_CDMA_BC10 },
+ { QMI_NAS_BAND_PREFERENCE_BC_11, MM_MODEM_BAND_CDMA_BC11 },
+ { QMI_NAS_BAND_PREFERENCE_BC_12, MM_MODEM_BAND_CDMA_BC12 },
+ { QMI_NAS_BAND_PREFERENCE_BC_14, MM_MODEM_BAND_CDMA_BC14 },
+ { QMI_NAS_BAND_PREFERENCE_BC_15, MM_MODEM_BAND_CDMA_BC15 },
+ { QMI_NAS_BAND_PREFERENCE_BC_16, MM_MODEM_BAND_CDMA_BC16 },
+ { QMI_NAS_BAND_PREFERENCE_BC_17, MM_MODEM_BAND_CDMA_BC17 },
+ { QMI_NAS_BAND_PREFERENCE_BC_18, MM_MODEM_BAND_CDMA_BC18 },
+ { QMI_NAS_BAND_PREFERENCE_BC_19, MM_MODEM_BAND_CDMA_BC19 },
/* GSM bands */
{ QMI_NAS_BAND_PREFERENCE_GSM_DCS_1800, MM_MODEM_BAND_DCS },
@@ -343,35 +410,28 @@ static const NasBandsMap nas_bands_map [] = {
},
{ QMI_NAS_BAND_PREFERENCE_GSM_PCS_1900, MM_MODEM_BAND_PCS },
{ QMI_NAS_BAND_PREFERENCE_GSM_850, MM_MODEM_BAND_G850 },
+ { QMI_NAS_BAND_PREFERENCE_GSM_450, MM_MODEM_BAND_G450 },
+ { QMI_NAS_BAND_PREFERENCE_GSM_480, MM_MODEM_BAND_G480 },
+ { QMI_NAS_BAND_PREFERENCE_GSM_750, MM_MODEM_BAND_G750 },
/* UMTS/WCDMA bands */
- { QMI_NAS_BAND_PREFERENCE_WCDMA_2100, MM_MODEM_BAND_U2100 },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_DCS_1800, MM_MODEM_BAND_U1800 },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_PCS_1900, MM_MODEM_BAND_U1900 },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_1700_US, MM_MODEM_BAND_U17IV },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_800, MM_MODEM_BAND_U800 },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_850_US, MM_MODEM_BAND_U850 },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_900, MM_MODEM_BAND_U900 },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_1700_JAPAN, MM_MODEM_BAND_U17IX },
- { QMI_NAS_BAND_PREFERENCE_WCDMA_2600, MM_MODEM_BAND_U2600 }
-
- /* NOTE. The following bands were unmatched:
- *
- * - QMI_NAS_BAND_PREFERENCE_GSM_900_PRIMARY
- * - QMI_NAS_BAND_PREFERENCE_GSM_450
- * - QMI_NAS_BAND_PREFERENCE_GSM_480
- * - QMI_NAS_BAND_PREFERENCE_GSM_750
- * - QMI_NAS_BAND_PREFERENCE_GSM_900_RAILWAILS
- * - QMI_NAS_BAND_PREFERENCE_WCDMA_1500
- * - QMI_NAS_BAND_PREFERENCE_WCDMA_850_JAPAN
- * - MM_MODEM_BAND_CDMA_BC13_IMT2000_2500
- * - MM_MODEM_BAND_U1900
- */
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_2100, MM_MODEM_BAND_UTRAN_1 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_DCS_1800, MM_MODEM_BAND_UTRAN_3 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_PCS_1900, MM_MODEM_BAND_UTRAN_2 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_1700_US, MM_MODEM_BAND_UTRAN_4 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_800, MM_MODEM_BAND_UTRAN_6 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_850_US, MM_MODEM_BAND_UTRAN_5 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_900, MM_MODEM_BAND_UTRAN_8 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_1700_JAPAN, MM_MODEM_BAND_UTRAN_9 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_2600, MM_MODEM_BAND_UTRAN_7 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_1500, MM_MODEM_BAND_UTRAN_11 },
+ { QMI_NAS_BAND_PREFERENCE_WCDMA_850_JAPAN, MM_MODEM_BAND_UTRAN_19 },
};
static void
-nas_add_qmi_bands (GArray *mm_bands,
- QmiNasBandPreference qmi_bands)
+nas_add_qmi_bands (GArray *mm_bands,
+ QmiNasBandPreference qmi_bands,
+ gpointer log_object)
{
static QmiNasBandPreference qmi_bands_expected = 0;
QmiNasBandPreference not_expected;
@@ -389,11 +449,10 @@ nas_add_qmi_bands (GArray *mm_bands,
/* Log about the bands that cannot be represented in ModemManager */
not_expected = ((qmi_bands_expected ^ qmi_bands) & qmi_bands);
if (not_expected) {
- gchar *aux;
+ g_autofree gchar *aux = NULL;
aux = qmi_nas_band_preference_build_string_from_mask (not_expected);
- mm_dbg ("Cannot add the following bands: '%s'", aux);
- g_free (aux);
+ mm_obj_dbg (log_object, "cannot add the following bands: '%s'", aux);
}
/* And add the expected ones */
@@ -409,46 +468,45 @@ typedef struct {
} NasLteBandsMap;
static const NasLteBandsMap nas_lte_bands_map [] = {
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_1, MM_MODEM_BAND_EUTRAN_I },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_2, MM_MODEM_BAND_EUTRAN_II },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_3, MM_MODEM_BAND_EUTRAN_III },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_4, MM_MODEM_BAND_EUTRAN_IV },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_5, MM_MODEM_BAND_EUTRAN_V },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_6, MM_MODEM_BAND_EUTRAN_VI },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_7, MM_MODEM_BAND_EUTRAN_VII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_8, MM_MODEM_BAND_EUTRAN_VIII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_9, MM_MODEM_BAND_EUTRAN_IX },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_10, MM_MODEM_BAND_EUTRAN_X },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_11, MM_MODEM_BAND_EUTRAN_XI },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_12, MM_MODEM_BAND_EUTRAN_XII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_13, MM_MODEM_BAND_EUTRAN_XIII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_14, MM_MODEM_BAND_EUTRAN_XIV },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_17, MM_MODEM_BAND_EUTRAN_XVII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_18, MM_MODEM_BAND_EUTRAN_XVIII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_19, MM_MODEM_BAND_EUTRAN_XIX },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_20, MM_MODEM_BAND_EUTRAN_XX },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_21, MM_MODEM_BAND_EUTRAN_XXI },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_24, MM_MODEM_BAND_EUTRAN_XXIV },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_25, MM_MODEM_BAND_EUTRAN_XXV },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_33, MM_MODEM_BAND_EUTRAN_XXXIII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_34, MM_MODEM_BAND_EUTRAN_XXXIV },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_35, MM_MODEM_BAND_EUTRAN_XXXV },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_36, MM_MODEM_BAND_EUTRAN_XXXVI },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_37, MM_MODEM_BAND_EUTRAN_XXXVII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_38, MM_MODEM_BAND_EUTRAN_XXXVIII },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_39, MM_MODEM_BAND_EUTRAN_XXXIX },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_40, MM_MODEM_BAND_EUTRAN_XL },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_41, MM_MODEM_BAND_EUTRAN_XLI },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_42, MM_MODEM_BAND_EUTRAN_XLI },
- { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_43, MM_MODEM_BAND_EUTRAN_XLIII }
-
- /* NOTE. The following bands were unmatched:
- *
- * - MM_MODEM_BAND_EUTRAN_XXII
- * - MM_MODEM_BAND_EUTRAN_XXIII
- * - MM_MODEM_BAND_EUTRAN_XXVI
- * - MM_MODEM_BAND_EUTRAN_XLIV
- */
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_1, MM_MODEM_BAND_EUTRAN_1 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_2, MM_MODEM_BAND_EUTRAN_2 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_3, MM_MODEM_BAND_EUTRAN_3 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_4, MM_MODEM_BAND_EUTRAN_4 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_5, MM_MODEM_BAND_EUTRAN_5 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_6, MM_MODEM_BAND_EUTRAN_6 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_7, MM_MODEM_BAND_EUTRAN_7 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_8, MM_MODEM_BAND_EUTRAN_8 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_9, MM_MODEM_BAND_EUTRAN_9 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_10, MM_MODEM_BAND_EUTRAN_10 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_11, MM_MODEM_BAND_EUTRAN_11 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_12, MM_MODEM_BAND_EUTRAN_12 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_13, MM_MODEM_BAND_EUTRAN_13 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_14, MM_MODEM_BAND_EUTRAN_14 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_17, MM_MODEM_BAND_EUTRAN_17 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_18, MM_MODEM_BAND_EUTRAN_18 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_19, MM_MODEM_BAND_EUTRAN_19 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_20, MM_MODEM_BAND_EUTRAN_20 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_21, MM_MODEM_BAND_EUTRAN_21 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_24, MM_MODEM_BAND_EUTRAN_24 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_25, MM_MODEM_BAND_EUTRAN_25 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_26, MM_MODEM_BAND_EUTRAN_26 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_27, MM_MODEM_BAND_EUTRAN_27 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_28, MM_MODEM_BAND_EUTRAN_28 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_29, MM_MODEM_BAND_EUTRAN_29 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_30, MM_MODEM_BAND_EUTRAN_30 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_31, MM_MODEM_BAND_EUTRAN_31 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_32, MM_MODEM_BAND_EUTRAN_32 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_33, MM_MODEM_BAND_EUTRAN_33 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_34, MM_MODEM_BAND_EUTRAN_34 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_35, MM_MODEM_BAND_EUTRAN_35 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_36, MM_MODEM_BAND_EUTRAN_36 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_37, MM_MODEM_BAND_EUTRAN_37 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_38, MM_MODEM_BAND_EUTRAN_38 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_39, MM_MODEM_BAND_EUTRAN_39 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_40, MM_MODEM_BAND_EUTRAN_40 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_41, MM_MODEM_BAND_EUTRAN_41 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_42, MM_MODEM_BAND_EUTRAN_42 },
+ { QMI_NAS_LTE_BAND_PREFERENCE_EUTRAN_43, MM_MODEM_BAND_EUTRAN_43 }
};
static void
@@ -467,49 +525,113 @@ nas_add_qmi_lte_bands (GArray *mm_bands,
}
}
+static void
+nas_add_extended_qmi_lte_bands (GArray *mm_bands,
+ const guint64 *extended_qmi_lte_bands,
+ guint extended_qmi_lte_bands_size,
+ gpointer log_object)
+{
+ guint i;
+
+ g_assert (mm_bands != NULL);
+
+ for (i = 0; i < extended_qmi_lte_bands_size; i++) {
+ guint j;
+
+ for (j = 0; j < 64; j++) {
+ guint val;
+
+ if (!(extended_qmi_lte_bands[i] & (((guint64) 1) << j)))
+ continue;
+
+ val = 1 + j + (i * 64);
+
+ /* MM_MODEM_BAND_EUTRAN_1 = 31,
+ * ...
+ * MM_MODEM_BAND_EUTRAN_71 = 101
+ */
+ if (val < 1 || val > 71)
+ mm_obj_dbg (log_object, "unexpected LTE band supported by module: EUTRAN %u", val);
+ else {
+ MMModemBand band;
+
+ band = (val + MM_MODEM_BAND_EUTRAN_1 - 1);
+ g_array_append_val (mm_bands, band);
+ }
+ }
+ }
+}
+
GArray *
-mm_modem_bands_from_qmi_band_preference (QmiNasBandPreference qmi_bands,
- QmiNasLteBandPreference qmi_lte_bands)
+mm_modem_bands_from_qmi_band_preference (QmiNasBandPreference qmi_bands,
+ QmiNasLteBandPreference qmi_lte_bands,
+ const guint64 *extended_qmi_lte_bands,
+ guint extended_qmi_lte_bands_size,
+ gpointer log_object)
{
GArray *mm_bands;
mm_bands = g_array_new (FALSE, FALSE, sizeof (MMModemBand));
- nas_add_qmi_bands (mm_bands, qmi_bands);
- nas_add_qmi_lte_bands (mm_bands, qmi_lte_bands);
+ nas_add_qmi_bands (mm_bands, qmi_bands, log_object);
+
+ if (extended_qmi_lte_bands && extended_qmi_lte_bands_size)
+ nas_add_extended_qmi_lte_bands (mm_bands, extended_qmi_lte_bands, extended_qmi_lte_bands_size, log_object);
+ else
+ nas_add_qmi_lte_bands (mm_bands, qmi_lte_bands);
return mm_bands;
}
void
-mm_modem_bands_to_qmi_band_preference (GArray *mm_bands,
- QmiNasBandPreference *qmi_bands,
- QmiNasLteBandPreference *qmi_lte_bands)
+mm_modem_bands_to_qmi_band_preference (GArray *mm_bands,
+ QmiNasBandPreference *qmi_bands,
+ QmiNasLteBandPreference *qmi_lte_bands,
+ guint64 *extended_qmi_lte_bands,
+ guint extended_qmi_lte_bands_size,
+ gpointer log_object)
{
guint i;
*qmi_bands = 0;
*qmi_lte_bands = 0;
+ if (extended_qmi_lte_bands)
+ memset (extended_qmi_lte_bands, 0, extended_qmi_lte_bands_size * sizeof (guint64));
for (i = 0; i < mm_bands->len; i++) {
MMModemBand band;
band = g_array_index (mm_bands, MMModemBand, i);
- if (band <= MM_MODEM_BAND_EUTRAN_XLIV &&
- band >= MM_MODEM_BAND_EUTRAN_I) {
- /* Add LTE band preference */
- guint j;
-
- for (j = 0; j < G_N_ELEMENTS (nas_lte_bands_map); j++) {
- if (nas_lte_bands_map[j].mm_band == band) {
- *qmi_lte_bands |= nas_lte_bands_map[j].qmi_band;
- break;
+ if (band >= MM_MODEM_BAND_EUTRAN_1 && band <= MM_MODEM_BAND_EUTRAN_71) {
+ if (extended_qmi_lte_bands && extended_qmi_lte_bands_size) {
+ /* Add extended LTE band preference */
+ guint val;
+ guint j;
+ guint k;
+
+ /* it's really (band - MM_MODEM_BAND_EUTRAN_1 +1 -1), because
+ * we want EUTRAN1 in index 0 */
+ val = band - MM_MODEM_BAND_EUTRAN_1;
+ j = val / 64;
+ g_assert (j < extended_qmi_lte_bands_size);
+ k = val % 64;
+
+ extended_qmi_lte_bands[j] |= ((guint64)1 << k);
+ } else {
+ /* Add LTE band preference */
+ guint j;
+
+ for (j = 0; j < G_N_ELEMENTS (nas_lte_bands_map); j++) {
+ if (nas_lte_bands_map[j].mm_band == band) {
+ *qmi_lte_bands |= nas_lte_bands_map[j].qmi_band;
+ break;
+ }
}
- }
- if (j == G_N_ELEMENTS (nas_lte_bands_map))
- mm_dbg ("Cannot add the following LTE band: '%s'",
- mm_modem_band_get_string (band));
+ if (j == G_N_ELEMENTS (nas_lte_bands_map))
+ mm_obj_dbg (log_object, "cannot add the following LTE band: '%s'",
+ mm_modem_band_get_string (band));
+ }
} else {
/* Add non-LTE band preference */
guint j;
@@ -522,8 +644,8 @@ mm_modem_bands_to_qmi_band_preference (GArray *mm_bands,
}
if (j == G_N_ELEMENTS (nas_bands_map))
- mm_dbg ("Cannot add the following band: '%s'",
- mm_modem_band_get_string (band));
+ mm_obj_dbg (log_object, "cannot add the following band: '%s'",
+ mm_modem_band_get_string (band));
}
}
}
@@ -537,94 +659,82 @@ typedef struct {
static const ActiveBandsMap active_bands_map [] = {
/* CDMA bands */
- { QMI_NAS_ACTIVE_BAND_BC_0, MM_MODEM_BAND_CDMA_BC0_CELLULAR_800 },
- { QMI_NAS_ACTIVE_BAND_BC_1, MM_MODEM_BAND_CDMA_BC1_PCS_1900 },
- { QMI_NAS_ACTIVE_BAND_BC_2, MM_MODEM_BAND_CDMA_BC2_TACS },
- { QMI_NAS_ACTIVE_BAND_BC_3, MM_MODEM_BAND_CDMA_BC3_JTACS },
- { QMI_NAS_ACTIVE_BAND_BC_4, MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS },
- { QMI_NAS_ACTIVE_BAND_BC_5, MM_MODEM_BAND_CDMA_BC5_NMT450 },
- { QMI_NAS_ACTIVE_BAND_BC_6, MM_MODEM_BAND_CDMA_BC6_IMT2000 },
- { QMI_NAS_ACTIVE_BAND_BC_7, MM_MODEM_BAND_CDMA_BC7_CELLULAR_700 },
- { QMI_NAS_ACTIVE_BAND_BC_8, MM_MODEM_BAND_CDMA_BC8_1800 },
- { QMI_NAS_ACTIVE_BAND_BC_9, MM_MODEM_BAND_CDMA_BC9_900 },
- { QMI_NAS_ACTIVE_BAND_BC_10, MM_MODEM_BAND_CDMA_BC10_SECONDARY_800 },
- { QMI_NAS_ACTIVE_BAND_BC_11, MM_MODEM_BAND_CDMA_BC11_PAMR_400 },
- { QMI_NAS_ACTIVE_BAND_BC_12, MM_MODEM_BAND_CDMA_BC12_PAMR_800 },
- { QMI_NAS_ACTIVE_BAND_BC_13, MM_MODEM_BAND_CDMA_BC13_IMT2000_2500 },
- { QMI_NAS_ACTIVE_BAND_BC_14, MM_MODEM_BAND_CDMA_BC14_PCS2_1900 },
- { QMI_NAS_ACTIVE_BAND_BC_15, MM_MODEM_BAND_CDMA_BC15_AWS },
- { QMI_NAS_ACTIVE_BAND_BC_16, MM_MODEM_BAND_CDMA_BC16_US_2500 },
- { QMI_NAS_ACTIVE_BAND_BC_17, MM_MODEM_BAND_CDMA_BC17_US_FLO_2500 },
- { QMI_NAS_ACTIVE_BAND_BC_18, MM_MODEM_BAND_CDMA_BC18_US_PS_700 },
- { QMI_NAS_ACTIVE_BAND_BC_19, MM_MODEM_BAND_CDMA_BC19_US_LOWER_700 },
+ { QMI_NAS_ACTIVE_BAND_BC_0, MM_MODEM_BAND_CDMA_BC0 },
+ { QMI_NAS_ACTIVE_BAND_BC_1, MM_MODEM_BAND_CDMA_BC1 },
+ { QMI_NAS_ACTIVE_BAND_BC_2, MM_MODEM_BAND_CDMA_BC2 },
+ { QMI_NAS_ACTIVE_BAND_BC_3, MM_MODEM_BAND_CDMA_BC3 },
+ { QMI_NAS_ACTIVE_BAND_BC_4, MM_MODEM_BAND_CDMA_BC4 },
+ { QMI_NAS_ACTIVE_BAND_BC_5, MM_MODEM_BAND_CDMA_BC5 },
+ { QMI_NAS_ACTIVE_BAND_BC_6, MM_MODEM_BAND_CDMA_BC6 },
+ { QMI_NAS_ACTIVE_BAND_BC_7, MM_MODEM_BAND_CDMA_BC7 },
+ { QMI_NAS_ACTIVE_BAND_BC_8, MM_MODEM_BAND_CDMA_BC8 },
+ { QMI_NAS_ACTIVE_BAND_BC_9, MM_MODEM_BAND_CDMA_BC9 },
+ { QMI_NAS_ACTIVE_BAND_BC_10, MM_MODEM_BAND_CDMA_BC10 },
+ { QMI_NAS_ACTIVE_BAND_BC_11, MM_MODEM_BAND_CDMA_BC11 },
+ { QMI_NAS_ACTIVE_BAND_BC_12, MM_MODEM_BAND_CDMA_BC12 },
+ { QMI_NAS_ACTIVE_BAND_BC_13, MM_MODEM_BAND_CDMA_BC13 },
+ { QMI_NAS_ACTIVE_BAND_BC_14, MM_MODEM_BAND_CDMA_BC14 },
+ { QMI_NAS_ACTIVE_BAND_BC_15, MM_MODEM_BAND_CDMA_BC15 },
+ { QMI_NAS_ACTIVE_BAND_BC_16, MM_MODEM_BAND_CDMA_BC16 },
+ { QMI_NAS_ACTIVE_BAND_BC_17, MM_MODEM_BAND_CDMA_BC17 },
+ { QMI_NAS_ACTIVE_BAND_BC_18, MM_MODEM_BAND_CDMA_BC18 },
+ { QMI_NAS_ACTIVE_BAND_BC_19, MM_MODEM_BAND_CDMA_BC19 },
/* GSM bands */
- { QMI_NAS_ACTIVE_BAND_GSM_850, MM_MODEM_BAND_G850 },
+ { QMI_NAS_ACTIVE_BAND_GSM_850, MM_MODEM_BAND_G850 },
{ QMI_NAS_ACTIVE_BAND_GSM_900_EXTENDED, MM_MODEM_BAND_EGSM },
- { QMI_NAS_ACTIVE_BAND_GSM_DCS_1800, MM_MODEM_BAND_DCS },
- { QMI_NAS_ACTIVE_BAND_GSM_PCS_1900, MM_MODEM_BAND_PCS },
+ { QMI_NAS_ACTIVE_BAND_GSM_DCS_1800, MM_MODEM_BAND_DCS },
+ { QMI_NAS_ACTIVE_BAND_GSM_PCS_1900, MM_MODEM_BAND_PCS },
+ { QMI_NAS_ACTIVE_BAND_GSM_450, MM_MODEM_BAND_G450 },
+ { QMI_NAS_ACTIVE_BAND_GSM_480, MM_MODEM_BAND_G480 },
+ { QMI_NAS_ACTIVE_BAND_GSM_750, MM_MODEM_BAND_G750 },
/* WCDMA bands */
- { QMI_NAS_ACTIVE_BAND_WCDMA_2100, MM_MODEM_BAND_U2100 },
- { QMI_NAS_ACTIVE_BAND_WCDMA_PCS_1900, MM_MODEM_BAND_PCS },
- { QMI_NAS_ACTIVE_BAND_WCDMA_DCS_1800, MM_MODEM_BAND_DCS },
- { QMI_NAS_ACTIVE_BAND_WCDMA_1700_US, MM_MODEM_BAND_U17IV },
- { QMI_NAS_ACTIVE_BAND_WCDMA_850, MM_MODEM_BAND_U850 },
- { QMI_NAS_ACTIVE_BAND_WCDMA_800, MM_MODEM_BAND_U800 },
- { QMI_NAS_ACTIVE_BAND_WCDMA_2600, MM_MODEM_BAND_U2600 },
- { QMI_NAS_ACTIVE_BAND_WCDMA_900, MM_MODEM_BAND_U900 },
- { QMI_NAS_ACTIVE_BAND_WCDMA_1700_JAPAN, MM_MODEM_BAND_U17IX },
- { QMI_NAS_ACTIVE_BAND_WCDMA_850_JAPAN, MM_MODEM_BAND_U850 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_2100, MM_MODEM_BAND_UTRAN_1 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_PCS_1900, MM_MODEM_BAND_UTRAN_2 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_DCS_1800, MM_MODEM_BAND_UTRAN_3 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_1700_US, MM_MODEM_BAND_UTRAN_4 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_850, MM_MODEM_BAND_UTRAN_5 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_800, MM_MODEM_BAND_UTRAN_6 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_2600, MM_MODEM_BAND_UTRAN_7 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_900, MM_MODEM_BAND_UTRAN_8 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_1700_JAPAN, MM_MODEM_BAND_UTRAN_9 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_1500_JAPAN, MM_MODEM_BAND_UTRAN_11 },
+ { QMI_NAS_ACTIVE_BAND_WCDMA_850_JAPAN, MM_MODEM_BAND_UTRAN_19 },
/* LTE bands */
- { QMI_NAS_ACTIVE_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_I },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_II },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_III },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_IV },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_V },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_6, MM_MODEM_BAND_EUTRAN_VI },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_VII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_VIII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_9, MM_MODEM_BAND_EUTRAN_IX },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_10, MM_MODEM_BAND_EUTRAN_X },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_XI },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_XII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_XIII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_14, MM_MODEM_BAND_EUTRAN_XIV },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_XVII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_XVIII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_XIX },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_XX },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_XXI },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_24, MM_MODEM_BAND_EUTRAN_XXIV },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_25, MM_MODEM_BAND_EUTRAN_XXV },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_33, MM_MODEM_BAND_EUTRAN_XXXIII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_34, MM_MODEM_BAND_EUTRAN_XXXIV },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_35, MM_MODEM_BAND_EUTRAN_XXXV },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_36, MM_MODEM_BAND_EUTRAN_XXXVI },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_37, MM_MODEM_BAND_EUTRAN_XXXVII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_XXXVIII },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_XXXIX },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_XL },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_XLI },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_42, MM_MODEM_BAND_EUTRAN_XLI },
- { QMI_NAS_ACTIVE_BAND_EUTRAN_43, MM_MODEM_BAND_EUTRAN_XLIII }
-
- /* NOTE. The following bands were unmatched:
- *
- * - QMI_NAS_ACTIVE_BAND_GSM_450
- * - QMI_NAS_ACTIVE_BAND_GSM_480
- * - QMI_NAS_ACTIVE_BAND_GSM_750
- * - QMI_NAS_ACTIVE_BAND_GSM_900_PRIMARY
- * - QMI_NAS_ACTIVE_BAND_GSM_900_RAILWAYS
- * - QMI_NAS_ACTIVE_BAND_WCDMA_1500_JAPAN
- * - QMI_NAS_ACTIVE_BAND_TDSCDMA_A
- * - QMI_NAS_ACTIVE_BAND_TDSCDMA_B
- * - QMI_NAS_ACTIVE_BAND_TDSCDMA_C
- * - QMI_NAS_ACTIVE_BAND_TDSCDMA_D
- * - QMI_NAS_ACTIVE_BAND_TDSCDMA_E
- * - QMI_NAS_ACTIVE_BAND_TDSCDMA_F
- */
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_1, MM_MODEM_BAND_EUTRAN_1 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_2, MM_MODEM_BAND_EUTRAN_2 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_3, MM_MODEM_BAND_EUTRAN_3 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_4, MM_MODEM_BAND_EUTRAN_4 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_5, MM_MODEM_BAND_EUTRAN_5 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_6, MM_MODEM_BAND_EUTRAN_6 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_7, MM_MODEM_BAND_EUTRAN_7 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_8, MM_MODEM_BAND_EUTRAN_8 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_9, MM_MODEM_BAND_EUTRAN_9 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_10, MM_MODEM_BAND_EUTRAN_10 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_11, MM_MODEM_BAND_EUTRAN_11 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_12, MM_MODEM_BAND_EUTRAN_12 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_13, MM_MODEM_BAND_EUTRAN_13 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_14, MM_MODEM_BAND_EUTRAN_14 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_17, MM_MODEM_BAND_EUTRAN_17 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_18, MM_MODEM_BAND_EUTRAN_18 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_19, MM_MODEM_BAND_EUTRAN_19 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_20, MM_MODEM_BAND_EUTRAN_20 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_21, MM_MODEM_BAND_EUTRAN_21 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_24, MM_MODEM_BAND_EUTRAN_24 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_25, MM_MODEM_BAND_EUTRAN_25 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_33, MM_MODEM_BAND_EUTRAN_33 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_34, MM_MODEM_BAND_EUTRAN_34 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_35, MM_MODEM_BAND_EUTRAN_35 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_36, MM_MODEM_BAND_EUTRAN_36 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_37, MM_MODEM_BAND_EUTRAN_37 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_38, MM_MODEM_BAND_EUTRAN_38 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_39, MM_MODEM_BAND_EUTRAN_39 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_40, MM_MODEM_BAND_EUTRAN_40 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_41, MM_MODEM_BAND_EUTRAN_41 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_42, MM_MODEM_BAND_EUTRAN_42 },
+ { QMI_NAS_ACTIVE_BAND_EUTRAN_43, MM_MODEM_BAND_EUTRAN_43 }
};
static void
@@ -688,6 +798,9 @@ mm_modem_access_technology_from_qmi_radio_interface (QmiNasRadioInterface interf
return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
case QMI_NAS_RADIO_INTERFACE_LTE:
return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ case QMI_NAS_RADIO_INTERFACE_5GNR:
+ return MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+ case QMI_NAS_RADIO_INTERFACE_UNKNOWN:
case QMI_NAS_RADIO_INTERFACE_TD_SCDMA:
case QMI_NAS_RADIO_INTERFACE_AMPS:
case QMI_NAS_RADIO_INTERFACE_NONE:
@@ -745,6 +858,7 @@ mm_modem_access_technology_from_qmi_data_capability (QmiNasDataCapability cap)
case QMI_NAS_DATA_CAPABILITY_HSDPA_PLUS:
case QMI_NAS_DATA_CAPABILITY_DC_HSDPA_PLUS:
return MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS;
+ case QMI_NAS_DATA_CAPABILITY_NONE:
default:
return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
@@ -771,6 +885,31 @@ mm_modem_access_technologies_from_qmi_data_capability_array (GArray *data_capabi
/*****************************************************************************/
MMModemMode
+mm_modem_mode_from_qmi_nas_radio_interface (QmiNasRadioInterface iface)
+{
+ switch (iface) {
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1X:
+ case QMI_NAS_RADIO_INTERFACE_GSM:
+ return MM_MODEM_MODE_2G;
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO:
+ case QMI_NAS_RADIO_INTERFACE_UMTS:
+ return MM_MODEM_MODE_3G;
+ case QMI_NAS_RADIO_INTERFACE_LTE:
+ return MM_MODEM_MODE_4G;
+ case QMI_NAS_RADIO_INTERFACE_5GNR:
+ return MM_MODEM_MODE_5G;
+ case QMI_NAS_RADIO_INTERFACE_NONE:
+ case QMI_NAS_RADIO_INTERFACE_AMPS:
+ case QMI_NAS_RADIO_INTERFACE_TD_SCDMA:
+ case QMI_NAS_RADIO_INTERFACE_UNKNOWN:
+ default:
+ return MM_MODEM_MODE_NONE;
+ }
+}
+
+/*****************************************************************************/
+
+MMModemMode
mm_modem_mode_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi)
{
MMModemMode mode = MM_MODEM_MODE_NONE;
@@ -844,6 +983,9 @@ mm_modem_mode_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi)
if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_LTE)
mode |= MM_MODEM_MODE_4G;
+ if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_5GNR)
+ mode |= MM_MODEM_MODE_5G;
+
return mode;
}
@@ -871,6 +1013,9 @@ mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mode,
if (mode & MM_MODEM_MODE_4G)
pref |= QMI_NAS_RAT_MODE_PREFERENCE_LTE;
+
+ if (mode & MM_MODEM_MODE_5G)
+ pref |= QMI_NAS_RAT_MODE_PREFERENCE_5GNR;
}
return pref;
@@ -898,7 +1043,8 @@ mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi)
if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_LTE)
caps |= MM_MODEM_CAPABILITY_LTE;
- /* FIXME: LTE Advanced? */
+ if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_5GNR)
+ caps |= MM_MODEM_CAPABILITY_5GNR;
return caps;
}
@@ -921,11 +1067,123 @@ mm_modem_capability_to_qmi_rat_mode_preference (MMModemCapability caps)
if (caps & MM_MODEM_CAPABILITY_LTE)
qmi |= QMI_NAS_RAT_MODE_PREFERENCE_LTE;
+ if (caps & MM_MODEM_CAPABILITY_5GNR)
+ qmi |= QMI_NAS_RAT_MODE_PREFERENCE_5GNR;
+
return qmi;
}
/*****************************************************************************/
+GArray *
+mm_modem_capability_to_qmi_acquisition_order_preference (MMModemCapability caps)
+{
+ GArray *array;
+ QmiNasRadioInterface value;
+
+ array = g_array_new (FALSE, FALSE, sizeof (QmiNasRadioInterface));
+
+ if (caps & MM_MODEM_CAPABILITY_5GNR) {
+ value = QMI_NAS_RADIO_INTERFACE_5GNR;
+ g_array_append_val (array, value);
+ }
+
+ if (caps & MM_MODEM_CAPABILITY_LTE) {
+ value = QMI_NAS_RADIO_INTERFACE_LTE;
+ g_array_append_val (array, value);
+ }
+
+ if (caps & MM_MODEM_CAPABILITY_GSM_UMTS) {
+ value = QMI_NAS_RADIO_INTERFACE_UMTS;
+ g_array_append_val (array, value);
+ value = QMI_NAS_RADIO_INTERFACE_GSM;
+ g_array_append_val (array, value);
+ }
+
+ if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO) {
+ value = QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO;
+ g_array_append_val (array, value);
+ value = QMI_NAS_RADIO_INTERFACE_CDMA_1X;
+ g_array_append_val (array, value);
+ }
+
+ return array;
+}
+
+static gboolean
+radio_interface_array_contains (GArray *array,
+ QmiNasRadioInterface act)
+{
+ guint i;
+
+ for (i = 0; i < array->len; i++) {
+ QmiNasRadioInterface value;
+
+ value = g_array_index (array, QmiNasRadioInterface, i);
+ if (value == act)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+radio_interface_array_add_missing (GArray *array,
+ GArray *all)
+{
+ guint i;
+
+ for (i = 0; i < all->len; i++) {
+ QmiNasRadioInterface value;
+
+ value = g_array_index (all, QmiNasRadioInterface, i);
+ if (!radio_interface_array_contains (array, value))
+ g_array_append_val (array, value);
+ }
+}
+
+GArray *
+mm_modem_mode_to_qmi_acquisition_order_preference (MMModemMode allowed,
+ MMModemMode preferred,
+ GArray *all)
+{
+ GArray *array;
+ QmiNasRadioInterface preferred_radio = QMI_NAS_RADIO_INTERFACE_UNKNOWN;
+ QmiNasRadioInterface value;
+
+ array = g_array_sized_new (FALSE, FALSE, sizeof (QmiNasRadioInterface), all->len);
+
+#define PROCESS_ALLOWED_PREFERRED_MODE(MODE,RADIO) \
+ if ((allowed & MODE) && (radio_interface_array_contains (all, RADIO))) { \
+ if ((preferred == MODE) && (preferred_radio == QMI_NAS_RADIO_INTERFACE_UNKNOWN)) \
+ preferred_radio = RADIO; \
+ else { \
+ value = RADIO; \
+ g_array_append_val (array, value); \
+ } \
+ }
+
+ PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_5G, QMI_NAS_RADIO_INTERFACE_5GNR);
+ PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_4G, QMI_NAS_RADIO_INTERFACE_LTE);
+ PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_3G, QMI_NAS_RADIO_INTERFACE_UMTS);
+ PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_3G, QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO);
+ PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_2G, QMI_NAS_RADIO_INTERFACE_GSM);
+ PROCESS_ALLOWED_PREFERRED_MODE (MM_MODEM_MODE_2G, QMI_NAS_RADIO_INTERFACE_CDMA_1X);
+
+#undef PROCESS_ALLOWED_PREFERRED_MODE
+
+ if (preferred_radio != QMI_NAS_RADIO_INTERFACE_UNKNOWN)
+ g_array_prepend_val (array, preferred_radio);
+
+ /* the acquisition order preference is a TLV that must ALWAYS contain the
+ * same list of QmiNasRadioInterface values, just with a different order. */
+ radio_interface_array_add_missing (array, all);
+ g_assert_cmpuint (array->len, ==, all->len);
+
+ return array;
+}
+
+/*****************************************************************************/
+
MMModemCapability
mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi)
{
@@ -949,7 +1207,7 @@ mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyP
if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE)
caps |= MM_MODEM_CAPABILITY_LTE;
- /* FIXME: LTE Advanced? */
+ /* NOTE: no 5GNR defined in Technology Preference */
return caps;
}
@@ -959,11 +1217,6 @@ mm_modem_capability_to_qmi_radio_technology_preference (MMModemCapability caps)
{
QmiNasRadioTechnologyPreference qmi = 0;
- /* It is not expected to have a modem supporting 3GPP and 3GPP2 at the same
- * time but not supporting SSP. */
- g_warn_if_fail (caps & MM_MODEM_CAPABILITY_GSM_UMTS &&
- caps & MM_MODEM_CAPABILITY_CDMA_EVDO);
-
if (caps & MM_MODEM_CAPABILITY_GSM_UMTS) {
qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP;
qmi |= QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM;
@@ -1043,7 +1296,8 @@ mm_modem_capability_from_qmi_band_preference (QmiNasBandPreference qmi)
/*****************************************************************************/
MMModemMode
-mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi)
+mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi,
+ gpointer log_object)
{
switch (qmi) {
case QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC:
@@ -1053,16 +1307,17 @@ mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcq
case QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_WCDMA:
return MM_MODEM_MODE_3G;
default:
- mm_dbg ("Unknown acquisition order preference: '%s'",
- qmi_nas_gsm_wcdma_acquisition_order_preference_get_string (qmi));
+ mm_obj_dbg (log_object, "unknown acquisition order preference: '%s'",
+ qmi_nas_gsm_wcdma_acquisition_order_preference_get_string (qmi));
return MM_MODEM_MODE_NONE;
}
}
QmiNasGsmWcdmaAcquisitionOrderPreference
-mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode)
+mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode,
+ gpointer log_object)
{
- gchar *str;
+ g_autofree gchar *str = NULL;
/* mode is not a mask in this case, only a value */
@@ -1073,13 +1328,16 @@ mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode)
return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_GSM;
case MM_MODEM_MODE_NONE:
return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC;
+ case MM_MODEM_MODE_CS:
+ case MM_MODEM_MODE_4G:
+ case MM_MODEM_MODE_5G:
+ case MM_MODEM_MODE_ANY:
default:
break;
}
str = mm_modem_mode_build_string_from_mask (mode);
- mm_dbg ("Unhandled modem mode: '%s'", str);
- g_free (str);
+ mm_obj_dbg (log_object, "unhandled modem mode: '%s'", str);
return QMI_NAS_GSM_WCDMA_ACQUISITION_ORDER_PREFERENCE_AUTOMATIC;
}
@@ -1168,6 +1426,11 @@ mm_sms_storage_to_qmi_storage_type (MMSmsStorage storage)
return QMI_WMS_STORAGE_TYPE_UIM;
case MM_SMS_STORAGE_ME:
return QMI_WMS_STORAGE_TYPE_NV;
+ case MM_SMS_STORAGE_UNKNOWN:
+ case MM_SMS_STORAGE_MT:
+ case MM_SMS_STORAGE_SR:
+ case MM_SMS_STORAGE_BM:
+ case MM_SMS_STORAGE_TA:
default:
return QMI_WMS_STORAGE_TYPE_NONE;
}
@@ -1181,6 +1444,7 @@ mm_sms_storage_from_qmi_storage_type (QmiWmsStorageType qmi_storage)
return MM_SMS_STORAGE_SM;
case QMI_WMS_STORAGE_TYPE_NV:
return MM_SMS_STORAGE_ME;
+ case QMI_WMS_STORAGE_TYPE_NONE:
default:
return MM_SMS_STORAGE_UNKNOWN;
}
@@ -1207,69 +1471,317 @@ mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag)
/*****************************************************************************/
QmiWdsAuthentication
-mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth)
+mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth,
+ gpointer log_object,
+ GError **error)
{
QmiWdsAuthentication out;
+ if (auth == MM_BEARER_ALLOWED_AUTH_UNKNOWN) {
+ mm_obj_dbg (log_object, "using default (CHAP) authentication method");
+ return QMI_WDS_AUTHENTICATION_CHAP;
+ }
+
+ if (auth == MM_BEARER_ALLOWED_AUTH_NONE)
+ return QMI_WDS_AUTHENTICATION_NONE;
+
+ /* otherwise find a bitmask that matches the input bitmask */
out = QMI_WDS_AUTHENTICATION_NONE;
if (auth & MM_BEARER_ALLOWED_AUTH_PAP)
out |= QMI_WDS_AUTHENTICATION_PAP;
if (auth & MM_BEARER_ALLOWED_AUTH_CHAP)
out |= QMI_WDS_AUTHENTICATION_CHAP;
+ /* and if the bitmask cannot be built, error out */
+ if (out == QMI_WDS_AUTHENTICATION_NONE) {
+ g_autofree gchar *str = NULL;
+
+ str = mm_bearer_allowed_auth_build_string_from_mask (auth);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Unsupported authentication methods (%s)",
+ str);
+ }
+ return out;
+}
+
+MMBearerAllowedAuth
+mm_bearer_allowed_auth_from_qmi_authentication (QmiWdsAuthentication auth)
+{
+ MMBearerAllowedAuth out = 0;
+
+ /* Exact match for NONE */
+ if (auth == QMI_WDS_AUTHENTICATION_NONE)
+ return MM_BEARER_ALLOWED_AUTH_NONE;
+
+ if (auth & QMI_WDS_AUTHENTICATION_PAP)
+ out |= MM_BEARER_ALLOWED_AUTH_PAP;
+ if (auth & QMI_WDS_AUTHENTICATION_CHAP)
+ out |= MM_BEARER_ALLOWED_AUTH_CHAP;
+
return out;
}
+MMBearerIpFamily
+mm_bearer_ip_family_from_qmi_ip_support_type (QmiWdsIpSupportType ip_support_type)
+{
+ switch (ip_support_type) {
+ case QMI_WDS_IP_SUPPORT_TYPE_IPV4:
+ return MM_BEARER_IP_FAMILY_IPV4;
+ case QMI_WDS_IP_SUPPORT_TYPE_IPV6:
+ return MM_BEARER_IP_FAMILY_IPV6;
+ case QMI_WDS_IP_SUPPORT_TYPE_IPV4V6:
+ return MM_BEARER_IP_FAMILY_IPV4V6;
+ default:
+ return MM_BEARER_IP_FAMILY_NONE;
+ }
+}
+
+MMBearerIpFamily
+mm_bearer_ip_family_from_qmi_pdp_type (QmiWdsPdpType pdp_type)
+{
+ switch (pdp_type) {
+ case QMI_WDS_PDP_TYPE_IPV4:
+ return MM_BEARER_IP_FAMILY_IPV4;
+ case QMI_WDS_PDP_TYPE_IPV6:
+ return MM_BEARER_IP_FAMILY_IPV6;
+ case QMI_WDS_PDP_TYPE_IPV4_OR_IPV6:
+ return MM_BEARER_IP_FAMILY_IPV4V6;
+ case QMI_WDS_PDP_TYPE_PPP:
+ default:
+ return MM_BEARER_IP_FAMILY_NONE;
+ }
+}
+
+gboolean
+mm_bearer_ip_family_to_qmi_pdp_type (MMBearerIpFamily ip_family,
+ QmiWdsPdpType *out_pdp_type)
+{
+ switch (ip_family) {
+ case MM_BEARER_IP_FAMILY_IPV4:
+ *out_pdp_type = QMI_WDS_PDP_TYPE_IPV4;
+ return TRUE;
+ case MM_BEARER_IP_FAMILY_IPV6:
+ *out_pdp_type = QMI_WDS_PDP_TYPE_IPV6;
+ return TRUE;
+ case MM_BEARER_IP_FAMILY_IPV4V6:
+ *out_pdp_type = QMI_WDS_PDP_TYPE_IPV4_OR_IPV6;
+ return TRUE;
+ case MM_BEARER_IP_FAMILY_NONE:
+ case MM_BEARER_IP_FAMILY_ANY:
+ default:
+ /* there is no valid conversion, so just return FALSE to indicate it */
+ return FALSE;
+ }
+}
+
+QmiWdsApnTypeMask
+mm_bearer_apn_type_to_qmi_apn_type (MMBearerApnType apn_type,
+ gpointer log_object)
+{
+ guint64 value = 0;
+
+ if (apn_type == MM_BEARER_APN_TYPE_NONE) {
+ mm_obj_dbg (log_object, "using default (internet) APN type");
+ return QMI_WDS_APN_TYPE_MASK_DEFAULT;
+ }
+
+ if (apn_type & MM_BEARER_APN_TYPE_DEFAULT)
+ value |= QMI_WDS_APN_TYPE_MASK_DEFAULT;
+ if (apn_type & MM_BEARER_APN_TYPE_IMS)
+ value |= QMI_WDS_APN_TYPE_MASK_IMS;
+ if (apn_type & MM_BEARER_APN_TYPE_MMS)
+ value |= QMI_WDS_APN_TYPE_MASK_MMS;
+ if (apn_type & MM_BEARER_APN_TYPE_MANAGEMENT)
+ value |= QMI_WDS_APN_TYPE_MASK_FOTA;
+ if (apn_type & MM_BEARER_APN_TYPE_INITIAL)
+ value |= QMI_WDS_APN_TYPE_MASK_IA;
+ if (apn_type & MM_BEARER_APN_TYPE_EMERGENCY)
+ value |= QMI_WDS_APN_TYPE_MASK_EMERGENCY;
+ return value;
+}
+
+MMBearerApnType
+mm_bearer_apn_type_from_qmi_apn_type (QmiWdsApnTypeMask apn_type)
+{
+ MMBearerApnType value = MM_BEARER_APN_TYPE_NONE;
+
+ if (apn_type & QMI_WDS_APN_TYPE_MASK_DEFAULT)
+ value |= MM_BEARER_APN_TYPE_DEFAULT;
+ if (apn_type & QMI_WDS_APN_TYPE_MASK_IMS)
+ value |= MM_BEARER_APN_TYPE_IMS;
+ if (apn_type & QMI_WDS_APN_TYPE_MASK_MMS)
+ value |= MM_BEARER_APN_TYPE_MMS;
+ if (apn_type & QMI_WDS_APN_TYPE_MASK_FOTA)
+ value |= MM_BEARER_APN_TYPE_MANAGEMENT;
+ if (apn_type & QMI_WDS_APN_TYPE_MASK_IA)
+ value |= MM_BEARER_APN_TYPE_INITIAL;
+ if (apn_type & QMI_WDS_APN_TYPE_MASK_EMERGENCY)
+ value |= MM_BEARER_APN_TYPE_EMERGENCY;
+ return value;
+}
+
/*****************************************************************************/
+static const MMMobileEquipmentError qmi_vcer_3gpp_errors[] = {
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPERATOR_DETERMINED_BARRING] = MM_MOBILE_EQUIPMENT_ERROR_OPERATOR_DETERMINED_BARRING,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INSUFFICIENT_RESOURCES] = MM_MOBILE_EQUIPMENT_ERROR_INSUFFICIENT_RESOURCES,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_APN] = MM_MOBILE_EQUIPMENT_ERROR_MISSING_OR_UNKNOWN_APN,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_PDP] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_ADDRESS_OR_TYPE,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_AUTHENTICATION_FAILED] = MM_MOBILE_EQUIPMENT_ERROR_USER_AUTHENTICATION_FAILED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_GGSN_REJECT] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_BY_GGSN_OR_GW,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_ACTIVATION_REJECT] = MM_MOBILE_EQUIPMENT_ERROR_ACTIVATION_REJECTED_UNSPECIFIED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPTION_NOT_SUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUPPORTED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPTION_UNSUBSCRIBED] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_NOT_SUBSCRIBED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_OPTION_TEMPORARILY_OUT_OF_ORDER] = MM_MOBILE_EQUIPMENT_ERROR_SERVICE_OPTION_OUT_OF_ORDER,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_NSAPI_ALREADY_USED] = MM_MOBILE_EQUIPMENT_ERROR_NSAPI_OR_PTI_ALREADY_IN_USE,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_REGULAR_DEACTIVATION] = MM_MOBILE_EQUIPMENT_ERROR_REGULAR_DEACTIVATION,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_QOS_NOT_ACCEPTED] = MM_MOBILE_EQUIPMENT_ERROR_QOS_NOT_ACCEPTED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_NETWORK_FAILURE] = MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_REATTACH_REQUIRED] = MM_MOBILE_EQUIPMENT_ERROR_NETWORK_FAILURE_ATTACH,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_FEATURE_NOT_SUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_FEATURE_NOT_SUPPORTED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_TFT_SEMANTIC_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERROR_IN_TFT_OPERATION,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_TFT_SYNTAX_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_TFT_OPERATION,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_PDP_CONTEXT] = MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN_PDP_CONTEXT,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_FILTER_SEMANTIC_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTIC_ERRORS_IN_PACKET_FILTER,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_FILTER_SYNTAX_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_SYNTACTICAL_ERROR_IN_PACKET_FILTER,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_PDP_WITHOUT_ACTIVE_TFT] = MM_MOBILE_EQUIPMENT_ERROR_PDP_CONTEXT_WITHOUT_TFT_ALREADY_ACTIVATED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_IPV4_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV4_ONLY_ALLOWED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_IPV6_ONLY_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_IPV6_ONLY_ALLOWED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_SINGLE_ADDRESS_BEARER_ONLY] = MM_MOBILE_EQUIPMENT_ERROR_SINGLE_ADDRESS_BEARERS_ONLY_ALLOWED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_ESM_INFO_NOT_RECEIVED] = MM_MOBILE_EQUIPMENT_ERROR_ESM_INFORMATION_NOT_RECEIVED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_PDN_CONNECTION_DOES_NOT_EXIST] = MM_MOBILE_EQUIPMENT_ERROR_PDN_CONNECTION_NONEXISTENT,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MULTIPLE_CONNECTION_TO_SAME_PDN_NOT_ALLOWED] = MM_MOBILE_EQUIPMENT_ERROR_MULTIPLE_PDN_CONNECTION_SAME_APN_NOT_ALLOWED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INVALID_TRANSACTION_ID] = MM_MOBILE_EQUIPMENT_ERROR_INVALID_TRANSACTION_ID_VALUE,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_INCORRECT_SEMANTIC] = MM_MOBILE_EQUIPMENT_ERROR_SEMANTICALLY_INCORRECT_MESSAGE,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INVALID_MANDATORY_INFO] = MM_MOBILE_EQUIPMENT_ERROR_INVALID_MANDATORY_INFORMATION,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_TYPE_UNSUPPORTED] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_IMPLEMENTED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_TYPE_NONCOMPATIBLE_STATE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_UNKNOWN_INFO_ELEMENT] = MM_MOBILE_EQUIPMENT_ERROR_IE_NOT_IMPLEMENTED,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_CONDITIONAL_IE_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_CONDITIONAL_IE_ERROR,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_MESSAGE_AND_PROTOCOL_STATE_UNCOMPATIBLE] = MM_MOBILE_EQUIPMENT_ERROR_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE,
+ [QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_PROTOCOL_ERROR] = MM_MOBILE_EQUIPMENT_ERROR_UNSPECIFIED_PROTOCOL_ERROR,
+ /* unmapped errors */
+ /* QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_LLC_SNDCP_FAILURE */
+ /* QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_APN_TYPE_CONFLICT */
+ /* QMI_WDS_VERBOSE_CALL_END_REASON_3GPP_INVALID_PROXY_CALL_SESSION_CONTROL_FUNCTION_ADDRESS */
+};
+
+GError *
+qmi_mobile_equipment_error_from_verbose_call_end_reason_3gpp (QmiWdsVerboseCallEndReason3gpp vcer_3gpp,
+ gpointer log_object)
+{
+ MMMobileEquipmentError error_code;
+ const gchar *msg;
+
+ /* convert to mobile equipment error */
+ error_code = qmi_vcer_3gpp_errors[vcer_3gpp];
+ if (error_code)
+ return mm_mobile_equipment_error_for_code (error_code, log_object);
+
+ /* provide a nicer error message on unmapped errors */
+ msg = qmi_wds_verbose_call_end_reason_3gpp_get_string (vcer_3gpp);
+ if (msg)
+ return g_error_new (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Unsupported error (%u): %s",
+ vcer_3gpp, msg);
+
+ /* fallback */
+ return g_error_new_literal (MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN,
+ "Unknown error");
+}
+
+/*****************************************************************************/
+/* QMI/WDA to MM translations */
+
+QmiDataEndpointType
+mm_port_subsys_to_qmi_endpoint_type (MMPortSubsys subsys)
+{
+ switch (subsys) {
+ case MM_PORT_SUBSYS_USBMISC:
+ return QMI_DATA_ENDPOINT_TYPE_HSUSB;
+ case MM_PORT_SUBSYS_RPMSG:
+ case MM_PORT_SUBSYS_QRTR:
+ return QMI_DATA_ENDPOINT_TYPE_EMBEDDED;
+ /* The WWAN subsystem abstracts the underlying transport bus, and so
+ * endpoint type can not be deducted from that. This function should
+ * then be revisited, but in practice, only MHI/PCI modem ports are
+ * exposed through the WWAN subsystem for now.
+ */
+ case MM_PORT_SUBSYS_WWAN:
+ return QMI_DATA_ENDPOINT_TYPE_PCIE;
+ case MM_PORT_SUBSYS_UNKNOWN:
+ case MM_PORT_SUBSYS_TTY:
+ case MM_PORT_SUBSYS_NET:
+ case MM_PORT_SUBSYS_UNIX:
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ * The only case where we need to apply some logic to decide what the current
+ * capabilities are is when we have a multimode CDMA/EVDO+GSM/UMTS device, in
+ * which case we'll check the SSP and TP current values to decide which
+ * capabilities are present and which have been disabled.
+ *
+ * For all the other cases, the DMS capabilities are exactly the current ones,
+ * as there would be no capability switching support.
+ */
MMModemCapability
-mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx)
+mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx,
+ gpointer log_object)
{
MMModemCapability tmp = MM_MODEM_CAPABILITY_NONE;
- gchar *nas_ssp_mode_preference_str;
- gchar *nas_tp_str;
- gchar *dms_capabilities_str;
- gchar *tmp_str;
-
- /* SSP logic to gather capabilities uses the Mode Preference TLV if available,
- * and if not available it falls back to using Band Preference TLVs */
- if (ctx->nas_ssp_mode_preference_mask)
- tmp = mm_modem_capability_from_qmi_rat_mode_preference (ctx->nas_ssp_mode_preference_mask);
-
- /* If no value retrieved from SSP, check TP. We only process TP
- * values if not 'auto'. */
- if ( tmp == MM_MODEM_CAPABILITY_NONE
- && ctx->nas_tp_mask
- && ctx->nas_tp_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO)
- tmp = mm_modem_capability_from_qmi_radio_technology_preference (ctx->nas_tp_mask);
-
- /* Final capabilities are the intersection between the Technology
- * Preference or SSP and the device's capabilities.
- * If the Technology Preference was "auto" or unknown we just fall back
- * to the Get Capabilities response.
- */
- if (tmp == MM_MODEM_CAPABILITY_NONE)
+ g_autofree gchar *nas_ssp_mode_preference_str = NULL;
+ g_autofree gchar *nas_tp_str = NULL;
+ g_autofree gchar *dms_capabilities_str = NULL;
+ g_autofree gchar *tmp_str = NULL;
+
+ /* If not a multimode device, we're done */
+#define MULTIMODE (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO)
+ if ((ctx->dms_capabilities & MULTIMODE) != MULTIMODE)
tmp = ctx->dms_capabilities;
- else
- tmp &= ctx->dms_capabilities;
+ else {
+ /* We have a multimode CDMA/EVDO+GSM/UMTS device, check SSP and TP */
+
+ /* SSP logic to gather capabilities uses the Mode Preference TLV if available */
+ if (ctx->nas_ssp_mode_preference_mask)
+ tmp = mm_modem_capability_from_qmi_rat_mode_preference (ctx->nas_ssp_mode_preference_mask);
+ /* If no value retrieved from SSP, check TP. We only process TP
+ * values if not 'auto' (0). */
+ else if (ctx->nas_tp_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO)
+ tmp = mm_modem_capability_from_qmi_radio_technology_preference (ctx->nas_tp_mask);
+
+ /* Final capabilities are the intersection between the Technology
+ * Preference or SSP and the device's capabilities.
+ * If the Technology Preference was "auto" or unknown we just fall back
+ * to the Get Capabilities response.
+ */
+ if (tmp == MM_MODEM_CAPABILITY_NONE)
+ tmp = ctx->dms_capabilities;
+ else
+ tmp &= ctx->dms_capabilities;
+ }
/* Log about the logic applied */
nas_ssp_mode_preference_str = qmi_nas_rat_mode_preference_build_string_from_mask (ctx->nas_ssp_mode_preference_mask);
nas_tp_str = qmi_nas_radio_technology_preference_build_string_from_mask (ctx->nas_tp_mask);
dms_capabilities_str = mm_modem_capability_build_string_from_mask (ctx->dms_capabilities);
tmp_str = mm_modem_capability_build_string_from_mask (tmp);
- mm_dbg ("Current capabilities built: '%s'\n"
- " SSP mode preference: '%s'\n"
- " TP: '%s'\n"
- " DMS Capabilities: '%s'",
- tmp_str,
- nas_ssp_mode_preference_str ? nas_ssp_mode_preference_str : "unknown",
- nas_tp_str ? nas_tp_str : "unknown",
- dms_capabilities_str);
- g_free (nas_ssp_mode_preference_str);
- g_free (nas_tp_str);
- g_free (dms_capabilities_str);
- g_free (tmp_str);
+ mm_obj_dbg (log_object,
+ "Current capabilities built: '%s'\n"
+ " SSP mode preference: '%s'\n"
+ " TP: '%s'\n"
+ " DMS Capabilities: '%s'",
+ tmp_str,
+ nas_ssp_mode_preference_str ? nas_ssp_mode_preference_str : "unknown",
+ nas_tp_str ? nas_tp_str : "unknown",
+ dms_capabilities_str);
return tmp;
}
@@ -1317,6 +1829,7 @@ mm_oma_session_type_to_qmi_oma_session_type (MMOmaSessionType mm_session_type)
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;
+ case MM_OMA_SESSION_TYPE_UNKNOWN:
default:
g_assert_not_reached ();
}
@@ -1376,3 +1889,528 @@ mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (QmiOmaSes
return MM_OMA_SESSION_STATE_FAILED_REASON_UNKNOWN;
}
}
+
+/*****************************************************************************/
+
+gboolean
+mm_error_from_qmi_loc_indication_status (QmiLocIndicationStatus status,
+ GError **error)
+{
+ switch (status) {
+ case QMI_LOC_INDICATION_STATUS_SUCCESS:
+ return TRUE;
+ case QMI_LOC_INDICATION_STATUS_GENERAL_FAILURE:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "LOC service: general failure");
+ return FALSE;
+ case QMI_LOC_INDICATION_STATUS_UNSUPPORTED:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "LOC service: unsupported");
+ return FALSE;
+ case QMI_LOC_INDICATION_STATUS_INVALID_PARAMETER:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "LOC service: invalid parameter");
+ return FALSE;
+ case QMI_LOC_INDICATION_STATUS_ENGINE_BUSY:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS, "LOC service: engine busy");
+ return FALSE;
+ case QMI_LOC_INDICATION_STATUS_PHONE_OFFLINE:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "LOC service: phone offline");
+ return FALSE;
+ case QMI_LOC_INDICATION_STATUS_TIMEOUT:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "LOC service: timeout");
+ return FALSE;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "LOC service: unknown failure");
+ return FALSE;
+ }
+}
+
+/*****************************************************************************/
+/* Convert between firmware unique ID (string) and QMI unique ID (16 bytes)
+ *
+ * The unique ID coming in the QMI message is a fixed-size 16 byte array, and its
+ * format really depends on the manufacturer. But, if the manufacturer is nice enough
+ * to use ASCII for this field, just use it ourselves as well, no need to obfuscate
+ * the information we expose in our interfaces.
+ *
+ * We also need to do the conversion in the other way around, because when
+ * selecting a new image to run we need to provide the QMI unique ID.
+ */
+
+#define EXPECTED_QMI_UNIQUE_ID_LENGTH 16
+
+gchar *
+mm_qmi_unique_id_to_firmware_unique_id (GArray *qmi_unique_id,
+ GError **error)
+{
+ guint i;
+ gboolean expect_nul_byte = FALSE;
+
+ if (qmi_unique_id->len != EXPECTED_QMI_UNIQUE_ID_LENGTH) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "unexpected QMI unique ID length: %u (expected: %u)",
+ qmi_unique_id->len, EXPECTED_QMI_UNIQUE_ID_LENGTH);
+ return NULL;
+ }
+
+ for (i = 0; i < qmi_unique_id->len; i++) {
+ guint8 val;
+
+ val = g_array_index (qmi_unique_id, guint8, i);
+
+ /* Check for ASCII chars */
+ if (g_ascii_isprint ((gchar) val)) {
+ /* Halt iteration if we found an ASCII char after a NUL byte */
+ if (expect_nul_byte)
+ break;
+
+ /* good char */
+ continue;
+ }
+
+ /* Allow NUL bytes at the end of the array */
+ if (val == '\0' && i > 0) {
+ if (!expect_nul_byte)
+ expect_nul_byte = TRUE;
+ continue;
+ }
+
+ /* Halt iteration, not something we can build as ASCII */
+ break;
+ }
+
+ if (i != qmi_unique_id->len)
+ return mm_utils_bin2hexstr ((const guint8 *)qmi_unique_id->data, qmi_unique_id->len);
+
+ return g_strndup ((const gchar *)qmi_unique_id->data, qmi_unique_id->len);
+}
+
+GArray *
+mm_firmware_unique_id_to_qmi_unique_id (const gchar *unique_id,
+ GError **error)
+{
+ guint len;
+ GArray *qmi_unique_id;
+
+ len = strlen (unique_id);
+
+ /* The length will be exactly EXPECTED_QMI_UNIQUE_ID_LENGTH*2 if given in HEX */
+ if (len == (2 * EXPECTED_QMI_UNIQUE_ID_LENGTH)) {
+ g_autofree guint8 *tmp = NULL;
+ gsize tmp_len;
+
+ tmp_len = 0;
+ tmp = mm_utils_hexstr2bin (unique_id, -1, &tmp_len, error);
+ if (!tmp) {
+ g_prefix_error (error, "Unexpected character found in unique id: ");
+ return NULL;
+ }
+ g_assert (tmp_len == EXPECTED_QMI_UNIQUE_ID_LENGTH);
+
+ qmi_unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), tmp_len);
+ g_array_insert_vals (qmi_unique_id, 0, tmp, tmp_len);
+ return qmi_unique_id;
+ }
+
+ /* The length will be EXPECTED_QMI_UNIQUE_ID_LENGTH or less if given in ASCII */
+ if (len > 0 && len <= EXPECTED_QMI_UNIQUE_ID_LENGTH) {
+ guint i;
+
+ for (i = 0; i < len; i++) {
+ if (!g_ascii_isprint (unique_id[i])) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected character found in unique id (not ASCII): %c", unique_id[i]);
+ return NULL;
+ }
+ }
+
+ qmi_unique_id = g_array_sized_new (FALSE, FALSE, sizeof (guint8), EXPECTED_QMI_UNIQUE_ID_LENGTH);
+ g_array_set_size (qmi_unique_id, EXPECTED_QMI_UNIQUE_ID_LENGTH);
+ memcpy (&qmi_unique_id->data[0], unique_id, len);
+ if (len < EXPECTED_QMI_UNIQUE_ID_LENGTH)
+ memset (&qmi_unique_id->data[len], 0, EXPECTED_QMI_UNIQUE_ID_LENGTH - len);
+ return qmi_unique_id;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected unique id length: %u", len);
+ return NULL;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_qmi_uim_get_card_status_output_parse (gpointer log_object,
+ QmiMessageUimGetCardStatusOutput *output,
+ MMModemLock *o_lock,
+ QmiUimPinState *o_pin1_state,
+ guint *o_pin1_retries,
+ guint *o_puk1_retries,
+ QmiUimPinState *o_pin2_state,
+ guint *o_pin2_retries,
+ guint *o_puk2_retries,
+ guint *o_pers_retries,
+ GError **error)
+{
+ QmiMessageUimGetCardStatusOutputCardStatusCardsElement *card;
+ QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement *app;
+ GArray *cards;
+ guint16 index_gw_primary = 0xFFFF;
+ guint8 gw_primary_slot_i = 0;
+ guint8 gw_primary_application_i = 0;
+ MMModemLock lock = MM_MODEM_LOCK_UNKNOWN;
+
+ /* This command supports MULTIPLE cards with MULTIPLE applications each. For our
+ * purposes, we're going to consider as the SIM to use the one identified as
+ * 'primary GW' exclusively. We don't really support Dual Sim Dual Standby yet. */
+
+ qmi_message_uim_get_card_status_output_get_card_status (
+ output,
+ &index_gw_primary,
+ NULL, /* index_1x_primary */
+ NULL, /* index_gw_secondary */
+ NULL, /* index_1x_secondary */
+ &cards,
+ NULL);
+
+ if (cards->len == 0) {
+ g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_FAILED,
+ "No cards reported");
+ return FALSE;
+ }
+
+ /* Look for the primary GW slot and application.
+ * If we don't have valid GW primary slot index and application index, assume
+ * we're missing the SIM altogether */
+ gw_primary_slot_i = ((index_gw_primary & 0xFF00) >> 8);
+ gw_primary_application_i = ((index_gw_primary & 0x00FF));
+
+ if (gw_primary_slot_i == 0xFF) {
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED,
+ "GW primary session index unknown");
+ return FALSE;
+ }
+ mm_obj_dbg (log_object, "GW primary session index: %u", gw_primary_slot_i);
+
+ if (gw_primary_application_i == 0xFF) {
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED,
+ "GW primary application index unknown");
+ return FALSE;
+ }
+ mm_obj_dbg (log_object, "GW primary application index: %u", gw_primary_application_i);
+
+ /* Validate slot index */
+ if (gw_primary_slot_i >= cards->len) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid GW primary session index: %u",
+ gw_primary_slot_i);
+ return FALSE;
+ }
+
+ /* Get card at slot */
+ card = &g_array_index (cards, QmiMessageUimGetCardStatusOutputCardStatusCardsElement, gw_primary_slot_i);
+
+ if (card->card_state == QMI_UIM_CARD_STATE_ABSENT) {
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED,
+ "No card found");
+ return FALSE;
+ }
+
+ if (card->card_state == QMI_UIM_CARD_STATE_ERROR) {
+ const gchar *card_error;
+
+ card_error = qmi_uim_card_error_get_string (card->error_code);
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Card error: %s", card_error ? card_error : "unknown error");
+ return FALSE;
+ }
+
+ if (card->card_state != QMI_UIM_CARD_STATE_PRESENT) {
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Card error: unexpected card state: 0x%x", card->card_state);
+ return FALSE;
+ }
+
+ /* Card is present */
+
+ if (card->applications->len == 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No applications reported in card");
+ return FALSE;
+ }
+
+ /* Validate application index */
+ if (gw_primary_application_i >= card->applications->len) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid GW primary application index: %u",
+ gw_primary_application_i);
+ return FALSE;
+ }
+
+ app = &g_array_index (card->applications, QmiMessageUimGetCardStatusOutputCardStatusCardsElementApplicationsElement, gw_primary_application_i);
+ if ((app->type != QMI_UIM_CARD_APPLICATION_TYPE_SIM) && (app->type != QMI_UIM_CARD_APPLICATION_TYPE_USIM)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported application type found in GW primary application index: %s",
+ qmi_uim_card_application_type_get_string (app->type));
+ return FALSE;
+ }
+
+ /* Illegal application state is fatal, consider it as a failed SIM right
+ * away and don't even attempt to retry */
+ if (app->state == QMI_UIM_CARD_APPLICATION_STATE_ILLEGAL) {
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Illegal SIM/USIM application state");
+ return FALSE;
+ }
+
+ /* If card not ready yet, return RETRY error.
+ * If the application state reports needing PIN/PUk, consider that ready as
+ * well, and let the logic fall down to check PIN1/PIN2. */
+ if (app->state != QMI_UIM_CARD_APPLICATION_STATE_READY &&
+ app->state != QMI_UIM_CARD_APPLICATION_STATE_PIN1_OR_UPIN_PIN_REQUIRED &&
+ app->state != QMI_UIM_CARD_APPLICATION_STATE_PUK1_OR_UPIN_PUK_REQUIRED &&
+ app->state != QMI_UIM_CARD_APPLICATION_STATE_CHECK_PERSONALIZATION_STATE &&
+ app->state != QMI_UIM_CARD_APPLICATION_STATE_PIN1_BLOCKED) {
+ mm_obj_dbg (log_object, "neither SIM nor USIM are ready");
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY,
+ "SIM not ready yet (retry)");
+ return FALSE;
+ }
+
+ /* Report state and retries if requested to do so */
+ if (o_pin1_state)
+ *o_pin1_state = app->pin1_state;
+ if (o_pin1_retries)
+ *o_pin1_retries = app->pin1_retries;
+ if (o_puk1_retries)
+ *o_puk1_retries = app->puk1_retries;
+ if (o_pin2_state)
+ *o_pin2_state = app->pin2_state;
+ if (o_pin2_retries)
+ *o_pin2_retries = app->pin2_retries;
+ if (o_puk2_retries)
+ *o_puk2_retries = app->puk2_retries;
+
+ /* Early bail out if lock status isn't wanted at this point, so that we
+ * don't fail with an error the unlock retries check */
+ if (!o_lock)
+ return TRUE;
+
+ /* Card is ready, what's the lock status? */
+
+ /* PIN1 */
+ switch (app->pin1_state) {
+ case QMI_UIM_PIN_STATE_NOT_INITIALIZED:
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "SIM PIN/PUK status not known yet");
+ return FALSE;
+
+ case QMI_UIM_PIN_STATE_PERMANENTLY_BLOCKED:
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "SIM PIN/PUK permanently blocked");
+ return FALSE;
+
+ case QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED:
+ lock = MM_MODEM_LOCK_SIM_PIN;
+ break;
+
+ case QMI_UIM_PIN_STATE_BLOCKED:
+ lock = MM_MODEM_LOCK_SIM_PUK;
+ break;
+
+ case QMI_UIM_PIN_STATE_DISABLED:
+ case QMI_UIM_PIN_STATE_ENABLED_VERIFIED:
+ lock = MM_MODEM_LOCK_NONE;
+ break;
+
+ default:
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Unknown SIM PIN/PUK status");
+ return FALSE;
+ }
+
+ /* Personalization */
+ if (lock == MM_MODEM_LOCK_NONE &&
+ app->state == QMI_UIM_CARD_APPLICATION_STATE_CHECK_PERSONALIZATION_STATE) {
+ if (app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_IN_PROGRESS ||
+ app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_UNKNOWN) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_RETRY,
+ "Personalization check in progress");
+ return FALSE;
+ }
+ if (app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_CODE_REQUIRED ||
+ app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_PUK_CODE_REQUIRED) {
+ gboolean pin;
+
+ pin = app->personalization_state == QMI_UIM_CARD_APPLICATION_PERSONALIZATION_STATE_CODE_REQUIRED;
+
+ switch (app->personalization_feature) {
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK:
+ lock = (pin ? MM_MODEM_LOCK_PH_NET_PIN : MM_MODEM_LOCK_PH_NET_PUK);
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK_SUBSET:
+ lock = (pin ? MM_MODEM_LOCK_PH_NETSUB_PIN : MM_MODEM_LOCK_PH_NETSUB_PUK);
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SERVICE_PROVIDER:
+ lock = (pin ? MM_MODEM_LOCK_PH_SP_PIN : MM_MODEM_LOCK_PH_SP_PUK);
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_CORPORATE:
+ lock = (pin ? MM_MODEM_LOCK_PH_CORP_PIN : MM_MODEM_LOCK_PH_CORP_PUK);
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_UIM:
+ if (pin) {
+ lock = MM_MODEM_LOCK_PH_SIM_PIN;
+ break;
+ }
+ /* fall through */
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_NETWORK_TYPE_1:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_NETWORK_TYPE_2:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_HRPD:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_SERVICE_PROVIDER:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_CORPORATE:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_RUIM:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_UNKNOWN:
+ default:
+ g_set_error (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG,
+ "Unsupported personalization feature");
+ return FALSE;
+ }
+
+ if (o_pers_retries)
+ *o_pers_retries = app->personalization_retries;
+ }
+ }
+
+ /* PIN2 */
+ if (lock == MM_MODEM_LOCK_NONE) {
+ switch (app->pin2_state) {
+ case QMI_UIM_PIN_STATE_NOT_INITIALIZED:
+ mm_obj_warn (log_object, "SIM PIN2/PUK2 status not known yet");
+ break;
+
+ case QMI_UIM_PIN_STATE_ENABLED_NOT_VERIFIED:
+ lock = MM_MODEM_LOCK_SIM_PIN2;
+ break;
+
+ case QMI_UIM_PIN_STATE_PERMANENTLY_BLOCKED:
+ mm_obj_warn (log_object, "PUK2 permanently blocked");
+ /* Fall through */
+ case QMI_UIM_PIN_STATE_BLOCKED:
+ lock = MM_MODEM_LOCK_SIM_PUK2;
+ break;
+
+ case QMI_UIM_PIN_STATE_DISABLED:
+ case QMI_UIM_PIN_STATE_ENABLED_VERIFIED:
+ break;
+
+ default:
+ mm_obj_warn (log_object, "unknown SIM PIN2/PUK2 status");
+ break;
+ }
+ }
+
+ *o_lock = lock;
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_qmi_uim_get_configuration_output_parse (gpointer log_object,
+ QmiMessageUimGetConfigurationOutput *output,
+ MMModem3gppFacility *o_lock,
+ GError **error)
+{
+ QmiMessageUimGetConfigurationOutputPersonalizationStatusElement *element;
+ GArray *elements;
+ guint idx;
+
+ *o_lock = MM_MODEM_3GPP_FACILITY_NONE;
+
+ if (!qmi_message_uim_get_configuration_output_get_personalization_status (output, &elements, error)) {
+ g_prefix_error (error, "UIM Get Personalization Status failed: ");
+ return FALSE;
+ }
+
+ for (idx = 0; idx < elements->len; idx++) {
+ element = &g_array_index (elements,
+ QmiMessageUimGetConfigurationOutputPersonalizationStatusElement,
+ idx);
+ switch (element->feature) {
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK:
+ *o_lock |= MM_MODEM_3GPP_FACILITY_NET_PERS;
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK_SUBSET:
+ *o_lock |= MM_MODEM_3GPP_FACILITY_NET_SUB_PERS;
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SERVICE_PROVIDER:
+ *o_lock |= MM_MODEM_3GPP_FACILITY_PROVIDER_PERS;
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_CORPORATE:
+ *o_lock |= MM_MODEM_3GPP_FACILITY_CORP_PERS;
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_UIM:
+ *o_lock |= MM_MODEM_3GPP_FACILITY_PH_SIM;
+ break;
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_NETWORK_TYPE_1:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_NETWORK_TYPE_2:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_HRPD:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_SERVICE_PROVIDER:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_CORPORATE:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_1X_RUIM:
+ case QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_UNKNOWN:
+ mm_obj_dbg (log_object, "ignoring lock in UIM feature: %s",
+ qmi_uim_card_application_personalization_feature_get_string (element->feature));
+ break;
+ default:
+ mm_obj_dbg (log_object, "ignoring lock in unhandled UIM feature: 0x%x",
+ (guint)element->feature);
+ }
+ }
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+QmiUimCardApplicationPersonalizationFeature
+qmi_personalization_feature_from_mm_modem_3gpp_facility (MMModem3gppFacility facility)
+{
+ switch (facility) {
+ case MM_MODEM_3GPP_FACILITY_NET_PERS:
+ return QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK;
+ case MM_MODEM_3GPP_FACILITY_NET_SUB_PERS:
+ return QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_NETWORK_SUBSET;
+ case MM_MODEM_3GPP_FACILITY_PROVIDER_PERS:
+ return QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_SERVICE_PROVIDER;
+ case MM_MODEM_3GPP_FACILITY_CORP_PERS:
+ return QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_CORPORATE;
+ case MM_MODEM_3GPP_FACILITY_PH_SIM:
+ return QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_GW_UIM;
+ case MM_MODEM_3GPP_FACILITY_NONE:
+ case MM_MODEM_3GPP_FACILITY_SIM:
+ case MM_MODEM_3GPP_FACILITY_FIXED_DIALING:
+ case MM_MODEM_3GPP_FACILITY_PH_FSIM:
+ default:
+ return QMI_UIM_CARD_APPLICATION_PERSONALIZATION_FEATURE_UNKNOWN;
+ }
+}
diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h
index fb3ef8d2..4891fae1 100644
--- a/src/mm-modem-helpers-qmi.h
+++ b/src/mm-modem-helpers-qmi.h
@@ -21,20 +21,27 @@
#include <ModemManager.h>
#include <libqmi-glib.h>
+#include "mm-port.h"
+
/*****************************************************************************/
/* QMI/DMS to MM translations */
-MMModemCapability mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network);
+MMModemCapability mm_modem_capability_from_qmi_radio_interface (QmiDmsRadioInterface network,
+ gpointer log_object);
-MMModemMode mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network);
+MMModemMode mm_modem_mode_from_qmi_radio_interface (QmiDmsRadioInterface network,
+ gpointer log_object);
MMModemLock mm_modem_lock_from_qmi_uim_pin_status (QmiDmsUimPinStatus status,
gboolean pin1);
+gboolean mm_pin_enabled_from_qmi_uim_pin_status (QmiDmsUimPinStatus status);
QmiDmsUimFacility mm_3gpp_facility_to_qmi_uim_facility (MMModem3gppFacility mm);
-GArray *mm_modem_bands_from_qmi_band_capabilities (QmiDmsBandCapability qmi_bands,
- QmiDmsLteBandCapability qmi_lte_bands);
+GArray *mm_modem_bands_from_qmi_band_capabilities (QmiDmsBandCapability qmi_bands,
+ QmiDmsLteBandCapability qmi_lte_bands,
+ GArray *extended_qmi_lte_bands,
+ gpointer log_object);
/*****************************************************************************/
/* QMI/NAS to MM translations */
@@ -45,6 +52,8 @@ MMModemAccessTechnology mm_modem_access_technologies_from_qmi_radio_interface_ar
MMModemAccessTechnology mm_modem_access_technology_from_qmi_data_capability (QmiNasDataCapability cap);
MMModemAccessTechnology mm_modem_access_technologies_from_qmi_data_capability_array (GArray *data_capabilities);
+MMModemMode mm_modem_mode_from_qmi_nas_radio_interface (QmiNasRadioInterface iface);
+
MMModemMode mm_modem_mode_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi);
QmiNasRadioTechnologyPreference mm_modem_mode_to_qmi_radio_technology_preference (MMModemMode mode,
gboolean is_cdma);
@@ -57,21 +66,34 @@ QmiNasRatModePreference mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mo
MMModemCapability mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi);
QmiNasRatModePreference mm_modem_capability_to_qmi_rat_mode_preference (MMModemCapability caps);
+GArray *mm_modem_capability_to_qmi_acquisition_order_preference (MMModemCapability caps);
+GArray *mm_modem_mode_to_qmi_acquisition_order_preference (MMModemMode allowed,
+ MMModemMode preferred,
+ GArray *all);
+
MMModemCapability mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi);
QmiNasRadioTechnologyPreference mm_modem_capability_to_qmi_radio_technology_preference (MMModemCapability caps);
MMModemCapability mm_modem_capability_from_qmi_band_preference (QmiNasBandPreference qmi);
-MMModemMode mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi);
-QmiNasGsmWcdmaAcquisitionOrderPreference mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode);
+MMModemMode mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi,
+ gpointer log_object);
+QmiNasGsmWcdmaAcquisitionOrderPreference mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode,
+ gpointer log_object);
GArray *mm_modem_bands_from_qmi_rf_band_information_array (GArray *info_array);
-GArray *mm_modem_bands_from_qmi_band_preference (QmiNasBandPreference qmi_bands,
- QmiNasLteBandPreference qmi_lte_bands);
-void mm_modem_bands_to_qmi_band_preference (GArray *mm_bands,
- QmiNasBandPreference *qmi_bands,
- QmiNasLteBandPreference *qmi_lte_bands);
+GArray *mm_modem_bands_from_qmi_band_preference (QmiNasBandPreference qmi_bands,
+ QmiNasLteBandPreference qmi_lte_bands,
+ const guint64 *extended_qmi_lte_bands,
+ guint extended_qmi_lte_bands_size,
+ gpointer log_object);
+void mm_modem_bands_to_qmi_band_preference (GArray *mm_bands,
+ QmiNasBandPreference *qmi_bands,
+ QmiNasLteBandPreference *qmi_lte_bands,
+ guint64 *extended_qmi_lte_bands,
+ guint extended_qmi_lte_bands_size,
+ gpointer log_object);
MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_qmi_registration_state (QmiNasAttachState attach_state,
QmiNasRegistrationState registration_state,
@@ -92,7 +114,25 @@ MMSmsState mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag);
/*****************************************************************************/
/* QMI/WDS to MM translations */
-QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth);
+QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth,
+ gpointer log_object,
+ GError **error);
+MMBearerAllowedAuth mm_bearer_allowed_auth_from_qmi_authentication (QmiWdsAuthentication auth);
+MMBearerIpFamily mm_bearer_ip_family_from_qmi_ip_support_type (QmiWdsIpSupportType ip_support_type);
+MMBearerIpFamily mm_bearer_ip_family_from_qmi_pdp_type (QmiWdsPdpType pdp_type);
+gboolean mm_bearer_ip_family_to_qmi_pdp_type (MMBearerIpFamily ip_family,
+ QmiWdsPdpType *out_pdp_type);
+QmiWdsApnTypeMask mm_bearer_apn_type_to_qmi_apn_type (MMBearerApnType apn_type,
+ gpointer log_object);
+MMBearerApnType mm_bearer_apn_type_from_qmi_apn_type (QmiWdsApnTypeMask apn_type);
+
+GError *qmi_mobile_equipment_error_from_verbose_call_end_reason_3gpp (QmiWdsVerboseCallEndReason3gpp vcer_3gpp,
+ gpointer log_object);
+
+/*****************************************************************************/
+/* QMI/WDA to MM translations */
+
+QmiDataEndpointType mm_port_subsys_to_qmi_endpoint_type (MMPortSubsys subsys);
/*****************************************************************************/
/* QMI/OMA to MM translations */
@@ -105,6 +145,12 @@ MMOmaSessionState mm_oma_session_state_from_qmi_oma_session_state (QmiOmaSession
MMOmaSessionStateFailedReason mm_oma_session_state_failed_reason_from_qmi_oma_session_failed_reason (QmiOmaSessionFailedReason qmi_session_failed_reason);
/*****************************************************************************/
+/* QMI/LOC to MM translations */
+
+gboolean mm_error_from_qmi_loc_indication_status (QmiLocIndicationStatus status,
+ GError **error);
+
+/*****************************************************************************/
/* Utility to gather current capabilities from various sources */
typedef struct {
@@ -116,6 +162,40 @@ typedef struct {
MMModemCapability dms_capabilities;
} MMQmiCapabilitiesContext;
-MMModemCapability mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx);
+MMModemCapability mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx,
+ gpointer log_object);
+
+/*****************************************************************************/
+/* QMI unique id manipulation */
+
+gchar *mm_qmi_unique_id_to_firmware_unique_id (GArray *qmi_unique_id,
+ GError **error);
+GArray *mm_firmware_unique_id_to_qmi_unique_id (const gchar *unique_id,
+ GError **error);
+
+/*****************************************************************************/
+/* Common UIM Get Card Status parsing */
+
+gboolean mm_qmi_uim_get_card_status_output_parse (gpointer log_object,
+ QmiMessageUimGetCardStatusOutput *output,
+ MMModemLock *o_lock,
+ QmiUimPinState *o_pin1_state,
+ guint *o_pin1_retries,
+ guint *o_puk1_retries,
+ QmiUimPinState *o_pin2_state,
+ guint *o_pin2_retries,
+ guint *o_puk2_retries,
+ guint *o_pers_retries,
+ GError **error);
+
+/*****************************************************************************/
+/* UIM Get Configuration parsing */
+
+gboolean mm_qmi_uim_get_configuration_output_parse (gpointer log_object,
+ QmiMessageUimGetConfigurationOutput *output,
+ MMModem3gppFacility *o_lock,
+ GError **error);
+
+QmiUimCardApplicationPersonalizationFeature qmi_personalization_feature_from_mm_modem_3gpp_facility (MMModem3gppFacility facility);
#endif /* MM_MODEM_HELPERS_QMI_H */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index e4967d70..6f645dbb 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -29,8 +29,10 @@
#include <libmm-glib.h>
#include "mm-sms-part.h"
+#include "mm-common-helpers.h"
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-helper-enums-types.h"
+#include "mm-log-object.h"
/*****************************************************************************/
@@ -68,6 +70,168 @@ mm_strip_tag (const gchar *str, const gchar *cmd)
/*****************************************************************************/
+gchar **
+mm_split_string_groups (const gchar *str)
+{
+ GPtrArray *array;
+ const gchar *start;
+ const gchar *next;
+
+ array = g_ptr_array_new ();
+
+ /*
+ * Manually parse splitting groups. Groups may be single elements, or otherwise
+ * lists given between parenthesis, e.g.:
+ *
+ * ("SM","ME"),("SM","ME"),("SM","ME")
+ * "SM","SM","SM"
+ * "SM",("SM","ME"),("SM","ME")
+ */
+
+ /* Iterate string splitting groups */
+ for (start = str; start; start = next) {
+ gchar *item;
+ gssize len = -1;
+
+ /* skip leading whitespaces */
+ while (*start == ' ')
+ start++;
+
+ if (*start == '(') {
+ start++;
+ next = strchr (start, ')');
+ if (next) {
+ len = next - start;
+ next = strchr (next, ',');
+ if (next)
+ next++;
+ }
+ } else {
+ next = strchr (start, ',');
+ if (next) {
+ len = next - start;
+ next++;
+ }
+ }
+
+ if (len < 0)
+ item = g_strdup (start);
+ else
+ item = g_strndup (start, len);
+
+ g_ptr_array_add (array, item);
+ }
+
+ if (array->len > 0) {
+ g_ptr_array_add (array, NULL);
+ return (gchar **) g_ptr_array_free (array, FALSE);
+ }
+
+ g_ptr_array_unref (array);
+ return NULL;
+}
+
+/*****************************************************************************/
+
+static int uint_compare_func (gconstpointer a, gconstpointer b)
+{
+ return (*(guint *)a - *(guint *)b);
+}
+
+GArray *
+mm_parse_uint_list (const gchar *str,
+ GError **error)
+{
+ GArray *array;
+ gchar *dupstr;
+ gchar *aux;
+ GError *inner_error = NULL;
+
+ if (!str || !str[0])
+ return NULL;
+
+ /* Parses into a GArray of guints, the list of numbers given in the string,
+ * also supporting number intervals.
+ * E.g.:
+ * 1-6 --> 1,2,3,4,5,6
+ * 1,2,4-6 --> 1,2,4,5,6
+ */
+ array = g_array_new (FALSE, FALSE, sizeof (guint));
+ aux = dupstr = g_strdup (str);
+
+ while (aux) {
+ gchar *next;
+ gchar *interval;
+
+ next = strchr (aux, ',');
+ if (next) {
+ *next = '\0';
+ next++;
+ }
+
+ interval = strchr (aux, '-');
+ if (interval) {
+ guint start = 0;
+ guint stop = 0;
+
+ *interval = '\0';
+ interval++;
+
+ if (!mm_get_uint_from_str (aux, &start)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't parse interval start integer: '%s'", aux);
+ goto out;
+ }
+ if (!mm_get_uint_from_str (interval, &stop)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't parse interval stop integer: '%s'", interval);
+ goto out;
+ }
+
+ if (start > stop) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "interval start (%u) cannot be bigger than interval stop (%u)", start, stop);
+ goto out;
+ }
+
+ for (; start <= stop; start++)
+ g_array_append_val (array, start);
+ } else {
+ guint num;
+
+ if (!mm_get_uint_from_str (aux, &num)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't parse integer: '%s'", aux);
+ goto out;
+ }
+
+ g_array_append_val (array, num);
+ }
+
+ aux = next;
+ }
+
+ if (!array->len)
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "couldn't parse list of integers: '%s'", str);
+ else
+ g_array_sort (array, uint_compare_func);
+
+out:
+ g_free (dupstr);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ g_array_unref (array);
+ return NULL;
+ }
+
+ return array;
+}
+
+/*****************************************************************************/
+
guint
mm_count_bits_set (gulong number)
{
@@ -78,11 +242,22 @@ mm_count_bits_set (gulong number)
return c;
}
+guint
+mm_find_bit_set (gulong number)
+{
+ guint c = 0;
+
+ for (c = 0; !(number & 0x1); c++)
+ number >>= 1;
+ return c;
+}
+
/*****************************************************************************/
gchar *
-mm_create_device_identifier (guint vid,
- guint pid,
+mm_create_device_identifier (guint vid,
+ guint pid,
+ gpointer log_object,
const gchar *ati,
const gchar *ati1,
const gchar *gsn,
@@ -90,9 +265,11 @@ mm_create_device_identifier (guint vid,
const gchar *model,
const gchar *manf)
{
- GString *devid, *msg = NULL;
- GChecksum *sum;
- gchar *p, *ret = NULL;
+ g_autoptr(GString) devid = NULL;
+ g_autoptr(GString) msg = NULL;
+ g_autoptr(GChecksum) sum = NULL;
+ const gchar *ret;
+ gchar *p = NULL;
gchar str_vid[10], str_pid[10];
/* Build up the device identifier */
@@ -113,14 +290,11 @@ mm_create_device_identifier (guint vid,
if (manf)
g_string_append (devid, manf);
- if (!strlen (devid->str)) {
- g_string_free (devid, TRUE);
+ if (!strlen (devid->str))
return NULL;
- }
p = devid->str;
msg = g_string_sized_new (strlen (devid->str) + 17);
-
sum = g_checksum_new (G_CHECKSUM_SHA1);
if (vid) {
@@ -128,7 +302,7 @@ mm_create_device_identifier (guint vid,
g_checksum_update (sum, (const guchar *) &str_vid[0], strlen (str_vid));
g_string_append_printf (msg, "%08x", vid);
}
- if (vid) {
+ if (pid) {
snprintf (str_pid, sizeof (str_pid) - 1, "%08x", pid);
g_checksum_update (sum, (const guchar *) &str_pid[0], strlen (str_pid));
g_string_append_printf (msg, "%08x", pid);
@@ -142,15 +316,10 @@ mm_create_device_identifier (guint vid,
}
p++;
}
- ret = g_strdup (g_checksum_get_string (sum));
- g_checksum_free (sum);
-
- mm_dbg ("Device ID source '%s'", msg->str);
- mm_dbg ("Device ID '%s'", ret);
- g_string_free (msg, TRUE);
- g_string_free (devid, TRUE);
- return ret;
+ ret = g_checksum_get_string (sum);
+ mm_obj_dbg (log_object, "device identifier built: %s -> %s", msg->str, ret);
+ return g_strdup (ret);
}
/*****************************************************************************/
@@ -216,41 +385,10 @@ mm_filter_current_bands (const GArray *supported_bands,
/*****************************************************************************/
-gchar *
-mm_new_iso8601_time (guint year,
- guint month,
- guint day,
- guint hour,
- guint minute,
- guint second,
- gboolean have_offset,
- gint offset_minutes)
-{
- GString *str;
-
- str = g_string_sized_new (30);
- g_string_append_printf (str, "%04d-%02d-%02dT%02d:%02d:%02d",
- year, month, day, hour, minute, second);
- if (have_offset) {
- if (offset_minutes >=0 ) {
- g_string_append_printf (str, "+%02d:%02d",
- offset_minutes / 60,
- offset_minutes % 60);
- } else {
- offset_minutes *= -1;
- g_string_append_printf (str, "-%02d:%02d",
- offset_minutes / 60,
- offset_minutes % 60);
- }
- }
- return g_string_free (str, FALSE);
-}
-
-/*****************************************************************************/
-
GArray *
mm_filter_supported_modes (const GArray *all,
- const GArray *supported_combinations)
+ const GArray *supported_combinations,
+ gpointer log_object)
{
MMModemModeCombination all_item;
guint i;
@@ -261,6 +399,9 @@ mm_filter_supported_modes (const GArray *all,
g_return_val_if_fail (all->len == 1, NULL);
g_return_val_if_fail (supported_combinations != NULL, NULL);
+ mm_obj_dbg (log_object, "filtering %u supported mode combinations with %u modes",
+ supported_combinations->len, all->len);
+
all_item = g_array_index (all, MMModemModeCombination, 0);
g_return_val_if_fail (all_item.allowed != MM_MODEM_MODE_NONE, NULL);
@@ -282,182 +423,449 @@ mm_filter_supported_modes (const GArray *all,
}
if (filtered_combinations->len == 0)
- mm_warn ("All supported mode combinations were filtered out.");
+ mm_obj_warn (log_object, "all supported mode combinations were filtered out");
/* Add default entry with the generic mask including all items */
if (!all_item_added) {
- mm_dbg ("Adding an explicit item with all supported modes allowed");
+ mm_obj_dbg (log_object, "adding an explicit item with all supported modes allowed");
g_array_append_val (filtered_combinations, all_item);
}
+ mm_obj_dbg (log_object, "device supports %u different mode combinations",
+ filtered_combinations->len);
+
return filtered_combinations;
}
/*****************************************************************************/
-GArray *
-mm_filter_supported_capabilities (MMModemCapability all,
- const GArray *supported_combinations)
+static const gchar bcd_chars[] = "0123456789\0\0\0\0\0\0";
+
+gchar *
+mm_bcd_to_string (const guint8 *bcd, gsize bcd_len, gboolean low_nybble_first)
{
- guint i;
- GArray *filtered_combinations;
+ GString *str;
+ gsize i;
- g_return_val_if_fail (all != MM_MODEM_CAPABILITY_NONE, NULL);
- g_return_val_if_fail (supported_combinations != NULL, NULL);
+ g_return_val_if_fail (bcd != NULL, NULL);
- /* We will filter out all combinations which have modes not listed in 'all' */
- filtered_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), supported_combinations->len);
- for (i = 0; i < supported_combinations->len; i++) {
- MMModemCapability capability;
+ str = g_string_sized_new (bcd_len * 2 + 1);
+ for (i = 0 ; i < bcd_len; i++) {
+ if (low_nybble_first)
+ str = g_string_append_c (str, bcd_chars[bcd[i] & 0xF]);
+ str = g_string_append_c (str, bcd_chars[(bcd[i] >> 4) & 0xF]);
+ if (!low_nybble_first)
+ str = g_string_append_c (str, bcd_chars[bcd[i] & 0xF]);
+ }
+ return g_string_free (str, FALSE);
+}
+
+/*****************************************************************************/
+
+GRegex *
+mm_voice_ring_regex_get (void)
+{
+ /* Example:
+ * <CR><LF>RING<CR><LF>
+ */
+ return g_regex_new ("\\r\\nRING\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+}
+
+GRegex *
+mm_voice_cring_regex_get (void)
+{
+ /* Example:
+ * <CR><LF>+CRING: VOICE<CR><LF>
+ * <CR><LF>+CRING: DATA<CR><LF>
+ */
+ return g_regex_new ("\\r\\n\\+CRING:\\s*(\\S+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+}
+
+GRegex *
+mm_voice_clip_regex_get (void)
+{
+ /*
+ * Only first 2 fields are mandatory:
+ * +CLIP: <number>,<type>[,<subaddr>,<satype>[,[<alpha>][,<CLI_validity>]]]
+ *
+ * Example:
+ * <CR><LF>+CLIP: "+393351391306",145,,,,0<CR><LF>
+ * \_ Number \_ Type
+ */
+ return g_regex_new ("\\r\\n\\+CLIP:\\s*([^,\\s]*)\\s*,\\s*(\\d+)\\s*,?(.*)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+}
+
+GRegex *
+mm_voice_ccwa_regex_get (void)
+{
+ /*
+ * Only first 3 fields are mandatory, but we read only the first one
+ * +CCWA: <number>,<type>,<class>,[<alpha>][,<CLI_validity>[,<subaddr>,<satype>[,<priority>]]]
+ *
+ * Example:
+ * <CR><LF>+CCWA: "+393351391306",145,1
+ * \_ Number \_ Type
+ */
+ return g_regex_new ("\\r\\n\\+CCWA:\\s*([^,\\s]*)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,?(.*)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+}
+
+static void
+call_info_free (MMCallInfo *info)
+{
+ if (!info)
+ return;
+ g_free (info->number);
+ g_slice_free (MMCallInfo, info);
+}
+
+gboolean
+mm_3gpp_parse_clcc_response (const gchar *str,
+ gpointer log_object,
+ GList **out_list,
+ GError **error)
+{
+ GRegex *r;
+ GList *list = NULL;
+ GError *inner_error = NULL;
+ GMatchInfo *match_info = NULL;
+
+ static const MMCallDirection call_direction[] = {
+ [0] = MM_CALL_DIRECTION_OUTGOING,
+ [1] = MM_CALL_DIRECTION_INCOMING,
+ };
+
+ static const MMCallState call_state[] = {
+ [0] = MM_CALL_STATE_ACTIVE,
+ [1] = MM_CALL_STATE_HELD,
+ [2] = MM_CALL_STATE_DIALING, /* Dialing (MOC) */
+ [3] = MM_CALL_STATE_RINGING_OUT, /* Alerting (MOC) */
+ [4] = MM_CALL_STATE_RINGING_IN, /* Incoming (MTC) */
+ [5] = MM_CALL_STATE_WAITING, /* Waiting (MTC) */
+
+ /* This next call state number isn't defined by 3GPP, because it
+ * doesn't make sense to have it when reporting a full list of calls
+ * via +CLCC (i.e. the absence of the call would mean it's terminated).
+ * But, this value may be used by other implementations (e.g. SimTech
+ * plugin) to report that a call is terminated even when the full
+ * call list isn't being reported. So, let's support it in the generic,
+ * parser, even if not strictly standard. */
+ [6] = MM_CALL_STATE_TERMINATED,
+ };
+
+ g_assert (out_list);
+
+ /*
+ * 1 2 3 4 5 6 7 8 9 10
+ * +CLCC: <idx>,<dir>,<stat>,<mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>[,<CLI validity>]]]]
+ * +CLCC: <idx>,<dir>,<stat>,<mode>,<mpty>[,<number>,<type>[,<alpha>[,<priority>[,<CLI validity>]]]]
+ * ...
+ */
+
+ r = g_regex_new ("\\+CLCC:\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+),\\s*(\\d+)" /* mandatory fields */
+ "(?:,\\s*([^,]*),\\s*(\\d+)" /* number and type */
+ "(?:,\\s*([^,]*)" /* alpha */
+ "(?:,\\s*(\\d*)" /* priority */
+ "(?:,\\s*(\\d*)" /* CLI validity */
+ ")?)?)?)?$",
+ G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF,
+ G_REGEX_MATCH_NEWLINE_CRLF,
+ NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ /* Parse the results */
+ while (g_match_info_matches (match_info)) {
+ MMCallInfo *call_info;
+ guint aux;
+
+ call_info = g_slice_new0 (MMCallInfo);
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &call_info->index)) {
+ mm_obj_warn (log_object, "couldn't parse call index from +CLCC line");
+ goto next;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &aux) ||
+ (aux >= G_N_ELEMENTS (call_direction))) {
+ mm_obj_warn (log_object, "couldn't parse call direction from +CLCC line");
+ goto next;
+ }
+ call_info->direction = call_direction[aux];
+
+ if (!mm_get_uint_from_match_info (match_info, 3, &aux) ||
+ (aux >= G_N_ELEMENTS (call_state))) {
+ mm_obj_warn (log_object, "couldn't parse call state from +CLCC line");
+ goto next;
+ }
+ call_info->state = call_state[aux];
- capability = g_array_index (supported_combinations, MMModemCapability, i);
- if (!(capability & ~all))
- g_array_append_val (filtered_combinations, capability);
+ if (!mm_get_uint_from_match_info (match_info, 4, &aux)) {
+ mm_obj_warn (log_object, "couldn't parse mode from +CLCC line");
+ goto next;
+ }
+
+ /*
+ * Skip calls in Fax-only and DATA-only mode (3GPP TS 27.007):
+ * 0: Voice
+ * 1: Data
+ * 2: Fax
+ * 3: Voice followed by data, voice mode
+ * 4: Alternating voice/data, voice mode
+ * 5: Alternating voice/fax, voice mode
+ * 6: Voice followed by data, data mode
+ * 7: Alternating voice/data, data mode
+ * 8: Alternating voice/fax, fax mode
+ * 9: unknown
+ */
+ if (aux != 0 && aux != 3 && aux != 4 && aux != 5) {
+ mm_obj_dbg (log_object, "+CLCC line is not a voice call, skipping.");
+ goto next;
+ }
+
+ if (g_match_info_get_match_count (match_info) >= 7)
+ call_info->number = mm_get_string_unquoted_from_match_info (match_info, 6);
+
+ list = g_list_append (list, call_info);
+ call_info = NULL;
+
+ next:
+ call_info_free (call_info);
+ g_match_info_next (match_info, NULL);
}
- if (filtered_combinations->len == 0)
- mm_warn ("All supported capability combinations were filtered out.");
+out:
+ g_clear_pointer (&match_info, g_match_info_free);
+ g_regex_unref (r);
- return filtered_combinations;
+ if (inner_error) {
+ mm_3gpp_call_info_list_free (list);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ *out_list = list;
+
+ return TRUE;
}
-/*****************************************************************************/
+void
+mm_3gpp_call_info_list_free (GList *call_info_list)
+{
+ g_list_free_full (call_info_list, (GDestroyNotify) call_info_free);
+}
-/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
-#define CREG1 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])"
+/*************************************************************************/
-/* +CREG: <n>,<stat> (GSM 07.07 CREG=1 solicited) */
-#define CREG2 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])"
+static MMFlowControl
+flow_control_array_to_mask (GArray *array,
+ const gchar *item,
+ gpointer log_object)
+{
+ MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
+ guint i;
-/* +CREG: <stat>,<lac>,<ci> (GSM 07.07 CREG=2 unsolicited) */
-#define CREG3 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+ for (i = 0; i < array->len; i++) {
+ guint mode;
-/* +CREG: <n>,<stat>,<lac>,<ci> (GSM 07.07 solicited and some CREG=2 unsolicited) */
-#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]*\")"
+ mode = g_array_index (array, guint, i);
+ switch (mode) {
+ case 0:
+ mm_obj_dbg (log_object, "%s supports no flow control", item);
+ mask |= MM_FLOW_CONTROL_NONE;
+ break;
+ case 1:
+ mm_obj_dbg (log_object, "%s supports XON/XOFF flow control", item);
+ mask |= MM_FLOW_CONTROL_XON_XOFF;
+ break;
+ case 2:
+ mm_obj_dbg (log_object, "%s supports RTS/CTS flow control", item);
+ mask |= MM_FLOW_CONTROL_RTS_CTS;
+ break;
+ default:
+ break;
+ }
+ }
-/* +CREG: <stat>,<lac>,<ci>,<AcT> (ETSI 27.007 CREG=2 unsolicited) */
-#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])"
+ return mask;
+}
-/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
-#define CREG8 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
+static MMFlowControl
+flow_control_match_info_to_mask (GMatchInfo *match_info,
+ guint index,
+ const gchar *item,
+ gpointer log_object,
+ GError **error)
+{
+ MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
+ gchar *aux = NULL;
+ GArray *array = NULL;
-/* +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 CREG9 "\\+(CREG|CGREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*[^,\\s]*"
+ if (!(aux = mm_get_string_unquoted_from_match_info (match_info, index))) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Error retrieving list of supported %s flow control methods", item);
+ goto out;
+ }
-/* +CREG: <stat>,<lac>,<ci>,<AcT>,<RAC> (ETSI 27.007 v9.20 CREG=2 unsolicited with RAC) */
-#define CREG10 "\\+(CREG|CGREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])\\s*,\\s*([^,\\s]*)"
+ if (!(array = mm_parse_uint_list (aux, error))) {
+ g_prefix_error (error, "Error parsing list of supported %s flow control methods: ", item);
+ goto out;
+ }
-/* +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])"
+ if ((mask = flow_control_array_to_mask (array, item, log_object)) == MM_FLOW_CONTROL_UNKNOWN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No known %s flow control method given", item);
+ goto out;
+ }
-/* +CEREG: <n>,<stat>,<lac>,<rac>,<ci>,<AcT> (ETSI 27.007 v8.6 CREG=2 solicited with RAC) */
-#define CEREG2 "\\+(CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])"
+out:
+ g_clear_pointer (&aux, g_free);
+ g_clear_pointer (&array, g_array_unref);
-GPtrArray *
-mm_3gpp_creg_regex_get (gboolean solicited)
+ return mask;
+}
+
+MMFlowControl
+mm_parse_ifc_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error)
{
- GPtrArray *array = g_ptr_array_sized_new (12);
- GRegex *regex;
+ GRegex *r;
+ GError *inner_error = NULL;
+ GMatchInfo *match_info = NULL;
+ MMFlowControl te_mask = MM_FLOW_CONTROL_UNKNOWN;
+ MMFlowControl ta_mask = MM_FLOW_CONTROL_UNKNOWN;
+ MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
+
+ r = g_regex_new ("(?:\\+IFC:)?\\s*\\((.*)\\),\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
- /* #1 */
- if (solicited)
- regex = g_regex_new (CREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
- /* #2 */
- if (solicited)
- regex = g_regex_new (CREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+ goto out;
+ }
- /* #3 */
- if (solicited)
- regex = g_regex_new (CREG3 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG3 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ /* Parse TE flow control methods */
+ if ((te_mask = flow_control_match_info_to_mask (match_info, 1, "TE", log_object, &inner_error)) == MM_FLOW_CONTROL_UNKNOWN)
+ goto out;
- /* #4 */
- if (solicited)
- regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG4 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ /* Parse TA flow control methods */
+ if ((ta_mask = flow_control_match_info_to_mask (match_info, 2, "TA", log_object, &inner_error)) == MM_FLOW_CONTROL_UNKNOWN)
+ goto out;
- /* #5 */
- if (solicited)
- regex = g_regex_new (CREG5 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG5 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ /* Only those methods in both TA and TE will be the ones we report */
+ mask = te_mask & ta_mask;
- /* #6 */
- if (solicited)
- regex = g_regex_new (CREG6 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG6 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+out:
- /* #7 */
- if (solicited)
- regex = g_regex_new (CREG7 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG7 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ g_clear_pointer (&match_info, g_match_info_free);
+ g_regex_unref (r);
- /* #8 */
- if (solicited)
- regex = g_regex_new (CREG8 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CREG8 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ if (inner_error)
+ g_propagate_error (error, inner_error);
- /* #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);
+ return mask;
+}
- /* #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);
+MMFlowControl
+mm_flow_control_from_string (const gchar *str,
+ GError **error)
+{
+ GFlagsClass *flags_class;
+ guint value;
+ guint i;
- /* CEREG #1 */
- if (solicited)
- regex = g_regex_new (CEREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CEREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ flags_class = G_FLAGS_CLASS (g_type_class_ref (MM_TYPE_FLOW_CONTROL));
- /* CEREG #2 */
- if (solicited)
- regex = g_regex_new (CEREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- else
- regex = g_regex_new ("\\r\\n" CEREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- g_assert (regex);
- g_ptr_array_add (array, regex);
+ for (i = 0; flags_class->values[i].value_nick; i++) {
+ if (!g_ascii_strcasecmp (str, flags_class->values[i].value_nick)) {
+ value = flags_class->values[i].value;
+ g_type_class_unref (flags_class);
+ return value;
+ }
+ }
+
+ g_type_class_unref (flags_class);
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Couldn't match '%s' with a valid MMFlowControl value",
+ str);
+ return MM_FLOW_CONTROL_UNKNOWN;
+}
+
+/*************************************************************************/
+
+static const gchar *creg_regex[] = {
+ /* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
+ [0] = "\\+(CREG|CGREG|CEREG|C5GREG):\\s*0*([0-9])",
+ /* +CREG: <n>,<stat> (GSM 07.07 CREG=1 solicited) */
+ [1] = "\\+(CREG|CGREG|CEREG|C5GREG):\\s*0*([0-9]),\\s*0*([0-9])",
+ /* +CREG: <stat>,<lac>,<ci> (GSM 07.07 CREG=2 unsolicited) */
+ [2] = "\\+(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) */
+ [3] = "\\+(CREG|CGREG|CEREG):\\s*([0-9]),\\s*([0-9])\\s*,\\s*([^,]*)\\s*,\\s*([^,\\s]*)",
+ [4] = "\\+(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) */
+ [5] = "\\+(CREG|CGREG|CEREG):\\s*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9])",
+ [6] = "\\+(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) */
+ [7] = "\\+(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>' */
+ [8] = "\\+(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) */
+ [9] = "\\+(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) */
+ [10] = "\\+(CEREG):\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])",
+ /* +CEREG: <n>,<stat>,<lac>,<rac>,<ci>,<AcT> (ETSI 27.007 v8.6 CREG=2 solicited with RAC) */
+ [11] = "\\+(CEREG):\\s*0*([0-9]),\\s*0*([0-9])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*0*([0-9])",
+ /* +C5GREG: <stat>,<lac>,<ci>,<AcT>,<Allowed_NSSAI_length>,<Allowed_NSSAI> (ETSI 27.007 CREG=2 unsolicited) */
+ [12] = "\\+(C5GREG):\\s*([0-9]+)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9]+)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)",
+ /* +C5GREG: <n>,<stat>,<lac>,<ci>,<AcT>,<Allowed_NSSAI_length>,<Allowed_NSSAI> (ETSI 27.007 solicited) */
+ [13] = "\\+(C5GREG):\\s*([0-9]+)\\s*,\\s*([0-9+])\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*([0-9]+)\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)",
+};
+
+GPtrArray *
+mm_3gpp_creg_regex_get (gboolean solicited)
+{
+ GPtrArray *array;
+ guint i;
+ array = g_ptr_array_sized_new (G_N_ELEMENTS (creg_regex));
+ for (i = 0; i < G_N_ELEMENTS (creg_regex); i++) {
+ GRegex *regex;
+ g_autofree gchar *pattern = NULL;
+
+ if (solicited) {
+ pattern = g_strdup_printf ("%s$", creg_regex[i]);
+ regex = g_regex_new (pattern, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ } else {
+ pattern = g_strdup_printf ("\\r\\n%s\\r\\n", creg_regex[i]);
+ regex = g_regex_new (pattern, G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ }
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+ }
return array;
}
@@ -482,6 +890,17 @@ mm_3gpp_ciev_regex_get (void)
/*************************************************************************/
GRegex *
+mm_3gpp_cgev_regex_get (void)
+{
+ return g_regex_new ("\\r\\n\\+CGEV:\\s*(.*)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE,
+ 0,
+ NULL);
+}
+
+/*************************************************************************/
+
+GRegex *
mm_3gpp_cusd_regex_get (void)
{
return g_regex_new ("\\r\\n\\+CUSD:\\s*(.*)\\r\\n",
@@ -514,6 +933,189 @@ mm_3gpp_cds_regex_get (void)
}
/*************************************************************************/
+/* AT+WS46=? response parser
+ *
+ * More than one numeric ID may appear in the list, that's why
+ * they are checked separately.
+ *
+ * NOTE: ignore WS46 prefix or it will break Cinterion handling.
+ *
+ * For the specific case of '25', we will check if any other mode supports
+ * 4G, and if there is none, we'll remove 4G caps from it. This is needed
+ * because pre-LTE modems used '25' to report GERAN+URAN instead of the
+ * new '29' value since LTE modems are around.
+ */
+
+typedef struct {
+ guint ws46;
+ MMModemMode mode;
+} Ws46Mode;
+
+/* 3GPP TS 27.007 v16.3.0, section 5.9: select wireless network +WS46 */
+static const Ws46Mode ws46_modes[] = {
+ /* GSM Digital Cellular Systems (GERAN only) */
+ { 12, MM_MODEM_MODE_2G },
+ /* UTRAN only */
+ { 22, MM_MODEM_MODE_3G },
+ /* 3GPP Systems (GERAN, UTRAN and E-UTRAN) */
+ { 25, MM_MODEM_MODE_ANY },
+ /* E-UTRAN only */
+ { 28, MM_MODEM_MODE_4G },
+ /* GERAN and UTRAN */
+ { 29, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G },
+ /* GERAN and E-UTRAN */
+ { 30, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G },
+ /* UTRAN and E-UTRAN */
+ { 31, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G },
+ /* GERAN, UTRAN, E-UTRAN and NG-RAN */
+ { 35, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G },
+ /* NG-RAN only */
+ { 36, MM_MODEM_MODE_5G },
+ /* E-UTRAN and NG-RAN */
+ { 37, MM_MODEM_MODE_4G | MM_MODEM_MODE_5G },
+ /* UTRAN, E-UTRAN and NG-RAN */
+ { 38, MM_MODEM_MODE_3G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G },
+ /* GERAN, E-UTRAN and NG-RAN */
+ { 39, MM_MODEM_MODE_2G | MM_MODEM_MODE_4G | MM_MODEM_MODE_5G },
+ /* UTRAN and NG-RAN */
+ { 40, MM_MODEM_MODE_3G | MM_MODEM_MODE_5G },
+ /* GERAN, UTRAN and NG-RAN */
+ { 41, MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_5G },
+ /* GERAN and NG-RAN */
+ { 42, MM_MODEM_MODE_2G | MM_MODEM_MODE_5G },
+};
+
+GArray *
+mm_3gpp_parse_ws46_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error)
+{
+ GArray *modes = NULL;
+ GArray *tech_values = NULL;
+ GRegex *r;
+ GError *inner_error = NULL;
+ GMatchInfo *match_info = NULL;
+ gchar *full_list = NULL;
+ guint val;
+ guint i;
+ guint j;
+ gboolean supported_5g = FALSE;
+ gboolean supported_4g = FALSE;
+ gboolean supported_3g = FALSE;
+ gboolean supported_2g = FALSE;
+ gboolean supported_mode_25 = FALSE;
+ gboolean supported_mode_29 = FALSE;
+
+ r = g_regex_new ("(?:\\+WS46:)?\\s*\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+ goto out;
+ }
+
+ if (!(full_list = mm_get_string_unquoted_from_match_info (match_info, 1))) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing full list string");
+ goto out;
+ }
+
+ if (!(tech_values = mm_parse_uint_list (full_list, &inner_error)))
+ goto out;
+
+ modes = g_array_new (FALSE, FALSE, sizeof (MMModemMode));
+
+ for (i = 0; i < tech_values->len; i++) {
+ val = g_array_index (tech_values, guint, i);
+
+ for (j = 0; j < G_N_ELEMENTS (ws46_modes); j++) {
+ if (ws46_modes[j].ws46 == val) {
+ if (val == 25)
+ supported_mode_25 = TRUE;
+ else {
+ if (val == 29)
+ supported_mode_29 = TRUE;
+ if (ws46_modes[j].mode & MM_MODEM_MODE_5G)
+ supported_5g = TRUE;
+ if (ws46_modes[j].mode & MM_MODEM_MODE_4G)
+ supported_4g = TRUE;
+ if (ws46_modes[j].mode & MM_MODEM_MODE_3G)
+ supported_3g = TRUE;
+ if (ws46_modes[j].mode & MM_MODEM_MODE_2G)
+ supported_2g = TRUE;
+ g_array_append_val (modes, ws46_modes[j].mode);
+ }
+ break;
+ }
+ }
+
+ if (j == G_N_ELEMENTS (ws46_modes))
+ mm_obj_warn (log_object, "Unknown +WS46 mode reported: %u", val);
+ }
+
+ if (supported_mode_25) {
+ MMModemMode mode_25;
+
+ mode_25 = MM_MODEM_MODE_2G | MM_MODEM_MODE_3G;
+ if (supported_4g) {
+ mode_25 |= MM_MODEM_MODE_4G;
+ g_array_append_val (modes, mode_25);
+ } else if (!supported_mode_29)
+ g_array_append_val (modes, mode_25);
+ }
+
+ if (modes->len == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No valid modes reported");
+ g_clear_pointer (&modes, g_array_unref);
+ goto out;
+ }
+
+ /* Fixup the ANY value, based on which are the supported modes */
+ for (i = 0; i < modes->len; i++) {
+ MMModemMode *mode;
+
+ mode = &g_array_index (modes, MMModemMode, i);
+ if (*mode == MM_MODEM_MODE_ANY) {
+ *mode = 0;
+ if (supported_2g)
+ *mode |= MM_MODEM_MODE_2G;
+ if (supported_3g)
+ *mode |= MM_MODEM_MODE_3G;
+ if (supported_4g)
+ *mode |= MM_MODEM_MODE_4G;
+ if (supported_5g)
+ *mode |= MM_MODEM_MODE_5G;
+
+ if (*mode == 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "No way to fixup the ANY value");
+ g_clear_pointer (&modes, g_array_unref);
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (tech_values)
+ g_array_unref (tech_values);
+
+ g_free (full_list);
+
+ g_clear_pointer (&match_info, g_match_info_free);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return NULL;
+ }
+
+ g_assert (modes && modes->len);
+ return modes;
+}
+
+/*************************************************************************/
static void
mm_3gpp_network_info_free (MM3gppNetworkInfo *info)
@@ -535,7 +1137,8 @@ get_mm_access_tech_from_etsi_access_tech (guint act)
{
/* See ETSI TS 27.007 */
switch (act) {
- case 0:
+ case 0: /* GSM */
+ case 8: /* EC-GSM-IoT (A/Gb mode) */
return MM_MODEM_ACCESS_TECHNOLOGY_GSM;
case 1:
return MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
@@ -549,22 +1152,30 @@ get_mm_access_tech_from_etsi_access_tech (guint act)
return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
case 6:
return MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
- case 7:
+ case 7: /* E-UTRAN */
+ case 9: /* E-UTRAN (NB-S1) */
+ case 10: /* E-UTRA connected to a 5GCN */
return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ case 11: /* NR connected to a 5G CN */
+ case 12: /* NG-RAN */
+ return MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+ case 13: /* E-UTRA-NR dual connectivity */
+ return (MM_MODEM_ACCESS_TECHNOLOGY_5GNR | MM_MODEM_ACCESS_TECHNOLOGY_LTE);
default:
return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
}
static MMModem3gppNetworkAvailability
-parse_network_status (const gchar *str)
+parse_network_status (const gchar *str,
+ gpointer log_object)
{
/* Expecting a value between '0' and '3' inclusive */
if (!str ||
strlen (str) != 1 ||
str[0] < '0' ||
str[0] > '3') {
- mm_warn ("Cannot parse network status: '%s'", str);
+ mm_obj_warn (log_object, "cannot parse network status value '%s'", str);
return MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN;
}
@@ -572,14 +1183,15 @@ parse_network_status (const gchar *str)
}
static MMModemAccessTechnology
-parse_access_tech (const gchar *str)
+parse_access_tech (const gchar *str,
+ gpointer log_object)
{
/* Recognized access technologies are between '0' and '7' inclusive... */
if (!str ||
strlen (str) != 1 ||
str[0] < '0' ||
str[0] > '7') {
- mm_warn ("Cannot parse access tech: '%s'", str);
+ mm_obj_warn (log_object, "cannot parse access technology value '%s'", str);
return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
}
@@ -587,14 +1199,15 @@ parse_access_tech (const gchar *str)
}
GList *
-mm_3gpp_parse_cops_test_response (const gchar *reply,
- GError **error)
+mm_3gpp_parse_cops_test_response (const gchar *reply,
+ MMModemCharset cur_charset,
+ gpointer log_object,
+ GError **error)
{
GRegex *r;
GList *info_list = NULL;
GMatchInfo *match_info;
gboolean umts_format = TRUE;
- GError *inner_error = NULL;
g_return_val_if_fail (reply != NULL, NULL);
if (error)
@@ -623,15 +1236,8 @@ mm_3gpp_parse_cops_test_response (const gchar *reply,
* +COPS: (2,"","T-Mobile","31026",0),(1,"AT&T","AT&T","310410"),0)
*/
- r = g_regex_new ("\\((\\d),\"([^\"\\)]*)\",([^,\\)]*),([^,\\)]*)[\\)]?,(\\d)\\)", G_REGEX_UNGREEDY, 0, &inner_error);
- if (inner_error) {
- mm_err ("Invalid regular expression: %s", inner_error->message);
- g_error_free (inner_error);
- g_set_error_literal (error,
- MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Could not parse scan results");
- return NULL;
- }
+ r = g_regex_new ("\\((\\d),\"([^\"\\)]*)\",([^,\\)]*),([^,\\)]*)[\\)]?,(\\d)\\)", G_REGEX_UNGREEDY, 0, NULL);
+ g_assert (r);
/* If we didn't get any hits, try the pre-UMTS format match */
if (!g_regex_match (r, reply, 0, &match_info)) {
@@ -652,15 +1258,8 @@ mm_3gpp_parse_cops_test_response (const gchar *reply,
* +COPS: (2,"T - Mobile",,"31026"),(1,"Einstein PCS",,"31064"),(1,"Cingular",,"31041"),,(0,1,3),(0,2)
*/
- r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^\\)]*)\\)", G_REGEX_UNGREEDY, 0, &inner_error);
- if (inner_error) {
- mm_err ("Invalid regular expression: %s", inner_error->message);
- g_error_free (inner_error);
- g_set_error_literal (error,
- MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Could not parse scan results");
- return NULL;
- }
+ r = g_regex_new ("\\((\\d),([^,\\)]*),([^,\\)]*),([^\\)]*)\\)", G_REGEX_UNGREEDY, 0, NULL);
+ g_assert (r);
g_regex_match (r, reply, 0, &match_info);
umts_format = FALSE;
@@ -675,20 +1274,25 @@ mm_3gpp_parse_cops_test_response (const gchar *reply,
info = g_new0 (MM3gppNetworkInfo, 1);
tmp = mm_get_string_unquoted_from_match_info (match_info, 1);
- info->status = parse_network_status (tmp);
+ info->status = parse_network_status (tmp, log_object);
g_free (tmp);
info->operator_long = mm_get_string_unquoted_from_match_info (match_info, 2);
info->operator_short = mm_get_string_unquoted_from_match_info (match_info, 3);
info->operator_code = mm_get_string_unquoted_from_match_info (match_info, 4);
+ /* The returned strings may be given in e.g. UCS2 */
+ mm_3gpp_normalize_operator (&info->operator_long, cur_charset, log_object);
+ mm_3gpp_normalize_operator (&info->operator_short, cur_charset, log_object);
+ mm_3gpp_normalize_operator (&info->operator_code, cur_charset, log_object);
+
/* Only try for access technology with UMTS-format matches.
* If none give, assume GSM */
tmp = (umts_format ?
mm_get_string_unquoted_from_match_info (match_info, 5) :
NULL);
info->access_tech = (tmp ?
- parse_access_tech (tmp) :
+ parse_access_tech (tmp, log_object) :
MM_MODEM_ACCESS_TECHNOLOGY_GSM);
g_free (tmp);
@@ -710,17 +1314,15 @@ mm_3gpp_parse_cops_test_response (const gchar *reply,
}
if (valid) {
- gchar *access_tech_str;
+ g_autofree gchar *access_tech_str = NULL;
access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech);
- mm_dbg ("Found network '%s' ('%s','%s'); availability: %s, access tech: %s",
- info->operator_code,
- info->operator_short ? info->operator_short : "no short name",
- info->operator_long ? info->operator_long : "no long name",
- mm_modem_3gpp_network_availability_get_string (info->status),
- access_tech_str);
- g_free (access_tech_str);
-
+ mm_obj_dbg (log_object, "found network '%s' ('%s','%s'); availability: %s, access tech: %s",
+ info->operator_code,
+ info->operator_short ? info->operator_short : "no short name",
+ info->operator_long ? info->operator_long : "no long name",
+ mm_modem_3gpp_network_availability_get_string (info->status),
+ access_tech_str);
info_list = g_list_prepend (info_list, info);
}
else
@@ -737,6 +1339,358 @@ mm_3gpp_parse_cops_test_response (const gchar *reply,
/*************************************************************************/
+gboolean
+mm_3gpp_parse_cops_read_response (const gchar *response,
+ guint *out_mode,
+ guint *out_format,
+ gchar **out_operator,
+ MMModemAccessTechnology *out_act,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint mode = 0;
+ guint format = 0;
+ gchar *operator = NULL;
+ guint actval = 0;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ g_assert (out_mode || out_format || out_operator || out_act);
+
+ /* We assume the response to be either:
+ * +COPS: <mode>,<format>,<oper>
+ * or:
+ * +COPS: <mode>,<format>,<oper>,<AcT>
+ */
+ r = g_regex_new ("\\+COPS:\\s*(\\d+),(\\d+),([^,]*)(?:,(\\d+))?(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+ goto out;
+ }
+
+ if (out_mode && !mm_get_uint_from_match_info (match_info, 1, &mode)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing mode");
+ goto out;
+ }
+
+ if (out_format && !mm_get_uint_from_match_info (match_info, 2, &format)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing format");
+ goto out;
+ }
+
+ if (out_operator && !(operator = mm_get_string_unquoted_from_match_info (match_info, 3))) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing operator");
+ goto out;
+ }
+
+ /* AcT is optional */
+ if (out_act && g_match_info_get_match_count (match_info) >= 5) {
+ if (!mm_get_uint_from_match_info (match_info, 4, &actval)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing AcT");
+ goto out;
+ }
+ act = get_mm_access_tech_from_etsi_access_tech (actval);
+ }
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_free (operator);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_mode)
+ *out_mode = mode;
+ if (out_format)
+ *out_format = format;
+ if (out_operator)
+ *out_operator = operator;
+ if (out_act)
+ *out_act = act;
+ return TRUE;
+}
+
+/*************************************************************************/
+/* Logic to compare two APN names */
+
+gboolean
+mm_3gpp_cmp_apn_name (const gchar *requested,
+ const gchar *existing)
+{
+ size_t requested_len;
+ size_t existing_len;
+
+ /* If both empty, that's a good match */
+ if ((!existing || !existing[0]) && (!requested || !requested[0]))
+ return TRUE;
+
+ /* Both must be given to compare properly */
+ if (!existing || !existing[0] || !requested || !requested[0])
+ return FALSE;
+
+ requested_len = strlen (requested);
+
+ /*
+ * 1) The requested APN should be at least the prefix of the existing one.
+ */
+ if (g_ascii_strncasecmp (existing, requested, requested_len) != 0)
+ return FALSE;
+
+ /*
+ * 2) If the existing one is actually the same as the requested one (i.e.
+ * there are no more different chars in the existing one), we're done.
+ */
+ if (existing[requested_len] == '\0')
+ return TRUE;
+
+ existing_len = strlen (existing);
+
+ /* 3) Special handling for PDP contexts reported by u-blox modems once the
+ * contexts have been activated at least once:
+ * "ac.vodafone.es.MNC001.MCC214.GPRS" should match "ac.vodafone.es"
+ */
+ if ((existing_len > (requested_len + 14)) &&
+ g_ascii_strncasecmp (&existing[requested_len], ".mnc", 4) == 0 &&
+ g_ascii_strncasecmp (&existing[requested_len + 7], ".mcc", 4) == 0)
+ return TRUE;
+
+ /* No match */
+ return FALSE;
+}
+
+/*************************************************************************/
+
+gboolean
+mm_3gpp_pdp_context_format_list_find_range (GList *pdp_format_list,
+ MMBearerIpFamily ip_family,
+ guint *out_min_cid,
+ guint *out_max_cid)
+{
+ GList *l;
+
+ for (l = pdp_format_list; l; l = g_list_next (l)) {
+ MM3gppPdpContextFormat *format = l->data;
+
+ /* Found exact PDP type? */
+ if (format->pdp_type == ip_family) {
+ if (out_min_cid)
+ *out_min_cid = format->min_cid;
+ if (out_max_cid)
+ *out_max_cid = format->max_cid;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*************************************************************************/
+
+MM3gppProfile *
+mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context)
+{
+ MM3gppProfile *profile;
+
+ profile = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_profile_id (profile, pdp_context->cid);
+ mm_3gpp_profile_set_apn (profile, pdp_context->apn);
+ mm_3gpp_profile_set_ip_type (profile, pdp_context->pdp_type);
+ return profile;
+}
+
+GList *
+mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list)
+{
+ GList *profile_list = NULL;
+ GList *l;
+
+ for (l = pdp_context_list; l; l = g_list_next (l)) {
+ MM3gppPdpContext *pdp_context;
+ MM3gppProfile *profile;
+
+ pdp_context = (MM3gppPdpContext *)l->data;
+ profile = mm_3gpp_profile_new_from_pdp_context (pdp_context);
+ profile_list = g_list_append (profile_list, profile);
+ }
+ return profile_list;
+}
+
+void
+mm_3gpp_profile_list_free (GList *profile_list)
+{
+ g_list_free_full (profile_list, g_object_unref);
+}
+
+MM3gppProfile *
+mm_3gpp_profile_list_find_by_profile_id (GList *profile_list,
+ gint profile_id,
+ GError **error)
+{
+ GList *l;
+
+ for (l = profile_list; l; l = g_list_next (l)) {
+ MM3gppProfile *iter_profile = l->data;
+
+ if (mm_3gpp_profile_get_profile_id (iter_profile) == profile_id)
+ return g_object_ref (iter_profile);
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "Profile '%d' not found", profile_id);
+ return NULL;
+}
+
+gint
+mm_3gpp_profile_list_find_empty (GList *profile_list,
+ gint min_profile_id,
+ gint max_profile_id,
+ GError **error)
+{
+ GList *l;
+ gint profile_id;
+
+ profile_id = min_profile_id;
+ for (l = profile_list; l; l = g_list_next (l)) {
+ MM3gppProfile *iter_profile = l->data;
+ gint iter_profile_id;
+
+ iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile);
+ if (iter_profile_id > profile_id)
+ break;
+ if (iter_profile_id == profile_id)
+ profile_id++;
+ }
+
+ if (profile_id > max_profile_id) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "No empty profile available");
+ return MM_3GPP_PROFILE_ID_UNKNOWN;
+ }
+
+ return profile_id;
+}
+
+gint
+mm_3gpp_profile_list_find_best (GList *profile_list,
+ MM3gppProfile *requested,
+ GEqualFunc cmp_apn,
+ MM3gppProfileCmpFlags cmp_flags,
+ gint min_profile_id,
+ gint max_profile_id,
+ gpointer log_object,
+ MM3gppProfile **out_reused,
+ gboolean *out_overwritten)
+{
+ GList *l;
+ MMBearerIpFamily requested_ip_type;
+ gint prev_profile_id = 0;
+ gint unused_profile_id = 0;
+ gint max_found_profile_id = 0;
+ gint max_allowed_profile_id = 0;
+ gint blank_profile_id = 0;
+
+ g_assert (out_reused);
+ g_assert (out_overwritten);
+
+ requested_ip_type = mm_3gpp_profile_get_ip_type (requested);
+
+ /* When looking for exact profile matches we should not compare
+ * the profile id, as the requested profile won't have one set */
+ cmp_flags |= MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID;
+
+ /* Look for the exact PDP context we want */
+ for (l = profile_list; l; l = g_list_next (l)) {
+ MM3gppProfile *iter_profile = l->data;
+ MMBearerIpFamily iter_ip_type;
+ const gchar *iter_apn;
+ gint iter_profile_id;
+
+ iter_profile_id = mm_3gpp_profile_get_profile_id (iter_profile);
+
+ /* Always prefer an exact match; compare all supported fields except for profile id */
+ if (mm_3gpp_profile_cmp (iter_profile, requested, cmp_apn, cmp_flags)) {
+ mm_obj_dbg (log_object, "found exact context at profile %d", iter_profile_id);
+ *out_reused = g_object_ref (iter_profile);
+ *out_overwritten = FALSE;
+ return iter_profile_id;
+ }
+
+ /* Same PDP type but with no APN set? we may use that one if no exact match found */
+ iter_ip_type = mm_3gpp_profile_get_ip_type (iter_profile);
+ iter_apn = mm_3gpp_profile_get_apn (iter_profile);
+ if ((iter_ip_type == requested_ip_type) && (!iter_apn || !iter_apn[0]) && !blank_profile_id)
+ blank_profile_id = iter_profile_id;
+
+ /* If an unused CID was not found yet and the previous CID is not (CID - 1),
+ * this means that (previous CID + 1) is an unused CID that can be used.
+ * This logic will allow us using unused CIDs that are available in the gaps
+ * between already defined contexts.
+ */
+ if (!unused_profile_id && prev_profile_id && ((prev_profile_id + 1) < iter_profile_id))
+ unused_profile_id = prev_profile_id + 1;
+
+ /* Update previous CID value to the current CID for use in the next loop,
+ * unless an unused CID was already found.
+ */
+ if (!unused_profile_id)
+ prev_profile_id = iter_profile_id;
+
+ /* Update max CID if we found a bigger one */
+ if (max_found_profile_id < iter_profile_id)
+ max_found_profile_id = iter_profile_id;
+ }
+
+ /* Try to use an unused CID detected in between the already defined contexts */
+ if (unused_profile_id) {
+ mm_obj_dbg (log_object, "found unused profile %d", unused_profile_id);
+ *out_reused = NULL;
+ *out_overwritten = FALSE;
+ return unused_profile_id;
+ }
+
+ /* If the max existing CID found during CGDCONT? is below the max allowed
+ * CID, then we can use the next available CID because it's an unused one. */
+ max_allowed_profile_id = max_profile_id;
+ if (max_found_profile_id && (max_found_profile_id < max_allowed_profile_id)) {
+ mm_obj_dbg (log_object, "found unused profile %d (<%d)", max_found_profile_id + 1, max_allowed_profile_id);
+ *out_reused = NULL;
+ *out_overwritten = FALSE;
+ return (max_found_profile_id + 1);
+ }
+
+ /* Rewrite a context defined with no APN, if any */
+ if (blank_profile_id) {
+ mm_obj_dbg (log_object, "rewriting profile %d with empty APN", blank_profile_id);
+ *out_reused = NULL;
+ *out_overwritten = TRUE;
+ return blank_profile_id;
+ }
+
+ /* Rewrite the last existing one found */
+ if (max_found_profile_id) {
+ mm_obj_dbg (log_object, "rewriting last profile %d detected", max_found_profile_id);
+ *out_reused = NULL;
+ *out_overwritten = TRUE;
+ return max_found_profile_id;
+ }
+
+ /* Otherwise, just fallback to min CID */
+ mm_obj_dbg (log_object, "falling back to profile %d", min_profile_id);
+ *out_reused = NULL;
+ *out_overwritten = TRUE;
+ return min_profile_id;
+}
+
+/*************************************************************************/
static void
mm_3gpp_pdp_context_format_free (MM3gppPdpContextFormat *format)
@@ -751,8 +1705,9 @@ mm_3gpp_pdp_context_format_list_free (GList *pdp_format_list)
}
GList *
-mm_3gpp_parse_cgdcont_test_response (const gchar *response,
- GError **error)
+mm_3gpp_parse_cgdcont_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error)
{
GRegex *r;
GMatchInfo *match_info;
@@ -764,7 +1719,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*\\(\\s*(\\d+)\\s*-?\\s*(\\d+)?[^\\)]*\\)\\s*,\\s*\\(?\"(\\S+)\"",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, &inner_error);
g_assert (r != NULL);
@@ -780,11 +1735,11 @@ mm_3gpp_parse_cgdcont_test_response (const gchar *response,
pdp_type_str = mm_get_string_unquoted_from_match_info (match_info, 3);
pdp_type = mm_3gpp_get_ip_family_from_pdp_type (pdp_type_str);
if (pdp_type == MM_BEARER_IP_FAMILY_NONE)
- mm_dbg ("Unhandled PDP type in CGDCONT=? reply: '%s'", pdp_type_str);
+ mm_obj_dbg (log_object, "unhandled PDP type in CGDCONT=? reply: '%s'", pdp_type_str);
else {
/* Read min CID */
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);
+ mm_obj_warn (log_object, "invalid min CID in CGDCONT=? reply for PDP type '%s'", pdp_type_str);
else {
MM3gppPdpContextFormat *format;
@@ -809,7 +1764,7 @@ mm_3gpp_parse_cgdcont_test_response (const gchar *response,
g_regex_unref (r);
if (inner_error) {
- mm_warn ("Unexpected error matching +CGDCONT response: '%s'", inner_error->message);
+ mm_obj_warn (log_object, "unexpected error matching +CGDCONT response: '%s'", inner_error->message);
g_error_free (inner_error);
}
@@ -852,7 +1807,7 @@ mm_3gpp_parse_cgdcont_read_response (const gchar *reply,
return NULL;
list = NULL;
- r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,([^,\\)]*),([^,\\)]*),([^,\\)]*)",
+ r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,([^, \\)]*)\\s*,([^, \\)]*)\\s*,([^, \\)]*)",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, &inner_error);
if (r) {
@@ -865,9 +1820,7 @@ mm_3gpp_parse_cgdcont_read_response (const gchar *reply,
str = mm_get_string_unquoted_from_match_info (match_info, 2);
ip_family = mm_3gpp_get_ip_family_from_pdp_type (str);
- if (ip_family == MM_BEARER_IP_FAMILY_NONE)
- mm_dbg ("Ignoring PDP context type: '%s'", str);
- else {
+ if (ip_family != MM_BEARER_IP_FAMILY_NONE) {
MM3gppPdpContext *pdp;
pdp = g_slice_new0 (MM3gppPdpContext);
@@ -906,31 +1859,89 @@ mm_3gpp_parse_cgdcont_read_response (const gchar *reply,
/*************************************************************************/
-static gulong
-parse_uint (char *str, int base, glong nmin, glong nmax, gboolean *valid)
+static void
+mm_3gpp_pdp_context_active_free (MM3gppPdpContextActive *pdp_active)
{
- gulong ret = 0;
- gchar *endquote;
+ g_slice_free (MM3gppPdpContextActive, pdp_active);
+}
- *valid = FALSE;
- if (!str)
- return 0;
+void
+mm_3gpp_pdp_context_active_list_free (GList *pdp_active_list)
+{
+ g_list_free_full (pdp_active_list, (GDestroyNotify) mm_3gpp_pdp_context_active_free);
+}
- /* Strip quotes */
- if (str[0] == '"')
- str++;
- endquote = strchr (str, '"');
- if (endquote)
- *endquote = '\0';
+gint
+mm_3gpp_pdp_context_active_cmp (MM3gppPdpContextActive *a,
+ MM3gppPdpContextActive *b)
+{
+ return (a->cid - b->cid);
+}
+
+GList *
+mm_3gpp_parse_cgact_read_response (const gchar *reply,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ GRegex *r;
+ GMatchInfo *match_info;
+ GList *list;
+
+ if (!reply || !reply[0])
+ /* Nothing configured, all done */
+ return NULL;
+
+ list = NULL;
+ r = g_regex_new ("\\+CGACT:\\s*(\\d+),(\\d+)",
+ G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &inner_error);
+ g_assert (r);
+
+ g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, &inner_error);
+ while (!inner_error && g_match_info_matches (match_info)) {
+ MM3gppPdpContextActive *pdp_active;
+ guint cid = 0;
+ guint aux = 0;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse CID from reply: '%s'",
+ reply);
+ break;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 2, &aux) || (aux != 0 && aux != 1)) {
+ inner_error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse context status from reply: '%s'",
+ reply);
+ break;
+ }
+
+ pdp_active = g_slice_new0 (MM3gppPdpContextActive);
+ pdp_active->cid = cid;
+ pdp_active->active = (gboolean) aux;
+ list = g_list_prepend (list, pdp_active);
- if (strlen (str)) {
- ret = strtol (str, NULL, base);
- if ((nmin == nmax) || (ret >= nmin && ret <= nmax))
- *valid = TRUE;
+ g_match_info_next (match_info, &inner_error);
}
- return *valid ? (guint) ret : 0;
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ mm_3gpp_pdp_context_active_list_free (list);
+ g_propagate_error (error, inner_error);
+ g_prefix_error (error, "Couldn't properly parse list of active/inactive PDP contexts. ");
+ return NULL;
+ }
+
+ list = g_list_sort (list, (GCompareFunc) mm_3gpp_pdp_context_active_cmp);
+
+ return list;
}
+/*************************************************************************/
+
static gboolean
item_is_lac_not_stat (GMatchInfo *info, guint32 item)
{
@@ -946,32 +1957,36 @@ item_is_lac_not_stat (GMatchInfo *info, guint32 item)
}
gboolean
-mm_3gpp_parse_creg_response (GMatchInfo *info,
- MMModem3gppRegistrationState *out_reg_state,
- gulong *out_lac,
- gulong *out_ci,
- MMModemAccessTechnology *out_act,
- gboolean *out_cgreg,
- gboolean *out_cereg,
- GError **error)
+mm_3gpp_parse_creg_response (GMatchInfo *info,
+ gpointer log_object,
+ MMModem3gppRegistrationState *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ MMModemAccessTechnology *out_act,
+ gboolean *out_cgreg,
+ gboolean *out_cereg,
+ gboolean *out_c5greg,
+ GError **error)
{
- gboolean success = FALSE, foo;
gint n_matches, act = -1;
- gulong stat = 0, lac = 0, ci = 0;
+ guint stat = 0;
+ guint64 lac = 0, ci = 0;
guint istat = 0, ilac = 0, ici = 0, iact = 0;
gchar *str;
- g_return_val_if_fail (info != NULL, FALSE);
- g_return_val_if_fail (out_reg_state != NULL, FALSE);
- g_return_val_if_fail (out_lac != NULL, FALSE);
- g_return_val_if_fail (out_ci != NULL, FALSE);
- g_return_val_if_fail (out_act != NULL, FALSE);
- g_return_val_if_fail (out_cgreg != NULL, FALSE);
- g_return_val_if_fail (out_cereg != NULL, FALSE);
+ g_assert (info != NULL);
+ g_assert (out_reg_state != NULL);
+ g_assert (out_lac != NULL);
+ g_assert (out_ci != NULL);
+ g_assert (out_act != NULL);
+ g_assert (out_cgreg != NULL);
+ g_assert (out_cereg != NULL);
+ g_assert (out_c5greg != NULL);
str = g_match_info_fetch (info, 1);
*out_cgreg = (str && strstr (str, "CGREG")) ? TRUE : FALSE;
*out_cereg = (str && strstr (str, "CEREG")) ? TRUE : FALSE;
+ *out_c5greg = (str && strstr (str, "C5GREG")) ? TRUE : FALSE;
g_free (str);
/* Normally the number of matches could be used to determine what each
@@ -1016,88 +2031,85 @@ mm_3gpp_parse_creg_response (GMatchInfo *info,
/* Check if the third item is the LAC to distinguish the two cases */
if (item_is_lac_not_stat (info, 3)) {
istat = 2;
- ilac = 3;
+ ilac = 3;
} else {
istat = 3;
- ilac = 4;
+ ilac = 4;
}
- ici = 5;
+ ici = 5;
iact = 6;
} else {
/* Check if the third item is the LAC to distinguish the two cases */
if (item_is_lac_not_stat (info, 3)) {
istat = 2;
- ilac = 3;
- ici = 4;
- iact = 5;
+ ilac = 3;
+ ici = 4;
+ iact = 5;
} else {
istat = 3;
- ilac = 4;
- ici = 5;
- iact = 6;
+ ilac = 4;
+ ici = 5;
+ iact = 6;
}
}
} else if (n_matches == 8) {
/* CEREG=2 (solicited with RAC): +CEREG: <n>,<stat>,<lac>,<rac>,<ci>,<AcT>
+ * C5GREG=2 (unsolicited): +C5GREG: <stat>,<tac>,<ci>,<AcT>,<Allowed_NSSAI_length>,<Allowed_NSSAI>
*/
if (*out_cereg) {
istat = 3;
- ilac = 4;
- ici = 6;
- iact = 7;
+ ilac = 4;
+ ici = 6;
+ iact = 7;
+ } else if (*out_c5greg) {
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ iact = 5;
}
- }
+ } else if (n_matches == 9) {
+ /* C5GREG=2 (solicited): +C5GREG: <n>,<stat>,<tac>,<ci>,<AcT>,<Allowed_NSSAI_length>,<Allowed_NSSAI> */
+ istat = 3;
+ ilac = 4;
+ ici = 5;
+ iact = 6;
+ }
/* Status */
- str = g_match_info_fetch (info, istat);
- stat = parse_uint (str, 10, 0, 5, &success);
- g_free (str);
- if (!success) {
+ if (!mm_get_uint_from_match_info (info, istat, &stat)) {
g_set_error_literal (error,
MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse the registration status response");
return FALSE;
}
- /* Location Area Code */
- if (ilac) {
- /* FIXME: some phones apparently swap the LAC bytes (LG, SonyEricsson,
- * Sagem). Need to handle that.
- */
- str = g_match_info_fetch (info, ilac);
- lac = parse_uint (str, 16, 1, 0xFFFF, &foo);
- g_free (str);
+ /* 'attached RLOS' is the last valid state */
+ if (stat > MM_MODEM_3GPP_REGISTRATION_STATE_ATTACHED_RLOS) {
+ mm_obj_warn (log_object, "unknown registration state value '%u'", stat);
+ stat = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
}
+ /* Location Area Code/Tracking Area Code
+ * FIXME: some phones apparently swap the LAC bytes (LG, SonyEricsson,
+ * Sagem). Need to handle that.
+ */
+ if (ilac)
+ mm_get_u64_from_hex_match_info (info, ilac, &lac);
+
/* Cell ID */
- if (ici) {
- str = g_match_info_fetch (info, ici);
- ci = parse_uint (str, 16, 1, 0x0FFFFFFE, &foo);
- g_free (str);
- }
+ if (ici)
+ mm_get_u64_from_hex_match_info (info, ici, &ci);
/* Access Technology */
- if (iact) {
- str = g_match_info_fetch (info, iact);
- act = (gint) parse_uint (str, 10, 0, 7, &foo);
- g_free (str);
- if (!foo)
- act = -1;
- }
-
- /* 'roaming' is the last valid state */
- if (stat > MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) {
- mm_warn ("Registration State '%lu' is unknown", stat);
- stat = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
- }
+ if (iact)
+ mm_get_int_from_match_info (info, iact, &act);
*out_reg_state = (MMModem3gppRegistrationState) stat;
if (stat != MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN) {
/* Don't fill in lac/ci/act if the device's state is unknown */
- *out_lac = lac;
- *out_ci = ci;
-
- *out_act = get_mm_access_tech_from_etsi_access_tech (act);
+ *out_lac = (gulong)lac;
+ *out_ci = (gulong)ci;
+ *out_act = (act >= 0 ? get_mm_access_tech_from_etsi_access_tech (act) : MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
}
return TRUE;
}
@@ -1127,7 +2139,7 @@ mm_3gpp_parse_cmgf_test_response (const gchar *reply,
if (!r)
return FALSE;
- if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
+ if (!g_regex_match (r, reply, 0, &match_info)) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -1161,6 +2173,794 @@ mm_3gpp_parse_cmgf_test_response (const gchar *reply,
/*************************************************************************/
+MM3gppPduInfo *
+mm_3gpp_parse_cmgr_read_response (const gchar *reply,
+ guint index,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ gint count;
+ gint status;
+ gchar *pdu;
+ MM3gppPduInfo *info = NULL;
+
+ /* +CMGR: <stat>,<alpha>,<length>(whitespace)<pdu> */
+ /* The <alpha> and <length> fields are matched, but not currently used */
+ r = g_regex_new ("\\+CMGR:\\s*(\\d+)\\s*,([^,]*),\\s*(\\d+)\\s*([^\\r\\n]*)", 0, 0, NULL);
+ g_assert (r);
+
+ if (!g_regex_match (r, reply, 0, &match_info)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse CMGR read result: response didn't match '%s'",
+ reply);
+ goto done;
+ }
+
+ /* g_match_info_get_match_count includes match #0 */
+ if ((count = g_match_info_get_match_count (match_info)) != 5) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to match CMGR fields (matched %d) '%s'",
+ count,
+ reply);
+ goto done;
+ }
+
+ if (!mm_get_int_from_match_info (match_info, 1, &status)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to extract CMGR status field '%s'",
+ reply);
+ goto done;
+ }
+
+
+ pdu = mm_get_string_unquoted_from_match_info (match_info, 4);
+ if (!pdu) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to extract CMGR pdu field '%s'",
+ reply);
+ goto done;
+ }
+
+ info = g_new0 (MM3gppPduInfo, 1);
+ info->index = index;
+ info->status = status;
+ info->pdu = pdu;
+
+done:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ return info;
+}
+
+/*****************************************************************************/
+/* AT+CRSM response parser */
+
+gboolean
+mm_3gpp_parse_crsm_response (const gchar *reply,
+ guint *sw1,
+ guint *sw2,
+ gchar **hex,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+
+ g_assert (sw1 != NULL);
+ g_assert (sw2 != NULL);
+ g_assert (hex != NULL);
+
+ *sw1 = 0;
+ *sw2 = 0;
+ *hex = NULL;
+
+ if (!reply || !g_str_has_prefix (reply, "+CRSM:")) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing +CRSM prefix");
+ return FALSE;
+ }
+
+ r = g_regex_new ("\\+CRSM:\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*\"?([0-9a-fA-F]+)\"?",
+ G_REGEX_RAW, 0, NULL);
+ g_assert (r != NULL);
+
+ if (g_regex_match (r, reply, 0, &match_info) &&
+ mm_get_uint_from_match_info (match_info, 1, sw1) &&
+ mm_get_uint_from_match_info (match_info, 2, sw2))
+ *hex = mm_get_string_unquoted_from_match_info (match_info, 3);
+
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (*hex == NULL) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse CRSM query result '%s'",
+ reply);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*************************************************************************/
+/* CGCONTRDP=N response parser */
+
+static gboolean
+split_local_address_and_subnet (const gchar *str,
+ gchar **local_address,
+ gchar **subnet)
+{
+ const gchar *separator;
+ guint count = 0;
+
+ /* E.g. split: "2.43.2.44.255.255.255.255"
+ * into:
+ * local address: "2.43.2.44",
+ * subnet: "255.255.255.255"
+ */
+ g_assert (str);
+ g_assert (local_address);
+ g_assert (subnet);
+
+ separator = str;
+ while (1) {
+ separator = strchr (separator, '.');
+ if (separator) {
+ count++;
+ if (count == 4) {
+ if (local_address)
+ *local_address = g_strndup (str, (separator - str));
+ if (subnet)
+ *subnet = g_strdup (++separator);
+ return TRUE;
+ }
+ separator++;
+ continue;
+ }
+
+ /* Not even the full IP? report error parsing */
+ if (count < 3)
+ return FALSE;
+
+ if (count == 3) {
+ if (local_address)
+ *local_address = g_strdup (str);
+ if (subnet)
+ *subnet = NULL;
+ return TRUE;
+ }
+ }
+}
+
+gboolean
+mm_3gpp_parse_cgcontrdp_response (const gchar *response,
+ guint *out_cid,
+ guint *out_bearer_id,
+ gchar **out_apn,
+ gchar **out_local_address,
+ gchar **out_subnet,
+ gchar **out_gateway_address,
+ gchar **out_dns_primary_address,
+ gchar **out_dns_secondary_address,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint cid = 0;
+ guint bearer_id = 0;
+ gchar *apn = NULL;
+ gchar *local_address_and_subnet = NULL;
+ gchar *local_address = NULL;
+ gchar *subnet = NULL;
+ gchar *gateway_address = NULL;
+ gchar *dns_primary_address = NULL;
+ gchar *dns_secondary_address = NULL;
+ guint field_format_extra_index = 0;
+
+ /* Response may be e.g.:
+ * +CGCONTRDP: 4,5,"ibox.tim.it.mnc001.mcc222.gprs","2.197.17.49.255.255.255.255","2.197.17.49","10.207.43.46","10.206.56.132","0.0.0.0","0.0.0.0",0
+ *
+ * We assume only ONE line is returned; because we request +CGCONTRDP with
+ * a specific N CID. Also, we don't parse all fields, we stop after
+ * secondary DNS.
+ *
+ * Only the 3 first parameters (cid, bearer id, apn) are mandatory in the
+ * response, all the others are optional, but, we'll anyway assume that APN
+ * may be empty.
+ *
+ * The format of the response changed in TS 27.007 v9.4.0, we try to detect
+ * both formats ('a' if >= v9.4.0, 'b' if < v9.4.0) with a single regex here.
+ */
+ r = g_regex_new ("\\+CGCONTRDP: "
+ "(\\d+),(\\d+),([^,]*)" /* cid, bearer id, apn */
+ "(?:,([^,]*))?" /* (a)ip+mask or (b)ip */
+ "(?:,([^,]*))?" /* (a)gateway or (b)mask */
+ "(?:,([^,]*))?" /* (a)dns1 or (b)gateway */
+ "(?:,([^,]*))?" /* (a)dns2 or (b)dns1 */
+ "(?:,([^,]*))?" /* (a)p-cscf primary or (b)dns2 */
+ "(?:,(.*))?" /* others, ignored */
+ "(?:\\r\\n)?",
+ 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Couldn't match +CGCONTRDP response");
+ goto out;
+ }
+
+ if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing cid");
+ goto out;
+ }
+
+ if (out_bearer_id && !mm_get_uint_from_match_info (match_info, 2, &bearer_id)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing bearer id");
+ goto out;
+ }
+
+ /* Remaining strings are optional or empty allowed */
+
+ if (out_apn)
+ apn = mm_get_string_unquoted_from_match_info (match_info, 3);
+
+ /*
+ * The +CGCONTRDP=[cid] response format before version TS 27.007 v9.4.0 had
+ * the subnet in its own comma-separated field. Try to detect that.
+ */
+ local_address_and_subnet = mm_get_string_unquoted_from_match_info (match_info, 4);
+ if (local_address_and_subnet && !split_local_address_and_subnet (local_address_and_subnet, &local_address, &subnet)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing local address and subnet");
+ goto out;
+ }
+ /* If we don't have a subnet in field 4, we're using the old format with subnet in an extra field */
+ if (!subnet) {
+ if (out_subnet)
+ subnet = mm_get_string_unquoted_from_match_info (match_info, 5);
+ field_format_extra_index = 1;
+ }
+
+ if (out_gateway_address)
+ gateway_address = mm_get_string_unquoted_from_match_info (match_info, 5 + field_format_extra_index);
+
+ if (out_dns_primary_address)
+ dns_primary_address = mm_get_string_unquoted_from_match_info (match_info, 6 + field_format_extra_index);
+
+ if (out_dns_secondary_address)
+ dns_secondary_address = mm_get_string_unquoted_from_match_info (match_info, 7 + field_format_extra_index);
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ g_free (local_address_and_subnet);
+
+ if (inner_error) {
+ g_free (apn);
+ g_free (local_address);
+ g_free (subnet);
+ g_free (gateway_address);
+ g_free (dns_primary_address);
+ g_free (dns_secondary_address);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_cid)
+ *out_cid = cid;
+ if (out_bearer_id)
+ *out_bearer_id = bearer_id;
+ if (out_apn)
+ *out_apn = apn;
+
+ /* Local address and subnet may always be retrieved, even if not requested
+ * by the caller, as we need them to know which +CGCONTRDP=[cid] response is
+ * being parsed. So make sure we free them if not needed. */
+ if (out_local_address)
+ *out_local_address = local_address;
+ else
+ g_free (local_address);
+ if (out_subnet)
+ *out_subnet = subnet;
+ else
+ g_free (subnet);
+
+ if (out_gateway_address)
+ *out_gateway_address = gateway_address;
+ if (out_dns_primary_address)
+ *out_dns_primary_address = dns_primary_address;
+ if (out_dns_secondary_address)
+ *out_dns_secondary_address = dns_secondary_address;
+ return TRUE;
+}
+
+/*************************************************************************/
+
+gboolean
+mm_3gpp_parse_cfun_query_response (const gchar *response,
+ guint *out_state,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint state = G_MAXUINT;
+
+ g_assert (out_state != NULL);
+
+ /* Response may be e.g.:
+ * +CFUN: 1,0
+ * ..but we don't care about the second number
+ */
+ r = g_regex_new ("\\+CFUN: (\\d+)(?:,(?:\\d+))?(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CFUN response: %s", response);
+ goto out;
+ }
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &state)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't read power state value");
+ goto out;
+ }
+
+ *out_state = state;
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+/* +CESQ response parser */
+
+gboolean
+mm_3gpp_parse_cesq_response (const gchar *response,
+ guint *out_rxlev,
+ guint *out_ber,
+ guint *out_rscp,
+ guint *out_ecn0,
+ guint *out_rsrq,
+ guint *out_rsrp,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ GError *inner_error = NULL;
+ guint rxlev = 99;
+ guint ber = 99;
+ guint rscp = 255;
+ guint ecn0 = 255;
+ guint rsrq = 255;
+ guint rsrp = 255;
+ gboolean success = FALSE;
+
+ g_assert (out_rxlev);
+ g_assert (out_ber);
+ g_assert (out_rscp);
+ g_assert (out_ecn0);
+ g_assert (out_rsrq);
+ g_assert (out_rsrp);
+
+ /* Response may be e.g.:
+ * +CESQ: 99,99,255,255,20,80
+ */
+ r = g_regex_new ("\\+CESQ: (\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)(?:\\r\\n)?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (!inner_error && g_match_info_matches (match_info)) {
+ if (!mm_get_uint_from_match_info (match_info, 1, &rxlev)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RXLEV");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 2, &ber)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read BER");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 3, &rscp)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSCP");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 4, &ecn0)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read Ec/N0");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 5, &rsrq)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRQ");
+ goto out;
+ }
+ if (!mm_get_uint_from_match_info (match_info, 6, &rsrp)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't read RSRP");
+ goto out;
+ }
+ success = TRUE;
+ }
+
+out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (!success) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CESQ response: %s", response);
+ return FALSE;
+ }
+
+ *out_rxlev = rxlev;
+ *out_ber = ber;
+ *out_rscp = rscp;
+ *out_ecn0 = ecn0;
+ *out_rsrq = rsrq;
+ *out_rsrp = rsrp;
+ return TRUE;
+}
+
+gboolean
+mm_3gpp_rxlev_to_rssi (guint rxlev,
+ gpointer log_object,
+ gdouble *out_rssi)
+{
+ if (rxlev <= 63) {
+ *out_rssi = -111.0 + rxlev;
+ return TRUE;
+ }
+
+ if (rxlev != 99)
+ mm_obj_warn (log_object, "unexpected rxlev: %u", rxlev);
+ return FALSE;
+}
+
+gboolean
+mm_3gpp_rscp_level_to_rscp (guint rscp_level,
+ gpointer log_object,
+ gdouble *out_rscp)
+{
+ if (rscp_level <= 96) {
+ *out_rscp = -121.0 + rscp_level;
+ return TRUE;
+ }
+
+ if (rscp_level != 255)
+ mm_obj_warn (log_object, "unexpected rscp level: %u", rscp_level);
+ return FALSE;
+}
+
+gboolean
+mm_3gpp_ecn0_level_to_ecio (guint ecn0_level,
+ gpointer log_object,
+ gdouble *out_ecio)
+{
+ if (ecn0_level <= 49) {
+ *out_ecio = -24.5 + (((gdouble) ecn0_level) * 0.5);
+ return TRUE;
+ }
+
+ if (ecn0_level != 255)
+ mm_obj_warn (log_object, "unexpected Ec/N0 level: %u", ecn0_level);
+ return FALSE;
+}
+
+gboolean
+mm_3gpp_rsrq_level_to_rsrq (guint rsrq_level,
+ gpointer log_object,
+ gdouble *out_rsrq)
+{
+ if (rsrq_level <= 34) {
+ *out_rsrq = -20.0 + (((gdouble) rsrq_level) * 0.5);
+ return TRUE;
+ }
+
+ if (rsrq_level != 255)
+ mm_obj_warn (log_object, "unexpected RSRQ level: %u", rsrq_level);
+ return FALSE;
+}
+
+gboolean
+mm_3gpp_rsrp_level_to_rsrp (guint rsrp_level,
+ gpointer log_object,
+ gdouble *out_rsrp)
+{
+ if (rsrp_level <= 97) {
+ *out_rsrp = -141.0 + rsrp_level;
+ return TRUE;
+ }
+
+ if (rsrp_level != 255)
+ mm_obj_warn (log_object, "unexpected RSRP level: %u", rsrp_level);
+ return FALSE;
+}
+
+gboolean
+mm_3gpp_cesq_response_to_signal_info (const gchar *response,
+ gpointer log_object,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ GError **error)
+{
+ guint rxlev = 0;
+ guint ber = 0;
+ guint rscp_level = 0;
+ guint ecn0_level = 0;
+ guint rsrq_level = 0;
+ guint rsrp_level = 0;
+ gdouble rssi = -G_MAXDOUBLE;
+ gdouble rscp = -G_MAXDOUBLE;
+ gdouble ecio = -G_MAXDOUBLE;
+ gdouble rsrq = -G_MAXDOUBLE;
+ gdouble rsrp = -G_MAXDOUBLE;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+
+ if (!mm_3gpp_parse_cesq_response (response,
+ &rxlev, &ber,
+ &rscp_level, &ecn0_level,
+ &rsrq_level, &rsrp_level,
+ error))
+ return FALSE;
+
+ /* GERAN RSSI */
+ if (mm_3gpp_rxlev_to_rssi (rxlev, log_object, &rssi)) {
+ gsm = mm_signal_new ();
+ mm_signal_set_rssi (gsm, rssi);
+ }
+
+ /* ignore BER */
+
+ /* UMTS RSCP */
+ if (mm_3gpp_rscp_level_to_rscp (rscp_level, log_object, &rscp)) {
+ umts = mm_signal_new ();
+ mm_signal_set_rscp (umts, rscp);
+ }
+
+ /* UMTS EcIo (assumed EcN0) */
+ if (mm_3gpp_ecn0_level_to_ecio (ecn0_level, log_object, &ecio)) {
+ if (!umts)
+ umts = mm_signal_new ();
+ mm_signal_set_ecio (umts, ecio);
+ }
+
+ /* LTE RSRQ */
+ if (mm_3gpp_rsrq_level_to_rsrq (rsrq_level, log_object, &rsrq)) {
+ lte = mm_signal_new ();
+ mm_signal_set_rsrq (lte, rsrq);
+ }
+
+ /* LTE RSRP */
+ if (mm_3gpp_rsrp_level_to_rsrp (rsrp_level, log_object, &rsrp)) {
+ if (!lte)
+ lte = mm_signal_new ();
+ mm_signal_set_rsrp (lte, rsrp);
+ }
+
+ if (!gsm && !umts && !lte) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't build detailed signal info");
+ return FALSE;
+ }
+
+ if (gsm)
+ *out_gsm = gsm;
+ if (umts)
+ *out_umts = umts;
+ if (lte)
+ *out_lte = lte;
+
+ return TRUE;
+}
+
+gboolean
+mm_3gpp_parse_cfun_query_generic_response (const gchar *response,
+ MMModemPowerState *out_state,
+ GError **error)
+{
+ guint state;
+
+ if (!mm_3gpp_parse_cfun_query_response (response, &state, error))
+ return FALSE;
+
+ switch (state) {
+ case 0:
+ *out_state = MM_MODEM_POWER_STATE_OFF;
+ return TRUE;
+ case 1:
+ *out_state = MM_MODEM_POWER_STATE_ON;
+ return TRUE;
+ case 4:
+ *out_state = MM_MODEM_POWER_STATE_LOW;
+ return TRUE;
+ default:
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown +CFUN state: %u", state);
+ return FALSE;
+ }
+}
+
+static MMModem3gppEpsUeModeOperation cemode_values[] = {
+ [0] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_2,
+ [1] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_1,
+ [2] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_CSPS_2,
+ [3] = MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_PS_1,
+};
+
+gchar *
+mm_3gpp_build_cemode_set_request (MMModem3gppEpsUeModeOperation mode)
+{
+ guint i;
+
+ g_return_val_if_fail (mode != MM_MODEM_3GPP_EPS_UE_MODE_OPERATION_UNKNOWN, NULL);
+
+ for (i = 0; i < G_N_ELEMENTS (cemode_values); i++) {
+ if (mode == cemode_values[i])
+ return g_strdup_printf ("+CEMODE=%u", i);
+ }
+
+ g_assert_not_reached ();
+ return NULL;
+}
+
+gboolean
+mm_3gpp_parse_cemode_query_response (const gchar *response,
+ MMModem3gppEpsUeModeOperation *out_mode,
+ GError **error)
+{
+ guint value = 0;
+
+ response = mm_strip_tag (response, "+CEMODE:");
+ if (mm_get_uint_from_str (response, &value) && value < G_N_ELEMENTS (cemode_values)) {
+ if (out_mode)
+ *out_mode = cemode_values[value];
+ return TRUE;
+ }
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse UE mode of operation: '%s' (value %u)",
+ response, value);
+ return FALSE;
+}
+
+/*************************************************************************/
+/* CCWA service query response parser */
+
+gboolean
+mm_3gpp_parse_ccwa_service_query_response (const gchar *response,
+ gpointer log_object,
+ gboolean *status,
+ GError **error)
+{
+ GRegex *r;
+ GError *inner_error = NULL;
+ GMatchInfo *match_info = NULL;
+ gint class_1_status = -1;
+
+ /*
+ * AT+CCWA=<n>[,<mode>]
+ * +CCWA: <status>,<class1>
+ * [+CCWA: <status>,<class2>
+ * [...]]
+ * OK
+ *
+ * If <classX> is 255 it applies to ALL classes.
+ *
+ * We're only interested in class 1 (voice)
+ */
+ r = g_regex_new ("\\+CCWA:\\s*(\\d+),\\s*(\\d+)$",
+ G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_NEWLINE_CRLF,
+ G_REGEX_MATCH_NEWLINE_CRLF,
+ NULL);
+ g_assert (r != NULL);
+
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ /* Parse the results */
+ while (g_match_info_matches (match_info)) {
+ guint st;
+ guint class;
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &class))
+ mm_obj_warn (log_object, "couldn't parse class from +CCWA line");
+ else if (class == 1 || class == 255) {
+ if (!mm_get_uint_from_match_info (match_info, 1, &st))
+ mm_obj_warn (log_object, "couldn't parse status from +CCWA line");
+ else {
+ class_1_status = st;
+ break;
+ }
+ }
+ g_match_info_next (match_info, NULL);
+ }
+
+out:
+ g_clear_pointer (&match_info, g_match_info_free);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (class_1_status < 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "call waiting status for voice class missing");
+ return FALSE;
+ }
+
+ if (class_1_status != 0 && class_1_status != 1) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "call waiting status for voice class invalid: %d", class_1_status);
+ return FALSE;
+ }
+
+ if (status)
+ *status = (gboolean) class_1_status;
+
+ return TRUE;
+}
+
+/*************************************************************************/
+/* CGATT helpers */
+
+gchar *
+mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state)
+{
+ guint cgatt_action;
+
+ switch (state) {
+ case MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED:
+ cgatt_action = 1;
+ break;
+ case MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED:
+ cgatt_action = 0;
+ break;
+ case MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN:
+ default:
+ return NULL;
+ }
+
+ return g_strdup_printf ("+CGATT=%u", cgatt_action);
+}
+
+/*************************************************************************/
+
static MMSmsStorage
storage_from_str (const gchar *str)
{
@@ -1180,39 +2980,51 @@ storage_from_str (const gchar *str)
}
gboolean
-mm_3gpp_parse_cpms_test_response (const gchar *reply,
- GArray **mem1,
- GArray **mem2,
- GArray **mem3)
+mm_3gpp_parse_cpms_test_response (const gchar *reply,
+ GArray **mem1,
+ GArray **mem2,
+ GArray **mem3,
+ GError **error)
{
- GRegex *r;
- gchar **split;
guint i;
- GArray *tmp1 = NULL;
- GArray *tmp2 = NULL;
- GArray *tmp3 = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GArray) tmp1 = NULL;
+ g_autoptr(GArray) tmp2 = NULL;
+ g_autoptr(GArray) tmp3 = NULL;
+ g_auto(GStrv) split = NULL;
g_assert (mem1 != NULL);
g_assert (mem2 != NULL);
g_assert (mem3 != NULL);
- /*
- * +CPMS: ("SM","ME"),("SM","ME"),("SM","ME")
- */
- split = g_strsplit_set (mm_strip_tag (reply, "+CPMS:"), "()", -1);
- if (!split)
+#define N_EXPECTED_GROUPS 3
+
+ split = mm_split_string_groups (mm_strip_tag (reply, "+CPMS:"));
+ if (!split) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't split +CPMS test response in groups");
+ return FALSE;
+ }
+
+ if (g_strv_length (split) != N_EXPECTED_GROUPS) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot parse +CPMS test response: invalid number of groups (%u != %u)",
+ g_strv_length (split), N_EXPECTED_GROUPS);
return FALSE;
+ }
r = g_regex_new ("\\s*\"([^,\\)]+)\"\\s*", 0, 0, NULL);
g_assert (r);
- for (i = 0; split[i]; i++) {
- GMatchInfo *match_info;
+ for (i = 0; i < N_EXPECTED_GROUPS; i++) {
+ GMatchInfo *match_info = NULL;
+ GArray *array;
- /* Got a range group to match */
- if (g_regex_match_full (r, split[i], strlen (split[i]), 0, 0, &match_info, NULL)) {
- GArray *array = NULL;
+ /* We always return a valid array, even if it may be empty */
+ array = g_array_new (FALSE, FALSE, sizeof (MMSmsStorage));
+ /* Got a range group to match */
+ if (g_regex_match (r, split[i], 0, &match_info)) {
while (g_match_info_matches (match_info)) {
gchar *str;
@@ -1220,9 +3032,6 @@ mm_3gpp_parse_cpms_test_response (const gchar *reply,
if (str) {
MMSmsStorage storage;
- if (!array)
- array = g_array_new (FALSE, FALSE, sizeof (MMSmsStorage));
-
storage = storage_from_str (str);
g_array_append_val (array, storage);
g_free (str);
@@ -1230,45 +3039,111 @@ mm_3gpp_parse_cpms_test_response (const gchar *reply,
g_match_info_next (match_info, NULL);
}
-
- if (!tmp1)
- tmp1 = array;
- else if (!tmp2)
- tmp2 = array;
- else if (!tmp3)
- tmp3 = array;
}
g_match_info_free (match_info);
- if (tmp3 != NULL)
- break; /* once we got the last group, exit... */
+ if (!tmp1)
+ tmp1 = array;
+ else if (!tmp2)
+ tmp2 = array;
+ else if (!tmp3)
+ tmp3 = array;
+ else
+ g_assert_not_reached ();
}
- g_strfreev (split);
- g_regex_unref (r);
-
- g_warn_if_fail (tmp1 != NULL);
- g_warn_if_fail (tmp2 != NULL);
- g_warn_if_fail (tmp3 != NULL);
-
- /* Only return TRUE if all sets have been parsed correctly */
+ /* Only return TRUE if all sets have been parsed correctly
+ * (even if the arrays may be empty) */
if (tmp1 && tmp2 && tmp3) {
- *mem1 = tmp1;
- *mem2 = tmp2;
- *mem3 = tmp3;
+ *mem1 = g_steal_pointer (&tmp1);
+ *mem2 = g_steal_pointer (&tmp2);
+ *mem3 = g_steal_pointer (&tmp3);
return TRUE;
}
- /* Otherwise, cleanup and return FALSE */
- if (tmp1)
- g_array_unref (tmp1);
- if (tmp2)
- g_array_unref (tmp2);
- if (tmp3)
- g_array_unref (tmp3);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot parse +CPMS test response: mem1 %s, mem2 %s, mem3 %s",
+ tmp1 ? "yes" : "no",
+ tmp2 ? "yes" : "no",
+ tmp3 ? "yes" : "no");
return FALSE;
}
+/**********************************************************************
+ * AT+CPMS?
+ * +CPMS: <memr>,<usedr>,<totalr>,<memw>,<usedw>,<totalw>, <mems>,<useds>,<totals>
+ */
+
+#define CPMS_QUERY_REGEX "\\+CPMS:\\s*\"(?P<memr>.*)\",[0-9]+,[0-9]+,\"(?P<memw>.*)\",[0-9]+,[0-9]+,\"(?P<mems>.*)\",[0-9]+,[0-9]"
+
+gboolean
+mm_3gpp_parse_cpms_query_response (const gchar *reply,
+ MMSmsStorage *memr,
+ MMSmsStorage *memw,
+ GError **error)
+{
+ GRegex *r = NULL;
+ gboolean ret = FALSE;
+ GMatchInfo *match_info = NULL;
+
+ r = g_regex_new (CPMS_QUERY_REGEX, G_REGEX_RAW, 0, NULL);
+
+ g_assert (r);
+
+ if (!g_regex_match (r, reply, 0, &match_info)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not parse CPMS query response '%s'", reply);
+ goto end;
+ }
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find matches in CPMS query reply '%s'", reply);
+ goto end;
+ }
+
+ if (!mm_3gpp_get_cpms_storage_match (match_info, "memr", memr, error)) {
+ goto end;
+ }
+
+ if (!mm_3gpp_get_cpms_storage_match (match_info, "memw", memw, error)) {
+ goto end;
+ }
+
+ ret = TRUE;
+
+end:
+ if (r != NULL)
+ g_regex_unref (r);
+
+ g_match_info_free (match_info);
+
+ return ret;
+}
+
+gboolean
+mm_3gpp_get_cpms_storage_match (GMatchInfo *match_info,
+ const gchar *match_name,
+ MMSmsStorage *storage,
+ GError **error)
+{
+ gboolean ret = TRUE;
+ gchar *str = NULL;
+
+ str = g_match_info_fetch_named (match_info, match_name);
+ if (str == NULL || str[0] == '\0') {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find '%s' from CPMS reply", match_name);
+ ret = FALSE;
+ } else {
+ *storage = storage_from_str (str);
+ }
+
+ g_free (str);
+
+ return ret;
+}
+
/*************************************************************************/
gboolean
@@ -1304,7 +3179,7 @@ mm_3gpp_parse_cscs_test_response (const gchar *reply,
if (!r)
return FALSE;
- if (g_regex_match_full (r, p, strlen (p), 0, 0, &match_info, NULL)) {
+ if (g_regex_match (r, p, 0, &match_info)) {
while (g_match_info_matches (match_info)) {
str = g_match_info_fetch (match_info, 1);
charsets |= mm_modem_charset_from_string (str);
@@ -1346,7 +3221,7 @@ mm_3gpp_parse_clck_test_response (const gchar *reply,
g_assert (r != NULL);
*out_facilities = MM_MODEM_3GPP_FACILITY_NONE;
- if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
+ if (g_regex_match (r, reply, 0, &match_info)) {
while (g_match_info_matches (match_info)) {
gchar *str;
@@ -1410,12 +3285,11 @@ mm_3gpp_parse_clck_write_response (const gchar *reply,
/*************************************************************************/
GStrv
-mm_3gpp_parse_cnum_exec_response (const gchar *reply,
- GError **error)
+mm_3gpp_parse_cnum_exec_response (const gchar *reply)
{
- GArray *array = NULL;
- GRegex *r;
- GMatchInfo *match_info;
+ g_autoptr(GPtrArray) array = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autoptr(GMatchInfo) match_info = NULL;
/* Empty strings also return NULL list */
if (!reply || !reply[0])
@@ -1425,26 +3299,136 @@ mm_3gpp_parse_cnum_exec_response (const gchar *reply,
G_REGEX_UNGREEDY, 0, NULL);
g_assert (r != NULL);
+ array = g_ptr_array_new ();
g_regex_match (r, reply, 0, &match_info);
while (g_match_info_matches (match_info)) {
- gchar *number;
+ g_autofree gchar *number = NULL;
number = g_match_info_fetch_named (match_info, "num");
+ if (number && number[0])
+ g_ptr_array_add (array, g_steal_pointer (&number));
+ g_match_info_next (match_info, NULL);
+ }
- if (number && number[0]) {
- if (!array)
- array = g_array_new (TRUE, TRUE, sizeof (gchar *));
- g_array_append_val (array, number);
- } else
- g_free (number);
+ if (!array->len)
+ return NULL;
- g_match_info_next (match_info, NULL);
+ g_ptr_array_add (array, NULL);
+ return (GStrv) g_ptr_array_free (g_steal_pointer (&array), FALSE);
+}
+
+/*************************************************************************/
+
+gchar *
+mm_3gpp_build_cmer_set_request (MM3gppCmerMode mode,
+ MM3gppCmerInd ind)
+{
+ guint mode_val;
+ guint ind_val;
+
+ if (mode == MM_3GPP_CMER_MODE_DISCARD_URCS)
+ return g_strdup ("+CMER=0");
+ if (mode < MM_3GPP_CMER_MODE_DISCARD_URCS || mode > MM_3GPP_CMER_MODE_FORWARD_URCS)
+ return NULL;
+ mode_val = mm_find_bit_set (mode);
+
+ if (ind < MM_3GPP_CMER_IND_DISABLE || ind > MM_3GPP_CMER_IND_ENABLE_ALL)
+ return NULL;
+ ind_val = mm_find_bit_set (ind);
+
+ return g_strdup_printf ("+CMER=%u,0,0,%u", mode_val, ind_val);
+}
+
+gboolean
+mm_3gpp_parse_cmer_test_response (const gchar *response,
+ gpointer log_object,
+ MM3gppCmerMode *out_supported_modes,
+ MM3gppCmerInd *out_supported_inds,
+ GError **error)
+{
+ gchar **split;
+ GError *inner_error = NULL;
+ GArray *array_supported_modes = NULL;
+ GArray *array_supported_inds = NULL;
+ gchar *aux = NULL;
+ gboolean ret = FALSE;
+ MM3gppCmerMode supported_modes = 0;
+ MM3gppCmerInd supported_inds = 0;
+ guint i;
+
+ /*
+ * AT+CMER=?
+ * +CMER: 1,0,0,(0-1),0
+ *
+ * AT+CMER=?
+ * +CMER: (0-3),(0),(0),(0-1),(0-1)
+ *
+ * AT+CMER=?
+ * +CMER: (1,2),0,0,(0-1),0
+ */
+
+ split = mm_split_string_groups (mm_strip_tag (response, "+CMER:"));
+ if (!split) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't split +CMER test response in groups");
+ goto out;
}
- g_match_info_free (match_info);
- g_regex_unref (r);
+ /* We want 1st and 4th groups */
+ if (g_strv_length (split) < 4) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Missing groups in +CMER test response (%u < 4)", g_strv_length (split));
+ goto out;
+ }
+
+ /* Modes in 1st group */
+ if (!(array_supported_modes = mm_parse_uint_list (split[0], &inner_error)))
+ goto out;
+ g_clear_pointer (&aux, g_free);
+
+ /* Ind settings in 4th group */
+ if (!(array_supported_inds = mm_parse_uint_list (split[3], &inner_error)))
+ goto out;
+ g_clear_pointer (&aux, g_free);
+
+ for (i = 0; i < array_supported_modes->len; i++) {
+ guint mode_val;
+
+ mode_val = g_array_index (array_supported_modes, guint, i);
+ if (mode_val <= 3)
+ supported_modes |= (MM3gppCmerMode) (1 << mode_val);
+ else
+ mm_obj_dbg (log_object, "unknown +CMER mode reported: %u", mode_val);
+ }
+
+ for (i = 0; i < array_supported_inds->len; i++) {
+ guint ind_val;
+
+ ind_val = g_array_index (array_supported_inds, guint, i);
+ if (ind_val <= 2)
+ supported_inds |= (MM3gppCmerInd) (1 << ind_val);
+ else
+ mm_obj_dbg (log_object, "unknown +CMER ind reported: %u", ind_val);
+ }
+
+ if (out_supported_modes)
+ *out_supported_modes = supported_modes;
+ if (out_supported_inds)
+ *out_supported_inds = supported_inds;
+ ret = TRUE;
+
+out:
+
+ if (array_supported_modes)
+ g_array_unref (array_supported_modes);
+ if (array_supported_inds)
+ g_array_unref (array_supported_inds);
+ g_clear_pointer (&aux, g_free);
+
+ g_strfreev (split);
+
+ if (inner_error)
+ g_propagate_error (error, inner_error);
- return (array ? (GStrv) g_array_free (array, FALSE) : NULL);
+ return ret;
}
/*************************************************************************/
@@ -1463,7 +3447,6 @@ cind_response_new (const gchar *desc, guint idx, gint min, gint max)
gchar *p;
g_return_val_if_fail (desc != NULL, NULL);
- g_return_val_if_fail (idx >= 0, NULL);
r = g_malloc0 (sizeof (MM3gppCindResponse));
@@ -1552,7 +3535,7 @@ mm_3gpp_parse_cind_test_response (const gchar *reply,
hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) cind_response_free);
- if (g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
+ if (g_regex_match (r, reply, 0, &match_info)) {
while (g_match_info_matches (match_info)) {
MM3gppCindResponse *resp;
gchar *desc, *tmp;
@@ -1609,7 +3592,7 @@ mm_3gpp_parse_cind_read_response (const gchar *reply,
r = g_regex_new ("(\\d+)[^0-9]+", G_REGEX_UNGREEDY, 0, NULL);
g_assert (r != NULL);
- if (!g_regex_match_full (r, reply, strlen (reply), 0, 0, &match_info, NULL)) {
+ if (!g_regex_match (r, reply, 0, &match_info)) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
"Could not parse the +CIND response '%s': didn't match",
reply);
@@ -1657,8 +3640,312 @@ done:
}
/*************************************************************************/
+/* +CGEV indication parser
+ *
+ * We provide full parsing support, including parameters, for these messages:
+ * +CGEV: NW DETACH
+ * +CGEV: ME DETACH
+ * +CGEV: NW PDN ACT <cid>
+ * +CGEV: ME PDN ACT <cid>[,<reason>[,<cid_other>]]
+ * +CGEV: NW ACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME ACT <p_cid>, <cid>, <event_type>
+ * +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: NW PDN DEACT <cid>
+ * +CGEV: ME PDN DEACT <cid>
+ * +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
+ * +CGEV: REJECT <PDP_type>, <PDP_addr>
+ * +CGEV: NW REACT <PDP_type>, <PDP_addr>, [<cid>]
+ *
+ * We don't provide parameter parsing for these messages:
+ * +CGEV: NW CLASS <class>
+ * +CGEV: ME CLASS <class>
+ * +CGEV: NW MODIFY <cid>, <change_reason>, <event_type>
+ * +CGEV: ME MODIFY <cid>, <change_reason>, <event_type>
+ */
-static void
+static gboolean
+deact_secondary (const gchar *str)
+{
+ /* We need to detect the ME/NW DEACT format.
+ * Either,
+ * +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * or,
+ * +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
+ */
+ str = strstr (str, "DEACT") + 5;
+ while (*str == ' ')
+ str++;
+
+ /* We will look for <p_cid> because we know it's NUMERIC */
+ return g_ascii_isdigit (*str);
+}
+
+MM3gppCgev
+mm_3gpp_parse_cgev_indication_action (const gchar *str)
+{
+ str = mm_strip_tag (str, "+CGEV:");
+ if (g_str_has_prefix (str, "NW DETACH"))
+ return MM_3GPP_CGEV_NW_DETACH;
+ if (g_str_has_prefix (str, "ME DETACH"))
+ return MM_3GPP_CGEV_ME_DETACH;
+ if (g_str_has_prefix (str, "NW CLASS"))
+ return MM_3GPP_CGEV_NW_CLASS;
+ if (g_str_has_prefix (str, "ME CLASS"))
+ return MM_3GPP_CGEV_ME_CLASS;
+ if (g_str_has_prefix (str, "NW PDN ACT"))
+ return MM_3GPP_CGEV_NW_ACT_PRIMARY;
+ if (g_str_has_prefix (str, "ME PDN ACT"))
+ return MM_3GPP_CGEV_ME_ACT_PRIMARY;
+ if (g_str_has_prefix (str, "NW ACT"))
+ return MM_3GPP_CGEV_NW_ACT_SECONDARY;
+ if (g_str_has_prefix (str, "ME ACT"))
+ return MM_3GPP_CGEV_ME_ACT_SECONDARY;
+ if (g_str_has_prefix (str, "NW DEACT"))
+ return (deact_secondary (str) ? MM_3GPP_CGEV_NW_DEACT_SECONDARY : MM_3GPP_CGEV_NW_DEACT_PDP);
+ if (g_str_has_prefix (str, "ME DEACT"))
+ return (deact_secondary (str) ? MM_3GPP_CGEV_ME_DEACT_SECONDARY : MM_3GPP_CGEV_ME_DEACT_PDP);
+ if (g_str_has_prefix (str, "NW PDN DEACT"))
+ return MM_3GPP_CGEV_NW_DEACT_PRIMARY;
+ if (g_str_has_prefix (str, "ME PDN DEACT"))
+ return MM_3GPP_CGEV_ME_DEACT_PRIMARY;
+ if (g_str_has_prefix (str, "NW MODIFY"))
+ return MM_3GPP_CGEV_NW_MODIFY;
+ if (g_str_has_prefix (str, "ME MODIFY"))
+ return MM_3GPP_CGEV_ME_MODIFY;
+ if (g_str_has_prefix (str, "NW REACT"))
+ return MM_3GPP_CGEV_NW_REACT;
+ if (g_str_has_prefix (str, "REJECT"))
+ return MM_3GPP_CGEV_REJECT;
+ return MM_3GPP_CGEV_UNKNOWN;
+}
+
+/*
+ * +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
+ * +CGEV: REJECT <PDP_type>, <PDP_addr>
+ * +CGEV: NW REACT <PDP_type>, <PDP_addr>, [<cid>]
+ */
+gboolean
+mm_3gpp_parse_cgev_indication_pdp (const gchar *str,
+ MM3gppCgev type,
+ gchar **out_pdp_type,
+ gchar **out_pdp_addr,
+ guint *out_cid,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *inner_error = NULL;
+ gchar *pdp_type = NULL;
+ gchar *pdp_addr = NULL;
+ guint cid = 0;
+
+ g_assert (type == MM_3GPP_CGEV_REJECT ||
+ type == MM_3GPP_CGEV_NW_REACT ||
+ type == MM_3GPP_CGEV_NW_DEACT_PDP ||
+ type == MM_3GPP_CGEV_ME_DEACT_PDP);
+
+ r = g_regex_new ("(?:"
+ "REJECT|"
+ "NW REACT|"
+ "NW DEACT|ME DEACT"
+ ")\\s*([^,]*),\\s*([^,]*)(?:,\\s*([0-9]+))?", 0, 0, NULL);
+
+ str = mm_strip_tag (str, "+CGEV:");
+ g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+ goto out;
+ }
+
+ if (out_pdp_type && !(pdp_type = mm_get_string_unquoted_from_match_info (match_info, 1))) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP type");
+ goto out;
+ }
+
+ if (out_pdp_addr && !(pdp_addr = mm_get_string_unquoted_from_match_info (match_info, 2))) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP addr");
+ goto out;
+ }
+
+ /* CID is optional */
+ if (out_cid &&
+ (g_match_info_get_match_count (match_info) >= 4) &&
+ !mm_get_uint_from_match_info (match_info, 3, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID");
+ goto out;
+ }
+
+out:
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_free (pdp_type);
+ g_free (pdp_addr);
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_pdp_type)
+ *out_pdp_type = pdp_type;
+ if (out_pdp_addr)
+ *out_pdp_addr = pdp_addr;
+ if (out_cid)
+ *out_cid = cid;
+ return TRUE;
+}
+
+/*
+ * +CGEV: NW PDN ACT <cid>
+ * +CGEV: ME PDN ACT <cid>[,<reason>[,<cid_other>]]
+ * +CGEV: NW PDN DEACT <cid>
+ * +CGEV: ME PDN DEACT <cid>
+ *
+ * NOTE: the special case of a "ME PDN ACT" notification with the additional
+ * <reason> and <cid_other> fields is telling us that <cid> was NOT connected
+ * but <cid_other> was connected instead, which may happen when trying to
+ * connect a IPv4v6 context but the modem ended up connecting a IPv4-only or
+ * IPv6-only context instead. We are right now ignoring this, and assuming the
+ * <cid> that we requested is the one reported as connected.
+ */
+gboolean
+mm_3gpp_parse_cgev_indication_primary (const gchar *str,
+ MM3gppCgev type,
+ guint *out_cid,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *inner_error = NULL;
+ guint cid = 0;
+
+ g_assert ((type == MM_3GPP_CGEV_NW_ACT_PRIMARY) ||
+ (type == MM_3GPP_CGEV_ME_ACT_PRIMARY) ||
+ (type == MM_3GPP_CGEV_NW_DEACT_PRIMARY) ||
+ (type == MM_3GPP_CGEV_ME_DEACT_PRIMARY));
+
+ r = g_regex_new ("(?:"
+ "NW PDN ACT|ME PDN ACT|"
+ "NW PDN DEACT|ME PDN DEACT|"
+ ")\\s*([0-9]+)", 0, 0, NULL);
+
+ str = mm_strip_tag (str, "+CGEV:");
+ g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+ goto out;
+ }
+
+ if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID");
+ goto out;
+ }
+
+out:
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_cid)
+ *out_cid = cid;
+ return TRUE;
+}
+
+/*
+ * +CGEV: NW ACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME ACT <p_cid>, <cid>, <event_type>
+ * +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
+ * +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
+ */
+gboolean
+mm_3gpp_parse_cgev_indication_secondary (const gchar *str,
+ MM3gppCgev type,
+ guint *out_p_cid,
+ guint *out_cid,
+ guint *out_event_type,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *inner_error = NULL;
+ guint p_cid = 0;
+ guint cid = 0;
+ guint event_type = 0;
+
+ g_assert (type == MM_3GPP_CGEV_NW_ACT_SECONDARY ||
+ type == MM_3GPP_CGEV_ME_ACT_SECONDARY ||
+ type == MM_3GPP_CGEV_NW_DEACT_SECONDARY ||
+ type == MM_3GPP_CGEV_ME_DEACT_SECONDARY);
+
+ r = g_regex_new ("(?:"
+ "NW ACT|ME ACT|"
+ "NW DEACT|ME DEACT"
+ ")\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+)", 0, 0, NULL);
+
+ str = mm_strip_tag (str, "+CGEV:");
+ g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error);
+ if (inner_error)
+ goto out;
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
+ goto out;
+ }
+
+ if (out_p_cid && !mm_get_uint_from_match_info (match_info, 1, &p_cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing primary CID");
+ goto out;
+ }
+
+ if (out_cid && !mm_get_uint_from_match_info (match_info, 2, &cid)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing secondary CID");
+ goto out;
+ }
+
+ if (out_event_type && !mm_get_uint_from_match_info (match_info, 3, &event_type)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing event type");
+ goto out;
+ }
+
+out:
+ if (match_info)
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ if (out_p_cid)
+ *out_p_cid = p_cid;
+ if (out_cid)
+ *out_cid = cid;
+ if (out_event_type)
+ *out_event_type = event_type;
+ return TRUE;
+}
+
+/*************************************************************************/
+
+void
mm_3gpp_pdu_info_free (MM3gppPduInfo *info)
{
g_free (info->pdu);
@@ -1703,6 +3990,7 @@ mm_3gpp_parse_pdu_cmgl_response (const gchar *str,
list = g_list_append (list, info);
g_match_info_next (match_info, &inner_error);
} else {
+ mm_3gpp_pdu_info_free (info);
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Error parsing +CMGL response: '%s'",
@@ -1728,8 +4016,8 @@ mm_3gpp_parse_pdu_cmgl_response (const gchar *str,
* many more facilities defined (for various flavors of call
* barring); we only map the ones we care about. */
typedef struct {
- MMModem3gppFacility facility;
- gchar *acronym;
+ MMModem3gppFacility facility;
+ const gchar *acronym;
} FacilityAcronym;
static const FacilityAcronym facility_acronyms[] = {
@@ -1756,7 +4044,7 @@ mm_3gpp_acronym_to_facility (const gchar *str)
return MM_MODEM_3GPP_FACILITY_NONE;
}
-gchar *
+const gchar *
mm_3gpp_facility_to_acronym (MMModem3gppFacility facility)
{
guint i;
@@ -1775,6 +4063,7 @@ MMModemAccessTechnology
mm_string_to_access_tech (const gchar *string)
{
MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ gsize len;
g_return_val_if_fail (string != NULL, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
@@ -1788,14 +4077,15 @@ mm_string_to_access_tech (const gchar *string)
else if (strcasestr (string, "HSPA"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
-
if (strcasestr (string, "HSUPA"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
if (strcasestr (string, "HSDPA"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
- if (strcasestr (string, "UMTS") || strcasestr (string, "3G"))
+ if (strcasestr (string, "UMTS") ||
+ strcasestr (string, "3G") ||
+ strcasestr (string, "WCDMA"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
if (strcasestr (string, "EDGE"))
@@ -1819,62 +4109,68 @@ mm_string_to_access_tech (const gchar *string)
if (strcasestr (string, "1xRTT") || strcasestr (string, "CDMA2000 1X"))
act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT;
+ /* Check "EVDO" and "CDMA" as standalone strings since their characters
+ * are included in other strings too.
+ */
+ len = strlen (string);
+ if (strncmp (string, "EVDO", 4) && (len >= 4 && !isalnum (string[4])))
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
+ if (strncmp (string, "CDMA", 4) && (len >= 4 && !isalnum (string[4])))
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT;
+ if (strncmp (string, "CDMA-EVDO", 9) && (len >= 9 && !isalnum (string[9])))
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
+
return act;
}
/*************************************************************************/
-gchar *
-mm_3gpp_parse_operator (const gchar *reply,
- MMModemCharset cur_charset)
+void
+mm_3gpp_normalize_operator (gchar **operator,
+ MMModemCharset cur_charset,
+ gpointer log_object)
{
- gchar *operator = NULL;
+ g_autofree gchar *normalized = NULL;
- if (reply && !strncmp (reply, "+COPS: ", 7)) {
- /* Got valid reply */
- GRegex *r;
- GMatchInfo *match_info;
+ g_assert (operator);
- reply += 7;
- r = g_regex_new ("(\\d),(\\d),\"(.+)\"", G_REGEX_UNGREEDY, 0, NULL);
- if (!r)
- return NULL;
+ if (*operator == NULL)
+ return;
- g_regex_match (r, reply, 0, &match_info);
- if (g_match_info_matches (match_info))
- operator = g_match_info_fetch (match_info, 3);
+ /* Despite +CSCS? may claim supporting UCS2, Some modems (e.g. Huawei)
+ * always report the operator name in ASCII in a +COPS response. */
+ if (cur_charset != MM_MODEM_CHARSET_UNKNOWN) {
+ g_autoptr(GError) error = NULL;
- g_match_info_free (match_info);
- g_regex_unref (r);
+ normalized = mm_modem_charset_str_to_utf8 (*operator, -1, cur_charset, TRUE, &error);
+ if (normalized)
+ goto out;
+
+ mm_obj_dbg (log_object, "couldn't convert operator string '%s' from charset '%s': %s",
+ *operator,
+ mm_modem_charset_to_string (cur_charset),
+ error->message);
}
- if (operator) {
- /* Some modems (Option & HSO) return the operator name as a hexadecimal
- * string of the bytes of the operator name as encoded by the current
- * character set.
- */
- if (cur_charset == MM_MODEM_CHARSET_UCS2) {
- /* In this case we're already checking UTF-8 validity */
- operator = mm_charset_take_and_convert_to_utf8 (operator, MM_MODEM_CHARSET_UCS2);
- }
- /* Ensure the operator name is valid UTF-8 so that we can send it
- * through D-Bus and such.
- */
- else if (!g_utf8_validate (operator, -1, NULL)) {
- g_free (operator);
- return NULL;
- }
+ /* Charset is unknown or there was an error in conversion; try to see
+ * if the contents we got are valid UTF-8 already. */
+ if (g_utf8_validate (*operator, -1, NULL))
+ normalized = g_strdup (*operator);
- /* Some modems (Novatel LTE) return the operator name as "Unknown" when
- * it fails to obtain the operator name. Return NULL in such case.
- */
- if (operator && g_ascii_strcasecmp (operator, "unknown") == 0) {
- g_free (operator);
- return NULL;
- }
+out:
+
+ /* Some modems (Novatel LTE) return the operator name as "Unknown" when
+ * it fails to obtain the operator name. Return NULL in such case.
+ */
+ if (!normalized || g_ascii_strcasecmp (normalized, "unknown") == 0) {
+ /* If normalization failed, just cleanup the string */
+ g_clear_pointer (operator, g_free);
+ return;
}
- return operator;
+ mm_obj_dbg (log_object, "operator normalized '%s'->'%s'", *operator, normalized);
+ g_clear_pointer (operator, g_free);
+ *operator = g_steal_pointer (&normalized);
}
/*************************************************************************/
@@ -1889,6 +4185,8 @@ mm_3gpp_get_pdp_type_from_ip_family (MMBearerIpFamily family)
return "IPV6";
case MM_BEARER_IP_FAMILY_IPV4V6:
return "IPV4V6";
+ case MM_BEARER_IP_FAMILY_NONE:
+ case MM_BEARER_IP_FAMILY_ANY:
default:
return NULL;
}
@@ -1897,8 +4195,12 @@ mm_3gpp_get_pdp_type_from_ip_family (MMBearerIpFamily family)
MMBearerIpFamily
mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type)
{
+ if (!pdp_type)
+ return MM_BEARER_IP_FAMILY_NONE;
if (g_str_equal (pdp_type, "IP"))
return MM_BEARER_IP_FAMILY_IPV4;
+ if (g_str_equal (pdp_type, "IPV4"))
+ return MM_BEARER_IP_FAMILY_IPV4;
if (g_str_equal (pdp_type, "IPV6"))
return MM_BEARER_IP_FAMILY_IPV6;
if (g_str_equal (pdp_type, "IPV4V6"))
@@ -1906,15 +4208,35 @@ mm_3gpp_get_ip_family_from_pdp_type (const gchar *pdp_type)
return MM_BEARER_IP_FAMILY_NONE;
}
-/*************************************************************************/
+gboolean
+mm_3gpp_normalize_ip_family (MMBearerIpFamily *family)
+{
+ /* if nothing specific requested, default to IPv4 */
+ if (*family == MM_BEARER_IP_FAMILY_NONE || *family == MM_BEARER_IP_FAMILY_ANY) {
+ *family = MM_BEARER_IP_FAMILY_IPV4;
+ return TRUE;
+ }
+
+ /* no need to normalize */
+ return FALSE;
+}
+/*************************************************************************/
+/* ICCID validation */
+/*
+ * 89: telecom (2 digits)
+ * cc: country (2 digits)
+ * oo: operator (2 digits)
+ * aaaaaaaaaaaaa: operator-specific account number (13 digits)
+ * x: checksum (1 digit)
+ */
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;
+ int i;
g_return_val_if_fail (raw_iccid != NULL, NULL);
@@ -1925,13 +4247,20 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
/* Make sure the buffer is only digits or 'F' */
buf = g_strdup (raw_iccid);
for (len = 0; buf[len]; len++) {
- if (isdigit (buf[len]))
+ /* Digit values allowed anywhere */
+ if (g_ascii_isdigit (buf[len]))
continue;
- if (buf[len] == 'F' || buf[len] == 'f') {
- buf[len] = 'F'; /* canonicalize the F */
- f_pos = len;
+
+ /* There are operators (e.g. the Chinese CMCC operator) that abuse the
+ * fact that 4 bits are used to store the BCD encoded numbers, and also
+ * use the [A-F] range as valid characters for the ICCID. Explicitly
+ * allow this range in the operator-specific part. */
+ if (len >= 6 && g_ascii_isxdigit (buf[len])) {
+ /* canonicalize hex digit */
+ buf[len] = g_ascii_toupper (buf[len]);
continue;
}
+
if (buf[len] == '\"') {
buf[len] = 0;
break;
@@ -1939,15 +4268,15 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
/* Invalid character */
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "ICCID response contained invalid character '%c'",
- buf[len]);
+ "ICCID response contained invalid character '%c' at index '%zu'",
+ buf[len], len);
goto error;
}
- /* BCD encoded ICCIDs are 20 digits long */
- if (len != 20) {
+ /* ICCIDs are 19 or 20 digits long */
+ if (len < 19 || len > 20) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
- "Invalid ICCID response size (was %zd, expected 20)",
+ "Invalid ICCID response size (was %zd, expected 19 or 20)",
len);
goto error;
}
@@ -1956,9 +4285,16 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
* should be '89' for telecommunication purposes according to ISO/IEC 7812.
*/
if (buf[0] == '8' && buf[1] == '9') {
- swap = FALSE;
+ swap = FALSE;
} else if (buf[0] == '9' && buf[1] == '8') {
- swap = TRUE;
+ /* swapped digits are only expected in raw +CRSM responses, which must all
+ * be 20-bytes long */
+ if (len != 20) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Invalid ICCID response size while swap needed (expected 20)");
+ goto error;
+ }
+ 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.
@@ -1968,23 +4304,14 @@ mm_3gpp_parse_iccid (const char *raw_iccid, GError **error)
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);
+ g_assert (len == 20);
+ swapped = g_malloc0 (21);
for (i = 0; i < 10; i++) {
swapped[i * 2] = buf[(i * 2) + 1];
swapped[(i * 2) + 1] = buf[i * 2];
@@ -2011,6 +4338,7 @@ gboolean
mm_3gpp_parse_operator_id (const gchar *operator_id,
guint16 *mcc,
guint16 *mnc,
+ gboolean *three_digit_mnc,
GError **error)
{
guint len;
@@ -2062,10 +4390,76 @@ mm_3gpp_parse_operator_id (const gchar *operator_id,
*mnc = atoi (aux);
}
+ if (three_digit_mnc)
+ *three_digit_mnc = len == 6;
+
return TRUE;
}
/*************************************************************************/
+/* Emergency numbers (+CRSM output) */
+
+GStrv
+mm_3gpp_parse_emergency_numbers (const char *raw, GError **error)
+{
+ gsize rawlen;
+ guint8 *bin;
+ gsize binlen;
+ gsize max_items;
+ GPtrArray *out;
+ guint i;
+
+ /* The emergency call code is of a variable length with a maximum length of
+ * 6 digits. Each emergency call code is coded on three bytes, with each
+ * digit within the code being coded on four bits. If a code of less that 6
+ * digits is chosen, then the unused nibbles shall be set to 'F'. */
+
+ rawlen = strlen (raw);
+ if (!rawlen) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "empty emergency numbers list");
+ return NULL;
+ }
+
+ if (rawlen % 6 != 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "invalid raw emergency numbers list length: %" G_GSIZE_FORMAT, rawlen);
+ return NULL;
+ }
+
+ bin = mm_utils_hexstr2bin (raw, -1, &binlen, error);
+ if (!bin) {
+ g_prefix_error (error, "invalid raw emergency numbers list contents: ");
+ return NULL;
+ }
+
+ max_items = binlen / 3;
+ out = g_ptr_array_sized_new (max_items + 1);
+
+ for (i = 0; i < max_items; i++) {
+ gchar *number;
+
+ number = mm_bcd_to_string (&bin[i*3], 3, TRUE /* low_nybble_first */);
+ if (number && number[0])
+ g_ptr_array_add (out, number);
+ else
+ g_free (number);
+ }
+
+ g_free (bin);
+
+ if (!out->len) {
+ g_ptr_array_unref (out);
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "uninitialized emergency numbers list");
+ return NULL;
+ }
+
+ g_ptr_array_add (out, NULL);
+ return (GStrv) g_ptr_array_free (out, FALSE);
+}
+
+/*************************************************************************/
gboolean
mm_cdma_parse_spservice_read_response (const gchar *reply,
@@ -2320,7 +4714,7 @@ mm_cdma_parse_eri (const gchar *reply,
*out_ind = ind;
while (iter->num != -1) {
- if (iter->num == ind) {
+ if ((guint)iter->num == ind) {
*out_roaming = iter->roam_ind;
if (out_desc)
*out_desc = iter->banner;
@@ -2439,7 +4833,7 @@ mm_cdma_get_index_from_rm_protocol (MMModemCdmaRmProtocol protocol,
return 0;
}
- /* just substracting 1 from the enum value should give us the index */
+ /* just subtracting 1 from the enum value should give us the index */
return (protocol - 1);
}
@@ -2617,3 +5011,396 @@ mm_parse_gsn (const char *gsn,
return success;
}
+
+/*****************************************************************************/
+/* +CCLK response parser */
+
+gboolean
+mm_parse_cclk_response (const char *response,
+ gchar **iso8601p,
+ MMNetworkTimezone **tzp,
+ GError **error)
+{
+ GRegex *r;
+ GMatchInfo *match_info = NULL;
+ GError *match_error = NULL;
+ guint year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0;
+ gint tz = 0;
+ gboolean ret = FALSE;
+
+ g_assert (iso8601p || tzp); /* at least one */
+
+ /* Sample replies:
+ * +CCLK: "15/03/05,14:14:26-32"
+ * +CCLK: 17/07/26,11:42:15+01
+ */
+ r = g_regex_new ("\\+CCLK:\\s*\"?(\\d+)/(\\d+)/(\\d+),(\\d+):(\\d+):(\\d+)([-+]\\d+)?\"?", 0, 0, NULL);
+ g_assert (r != NULL);
+
+ if (!g_regex_match_full (r, response, -1, 0, 0, &match_info, &match_error)) {
+ if (match_error) {
+ g_propagate_error (error, match_error);
+ g_prefix_error (error, "Could not parse +CCLK results: ");
+ } else {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't match +CCLK reply: %s", response);
+ }
+ goto out;
+ }
+
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ g_assert (g_match_info_get_match_count (match_info) >= 7);
+
+ /* Parse mandatory date and time fields */
+ if (!mm_get_uint_from_match_info (match_info, 1, &year) ||
+ !mm_get_uint_from_match_info (match_info, 2, &month) ||
+ !mm_get_uint_from_match_info (match_info, 3, &day) ||
+ !mm_get_uint_from_match_info (match_info, 4, &hour) ||
+ !mm_get_uint_from_match_info (match_info, 5, &minute) ||
+ !mm_get_uint_from_match_info (match_info, 6, &second)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse +CCLK reply: %s", response);
+ goto out;
+ }
+
+ /* Read optional time zone offset; if not given assume UTC (tz = 0).
+ * Note that timezone offset is given in 15 minute intervals.
+ */
+ if ((g_match_info_get_match_count (match_info) >= 8) &&
+ (!mm_get_int_from_match_info (match_info, 7, &tz))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Failed to parse timezone in +CCLK reply: %s", response);
+ goto out;
+ }
+
+ /* Adjust year to support YYYY format, as per +CSDF in 3GPP TS 27.007. Also,
+ * don't assume the reported date is actually the current real one, as some
+ * devices report an initial date of e.g. January 1st 1980. */
+ if (year < 100) {
+ if (year >= 70)
+ year += 1900;
+ else
+ year += 2000;
+ }
+
+ if (tzp) {
+ *tzp = mm_network_timezone_new ();
+ mm_network_timezone_set_offset (*tzp, tz * 15);
+ }
+
+ if (iso8601p) {
+ /* Return ISO-8601 format date/time string */
+ *iso8601p = mm_new_iso8601_time (year, month, day, hour,
+ minute, second,
+ TRUE, (tz * 15));
+ }
+
+ ret = TRUE;
+
+ out:
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ return ret;
+}
+
+/*****************************************************************************/
+/* +CSIM response parser */
+#define MM_MIN_SIM_RETRY_HEX 0x63C0
+#define MM_MAX_SIM_RETRY_HEX 0x63CF
+
+gint
+mm_parse_csim_response (const gchar *response,
+ GError **error)
+{
+ GMatchInfo *match_info = NULL;
+ GRegex *r = NULL;
+ gchar *str_code = NULL;
+ gint retries = -1;
+ guint hex_code;
+ GError *inner_error = NULL;
+
+ r = g_regex_new ("\\+CSIM:\\s*[0-9]+,\\s*\".*([0-9a-fA-F]{4})\"", G_REGEX_RAW, 0, NULL);
+ g_regex_match (r, response, 0, &match_info);
+
+ if (!g_match_info_matches (match_info)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not recognize +CSIM response '%s'", response);
+ goto out;
+ }
+
+ str_code = mm_get_string_unquoted_from_match_info (match_info, 1);
+ if (str_code == NULL) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not find expected string code in response '%s'", response);
+ goto out;
+ }
+
+ if (!mm_get_uint_from_hex_str (str_code, &hex_code)) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Could not recognize expected hex code in response '%s'", response);
+ goto out;
+ }
+
+ switch (hex_code) {
+ case 0x6300:
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM verification failed");
+ goto out;
+ case 0x6983:
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM authentication method blocked");
+ goto out;
+ case 0x6984:
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM reference data invalidated");
+ goto out;
+ case 0x6A86:
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Incorrect parameters in SIM request");
+ goto out;
+ case 0x6A88:
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "SIM reference data not found");
+ goto out;
+ default:
+ break;
+ }
+
+ if (hex_code < MM_MIN_SIM_RETRY_HEX || hex_code > MM_MAX_SIM_RETRY_HEX) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unknown error returned '0x%04x'", hex_code);
+ goto out;
+ }
+
+ retries = (gint)(hex_code - MM_MIN_SIM_RETRY_HEX);
+
+out:
+ g_regex_unref (r);
+ g_match_info_free (match_info);
+ g_free (str_code);
+
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return -1;
+ }
+
+ g_assert (retries >= 0);
+ return retries;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_parse_supl_address (const gchar *supl,
+ gchar **out_fqdn,
+ guint32 *out_ip,
+ guint16 *out_port,
+ GError **error)
+{
+ gboolean valid = FALSE;
+ gchar **split;
+ guint port;
+ guint32 ip;
+
+ split = g_strsplit (supl, ":", -1);
+ if (g_strv_length (split) != 2) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid SUPL address format: expected FQDN:PORT or IP:PORT");
+ goto out;
+ }
+
+ if (!mm_get_uint_from_str (split[1], &port)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid SUPL port number specified: not a number: %s", split[1]);
+ goto out;
+ }
+
+ if (port == 0 || port > G_MAXUINT16) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
+ "Invalid SUPL port number specified: out of range: %u", port);
+ goto out;
+ }
+
+ /* Port is valid */
+ if (out_port)
+ *out_port = (guint16) port;
+
+ /* Try to parse first item as IP */
+ if (inet_pton (AF_INET, split[0], &ip) <= 0) {
+ /* Otherwise, assume it's a domain name */
+ if (out_fqdn)
+ *out_fqdn = g_strdup (split[0]);
+ if (out_ip)
+ *out_ip = 0;
+ } else {
+ if (out_ip)
+ *out_ip = ip;
+ if (out_fqdn)
+ *out_fqdn = NULL;
+ }
+
+ valid = TRUE;
+
+out:
+ g_strfreev (split);
+ return valid;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_sim_parse_cpol_query_response (const gchar *response,
+ guint *out_index,
+ gchar **out_operator_code,
+ gboolean *out_gsm_act,
+ gboolean *out_gsm_compact_act,
+ gboolean *out_utran_act,
+ gboolean *out_eutran_act,
+ gboolean *out_ngran_act,
+ guint *out_act_count,
+ GError **error)
+{
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GRegex) r = NULL;
+ g_autofree gchar *operator_code = NULL;
+ guint format = 0;
+ guint act = 0;
+ guint match_count;
+
+ r = g_regex_new ("\\+CPOL:\\s*(\\d+),\\s*(\\d+),\\s*\"?(\\d+)\"?"
+ "(?:,\\s*(\\d+))?" /* GSM_AcTn */
+ "(?:,\\s*(\\d+))?" /* GSM_Compact_AcTn */
+ "(?:,\\s*(\\d+))?" /* UTRAN_AcTn */
+ "(?:,\\s*(\\d+))?" /* E-UTRAN_AcTn */
+ "(?:,\\s*(\\d+))?", /* NG-RAN_AcTn */
+ G_REGEX_RAW, 0, NULL);
+ g_regex_match (r, response, 0, &match_info);
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPOL reply: %s", response);
+ return FALSE;
+ }
+
+ match_count = g_match_info_get_match_count (match_info);
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ g_assert (match_count >= 4);
+
+ if (!mm_get_uint_from_match_info (match_info, 2, &format) ||
+ !(operator_code = mm_get_string_unquoted_from_match_info (match_info, 3))) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPOL reply parameters: %s", response);
+ return FALSE;
+ }
+
+ if (format != 2) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "+CPOL reply not using numeric operator code: %s", response);
+ return FALSE;
+ }
+
+ if (out_index)
+ if (!mm_get_uint_from_match_info (match_info, 1, out_index)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPOL index: %s", response);
+ return FALSE;
+ }
+ if (out_operator_code)
+ *out_operator_code = g_steal_pointer (&operator_code);
+ if (out_gsm_act)
+ *out_gsm_act = match_count >= 5 &&
+ mm_get_uint_from_match_info (match_info, 4, &act) &&
+ act != 0;
+ if (out_gsm_compact_act)
+ *out_gsm_compact_act = match_count >= 6 &&
+ mm_get_uint_from_match_info (match_info, 5, &act) &&
+ act != 0;
+ if (out_utran_act)
+ *out_utran_act = match_count >= 7 &&
+ mm_get_uint_from_match_info (match_info, 6, &act) &&
+ act != 0;
+ if (out_eutran_act)
+ *out_eutran_act = match_count >= 8 &&
+ mm_get_uint_from_match_info (match_info, 7, &act) &&
+ act != 0;
+ if (out_ngran_act)
+ *out_ngran_act = match_count >= 9 &&
+ mm_get_uint_from_match_info (match_info, 8, &act) &&
+ act != 0;
+ /* number of access technologies (0...5) in modem response */
+ if (out_act_count)
+ *out_act_count = match_count - 4;
+
+ return TRUE;
+}
+
+gboolean
+mm_sim_parse_cpol_test_response (const gchar *response,
+ guint *out_min_index,
+ guint *out_max_index,
+ GError **error)
+{
+ g_autoptr(GMatchInfo) match_info = NULL;
+ g_autoptr(GRegex) r = NULL;
+ guint match_count;
+ guint min_index;
+ guint max_index;
+
+ r = g_regex_new ("\\+CPOL:\\s*\\((\\d+)\\s*-\\s*(\\d+)\\)",
+ G_REGEX_RAW, 0, NULL);
+ g_regex_match (r, response, 0, &match_info);
+
+ if (!g_match_info_matches (match_info)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse +CPOL=? reply: %s", response);
+ return FALSE;
+ }
+
+ match_count = g_match_info_get_match_count (match_info);
+ /* Remember that g_match_info_get_match_count() includes match #0 */
+ g_assert (match_count >= 3);
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &min_index) ||
+ !mm_get_uint_from_match_info (match_info, 2, &max_index)) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't parse indices in +CPOL=? reply: %s", response);
+ return FALSE;
+ }
+
+ if (out_min_index)
+ *out_min_index = min_index;
+ if (out_max_index)
+ *out_max_index = max_index;
+
+ return TRUE;
+}
+
+#define EID_BYTE_LENGTH 16
+
+gchar *
+mm_decode_eid (const gchar *eid, gsize eid_len)
+{
+ if (eid_len != EID_BYTE_LENGTH)
+ return NULL;
+
+ return mm_bcd_to_string ((const guint8 *) eid, eid_len, FALSE /* low_nybble_first */);
+}
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index 0ec59aff..ed5364ef 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -20,6 +20,9 @@
#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
#include "glib-object.h"
#include "mm-charsets.h"
@@ -36,22 +39,26 @@
/* Common utilities */
/*****************************************************************************/
-#define MM_MODEM_CAPABILITY_3GPP_LTE \
- (MM_MODEM_CAPABILITY_LTE | \
- MM_MODEM_CAPABILITY_LTE_ADVANCED)
-
#define MM_MODEM_CAPABILITY_3GPP \
(MM_MODEM_CAPABILITY_GSM_UMTS | \
- MM_MODEM_CAPABILITY_3GPP_LTE)
+ MM_MODEM_CAPABILITY_LTE | \
+ MM_MODEM_CAPABILITY_5GNR)
gchar *mm_strip_quotes (gchar *str);
const gchar *mm_strip_tag (const gchar *str,
const gchar *cmd);
+gchar **mm_split_string_groups (const gchar *str);
+
+GArray *mm_parse_uint_list (const gchar *str,
+ GError **error);
+
guint mm_count_bits_set (gulong number);
+guint mm_find_bit_set (gulong number);
-gchar *mm_create_device_identifier (guint vid,
- guint pid,
+gchar *mm_create_device_identifier (guint vid,
+ guint pid,
+ gpointer log_object,
const gchar *ati,
const gchar *ati1,
const gchar *gsn,
@@ -64,20 +71,56 @@ guint mm_netmask_to_cidr (const gchar *netmask);
GArray *mm_filter_current_bands (const GArray *supported_bands,
const GArray *current_bands);
-gchar *mm_new_iso8601_time (guint year,
- guint month,
- guint day,
- guint hour,
- guint minute,
- guint second,
- gboolean have_offset,
- gint offset_minutes);
-
GArray *mm_filter_supported_modes (const GArray *all,
- const GArray *supported_combinations);
+ const GArray *supported_combinations,
+ gpointer log_object);
+
+gchar *mm_bcd_to_string (const guint8 *bcd,
+ gsize bcd_len,
+ gboolean low_nybble_first);
+
+/*****************************************************************************/
+/* VOICE specific helpers and utilities */
+/*****************************************************************************/
-GArray *mm_filter_supported_capabilities (MMModemCapability all,
- const GArray *supported_combinations);
+GRegex *mm_voice_ring_regex_get (void);
+GRegex *mm_voice_cring_regex_get (void);
+GRegex *mm_voice_clip_regex_get (void);
+GRegex *mm_voice_ccwa_regex_get (void);
+
+/* +CLCC response parser */
+typedef struct {
+ guint index;
+ MMCallDirection direction;
+ MMCallState state;
+ gchar *number; /* optional */
+} MMCallInfo;
+gboolean mm_3gpp_parse_clcc_response (const gchar *str,
+ gpointer log_object,
+ GList **out_list,
+ GError **error);
+void mm_3gpp_call_info_list_free (GList *call_info_list);
+
+/*****************************************************************************/
+/* SERIAL specific helpers and utilities */
+
+/* AT+IFC=? response parser.
+ * For simplicity, we'll only consider flow control methods available in both
+ * TE and TA. */
+
+typedef enum { /*< underscore_name=mm_flow_control >*/
+ MM_FLOW_CONTROL_UNKNOWN = 0,
+ MM_FLOW_CONTROL_NONE = 1 << 0, /* IFC=0,0 */
+ MM_FLOW_CONTROL_XON_XOFF = 1 << 1, /* IFC=1,1 */
+ MM_FLOW_CONTROL_RTS_CTS = 1 << 2, /* IFC=2,2 */
+} MMFlowControl;
+
+MMFlowControl mm_parse_ifc_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error);
+
+MMFlowControl mm_flow_control_from_string (const gchar *str,
+ GError **error);
/*****************************************************************************/
/* 3GPP specific helpers and utilities */
@@ -87,10 +130,15 @@ GArray *mm_filter_supported_capabilities (MMModemCapability all,
GPtrArray *mm_3gpp_creg_regex_get (gboolean solicited);
void mm_3gpp_creg_regex_destroy (GPtrArray *array);
GRegex *mm_3gpp_ciev_regex_get (void);
+GRegex *mm_3gpp_cgev_regex_get (void);
GRegex *mm_3gpp_cusd_regex_get (void);
GRegex *mm_3gpp_cmti_regex_get (void);
GRegex *mm_3gpp_cds_regex_get (void);
+/* AT+WS46=? response parser: returns array of MMModemMode values */
+GArray *mm_3gpp_parse_ws46_test_response (const gchar *response,
+ gpointer log_object,
+ GError **error);
/* AT+COPS=? (network scan) response parser */
typedef struct {
@@ -101,8 +149,22 @@ typedef struct {
MMModemAccessTechnology access_tech;
} MM3gppNetworkInfo;
void mm_3gpp_network_info_list_free (GList *info_list);
-GList *mm_3gpp_parse_cops_test_response (const gchar *reply,
- GError **error);
+GList *mm_3gpp_parse_cops_test_response (const gchar *reply,
+ MMModemCharset cur_charset,
+ gpointer log_object,
+ GError **error);
+
+/* AT+COPS? (current operator) response parser */
+gboolean mm_3gpp_parse_cops_read_response (const gchar *response,
+ guint *out_mode,
+ guint *out_format,
+ gchar **out_operator,
+ MMModemAccessTechnology *out_act,
+ GError **error);
+
+/* Logic to compare two APN names */
+gboolean mm_3gpp_cmp_apn_name (const gchar *requested,
+ const gchar *existing);
/* AT+CGDCONT=? (PDP context format) test parser */
typedef struct {
@@ -111,8 +173,14 @@ typedef struct {
MMBearerIpFamily pdp_type;
} MM3gppPdpContextFormat;
void mm_3gpp_pdp_context_format_list_free (GList *pdp_format_list);
-GList *mm_3gpp_parse_cgdcont_test_response (const gchar *reply,
- GError **error);
+GList *mm_3gpp_parse_cgdcont_test_response (const gchar *reply,
+ gpointer log_object,
+ GError **error);
+
+gboolean mm_3gpp_pdp_context_format_list_find_range (GList *pdp_format_list,
+ MMBearerIpFamily ip_family,
+ guint *out_min_cid,
+ guint *out_max_cid);
/* AT+CGDCONT? (PDP context query) response parser */
typedef struct {
@@ -124,15 +192,28 @@ void mm_3gpp_pdp_context_list_free (GList *pdp_list);
GList *mm_3gpp_parse_cgdcont_read_response (const gchar *reply,
GError **error);
+/* AT+CGACT? (active PDP context query) response parser */
+typedef struct {
+ guint cid;
+ gboolean active;
+} MM3gppPdpContextActive;
+void mm_3gpp_pdp_context_active_list_free (GList *pdp_active_list);
+gint mm_3gpp_pdp_context_active_cmp (MM3gppPdpContextActive *a,
+ MM3gppPdpContextActive *b);
+GList *mm_3gpp_parse_cgact_read_response (const gchar *reply,
+ GError **error);
+
/* CREG/CGREG response/unsolicited message parser */
-gboolean mm_3gpp_parse_creg_response (GMatchInfo *info,
- MMModem3gppRegistrationState *out_reg_state,
- gulong *out_lac,
- gulong *out_ci,
- MMModemAccessTechnology *out_act,
- gboolean *out_cgreg,
- gboolean *out_cereg,
- GError **error);
+gboolean mm_3gpp_parse_creg_response (GMatchInfo *info,
+ gpointer log_object,
+ MMModem3gppRegistrationState *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ MMModemAccessTechnology *out_act,
+ gboolean *out_cgreg,
+ gboolean *out_cereg,
+ gboolean *out_c5greg,
+ GError **error);
/* AT+CMGF=? (SMS message format) response parser */
gboolean mm_3gpp_parse_cmgf_test_response (const gchar *reply,
@@ -141,10 +222,21 @@ gboolean mm_3gpp_parse_cmgf_test_response (const gchar *reply,
GError **error);
/* AT+CPMS=? (Preferred SMS storage) response parser */
-gboolean mm_3gpp_parse_cpms_test_response (const gchar *reply,
- GArray **mem1,
- GArray **mem2,
- GArray **mem3);
+gboolean mm_3gpp_parse_cpms_test_response (const gchar *reply,
+ GArray **mem1,
+ GArray **mem2,
+ GArray **mem3,
+ GError **error);
+
+/* AT+CPMS? (Current SMS storage) response parser */
+gboolean mm_3gpp_parse_cpms_query_response (const gchar *reply,
+ MMSmsStorage *mem1,
+ MMSmsStorage *mem2,
+ GError** error);
+gboolean mm_3gpp_get_cpms_storage_match (GMatchInfo *match_info,
+ const gchar *match_name,
+ MMSmsStorage *storage,
+ GError **error);
/* AT+CSCS=? (Supported charsets) response parser */
gboolean mm_3gpp_parse_cscs_test_response (const gchar *reply,
@@ -159,8 +251,32 @@ gboolean mm_3gpp_parse_clck_write_response (const gchar *reply,
gboolean *enabled);
/* AT+CNUM (Own numbers) response parser */
-GStrv mm_3gpp_parse_cnum_exec_response (const gchar *reply,
- GError **error);
+GStrv mm_3gpp_parse_cnum_exec_response (const gchar *reply);
+
+/* AT+CMER=? (Mobile Equipment Event Reporting) response parser */
+typedef enum { /*< underscore_name=mm_3gpp_cmer_mode >*/
+ MM_3GPP_CMER_MODE_NONE = 0,
+ MM_3GPP_CMER_MODE_DISCARD_URCS = 1 << 0,
+ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED = 1 << 1,
+ MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED = 1 << 2,
+ MM_3GPP_CMER_MODE_FORWARD_URCS = 1 << 3,
+} MM3gppCmerMode;
+typedef enum { /*< underscore_name=mm_3gpp_cmer_ind >*/
+ MM_3GPP_CMER_IND_NONE = 0,
+ /* no indicator event reporting */
+ MM_3GPP_CMER_IND_DISABLE = 1 << 0,
+ /* Only indicator events that are not caused by +CIND */
+ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND = 1 << 1,
+ /* All indicator events */
+ MM_3GPP_CMER_IND_ENABLE_ALL = 1 << 2,
+} MM3gppCmerInd;
+gchar *mm_3gpp_build_cmer_set_request (MM3gppCmerMode mode,
+ MM3gppCmerInd ind);
+gboolean mm_3gpp_parse_cmer_test_response (const gchar *reply,
+ gpointer log_object,
+ MM3gppCmerMode *supported_modes,
+ MM3gppCmerInd *supported_inds,
+ GError **error);
/* AT+CIND=? (Supported indicators) response parser */
typedef struct MM3gppCindResponse MM3gppCindResponse;
@@ -175,37 +291,194 @@ gint mm_3gpp_cind_response_get_max (MM3gppCindResponse *r);
GByteArray *mm_3gpp_parse_cind_read_response (const gchar *reply,
GError **error);
+/* +CGEV indication parser */
+typedef enum {
+ MM_3GPP_CGEV_UNKNOWN,
+ MM_3GPP_CGEV_NW_DETACH,
+ MM_3GPP_CGEV_ME_DETACH,
+ MM_3GPP_CGEV_NW_CLASS,
+ MM_3GPP_CGEV_ME_CLASS,
+ MM_3GPP_CGEV_NW_ACT_PRIMARY,
+ MM_3GPP_CGEV_ME_ACT_PRIMARY,
+ MM_3GPP_CGEV_NW_ACT_SECONDARY,
+ MM_3GPP_CGEV_ME_ACT_SECONDARY,
+ MM_3GPP_CGEV_NW_DEACT_PRIMARY,
+ MM_3GPP_CGEV_ME_DEACT_PRIMARY,
+ MM_3GPP_CGEV_NW_DEACT_SECONDARY,
+ MM_3GPP_CGEV_ME_DEACT_SECONDARY,
+ MM_3GPP_CGEV_NW_DEACT_PDP,
+ MM_3GPP_CGEV_ME_DEACT_PDP,
+ MM_3GPP_CGEV_NW_MODIFY,
+ MM_3GPP_CGEV_ME_MODIFY,
+ MM_3GPP_CGEV_REJECT,
+ MM_3GPP_CGEV_NW_REACT,
+} MM3gppCgev;
+
+MM3gppCgev mm_3gpp_parse_cgev_indication_action (const gchar *str);
+gboolean mm_3gpp_parse_cgev_indication_pdp (const gchar *str,
+ MM3gppCgev type,
+ gchar **out_pdp_type,
+ gchar **out_pdp_addr,
+ guint *out_cid,
+ GError **error);
+gboolean mm_3gpp_parse_cgev_indication_primary (const gchar *str,
+ MM3gppCgev type,
+ guint *out_cid,
+ GError **error);
+gboolean mm_3gpp_parse_cgev_indication_secondary (const gchar *str,
+ MM3gppCgev type,
+ guint *out_p_cid,
+ guint *out_cid,
+ guint *out_event_type,
+ GError **error);
+
/* AT+CMGL=4 (list sms parts) response parser */
typedef struct {
gint index;
gint status;
gchar *pdu;
} MM3gppPduInfo;
+void mm_3gpp_pdu_info_free (MM3gppPduInfo *info);
void mm_3gpp_pdu_info_list_free (GList *info_list);
GList *mm_3gpp_parse_pdu_cmgl_response (const gchar *str,
GError **error);
+/* AT+CMGR (Read message) response parser */
+MM3gppPduInfo *mm_3gpp_parse_cmgr_read_response (const gchar *reply,
+ guint index,
+ GError **error);
+
+
+/* AT+CRSM response parser */
+gboolean mm_3gpp_parse_crsm_response (const gchar *reply,
+ guint *sw1,
+ guint *sw2,
+ gchar **hex,
+ GError **error);
+
+/* AT+CGCONTRDP=N response parser */
+gboolean mm_3gpp_parse_cgcontrdp_response (const gchar *response,
+ guint *out_cid,
+ guint *out_bearer_id,
+ gchar **out_apn,
+ gchar **out_local_address,
+ gchar **out_subnet,
+ gchar **out_gateway_address,
+ gchar **out_dns_primary_address,
+ gchar **out_dns_secondary_address,
+ GError **error);
+
+/* CFUN? response parser
+ * Note: a custom method with values not translated into MMModemPowerState is
+ * provided, because they may be vendor specific.
+ */
+gboolean mm_3gpp_parse_cfun_query_response (const gchar *response,
+ guint *out_state,
+ GError **error);
+gboolean mm_3gpp_parse_cfun_query_generic_response (const gchar *response,
+ MMModemPowerState *out_state,
+ GError **error);
+
+/* +CESQ response parser */
+gboolean mm_3gpp_parse_cesq_response (const gchar *response,
+ guint *out_rxlev,
+ guint *out_ber,
+ guint *out_rscp,
+ guint *out_ecn0,
+ guint *out_rsrq,
+ guint *out_rsrp,
+ GError **error);
+
+gboolean mm_3gpp_cesq_response_to_signal_info (const gchar *response,
+ gpointer log_object,
+ MMSignal **out_gsm,
+ MMSignal **out_umts,
+ MMSignal **out_lte,
+ GError **error);
+
+/* CEMODE? response parser */
+gchar *mm_3gpp_build_cemode_set_request (MMModem3gppEpsUeModeOperation mode);
+gboolean mm_3gpp_parse_cemode_query_response (const gchar *response,
+ MMModem3gppEpsUeModeOperation *out_mode,
+ GError **error);
+
+/* CCWA service query response parser */
+gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response,
+ gpointer log_object,
+ gboolean *status,
+ GError **error);
+
+/* CGATT helpers */
+gchar *mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state);
+
/* Additional 3GPP-specific helpers */
-MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str);
-gchar *mm_3gpp_facility_to_acronym (MMModem3gppFacility facility);
+MMModem3gppFacility mm_3gpp_acronym_to_facility (const gchar *str);
+const gchar *mm_3gpp_facility_to_acronym (MMModem3gppFacility facility);
MMModemAccessTechnology mm_string_to_access_tech (const gchar *string);
-gchar *mm_3gpp_parse_operator (const gchar *reply,
- MMModemCharset cur_charset);
+void mm_3gpp_normalize_operator (gchar **operator,
+ MMModemCharset cur_charset,
+ gpointer log_object);
gboolean mm_3gpp_parse_operator_id (const gchar *operator_id,
guint16 *mcc,
guint16 *mnc,
+ gboolean *three_digit_mnc,
GError **error);
-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);
+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);
+gboolean mm_3gpp_normalize_ip_family (MMBearerIpFamily *family);
char *mm_3gpp_parse_iccid (const char *raw_iccid, GError **error);
+
+gboolean mm_3gpp_rscp_level_to_rscp (guint rscp_level,
+ gpointer log_object,
+ gdouble *out_rscp);
+gboolean mm_3gpp_rxlev_to_rssi (guint rxlev,
+ gpointer log_object,
+ gdouble *out_rssi);
+gboolean mm_3gpp_ecn0_level_to_ecio (guint ecn0_level,
+ gpointer log_object,
+ gdouble *out_ecio);
+gboolean mm_3gpp_rsrq_level_to_rsrq (guint rsrq_level,
+ gpointer log_object,
+ gdouble *out_rsrq);
+gboolean mm_3gpp_rsrp_level_to_rsrp (guint rsrp_level,
+ gpointer log_object,
+ gdouble *out_rsrp);
+
+GStrv mm_3gpp_parse_emergency_numbers (const char *raw, GError **error);
+
+/* PDP context -> profile */
+MM3gppProfile *mm_3gpp_profile_new_from_pdp_context (MM3gppPdpContext *pdp_context);
+
+/* Profile list operations */
+GList *mm_3gpp_profile_list_new_from_pdp_context_list (GList *pdp_context_list);
+void mm_3gpp_profile_list_free (GList *profile_list);
+
+gint mm_3gpp_profile_list_find_empty (GList *profile_list,
+ gint min_profile_id,
+ gint max_profile_id,
+ GError **error);
+gint mm_3gpp_profile_list_find_best (GList *profile_list,
+ MM3gppProfile *requested,
+ GEqualFunc cmp_apn,
+ MM3gppProfileCmpFlags cmp_flags,
+ gint min_profile_id,
+ gint max_profile_id,
+ gpointer log_object,
+ MM3gppProfile **out_reused,
+ gboolean *out_overwritten);
+
+MM3gppProfile *mm_3gpp_profile_list_find_by_profile_id (GList *profile_list,
+ gint profile_id,
+ GError **error);
+
/*****************************************************************************/
/* CDMA specific helpers and utilities */
/*****************************************************************************/
@@ -246,4 +519,64 @@ gboolean mm_parse_gsn (const char *gsn,
gchar **out_meid,
gchar **out_esn);
+/* +CCLK response parser */
+gboolean mm_parse_cclk_response (const gchar *response,
+ gchar **iso8601p,
+ MMNetworkTimezone **tzp,
+ GError **error);
+
+/* +CSIM response parser */
+gint mm_parse_csim_response (const gchar *response,
+ GError **error);
+
+gboolean mm_parse_supl_address (const gchar *supl,
+ gchar **out_fqdn,
+ guint32 *out_ip,
+ guint16 *out_port,
+ GError **error);
+
+/*****************************************************************************/
+/* SIM specific helpers and utilities */
+/*****************************************************************************/
+
+/* +CPOL? response parser (for a single entry) - accepts only numeric operator format*/
+gboolean mm_sim_parse_cpol_query_response (const gchar *response,
+ guint *out_index,
+ gchar **out_operator_code,
+ gboolean *out_gsm_act,
+ gboolean *out_gsm_compact_act,
+ gboolean *out_utran_act,
+ gboolean *out_eutran_act,
+ gboolean *out_ngran_act,
+ guint *out_act_count,
+ GError **error);
+
+/* +CPOL=? response parser for getting supported min and max index */
+gboolean mm_sim_parse_cpol_test_response (const gchar *response,
+ guint *out_min_index,
+ guint *out_max_index,
+ GError **error);
+
+/*****************************************************************************/
+
+/* Useful when clamp-ing an unsigned integer with implicit low limit set to 0,
+ * and in order to avoid -Wtype-limits warnings. */
+#define MM_CLAMP_HIGH(x, high) (((x) > (high)) ? (high) : (x))
+
+/*****************************************************************************/
+/* Signal quality percentage from different sources */
+
+/* Limit the value betweeen [-113,-51] and scale it to a percentage */
+#define MM_RSSI_TO_QUALITY(rssi) \
+ (guint8)(100 - ((CLAMP (rssi, -113, -51) + 51) * 100 / (-113 + 51)))
+
+/* Limit the value betweeen [-110,-60] and scale it to a percentage */
+#define MM_RSRP_TO_QUALITY(rsrp) \
+ (guint8)(100 - ((CLAMP (rsrp, -110, -60) + 60) * 100 / (-110 + 60)))
+
+/*****************************************************************************/
+
+/* Helper function to decode eid read from esim */
+gchar *mm_decode_eid (const gchar *eid, gsize eid_len);
+
#endif /* MM_MODEM_HELPERS_H */
diff --git a/src/mm-netlink.c b/src/mm-netlink.c
new file mode 100644
index 00000000..5fd2ab34
--- /dev/null
+++ b/src/mm-netlink.c
@@ -0,0 +1,464 @@
+/* -*- 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:
+ *
+ * Basic netlink support based on the QmiNetPortManagerRmnet from libqmi:
+ * Copyright (C) 2020 Eric Caruso <ejcaruso@chromium.org>
+ * Copyright (C) 2020 Andrew Lassalle <andrewlassalle@chromium.org>
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <config.h>
+
+#include <ModemManager.h>
+#include <mm-errors-types.h>
+
+#include "mm-log-object.h"
+#include "mm-utils.h"
+#include "mm-netlink.h"
+
+struct _MMNetlink {
+ GObject parent;
+ /* Netlink socket */
+ GSocket *socket;
+ GSource *source;
+ /* Netlink state */
+ guint current_sequence_id;
+ GHashTable *transactions;
+};
+
+struct _MMNetlinkClass {
+ GObjectClass parent_class;
+};
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMNetlink, mm_netlink, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+
+/*****************************************************************************/
+/*
+ * Netlink message construction functions
+ */
+
+typedef GByteArray NetlinkMessage;
+
+typedef struct {
+ struct nlmsghdr msghdr;
+ struct ifinfomsg ifreq;
+} NetlinkHeader;
+
+static NetlinkHeader *
+netlink_message_header (NetlinkMessage *msg)
+{
+ return (NetlinkHeader *) (msg->data);
+}
+
+static guint
+get_pos_of_next_attr (NetlinkMessage *msg)
+{
+ return NLMSG_ALIGN (msg->len);
+}
+
+static void
+append_netlink_attribute (NetlinkMessage *msg,
+ gushort type,
+ gconstpointer value,
+ gushort len)
+{
+ guint attr_len;
+ guint old_len;
+ guint next_attr_rel_pos;
+ char *next_attr_abs_pos;
+ struct rtattr new_attr;
+
+ /* Expand the buffer to hold the new attribute */
+ attr_len = RTA_ALIGN (RTA_LENGTH (len));
+ old_len = msg->len;
+ next_attr_rel_pos = get_pos_of_next_attr (msg);
+
+ g_byte_array_set_size (msg, next_attr_rel_pos + attr_len);
+ /* fill new bytes with zero, since some padding is added between attributes. */
+ memset ((char *) msg->data + old_len, 0, msg->len - old_len);
+
+ new_attr.rta_type = type;
+ new_attr.rta_len = attr_len;
+ next_attr_abs_pos = (char *) msg->data + next_attr_rel_pos;
+ memcpy (next_attr_abs_pos, &new_attr, sizeof (struct rtattr));
+
+ if (value)
+ memcpy (RTA_DATA (next_attr_abs_pos), value, len);
+
+ /* Update the total netlink message length */
+ netlink_message_header (msg)->msghdr.nlmsg_len = msg->len;
+}
+
+static void
+append_netlink_attribute_uint32 (NetlinkMessage *msg,
+ gushort type,
+ guint32 value)
+{
+ append_netlink_attribute (msg, type, &value, sizeof (value));
+}
+
+static NetlinkMessage *
+netlink_message_new (guint ifindex,
+ guint16 type)
+{
+ NetlinkMessage *msg;
+ NetlinkHeader *hdr;
+
+ int size = sizeof (NetlinkHeader);
+
+ msg = g_byte_array_new ();
+ g_byte_array_set_size (msg, size);
+ memset ((char *) msg->data, 0, size);
+
+ hdr = netlink_message_header (msg);
+ hdr->msghdr.nlmsg_len = msg->len;
+ hdr->msghdr.nlmsg_type = type;
+ hdr->msghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ hdr->ifreq.ifi_family = AF_UNSPEC;
+ hdr->ifreq.ifi_index = ifindex;
+ hdr->ifreq.ifi_flags = 0;
+ hdr->ifreq.ifi_change = 0xFFFFFFFF;
+
+ return msg;
+}
+
+static NetlinkMessage *
+netlink_message_new_setlink (guint ifindex,
+ gboolean up,
+ guint mtu)
+{
+ NetlinkMessage *msg;
+ NetlinkHeader *hdr;
+
+ msg = netlink_message_new (ifindex, RTM_SETLINK);
+ hdr = netlink_message_header (msg);
+
+ hdr->ifreq.ifi_flags = up ? IFF_UP : 0;
+ hdr->ifreq.ifi_change = 0xFFFFFFFF;
+
+ if (mtu)
+ append_netlink_attribute_uint32 (msg, IFLA_MTU, mtu);
+
+ return msg;
+}
+
+static void
+netlink_message_free (NetlinkMessage *msg)
+{
+ g_byte_array_unref (msg);
+}
+
+/*****************************************************************************/
+/* Netlink transactions */
+
+typedef struct {
+ MMNetlink *self;
+ guint32 sequence_id;
+ GSource *timeout_source;
+ GTask *completion_task;
+} Transaction;
+
+static gboolean
+transaction_timed_out (Transaction *tr)
+{
+ GTask *task;
+ guint32 sequence_id;
+
+ task = g_steal_pointer (&tr->completion_task);
+ sequence_id = tr->sequence_id;
+
+ g_hash_table_remove (tr->self->transactions,
+ GUINT_TO_POINTER (tr->sequence_id));
+
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_TIMED_OUT,
+ "Netlink message with sequence ID %u timed out",
+ sequence_id);
+
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+transaction_complete_with_error (Transaction *tr,
+ GError *error)
+{
+ GTask *task;
+
+ task = g_steal_pointer (&tr->completion_task);
+
+ g_hash_table_remove (tr->self->transactions,
+ GUINT_TO_POINTER (tr->sequence_id));
+
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+transaction_complete (Transaction *tr,
+ gint saved_errno)
+{
+ GTask *task;
+ guint32 sequence_id;
+
+ task = g_steal_pointer (&tr->completion_task);
+ sequence_id = tr->sequence_id;
+
+ g_hash_table_remove (tr->self->transactions,
+ GUINT_TO_POINTER (tr->sequence_id));
+
+ if (!saved_errno) {
+ g_task_return_boolean (task, TRUE);
+ } else {
+ g_task_return_new_error (task, G_IO_ERROR, g_io_error_from_errno (saved_errno),
+ "Netlink message with transaction %u failed",
+ sequence_id);
+ }
+
+ g_object_unref (task);
+}
+
+static void
+transaction_free (Transaction *tr)
+{
+ g_assert (tr->completion_task == NULL);
+ g_source_destroy (tr->timeout_source);
+ g_source_unref (tr->timeout_source);
+ g_slice_free (Transaction, tr);
+}
+
+static Transaction *
+transaction_new (MMNetlink *self,
+ NetlinkMessage *msg,
+ guint timeout,
+ GTask *task)
+{
+ Transaction *tr;
+
+ tr = g_slice_new0 (Transaction);
+ tr->self = self;
+ tr->sequence_id = ++self->current_sequence_id;
+ netlink_message_header (msg)->msghdr.nlmsg_seq = tr->sequence_id;
+ if (timeout) {
+ tr->timeout_source = g_timeout_source_new_seconds (timeout);
+ g_source_set_callback (tr->timeout_source,
+ (GSourceFunc) transaction_timed_out,
+ tr,
+ NULL);
+ g_source_attach (tr->timeout_source,
+ g_main_context_get_thread_default ());
+ }
+ tr->completion_task = g_object_ref (task);
+
+ g_hash_table_insert (self->transactions,
+ GUINT_TO_POINTER (tr->sequence_id),
+ tr);
+ return tr;
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_netlink_setlink_finish (MMNetlink *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+void
+mm_netlink_setlink (MMNetlink *self,
+ guint ifindex,
+ gboolean up,
+ guint mtu,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ NetlinkMessage *msg;
+ Transaction *tr;
+ gssize bytes_sent;
+ GError *error = NULL;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (!self->socket) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "netlink support not available");
+ g_object_unref (task);
+ return;
+ }
+
+ msg = netlink_message_new_setlink (ifindex, up, mtu);
+
+ /* The task ownership is transferred to the transaction. */
+ tr = transaction_new (self, msg, 5, task);
+
+ bytes_sent = g_socket_send (self->socket,
+ (const gchar *) msg->data,
+ msg->len,
+ cancellable,
+ &error);
+ netlink_message_free (msg);
+
+ if (bytes_sent < 0)
+ transaction_complete_with_error (tr, error);
+
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+
+static gboolean
+netlink_message_cb (GSocket *socket,
+ GIOCondition condition,
+ MMNetlink *self)
+{
+ g_autoptr(GError) error = NULL;
+ gchar buf[512];
+ gssize bytes_received;
+ guint buffer_len;
+ struct nlmsghdr *hdr;
+
+ if (condition & G_IO_HUP || condition & G_IO_ERR) {
+ mm_obj_warn (self, "socket connection closed");
+ return G_SOURCE_REMOVE;
+ }
+
+ bytes_received = g_socket_receive (socket, buf, sizeof (buf), NULL, &error);
+ if (bytes_received < 0) {
+ mm_obj_warn (self, "socket i/o failure: %s", error->message);
+ return G_SOURCE_REMOVE;
+ }
+
+ buffer_len = (guint) bytes_received;
+ for (hdr = (struct nlmsghdr *) buf; NLMSG_OK (hdr, buffer_len);
+ NLMSG_NEXT (hdr, buffer_len)) {
+ Transaction *tr;
+ struct nlmsgerr *err;
+
+ if (hdr->nlmsg_type != NLMSG_ERROR)
+ continue;
+
+ tr = g_hash_table_lookup (self->transactions,
+ GUINT_TO_POINTER (hdr->nlmsg_seq));
+ if (!tr)
+ continue;
+
+ err = NLMSG_DATA (buf);
+ transaction_complete (tr, err->error);
+ }
+ return G_SOURCE_CONTINUE;
+}
+
+static gboolean
+setup_netlink_socket (MMNetlink *self,
+ GError **error)
+{
+ gint socket_fd;
+
+ socket_fd = socket (AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+ if (socket_fd < 0) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Failed to create netlink socket");
+ return FALSE;
+ }
+
+ self->socket = g_socket_new_from_fd (socket_fd, error);
+ if (!self->socket) {
+ close (socket_fd);
+ return FALSE;
+ }
+
+ self->source = g_socket_create_source (self->socket,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ NULL);
+ g_source_set_callback (self->source,
+ (GSourceFunc) netlink_message_cb,
+ self,
+ NULL);
+ g_source_attach (self->source, NULL);
+
+ return TRUE;
+}
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("netlink");
+}
+
+/********************************************************************/
+
+static void
+mm_netlink_init (MMNetlink *self)
+{
+ g_autoptr(GError) error = NULL;
+
+ if (!setup_netlink_socket (self, &error)) {
+ mm_obj_warn (self, "couldn't setup netlink socket: %s", error->message);
+ return;
+ }
+
+ self->current_sequence_id = 0;
+ self->transactions = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify) transaction_free);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMNetlink *self = MM_NETLINK (object);
+
+ g_assert (g_hash_table_size (self->transactions) == 0);
+
+ g_clear_pointer (&self->transactions, g_hash_table_unref);
+ if (self->source)
+ g_source_destroy (self->source);
+ g_clear_pointer (&self->source, g_source_unref);
+ g_clear_object (&self->socket);
+
+ G_OBJECT_CLASS (mm_netlink_parent_class)->dispose (object);
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
+mm_netlink_class_init (MMNetlinkClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = dispose;
+}
+
+MM_DEFINE_SINGLETON_GETTER (MMNetlink, mm_netlink_get, MM_TYPE_NETLINK);
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/mm-netlink.h b/src/mm-netlink.h
new file mode 100644
index 00000000..5ced1eaf
--- /dev/null
+++ b/src/mm-netlink.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:
+ *
+ * Basic netlink support from libqmi:
+ * Copyright (C) 2020 Eric Caruso <ejcaruso@chromium.org>
+ * Copyright (C) 2020 Andrew Lassalle <andrewlassalle@chromium.org>
+ *
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_NETLINK_H
+#define MM_NETLINK_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_NETLINK (mm_netlink_get_type ())
+#define MM_NETLINK(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MM_TYPE_NETLINK, MMNetlink))
+#define MM_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MM_TYPE_NETLINK, MMNetlinkClass))
+#define MM_NETLINK_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MM_TYPE_NETLINK, MMNetlinkClass))
+#define MM_IS_NETLINK(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MM_TYPE_NETLINK))
+#define MM_IS_NETLINK_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MM_TYPE_NETLINK))
+
+typedef struct _MMNetlink MMNetlink;
+typedef struct _MMNetlinkClass MMNetlinkClass;
+
+GType mm_netlink_get_type (void) G_GNUC_CONST;
+MMNetlink *mm_netlink_get (void);
+
+void mm_netlink_setlink (MMNetlink *self,
+ guint ifindex,
+ gboolean up,
+ guint mtu,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_netlink_setlink_finish (MMNetlink *self,
+ GAsyncResult *res,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* MM_MODEM_HELPERS_NETLINK_H */
diff --git a/src/mm-plugin-manager.c b/src/mm-plugin-manager.c
index 73aa7494..4e9f3008 100644
--- a/src/mm-plugin-manager.c
+++ b/src/mm-plugin-manager.c
@@ -12,8 +12,8 @@
*
* Copyright (C) 2008 - 2009 Novell, Inc.
* Copyright (C) 2009 - 2012 Red Hat, Inc.
- * Copyright (C) 2011 - 2012 Aleksander Morgado <aleksander@gnu.org>
* Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2011 - 2019 Aleksander Morgado <aleksander@gnu.org>
*/
#include <string.h>
@@ -27,29 +27,32 @@
#include "mm-plugin-manager.h"
#include "mm-plugin.h"
-#include "mm-log.h"
+#include "mm-shared.h"
+#include "mm-utils.h"
+#include "mm-log-object.h"
-/* Default time to defer probing checks */
-#define DEFER_TIMEOUT_SECS 3
-
-/* Time to wait for other ports to appear once the first port is exposed */
-#define MIN_PROBING_TIME_SECS 2
+#define SHARED_PREFIX "libmm-shared"
+#define PLUGIN_PREFIX "libmm-plugin"
-static void initable_iface_init (GInitableIface *iface);
+static void initable_iface_init (GInitableIface *iface);
+static void log_object_iface_init (MMLogObjectInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMPluginManager, mm_plugin_manager, G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
- initable_iface_init))
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
PROP_PLUGIN_DIR,
+ PROP_FILTER,
LAST_PROP
};
struct _MMPluginManagerPrivate {
/* Path to look for plugins */
gchar *plugin_dir;
+ /* Device filter */
+ MMFilter *filter;
/* This list contains all plugins except for the generic one, order is not
* important. It is loaded once when the program starts, and the list is NOT
@@ -57,285 +60,267 @@ struct _MMPluginManagerPrivate {
GList *plugins;
/* Last, the generic plugin. */
MMPlugin *generic;
+
+ /* List of ongoing device support checks */
+ GList *device_contexts;
+
+ /* Full list of subsystems requested by the registered plugins */
+ gchar **subsystems;
};
/*****************************************************************************/
-/* Look for plugin */
+/* Build plugin list for a single port */
-MMPlugin *
-mm_plugin_manager_peek_plugin (MMPluginManager *self,
- const gchar *plugin_name)
+static GList *
+plugin_manager_build_plugins_list (MMPluginManager *self,
+ MMDevice *device,
+ MMKernelDevice *port)
{
+ GList *list = NULL;
GList *l;
+ gboolean supported_found = FALSE;
- if (self->priv->generic && g_str_equal (plugin_name, mm_plugin_get_name (self->priv->generic)))
- return self->priv->generic;
-
- for (l = self->priv->plugins; l; l = g_list_next (l)) {
- MMPlugin *plugin = MM_PLUGIN (l->data);
+ for (l = self->priv->plugins; l && !supported_found; l = g_list_next (l)) {
+ MMPluginSupportsHint hint;
- if (g_str_equal (plugin_name, mm_plugin_get_name (plugin)))
- return plugin;
+ hint = mm_plugin_discard_port_early (MM_PLUGIN (l->data), device, port);
+ switch (hint) {
+ case MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED:
+ /* Fully discard */
+ break;
+ case MM_PLUGIN_SUPPORTS_HINT_MAYBE:
+ /* Maybe supported, add to tail of list */
+ list = g_list_append (list, g_object_ref (l->data));
+ break;
+ case MM_PLUGIN_SUPPORTS_HINT_LIKELY:
+ /* Likely supported, add to head of list */
+ list = g_list_prepend (list, g_object_ref (l->data));
+ break;
+ case MM_PLUGIN_SUPPORTS_HINT_SUPPORTED:
+ /* Really supported, clean existing list and add it alone */
+ if (list) {
+ g_list_free_full (list, g_object_unref);
+ list = NULL;
+ }
+ list = g_list_prepend (list, g_object_ref (l->data));
+ /* This will end the loop as well */
+ supported_found = TRUE;
+ break;
+ default:
+ g_assert_not_reached ();
+ }
}
- return NULL;
+ /* Add the generic plugin at the end of the list */
+ if (self->priv->generic)
+ list = g_list_append (list, g_object_ref (self->priv->generic));
+
+ return list;
}
/*****************************************************************************/
-/* Find device support */
+/* Common context for async operations
+ *
+ * The DeviceContext and PortContext structs are not proper objects, and that
+ * means that they cannot be given as core parameter of GIO async results.
+ * Instead, we'll use the MMPluginManager as that core parameter always, and
+ * we'll pass along a common context with all the remaining details as user
+ * data.
+ */
+
+typedef struct _DeviceContext DeviceContext;
+typedef struct _PortContext PortContext;
+
+static DeviceContext *device_context_ref (DeviceContext *device_context);
+static void device_context_unref (DeviceContext *device_context);
+static PortContext *port_context_ref (PortContext *port_context);
+static void port_context_unref (PortContext *port_context);
typedef struct {
MMPluginManager *self;
+ DeviceContext *device_context;
+ PortContext *port_context;
+ GTask *task;
+} CommonAsyncContext;
+
+static void
+common_async_context_free (CommonAsyncContext *common)
+{
+ if (common->port_context)
+ port_context_unref (common->port_context);
+ if (common->device_context)
+ device_context_unref (common->device_context);
+ if (common->self)
+ g_object_unref (common->self);
+ if (common->task)
+ g_object_unref (common->task);
+ g_slice_free (CommonAsyncContext, common);
+}
+
+static CommonAsyncContext *
+common_async_context_new (MMPluginManager *self,
+ DeviceContext *device_context,
+ PortContext *port_context,
+ GTask *task)
+{
+ CommonAsyncContext *common;
+
+ common = g_slice_new0 (CommonAsyncContext);
+ common->self = (self ? g_object_ref (self) : NULL);
+ common->device_context = (device_context ? device_context_ref (device_context) : NULL);
+ common->port_context = (port_context ? port_context_ref (port_context) : NULL);
+ common->task = (task ? g_object_ref (task) : NULL);
+ return common;
+}
+
+/*****************************************************************************/
+/* Port context */
+
+/* Default time to defer probing checks */
+#define DEFER_TIMEOUT_SECS 3
+
+/*
+ * Port context
+ *
+ * This structure holds all the probing information related to a single port.
+ */
+struct _PortContext {
+ /* Reference counting */
+ volatile gint ref_count;
+ /* The name of the context */
+ gchar *name;
+ /* The device where the port is*/
MMDevice *device;
- GSimpleAsyncResult *result;
- GTimer *timer;
- guint timeout_id;
- gulong grabbed_id;
- gulong released_id;
+ /* The reported kernel port object */
+ MMKernelDevice *port;
- GList *running_probes;
-} FindDeviceSupportContext;
+ /* The operation task */
+ GTask *task;
+ /* Internal ancellable */
+ GCancellable *cancellable;
-typedef struct {
- FindDeviceSupportContext *parent_ctx;
- GUdevDevice *port;
+ /* Timer tracking how much time is required for the port support check */
+ GTimer *timer;
+ /* This list contains all the plugins that have to be tested with a given
+ * port. The list is created once when the task is started, and is never
+ * modified afterwards. */
GList *plugins;
+ /* This is the current plugin being tested. If NULL, there are no more
+ * plugins to try. */
GList *current;
+ /* A best plugin has been found for this port. */
MMPlugin *best_plugin;
+ /* A plugin was suggested for this port. */
MMPlugin *suggested_plugin;
+
+ /* The probe has been deferred */
guint defer_id;
+ /* The probe must be deferred until a result is suggested by other
+ * port probe results (e.g. for WWAN ports). */
gboolean defer_until_suggested;
-} PortProbeContext;
-
-static void port_probe_context_step (PortProbeContext *port_probe_ctx);
-static void suggest_port_probe_result (FindDeviceSupportContext *ctx,
- PortProbeContext *origin,
- MMPlugin *suggested_plugin);
+};
static void
-port_probe_context_free (PortProbeContext *ctx)
+port_context_unref (PortContext *port_context)
{
- g_assert (ctx->defer_id == 0);
-
- if (ctx->best_plugin)
- g_object_unref (ctx->best_plugin);
- if (ctx->suggested_plugin)
- g_object_unref (ctx->suggested_plugin);
- if (ctx->plugins)
- g_list_free_full (ctx->plugins, (GDestroyNotify)g_object_unref);
- g_object_unref (ctx->port);
- g_slice_free (PortProbeContext, ctx);
+ if (g_atomic_int_dec_and_test (&port_context->ref_count)) {
+ /* There must never be a deferred task scheduled for this port */
+ g_assert (port_context->defer_id == 0);
+
+ /* The port support check task must have been completed previously */
+ g_assert (!port_context->task);
+
+ if (port_context->best_plugin)
+ g_object_unref (port_context->best_plugin);
+ if (port_context->suggested_plugin)
+ g_object_unref (port_context->suggested_plugin);
+ if (port_context->plugins)
+ g_list_free_full (port_context->plugins, g_object_unref);
+ if (port_context->cancellable)
+ g_object_unref (port_context->cancellable);
+ g_free (port_context->name);
+ g_timer_destroy (port_context->timer);
+ g_object_unref (port_context->port);
+ g_object_unref (port_context->device);
+ g_slice_free (PortContext, port_context);
+ }
}
-static void
-find_device_support_context_complete_and_free (FindDeviceSupportContext *ctx)
+static PortContext *
+port_context_ref (PortContext *port_context)
{
- g_assert (ctx->timeout_id == 0);
-
- mm_dbg ("(Plugin Manager) [%s] device support check finished in '%lf' seconds",
- mm_device_get_path (ctx->device),
- g_timer_elapsed (ctx->timer, NULL));
- g_timer_destroy (ctx->timer);
-
- /* Set async operation result */
- if (!mm_device_peek_plugin (ctx->device)) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "not supported by any plugin");
- } else {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- }
-
- g_simple_async_result_complete (ctx->result);
-
- g_signal_handler_disconnect (ctx->device, ctx->grabbed_id);
- g_signal_handler_disconnect (ctx->device, ctx->released_id);
-
- g_warn_if_fail (ctx->running_probes == NULL);
-
- g_object_unref (ctx->result);
- g_object_unref (ctx->device);
- g_object_unref (ctx->self);
- g_slice_free (FindDeviceSupportContext, ctx);
+ g_atomic_int_inc (&port_context->ref_count);
+ return port_context;
}
-gboolean
-mm_plugin_manager_find_device_support_finish (MMPluginManager *self,
- GAsyncResult *result,
- GError **error)
+static MMPlugin *
+port_context_run_finish (MMPluginManager *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-port_probe_context_finished (PortProbeContext *port_probe_ctx)
+port_context_complete (PortContext *port_context)
{
- FindDeviceSupportContext *ctx = port_probe_ctx->parent_ctx;
- MMPlugin *device_plugin;
+ MMPluginManager *self;
+ GTask *task;
- /* Get info about the currently scheduled plugin in the device */
- device_plugin = (MMPlugin *)mm_device_peek_plugin (ctx->device);
+ /* If already completed, do nothing */
+ if (!port_context->task)
+ return;
- if (!port_probe_ctx->best_plugin) {
- /* If the port appeared after an already probed port, which decided that
- * the Generic plugin was the best one (which is by default not initially
- * suggested), we'll end up arriving here. Don't ignore it, it may well
- * be a wwan port that we do need to grab. */
- if (device_plugin) {
- mm_dbg ("(Plugin Manager) [%s] assuming port can be handled by the '%s' plugin",
- g_udev_device_get_name (port_probe_ctx->port),
- mm_plugin_get_name (device_plugin));
- } else {
- gboolean cancel_remaining;
- GList *l;
-
- mm_dbg ("(Plugin Manager) [%s] not supported by any plugin",
- g_udev_device_get_name (port_probe_ctx->port));
-
- /* Tell the device to ignore this port */
- mm_device_ignore_port (ctx->device, port_probe_ctx->port);
-
- /* If this is the last valid probe which was running (i.e. the last one
- * not being deferred-until-suggested), cancel all remaining ones. */
- cancel_remaining = TRUE;
- for (l = ctx->running_probes; l; l = g_list_next (l)) {
- PortProbeContext *other = l->data;
-
- /* Do not cancel anything if we find at least one probe which is not
- * waiting for the suggested plugin */
- if (other != port_probe_ctx && !other->defer_until_suggested) {
- cancel_remaining = FALSE;
- break;
- }
- }
+ /* Steal the task from the context, we only will complete once */
+ task = port_context->task;
+ port_context->task = NULL;
- if (cancel_remaining)
- /* Set a NULL suggested plugin, will cancel the probes */
- suggest_port_probe_result (ctx, port_probe_ctx, NULL);
- }
- } else {
- /* Notify the plugin to the device, if this is the first port probing
- * result we got.
- * Also, if the previously suggested plugin was the GENERIC one and now
- * we're reporting a more specific one, use the new one.
- */
- if (!device_plugin ||
- (g_str_equal (mm_plugin_get_name (device_plugin), MM_PLUGIN_GENERIC_NAME) &&
- device_plugin != port_probe_ctx->best_plugin)) {
- /* Only log best plugin if it's not the generic one */
- if (!g_str_equal (mm_plugin_get_name (port_probe_ctx->best_plugin), MM_PLUGIN_GENERIC_NAME))
- mm_dbg ("(Plugin Manager) (%s) [%s]: found best plugin for device (%s)",
- mm_plugin_get_name (port_probe_ctx->best_plugin),
- g_udev_device_get_name (port_probe_ctx->port),
- mm_device_get_path (ctx->device));
-
- mm_device_set_plugin (ctx->device, G_OBJECT (port_probe_ctx->best_plugin));
-
- /* Suggest this plugin also to other port probes */
- suggest_port_probe_result (ctx, port_probe_ctx, port_probe_ctx->best_plugin);
- }
- /* Warn if the best plugin found for this port differs from the
- * best plugin found for the the first probed port */
- else if (!g_str_equal (mm_plugin_get_name (device_plugin),
- mm_plugin_get_name (port_probe_ctx->best_plugin))) {
- /* Icera modems may not reply to the icera probing in all ports. We handle this by
- * checking the forbidden/allowed icera flags in both the current and the expected
- * plugins. If either of these plugins requires icera and the other doesn't, we
- * pick the Icera one as best plugin. */
- gboolean previous_forbidden_icera;
- gboolean previous_allowed_icera;
- gboolean new_forbidden_icera;
- gboolean new_allowed_icera;
-
- g_object_get (device_plugin,
- MM_PLUGIN_ALLOWED_ICERA, &previous_allowed_icera,
- MM_PLUGIN_FORBIDDEN_ICERA, &previous_forbidden_icera,
- NULL);
- g_assert (previous_allowed_icera == FALSE || previous_forbidden_icera == FALSE);
-
- g_object_get (port_probe_ctx->best_plugin,
- MM_PLUGIN_ALLOWED_ICERA, &new_allowed_icera,
- MM_PLUGIN_FORBIDDEN_ICERA, &new_forbidden_icera,
- NULL);
- g_assert (new_allowed_icera == FALSE || new_forbidden_icera == FALSE);
-
- if (previous_allowed_icera && new_forbidden_icera) {
- mm_warn ("(Plugin Manager) (%s): will use plugin '%s' instead of '%s', modem is Icera-capable",
- g_udev_device_get_name (port_probe_ctx->port),
- mm_plugin_get_name (MM_PLUGIN (mm_device_peek_plugin (ctx->device))),
- mm_plugin_get_name (port_probe_ctx->best_plugin));
- } else if (new_allowed_icera && previous_forbidden_icera) {
- mm_warn ("(Plugin Manager) (%s): overriding previously selected device plugin '%s' with '%s', modem is Icera-capable",
- g_udev_device_get_name (port_probe_ctx->port),
- mm_plugin_get_name (MM_PLUGIN (mm_device_peek_plugin (ctx->device))),
- mm_plugin_get_name (port_probe_ctx->best_plugin));
- mm_device_set_plugin (ctx->device, G_OBJECT (port_probe_ctx->best_plugin));
- } else {
- mm_warn ("(Plugin Manager) (%s): plugin mismatch error (expected: '%s', got: '%s')",
- g_udev_device_get_name (port_probe_ctx->port),
- mm_plugin_get_name (MM_PLUGIN (mm_device_peek_plugin (ctx->device))),
- mm_plugin_get_name (port_probe_ctx->best_plugin));
- }
- }
- }
+ /* Log about the time required to complete the checks */
+ self = g_task_get_source_object (task);
+ mm_obj_dbg (self, "task %s: finished in '%lf' seconds",
+ port_context->name, g_timer_elapsed (port_context->timer, NULL));
- /* Remove us from the list of running probes */
- g_assert (g_list_find (ctx->running_probes, port_probe_ctx) != NULL);
- ctx->running_probes = g_list_remove (ctx->running_probes, port_probe_ctx);
+ if (!port_context->best_plugin)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Unsupported");
+ else
+ g_task_return_pointer (task, g_object_ref (port_context->best_plugin), g_object_unref);
+ g_object_unref (task);
+}
- /* If there are running probes around, wait for them to finish */
- if (ctx->running_probes != NULL) {
- GList *l;
- GString *s = NULL;
- guint i = 0;
+static void port_context_next (PortContext *port_context);
- for (l = ctx->running_probes; l; l = g_list_next (l)) {
- const gchar *portname = g_udev_device_get_name (((PortProbeContext *)l->data)->port);
+static void
+port_context_supported (PortContext *port_context,
+ MMPlugin *plugin)
+{
+ MMPluginManager *self;
- if (!s)
- s = g_string_new (portname);
- else
- g_string_append_printf (s, ", %s", portname);
- i++;
- }
+ g_assert (plugin);
+ self = g_task_get_source_object (port_context->task);
- mm_dbg ("(Plugin Manager) '%s' port probe finished, still %u running probes in this device (%s)",
- g_udev_device_get_name (port_probe_ctx->port), i, s->str);
- g_string_free (s, TRUE);
- }
- /* If we didn't use the minimum probing time, wait for it to finish */
- else if (ctx->timeout_id > 0) {
- mm_dbg ("(Plugin Manager) '%s' port probe finished, last one in device, "
- "but minimum probing time not consumed yet ('%lf' seconds elapsed)",
- g_udev_device_get_name (port_probe_ctx->port),
- g_timer_elapsed (ctx->timer, NULL));
- } else {
- mm_dbg ("(Plugin Manager) '%s' port probe finished, last one in device",
- g_udev_device_get_name (port_probe_ctx->port));
- /* If we just finished the last running probe, we can now finish the device
- * support check */
- find_device_support_context_complete_and_free (ctx);
- }
+ mm_obj_dbg (self, "task %s: found best plugin for port (%s)",
+ port_context->name, mm_plugin_get_name (plugin));
- port_probe_context_free (port_probe_ctx);
+ /* Found a best plugin, store it to return it */
+ port_context->best_plugin = g_object_ref (plugin);
+ port_context_complete (port_context);
}
static gboolean
-deferred_support_check_idle (PortProbeContext *port_probe_ctx)
+port_context_defer_ready (PortContext *port_context)
{
- port_probe_ctx->defer_id = 0;
- port_probe_context_step (port_probe_ctx);
- return FALSE;
+ port_context->defer_id = 0;
+ port_context_next (port_context);
+ return G_SOURCE_REMOVE;
}
static void
-suggest_single_port_probe_result (PortProbeContext *target_port_probe_ctx,
- MMPlugin *suggested_plugin,
- gboolean reschedule_deferred)
+port_context_set_suggestion (PortContext *port_context,
+ MMPlugin *suggested_plugin)
{
- gboolean forbidden_icera;
+ MMPluginManager *self;
+ gboolean forbidden_icera;
/* Plugin suggestions serve two different purposes here:
* 1) Finish all the probes which were deferred until suggested.
@@ -346,36 +331,34 @@ suggest_single_port_probe_result (PortProbeContext *target_port_probe_ctx,
* the deferred until suggested probes get finished.
*/
- if (target_port_probe_ctx->best_plugin || target_port_probe_ctx->suggested_plugin)
+ /* Do nothing if already best plugin found, or if a plugin has already been
+ * suggested before */
+ if (port_context->best_plugin || port_context->suggested_plugin)
return;
+ /* There may not be a task at this point, so be gentle */
+ self = port_context->task ? g_task_get_source_object (port_context->task) : NULL;
+
/* Complete tasks which were deferred until suggested */
- if (target_port_probe_ctx->defer_until_suggested) {
+ if (port_context->defer_until_suggested) {
/* Reset the defer until suggested flag; we consider this
* cancelled probe completed now. */
- target_port_probe_ctx->defer_until_suggested = FALSE;
+ port_context->defer_until_suggested = FALSE;
if (suggested_plugin) {
- mm_dbg ("(Plugin Manager) (%s) [%s] deferred task completed, got suggested plugin",
- mm_plugin_get_name (suggested_plugin),
- g_udev_device_get_name (target_port_probe_ctx->port));
+ mm_obj_dbg (self, "task %s: deferred task completed, got suggested plugin (%s)",
+ port_context->name, mm_plugin_get_name (suggested_plugin));
/* Advance to the suggested plugin and re-check support there */
- target_port_probe_ctx->suggested_plugin = g_object_ref (suggested_plugin);
- target_port_probe_ctx->current = g_list_find (target_port_probe_ctx->current,
- target_port_probe_ctx->suggested_plugin);
- } else {
- mm_dbg ("(Plugin Manager) [%s] deferred task cancelled, no suggested plugin",
- g_udev_device_get_name (target_port_probe_ctx->port));
- target_port_probe_ctx->best_plugin = NULL;
- target_port_probe_ctx->current = NULL;
+ port_context->suggested_plugin = g_object_ref (suggested_plugin);
+ port_context->current = g_list_find (port_context->current, port_context->suggested_plugin);
+ /* Schedule checking support */
+ g_assert (port_context->defer_id == 0);
+ port_context->defer_id = g_idle_add ((GSourceFunc) port_context_defer_ready, port_context);
+ return;
}
- /* Schedule checking support, which will end the operation */
- if (reschedule_deferred) {
- g_assert (target_port_probe_ctx->defer_id == 0);
- target_port_probe_ctx->defer_id = g_idle_add ((GSourceFunc)deferred_support_check_idle,
- target_port_probe_ctx);
- }
+ mm_obj_dbg (self, "task %s: deferred task completed, no suggested plugin", port_context->name);
+ port_context_complete (port_context);
return;
}
@@ -384,7 +367,7 @@ suggest_single_port_probe_result (PortProbeContext *target_port_probe_ctx,
return;
/* The GENERIC plugin is NEVER suggested to others */
- if (g_str_equal (mm_plugin_get_name (suggested_plugin), MM_PLUGIN_GENERIC_NAME))
+ if (mm_plugin_is_generic (suggested_plugin))
return;
/* If the plugin has MM_PLUGIN_FORBIDDEN_ICERA set, we do *not* suggest
@@ -402,424 +385,1332 @@ suggest_single_port_probe_result (PortProbeContext *target_port_probe_ctx,
* should run its probing independently, and we'll later decide
* which result applies to the whole device.
*/
- mm_dbg ("(Plugin Manager) (%s) [%s] suggested plugin for port",
- mm_plugin_get_name (suggested_plugin),
- g_udev_device_get_name (target_port_probe_ctx->port));
- target_port_probe_ctx->suggested_plugin = g_object_ref (suggested_plugin);
+ mm_obj_dbg (self, "task %s: got suggested plugin (%s)",
+ port_context->name, mm_plugin_get_name (suggested_plugin));
+ port_context->suggested_plugin = g_object_ref (suggested_plugin);
}
static void
-suggest_port_probe_result (FindDeviceSupportContext *ctx,
- PortProbeContext *origin,
- MMPlugin *suggested_plugin)
+port_context_unsupported (PortContext *port_context,
+ MMPlugin *plugin)
{
- GList *l;
+ MMPluginManager *self;
- for (l = ctx->running_probes; l; l = g_list_next (l)) {
- PortProbeContext *port_probe_ctx = l->data;
+ g_assert (plugin);
+ self = g_task_get_source_object (port_context->task);
- if (port_probe_ctx != origin)
- suggest_single_port_probe_result (port_probe_ctx, suggested_plugin, TRUE);
+ /* If there is no suggested plugin, go on to the next one */
+ if (!port_context->suggested_plugin) {
+ port_context->current = g_list_next (port_context->current);
+ port_context_next (port_context);
+ return;
}
+
+ /* If the plugin that just completed the support check claims
+ * not to support this port, but this plugin is clearly the
+ * right plugin since it claimed this port's physical modem,
+ * just cancel the port probing and avoid more tests.
+ */
+ if (port_context->suggested_plugin == plugin) {
+ mm_obj_dbg (self, "task %s: ignoring port unsupported by physical modem's plugin",
+ port_context->name);
+ port_context_complete (port_context);
+ return;
+ }
+
+ /* The last plugin we tried is NOT the one we got suggested, so
+ * directly check support with the suggested plugin. If we
+ * already checked its support, it won't be checked again. */
+ port_context->current = g_list_find (port_context->current, port_context->suggested_plugin);
+ port_context_next (port_context);
+}
+
+static void
+port_context_defer (PortContext *port_context)
+{
+ MMPluginManager *self;
+
+ self = g_task_get_source_object (port_context->task);
+
+ /* Try with the suggested one after being deferred */
+ if (port_context->suggested_plugin) {
+ mm_obj_dbg (self, "task %s: deferring support check (%s suggested)",
+ port_context->name, mm_plugin_get_name (MM_PLUGIN (port_context->suggested_plugin)));
+ port_context->current = g_list_find (port_context->current, port_context->suggested_plugin);
+ } else
+ mm_obj_dbg (self, "task %s: deferring support check", port_context->name);
+
+ /* Schedule checking support.
+ *
+ * In this case we don't pass a port context reference because we're able
+ * to fully cancel the timeout ourselves. */
+ port_context->defer_id = g_timeout_add_seconds (DEFER_TIMEOUT_SECS,
+ (GSourceFunc) port_context_defer_ready,
+ port_context);
}
static void
-plugin_supports_port_ready (MMPlugin *plugin,
- GAsyncResult *result,
- PortProbeContext *port_probe_ctx)
+port_context_defer_until_suggested (PortContext *port_context,
+ MMPlugin *plugin)
{
- MMPluginSupportsResult support_result;
- GError *error = NULL;
+ MMPluginManager *self;
- /* Get supports check results */
- support_result = mm_plugin_supports_port_finish (plugin, result, &error);
+ g_assert (plugin);
+ self = g_task_get_source_object (port_context->task);
+
+ /* If we arrived here and we already have a plugin suggested, use it */
+ if (port_context->suggested_plugin) {
+ /* We can finish this context */
+ if (port_context->suggested_plugin == plugin) {
+ mm_obj_dbg (self, "task %s: completed, got suggested plugin (%s)",
+ port_context->name, mm_plugin_get_name (port_context->suggested_plugin));
+ /* Store best plugin and end operation */
+ port_context->best_plugin = g_object_ref (port_context->suggested_plugin);
+ port_context_complete (port_context);
+ return;
+ }
+
+ /* Recheck support in deferred task */
+ mm_obj_dbg (self, "task %s: re-checking support on deferred task, got suggested plugin (%s)",
+ port_context->name, mm_plugin_get_name (port_context->suggested_plugin));
+ port_context->current = g_list_find (port_context->current, port_context->suggested_plugin);
+ port_context_next (port_context);
+ return;
+ }
+
+ /* We are deferred until a suggested plugin is given. If last supports task
+ * of a given device is finished without finding a best plugin, this task
+ * will get finished reporting unsupported. */
+ mm_obj_dbg (self, "task %s: deferring support check until result suggested", port_context->name);
+ port_context->defer_until_suggested = TRUE;
+}
+static void
+plugin_supports_port_ready (MMPlugin *plugin,
+ GAsyncResult *res,
+ PortContext *port_context)
+{
+ MMPluginManager *self;
+ MMPluginSupportsResult support_result;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (port_context->task);
+
+ /* Get supports check results */
+ support_result = mm_plugin_supports_port_finish (plugin, res, &error);
if (error) {
- mm_warn ("(Plugin Manager) (%s) [%s] error when checking support: '%s'",
- mm_plugin_get_name (plugin),
- g_udev_device_get_name (port_probe_ctx->port),
- error->message);
+ g_assert_cmpuint (support_result, ==, MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
+ mm_obj_warn (self, "task %s: error when checking support with plugin '%s': %s",
+ port_context->name, mm_plugin_get_name (plugin), error->message);
g_error_free (error);
}
switch (support_result) {
case MM_PLUGIN_SUPPORTS_PORT_SUPPORTED:
- /* Found a best plugin */
- port_probe_ctx->best_plugin = g_object_ref (plugin);
-
- if (port_probe_ctx->suggested_plugin &&
- port_probe_ctx->suggested_plugin != plugin) {
- /* The last plugin we tried said it supported this port, but it
- * doesn't correspond with the one we're being suggested. */
- mm_dbg ("(Plugin Manager) (%s) [%s] found best plugin for port, "
- "but not the same as the suggested one (%s)",
- mm_plugin_get_name (port_probe_ctx->best_plugin),
- g_udev_device_get_name (port_probe_ctx->port),
- mm_plugin_get_name (port_probe_ctx->suggested_plugin));
- } else {
- mm_dbg ("(Plugin Manager) (%s) [%s] found best plugin for port",
- mm_plugin_get_name (port_probe_ctx->best_plugin),
- g_udev_device_get_name (port_probe_ctx->port));
- }
- port_probe_ctx->current = NULL;
+ port_context_supported (port_context, plugin);
+ break;
+ case MM_PLUGIN_SUPPORTS_PORT_UNKNOWN:
+ case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
+ port_context_unsupported (port_context, plugin);
+ break;
+ case MM_PLUGIN_SUPPORTS_PORT_DEFER:
+ port_context_defer (port_context);
+ break;
+ case MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED:
+ port_context_defer_until_suggested (port_context, plugin);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
- /* Step, which will end the port probe operation */
- port_probe_context_step (port_probe_ctx);
- return;
+ /* We received a full reference, to make sure the context was always
+ * valid during the async call */
+ port_context_unref (port_context);
+}
+static void
+port_context_next (PortContext *port_context)
+{
+ MMPluginManager *self;
+ MMPlugin *plugin;
- case MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED:
- if (port_probe_ctx->suggested_plugin) {
- if (port_probe_ctx->suggested_plugin == plugin) {
- /* If the plugin that just completed the support check claims
- * not to support this port, but this plugin is clearly the
- * right plugin since it claimed this port's physical modem,
- * just drop the port.
- */
- mm_dbg ("(Plugin Manager) [%s] ignoring port unsupported by physical modem's plugin",
- g_udev_device_get_name (port_probe_ctx->port));
- port_probe_ctx->best_plugin = NULL;
- port_probe_ctx->current = NULL;
- } else {
- /* The last plugin we tried is NOT the one we got suggested, so
- * directly check support with the suggested plugin. If we
- * already checked its support, it won't be checked again. */
- port_probe_ctx->current = g_list_find (port_probe_ctx->current,
- port_probe_ctx->suggested_plugin);
- }
- } else {
- /* If the plugin knows it doesn't support the modem, just keep on
- * checking the next plugin.
- */
- port_probe_ctx->current = g_list_next (port_probe_ctx->current);
- }
+ self = g_task_get_source_object (port_context->task);
+
+ /* If we're cancelled, done */
+ if (g_cancellable_is_cancelled (port_context->cancellable)) {
+ port_context_complete (port_context);
+ return;
+ }
- /* Step */
- port_probe_context_step (port_probe_ctx);
+ /* Already checked all plugins? */
+ if (!port_context->current) {
+ port_context_complete (port_context);
return;
+ }
+ /* Ask the current plugin to check support of this port.
+ *
+ * A full new reference to the port context is given as user data to the
+ * async method because we want to make sure the context is still valid
+ * once the method finishes. */
+ plugin = MM_PLUGIN (port_context->current->data);
+ mm_obj_dbg (self, "task %s: checking with plugin '%s'",
+ port_context->name, mm_plugin_get_name (plugin));
+ mm_plugin_supports_port (plugin,
+ port_context->device,
+ port_context->port,
+ port_context->cancellable,
+ (GAsyncReadyCallback) plugin_supports_port_ready,
+ port_context_ref (port_context));
+}
- case MM_PLUGIN_SUPPORTS_PORT_DEFER:
- /* Try with the suggested one after being deferred */
- if (port_probe_ctx->suggested_plugin) {
- mm_dbg ("(Plugin Manager) (%s) [%s] deferring support check, suggested: %s",
- mm_plugin_get_name (MM_PLUGIN (port_probe_ctx->current->data)),
- g_udev_device_get_name (port_probe_ctx->port),
- mm_plugin_get_name (MM_PLUGIN (port_probe_ctx->suggested_plugin)));
- port_probe_ctx->current = g_list_find (port_probe_ctx->current,
- port_probe_ctx->suggested_plugin);
- } else {
- mm_dbg ("(Plugin Manager) (%s) [%s] deferring support check",
- mm_plugin_get_name (MM_PLUGIN (port_probe_ctx->current->data)),
- g_udev_device_get_name (port_probe_ctx->port));
+static gboolean
+port_context_cancel (PortContext *port_context)
+{
+ MMPluginManager *self;
+
+ /* Port context cancellation, which only makes sense if the context is
+ * actually being run, so just exit if it isn't. */
+ if (!port_context->task)
+ return FALSE;
+
+ /* If cancelled already, do nothing */
+ if (g_cancellable_is_cancelled (port_context->cancellable))
+ return FALSE;
+
+ self = g_task_get_source_object (port_context->task);
+ mm_obj_dbg (self, "task %s: cancellation requested", port_context->name);
+
+ /* Make sure we hold a port context reference while cancelling, as the
+ * cancellable signal handlers may end up unref-ing our last reference
+ * otherwise. */
+ port_context_ref (port_context);
+ {
+ /* The port context is cancelled now */
+ g_cancellable_cancel (port_context->cancellable);
+
+ /* If the task was deferred, we can cancel and complete it right away */
+ if (port_context->defer_id) {
+ g_source_remove (port_context->defer_id);
+ port_context->defer_id = 0;
+ port_context_complete (port_context);
}
+ /* If the task was deferred until a result is suggested, we can also
+ * complete it right away */
+ else if (port_context->defer_until_suggested)
+ port_context_complete (port_context);
+ /* else, the task may be currently checking support with a given plugin */
+ }
+ port_context_unref (port_context);
- /* Schedule checking support */
- port_probe_ctx->defer_id = g_timeout_add_seconds (DEFER_TIMEOUT_SECS,
- (GSourceFunc)deferred_support_check_idle,
- port_probe_ctx);
- return;
+ return TRUE;
+}
+static void
+port_context_run (MMPluginManager *self,
+ PortContext *port_context,
+ GList *plugins,
+ MMPlugin *suggested,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_assert (!port_context->task);
+ g_assert (!port_context->defer_id);
+ g_assert (!port_context->plugins);
+ g_assert (!port_context->current);
+ g_assert (!port_context->suggested_plugin);
+
+ /* Setup plugins to probe and first one to check. */
+ port_context->plugins = g_list_copy_deep (plugins, (GCopyFunc) g_object_ref, NULL);
+ port_context->current = port_context->plugins;
+
+ /* If we got one suggested, it will be the first one */
+ if (suggested) {
+ port_context->suggested_plugin = g_object_ref (suggested);
+ port_context->current = g_list_find (port_context->current, port_context->suggested_plugin);
+ if (!port_context->current)
+ mm_obj_warn (self, "task %s: suggested plugin (%s) not among the ones to test",
+ port_context->name, mm_plugin_get_name (suggested));
+ }
- case MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED:
- /* If we're deferred until suggested, but there is already a plugin
- * suggested in the parent device context, grab it. This may happen if
- * e.g. a wwan interface arrives *after* a port has already been probed.
- */
- if (!port_probe_ctx->suggested_plugin) {
- MMPlugin *device_plugin;
-
- /* Get info about the currently scheduled plugin in the device */
- device_plugin = (MMPlugin *)mm_device_peek_plugin (port_probe_ctx->parent_ctx->device);
- if (device_plugin) {
- mm_dbg ("(Plugin Manager) (%s) [%s] task deferred until result suggested and got suggested plugin",
- mm_plugin_get_name (device_plugin),
- g_udev_device_get_name (port_probe_ctx->port));
- /* Flag it as deferred before suggesting probe result */
- port_probe_ctx->defer_until_suggested = TRUE;
- suggest_single_port_probe_result (port_probe_ctx, device_plugin, FALSE);
+ /* Log the list of plugins found and specify which are the ones that are going
+ * to be run */
+ {
+ gboolean suggested_found = FALSE;
+ GList *l;
+
+ mm_obj_dbg (self, "task %s: found '%u' plugins to try",
+ port_context->name, g_list_length (port_context->plugins));
+
+ for (l = port_context->plugins; l; l = g_list_next (l)) {
+ MMPlugin *plugin;
+
+ plugin = MM_PLUGIN (l->data);
+ if (suggested_found) {
+ mm_obj_dbg (self, "task %s: may try with plugin '%s'",
+ port_context->name, mm_plugin_get_name (plugin));
+ continue;
+ }
+ if (suggested && l == port_context->current) {
+ suggested_found = TRUE;
+ mm_obj_dbg (self, "task %s: will try with plugin '%s' (suggested)",
+ port_context->name, mm_plugin_get_name (plugin));
+ continue;
}
+ if (suggested && !suggested_found) {
+ mm_obj_dbg (self, "task %s: won't try with plugin '%s' (skipped)",
+ port_context->name, mm_plugin_get_name (plugin));
+ continue;
+ }
+ mm_obj_dbg (self, "task %s: will try with plugin '%s'",
+ port_context->name, mm_plugin_get_name (plugin));
}
+ }
- /* If we arrived here and we already have a plugin suggested, use it */
- if (port_probe_ctx->suggested_plugin) {
- if (port_probe_ctx->suggested_plugin == plugin) {
- mm_dbg ("(Plugin Manager) (%s) [%s] task completed, got suggested plugin",
- mm_plugin_get_name (port_probe_ctx->suggested_plugin),
- g_udev_device_get_name (port_probe_ctx->port));
- port_probe_ctx->best_plugin = g_object_ref (port_probe_ctx->suggested_plugin);
- port_probe_ctx->current = NULL;
- } else {
- mm_dbg ("(Plugin Manager) (%s) [%s] re-checking support on deferred task, got suggested plugin",
- mm_plugin_get_name (port_probe_ctx->suggested_plugin),
- g_udev_device_get_name (port_probe_ctx->port));
- port_probe_ctx->current = g_list_find (port_probe_ctx->current,
- port_probe_ctx->suggested_plugin);
- }
+ /* The full port context is now cancellable. We pass this cancellable also
+ * to the inner GTask, so that if we're cancelled we always return a
+ * cancellation error, regardless of what the standard logic does. */
+ port_context->cancellable = g_cancellable_new ();
- /* Schedule checking support, which will end the operation */
- port_probe_context_step (port_probe_ctx);
- return;
- }
+ /* Create an inner task for the port context. The result we expect is the
+ * best plugin found for the port. */
+ port_context->task = g_task_new (self, port_context->cancellable, callback, user_data);
- /* We are deferred until a suggested plugin is given. If last supports task
- * of a given device is finished without finding a best plugin, this task
- * will get finished reporting unsupported. */
- mm_dbg ("(Plugin Manager) [%s] deferring support check until result suggested",
- g_udev_device_get_name (port_probe_ctx->port));
- port_probe_ctx->defer_until_suggested = TRUE;
- return;
+ mm_obj_dbg (self, "task %s: started", port_context->name);
+
+ /* Go probe with the first plugin */
+ port_context_next (port_context);
+}
+
+static PortContext *
+port_context_new (MMPluginManager *self,
+ const gchar *parent_name,
+ MMDevice *device,
+ MMKernelDevice *port)
+{
+ PortContext *port_context;
+
+ port_context = g_slice_new0 (PortContext);
+ port_context->ref_count = 1;
+ port_context->device = g_object_ref (device);
+ port_context->port = g_object_ref (port);
+ port_context->timer = g_timer_new ();
+
+ /* Set context name */
+ port_context->name = g_strdup_printf ("%s,%s", parent_name, mm_kernel_device_get_name (port));
+
+ return port_context;
+}
+
+/*****************************************************************************/
+/* Device context */
+
+/* Time to wait for ports to appear before starting to probe the first one */
+#define MIN_WAIT_TIME_MSECS 1500
+
+/* Time to wait for other ports to appear once the first port is exposed
+ * (needs to be > MIN_WAIT_TIME_MSECS!!) */
+#define MIN_PROBING_TIME_MSECS 2500
+
+/* Additional time to wait for other ports to appear after the last port is
+ * exposed in the system. */
+#define EXTRA_PROBING_TIME_MSECS 1500
+
+/* The wait time we define must always be less than the probing time */
+G_STATIC_ASSERT (MIN_WAIT_TIME_MSECS < MIN_PROBING_TIME_MSECS);
+
+/*
+ * Device context
+ *
+ * This structure holds all the information related to a single device. This
+ * information includes references to all port contexts generated in the device,
+ * as well as a reference to the parent plugin manager object and the async
+ * task to complete when finished.
+ */
+struct _DeviceContext {
+ /* Reference counting */
+ volatile gint ref_count;
+ /* The name of the context */
+ gchar *name;
+ /* The plugin manager */
+ MMPluginManager *self;
+ /* The device for which we're looking support */
+ MMDevice *device;
+
+ /* The operation task */
+ GTask *task;
+ /* Internal cancellable */
+ GCancellable *cancellable;
+
+ /* Timer tracking how much time is required for the device support check */
+ GTimer *timer;
+
+ /* The best plugin at a given moment. Once the last port task finishes, this
+ * will be the one being returned in the async result */
+ MMPlugin *best_plugin;
+
+ /* Minimum wait time. No port probing can start before this timeout expires.
+ * Once the timeout is expired, the id is reset to 0. */
+ guint min_wait_time_id;
+ /* Port support check contexts waiting to be run after min wait time */
+ GList *wait_port_contexts;
+
+ /* Minimum probing time, which is a timeout initialized as soon as the first
+ * port is added to the device context. The device support check task cannot
+ * be finished before this timeout expires. Once the timeout is expired, the
+ * id is reset to 0. */
+ guint min_probing_time_id;
+
+ /* Extra probing time, which is a timeout refreshed every time a new port
+ * is added to the device context. The device support check task cannot be
+ * finished before this timeout expires. Once the timeout is expired, the id
+ * is reset to 0. */
+ guint extra_probing_time_id;
+
+ /* Signal connection ids for the grabbed/released signals from the device.
+ * These are the signals that will give us notifications of what ports are
+ * available (or suddenly unavailable) in the device. */
+ gulong grabbed_id;
+ gulong released_id;
+
+ /* Port support check contexts being run */
+ GList *port_contexts;
+};
+
+static void
+device_context_unref (DeviceContext *device_context)
+{
+ if (g_atomic_int_dec_and_test (&device_context->ref_count)) {
+ /* When the last reference is gone there must be no source scheduled and no
+ * pending port tasks. */
+ g_assert (!device_context->grabbed_id);
+ g_assert (!device_context->released_id);
+ g_assert (!device_context->min_wait_time_id);
+ g_assert (!device_context->min_probing_time_id);
+ g_assert (!device_context->extra_probing_time_id);
+ g_assert (!device_context->port_contexts);
+
+ /* The device support check task must have been completed previously */
+ g_assert (!device_context->task);
+
+ g_free (device_context->name);
+ g_timer_destroy (device_context->timer);
+ if (device_context->cancellable)
+ g_object_unref (device_context->cancellable);
+ if (device_context->best_plugin)
+ g_object_unref (device_context->best_plugin);
+ g_object_unref (device_context->device);
+ g_object_unref (device_context->self);
+ g_slice_free (DeviceContext, device_context);
+ }
+}
+
+static DeviceContext *
+device_context_ref (DeviceContext *device_context)
+{
+ g_atomic_int_inc (&device_context->ref_count);
+ return device_context;
+}
+
+static PortContext *
+device_context_peek_running_port_context (DeviceContext *device_context,
+ MMKernelDevice *port)
+{
+ GList *l;
+
+ for (l = device_context->port_contexts; l; l = g_list_next (l)) {
+ PortContext *port_context;
+
+ port_context = (PortContext *)(l->data);
+ if ((port_context->port == port) ||
+ (!g_strcmp0 (mm_kernel_device_get_name (port_context->port), mm_kernel_device_get_name (port))))
+ return port_context;
+ }
+ return NULL;
+}
+
+static PortContext *
+device_context_peek_waiting_port_context (DeviceContext *device_context,
+ MMKernelDevice *port)
+{
+ GList *l;
+
+ for (l = device_context->wait_port_contexts; l; l = g_list_next (l)) {
+ PortContext *port_context;
+
+ port_context = (PortContext *)(l->data);
+ if ((port_context->port == port) ||
+ (!g_strcmp0 (mm_kernel_device_get_name (port_context->port), mm_kernel_device_get_name (port))))
+ return port_context;
}
+ return NULL;
+}
+
+static MMPlugin *
+device_context_run_finish (MMPluginManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_PLUGIN (g_task_propagate_pointer (G_TASK (res), error));
}
static void
-port_probe_context_step (PortProbeContext *port_probe_ctx)
+device_context_complete (DeviceContext *device_context)
{
- FindDeviceSupportContext *ctx = port_probe_ctx->parent_ctx;
+ MMPluginManager *self;
+ GTask *task;
- /* Already checked all plugins? */
- if (!port_probe_ctx->current) {
- port_probe_context_finished (port_probe_ctx);
+ self = g_task_get_source_object (device_context->task);
+
+ /* If the context is completed before the 2500ms minimum probing time, we need to wait
+ * until that happens, so that we give enough time to udev/hotplug to report the
+ * new port additions. */
+ if (device_context->min_probing_time_id) {
+ mm_obj_dbg (self, "task %s: all port probings completed, but not reached min probing time yet",
+ device_context->name);
return;
}
- /* Ask the current plugin to check support of this port */
- mm_plugin_supports_port (MM_PLUGIN (port_probe_ctx->current->data),
- ctx->device,
- port_probe_ctx->port,
- (GAsyncReadyCallback)plugin_supports_port_ready,
- port_probe_ctx);
+ /* If the context is completed less than 1500ms before the last port was exposed,
+ * wait some more. */
+ if (device_context->extra_probing_time_id) {
+ mm_obj_dbg (self, "task %s: all port probings completed, but not reached extra probing time yet",
+ device_context->name);
+ return;
+ }
+
+ /* Steal the task from the context */
+ g_assert (device_context->task);
+ task = device_context->task;
+ device_context->task = NULL;
+
+ /* Log about the time required to complete the checks */
+ mm_obj_dbg (self, "task %s: finished in '%lf' seconds",
+ device_context->name, g_timer_elapsed (device_context->timer, NULL));
+
+ /* Remove signal handlers */
+ if (device_context->grabbed_id) {
+ g_signal_handler_disconnect (device_context->device, device_context->grabbed_id);
+ device_context->grabbed_id = 0;
+ }
+ if (device_context->released_id) {
+ g_signal_handler_disconnect (device_context->device, device_context->released_id);
+ device_context->released_id = 0;
+ }
+
+ /* On completion, the minimum wait time must have been already elapsed */
+ g_assert (!device_context->min_wait_time_id);
+
+ /* Task completion */
+ if (!device_context->best_plugin)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "not supported by any plugin");
+ else
+ g_task_return_pointer (task, g_object_ref (device_context->best_plugin), g_object_unref);
+ g_object_unref (task);
}
-static GList *
-build_plugins_list (MMPluginManager *self,
- MMDevice *device,
- GUdevDevice *port)
+static void
+device_context_suggest_plugin (DeviceContext *device_context,
+ PortContext *port_context,
+ MMPlugin *suggested_plugin)
{
- GList *list = NULL;
GList *l;
- gboolean supported_found = FALSE;
+ GList *listdup;
+
+ /* If the suggested plugin is NULL, we'll propagate the suggestion only if all
+ * the port contexts are deferred until suggested. */
+ if (!suggested_plugin) {
+ for (l = device_context->port_contexts; l; l = g_list_next (l)) {
+ PortContext *other_port_context = (PortContext *)(l->data);
+
+ /* Do not propagate NULL if we find at least one probe which is not
+ * waiting for the suggested plugin */
+ if (other_port_context != port_context && !other_port_context->defer_until_suggested)
+ return;
+ }
+ }
- for (l = self->priv->plugins; l && !supported_found; l = g_list_next (l)) {
- MMPluginSupportsHint hint;
+ /* Do the suggestion propagation.
+ * Shallow copy, just so that we can iterate safely without worrying about the
+ * original list being modified while hte completions happen */
+ listdup = g_list_copy (device_context->port_contexts);
+ for (l = listdup; l; l = g_list_next (l))
+ port_context_set_suggestion ((PortContext *)(l->data), suggested_plugin);
+ g_list_free (listdup);
+}
- hint = mm_plugin_discard_port_early (MM_PLUGIN (l->data), device, port);
- switch (hint) {
- case MM_PLUGIN_SUPPORTS_HINT_UNSUPPORTED:
- /* Fully discard */
- break;
- case MM_PLUGIN_SUPPORTS_HINT_MAYBE:
- /* Maybe supported, add to tail of list */
- list = g_list_append (list, g_object_ref (l->data));
- break;
- case MM_PLUGIN_SUPPORTS_HINT_LIKELY:
- /* Likely supported, add to head of list */
- list = g_list_prepend (list, g_object_ref (l->data));
- break;
- case MM_PLUGIN_SUPPORTS_HINT_SUPPORTED:
- /* Really supported, clean existing list and add it alone */
- if (list) {
- g_list_free_full (list, (GDestroyNotify)g_object_unref);
- list = NULL;
- }
- list = g_list_prepend (list, g_object_ref (l->data));
- /* This will end the loop as well */
- supported_found = TRUE;
- break;
- default:
- g_assert_not_reached();
+static void
+device_context_set_best_plugin (DeviceContext *device_context,
+ PortContext *port_context,
+ MMPlugin *best_plugin)
+{
+ MMPluginManager *self;
+
+ self = g_task_get_source_object (device_context->task);
+
+ if (!best_plugin) {
+ /* If the port appeared after an already probed port, which decided that
+ * the Generic plugin was the best one (which is by default not initially
+ * suggested), we'll end up arriving here. Don't ignore it, it may well
+ * be a wwan port that we do need to grab. */
+ if (device_context->best_plugin) {
+ mm_obj_dbg (self, "task %s: assuming port can be handled by the '%s' plugin",
+ port_context->name, mm_plugin_get_name (device_context->best_plugin));
+ return;
}
+
+ /* Unsupported error, this is generic when we cannot find a plugin */
+ mm_obj_dbg (self, "task %s: not supported by any plugin" ,
+ port_context->name);
+
+ /* Tell the device to ignore this port */
+ mm_device_ignore_port (device_context->device, port_context->port);
+
+ /* If this is the last valid probe which was running (i.e. the last one
+ * not being deferred-until-suggested), cancel all remaining ones. */
+ device_context_suggest_plugin (device_context, port_context, NULL);
+ return;
}
- /* Add the generic plugin at the end of the list */
- if (self->priv->generic)
- list = g_list_append (list, g_object_ref (self->priv->generic));
+ /* Store the plugin as the best one in the device if this is the first
+ * result we got. Also, if the previously suggested plugin was the GENERIC
+ * one and now we're reporting a more specific one, use the new one.
+ */
+ if (!device_context->best_plugin ||
+ (mm_plugin_is_generic (device_context->best_plugin) &&
+ device_context->best_plugin != best_plugin)) {
+ /* Only log best plugin if it's not the generic one */
+ if (!mm_plugin_is_generic (best_plugin))
+ mm_obj_dbg (self, "task %s: found best plugin: %s",
+ port_context->name, mm_plugin_get_name (best_plugin));
+ /* Store and suggest this plugin also to other port probes */
+ device_context->best_plugin = g_object_ref (best_plugin);
+ device_context_suggest_plugin (device_context, port_context, best_plugin);
+ return;
+ }
- mm_dbg ("(Plugin Manager) [%s] Found '%u' plugins to try...",
- g_udev_device_get_name (port),
- g_list_length (list));
- for (l = list; l; l = g_list_next (l)) {
- mm_dbg ("(Plugin Manager) [%s] Will try with plugin '%s'",
- g_udev_device_get_name (port),
- mm_plugin_get_name (MM_PLUGIN (l->data)));
+ /* Warn if the best plugin found for this port differs from the
+ * best plugin found for the the first probed port */
+ if (!g_str_equal (mm_plugin_get_name (device_context->best_plugin), mm_plugin_get_name (best_plugin))) {
+ /* Icera modems may not reply to the icera probing in all ports. We handle this by
+ * checking the forbidden/allowed icera flags in both the current and the expected
+ * plugins. If either of these plugins requires icera and the other doesn't, we
+ * pick the Icera one as best plugin. */
+ gboolean previous_forbidden_icera;
+ gboolean previous_allowed_icera;
+ gboolean new_forbidden_icera;
+ gboolean new_allowed_icera;
+
+ g_object_get (device_context->best_plugin,
+ MM_PLUGIN_ALLOWED_ICERA, &previous_allowed_icera,
+ MM_PLUGIN_FORBIDDEN_ICERA, &previous_forbidden_icera,
+ NULL);
+ g_assert (previous_allowed_icera == FALSE || previous_forbidden_icera == FALSE);
+
+ g_object_get (best_plugin,
+ MM_PLUGIN_ALLOWED_ICERA, &new_allowed_icera,
+ MM_PLUGIN_FORBIDDEN_ICERA, &new_forbidden_icera,
+ NULL);
+ g_assert (new_allowed_icera == FALSE || new_forbidden_icera == FALSE);
+
+ if (previous_allowed_icera && new_forbidden_icera)
+ mm_obj_warn (self, "task %s: will use plugin '%s' instead of '%s', modem is icera-capable",
+ port_context->name,
+ mm_plugin_get_name (device_context->best_plugin),
+ mm_plugin_get_name (best_plugin));
+ else if (new_allowed_icera && previous_forbidden_icera) {
+ mm_obj_warn (self, "task %s: overriding previously selected device plugin '%s' with '%s', modem is icera-capable",
+ port_context->name,
+ mm_plugin_get_name (device_context->best_plugin),
+ mm_plugin_get_name (best_plugin));
+ g_object_unref (device_context->best_plugin);
+ device_context->best_plugin = g_object_ref (best_plugin);
+ } else
+ mm_obj_warn (self, "task %s: plugin mismatch error (device reports '%s', port reports '%s')",
+ port_context->name,
+ mm_plugin_get_name (device_context->best_plugin),
+ mm_plugin_get_name (best_plugin));
+ return;
}
- return list;
+ /* Device plugin equal to best plugin */
+ mm_obj_dbg (self, "task %s: best plugin matches device reported one: %s",
+ port_context->name, mm_plugin_get_name (best_plugin));
}
static void
-device_port_grabbed_cb (MMDevice *device,
- GUdevDevice *port,
- FindDeviceSupportContext *ctx)
+device_context_continue (DeviceContext *device_context)
{
- PortProbeContext *port_probe_ctx;
+ MMPluginManager *self;
+ GList *l;
+ GString *s = NULL;
+ guint n = 0;
+ guint n_active = 0;
- /* Launch probing task on this port with the first plugin of the list */
- port_probe_ctx = g_slice_new0 (PortProbeContext);
- port_probe_ctx->parent_ctx = ctx;
- port_probe_ctx->port = g_object_ref (port);
+ self = g_task_get_source_object (device_context->task);
- /* Setup plugins to probe and first one to check */
- port_probe_ctx->plugins = build_plugins_list (ctx->self, device, port);
- port_probe_ctx->current = port_probe_ctx->plugins;
+ /* If there are no running port contexts around, we're free to finish */
+ if (!device_context->port_contexts) {
+ mm_obj_dbg (self, "task %s: no more ports to probe", device_context->name);
+ device_context_complete (device_context);
+ return;
+ }
- /* If we got one suggested, it will be the first one, unless it is the generic plugin */
- port_probe_ctx->suggested_plugin = (!!mm_device_peek_plugin (device) ?
- MM_PLUGIN (mm_device_get_plugin (device)) :
- NULL);
- if (port_probe_ctx->suggested_plugin) {
- if (g_str_equal (mm_plugin_get_name (port_probe_ctx->suggested_plugin),
- MM_PLUGIN_GENERIC_NAME))
- /* Initially ignore generic plugin suggested */
- g_clear_object (&port_probe_ctx->suggested_plugin);
+ /* We'll count how many port contexts are 'active' (i.e. not deferred
+ * until a result is suggested). Also, prepare to log about the pending
+ * ports */
+ for (l = device_context->port_contexts; l; l = g_list_next (l)) {
+ PortContext *port_context = (PortContext *) (l->data);
+ const gchar *portname;
+
+ portname = mm_kernel_device_get_name (port_context->port);
+ if (!s)
+ s = g_string_new (portname);
else
- port_probe_ctx->current = g_list_find (port_probe_ctx->current,
- port_probe_ctx->suggested_plugin);
+ g_string_append_printf (s, ", %s", portname);
+
+ /* Active? */
+ if (!port_context->defer_until_suggested)
+ n_active++;
+ n++;
}
- /* Set as running */
- ctx->running_probes = g_list_prepend (ctx->running_probes, port_probe_ctx);
+ g_assert (n > 0 && s);
+ mm_obj_dbg (self, "task %s: still %u running probes (%u active): %s",
+ device_context->name, n, n_active, s->str);
+ g_string_free (s, TRUE);
- /* Launch supports check in the Plugin Manager */
- port_probe_context_step (port_probe_ctx);
+ if (n_active == 0) {
+ mm_obj_dbg (self, "task %s: no active tasks to probe", device_context->name);
+ device_context_suggest_plugin (device_context, NULL, NULL);
+ }
}
static void
-device_port_released_cb (MMDevice *device,
- GUdevDevice *port,
- FindDeviceSupportContext *ctx)
+port_context_run_ready (MMPluginManager *self,
+ GAsyncResult *res,
+ CommonAsyncContext *common)
{
- /* TODO: abort probing on that port */
+ GError *error = NULL;
+ MMPlugin *best_plugin;
+
+ /* Returns a full reference to the best plugin */
+ best_plugin = port_context_run_finish (self, res, &error);
+ if (!best_plugin) {
+ /* The only error we can ignore is UNSUPPORTED */
+ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ /* This error is not critical */
+ device_context_set_best_plugin (common->device_context, common->port_context, NULL);
+ } else
+ mm_obj_warn (self, "task %s: failed: %s", common->port_context->name, error->message);
+ g_error_free (error);
+ } else {
+ /* Set the plugin as the best one in the device context */
+ device_context_set_best_plugin (common->device_context, common->port_context, best_plugin);
+ g_object_unref (best_plugin);
+ }
+
+ /* We MUST have the port context in the list at this point, because we're
+ * going to remove the reference, so assert if this is not true. The caller
+ * must always make sure that the port_context is available in the list */
+ g_assert (g_list_find (common->device_context->port_contexts, common->port_context));
+ common->device_context->port_contexts = g_list_remove (common->device_context->port_contexts,
+ common->port_context);
+ port_context_unref (common->port_context);
+
+ /* Continue the device context logic */
+ device_context_continue (common->device_context);
+
+ /* Cleanup the context of the async operation */
+ common_async_context_free (common);
}
static gboolean
-min_probing_timeout_cb (FindDeviceSupportContext *ctx)
+device_context_min_probing_time_elapsed (DeviceContext *device_context)
{
- ctx->timeout_id = 0;
+ MMPluginManager *self;
- /* If there are no running probes around, we're free to finish */
- if (ctx->running_probes == NULL) {
- mm_dbg ("(Plugin Manager) [%s] Minimum probing time consumed and no more ports to probe",
- mm_device_get_path (ctx->device));
- find_device_support_context_complete_and_free (ctx);
- } else {
- GList *l;
- gboolean not_deferred = FALSE;
+ device_context->min_probing_time_id = 0;
- mm_dbg ("(Plugin Manager) [%s] Minimum probing time consumed",
- mm_device_get_path (ctx->device));
+ self = g_task_get_source_object (device_context->task);
+ mm_obj_dbg (self, "task %s: min probing time elapsed", device_context->name);
- /* If all we got were probes with 'deferred_until_suggested', just cancel
- * the probing. May happen e.g. with just 'net' ports */
- for (l = ctx->running_probes; l; l = g_list_next (l)) {
- PortProbeContext *port_probe_ctx = l->data;
+ /* Wakeup the device context logic */
+ device_context_continue (device_context);
+ return G_SOURCE_REMOVE;
+}
- if (!port_probe_ctx->defer_until_suggested) {
- not_deferred = TRUE;
- break;
- }
+static gboolean
+device_context_extra_probing_time_elapsed (DeviceContext *device_context)
+{
+ MMPluginManager *self;
+
+ device_context->extra_probing_time_id = 0;
+
+ self = g_task_get_source_object (device_context->task);
+ mm_obj_dbg (self, "task %s: extra probing time elapsed", device_context->name);
+
+ /* Wakeup the device context logic */
+ device_context_continue (device_context);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+device_context_run_port_context (DeviceContext *device_context,
+ PortContext *port_context)
+{
+ GList *plugins;
+ MMPlugin *suggested = NULL;
+ MMPluginManager *self;
+
+ /* Recover plugin manager */
+ self = MM_PLUGIN_MANAGER (device_context->self);
+
+ /* Setup plugins to probe and first one to check.
+ * Make sure this plugins list is built after the MIN WAIT TIME has been expired
+ * (so that per-driver filters work correctly) */
+ plugins = plugin_manager_build_plugins_list (self, device_context->device, port_context->port);
+
+ /* If we got one already set in the device context, it will be the first one,
+ * unless it is the generic plugin */
+ if (device_context->best_plugin && !mm_plugin_is_generic (device_context->best_plugin))
+ suggested = device_context->best_plugin;
+
+ port_context_run (self,
+ port_context,
+ plugins,
+ suggested,
+ (GAsyncReadyCallback) port_context_run_ready,
+ common_async_context_new (self,
+ device_context,
+ port_context,
+ NULL));
+ g_list_free_full (plugins, g_object_unref);
+}
+
+static gboolean
+device_context_min_wait_time_elapsed (DeviceContext *device_context)
+{
+ MMPluginManager *self;
+ GList *l;
+ GList *tmp;
+
+ self = device_context->self;
+
+ device_context->min_wait_time_id = 0;
+ mm_obj_dbg (self, "task %s: min wait time elapsed", device_context->name);
+
+ /* Move list of port contexts out of the wait list */
+ g_assert (!device_context->port_contexts);
+ tmp = device_context->wait_port_contexts;
+ device_context->wait_port_contexts = NULL;
+
+ /* Launch supports check for each port in the Plugin Manager */
+ for (l = tmp; l; l = g_list_next (l)) {
+ PortContext *port_context = (PortContext *)(l->data);
+
+ if (!mm_filter_device_and_port (self->priv->filter, port_context->device, port_context->port)) {
+ /* If port is filtered, unref it right away */
+ port_context_unref (port_context);
+ } else {
+ /* If port not filtered, store and run it */
+ device_context->port_contexts = g_list_append (device_context->port_contexts, port_context);
+ device_context_run_port_context (device_context, port_context);
}
+ }
+ g_list_free (tmp);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+device_context_port_released (DeviceContext *device_context,
+ MMKernelDevice *port)
+{
+ MMPluginManager *self;
+ PortContext *port_context;
+
+ self = g_task_get_source_object (device_context->task);
+ mm_obj_dbg (self, "task %s: port released: %s",
+ device_context->name, mm_kernel_device_get_name (port));
+
+ /* Check if there's a waiting port context */
+ port_context = device_context_peek_waiting_port_context (device_context, port);
+ if (port_context) {
+ /* We didn't run the port context yet, we can remove it right away */
+ device_context->wait_port_contexts = g_list_remove (device_context->wait_port_contexts, port_context);
+ port_context_unref (port_context);
+ return;
+ }
- if (!not_deferred)
- suggest_port_probe_result (ctx, NULL, NULL);
+ /* Now, check running port contexts, which will need cancellation if found */
+ port_context = device_context_peek_running_port_context (device_context, port);
+ if (port_context) {
+ /* Request cancellation of this single port, will be completed asynchronously */
+ port_context_cancel (port_context);
+ return;
}
- return FALSE;
+ /* This is not something worth warning. If the probing task has already
+ * been finished, it will already be removed from the list */
+ mm_obj_dbg (self, "task %s: port wasn't found: %s",
+ device_context->name, mm_kernel_device_get_name (port));
}
-void
-mm_plugin_manager_find_device_support (MMPluginManager *self,
- MMDevice *device,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- FindDeviceSupportContext *ctx;
-
- mm_dbg ("(Plugin Manager) [%s] Checking device support...",
- mm_device_get_path (device));
-
- ctx = g_slice_new0 (FindDeviceSupportContext);
- ctx->self = g_object_ref (self);
- ctx->device = g_object_ref (device);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_plugin_manager_find_device_support);
-
- /* Connect to device port grabbed/released notifications */
- ctx->grabbed_id = g_signal_connect (device,
- MM_DEVICE_PORT_GRABBED,
- G_CALLBACK (device_port_grabbed_cb),
- ctx);
- ctx->released_id = g_signal_connect (device,
- MM_DEVICE_PORT_RELEASED,
- G_CALLBACK (device_port_released_cb),
- ctx);
-
- /* Set the initial timeout of 2s. We force the probing time of the device to
+static void
+device_context_port_grabbed (DeviceContext *device_context,
+ MMKernelDevice *port)
+{
+ MMPluginManager *self;
+ PortContext *port_context;
+
+ /* Recover plugin manager */
+ self = MM_PLUGIN_MANAGER (device_context->self);
+
+ mm_obj_dbg (self, "task %s: port grabbed: %s",
+ device_context->name, mm_kernel_device_get_name (port));
+
+ /* Ignore if for any reason we still have it in the running list */
+ port_context = device_context_peek_running_port_context (device_context, port);
+ if (port_context) {
+ mm_obj_warn (self, "task %s: port context already being processed",
+ device_context->name);
+ return;
+ }
+
+ /* Ignore if for any reason we still have it in the waiting list */
+ port_context = device_context_peek_waiting_port_context (device_context, port);
+ if (port_context) {
+ mm_obj_warn (self, "task %s: port context already scheduled",
+ device_context->name);
+ return;
+ }
+
+ /* Refresh the extra probing timeout. */
+ if (device_context->extra_probing_time_id)
+ g_source_remove (device_context->extra_probing_time_id);
+ device_context->extra_probing_time_id = g_timeout_add (EXTRA_PROBING_TIME_MSECS,
+ (GSourceFunc) device_context_extra_probing_time_elapsed,
+ device_context);
+
+ /* Setup a new port context for the newly grabbed port */
+ port_context = port_context_new (self,
+ device_context->name,
+ device_context->device,
+ port);
+
+ mm_obj_dbg (self, "task %s: new support task for port",
+ port_context->name);
+
+ /* Îf still waiting the min wait time, store it in the waiting list */
+ if (device_context->min_wait_time_id) {
+ mm_obj_dbg (self, "task %s: deferred until min wait time elapsed",
+ port_context->name);
+ /* Store the port reference in the list within the device */
+ device_context->wait_port_contexts = g_list_prepend (device_context->wait_port_contexts, port_context);
+ return;
+ }
+
+ /* Store the port reference in the list within the device */
+ device_context->port_contexts = g_list_prepend (device_context->port_contexts, port_context) ;
+
+ /* If the port has been grabbed after the min wait timeout expired, launch
+ * probing directly */
+ device_context_run_port_context (device_context, port_context);
+}
+
+static gboolean
+device_context_cancel (DeviceContext *device_context)
+{
+ MMPluginManager *self;
+
+ /* If cancelled already, do nothing */
+ if (g_cancellable_is_cancelled (device_context->cancellable))
+ return FALSE;
+
+ self = g_task_get_source_object (device_context->task);
+ mm_obj_dbg (self, "task %s: cancellation requested", device_context->name);
+
+ /* The device context is cancelled now */
+ g_cancellable_cancel (device_context->cancellable);
+
+ /* Remove all port contexts in the waiting list. This will allow early cancellation
+ * if it arrives before the min wait time has elapsed */
+ if (device_context->wait_port_contexts) {
+ g_assert (!device_context->port_contexts);
+ g_list_free_full (device_context->wait_port_contexts, (GDestroyNotify) port_context_unref);
+ device_context->wait_port_contexts = NULL;
+ }
+
+ /* Cancel all ongoing port contexts, if they're not already cancelled */
+ if (device_context->port_contexts) {
+ g_assert (!device_context->wait_port_contexts);
+ /* Request cancellation, will be completed asynchronously */
+ g_list_foreach (device_context->port_contexts, (GFunc) port_context_cancel, NULL);
+ }
+
+ /* Cancel all timeouts */
+ if (device_context->min_wait_time_id) {
+ g_source_remove (device_context->min_wait_time_id);
+ device_context->min_wait_time_id = 0;
+ }
+ if (device_context->min_probing_time_id) {
+ g_source_remove (device_context->min_probing_time_id);
+ device_context->min_probing_time_id = 0;
+ }
+ if (device_context->extra_probing_time_id) {
+ g_source_remove (device_context->extra_probing_time_id);
+ device_context->extra_probing_time_id = 0;
+ }
+
+ /* Wakeup the device context logic. If we were still waiting for the
+ * min probing time, this will complete the device context. */
+ device_context_continue (device_context);
+ return TRUE;
+}
+
+static void
+device_context_run (MMPluginManager *self,
+ DeviceContext *device_context,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_assert (!device_context->task);
+ g_assert (!device_context->grabbed_id);
+ g_assert (!device_context->released_id);
+ g_assert (!device_context->min_wait_time_id);
+ g_assert (!device_context->min_probing_time_id);
+ g_assert (!device_context->extra_probing_time_id);
+
+ /* Connect to device port grabbed/released notifications from the device */
+ device_context->grabbed_id = g_signal_connect_swapped (device_context->device,
+ MM_DEVICE_PORT_GRABBED,
+ G_CALLBACK (device_context_port_grabbed),
+ device_context);
+ device_context->released_id = g_signal_connect_swapped (device_context->device,
+ MM_DEVICE_PORT_RELEASED,
+ G_CALLBACK (device_context_port_released),
+ device_context);
+
+ /* Set the initial waiting timeout. We don't want to probe any port before
+ * this timeout expires, so that we get as many ports added in the device
+ * as possible. If we don't do this, some plugin filters won't work properly,
+ * like the 'forbidden-drivers' one.
+ */
+ device_context->min_wait_time_id = g_timeout_add (MIN_WAIT_TIME_MSECS,
+ (GSourceFunc) device_context_min_wait_time_elapsed,
+ device_context);
+
+ /* Set the initial probing timeout. We force the probing time of the device to
* be at least this amount of time, so that the kernel has enough time to
* bring up ports. Given that we launch this only when the first port of the
* device has been exposed in udev, this timeout effectively means that we
- * leave up to 2s to the remaining ports to appear. */
- ctx->timer = g_timer_new ();
- ctx->timeout_id = g_timeout_add_seconds (MIN_PROBING_TIME_SECS,
- (GSourceFunc)min_probing_timeout_cb,
- ctx);
+ * leave up to 2s to the remaining ports to appear.
+ */
+ device_context->min_probing_time_id = g_timeout_add (MIN_PROBING_TIME_MSECS,
+ (GSourceFunc) device_context_min_probing_time_elapsed,
+ device_context);
+
+ /* The full device context is now cancellable. We pass this cancellable also
+ * to the inner GTask, so that if we're cancelled we always return a
+ * cancellation error, regardless of what the standard logic does. */
+ device_context->cancellable = g_cancellable_new ();
+
+ /* Create an inner task for the device context. We'll complete this task when
+ * the last port has been probed. */
+ device_context->task = g_task_new (self, device_context->cancellable, callback, user_data);
+}
+
+static DeviceContext *
+device_context_new (MMPluginManager *self,
+ MMDevice *device)
+{
+ static gulong unique_task_id = 0;
+ DeviceContext *device_context;
+
+ /* Create new device context and store the task */
+ device_context = g_slice_new0 (DeviceContext);
+ device_context->ref_count = 1;
+ device_context->self = g_object_ref (self);
+ device_context->device = g_object_ref (device);
+ device_context->timer = g_timer_new ();
+
+ /* Set context name (just for logging) */
+ device_context->name = g_strdup_printf ("%lu", unique_task_id++);
+
+ return device_context;
+}
+
+/*****************************************************************************/
+/* Look for plugin to support the given device
+ *
+ * This operation is initiated when the new MMDevice is detected. Once that
+ * happens, a new support check for the whole device will arrive at the plugin
+ * manager.
+ *
+ * Ports in the device, though, are added dynamically and automatically
+ * afterwards once the device support check has been created. It is the device
+ * support check context itself adding the newly added ports.
+ *
+ * The device support check task is finished once all port support check tasks
+ * have also been finished.
+ *
+ * Given that the ports are added dynamically, there is some minimum duration
+ * for the device support check task, otherwise we may end up not detecting
+ * any port.
+ *
+ * The device support check tasks are stored also in the plugin manager, so
+ * that the cancellation API doesn't require anything more specific than the
+ * device for which the support check task should be cancelled.
+ */
+
+MMPlugin *
+mm_plugin_manager_device_support_check_finish (MMPluginManager *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return MM_PLUGIN (g_task_propagate_pointer (G_TASK (res), error));
+}
+
+static DeviceContext *
+plugin_manager_peek_device_context (MMPluginManager *self,
+ MMDevice *device)
+{
+ GList *l;
+
+ for (l = self->priv->device_contexts; l; l = g_list_next (l)) {
+ DeviceContext *device_context;
+
+ device_context = (DeviceContext *)(l->data);
+ if ((device == device_context->device) ||
+ (!g_strcmp0 (mm_device_get_uid (device_context->device), mm_device_get_uid (device))))
+ return device_context;
+ }
+ return NULL;
+}
+
+gboolean
+mm_plugin_manager_device_support_check_cancel (MMPluginManager *self,
+ MMDevice *device)
+{
+ DeviceContext *device_context;
+
+ /* If the device context isn't found, ignore the cancellation request. */
+ device_context = plugin_manager_peek_device_context (self, device);
+ if (!device_context)
+ return FALSE;
+
+ /* Request cancellation, will be completed asynchronously */
+ return device_context_cancel (device_context);
+}
+
+static void
+device_context_run_ready (MMPluginManager *self,
+ GAsyncResult *res,
+ CommonAsyncContext *common)
+{
+ GError *error = NULL;
+ MMPlugin *best_plugin;
+
+ /* We get a full reference back */
+ best_plugin = device_context_run_finish (self, res, &error);
+
+ /*
+ * Once the task is finished, we can also remove it from the plugin manager
+ * list. We MUST have the port context in the list at this point, because
+ * we're going to dispose the reference, so assert if this is not true.
+ */
+ g_assert (g_list_find (common->self->priv->device_contexts, common->device_context));
+ common->self->priv->device_contexts = g_list_remove (common->self->priv->device_contexts,
+ common->device_context);
+ device_context_unref (common->device_context);
+
+ /* Report result or error once removed from our internal list */
+ if (!best_plugin)
+ g_task_return_error (common->task, error);
+ else
+ g_task_return_pointer (common->task, best_plugin, g_object_unref);
+ common_async_context_free (common);
+}
+
+void
+mm_plugin_manager_device_support_check (MMPluginManager *self,
+ MMDevice *device,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ DeviceContext *device_context;
+ GTask *task;
+
+ /*
+ * Create a new task for the device support check request.
+ *
+ * Note that we handle cancellations ourselves, as we don't want the caller
+ * to be required to keep track of a GCancellable for each of these tasks.
+ */
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Fail if there is already a task for the same device */
+ device_context = plugin_manager_peek_device_context (self, device);
+ if (device_context) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_IN_PROGRESS,
+ "Device support check task already available for device '%s'",
+ mm_device_get_uid (device));
+ g_object_unref (task);
+ return;
+ }
+
+ /* Create new device context */
+ device_context = device_context_new (self, device);
+
+ /* Track the device context in the list within the plugin manager. */
+ self->priv->device_contexts = g_list_prepend (self->priv->device_contexts, device_context);
+
+ mm_obj_dbg (self, "task %s: new support task for device: %s",
+ device_context->name, mm_device_get_uid (device_context->device));
+
+ /* Run device context */
+ device_context_run (self,
+ device_context,
+ (GAsyncReadyCallback) device_context_run_ready,
+ common_async_context_new (self,
+ device_context,
+ NULL,
+ task));
+ g_object_unref (task);
}
/*****************************************************************************/
+/* Look for plugin */
+
+MMPlugin *
+mm_plugin_manager_peek_plugin (MMPluginManager *self,
+ const gchar *plugin_name)
+{
+ GList *l;
+
+ if (self->priv->generic && g_str_equal (plugin_name, mm_plugin_get_name (self->priv->generic)))
+ return self->priv->generic;
+
+ for (l = self->priv->plugins; l; l = g_list_next (l)) {
+ MMPlugin *plugin = MM_PLUGIN (l->data);
+
+ if (g_str_equal (plugin_name, mm_plugin_get_name (plugin)))
+ return plugin;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+const gchar **
+mm_plugin_manager_get_subsystems (MMPluginManager *self)
+{
+ return (const gchar **) self->priv->subsystems;
+}
+
+/*****************************************************************************/
+
+static void
+register_plugin_whitelist_tags (MMPluginManager *self,
+ MMPlugin *plugin)
+{
+ const gchar **tags;
+ guint i;
+
+ if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_WHITELIST))
+ return;
+
+ tags = mm_plugin_get_allowed_udev_tags (plugin);
+ for (i = 0; tags && tags[i]; i++)
+ mm_filter_register_plugin_whitelist_tag (self->priv->filter, tags[i]);
+}
+
+static void
+register_plugin_whitelist_vendor_ids (MMPluginManager *self,
+ MMPlugin *plugin)
+{
+ const guint16 *vendor_ids;
+ guint i;
+
+ if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_WHITELIST))
+ return;
+
+ vendor_ids = mm_plugin_get_allowed_vendor_ids (plugin);
+ for (i = 0; vendor_ids && vendor_ids[i]; i++)
+ mm_filter_register_plugin_whitelist_vendor_id (self->priv->filter, vendor_ids[i]);
+}
+
+static void
+register_plugin_whitelist_product_ids (MMPluginManager *self,
+ MMPlugin *plugin)
+{
+ const mm_uint16_pair *product_ids;
+ guint i;
+
+ if (!mm_filter_check_rule_enabled (self->priv->filter, MM_FILTER_RULE_PLUGIN_WHITELIST))
+ return;
+
+ product_ids = mm_plugin_get_allowed_product_ids (plugin);
+ for (i = 0; product_ids && product_ids[i].l; i++)
+ mm_filter_register_plugin_whitelist_product_id (self->priv->filter, product_ids[i].l, product_ids[i].r);
+}
static MMPlugin *
-load_plugin (const gchar *path)
+load_plugin (MMPluginManager *self,
+ const gchar *path)
{
- MMPlugin *plugin = NULL;
- GModule *module;
- MMPluginCreateFunc plugin_create_func;
- gint *major_plugin_version;
- gint *minor_plugin_version;
- gchar *path_display;
+ MMPlugin *plugin = NULL;
+ GModule *module;
+ MMPluginCreateFunc plugin_create_func;
+ gint *major_plugin_version;
+ gint *minor_plugin_version;
+ gchar *path_display;
/* Get printable UTF-8 string of the path */
path_display = g_filename_display_name (path);
- module = g_module_open (path, G_MODULE_BIND_LAZY);
+ module = g_module_open (path, 0);
if (!module) {
- g_warning ("Could not load plugin '%s': %s", path_display, g_module_error ());
+ mm_obj_warn (self, "could not load plugin '%s': %s", path_display, g_module_error ());
goto out;
}
if (!g_module_symbol (module, "mm_plugin_major_version", (gpointer *) &major_plugin_version)) {
- g_warning ("Could not load plugin '%s': Missing major version info", path_display);
+ mm_obj_warn (self, "could not load plugin '%s': Missing major version info", path_display);
goto out;
}
if (*major_plugin_version != MM_PLUGIN_MAJOR_VERSION) {
- g_warning ("Could not load plugin '%s': Plugin major version %d, %d is required",
- path_display, *major_plugin_version, MM_PLUGIN_MAJOR_VERSION);
+ mm_obj_warn (self, "could not load plugin '%s': Plugin major version %d, %d is required",
+ path_display, *major_plugin_version, MM_PLUGIN_MAJOR_VERSION);
goto out;
}
if (!g_module_symbol (module, "mm_plugin_minor_version", (gpointer *) &minor_plugin_version)) {
- g_warning ("Could not load plugin '%s': Missing minor version info", path_display);
+ mm_obj_warn (self, "could not load plugin '%s': Missing minor version info", path_display);
goto out;
}
if (*minor_plugin_version != MM_PLUGIN_MINOR_VERSION) {
- g_warning ("Could not load plugin '%s': Plugin minor version %d, %d is required",
+ mm_obj_warn (self, "could not load plugin '%s': Plugin minor version %d, %d is required",
path_display, *minor_plugin_version, MM_PLUGIN_MINOR_VERSION);
goto out;
}
if (!g_module_symbol (module, "mm_plugin_create", (gpointer *) &plugin_create_func)) {
- g_warning ("Could not load plugin '%s': %s", path_display, g_module_error ());
+ mm_obj_warn (self, "could not load plugin '%s': %s", path_display, g_module_error ());
goto out;
}
plugin = (*plugin_create_func) ();
if (plugin) {
+ mm_obj_dbg (self, "loaded plugin '%s' from '%s'", mm_plugin_get_name (plugin), path_display);
g_object_weak_ref (G_OBJECT (plugin), (GWeakNotify) g_module_close, module);
} else
- mm_warn ("Could not load plugin '%s': initialization failed", path_display);
+ mm_obj_warn (self, "could not load plugin '%s': initialization failed", path_display);
out:
if (module && !plugin)
@@ -830,104 +1721,222 @@ out:
return plugin;
}
+static void
+load_shared (MMPluginManager *self,
+ const gchar *path)
+{
+ GModule *module;
+ gchar *path_display;
+ const gchar **shared_name = NULL;
+ gint *major_shared_version;
+ gint *minor_shared_version;
+
+ /* Get printable UTF-8 string of the path */
+ path_display = g_filename_display_name (path);
+
+ module = g_module_open (path, 0);
+ if (!module) {
+ mm_obj_warn (self, "could not load shared '%s': %s", path_display, g_module_error ());
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_shared_major_version", (gpointer *) &major_shared_version)) {
+ mm_obj_warn (self, "could not load shared '%s': Missing major version info", path_display);
+ goto out;
+ }
+
+ if (*major_shared_version != MM_SHARED_MAJOR_VERSION) {
+ mm_obj_warn (self, "could not load shared '%s': Shared major version %d, %d is required",
+ path_display, *major_shared_version, MM_SHARED_MAJOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_shared_minor_version", (gpointer *) &minor_shared_version)) {
+ mm_obj_warn (self, "could not load shared '%s': Missing minor version info", path_display);
+ goto out;
+ }
+
+ if (*minor_shared_version != MM_SHARED_MINOR_VERSION) {
+ mm_obj_warn (self, "could not load shared '%s': Shared minor version %d, %d is required",
+ path_display, *minor_shared_version, MM_SHARED_MINOR_VERSION);
+ goto out;
+ }
+
+ if (!g_module_symbol (module, "mm_shared_name", (gpointer *) &shared_name)) {
+ mm_obj_warn (self, "could not load shared '%s': Missing name", path_display);
+ goto out;
+ }
+
+ mm_obj_dbg (self, "loaded shared '%s' utils from '%s'", *shared_name, path_display);
+
+out:
+ if (module && !(*shared_name))
+ g_module_close (module);
+
+ g_free (path_display);
+}
+
static gboolean
-load_plugins (MMPluginManager *self,
- GError **error)
+load_plugins (MMPluginManager *self,
+ GError **error)
{
- GDir *dir = NULL;
- const gchar *fname;
- gchar *plugindir_display = NULL;
+ GDir *dir = NULL;
+ const gchar *fname;
+ GList *shared_paths = NULL;
+ GList *plugin_paths = NULL;
+ GList *l;
+ GPtrArray *subsystems = NULL;
+ g_autofree gchar *subsystems_str = NULL;
+ g_autofree gchar *plugindir_display = NULL;
if (!g_module_supported ()) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
- "GModules are not supported on your platform!");
+ "modules are not supported on your platform!");
goto out;
}
/* Get printable UTF-8 string of the path */
plugindir_display = g_filename_display_name (self->priv->plugin_dir);
- mm_dbg ("Looking for plugins in '%s'", plugindir_display);
+ mm_obj_dbg (self, "looking for plugins in '%s'", plugindir_display);
dir = g_dir_open (self->priv->plugin_dir, 0, NULL);
if (!dir) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_NO_PLUGINS,
- "Plugin directory '%s' not found",
+ "plugin directory '%s' not found",
plugindir_display);
goto out;
}
while ((fname = g_dir_read_name (dir)) != NULL) {
- gchar *path;
- MMPlugin *plugin;
-
if (!g_str_has_suffix (fname, G_MODULE_SUFFIX))
continue;
+ if (g_str_has_prefix (fname, SHARED_PREFIX))
+ shared_paths = g_list_prepend (shared_paths, g_module_build_path (self->priv->plugin_dir, fname));
+ else if (g_str_has_prefix (fname, PLUGIN_PREFIX))
+ plugin_paths = g_list_prepend (plugin_paths, g_module_build_path (self->priv->plugin_dir, fname));
+ }
- path = g_module_build_path (self->priv->plugin_dir, fname);
- plugin = load_plugin (path);
- g_free (path);
+ /* Load all shared utils */
+ for (l = shared_paths; l; l = g_list_next (l))
+ load_shared (self, (const gchar *)(l->data));
+ /* Load all plugins */
+ subsystems = g_ptr_array_new ();
+ for (l = plugin_paths; l; l = g_list_next (l)) {
+ MMPlugin *plugin;
+ const gchar **plugin_subsystems;
+ guint i;
+
+ plugin = load_plugin (self, (const gchar *)(l->data));
if (!plugin)
continue;
- mm_dbg ("Loaded plugin '%s'", mm_plugin_get_name (plugin));
+ /* Ignore plugins that don't specify subsystems */
+ plugin_subsystems = mm_plugin_get_allowed_subsystems (plugin);
+ if (!plugin_subsystems) {
+ mm_obj_warn (self, "plugin '%s' doesn't specify allowed subsystems: ignored",
+ mm_plugin_get_name (plugin));
+ continue;
+ }
- if (g_str_equal (mm_plugin_get_name (plugin), MM_PLUGIN_GENERIC_NAME))
- /* Generic plugin */
+ /* Process generic plugin */
+ if (mm_plugin_is_generic (plugin)) {
+ if (self->priv->generic) {
+ mm_obj_warn (self, "plugin '%s' is generic and another one is already registered: ignored",
+ mm_plugin_get_name (plugin));
+ continue;
+ }
self->priv->generic = plugin;
- else
- /* Vendor specific plugin */
+ } else
self->priv->plugins = g_list_append (self->priv->plugins, plugin);
+
+ /* Track required subsystems, avoiding duplicates in the list */
+ for (i = 0; plugin_subsystems[i]; i++) {
+ if (!g_ptr_array_find_with_equal_func (subsystems, plugin_subsystems[i], g_str_equal, NULL))
+ g_ptr_array_add (subsystems, g_strdup (plugin_subsystems[i]));
+ }
+
+ /* Register plugin whitelist rules in filter, if any */
+ register_plugin_whitelist_tags (self, plugin);
+ register_plugin_whitelist_vendor_ids (self, plugin);
+ register_plugin_whitelist_product_ids (self, plugin);
}
/* Check the generic plugin once all looped */
if (!self->priv->generic)
- mm_warn ("Generic plugin not loaded");
+ mm_obj_dbg (self, "generic plugin not loaded");
/* Treat as error if we don't find any plugin */
if (!self->priv->plugins && !self->priv->generic) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_NO_PLUGINS,
- "No plugins found in plugin directory '%s'",
+ "no plugins found in plugin directory '%s'",
plugindir_display);
goto out;
}
- mm_dbg ("Successfully loaded %u plugins",
- g_list_length (self->priv->plugins) + !!self->priv->generic);
+ /* Validate required subsystems */
+ if (!subsystems->len) {
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NO_PLUGINS,
+ "empty list of subsystems required by plugins");
+ goto out;
+ }
+ /* Add trailing NULL and store as GStrv */
+ g_ptr_array_add (subsystems, NULL);
+ self->priv->subsystems = (gchar **) g_ptr_array_free (subsystems, FALSE);
+ subsystems_str = g_strjoinv (", ", self->priv->subsystems);
+
+ mm_obj_dbg (self, "successfully loaded %u plugins registering %u subsystems: %s",
+ g_list_length (self->priv->plugins) + !!self->priv->generic,
+ g_strv_length (self->priv->subsystems), subsystems_str);
out:
+ g_list_free_full (shared_paths, g_free);
+ g_list_free_full (plugin_paths, g_free);
if (dir)
g_dir_close (dir);
- g_free (plugindir_display);
/* Return TRUE if at least one plugin found */
return (self->priv->plugins || self->priv->generic);
}
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("plugin-manager");
+}
+
+/*****************************************************************************/
+
MMPluginManager *
-mm_plugin_manager_new (const gchar *plugin_dir,
- GError **error)
+mm_plugin_manager_new (const gchar *plugin_dir,
+ MMFilter *filter,
+ GError **error)
{
return g_initable_new (MM_TYPE_PLUGIN_MANAGER,
NULL,
error,
MM_PLUGIN_MANAGER_PLUGIN_DIR, plugin_dir,
+ MM_PLUGIN_MANAGER_FILTER, filter,
NULL);
}
static void
-mm_plugin_manager_init (MMPluginManager *manager)
+mm_plugin_manager_init (MMPluginManager *self)
{
/* Initialize opaque pointer to private data */
- manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
- MM_TYPE_PLUGIN_MANAGER,
- MMPluginManagerPrivate);
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_PLUGIN_MANAGER,
+ MMPluginManagerPrivate);
}
static void
@@ -943,6 +1952,9 @@ set_property (GObject *object,
g_free (priv->plugin_dir);
priv->plugin_dir = g_value_dup_string (value);
break;
+ case PROP_FILTER:
+ priv->filter = g_value_dup_object (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -961,6 +1973,9 @@ get_property (GObject *object,
case PROP_PLUGIN_DIR:
g_value_set_string (value, priv->plugin_dir);
break;
+ case PROP_FILTER:
+ g_value_set_object (value, priv->filter);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -981,15 +1996,11 @@ dispose (GObject *object)
{
MMPluginManager *self = MM_PLUGIN_MANAGER (object);
- /* Cleanup list of plugins */
- if (self->priv->plugins) {
- g_list_free_full (self->priv->plugins, (GDestroyNotify)g_object_unref);
- self->priv->plugins = NULL;
- }
+ g_list_free_full (g_steal_pointer (&self->priv->plugins), g_object_unref);
g_clear_object (&self->priv->generic);
-
- g_free (self->priv->plugin_dir);
- self->priv->plugin_dir = NULL;
+ g_clear_pointer (&self->priv->plugin_dir, g_free);
+ g_clear_object (&self->priv->filter);
+ g_clear_pointer (&self->priv->subsystems, g_strfreev);
G_OBJECT_CLASS (mm_plugin_manager_parent_class)->dispose (object);
}
@@ -1001,6 +2012,12 @@ initable_iface_init (GInitableIface *iface)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_plugin_manager_class_init (MMPluginManagerClass *manager_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (manager_class);
@@ -1020,4 +2037,11 @@ mm_plugin_manager_class_init (MMPluginManagerClass *manager_class)
"Where to look for plugins",
NULL,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property
+ (object_class, PROP_FILTER,
+ g_param_spec_object (MM_PLUGIN_MANAGER_FILTER,
+ "Filter",
+ "Device filter",
+ MM_TYPE_FILTER,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
diff --git a/src/mm-plugin-manager.h b/src/mm-plugin-manager.h
index 68ca4d2d..79e7c1a1 100644
--- a/src/mm-plugin-manager.h
+++ b/src/mm-plugin-manager.h
@@ -22,6 +22,7 @@
#include "mm-device.h"
#include "mm-plugin.h"
+#include "mm-filter.h"
#include "mm-base-modem.h"
#define MM_TYPE_PLUGIN_MANAGER (mm_plugin_manager_get_type ())
@@ -32,6 +33,7 @@
#define MM_PLUGIN_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_MANAGER, MMPluginManagerClass))
#define MM_PLUGIN_MANAGER_PLUGIN_DIR "plugin-dir" /* Construct-only */
+#define MM_PLUGIN_MANAGER_FILTER "filter" /* Construct-only */
typedef struct _MMPluginManager MMPluginManager;
typedef struct _MMPluginManagerClass MMPluginManagerClass;
@@ -47,22 +49,22 @@ struct _MMPluginManagerClass {
};
GType mm_plugin_manager_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPluginManager, g_object_unref)
-MMPluginManager *mm_plugin_manager_new (const gchar *plugindir,
- GError **error);
-
-/* Asynchronous operation to find the best plugin giving support to a
- * given device. */
-void mm_plugin_manager_find_device_support (MMPluginManager *self,
- MMDevice *device,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_plugin_manager_find_device_support_finish (MMPluginManager *self,
- GAsyncResult *result,
- GError **error);
-
-/* Get plugin */
-MMPlugin *mm_plugin_manager_peek_plugin (MMPluginManager *self,
- const gchar *plugin_name);
+MMPluginManager *mm_plugin_manager_new (const gchar *plugindir,
+ MMFilter *filter,
+ GError **error);
+void mm_plugin_manager_device_support_check (MMPluginManager *self,
+ MMDevice *device,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_plugin_manager_device_support_check_cancel (MMPluginManager *self,
+ MMDevice *device);
+MMPlugin * mm_plugin_manager_device_support_check_finish (MMPluginManager *self,
+ GAsyncResult *res,
+ GError **error);
+MMPlugin *mm_plugin_manager_peek_plugin (MMPluginManager *self,
+ const gchar *plugin_name);
+const gchar **mm_plugin_manager_get_subsystems (MMPluginManager *self);
#endif /* MM_PLUGIN_MANAGER_H */
diff --git a/src/mm-plugin.c b/src/mm-plugin.c
index 2b4adeab..2020b376 100644
--- a/src/mm-plugin.c
+++ b/src/mm-plugin.c
@@ -23,24 +23,34 @@
#include <unistd.h>
#include <string.h>
-#include <gudev/gudev.h>
-
#include <ModemManager.h>
#include <mm-errors-types.h>
#include "mm-plugin.h"
#include "mm-device.h"
+#include "mm-kernel-device.h"
+#include "mm-kernel-device-generic.h"
#include "mm-port-serial-at.h"
#include "mm-port-serial-qcdm.h"
#include "mm-serial-parsers.h"
#include "mm-private-boxed-types.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-daemon-enums-types.h"
-G_DEFINE_TYPE (MMPlugin, mm_plugin, G_TYPE_OBJECT)
+#if defined WITH_QMI
+# include "mm-broadband-modem-qmi.h"
+#endif
+#if defined WITH_MBIM
+# include "mm-broadband-modem-mbim.h"
+#endif
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMPlugin, mm_plugin, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
-/* Virtual port corresponding to the embeded modem */
-static gchar *virtual_port[] = {"smd0", NULL};
+/* Virtual port corresponding to the embedded modem */
+static const gchar *virtual_port[] = {"smd0", NULL};
#define HAS_POST_PROBING_FILTERS(self) \
(self->priv->vendor_strings || \
@@ -48,12 +58,15 @@ static gchar *virtual_port[] = {"smd0", NULL};
self->priv->forbidden_product_strings || \
self->priv->allowed_icera || \
self->priv->forbidden_icera || \
+ self->priv->allowed_xmm || \
+ self->priv->forbidden_xmm || \
self->priv->custom_init)
struct _MMPluginPrivate {
- gchar *name;
+ gchar *name;
GHashTable *tasks;
+ gboolean is_generic;
/* Pre-probing filters */
gchar **subsystems;
@@ -70,6 +83,8 @@ struct _MMPluginPrivate {
mm_str_pair *forbidden_product_strings;
gboolean allowed_icera;
gboolean forbidden_icera;
+ gboolean allowed_xmm;
+ gboolean forbidden_xmm;
/* Probing setup */
gboolean at;
@@ -78,6 +93,7 @@ struct _MMPluginPrivate {
gboolean qmi;
gboolean mbim;
gboolean icera_probe;
+ gboolean xmm_probe;
MMPortProbeAtCommand *custom_at_probe;
guint64 send_delay;
gboolean remove_echo;
@@ -92,6 +108,7 @@ struct _MMPluginPrivate {
enum {
PROP_0,
PROP_NAME,
+ PROP_IS_GENERIC,
PROP_ALLOWED_SUBSYSTEMS,
PROP_ALLOWED_DRIVERS,
PROP_FORBIDDEN_DRIVERS,
@@ -110,6 +127,9 @@ enum {
PROP_ICERA_PROBE,
PROP_ALLOWED_ICERA,
PROP_FORBIDDEN_ICERA,
+ PROP_XMM_PROBE,
+ PROP_ALLOWED_XMM,
+ PROP_FORBIDDEN_XMM,
PROP_CUSTOM_AT_PROBE,
PROP_CUSTOM_INIT,
PROP_SEND_DELAY,
@@ -126,6 +146,36 @@ mm_plugin_get_name (MMPlugin *self)
return self->priv->name;
}
+const gchar **
+mm_plugin_get_allowed_subsystems (MMPlugin *self)
+{
+ return (const gchar **) self->priv->subsystems;
+}
+
+const gchar **
+mm_plugin_get_allowed_udev_tags (MMPlugin *self)
+{
+ return (const gchar **) self->priv->udev_tags;
+}
+
+const guint16 *
+mm_plugin_get_allowed_vendor_ids (MMPlugin *self)
+{
+ return self->priv->vendor_ids;
+}
+
+const mm_uint16_pair *
+mm_plugin_get_allowed_product_ids (MMPlugin *self)
+{
+ return self->priv->product_ids;
+}
+
+gboolean
+mm_plugin_is_generic (MMPlugin *self)
+{
+ return self->priv->is_generic;
+}
+
/*****************************************************************************/
static gboolean
@@ -162,21 +212,17 @@ is_virtual_port (const gchar *device_name)
/* Returns TRUE if the support check request was filtered out */
static gboolean
-apply_subsystem_filter (MMPlugin *self,
- GUdevDevice *port)
+apply_subsystem_filter (MMPlugin *self,
+ MMKernelDevice *port)
{
if (self->priv->subsystems) {
const gchar *subsys;
guint i;
- subsys = g_udev_device_get_subsystem (port);
+ subsys = mm_kernel_device_get_subsystem (port);
for (i = 0; self->priv->subsystems[i]; i++) {
if (g_str_equal (subsys, self->priv->subsystems[i]))
break;
- /* New kernels may report as 'usbmisc' the subsystem */
- else if (g_str_equal (self->priv->subsystems[i], "usb") &&
- g_str_equal (subsys, "usbmisc"))
- break;
}
/* If we didn't match any subsystem: unsupported */
@@ -189,11 +235,11 @@ apply_subsystem_filter (MMPlugin *self,
/* Returns TRUE if the support check request was filtered out */
static gboolean
-apply_pre_probing_filters (MMPlugin *self,
- MMDevice *device,
- GUdevDevice *port,
- gboolean *need_vendor_probing,
- gboolean *need_product_probing)
+apply_pre_probing_filters (MMPlugin *self,
+ MMDevice *device,
+ MMKernelDevice *port,
+ gboolean *need_vendor_probing,
+ gboolean *need_product_probing)
{
guint16 vendor;
guint16 product;
@@ -207,29 +253,36 @@ apply_pre_probing_filters (MMPlugin *self,
/* The plugin may specify that only some subsystems are supported. If that
* is the case, filter by subsystem */
if (apply_subsystem_filter (self, port)) {
- mm_dbg ("(%s) [%s] filtered by subsystem",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "port %s filtered by subsystem", mm_kernel_device_get_name (port));
return TRUE;
}
/* The plugin may specify that only some drivers are supported, or that some
- * drivers are not supported. If that is the case, filter by driver */
+ * drivers are not supported. If that is the case, filter by driver.
+ *
+ * The QMI and MBIM *forbidden* drivers filter is implicit. This is, if the
+ * plugin doesn't explicitly specify that QMI is allowed and we find a QMI
+ * port, the plugin will filter the device. Same for MBIM.
+ *
+ * The opposite, though, is not applicable. If the plugin specifies that QMI
+ * is allowed, we won't take that as a mandatory requirement to look for the
+ * QMI driver (as the plugin may handle non-QMI modems as well)
+ */
if (self->priv->drivers ||
- self->priv->forbidden_drivers) {
+ self->priv->forbidden_drivers ||
+ !self->priv->qmi ||
+ !self->priv->mbim) {
static const gchar *virtual_drivers [] = { "virtual", NULL };
const gchar **drivers;
/* Detect any modems accessible through the list of virtual ports */
- drivers = (is_virtual_port (g_udev_device_get_name (port)) ?
+ drivers = (is_virtual_port (mm_kernel_device_get_name (port)) ?
virtual_drivers :
mm_device_get_drivers (device));
/* If error retrieving driver: unsupported */
if (!drivers) {
- mm_dbg ("(%s) [%s] filtered as couldn't retrieve drivers",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "port %s filtered as couldn't retrieve drivers", mm_kernel_device_get_name (port));
return TRUE;
}
@@ -248,28 +301,51 @@ apply_pre_probing_filters (MMPlugin *self,
/* If we didn't match any driver: unsupported */
if (!found) {
- mm_dbg ("(%s) [%s] filtered by drivers",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "port %s filtered by drivers", mm_kernel_device_get_name (port));
return TRUE;
}
}
+
/* Filtering by forbidden drivers */
- else {
+ if (self->priv->forbidden_drivers) {
for (i = 0; self->priv->forbidden_drivers[i]; i++) {
guint j;
for (j = 0; drivers[j]; j++) {
/* If we match a forbidden driver: unsupported */
if (g_str_equal (drivers[j], self->priv->forbidden_drivers[i])) {
- mm_dbg ("(%s) [%s] filtered by forbidden drivers",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "port %s filtered by forbidden drivers", mm_kernel_device_get_name (port));
return TRUE;
}
}
}
}
+
+ /* Implicit filter for forbidden QMI driver */
+ if (!self->priv->qmi) {
+ guint j;
+
+ for (j = 0; drivers[j]; j++) {
+ /* If we match the QMI driver: unsupported */
+ if (g_str_equal (drivers[j], "qmi_wwan")) {
+ mm_obj_dbg (self, "port %s filtered by implicit QMI driver", mm_kernel_device_get_name (port));
+ return TRUE;
+ }
+ }
+ }
+
+ /* Implicit filter for forbidden MBIM driver */
+ if (!self->priv->mbim) {
+ guint j;
+
+ for (j = 0; drivers[j]; j++) {
+ /* If we match the MBIM driver: unsupported */
+ if (g_str_equal (drivers[j], "cdc_mbim")) {
+ mm_obj_dbg (self, "port %s filtered by implicit MBIM driver", mm_kernel_device_get_name (port));
+ return TRUE;
+ }
+ }
+ }
}
vendor = mm_device_get_vendor (device);
@@ -308,6 +384,14 @@ apply_pre_probing_filters (MMPlugin *self,
if (!self->priv->product_ids[i].l)
product_filtered = TRUE;
}
+
+ /* When both vendor ids and product ids are given, it may be the case that
+ * we're allowing a full VID1 and only a subset of another VID2, so try to
+ * handle that properly. */
+ if (vendor_filtered && !product_filtered)
+ vendor_filtered = FALSE;
+ if (product_filtered && self->priv->vendor_ids && !vendor_filtered)
+ product_filtered = FALSE;
}
/* If we got filtered by vendor or product IDs; mark it as unsupported only if:
@@ -319,11 +403,9 @@ apply_pre_probing_filters (MMPlugin *self,
((!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));
+ g_str_equal (mm_kernel_device_get_subsystem (port), "net") ||
+ g_str_has_prefix (mm_kernel_device_get_name (port), "cdc-wdm"))) {
+ mm_obj_dbg (self, "port %s filtered by vendor/product IDs", mm_kernel_device_get_name (port));
return TRUE;
}
@@ -333,9 +415,7 @@ apply_pre_probing_filters (MMPlugin *self,
for (i = 0; self->priv->forbidden_product_ids[i].l; i++) {
if (vendor == self->priv->forbidden_product_ids[i].l &&
product == self->priv->forbidden_product_ids[i].r) {
- mm_dbg ("(%s) [%s] filtered by forbidden vendor/product IDs",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "port %s filtered by forbidden vendor/product IDs", mm_kernel_device_get_name (port));
return TRUE;
}
}
@@ -367,17 +447,14 @@ apply_pre_probing_filters (MMPlugin *self,
* supported. If that is the case, filter by udev tag */
if (self->priv->udev_tags) {
for (i = 0; self->priv->udev_tags[i]; i++) {
- /* Check if the port was tagged */
- if (g_udev_device_get_property_as_boolean (port,
- self->priv->udev_tags[i]))
+ /* Check if the port or device was tagged */
+ if (mm_kernel_device_get_global_property_as_boolean (port, self->priv->udev_tags[i]))
break;
}
/* If we didn't match any udev tag: unsupported */
if (!self->priv->udev_tags[i]) {
- mm_dbg ("(%s) [%s] filtered by udev tags",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "port %s filtered by udev tags", mm_kernel_device_get_name (port));
return TRUE;
}
}
@@ -424,9 +501,7 @@ apply_post_probing_filters (MMPlugin *self,
if (vendor_filtered) {
if (!self->priv->product_strings) {
- mm_dbg ("(%s) [%s] filtered by vendor strings",
- self->priv->name,
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port %s filtered by vendor strings", mm_port_probe_get_port_name (probe));
return TRUE;
}
} else
@@ -449,9 +524,7 @@ apply_post_probing_filters (MMPlugin *self,
if (self->priv->product_strings) {
/* If we didn't get any vendor or product: filtered */
if (!vendor || !product) {
- mm_dbg ("(%s) [%s] filtered as no vendor/product strings given",
- self->priv->name,
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port %s filtered as no vendor/product strings given", mm_port_probe_get_port_name (probe));
return TRUE;
}
else {
@@ -472,9 +545,7 @@ apply_post_probing_filters (MMPlugin *self,
/* If we didn't match any product: unsupported */
if (!self->priv->product_strings[i].l) {
- mm_dbg ("(%s) [%s] filtered by vendor/product strings",
- self->priv->name,
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port %s filtered by vendor/product strings", mm_port_probe_get_port_name (probe));
return TRUE;
}
}
@@ -486,17 +557,15 @@ apply_post_probing_filters (MMPlugin *self,
gchar *casefolded_vendor;
gchar *casefolded_product;
- casefolded_vendor = g_utf8_casefold (self->priv->product_strings[i].l, -1);
- casefolded_product = g_utf8_casefold (self->priv->product_strings[i].r, -1);
+ casefolded_vendor = g_utf8_casefold (self->priv->forbidden_product_strings[i].l, -1);
+ casefolded_product = g_utf8_casefold (self->priv->forbidden_product_strings[i].r, -1);
found = (!!strstr (vendor, casefolded_vendor) &&
!!strstr (product, casefolded_product));
g_free (casefolded_vendor);
g_free (casefolded_product);
if (found) {
/* If we match a forbidden product: unsupported */
- mm_dbg ("(%s) [%s] filtered by forbidden vendor/product strings",
- self->priv->name,
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port %s filtered by forbidden vendor/product strings", mm_port_probe_get_port_name (probe));
return TRUE;
}
}
@@ -510,9 +579,7 @@ apply_post_probing_filters (MMPlugin *self,
if (self->priv->allowed_icera &&
!mm_port_probe_is_icera (probe)) {
/* Unsupported! */
- mm_dbg ("(%s) [%s] filtered as modem is not icera",
- self->priv->name,
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port %s filtered as modem is not icera", mm_port_probe_get_port_name (probe));
return TRUE;
}
@@ -521,9 +588,25 @@ apply_post_probing_filters (MMPlugin *self,
if (self->priv->forbidden_icera &&
mm_port_probe_is_icera (probe)) {
/* Unsupported! */
- mm_dbg ("(%s) [%s] filtered as modem is icera",
- self->priv->name,
- mm_port_probe_get_port_name (probe));
+ mm_obj_dbg (self, "port %s filtered as modem is icera", mm_port_probe_get_port_name (probe));
+ return TRUE;
+ }
+
+ /* The plugin may specify that only Xmm-based modems are supported.
+ * If that is the case, filter by allowed Xmm support */
+ if (self->priv->allowed_xmm &&
+ !mm_port_probe_is_xmm (probe)) {
+ /* Unsupported! */
+ mm_obj_dbg (self, "port %s filtered as modem is not XMM", mm_port_probe_get_port_name (probe));
+ return TRUE;
+ }
+
+ /* The plugin may specify that Xmm-based modems are NOT supported.
+ * If that is the case, filter by forbidden Xmm support */
+ if (self->priv->forbidden_xmm &&
+ mm_port_probe_is_xmm (probe)) {
+ /* Unsupported! */
+ mm_obj_dbg (self, "port %s filtered as modem is XMM", mm_port_probe_get_port_name (probe));
return TRUE;
}
@@ -532,125 +615,127 @@ apply_post_probing_filters (MMPlugin *self,
/* Context for the asynchronous probing operation */
typedef struct {
- GSimpleAsyncResult *result;
MMPlugin *self;
- MMPortProbeFlag flags;
MMDevice *device;
+ MMPortProbeFlag flags;
} PortProbeRunContext;
static void
+port_probe_run_context_free (PortProbeRunContext *ctx)
+{
+ g_object_unref (ctx->device);
+ g_object_unref (ctx->self);
+ g_slice_free (PortProbeRunContext, ctx);
+}
+
+static void
port_probe_run_ready (MMPortProbe *probe,
GAsyncResult *probe_result,
- PortProbeRunContext *ctx)
+ GTask *task)
{
GError *error = NULL;
+ MMPluginSupportsResult result = MM_PLUGIN_SUPPORTS_PORT_UNKNOWN;
+ PortProbeRunContext *ctx;
if (!mm_port_probe_run_finish (probe, probe_result, &error)) {
/* Probing failed saying the port is unsupported. This is not to be
* treated as a generic error, the plugin is just telling us as nicely
* as it can that the port is not supported, so don't warn these cases.
*/
- if (g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED)) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED),
- NULL);
- g_error_free (error);
+ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED)) {
+ result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+ goto out;
}
+
/* Probing failed but the plugin tells us to retry; so we'll defer the
* probing a bit */
- else if (g_error_matches (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_RETRY)) {
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER),
- NULL);
- g_error_free (error);
- }
- /* For remaining errors, just propagate them */
- else {
- g_simple_async_result_take_error (ctx->result, error);
- }
- } else {
- /* Probing succeeded */
- MMPluginSupportsResult supports_result;
-
- if (!apply_post_probing_filters (ctx->self, ctx->flags, probe)) {
- /* Port is supported! */
- supports_result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED;
-
- /* If we were looking for AT ports, and the port is AT,
- * and we were told that only one AT port is expected, cancel AT
- * probings in the other available support tasks of the SAME
- * device. */
- if (ctx->self->priv->single_at &&
- ctx->flags & MM_PORT_PROBE_AT &&
- mm_port_probe_is_at (probe)) {
- GList *l;
-
- for (l = mm_device_peek_port_probe_list (ctx->device); l; l = g_list_next (l)) {
- if (l->data != probe) {
- mm_port_probe_run_cancel_at_probing (MM_PORT_PROBE (l->data));
- }
- }
- }
- } else {
- /* Unsupported port, remove from internal tracking HT */
- supports_result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
+ if (g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)) {
+ result = MM_PLUGIN_SUPPORTS_PORT_DEFER;
+ goto out;
}
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- GUINT_TO_POINTER (supports_result),
- NULL);
+ /* For remaining errors, just propagate them */
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
}
- /* Complete the async supports port request */
- g_simple_async_result_complete_in_idle (ctx->result);
+ /* Probing succeeded, recover context */
+ ctx = g_task_get_task_data (task);
+
+ /* Apply post probing filters */
+ if (!apply_post_probing_filters (ctx->self, ctx->flags, probe)) {
+ /* Port is supported! */
+ result = MM_PLUGIN_SUPPORTS_PORT_SUPPORTED;
+
+ /* If we were looking for AT ports, and the port is AT,
+ * and we were told that only one AT port is expected, cancel AT
+ * probings in the other available support tasks of the SAME
+ * device. */
+ if (ctx->self->priv->single_at &&
+ ctx->flags & MM_PORT_PROBE_AT &&
+ mm_port_probe_is_at (probe)) {
+ GList *l;
+
+ for (l = mm_device_peek_port_probe_list (ctx->device); l; l = g_list_next (l)) {
+ if (l->data != probe)
+ mm_port_probe_run_cancel_at_probing (MM_PORT_PROBE (l->data));
+ }
+ }
+ } else
+ /* Filtered by post probing filters */
+ result = MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- g_object_unref (ctx->device);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
+out:
+ /* Complete action */
+ g_clear_error (&error);
+ g_task_return_int (task, result);
+ g_object_unref (task);
}
+G_STATIC_ASSERT (MM_PLUGIN_SUPPORTS_PORT_UNKNOWN == -1);
+
MMPluginSupportsResult
-mm_plugin_supports_port_finish (MMPlugin *self,
- GAsyncResult *result,
- GError **error)
+mm_plugin_supports_port_finish (MMPlugin *self,
+ GAsyncResult *result,
+ GError **error)
{
- g_return_val_if_fail (MM_IS_PLUGIN (self),
- MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
- g_return_val_if_fail (G_IS_ASYNC_RESULT (result),
- MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
-
- /* Propagate error, if any */
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) {
- return MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED;
- }
+ GError *inner_error = NULL;
+ gssize value;
+
+ g_return_val_if_fail (MM_IS_PLUGIN (self), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
+ g_return_val_if_fail (G_IS_TASK (result), MM_PLUGIN_SUPPORTS_PORT_UNKNOWN);
- return (MMPluginSupportsResult) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result)));
+ value = g_task_propagate_int (G_TASK (result), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_PLUGIN_SUPPORTS_PORT_UNKNOWN;
+ }
+ return (MMPluginSupportsResult)value;
}
void
-mm_plugin_supports_port (MMPlugin *self,
- MMDevice *device,
- GUdevDevice *port,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_plugin_supports_port (MMPlugin *self,
+ MMDevice *device,
+ MMKernelDevice *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
MMPortProbe *probe = NULL;
- GSimpleAsyncResult *async_result;
+ GTask *task;
PortProbeRunContext *ctx;
gboolean need_vendor_probing;
gboolean need_product_probing;
MMPortProbeFlag probe_run_flags;
gchar *probe_list_str;
- async_result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_plugin_supports_port);
+ g_return_if_fail (MM_IS_PLUGIN (self));
+ g_return_if_fail (MM_IS_DEVICE (device));
+ g_return_if_fail (MM_IS_KERNEL_DEVICE (port));
+
+ /* Create new cancellable task */
+ task = g_task_new (self, cancellable, callback, user_data);
/* Apply filters before launching the probing */
if (apply_pre_probing_filters (self,
@@ -659,62 +744,64 @@ mm_plugin_supports_port (MMPlugin *self,
&need_vendor_probing,
&need_product_probing)) {
/* Filtered! */
- g_simple_async_result_set_op_res_gpointer (async_result,
- GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED),
- NULL);
- g_simple_async_result_complete_in_idle (async_result);
- goto out;
+ g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED);
+ g_object_unref (task);
+ return;
}
/* Need to launch new probing */
- probe = MM_PORT_PROBE (mm_device_get_port_probe (device, port));
+ probe = MM_PORT_PROBE (mm_device_peek_port_probe (device, port));
if (!probe) {
/* This may happen if the ports get removed from the device while
* probing is ongoing */
- g_simple_async_result_set_error (async_result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "(%s) Missing port probe for port (%s/%s)",
- self->priv->name,
- g_udev_device_get_subsystem (port),
- g_udev_device_get_name (port));
- g_simple_async_result_complete_in_idle (async_result);
- goto out;
- }
-
- /* Before launching any probing, check if the port is a net device. */
- if (g_str_equal (g_udev_device_get_subsystem (port), "net")) {
- mm_dbg ("(%s) [%s] probing deferred until result suggested",
- self->priv->name,
- g_udev_device_get_name (port));
- g_simple_async_result_set_op_res_gpointer (
- async_result,
- GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED),
- NULL);
- g_simple_async_result_complete_in_idle (async_result);
- goto out;
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "(%s) Missing port probe for port (%s/%s)",
+ self->priv->name,
+ mm_kernel_device_get_subsystem (port),
+ mm_kernel_device_get_name (port));
+ g_object_unref (task);
+ return;
}
/* Build flags depending on what probing needed */
- if (!g_str_has_prefix (g_udev_device_get_name (port), "cdc-wdm")) {
- /* Serial ports... */
- probe_run_flags = MM_PORT_PROBE_NONE;
+ probe_run_flags = MM_PORT_PROBE_NONE;
+ if (g_str_equal (mm_kernel_device_get_subsystem (port), "tty")) {
if (self->priv->at)
probe_run_flags |= MM_PORT_PROBE_AT;
else if (self->priv->single_at)
probe_run_flags |= MM_PORT_PROBE_AT;
if (self->priv->qcdm)
probe_run_flags |= MM_PORT_PROBE_QCDM;
- } else {
- /* cdc-wdm ports... */
- probe_run_flags = MM_PORT_PROBE_NONE;
- if (self->priv->qmi && g_str_equal (mm_device_utils_get_port_driver (port), "qmi_wwan"))
+ } else if (g_str_equal (mm_kernel_device_get_subsystem (port), "usbmisc")) {
+ if (self->priv->qmi && !g_strcmp0 (mm_kernel_device_get_driver (port), "qmi_wwan"))
probe_run_flags |= MM_PORT_PROBE_QMI;
- else if (self->priv->mbim && g_str_equal (mm_device_utils_get_port_driver (port), "cdc_mbim"))
+ else if (self->priv->mbim && !g_strcmp0 (mm_kernel_device_get_driver (port), "cdc_mbim"))
probe_run_flags |= MM_PORT_PROBE_MBIM;
else
probe_run_flags |= MM_PORT_PROBE_AT;
+ } else if (g_str_equal (mm_kernel_device_get_subsystem (port), "rpmsg")) {
+ if (self->priv->at)
+ probe_run_flags |= MM_PORT_PROBE_AT;
+ if (self->priv->qmi)
+ probe_run_flags |= MM_PORT_PROBE_QMI;
+ } else if (g_str_equal (mm_kernel_device_get_subsystem (port), "wwan")) {
+ if (self->priv->mbim)
+ probe_run_flags |= MM_PORT_PROBE_MBIM;
+ if (self->priv->qmi)
+ probe_run_flags |= MM_PORT_PROBE_QMI;
+ if (self->priv->qcdm)
+ probe_run_flags |= MM_PORT_PROBE_QCDM;
+ if (self->priv->at)
+ probe_run_flags |= MM_PORT_PROBE_AT;
}
+#if defined WITH_QRTR
+ else if (g_str_equal (mm_kernel_device_get_subsystem (port), "qrtr")) {
+ if (self->priv->qmi)
+ probe_run_flags |= MM_PORT_PROBE_QMI;
+ }
+#endif
/* For potential AT ports, check for more things */
if (probe_run_flags & MM_PORT_PROBE_AT) {
@@ -724,16 +811,18 @@ mm_plugin_supports_port (MMPlugin *self,
probe_run_flags |= MM_PORT_PROBE_AT_PRODUCT;
if (self->priv->icera_probe || self->priv->allowed_icera || self->priv->forbidden_icera)
probe_run_flags |= MM_PORT_PROBE_AT_ICERA;
+ if (self->priv->xmm_probe || self->priv->allowed_xmm || self->priv->forbidden_xmm)
+ probe_run_flags |= MM_PORT_PROBE_AT_XMM;
}
- /* If no explicit probing was required, just request to grab it without probing anything.
- * This may happen, e.g. with cdc-wdm ports which do not need QMI/MBIM probing. */
+ /* If no explicit probing was required, just request to grab it without
+ * probing anything. This happens for all net ports and e.g. for cdc-wdm
+ * ports which do not need QMI/MBIM probing. */
if (probe_run_flags == MM_PORT_PROBE_NONE) {
- g_simple_async_result_set_op_res_gpointer (async_result,
- GUINT_TO_POINTER (MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED),
- NULL);
- g_simple_async_result_complete_in_idle (async_result);
- goto out;
+ mm_obj_dbg (self, "probing of port %s deferred until result suggested", mm_kernel_device_get_name (port));
+ g_task_return_int (task, MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED);
+ g_object_unref (task);
+ return;
}
/* If a modem is already available and the plugin says that only one AT port is
@@ -742,10 +831,9 @@ mm_plugin_supports_port (MMPlugin *self,
if (self->priv->single_at &&
mm_port_probe_list_has_at_port (mm_device_peek_port_probe_list (device)) &&
!mm_port_probe_is_at (probe)) {
- mm_dbg ("(%s) [%s] not setting up AT probing tasks: "
- "modem already has the expected single AT port",
- self->priv->name,
- g_udev_device_get_name (port));
+ mm_obj_dbg (self, "not setting up AT probing tasks in port %s: "
+ "modem already has the expected single AT port",
+ mm_kernel_device_get_name (port));
/* Assuming it won't be an AT port. We still run the probe anyway, in
* case we need to check for other port types (e.g. QCDM) */
@@ -753,18 +841,19 @@ mm_plugin_supports_port (MMPlugin *self,
}
/* Setup async call context */
- ctx = g_new (PortProbeRunContext, 1);
- ctx->self = g_object_ref (self);
+ ctx = g_slice_new0 (PortProbeRunContext);
+ ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
- ctx->result = g_object_ref (async_result);
- ctx->flags = probe_run_flags;
+ ctx->flags = probe_run_flags;
+
+ /* Store context in task */
+ g_task_set_task_data (task, ctx, (GDestroyNotify) port_probe_run_context_free);
/* Launch the probe */
probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
- mm_dbg ("(%s) [%s] probe required: '%s'",
- self->priv->name,
- g_udev_device_get_name (port),
- probe_list_str);
+ mm_obj_dbg (self, "probes required for port %s: '%s'",
+ mm_kernel_device_get_name (port),
+ probe_list_str);
g_free (probe_list_str);
mm_port_probe_run (probe,
@@ -774,21 +863,17 @@ mm_plugin_supports_port (MMPlugin *self,
self->priv->send_lf,
self->priv->custom_at_probe,
self->priv->custom_init,
- (GAsyncReadyCallback)port_probe_run_ready,
- ctx);
-
-out:
- g_object_unref (async_result);
- if (probe)
- g_object_unref (probe);
+ cancellable,
+ (GAsyncReadyCallback) port_probe_run_ready,
+ task);
}
/*****************************************************************************/
MMPluginSupportsHint
-mm_plugin_discard_port_early (MMPlugin *self,
- MMDevice *device,
- GUdevDevice *port)
+mm_plugin_discard_port_early (MMPlugin *self,
+ MMDevice *device,
+ MMKernelDevice *port)
{
gboolean need_vendor_probing = FALSE;
gboolean need_product_probing = FALSE;
@@ -818,22 +903,25 @@ mm_plugin_discard_port_early (MMPlugin *self,
MMBaseModem *
mm_plugin_create_modem (MMPlugin *self,
- MMDevice *device,
+ MMDevice *device,
GError **error)
{
- MMBaseModem *modem;
- GList *port_probes = NULL;
+ MMBaseModem *modem;
+ GList *port_probes = NULL;
const gchar **virtual_ports = NULL;
+ const gchar **drivers;
if (!mm_device_is_virtual (device))
port_probes = mm_device_peek_port_probe_list (device);
else
virtual_ports = mm_device_virtual_peek_ports (device);
+ drivers = mm_device_get_drivers (device);
+
/* Let the plugin create the modem from the port probe results */
modem = MM_PLUGIN_GET_CLASS (self)->create_modem (MM_PLUGIN (self),
- mm_device_get_path (device),
- mm_device_get_drivers (device),
+ mm_device_get_uid (device),
+ drivers,
mm_device_get_vendor (device),
mm_device_get_product (device),
port_probes,
@@ -848,9 +936,22 @@ mm_plugin_create_modem (MMPlugin *self,
/* Grab each port */
for (l = port_probes; l; l = g_list_next (l)) {
- GError *inner_error = NULL;
- MMPortProbe *probe = MM_PORT_PROBE (l->data);
- gboolean grabbed;
+ GError *inner_error = NULL;
+ MMPortProbe *probe;
+ gboolean grabbed = FALSE;
+ gboolean force_ignored = FALSE;
+ const gchar *subsys;
+ const gchar *name;
+ const gchar *driver;
+ MMPortType port_type;
+
+ probe = MM_PORT_PROBE (l->data);
+
+ subsys = mm_port_probe_get_port_subsys (probe);
+ name = mm_port_probe_get_port_name (probe);
+ port_type = mm_port_probe_get_port_type (probe);
+
+ driver = mm_kernel_device_get_driver (mm_port_probe_peek_port (probe));
/* If grabbing a port fails, just warn. We'll decide if the modem is
* valid or not when all ports get organized */
@@ -859,53 +960,88 @@ mm_plugin_create_modem (MMPlugin *self,
* probed and accepted by the generic plugin, which is overwritten
* by the specific one when needed. */
if (apply_subsystem_filter (self, mm_port_probe_peek_port (probe))) {
- grabbed = FALSE;
inner_error = g_error_new (MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"unsupported subsystem: '%s'",
- mm_port_probe_get_port_subsys (probe));
+ subsys);
+ goto next;
}
+
/* Ports that are explicitly blacklisted will be grabbed as ignored */
- else if (mm_port_probe_is_ignored (probe)) {
- mm_dbg ("(%s/%s): port is blacklisted",
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe));
- grabbed = mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- MM_PORT_TYPE_IGNORED,
- MM_PORT_SERIAL_AT_FLAG_NONE,
- &inner_error);
+ if (mm_port_probe_is_ignored (probe)) {
+ mm_obj_dbg (self, "port %s is blacklisted", name);
+ force_ignored = TRUE;
+ goto grab_port;
}
-#if !defined WITH_QMI
- else if (mm_port_probe_get_port_type (probe) == MM_PORT_TYPE_NET &&
- g_str_equal (mm_device_utils_get_port_driver (mm_port_probe_peek_port (probe)),
- "qmi_wwan")) {
- /* Try to generically grab the port, but flagged as ignored */
- grabbed = mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
- MM_PORT_TYPE_IGNORED,
- MM_PORT_SERIAL_AT_FLAG_NONE,
- &inner_error);
+
+ /* Force network ignore rules for devices that use qmi_wwan */
+ if (drivers && g_strv_contains (drivers, "qmi_wwan")) {
+#if defined WITH_QMI
+ if (MM_IS_BROADBAND_MODEM_QMI (modem) &&
+ port_type == MM_PORT_TYPE_NET &&
+ g_strcmp0 (driver, "qmi_wwan") != 0) {
+ /* Non-QMI net ports are ignored in QMI modems */
+ mm_obj_dbg (self, "ignoring non-QMI net port %s in QMI modem", name);
+ force_ignored = TRUE;
+ goto grab_port;
+ }
+
+ if (!MM_IS_BROADBAND_MODEM_QMI (modem) &&
+ port_type == MM_PORT_TYPE_NET &&
+ g_strcmp0 (driver, "qmi_wwan") == 0) {
+ /* QMI net ports are ignored in non-QMI modems */
+ mm_obj_dbg (self, "ignoring QMI net port %s in non-QMI modem", name);
+ force_ignored = TRUE;
+ goto grab_port;
+ }
+#else
+ if (port_type == MM_PORT_TYPE_NET &&
+ g_strcmp0 (driver, "qmi_wwan") == 0) {
+ /* QMI net ports are ignored if QMI support not built */
+ mm_obj_dbg (self, "ignoring QMI net port %s as QMI support isn't available", name);
+ force_ignored = TRUE;
+ goto grab_port;
+ }
+#endif
}
+
+ /* Force network ignore rules for devices that use cdc_mbim */
+ if (drivers && g_strv_contains (drivers, "cdc_mbim")) {
+#if defined WITH_MBIM
+ if (MM_IS_BROADBAND_MODEM_MBIM (modem) &&
+ port_type == MM_PORT_TYPE_NET &&
+ g_strcmp0 (driver, "cdc_mbim") != 0) {
+ /* Non-MBIM net ports are ignored in MBIM modems */
+ mm_obj_dbg (self, "ignoring non-MBIM net port %s in MBIM modem", name);
+ force_ignored = TRUE;
+ goto grab_port;
+ }
+
+ if (!MM_IS_BROADBAND_MODEM_MBIM (modem) &&
+ port_type == MM_PORT_TYPE_NET &&
+ g_strcmp0 (driver, "cdc_mbim") == 0) {
+ /* MBIM net ports are ignored in non-MBIM modems */
+ mm_obj_dbg (self, "ignoring MBIM net port %s in non-MBIM modem", name);
+ force_ignored = TRUE;
+ goto grab_port;
+ }
+#else
+ if (port_type == MM_PORT_TYPE_NET &&
+ g_strcmp0 (driver, "cdc_mbim") == 0) {
+ mm_obj_dbg (self, "ignoring MBIM net port %s as MBIM support isn't available", name);
+ force_ignored = TRUE;
+ goto grab_port;
+ }
#endif
-#if !defined WITH_MBIM
- else if (mm_port_probe_get_port_type (probe) == MM_PORT_TYPE_NET &&
- g_str_equal (mm_device_utils_get_port_driver (mm_port_probe_peek_port (probe)),
- "cdc_mbim")) {
- /* Try to generically grab the port, but flagged as ignored */
+ }
+
+ grab_port:
+ if (force_ignored)
grabbed = mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ mm_port_probe_peek_port (probe),
MM_PORT_TYPE_IGNORED,
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error);
- }
-#endif
else if (MM_PLUGIN_GET_CLASS (self)->grab_port)
grabbed = MM_PLUGIN_GET_CLASS (self)->grab_port (MM_PLUGIN (self),
modem,
@@ -913,17 +1049,14 @@ mm_plugin_create_modem (MMPlugin *self,
&inner_error);
else
grabbed = mm_base_modem_grab_port (modem,
- mm_port_probe_get_port_subsys (probe),
- mm_port_probe_get_port_name (probe),
- mm_port_probe_get_parent_path (probe),
+ mm_port_probe_peek_port (probe),
mm_port_probe_get_port_type (probe),
MM_PORT_SERIAL_AT_FLAG_NONE,
&inner_error);
+
+ next:
if (!grabbed) {
- mm_warn ("Could not grab port (%s/%s): '%s'",
- mm_port_probe_get_port_subsys (MM_PORT_PROBE (l->data)),
- mm_port_probe_get_port_name (MM_PORT_PROBE (l->data)),
- inner_error ? inner_error->message : "unknown error");
+ mm_obj_warn (self, "could not grab port %s: %s", name, inner_error ? inner_error->message : "unknown error");
g_clear_error (&inner_error);
}
}
@@ -931,20 +1064,38 @@ mm_plugin_create_modem (MMPlugin *self,
guint i;
for (i = 0; virtual_ports[i]; i++) {
- GError *inner_error = NULL;
-
- if (!mm_base_modem_grab_port (modem,
- "virtual",
- virtual_ports[i],
- NULL,
- MM_PORT_TYPE_AT,
- MM_PORT_SERIAL_AT_FLAG_NONE,
- &inner_error)) {
- mm_warn ("Could not grab port (virtual/%s): '%s'",
- virtual_ports[i],
- inner_error ? inner_error->message : "unknown error");
+ GError *inner_error = NULL;
+ MMKernelDevice *kernel_device;
+ MMKernelEventProperties *properties;
+
+ properties = mm_kernel_event_properties_new ();
+ mm_kernel_event_properties_set_action (properties, "add");
+ mm_kernel_event_properties_set_subsystem (properties, "virtual");
+ mm_kernel_event_properties_set_name (properties, virtual_ports[i]);
+
+ /* Give an empty set of rules, because we don't want them to be
+ * loaded from the udev rules path (as there may not be any
+ * installed yet). */
+ kernel_device = mm_kernel_device_generic_new_with_rules (properties, NULL, &inner_error);
+ if (!kernel_device) {
+ mm_obj_warn (self, "could not create generic device for virtual port %s: %s",
+ virtual_ports[i],
+ inner_error ? inner_error->message : "unknown error");
+ g_clear_error (&inner_error);
+ } else if (!mm_base_modem_grab_port (modem,
+ kernel_device,
+ MM_PORT_TYPE_AT,
+ MM_PORT_SERIAL_AT_FLAG_NONE,
+ &inner_error)) {
+ mm_obj_warn (self, "could not grab virtual port %s: %s",
+ virtual_ports[i],
+ inner_error ? inner_error->message : "unknown error");
g_clear_error (&inner_error);
}
+
+ if (kernel_device)
+ g_object_unref (kernel_device);
+ g_object_unref (properties);
}
}
@@ -957,6 +1108,19 @@ mm_plugin_create_modem (MMPlugin *self,
/*****************************************************************************/
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMPlugin *self;
+ g_autofree gchar *plugin_name_lowercase = NULL;
+
+ self = MM_PLUGIN (_self);
+ plugin_name_lowercase = g_ascii_strdown (self->priv->name, -1);
+ return g_strdup_printf ("plugin/%s", plugin_name_lowercase);
+}
+
+/*****************************************************************************/
+
static void
mm_plugin_init (MMPlugin *self)
{
@@ -983,6 +1147,10 @@ set_property (GObject *object,
/* Construct only */
self->priv->name = g_value_dup_string (value);
break;
+ case PROP_IS_GENERIC:
+ /* Construct only */
+ self->priv->is_generic = g_value_get_boolean (value);
+ break;
case PROP_ALLOWED_SUBSYSTEMS:
/* Construct only */
self->priv->subsystems = g_value_dup_boxed (value);
@@ -1055,6 +1223,18 @@ set_property (GObject *object,
/* Construct only */
self->priv->forbidden_icera = g_value_get_boolean (value);
break;
+ case PROP_XMM_PROBE:
+ /* Construct only */
+ self->priv->xmm_probe = g_value_get_boolean (value);
+ break;
+ case PROP_ALLOWED_XMM:
+ /* Construct only */
+ self->priv->allowed_xmm = g_value_get_boolean (value);
+ break;
+ case PROP_FORBIDDEN_XMM:
+ /* Construct only */
+ self->priv->forbidden_xmm = g_value_get_boolean (value);
+ break;
case PROP_CUSTOM_AT_PROBE:
/* Construct only */
self->priv->custom_at_probe = g_value_dup_boxed (value);
@@ -1093,6 +1273,9 @@ get_property (GObject *object,
case PROP_NAME:
g_value_set_string (value, self->priv->name);
break;
+ case PROP_IS_GENERIC:
+ g_value_set_boolean (value, self->priv->is_generic);
+ break;
case PROP_ALLOWED_SUBSYSTEMS:
g_value_set_boxed (value, self->priv->subsystems);
break;
@@ -1147,6 +1330,15 @@ get_property (GObject *object,
case PROP_FORBIDDEN_ICERA:
g_value_set_boolean (value, self->priv->forbidden_icera);
break;
+ case PROP_XMM_PROBE:
+ g_value_set_boolean (value, self->priv->xmm_probe);
+ break;
+ case PROP_ALLOWED_XMM:
+ g_value_set_boolean (value, self->priv->allowed_xmm);
+ break;
+ case PROP_FORBIDDEN_XMM:
+ g_value_set_boolean (value, self->priv->forbidden_xmm);
+ break;
case PROP_CUSTOM_AT_PROBE:
g_value_set_boxed (value, self->priv->custom_at_probe);
break;
@@ -1196,6 +1388,12 @@ finalize (GObject *object)
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_plugin_class_init (MMPluginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1216,6 +1414,14 @@ mm_plugin_class_init (MMPluginClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
+ (object_class, PROP_IS_GENERIC,
+ g_param_spec_boolean (MM_PLUGIN_IS_GENERIC,
+ "Generic",
+ "Whether the plugin is the generic one",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
(object_class, PROP_ALLOWED_SUBSYSTEMS,
g_param_spec_boxed (MM_PLUGIN_ALLOWED_SUBSYSTEMS,
"Allowed subsystems",
@@ -1370,6 +1576,30 @@ mm_plugin_class_init (MMPluginClass *klass)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
+ (object_class, PROP_XMM_PROBE,
+ g_param_spec_boolean (MM_PLUGIN_XMM_PROBE,
+ "XMM probe",
+ "Request to probe for XMM support.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_ALLOWED_XMM,
+ g_param_spec_boolean (MM_PLUGIN_ALLOWED_XMM,
+ "Allowed XMM",
+ "Whether XMM support is allowed in this plugin.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
+ (object_class, PROP_FORBIDDEN_XMM,
+ g_param_spec_boolean (MM_PLUGIN_FORBIDDEN_XMM,
+ "Allowed XMM",
+ "Whether XMM support is forbidden in this plugin.",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
(object_class, PROP_CUSTOM_AT_PROBE,
g_param_spec_boxed (MM_PLUGIN_CUSTOM_AT_PROBE,
"Custom AT Probe",
diff --git a/src/mm-plugin.h b/src/mm-plugin.h
index 7497afbd..b27e7a2f 100644
--- a/src/mm-plugin.h
+++ b/src/mm-plugin.h
@@ -20,17 +20,25 @@
#include <glib.h>
#include <glib-object.h>
-#include <gudev/gudev.h>
#include "mm-base-modem.h"
#include "mm-port.h"
#include "mm-port-probe.h"
#include "mm-device.h"
+#include "mm-kernel-device.h"
-#define MM_PLUGIN_GENERIC_NAME "Generic"
#define MM_PLUGIN_MAJOR_VERSION 4
#define MM_PLUGIN_MINOR_VERSION 0
+#if defined (G_HAVE_GNUC_VISIBILITY)
+#define VISIBILITY __attribute__((visibility("protected")))
+#else
+#define VISIBILITY
+#endif
+
+#define MM_PLUGIN_DEFINE_MAJOR_VERSION VISIBILITY int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION;
+#define MM_PLUGIN_DEFINE_MINOR_VERSION VISIBILITY int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION;
+
#define MM_TYPE_PLUGIN (mm_plugin_get_type ())
#define MM_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN, MMPlugin))
#define MM_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN, MMPluginClass))
@@ -39,6 +47,7 @@
#define MM_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN, MMPluginClass))
#define MM_PLUGIN_NAME "name"
+#define MM_PLUGIN_IS_GENERIC "is-generic"
#define MM_PLUGIN_ALLOWED_SUBSYSTEMS "allowed-subsystems"
#define MM_PLUGIN_ALLOWED_DRIVERS "allowed-drivers"
#define MM_PLUGIN_FORBIDDEN_DRIVERS "forbidden-drivers"
@@ -57,6 +66,9 @@
#define MM_PLUGIN_ICERA_PROBE "icera-probe"
#define MM_PLUGIN_ALLOWED_ICERA "allowed-icera"
#define MM_PLUGIN_FORBIDDEN_ICERA "forbidden-icera"
+#define MM_PLUGIN_XMM_PROBE "xmm-probe"
+#define MM_PLUGIN_ALLOWED_XMM "allowed-xmm"
+#define MM_PLUGIN_FORBIDDEN_XMM "forbidden-xmm"
#define MM_PLUGIN_CUSTOM_INIT "custom-init"
#define MM_PLUGIN_CUSTOM_AT_PROBE "custom-at-probe"
#define MM_PLUGIN_SEND_DELAY "send-delay"
@@ -64,7 +76,8 @@
#define MM_PLUGIN_SEND_LF "send-lf"
typedef enum {
- MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED = 0x0,
+ MM_PLUGIN_SUPPORTS_PORT_UNKNOWN = -1,
+ MM_PLUGIN_SUPPORTS_PORT_UNSUPPORTED,
MM_PLUGIN_SUPPORTS_PORT_DEFER,
MM_PLUGIN_SUPPORTS_PORT_DEFER_UNTIL_SUGGESTED,
MM_PLUGIN_SUPPORTS_PORT_SUPPORTED
@@ -93,8 +106,8 @@ struct _MMPluginClass {
/* Plugins need to provide a method to create a modem object given
* a list of port probes (Mandatory) */
- MMBaseModem *(*create_modem) (MMPlugin *plugin,
- const gchar *sysfs_path,
+ MMBaseModem *(*create_modem) (MMPlugin *self,
+ const gchar *uid,
const gchar **drivers,
guint16 vendor,
guint16 product,
@@ -103,32 +116,39 @@ struct _MMPluginClass {
/* Plugins need to provide a method to grab independent ports
* identified by port probes (Optional) */
- gboolean (*grab_port) (MMPlugin *plugin,
+ gboolean (*grab_port) (MMPlugin *self,
MMBaseModem *modem,
MMPortProbe *probe,
GError **error);
};
GType mm_plugin_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPlugin, g_object_unref)
-const gchar *mm_plugin_get_name (MMPlugin *plugin);
+const gchar *mm_plugin_get_name (MMPlugin *self);
+const gchar **mm_plugin_get_allowed_subsystems (MMPlugin *self);
+const gchar **mm_plugin_get_allowed_udev_tags (MMPlugin *self);
+const guint16 *mm_plugin_get_allowed_vendor_ids (MMPlugin *self);
+const mm_uint16_pair *mm_plugin_get_allowed_product_ids (MMPlugin *self);
+gboolean mm_plugin_is_generic (MMPlugin *self);
/* This method will run all pre-probing filters, to see if we can discard this
* plugin from the probing logic as soon as possible. */
-MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *plugin,
- MMDevice *device,
- GUdevDevice *port);
-
-void mm_plugin_supports_port (MMPlugin *plugin,
- MMDevice *device,
- GUdevDevice *port,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *plugin,
- GAsyncResult *result,
- GError **error);
-
-MMBaseModem *mm_plugin_create_modem (MMPlugin *plugin,
+MMPluginSupportsHint mm_plugin_discard_port_early (MMPlugin *self,
+ MMDevice *device,
+ MMKernelDevice *port);
+
+void mm_plugin_supports_port (MMPlugin *self,
+ MMDevice *device,
+ MMKernelDevice *port,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMPluginSupportsResult mm_plugin_supports_port_finish (MMPlugin *self,
+ GAsyncResult *result,
+ GError **error);
+
+MMBaseModem *mm_plugin_create_modem (MMPlugin *self,
MMDevice *device,
GError **error);
diff --git a/src/mm-port-mbim.c b/src/mm-port-mbim.c
index 2b649963..84e2274d 100644
--- a/src/mm-port-mbim.c
+++ b/src/mm-port-mbim.c
@@ -10,150 +10,641 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@gnu.org>
*/
+#include <config.h>
+
#include <stdio.h>
#include <stdlib.h>
+#if defined WITH_QMI
+# include <libqmi-glib.h>
+#endif
+
#include <ModemManager.h>
#include <mm-errors-types.h>
#include "mm-port-mbim.h"
-#include "mm-log.h"
+#include "mm-port-net.h"
+#include "mm-log-object.h"
G_DEFINE_TYPE (MMPortMbim, mm_port_mbim, MM_TYPE_PORT)
struct _MMPortMbimPrivate {
- gboolean in_progress;
+ gboolean in_progress;
MbimDevice *mbim_device;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ gboolean qmi_supported;
+ QmiDevice *qmi_device;
+ GList *qmi_clients;
+#endif
};
/*****************************************************************************/
-typedef struct {
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+gboolean
+mm_port_mbim_supports_qmi (MMPortMbim *self)
+{
+ return !!self->priv->qmi_device;
+}
+
+QmiClient *
+mm_port_mbim_peek_qmi_client (MMPortMbim *self,
+ QmiService service)
+{
+ GList *l;
+
+ for (l = self->priv->qmi_clients; l; l = g_list_next (l)) {
+ QmiClient *qmi_client = QMI_CLIENT (l->data);
+
+ if (qmi_client_get_service (qmi_client) == service)
+ return qmi_client;
+ }
+
+ return NULL;
+}
+
+QmiClient *
+mm_port_mbim_get_qmi_client (MMPortMbim *self,
+ QmiService service)
+{
+ QmiClient *client;
+
+ client = mm_port_mbim_peek_qmi_client (self, service);
+ return (client ? g_object_ref (client) : NULL);
+}
+
+gboolean
+mm_port_mbim_allocate_qmi_client_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+allocate_client_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
MMPortMbim *self;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
-} PortContext;
+ QmiClient *qmi_client;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ qmi_client = qmi_device_allocate_client_finish (qmi_device, res, &error);
+ if (!qmi_client) {
+ g_prefix_error (&error,
+ "Couldn't create QMI client for service '%s': ",
+ qmi_service_get_string ((QmiService) GPOINTER_TO_INT (g_task_get_task_data (task))));
+ g_task_return_error (task, error);
+ } else {
+ /* Store the client in our internal list */
+ self->priv->qmi_clients = g_list_prepend (self->priv->qmi_clients, qmi_client);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+void
+mm_port_mbim_allocate_qmi_client (MMPortMbim *self,
+ QmiService service,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (!mm_port_mbim_is_open (self)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Port is closed");
+ g_object_unref (task);
+ return;
+ }
+
+ if (!mm_port_mbim_supports_qmi (self)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Port doesn't support QMI over MBIM");
+ g_object_unref (task);
+ return;
+ }
+
+ if (!!mm_port_mbim_peek_qmi_client (self, service)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS,
+ "Client for service '%s' already allocated",
+ qmi_service_get_string (service));
+ g_object_unref (task);
+ return;
+ }
+
+ g_task_set_task_data (task, GINT_TO_POINTER (service), NULL);
+ qmi_device_allocate_client (self->priv->qmi_device,
+ service,
+ QMI_CID_NONE,
+ 10,
+ cancellable,
+ (GAsyncReadyCallback)allocate_client_ready,
+ task);
+}
+
+#endif
+
+/*****************************************************************************/
+
+typedef struct {
+ gchar *link_name;
+ guint session_id;
+} SetupLinkResult;
static void
-port_context_complete_and_free (PortContext *ctx)
+setup_link_result_free (SetupLinkResult *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_slice_free (PortContext, ctx);
+ g_free (ctx->link_name);
+ g_slice_free (SetupLinkResult, ctx);
}
-static PortContext *
-port_context_new (MMPortMbim *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+gchar *
+mm_port_mbim_setup_link_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ guint *session_id,
+ GError **error)
{
- PortContext *ctx;
+ SetupLinkResult *result;
+ gchar *link_name;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return NULL;
+
+ if (session_id)
+ *session_id = result->session_id;
+ link_name = g_steal_pointer (&result->link_name);
+ setup_link_result_free (result);
- ctx = g_slice_new0 (PortContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- port_context_new);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
- return ctx;
+ return link_name;
+}
+
+static void
+device_add_link_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetupLinkResult *result;
+ GError *error = NULL;
+
+ result = g_slice_new0 (SetupLinkResult);
+
+ result->link_name = mbim_device_add_link_finish (device, res, &result->session_id, &error);
+ if (!result->link_name) {
+ g_prefix_error (&error, "failed to add link for device: ");
+ g_task_return_error (task, error);
+ setup_link_result_free (result);
+ } else
+ g_task_return_pointer (task, result, (GDestroyNotify)setup_link_result_free);
+ g_object_unref (task);
+}
+
+void
+mm_port_mbim_setup_link (MMPortMbim *self,
+ MMPort *data,
+ const gchar *link_prefix_hint,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->mbim_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open");
+ g_object_unref (task);
+ return;
+ }
+
+ mbim_device_add_link (self->priv->mbim_device,
+ MBIM_DEVICE_SESSION_ID_AUTOMATIC,
+ mm_kernel_device_get_name (mm_port_peek_kernel_device (data)),
+ link_prefix_hint,
+ NULL,
+ (GAsyncReadyCallback) device_add_link_ready,
+ task);
}
/*****************************************************************************/
gboolean
-mm_port_mbim_open_finish (MMPortMbim *self,
+mm_port_mbim_cleanup_link_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+device_delete_link_ready (MbimDevice *device,
GAsyncResult *res,
- GError **error)
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mbim_device_delete_link_finish (device, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_port_mbim_cleanup_link (MMPortMbim *self,
+ const gchar *link_name,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->mbim_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open");
+ g_object_unref (task);
+ return;
+ }
+
+ mbim_device_delete_link (self->priv->mbim_device,
+ link_name,
+ NULL,
+ (GAsyncReadyCallback) device_delete_link_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MbimDevice *device;
+ MMPort *data;
+} ResetContext;
+
+static void
+reset_context_free (ResetContext *ctx)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ g_clear_object (&ctx->device);
+ g_clear_object (&ctx->data);
+ g_slice_free (ResetContext, ctx);
+}
+
+gboolean
+mm_port_mbim_reset_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-mbim_device_open_ready (MbimDevice *mbim_device,
+delete_all_links_ready (MbimDevice *device,
GAsyncResult *res,
- PortContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ MMPortMbim *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ /* link deletion not fatal */
+ if (!mbim_device_delete_all_links_finish (device, res, &error)) {
+ mm_obj_dbg (self, "couldn't delete all links: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+reset_device_new_ready (GObject *source,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortMbim *self;
+ ResetContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->device = mbim_device_new_finish (res, &error);
+ if (!ctx->device) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* first, delete all links found, if any */
+ mm_obj_dbg (self, "deleting all links in data interface '%s'",
+ mm_port_get_device (ctx->data));
+ mbim_device_delete_all_links (ctx->device,
+ mm_port_get_device (ctx->data),
+ NULL,
+ (GAsyncReadyCallback)delete_all_links_ready,
+ task);
+}
+
+void
+mm_port_mbim_reset (MMPortMbim *self,
+ MMPort *data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ ResetContext *ctx;
+ g_autoptr(GFile) file = NULL;
+ g_autofree gchar *fullpath = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->mbim_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is already open");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (ResetContext);
+ ctx->data = g_object_ref (data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) reset_context_free);
+
+ fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self)));
+ file = g_file_new_for_path (fullpath);
+
+ mbim_device_new (file, NULL,
+ (GAsyncReadyCallback) reset_device_new_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_port_mbim_open_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+qmi_device_open_ready (QmiDevice *dev,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMPortMbim *self;
+
+ self = g_task_get_source_object (task);
+
+ if (!qmi_device_open_finish (dev, res, &error)) {
+ mm_obj_dbg (self, "error: couldn't open QmiDevice: %s", error->message);
+ g_error_free (error);
+ g_clear_object (&self->priv->qmi_device);
+ /* Ignore error and complete */
+ mm_obj_info (self, "MBIM device is not QMI capable");
+ self->priv->qmi_supported = FALSE;
+ } else {
+ mm_obj_info (self, "MBIM device is QMI capable");
+ }
+
+ self->priv->in_progress = FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+qmi_device_new_ready (GObject *unused,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMPortMbim *self;
+
+ self = g_task_get_source_object (task);
+
+ self->priv->qmi_device = qmi_device_new_finish (res, &error);
+ if (!self->priv->qmi_device) {
+ mm_obj_dbg (self, "error: couldn't create QmiDevice: %s", error->message);
+ g_error_free (error);
+ /* Ignore error and complete */
+ mm_obj_info (self, "MBIM device is not QMI capable");
+ self->priv->qmi_supported = FALSE;
+ self->priv->in_progress = FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Try to open using QMI over MBIM */
+ mm_obj_dbg (self, "trying to open QMI over MBIM device...");
+ qmi_device_open (self->priv->qmi_device,
+ (QMI_DEVICE_OPEN_FLAGS_PROXY |
+ QMI_DEVICE_OPEN_FLAGS_MBIM |
+ QMI_DEVICE_OPEN_FLAGS_VERSION_INFO |
+ QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS),
+ 15,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback)qmi_device_open_ready,
+ task);
+}
+
+static void
+mbim_query_device_services_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+ MbimDeviceServiceElement **device_services;
+ guint32 device_services_count;
+ GFile *file;
+
+ self = g_task_get_source_object (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (response &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
+ mbim_message_device_services_response_parse (
+ response,
+ &device_services_count,
+ NULL, /* max_dss_sessions */
+ &device_services,
+ &error)) {
+ guint32 i;
+
+ /* Look for the QMI service */
+ for (i = 0; i < device_services_count; i++) {
+ if (mbim_uuid_to_service (&device_services[i]->device_service_id) == MBIM_SERVICE_QMI)
+ break;
+ }
+ /* If we were able to successfully list device services and none of them
+ * is the QMI service, we'll skip trying to check QMI support. */
+ if (i == device_services_count)
+ self->priv->qmi_supported = FALSE;
+ mbim_device_service_element_array_free (device_services);
+ } else {
+ /* Ignore error */
+ mm_obj_dbg (self, "Couldn't query device services, will attempt QMI open anyway: %s", error->message);
+ g_error_free (error);
+ }
+
+ if (response)
+ mbim_message_unref (response);
+
+ /* File path of the device */
+ file = G_FILE (g_task_get_task_data (task));
+
+ if (!file || !self->priv->qmi_supported) {
+ mm_obj_info (self, "MBIM device is not QMI capable");
+ self->priv->in_progress = FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Attempt to create and open the QMI device */
+ mm_obj_dbg (self, "checking if QMI over MBIM is supported...");
+ qmi_device_new (file,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_new_ready,
+ task);
+}
+
+static void
+mbim_query_device_services (GTask *task)
+{
+ MbimMessage *message;
+ MMPortMbim *self;
+
+ self = g_task_get_source_object (task);
+
+ message = mbim_message_device_services_query_new (NULL);
+ mbim_device_command (self->priv->mbim_device,
+ message,
+ 20,
+ NULL,
+ (GAsyncReadyCallback)mbim_query_device_services_ready,
+ task);
+ mbim_message_unref (message);
+}
+
+#endif
+
+static void
+mbim_device_open_ready (MbimDevice *mbim_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMPortMbim *self;
+
+ self = g_task_get_source_object (task);
- /* Reset the progress flag */
- ctx->self->priv->in_progress = FALSE;
if (!mbim_device_open_full_finish (mbim_device, res, &error)) {
- g_clear_object (&ctx->self->priv->mbim_device);
- g_simple_async_result_take_error (ctx->result, error);
- } else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_clear_object (&self->priv->mbim_device);
+ self->priv->in_progress = FALSE;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- port_context_complete_and_free (ctx);
+ mm_obj_dbg (self, "MBIM device is now open");
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (self->priv->qmi_supported) {
+ mbim_query_device_services (task);
+ return;
+ }
+#endif
+
+ self->priv->in_progress = FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-mbim_device_new_ready (GObject *unused,
+mbim_device_new_ready (GObject *unused,
GAsyncResult *res,
- PortContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ GError *error = NULL;
+ MMPortMbim *self;
- ctx->self->priv->mbim_device = mbim_device_new_finish (res, &error);
- if (!ctx->self->priv->mbim_device) {
- g_simple_async_result_take_error (ctx->result, error);
- port_context_complete_and_free (ctx);
+ self = g_task_get_source_object (task);
+ self->priv->mbim_device = mbim_device_new_finish (res, &error);
+ if (!self->priv->mbim_device) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Now open the MBIM device */
- mbim_device_open_full (ctx->self->priv->mbim_device,
- MBIM_DEVICE_OPEN_FLAGS_PROXY,
- 30,
- ctx->cancellable,
+ mbim_device_open_full (self->priv->mbim_device,
+ MBIM_DEVICE_OPEN_FLAGS_PROXY | MBIM_DEVICE_OPEN_FLAGS_MS_MBIMEX_V2,
+ 45,
+ g_task_get_cancellable (task),
(GAsyncReadyCallback)mbim_device_open_ready,
- ctx);
+ task);
}
void
-mm_port_mbim_open (MMPortMbim *self,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_port_mbim_open (MMPortMbim *self,
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ gboolean try_qmi_over_mbim,
+#endif
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GFile *file;
gchar *fullpath;
- PortContext *ctx;
+ GTask *task;
g_return_if_fail (MM_IS_PORT_MBIM (self));
- ctx = port_context_new (self, cancellable, callback, user_data);
+ task = g_task_new (self, cancellable, callback, user_data);
if (self->priv->in_progress) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "MBIM device open/close operation in progress");
- port_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "MBIM device open/close operation in progress");
+ g_object_unref (task);
return;
}
if (self->priv->mbim_device) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- port_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self)));
file = g_file_new_for_path (fullpath);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* If we want to try QMI over MBIM, store the GFile as task data */
+ if (try_qmi_over_mbim)
+ g_task_set_task_data (task, g_object_ref (file), g_object_unref);
+#endif
+
self->priv->in_progress = TRUE;
mbim_device_new (file,
- ctx->cancellable,
+ cancellable,
(GAsyncReadyCallback)mbim_device_new_ready,
- ctx);
+ task);
g_free (fullpath);
g_object_unref (file);
@@ -171,65 +662,150 @@ mm_port_mbim_is_open (MMPortMbim *self)
/*****************************************************************************/
+typedef struct {
+ MbimDevice *mbim_device;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ QmiDevice *qmi_device;
+#endif
+} PortMbimCloseContext;
+
+static void
+port_mbim_close_context_free (PortMbimCloseContext *ctx)
+{
+ g_clear_object (&ctx->mbim_device);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ g_clear_object (&ctx->qmi_device);
+#endif
+ g_slice_free (PortMbimCloseContext, ctx);
+}
+
gboolean
-mm_port_mbim_close_finish (MMPortMbim *self,
- GAsyncResult *res,
- GError **error)
+mm_port_mbim_close_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
-mbim_device_close_ready (MbimDevice *device,
+mbim_device_close_ready (MbimDevice *mbim_device,
GAsyncResult *res,
- PortContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ GError *error = NULL;
+ MMPortMbim *self;
+
+ self = g_task_get_source_object (task);
+
+ g_assert (!self->priv->mbim_device);
+ self->priv->in_progress = FALSE;
- if (!mbim_device_close_finish (device, res, &error))
- g_simple_async_result_take_error (ctx->result, error);
+ if (!mbim_device_close_finish (mbim_device, res, &error))
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+port_mbim_device_close (GTask *task)
+{
+ PortMbimCloseContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->mbim_device);
+ mbim_device_close (ctx->mbim_device,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)mbim_device_close_ready,
+ task);
+}
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+qmi_device_close_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMPortMbim *self;
+
+ self = g_task_get_source_object (task);
- ctx->self->priv->in_progress = FALSE;
- g_clear_object (&ctx->self->priv->mbim_device);
+ if (!qmi_device_close_finish (qmi_device, res, &error)) {
+ mm_obj_warn (self, "Couldn't properly close QMI device: %s", error->message);
+ g_error_free (error);
+ }
- port_context_complete_and_free (ctx);
+ port_mbim_device_close (task);
}
+#endif
+
void
-mm_port_mbim_close (MMPortMbim *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_port_mbim_close (MMPortMbim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- PortContext *ctx;
+ PortMbimCloseContext *ctx;
+ GTask *task;
g_return_if_fail (MM_IS_PORT_MBIM (self));
- ctx = port_context_new (self, NULL, callback, user_data);
+ task = g_task_new (self, NULL, callback, user_data);
if (self->priv->in_progress) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "MBIM device open/close operation in progress");
- port_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "MBIM device open/close operation in progress");
+ g_object_unref (task);
return;
}
if (!self->priv->mbim_device) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- port_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
self->priv->in_progress = TRUE;
- mbim_device_close (self->priv->mbim_device,
- 5,
- NULL,
- (GAsyncReadyCallback)mbim_device_close_ready,
- ctx);
- g_clear_object (&self->priv->mbim_device);
+
+ /* Store device(s) to close in the context */
+ ctx = g_slice_new0 (PortMbimCloseContext);
+ ctx->mbim_device = g_steal_pointer (&self->priv->mbim_device);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)port_mbim_close_context_free);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (self->priv->qmi_device) {
+ GList *l;
+
+ /* Release all allocated clients */
+ for (l = self->priv->qmi_clients; l; l = g_list_next (l)) {
+ QmiClient *qmi_client = QMI_CLIENT (l->data);
+
+ mm_obj_dbg (self, "Releasing client for service '%s'...",
+ qmi_service_get_string (qmi_client_get_service (qmi_client)));
+ qmi_device_release_client (self->priv->qmi_device,
+ qmi_client,
+ QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+ 3, NULL, NULL, NULL);
+ }
+ g_list_free_full (self->priv->qmi_clients, g_object_unref);
+ self->priv->qmi_clients = NULL;
+
+ ctx->qmi_device = g_steal_pointer (&self->priv->qmi_device);
+ qmi_device_close_async (ctx->qmi_device,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)qmi_device_close_ready,
+ task);
+ return;
+ }
+#endif
+
+ port_mbim_device_close (task);
}
/*****************************************************************************/
@@ -245,11 +821,12 @@ mm_port_mbim_peek_device (MMPortMbim *self)
/*****************************************************************************/
MMPortMbim *
-mm_port_mbim_new (const gchar *name)
+mm_port_mbim_new (const gchar *name,
+ MMPortSubsys subsys)
{
return MM_PORT_MBIM (g_object_new (MM_TYPE_PORT_MBIM,
MM_PORT_DEVICE, name,
- MM_PORT_SUBSYS, MM_PORT_SUBSYS_USB,
+ MM_PORT_SUBSYS, subsys,
MM_PORT_TYPE, MM_PORT_TYPE_MBIM,
NULL));
}
@@ -258,6 +835,12 @@ static void
mm_port_mbim_init (MMPortMbim *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_MBIM, MMPortMbimPrivate);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* By default, always assume that QMI is supported, we'll later check if
+ * that's true or not. */
+ self->priv->qmi_supported = TRUE;
+#endif
}
static void
@@ -265,6 +848,12 @@ dispose (GObject *object)
{
MMPortMbim *self = MM_PORT_MBIM (object);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ g_list_free_full (self->priv->qmi_clients, g_object_unref);
+ self->priv->qmi_clients = NULL;
+ g_clear_object (&self->priv->qmi_device);
+#endif
+
/* Clear device object */
g_clear_object (&self->priv->mbim_device);
diff --git a/src/mm-port-mbim.h b/src/mm-port-mbim.h
index 738f1c55..81e3deb7 100644
--- a/src/mm-port-mbim.h
+++ b/src/mm-port-mbim.h
@@ -10,18 +10,24 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2013-2021 Aleksander Morgado <aleksander@gnu.org>
*/
#ifndef MM_PORT_MBIM_H
#define MM_PORT_MBIM_H
+#include <config.h>
+
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <libmbim-glib.h>
+#if defined WITH_QMI
+# include <libqmi-glib.h>
+#endif
+
#include "mm-port.h"
#define MM_TYPE_PORT_MBIM (mm_port_mbim_get_type ())
@@ -45,10 +51,15 @@ struct _MMPortMbimClass {
};
GType mm_port_mbim_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortMbim, g_object_unref)
-MMPortMbim *mm_port_mbim_new (const gchar *name);
+MMPortMbim *mm_port_mbim_new (const gchar *name,
+ MMPortSubsys subsys);
void mm_port_mbim_open (MMPortMbim *self,
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ gboolean try_qmi_over_mbim,
+#endif
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -63,6 +74,48 @@ gboolean mm_port_mbim_close_finish (MMPortMbim *self,
GAsyncResult *res,
GError **error);
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+gboolean mm_port_mbim_supports_qmi (MMPortMbim *self);
+QmiClient *mm_port_mbim_peek_qmi_client (MMPortMbim *self,
+ QmiService service);
+QmiClient *mm_port_mbim_get_qmi_client (MMPortMbim *self,
+ QmiService service);
+void mm_port_mbim_allocate_qmi_client (MMPortMbim *self,
+ QmiService service,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_mbim_allocate_qmi_client_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error);
+#endif
+
MbimDevice *mm_port_mbim_peek_device (MMPortMbim *self);
+void mm_port_mbim_setup_link (MMPortMbim *self,
+ MMPort *data,
+ const gchar *link_prefix_hint,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *mm_port_mbim_setup_link_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ guint *session_id,
+ GError **error);
+
+void mm_port_mbim_cleanup_link (MMPortMbim *self,
+ const gchar *link_name,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_mbim_cleanup_link_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_port_mbim_reset (MMPortMbim *self,
+ MMPort *data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_mbim_reset_finish (MMPortMbim *self,
+ GAsyncResult *res,
+ GError **error);
+
#endif /* MM_PORT_MBIM_H */
diff --git a/src/mm-port-net.c b/src/mm-port-net.c
new file mode 100644
index 00000000..f1122ef6
--- /dev/null
+++ b/src/mm-port-net.c
@@ -0,0 +1,122 @@
+/* -*- 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) 2021 - Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <net/if.h>
+
+#include <ModemManager.h>
+#include <mm-errors-types.h>
+
+#include "mm-port-net.h"
+#include "mm-log-object.h"
+#include "mm-netlink.h"
+
+G_DEFINE_TYPE (MMPortNet, mm_port_net, MM_TYPE_PORT)
+
+struct _MMPortNetPrivate {
+ guint ifindex;
+};
+
+static void
+ensure_ifindex (MMPortNet *self)
+{
+ if (!self->priv->ifindex) {
+ self->priv->ifindex = if_nametoindex (mm_port_get_device (MM_PORT (self)));
+ if (!self->priv->ifindex)
+ mm_obj_warn (self, "couldn't get interface index");
+ else
+ mm_obj_dbg (self, "interface index: %u", self->priv->ifindex);
+ }
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_port_net_link_setup_finish (MMPortNet *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+netlink_setlink_ready (MMNetlink *netlink,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!mm_netlink_setlink_finish (netlink, res, &error)) {
+ g_prefix_error (&error, "netlink operation failed: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_port_net_link_setup (MMPortNet *self,
+ gboolean up,
+ guint mtu,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ ensure_ifindex (self);
+ if (!self->priv->ifindex) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "no valid interface index found for %s",
+ mm_port_get_device (MM_PORT (self)));
+ g_object_unref (task);
+ return;
+ }
+
+ mm_netlink_setlink (mm_netlink_get (), /* singleton */
+ self->priv->ifindex,
+ up,
+ mtu,
+ cancellable,
+ (GAsyncReadyCallback) netlink_setlink_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMPortNet *
+mm_port_net_new (const gchar *name)
+{
+ return MM_PORT_NET (g_object_new (MM_TYPE_PORT_NET,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_NET,
+ MM_PORT_TYPE, MM_PORT_TYPE_NET,
+ NULL));
+}
+
+static void
+mm_port_net_init (MMPortNet *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_NET, MMPortNetPrivate);
+}
+
+static void
+mm_port_net_class_init (MMPortNetClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMPortNetPrivate));
+}
diff --git a/src/mm-port-net.h b/src/mm-port-net.h
new file mode 100644
index 00000000..d6bf9053
--- /dev/null
+++ b/src/mm-port-net.h
@@ -0,0 +1,63 @@
+/* -*- 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) 2021 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_PORT_NET_H
+#define MM_PORT_NET_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "mm-port.h"
+
+/* Default MTU expected in a wwan interface */
+#define MM_PORT_NET_MTU_DEFAULT 1500
+
+#define MM_TYPE_PORT_NET (mm_port_net_get_type ())
+#define MM_PORT_NET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_NET, MMPortNet))
+#define MM_PORT_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_NET, MMPortNetClass))
+#define MM_IS_PORT_NET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PORT_NET))
+#define MM_IS_PORT_NET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PORT_NET))
+#define MM_PORT_NET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PORT_NET, MMPortNetClass))
+
+typedef struct _MMPortNet MMPortNet;
+typedef struct _MMPortNetClass MMPortNetClass;
+typedef struct _MMPortNetPrivate MMPortNetPrivate;
+
+struct _MMPortNet {
+ MMPort parent;
+ MMPortNetPrivate *priv;
+};
+
+struct _MMPortNetClass {
+ MMPortClass parent;
+};
+
+GType mm_port_net_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortNet, g_object_unref)
+
+MMPortNet *mm_port_net_new (const gchar *name);
+
+void mm_port_net_link_setup (MMPortNet *self,
+ gboolean up,
+ guint mtu,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_net_link_setup_finish (MMPortNet *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_PORT_NET_H */
diff --git a/src/mm-port-probe-at.c b/src/mm-port-probe-at.c
index 10cce9e5..37a5ab27 100644
--- a/src/mm-port-probe-at.c
+++ b/src/mm-port-probe-at.c
@@ -40,8 +40,6 @@ mm_port_probe_response_processor_is_at (const gchar *command,
GError **result_error)
{
if (error) {
- mm_dbg ("Parsing AT got: '%s'", error->message);
-
/* Timeout errors are the only ones not fatal;
* they will just go on to the next command. */
if (g_error_matches (error,
diff --git a/src/mm-port-probe-at.h b/src/mm-port-probe-at.h
index 359a6f5d..851bf0fb 100644
--- a/src/mm-port-probe-at.h
+++ b/src/mm-port-probe-at.h
@@ -52,7 +52,7 @@ typedef gboolean (* MMPortProbeAtResponseProcessor) (const gchar *command,
/* Struct to configure port probing commands */
typedef struct {
/* The AT command */
- gchar *command;
+ const gchar *command;
/* Timeout of the command, in seconds */
guint timeout;
/* The response processor */
@@ -76,5 +76,4 @@ gboolean mm_port_probe_response_processor_is_at (const gchar *command,
GVariant **result,
GError **result_error);
-
#endif /* MM_PORT_PROBE_AT_H */
diff --git a/src/mm-port-probe.c b/src/mm-port-probe.c
index 94e7d3b5..e955a97e 100644
--- a/src/mm-port-probe.c
+++ b/src/mm-port-probe.c
@@ -11,8 +11,8 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 - 2011 Red Hat, Inc.
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2009 - 2018 Red Hat, Inc.
+ * Copyright (C) 2011 - 2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#include "config.h"
@@ -22,10 +22,12 @@
#include <string.h>
#include <ModemManager.h>
+#include <ModemManager-tags.h>
+
#include <mm-errors-types.h>
#include "mm-port-probe.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-port-serial-at.h"
#include "mm-port-serial.h"
#include "mm-serial-parsers.h"
@@ -40,6 +42,10 @@
#include "mm-port-qmi.h"
#endif
+#if defined WITH_QRTR
+#include "mm-kernel-device-qrtr.h"
+#endif
+
#if defined WITH_MBIM
#include "mm-port-mbim.h"
#endif
@@ -52,6 +58,7 @@
* |----> Vendor
* |----> Product
* |----> Is Icera?
+ * |----> Is Xmm?
* ----> QCDM Serial Open
* |----> QCDM?
* ----> QMI Device Open
@@ -60,7 +67,10 @@
* |----> MBIM capabilities check
*/
-G_DEFINE_TYPE (MMPortProbe, mm_port_probe, G_TYPE_OBJECT)
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMPortProbe, mm_port_probe, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
@@ -71,59 +81,10 @@ enum {
static GParamSpec *properties[PROP_LAST];
-typedef struct {
- /* ---- Generic task context ---- */
-
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
- guint32 flags;
- guint source_id;
-
- /* ---- Serial probing specific context ---- */
-
- guint buffer_full_id;
- MMPortSerial *serial;
-
- /* ---- AT probing specific context ---- */
-
- GCancellable *at_probing_cancellable;
- /* Send delay for AT commands */
- guint64 at_send_delay;
- /* Flag to leave/remove echo in AT responses */
- gboolean at_remove_echo;
- /* Flag to send line-feed at the end of AT commands */
- gboolean at_send_lf;
- /* Number of times we tried to open the AT port */
- guint at_open_tries;
- /* Custom initialization setup */
- gboolean at_custom_init_run;
- MMPortProbeAtCustomInit at_custom_init;
- MMPortProbeAtCustomInitFinish at_custom_init_finish;
- /* Custom commands to look for AT support */
- const MMPortProbeAtCommand *at_custom_probe;
- /* Current group of AT commands to be sent */
- const MMPortProbeAtCommand *at_commands;
- /* Current AT Result processor */
- void (* at_result_processor) (MMPortProbe *self,
- GVariant *result);
-
-#if defined WITH_QMI
- /* ---- QMI probing specific context ---- */
- MMPortQmi *port_qmi;
-#endif
-
-#if defined WITH_MBIM
- /* ---- MBIM probing specific context ---- */
- MMPortMbim *mbim_port;
-#endif
-
-} PortProbeRunTask;
-
struct _MMPortProbePrivate {
/* Properties */
MMDevice *device;
- GUdevDevice *port;
- GUdevDevice *parent;
+ MMKernelDevice *port;
/* Probing results */
guint32 flags;
@@ -132,16 +93,73 @@ struct _MMPortProbePrivate {
gchar *vendor;
gchar *product;
gboolean is_icera;
+ gboolean is_xmm;
gboolean is_qmi;
gboolean is_mbim;
/* From udev tags */
gboolean is_ignored;
+ gboolean is_gps;
+ gboolean is_audio;
+ gboolean maybe_at_primary;
+ gboolean maybe_at_secondary;
+ gboolean maybe_at_ppp;
+ gboolean maybe_qcdm;
+ gboolean maybe_qmi;
+ gboolean maybe_mbim;
/* Current probing task. Only one can be available at a time */
- PortProbeRunTask *task;
+ GTask *task;
};
+/*****************************************************************************/
+/* Probe task completions.
+ * Always make sure that the stored task is NULL when the task is completed.
+ */
+
+static gboolean
+port_probe_task_return_error_if_cancelled (MMPortProbe *self)
+{
+ GTask *task;
+
+ task = self->priv->task;
+ self->priv->task = NULL;
+
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return TRUE;
+ }
+
+ self->priv->task = task;
+ return FALSE;
+}
+
+static void
+port_probe_task_return_error (MMPortProbe *self,
+ GError *error)
+{
+ GTask *task;
+
+ task = self->priv->task;
+ self->priv->task = NULL;
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static void
+port_probe_task_return_boolean (MMPortProbe *self,
+ gboolean result)
+{
+ GTask *task;
+
+ task = self->priv->task;
+ self->priv->task = NULL;
+ g_task_return_boolean (task, result);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+
void
mm_port_probe_set_result_at (MMPortProbe *self,
gboolean at)
@@ -150,9 +168,7 @@ mm_port_probe_set_result_at (MMPortProbe *self,
self->priv->flags |= MM_PORT_PROBE_AT;
if (self->priv->is_at) {
- mm_dbg ("(%s/%s) port is AT-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is AT-capable");
/* Also set as not a QCDM/QMI/MBIM port */
self->priv->is_qcdm = FALSE;
@@ -160,15 +176,15 @@ mm_port_probe_set_result_at (MMPortProbe *self,
self->priv->is_mbim = FALSE;
self->priv->flags |= (MM_PORT_PROBE_QCDM | MM_PORT_PROBE_QMI | MM_PORT_PROBE_MBIM);
} else {
- mm_dbg ("(%s/%s) port is not AT-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is not AT-capable");
self->priv->vendor = NULL;
self->priv->product = NULL;
self->priv->is_icera = FALSE;
+ self->priv->is_xmm = FALSE;
self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR |
MM_PORT_PROBE_AT_PRODUCT |
- MM_PORT_PROBE_AT_ICERA);
+ MM_PORT_PROBE_AT_ICERA |
+ MM_PORT_PROBE_AT_XMM);
}
}
@@ -177,15 +193,11 @@ mm_port_probe_set_result_at_vendor (MMPortProbe *self,
const gchar *at_vendor)
{
if (at_vendor) {
- mm_dbg ("(%s/%s) vendor probing finished",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "vendor probing finished");
self->priv->vendor = g_utf8_casefold (at_vendor, -1);
self->priv->flags |= MM_PORT_PROBE_AT_VENDOR;
} else {
- mm_dbg ("(%s/%s) couldn't probe for vendor string",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "couldn't probe for vendor string");
self->priv->vendor = NULL;
self->priv->product = NULL;
self->priv->flags |= (MM_PORT_PROBE_AT_VENDOR | MM_PORT_PROBE_AT_PRODUCT);
@@ -197,15 +209,11 @@ mm_port_probe_set_result_at_product (MMPortProbe *self,
const gchar *at_product)
{
if (at_product) {
- mm_dbg ("(%s/%s) product probing finished",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "product probing finished");
self->priv->product = g_utf8_casefold (at_product, -1);
self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT;
} else {
- mm_dbg ("(%s/%s) couldn't probe for product string",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "couldn't probe for product string");
self->priv->product = NULL;
self->priv->flags |= MM_PORT_PROBE_AT_PRODUCT;
}
@@ -216,21 +224,32 @@ mm_port_probe_set_result_at_icera (MMPortProbe *self,
gboolean is_icera)
{
if (is_icera) {
- mm_dbg ("(%s/%s) Modem is Icera-based",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "modem is Icera-based");
self->priv->is_icera = TRUE;
self->priv->flags |= MM_PORT_PROBE_AT_ICERA;
} else {
- mm_dbg ("(%s/%s) Modem is probably not Icera-based",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "modem is probably not Icera-based");
self->priv->is_icera = FALSE;
self->priv->flags |= MM_PORT_PROBE_AT_ICERA;
}
}
void
+mm_port_probe_set_result_at_xmm (MMPortProbe *self,
+ gboolean is_xmm)
+{
+ if (is_xmm) {
+ mm_obj_dbg (self, "modem is XMM-based");
+ self->priv->is_xmm = TRUE;
+ self->priv->flags |= MM_PORT_PROBE_AT_XMM;
+ } else {
+ mm_obj_dbg (self, "modem is probably not XMM-based");
+ self->priv->is_xmm = FALSE;
+ self->priv->flags |= MM_PORT_PROBE_AT_XMM;
+ }
+}
+
+void
mm_port_probe_set_result_qcdm (MMPortProbe *self,
gboolean qcdm)
{
@@ -238,9 +257,7 @@ mm_port_probe_set_result_qcdm (MMPortProbe *self,
self->priv->flags |= MM_PORT_PROBE_QCDM;
if (self->priv->is_qcdm) {
- mm_dbg ("(%s/%s) port is QCDM-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is QCDM-capable");
/* Also set as not an AT/QMI/MBIM port */
self->priv->is_at = FALSE;
@@ -249,16 +266,16 @@ mm_port_probe_set_result_qcdm (MMPortProbe *self,
self->priv->vendor = NULL;
self->priv->product = NULL;
self->priv->is_icera = FALSE;
+ self->priv->is_xmm = FALSE;
self->priv->flags |= (MM_PORT_PROBE_AT |
MM_PORT_PROBE_AT_VENDOR |
MM_PORT_PROBE_AT_PRODUCT |
MM_PORT_PROBE_AT_ICERA |
+ MM_PORT_PROBE_AT_XMM |
MM_PORT_PROBE_QMI |
MM_PORT_PROBE_MBIM);
} else
- mm_dbg ("(%s/%s) port is not QCDM-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is not QCDM-capable");
}
void
@@ -269,9 +286,7 @@ mm_port_probe_set_result_qmi (MMPortProbe *self,
self->priv->flags |= MM_PORT_PROBE_QMI;
if (self->priv->is_qmi) {
- mm_dbg ("(%s/%s) port is QMI-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is QMI-capable");
/* Also set as not an AT/QCDM/MBIM port */
self->priv->is_at = FALSE;
@@ -283,12 +298,11 @@ mm_port_probe_set_result_qmi (MMPortProbe *self,
MM_PORT_PROBE_AT_VENDOR |
MM_PORT_PROBE_AT_PRODUCT |
MM_PORT_PROBE_AT_ICERA |
+ MM_PORT_PROBE_AT_XMM |
MM_PORT_PROBE_QCDM |
MM_PORT_PROBE_MBIM);
} else
- mm_dbg ("(%s/%s) port is not QMI-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is not QMI-capable");
}
void
@@ -299,9 +313,7 @@ mm_port_probe_set_result_mbim (MMPortProbe *self,
self->priv->flags |= MM_PORT_PROBE_MBIM;
if (self->priv->is_mbim) {
- mm_dbg ("(%s/%s) port is MBIM-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is MBIM-capable");
/* Also set as not an AT/QCDM/QMI port */
self->priv->is_at = FALSE;
@@ -313,103 +325,113 @@ mm_port_probe_set_result_mbim (MMPortProbe *self,
MM_PORT_PROBE_AT_VENDOR |
MM_PORT_PROBE_AT_PRODUCT |
MM_PORT_PROBE_AT_ICERA |
+ MM_PORT_PROBE_AT_XMM |
MM_PORT_PROBE_QCDM |
MM_PORT_PROBE_QMI);
} else
- mm_dbg ("(%s/%s) port is not MBIM-capable",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "port is not MBIM-capable");
}
-static gboolean serial_probe_at (MMPortProbe *self);
-static gboolean serial_probe_qcdm (MMPortProbe *self);
-static void serial_probe_schedule (MMPortProbe *self);
+/*****************************************************************************/
-static void
-port_probe_run_task_free (PortProbeRunTask *task)
-{
- if (task->source_id)
- g_source_remove (task->source_id);
+typedef struct {
+ /* ---- Generic task context ---- */
+ guint32 flags;
+ guint source_id;
+ GCancellable *cancellable;
- if (task->serial) {
- if (task->buffer_full_id) {
- g_warn_if_fail (MM_IS_PORT_SERIAL_AT (task->serial));
- g_signal_handler_disconnect (task->serial, task->buffer_full_id);
- }
- if (mm_port_serial_is_open (task->serial))
- mm_port_serial_close (task->serial);
- g_object_unref (task->serial);
- }
+ /* ---- Serial probing specific context ---- */
+
+ guint buffer_full_id;
+ MMPortSerial *serial;
+
+ /* ---- AT probing specific context ---- */
+
+ GCancellable *at_probing_cancellable;
+ gulong at_probing_cancellable_linked;
+ /* Send delay for AT commands */
+ guint64 at_send_delay;
+ /* Flag to leave/remove echo in AT responses */
+ gboolean at_remove_echo;
+ /* Flag to send line-feed at the end of AT commands */
+ gboolean at_send_lf;
+ /* Number of times we tried to open the AT port */
+ guint at_open_tries;
+ /* Custom initialization setup */
+ gboolean at_custom_init_run;
+ MMPortProbeAtCustomInit at_custom_init;
+ MMPortProbeAtCustomInitFinish at_custom_init_finish;
+ /* Custom commands to look for AT support */
+ const MMPortProbeAtCommand *at_custom_probe;
+ /* Current group of AT commands to be sent */
+ const MMPortProbeAtCommand *at_commands;
+ /* Seconds between each AT command sent in the group */
+ guint at_commands_wait_secs;
+ /* Current AT Result processor */
+ void (* at_result_processor) (MMPortProbe *self,
+ GVariant *result);
#if defined WITH_QMI
- if (task->port_qmi) {
- if (mm_port_qmi_is_open (task->port_qmi))
- mm_port_qmi_close (task->port_qmi);
- g_object_unref (task->port_qmi);
- }
+ /* ---- QMI probing specific context ---- */
+ MMPortQmi *port_qmi;
#endif
#if defined WITH_MBIM
- if (task->mbim_port) {
- /* We should have closed it cleanly before */
- g_assert (!mm_port_mbim_is_open (task->mbim_port));
- g_object_unref (task->mbim_port);
- }
+ /* ---- MBIM probing specific context ---- */
+ MMPortMbim *mbim_port;
#endif
+} PortProbeRunContext;
- if (task->cancellable)
- g_object_unref (task->cancellable);
- if (task->at_probing_cancellable)
- g_object_unref (task->at_probing_cancellable);
-
- g_object_unref (task->result);
- g_free (task);
-}
+static gboolean serial_probe_at (MMPortProbe *self);
+static gboolean serial_probe_qcdm (MMPortProbe *self);
+static void serial_probe_schedule (MMPortProbe *self);
static void
-port_probe_run_task_complete (PortProbeRunTask *task,
- gboolean result,
- GError *error)
+port_probe_run_context_free (PortProbeRunContext *ctx)
{
- /* 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->serial && task->buffer_full_id) {
- g_signal_handler_disconnect (task->serial, task->buffer_full_id);
- task->buffer_full_id = 0;
+ if (ctx->cancellable && ctx->at_probing_cancellable_linked) {
+ g_cancellable_disconnect (ctx->cancellable, ctx->at_probing_cancellable_linked);
+ ctx->at_probing_cancellable_linked = 0;
}
- if (error)
- g_simple_async_result_take_error (task->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (task->result, result);
+ if (ctx->source_id) {
+ g_source_remove (ctx->source_id);
+ ctx->source_id = 0;
+ }
- /* Always complete in idle */
- g_simple_async_result_complete_in_idle (task->result);
-}
+ if (ctx->serial && ctx->buffer_full_id) {
+ g_signal_handler_disconnect (ctx->serial, ctx->buffer_full_id);
+ ctx->buffer_full_id = 0;
+ }
-static gboolean
-port_probe_run_is_cancelled (MMPortProbe *self)
-{
- PortProbeRunTask *task = self->priv->task;
+ if (ctx->serial) {
+ if (mm_port_serial_is_open (ctx->serial))
+ mm_port_serial_close (ctx->serial);
+ g_object_unref (ctx->serial);
+ }
- /* Manually check if cancelled.
- * TODO: Make the serial port response wait cancellable,
- * so that we can connect a callback to the cancellable and forget about
- * manually checking it.
- */
- if (g_cancellable_is_cancelled (task->cancellable)) {
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "(%s/%s) port probing cancelled",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port)));
- return TRUE;
+#if defined WITH_QMI
+ if (ctx->port_qmi) {
+ /* We should have closed it cleanly before */
+ g_assert (!mm_port_qmi_is_open (ctx->port_qmi));
+ g_object_unref (ctx->port_qmi);
}
+#endif
- return FALSE;
+#if defined WITH_MBIM
+ if (ctx->mbim_port) {
+ /* We should have closed it cleanly before */
+ g_assert (!mm_port_mbim_is_open (ctx->mbim_port));
+ g_object_unref (ctx->mbim_port);
+ }
+#endif
+
+ if (ctx->at_probing_cancellable)
+ g_object_unref (ctx->at_probing_cancellable);
+ if (ctx->cancellable)
+ g_object_unref (ctx->cancellable);
+
+ g_slice_free (PortProbeRunContext, ctx);
}
/***************************************************************/
@@ -420,30 +442,46 @@ static gboolean wdm_probe (MMPortProbe *self);
#if defined WITH_QMI
static void
-port_qmi_open_ready (MMPortQmi *port_qmi,
+qmi_port_close_ready (MMPortQmi *qmi_port,
+ GAsyncResult *res,
+ MMPortProbe *self)
+{
+ PortProbeRunContext *ctx;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+
+ mm_port_qmi_close_finish (qmi_port, res, NULL);
+
+ /* Keep on */
+ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self);
+}
+
+static void
+port_qmi_open_ready (MMPortQmi *port_qmi,
GAsyncResult *res,
- MMPortProbe *self)
+ MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
- GError *error = NULL;
- gboolean is_qmi;
+ GError *error = NULL;
+ PortProbeRunContext *ctx;
+ gboolean is_qmi;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
is_qmi = mm_port_qmi_open_finish (port_qmi, res, &error);
if (!is_qmi) {
- mm_dbg ("(%s/%s) error checking QMI support: '%s'",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- error ? error->message : "unknown error");
+ mm_obj_dbg (self, "error checking QMI support: %s",
+ error ? error->message : "unknown error");
g_clear_error (&error);
}
/* Set probing result */
mm_port_probe_set_result_qmi (self, is_qmi);
- mm_port_qmi_close (port_qmi);
-
- /* Keep on */
- task->source_id = g_idle_add ((GSourceFunc)wdm_probe, self);
+ mm_port_qmi_close (ctx->port_qmi,
+ (GAsyncReadyCallback) qmi_port_close_ready,
+ self);
}
#endif /* WITH_QMI */
@@ -451,65 +489,88 @@ port_qmi_open_ready (MMPortQmi *port_qmi,
static void
wdm_probe_qmi (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
-#if defined WITH_QMI
- mm_dbg ("(%s/%s) probing QMI...",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+#if defined WITH_QMI
/* Create a port and try to open it */
- task->port_qmi = mm_port_qmi_new (g_udev_device_get_name (self->priv->port));
- mm_port_qmi_open (task->port_qmi,
+ mm_obj_dbg (self, "probing QMI...");
+
+#if defined WITH_QRTR
+ if (MM_IS_KERNEL_DEVICE_QRTR (self->priv->port)) {
+ g_autoptr(QrtrNode) node = NULL;
+
+ node = mm_kernel_device_qrtr_get_node (MM_KERNEL_DEVICE_QRTR (self->priv->port));
+
+ /* Will set MM_PORT_SUBSYS_QRTR when creating the mm-port */
+ ctx->port_qmi = mm_port_qmi_new_from_node (mm_kernel_device_get_name (self->priv->port), node);
+ } else
+#endif /* WITH_QRTR */
+ {
+ MMPortSubsys subsys = MM_PORT_SUBSYS_USBMISC;
+
+ if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "rpmsg"))
+ subsys = MM_PORT_SUBSYS_RPMSG;
+
+ ctx->port_qmi = mm_port_qmi_new (mm_kernel_device_get_name (self->priv->port), subsys);
+ }
+
+ mm_port_qmi_open (ctx->port_qmi,
FALSE,
NULL,
- (GAsyncReadyCallback)port_qmi_open_ready,
+ (GAsyncReadyCallback) port_qmi_open_ready,
self);
#else
/* If not compiled with QMI support, just assume we won't have any QMI port */
mm_port_probe_set_result_qmi (self, FALSE);
- task->source_id = g_idle_add ((GSourceFunc)wdm_probe, self);
+ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self);
#endif /* WITH_QMI */
}
#if defined WITH_MBIM
static void
-mbim_port_close_ready (MMPortMbim *mbim_port,
+mbim_port_close_ready (MMPortMbim *mbim_port,
GAsyncResult *res,
- MMPortProbe *self)
+ MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
mm_port_mbim_close_finish (mbim_port, res, NULL);
/* Keep on */
- task->source_id = g_idle_add ((GSourceFunc)wdm_probe, self);
+ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self);
}
static void
-mbim_port_open_ready (MMPortMbim *mbim_port,
+mbim_port_open_ready (MMPortMbim *mbim_port,
GAsyncResult *res,
- MMPortProbe *self)
+ MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
- GError *error = NULL;
- gboolean is_mbim;
+ GError *error = NULL;
+ PortProbeRunContext *ctx;
+ gboolean is_mbim;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
is_mbim = mm_port_mbim_open_finish (mbim_port, res, &error);
if (!is_mbim) {
- mm_dbg ("(%s/%s) error checking MBIM support: '%s'",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- error ? error->message : "unknown error");
+ mm_obj_dbg (self, "error checking MBIM support: %s",
+ error ? error->message : "unknown error");
g_clear_error (&error);
}
/* Set probing result */
mm_port_probe_set_result_mbim (self, is_mbim);
- mm_port_mbim_close (task->mbim_port,
- (GAsyncReadyCallback)mbim_port_close_ready,
+ mm_port_mbim_close (ctx->mbim_port,
+ (GAsyncReadyCallback) mbim_port_close_ready,
self);
}
@@ -518,50 +579,91 @@ mbim_port_open_ready (MMPortMbim *mbim_port,
static void
wdm_probe_mbim (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
#if defined WITH_MBIM
- mm_dbg ("(%s/%s) probing MBIM...",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "probing MBIM...");
/* Create a port and try to open it */
- task->mbim_port = mm_port_mbim_new (g_udev_device_get_name (self->priv->port));
- mm_port_mbim_open (task->mbim_port,
+ ctx->mbim_port = mm_port_mbim_new (mm_kernel_device_get_name (self->priv->port),
+ MM_PORT_SUBSYS_USBMISC);
+ mm_port_mbim_open (ctx->mbim_port,
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ FALSE, /* Don't check QMI over MBIM support at this stage */
+#endif
NULL,
- (GAsyncReadyCallback)mbim_port_open_ready,
+ (GAsyncReadyCallback) mbim_port_open_ready,
self);
#else
/* If not compiled with MBIM support, just assume we won't have any MBIM port */
mm_port_probe_set_result_mbim (self, FALSE);
- task->source_id = g_idle_add ((GSourceFunc)wdm_probe, self);
+ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self);
#endif /* WITH_MBIM */
}
static gboolean
wdm_probe (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
- task->source_id = 0;
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+ ctx->source_id = 0;
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
- return FALSE;
+ if (port_probe_task_return_error_if_cancelled (self))
+ return G_SOURCE_REMOVE;
- if ((task->flags & MM_PORT_PROBE_QMI) &&
- !(self->priv->flags & MM_PORT_PROBE_QMI))
- /* QMI probing needed */
+ /* QMI probing needed? */
+ if ((ctx->flags & MM_PORT_PROBE_QMI) &&
+ !(self->priv->flags & MM_PORT_PROBE_QMI)) {
wdm_probe_qmi (self);
- else if ((task->flags & MM_PORT_PROBE_MBIM) &&
- !(self->priv->flags & MM_PORT_PROBE_MBIM))
- /* MBIM probing needed */
+ return G_SOURCE_REMOVE;
+ }
+
+ /* MBIM probing needed */
+ if ((ctx->flags & MM_PORT_PROBE_MBIM) &&
+ !(self->priv->flags & MM_PORT_PROBE_MBIM)) {
wdm_probe_mbim (self);
- else
- /* All done now */
- port_probe_run_task_complete (task, TRUE, NULL);
+ return G_SOURCE_REMOVE;
+ }
- return FALSE;
+ /* All done now */
+ port_probe_task_return_boolean (self, TRUE);
+ return G_SOURCE_REMOVE;
+}
+
+/***************************************************************/
+
+static void
+common_serial_port_setup (MMPortProbe *self,
+ MMPortSerial *serial)
+{
+ const gchar *flow_control_tag;
+
+ if (mm_kernel_device_has_property (self->priv->port, ID_MM_TTY_BAUDRATE))
+ g_object_set (serial,
+ MM_PORT_SERIAL_BAUD, mm_kernel_device_get_property_as_int (self->priv->port, ID_MM_TTY_BAUDRATE),
+ NULL);
+
+ flow_control_tag = mm_kernel_device_get_property (self->priv->port, ID_MM_TTY_FLOW_CONTROL);
+ if (flow_control_tag) {
+ MMFlowControl flow_control;
+ GError *error = NULL;
+
+ flow_control = mm_flow_control_from_string (flow_control_tag, &error);
+ if (flow_control == MM_FLOW_CONTROL_UNKNOWN) {
+ mm_obj_warn (self, "unsupported flow control settings in port: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_object_set (serial,
+ MM_PORT_SERIAL_FLOW_CONTROL, flow_control,
+ NULL);
+ }
+ }
}
/***************************************************************/
@@ -569,45 +671,44 @@ wdm_probe (MMPortProbe *self)
static void
serial_probe_qcdm_parse_response (MMPortSerialQcdm *port,
- GAsyncResult *res,
- MMPortProbe *self)
+ GAsyncResult *res,
+ MMPortProbe *self)
{
- QcdmResult *result;
- gint err = QCDM_SUCCESS;
- gboolean is_qcdm = FALSE;
- gboolean retry = FALSE;
- PortProbeRunTask *task = self->priv->task;
- GError *error = NULL;
- GByteArray *response;
+ QcdmResult *result;
+ gint err = QCDM_SUCCESS;
+ gboolean is_qcdm = FALSE;
+ gboolean retry = FALSE;
+ GError *error = NULL;
+ GByteArray *response;
+ PortProbeRunContext *ctx;
- response = mm_port_serial_qcdm_command_finish (port, res, &error);
+ ctx = g_task_get_task_data (self->priv->task);
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
- goto out;
+ if (port_probe_task_return_error_if_cancelled (self))
+ return;
+ response = mm_port_serial_qcdm_command_finish (port, res, &error);
if (!error) {
/* Parse the response */
- result = qcdm_cmd_version_info_result ((const gchar *) response->data,
- response->len,
- &err);
+ result = qcdm_cmd_version_info_result ((const gchar *) response->data, response->len, &err);
if (!result) {
- mm_warn ("(%s/%s) failed to parse QCDM version info command result: %d",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- err);
+ mm_obj_warn (self, "failed to parse QCDM version info command result: %d", err);
retry = TRUE;
} else {
/* yay, probably a QCDM port */
is_qcdm = TRUE;
qcdm_result_unref (result);
}
+ g_byte_array_unref (response);
} else if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_PARSE_FAILED)) {
/* Failed to unescape QCDM packet: don't retry */
- mm_dbg ("QCDM parsing error: %s", error->message);
+ mm_obj_dbg (self, "QCDM parsing error: %s", error->message);
+ g_error_free (error);
} else {
if (!g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT))
- mm_dbg ("QCDM probe error: (%d) %s", error->code, error->message);
+ mm_obj_dbg (self, "QCDM probe error: (%d) %s", error->code, error->message);
+ g_error_free (error);
retry = TRUE;
}
@@ -617,90 +718,86 @@ serial_probe_qcdm_parse_response (MMPortSerialQcdm *port,
cmd2 = g_object_steal_data (G_OBJECT (self), "cmd2");
if (cmd2) {
/* second try */
- mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (task->serial),
+ mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->serial),
cmd2,
3,
NULL,
- (GAsyncReadyCallback)serial_probe_qcdm_parse_response,
+ (GAsyncReadyCallback) serial_probe_qcdm_parse_response,
self);
g_byte_array_unref (cmd2);
- goto out;
+ return;
}
-
/* no more retries left */
}
/* Set probing result */
mm_port_probe_set_result_qcdm (self, is_qcdm);
-
/* Reschedule probing */
serial_probe_schedule (self);
-
-out:
- if (response)
- g_byte_array_unref (response);
- if (error)
- g_error_free (error);
}
static gboolean
serial_probe_qcdm (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
- GError *error = NULL;
- GByteArray *verinfo = NULL;
- GByteArray *verinfo2;
- gint len;
- guint8 marker = 0x7E;
+ GError *error = NULL;
+ GByteArray *verinfo = NULL;
+ GByteArray *verinfo2;
+ gint len;
+ guint8 marker = 0x7E;
+ PortProbeRunContext *ctx;
+ MMPortSubsys subsys = MM_PORT_SUBSYS_TTY;
+
- task->source_id = 0;
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+ ctx->source_id = 0;
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
- return FALSE;
+ if (port_probe_task_return_error_if_cancelled (self))
+ return G_SOURCE_REMOVE;
- mm_dbg ("(%s/%s) probing QCDM...",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
+ mm_obj_dbg (self, "probing QCDM...");
/* If open, close the AT port */
- if (task->serial) {
+ if (ctx->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;
+ if (ctx->buffer_full_id) {
+ g_signal_handler_disconnect (ctx->serial, ctx->buffer_full_id);
+ ctx->buffer_full_id = 0;
}
- mm_port_serial_close (task->serial);
- g_object_unref (task->serial);
+ mm_port_serial_close (ctx->serial);
+ g_object_unref (ctx->serial);
}
+ if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "wwan"))
+ subsys = MM_PORT_SUBSYS_WWAN;
+
/* Open the QCDM port */
- task->serial = MM_PORT_SERIAL (mm_port_serial_qcdm_new (g_udev_device_get_name (self->priv->port)));
- if (!task->serial) {
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "(%s/%s) Couldn't create QCDM port",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port)));
- return FALSE;
+ ctx->serial = MM_PORT_SERIAL (mm_port_serial_qcdm_new (mm_kernel_device_get_name (self->priv->port), subsys));
+ if (!ctx->serial) {
+ port_probe_task_return_error (self,
+ g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "(%s/%s) Couldn't create QCDM port",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port)));
+ return G_SOURCE_REMOVE;
}
+ /* Setup port if needed */
+ common_serial_port_setup (self, ctx->serial);
+
/* Try to open the port */
- if (!mm_port_serial_open (task->serial, &error)) {
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_OPEN_FAILED,
- "(%s/%s) Failed to open QCDM port: %s",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- (error ? error->message : "unknown error")));
+ if (!mm_port_serial_open (ctx->serial, &error)) {
+ port_probe_task_return_error (self,
+ g_error_new (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_OPEN_FAILED,
+ "(%s/%s) Failed to open QCDM port: %s",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port),
+ (error ? error->message : "unknown error")));
g_clear_error (&error);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/* Build up the probe command; 0x7E is the frame marker, so put one at the
@@ -713,15 +810,13 @@ serial_probe_qcdm (MMPortProbe *self)
len = qcdm_cmd_version_info_new ((char *) (verinfo->data + 1), 9);
if (len <= 0) {
g_byte_array_unref (verinfo);
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_OPEN_FAILED,
- "(%s/%s) Failed to create QCDM versin info command",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port)));
- return FALSE;
+ port_probe_task_return_error (self,
+ g_error_new (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_OPEN_FAILED,
+ "(%s/%s) Failed to create QCDM version info command",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port)));
+ return G_SOURCE_REMOVE;
}
verinfo->len = len + 1;
@@ -730,15 +825,15 @@ serial_probe_qcdm (MMPortProbe *self)
g_byte_array_append (verinfo2, verinfo->data, verinfo->len);
g_object_set_data_full (G_OBJECT (self), "cmd2", verinfo2, (GDestroyNotify) g_byte_array_unref);
- mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (task->serial),
+ mm_port_serial_qcdm_command (MM_PORT_SERIAL_QCDM (ctx->serial),
verinfo,
3,
NULL,
- (GAsyncReadyCallback)serial_probe_qcdm_parse_response,
+ (GAsyncReadyCallback) serial_probe_qcdm_parse_response,
self);
g_byte_array_unref (verinfo);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/***************************************************************/
@@ -795,6 +890,22 @@ is_non_at_response (const guint8 *data, gsize len)
}
static void
+serial_probe_at_xmm_result_processor (MMPortProbe *self,
+ GVariant *result)
+{
+ if (result) {
+ /* If any result given, it must be a string */
+ g_assert (g_variant_is_of_type (result, G_VARIANT_TYPE_STRING));
+ if (strstr (g_variant_get_string (result, NULL), "XACT:")) {
+ mm_port_probe_set_result_at_xmm (self, TRUE);
+ return;
+ }
+ }
+
+ mm_port_probe_set_result_at_xmm (self, FALSE);
+}
+
+static void
serial_probe_at_icera_result_processor (MMPortProbe *self,
GVariant *result)
{
@@ -859,114 +970,116 @@ serial_probe_at_result_processor (MMPortProbe *self,
static void
serial_probe_at_parse_response (MMPortSerialAt *port,
- GAsyncResult *res,
- MMPortProbe *self)
+ GAsyncResult *res,
+ MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
- GVariant *result = NULL;
- GError *result_error = NULL;
- const gchar *response;
- GError *error = NULL;
+ GVariant *result = NULL;
+ GError *result_error = NULL;
+ const gchar *response = NULL;
+ GError *error = NULL;
+ PortProbeRunContext *ctx;
- response = mm_port_serial_at_command_finish (port, res, &error);
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
- goto out;
+ if (port_probe_task_return_error_if_cancelled (self))
+ return;
/* If AT probing cancelled, end this partial probing */
- if (g_cancellable_is_cancelled (task->at_probing_cancellable)) {
- mm_dbg ("(%s/%s) no need to keep on probing the port for AT support",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
- task->at_result_processor (self, NULL);
+ if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) {
+ mm_obj_dbg (self, "no need to keep on probing the port for AT support");
+ ctx->at_result_processor (self, NULL);
serial_probe_schedule (self);
- goto out;
+ return;
}
- if (!task->at_commands->response_processor (task->at_commands->command,
- response,
- !!task->at_commands[1].command,
- error,
- &result,
- &result_error)) {
+ response = mm_port_serial_at_command_finish (port, res, &error);
+
+ if (!ctx->at_commands->response_processor (ctx->at_commands->command,
+ response,
+ !!ctx->at_commands[1].command,
+ error,
+ &result,
+ &result_error)) {
/* Were we told to abort the whole probing? */
if (result_error) {
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "(%s/%s) error while probing AT features: %s",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- result_error->message));
+ port_probe_task_return_error (self,
+ g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "(%s/%s) error while probing AT features: %s",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port),
+ result_error->message));
goto out;
}
/* Go on to next command */
- task->at_commands++;
- if (!task->at_commands->command) {
+ ctx->at_commands++;
+ if (!ctx->at_commands->command) {
/* Was it the last command in the group? If so,
* end this partial probing */
- task->at_result_processor (self, NULL);
+ ctx->at_result_processor (self, NULL);
/* Reschedule */
serial_probe_schedule (self);
goto out;
}
/* Schedule the next command in the probing group */
- task->source_id = g_idle_add ((GSourceFunc)serial_probe_at, self);
+ if (ctx->at_commands_wait_secs == 0)
+ ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_at, self);
+ else {
+ mm_obj_dbg (self, "re-scheduling next command in probing group in %u seconds...",
+ ctx->at_commands_wait_secs);
+ ctx->source_id = g_timeout_add_seconds (ctx->at_commands_wait_secs, (GSourceFunc) serial_probe_at, self);
+ }
goto out;
}
/* Run result processor.
* Note that custom init commands are allowed to not return anything */
- task->at_result_processor (self, result);
+ ctx->at_result_processor (self, result);
/* Reschedule probing */
serial_probe_schedule (self);
out:
- if (result)
- g_variant_unref (result);
- if (error)
- g_error_free (error);
- if (result_error)
- g_error_free (result_error);
+ g_clear_pointer (&result, g_variant_unref);
+ g_clear_error (&error);
+ g_clear_error (&result_error);
}
static gboolean
serial_probe_at (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
- task->source_id = 0;
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+ ctx->source_id = 0;
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
- return FALSE;
+ if (port_probe_task_return_error_if_cancelled (self))
+ return G_SOURCE_REMOVE;
/* If AT probing cancelled, end this partial probing */
- if (g_cancellable_is_cancelled (task->at_probing_cancellable)) {
- mm_dbg ("(%s/%s) no need to launch probing for AT support",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
- task->at_result_processor (self, NULL);
+ if (g_cancellable_is_cancelled (ctx->at_probing_cancellable)) {
+ mm_obj_dbg (self, "no need to launch probing for AT support");
+ ctx->at_result_processor (self, NULL);
serial_probe_schedule (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
mm_port_serial_at_command (
- MM_PORT_SERIAL_AT (task->serial),
- task->at_commands->command,
- task->at_commands->timeout,
+ MM_PORT_SERIAL_AT (ctx->serial),
+ ctx->at_commands->command,
+ ctx->at_commands->timeout,
FALSE,
FALSE,
- task->at_probing_cancellable,
+ ctx->at_probing_cancellable,
(GAsyncReadyCallback)serial_probe_at_parse_response,
self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
static const MMPortProbeAtCommand at_probing[] = {
@@ -992,6 +1105,13 @@ static const MMPortProbeAtCommand product_probing[] = {
static const MMPortProbeAtCommand icera_probing[] = {
{ "%IPSYS?", 3, mm_port_probe_response_processor_string },
+ { "%IPSYS?", 3, mm_port_probe_response_processor_string },
+ { "%IPSYS?", 3, mm_port_probe_response_processor_string },
+ { NULL }
+};
+
+static const MMPortProbeAtCommand xmm_probing[] = {
+ { "+XACT=?", 3, mm_port_probe_response_processor_string },
{ NULL }
};
@@ -999,17 +1119,20 @@ static void
at_custom_init_ready (MMPortProbe *self,
GAsyncResult *res)
{
- PortProbeRunTask *task = self->priv->task;
- GError *error = NULL;
+ GError *error = NULL;
+ PortProbeRunContext *ctx;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
- if (!task->at_custom_init_finish (self, res, &error)) {
+ if (!ctx->at_custom_init_finish (self, res, &error)) {
/* All errors propagated up end up forcing an UNSUPPORTED result */
- port_probe_run_task_complete (task, FALSE, error);
+ port_probe_task_return_error (self, error);
return;
}
/* Keep on with remaining probings */
- task->at_custom_init_run = TRUE;
+ ctx->at_custom_init_run = TRUE;
serial_probe_schedule (self);
}
@@ -1018,77 +1141,93 @@ at_custom_init_ready (MMPortProbe *self,
static void
serial_probe_schedule (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
+ if (port_probe_task_return_error_if_cancelled (self))
return;
/* If we got some custom initialization setup requested, go on with it
- * first. */
- if (!task->at_custom_init_run &&
- task->at_custom_init &&
- task->at_custom_init_finish) {
- task->at_custom_init (self,
- MM_PORT_SERIAL_AT (task->serial),
- task->at_probing_cancellable,
- (GAsyncReadyCallback)at_custom_init_ready,
- NULL);
+ * first. We completely ignore the custom initialization if the serial port
+ * that we receive in the context isn't an AT port (e.g. if it was flagged
+ * as not being an AT port early) */
+ if (!ctx->at_custom_init_run &&
+ ctx->at_custom_init &&
+ ctx->at_custom_init_finish &&
+ MM_IS_PORT_SERIAL_AT (ctx->serial)) {
+ ctx->at_custom_init (self,
+ MM_PORT_SERIAL_AT (ctx->serial),
+ ctx->at_probing_cancellable,
+ (GAsyncReadyCallback) at_custom_init_ready,
+ NULL);
return;
}
/* Cleanup */
- task->at_result_processor = NULL;
- task->at_commands = NULL;
+ ctx->at_result_processor = NULL;
+ ctx->at_commands = NULL;
+ ctx->at_commands_wait_secs = 0;
/* AT check requested and not already probed? */
- if ((task->flags & MM_PORT_PROBE_AT) &&
+ if ((ctx->flags & MM_PORT_PROBE_AT) &&
!(self->priv->flags & MM_PORT_PROBE_AT)) {
/* Prepare AT probing */
- if (task->at_custom_probe)
- task->at_commands = task->at_custom_probe;
+ if (ctx->at_custom_probe)
+ ctx->at_commands = ctx->at_custom_probe;
else
- task->at_commands = at_probing;
- task->at_result_processor = serial_probe_at_result_processor;
+ ctx->at_commands = at_probing;
+ ctx->at_result_processor = serial_probe_at_result_processor;
}
/* Vendor requested and not already probed? */
- else if ((task->flags & MM_PORT_PROBE_AT_VENDOR) &&
+ else if ((ctx->flags & MM_PORT_PROBE_AT_VENDOR) &&
!(self->priv->flags & MM_PORT_PROBE_AT_VENDOR)) {
/* Prepare AT vendor probing */
- task->at_result_processor = serial_probe_at_vendor_result_processor;
- task->at_commands = vendor_probing;
+ ctx->at_result_processor = serial_probe_at_vendor_result_processor;
+ ctx->at_commands = vendor_probing;
}
/* Product requested and not already probed? */
- else if ((task->flags & MM_PORT_PROBE_AT_PRODUCT) &&
+ else if ((ctx->flags & MM_PORT_PROBE_AT_PRODUCT) &&
!(self->priv->flags & MM_PORT_PROBE_AT_PRODUCT)) {
/* Prepare AT product probing */
- task->at_result_processor = serial_probe_at_product_result_processor;
- task->at_commands = product_probing;
+ ctx->at_result_processor = serial_probe_at_product_result_processor;
+ ctx->at_commands = product_probing;
}
/* Icera support check requested and not already done? */
- else if ((task->flags & MM_PORT_PROBE_AT_ICERA) &&
+ else if ((ctx->flags & MM_PORT_PROBE_AT_ICERA) &&
!(self->priv->flags & MM_PORT_PROBE_AT_ICERA)) {
/* Prepare AT product probing */
- task->at_result_processor = serial_probe_at_icera_result_processor;
- task->at_commands = icera_probing;
+ ctx->at_result_processor = serial_probe_at_icera_result_processor;
+ ctx->at_commands = icera_probing;
+ /* By default, wait 2 seconds between ICERA probing retries */
+ ctx->at_commands_wait_secs = 2;
+ }
+ /* XMM support check requested and not already done? */
+ else if ((ctx->flags & MM_PORT_PROBE_AT_XMM) &&
+ !(self->priv->flags & MM_PORT_PROBE_AT_XMM)) {
+ /* Prepare AT product probing */
+ ctx->at_result_processor = serial_probe_at_xmm_result_processor;
+ ctx->at_commands = xmm_probing;
}
/* If a next AT group detected, go for it */
- if (task->at_result_processor &&
- task->at_commands) {
- task->source_id = g_idle_add ((GSourceFunc)serial_probe_at, self);
+ if (ctx->at_result_processor &&
+ ctx->at_commands) {
+ ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_at, self);
return;
}
/* QCDM requested and not already probed? */
- if ((task->flags & MM_PORT_PROBE_QCDM) &&
+ if ((ctx->flags & MM_PORT_PROBE_QCDM) &&
!(self->priv->flags & MM_PORT_PROBE_QCDM)) {
- task->source_id = g_idle_add ((GSourceFunc)serial_probe_qcdm, self);
+ ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_qcdm, self);
return;
}
- /* All done! Finish asynchronously */
- port_probe_run_task_complete (task, TRUE, NULL);
+ /* All done! */
+ port_probe_task_return_boolean (self, TRUE);
}
static void
@@ -1104,27 +1243,29 @@ serial_flash_ready (MMPortSerial *port,
static void
serial_buffer_full (MMPortSerial *serial,
- GByteArray *buffer,
- MMPortProbe *self)
+ GByteArray *buffer,
+ MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
+ PortProbeRunContext *ctx;
- if (is_non_at_response (buffer->data, buffer->len)) {
- mm_dbg ("(%s/%s) serial buffer full",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
- /* Don't explicitly close the AT port, just end the AT probing
- * (or custom init probing) */
- mm_port_probe_set_result_at (self, FALSE);
- g_cancellable_cancel (task->at_probing_cancellable);
- }
+ if (!is_non_at_response (buffer->data, buffer->len))
+ return;
+
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+
+ mm_obj_dbg (self, "serial buffer full");
+ /* Don't explicitly close the AT port, just end the AT probing
+ * (or custom init probing) */
+ mm_port_probe_set_result_at (self, FALSE);
+ g_cancellable_cancel (ctx->at_probing_cancellable);
}
static gboolean
-serial_parser_filter_cb (gpointer filter,
- gpointer user_data,
- GString *response,
- GError **error)
+serial_parser_filter_cb (gpointer filter,
+ gpointer user_data,
+ GString *response,
+ GError **error)
{
if (is_non_at_response ((const guint8 *) response->str, response->len)) {
g_set_error (error,
@@ -1140,174 +1281,157 @@ serial_parser_filter_cb (gpointer filter,
static gboolean
serial_open_at (MMPortProbe *self)
{
- PortProbeRunTask *task = self->priv->task;
- GError *error = NULL;
+ GError *error = NULL;
+ PortProbeRunContext *ctx;
- task->source_id = 0;
+ g_assert (self->priv->task);
+ ctx = g_task_get_task_data (self->priv->task);
+ ctx->source_id = 0;
/* If already cancelled, do nothing else */
- if (port_probe_run_is_cancelled (self))
- return FALSE;
+ if (port_probe_task_return_error_if_cancelled (self))
+ return G_SOURCE_REMOVE;
/* Create AT serial port if not done before */
- if (!task->serial) {
+ if (!ctx->serial) {
gpointer parser;
MMPortSubsys subsys = MM_PORT_SUBSYS_TTY;
- if (g_str_has_prefix (g_udev_device_get_subsystem (self->priv->port), "usb"))
- subsys = MM_PORT_SUBSYS_USB;
-
- task->serial = MM_PORT_SERIAL (mm_port_serial_at_new (g_udev_device_get_name (self->priv->port),
- subsys));
- if (!task->serial) {
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "(%s/%s) couldn't create AT port",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port)));
- return FALSE;
+ if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "usbmisc"))
+ subsys = MM_PORT_SUBSYS_USBMISC;
+ else if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "rpmsg"))
+ subsys = MM_PORT_SUBSYS_RPMSG;
+ else if (g_str_equal (mm_kernel_device_get_subsystem (self->priv->port), "wwan"))
+ subsys = MM_PORT_SUBSYS_WWAN;
+
+ ctx->serial = MM_PORT_SERIAL (mm_port_serial_at_new (mm_kernel_device_get_name (self->priv->port), subsys));
+ if (!ctx->serial) {
+ port_probe_task_return_error (self,
+ g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "(%s/%s) couldn't create AT port",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port)));
+ return G_SOURCE_REMOVE;
}
- g_object_set (task->serial,
+ g_object_set (ctx->serial,
MM_PORT_SERIAL_SPEW_CONTROL, TRUE,
- MM_PORT_SERIAL_SEND_DELAY, (subsys == MM_PORT_SUBSYS_TTY ? task->at_send_delay : 0),
- MM_PORT_SERIAL_AT_REMOVE_ECHO, task->at_remove_echo,
- MM_PORT_SERIAL_AT_SEND_LF, task->at_send_lf,
+ MM_PORT_SERIAL_SEND_DELAY, (guint64)(subsys == MM_PORT_SUBSYS_TTY ? ctx->at_send_delay : 0),
+ MM_PORT_SERIAL_AT_REMOVE_ECHO, ctx->at_remove_echo,
+ MM_PORT_SERIAL_AT_SEND_LF, ctx->at_send_lf,
NULL);
+ common_serial_port_setup (self, ctx->serial);
+
parser = mm_serial_parser_v1_new ();
mm_serial_parser_v1_add_filter (parser,
serial_parser_filter_cb,
NULL);
- mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (task->serial),
+ mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (ctx->serial),
mm_serial_parser_v1_parse,
parser,
mm_serial_parser_v1_destroy);
}
/* Try to open the port */
- if (!mm_port_serial_open (task->serial, &error)) {
+ if (!mm_port_serial_open (ctx->serial, &error)) {
/* Abort if maximum number of open tries reached */
- if (++task->at_open_tries > 4) {
+ if (++ctx->at_open_tries > 4) {
/* took too long to open the port; give up */
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "(%s/%s) failed to open port after 4 tries",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port)));
- } else if (g_error_matches (error,
- MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) {
+ port_probe_task_return_error (self,
+ g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "(%s/%s) failed to open port after 4 tries",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port)));
+ g_clear_error (&error);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED_NO_DEVICE)) {
/* this is nozomi being dumb; try again */
- task->source_id = g_timeout_add_seconds (1,
- (GSourceFunc)serial_open_at,
- self);
- } else {
- port_probe_run_task_complete (
- task,
- FALSE,
- g_error_new (MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_OPEN_FAILED,
- "(%s/%s) failed to open port: %s",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- (error ? error->message : "unknown error")));
+ ctx->source_id = g_timeout_add_seconds (1, (GSourceFunc) serial_open_at, self);
+ g_clear_error (&error);
+ return G_SOURCE_REMOVE;
}
+ port_probe_task_return_error (self,
+ g_error_new (MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_OPEN_FAILED,
+ "(%s/%s) failed to open port: %s",
+ mm_kernel_device_get_subsystem (self->priv->port),
+ mm_kernel_device_get_name (self->priv->port),
+ (error ? error->message : "unknown error")));
g_clear_error (&error);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/* success, start probing */
- task->buffer_full_id = g_signal_connect (task->serial,
- "buffer-full",
- G_CALLBACK (serial_buffer_full),
- self);
-
- mm_port_serial_flash (MM_PORT_SERIAL (task->serial),
+ ctx->buffer_full_id = g_signal_connect (ctx->serial, "buffer-full",
+ G_CALLBACK (serial_buffer_full), self);
+ mm_port_serial_flash (MM_PORT_SERIAL (ctx->serial),
100,
TRUE,
- (GAsyncReadyCallback)serial_flash_ready,
+ (GAsyncReadyCallback) serial_flash_ready,
self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
-gboolean
-mm_port_probe_run_cancel_at_probing (MMPortProbe *self)
+static void
+at_cancellable_cancel (GCancellable *cancellable,
+ PortProbeRunContext *ctx)
{
- g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
-
- if (self->priv->task) {
- mm_dbg ("(%s/%s) requested to cancel all AT probing",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
- g_cancellable_cancel (self->priv->task->at_probing_cancellable);
- return TRUE;
- }
-
- return FALSE;
+ /* Avoid trying to disconnect cancellable on the handler, or we'll deadlock */
+ ctx->at_probing_cancellable_linked = 0;
+ g_cancellable_cancel (ctx->at_probing_cancellable);
}
gboolean
-mm_port_probe_run_cancel (MMPortProbe *self)
+mm_port_probe_run_cancel_at_probing (MMPortProbe *self)
{
+ PortProbeRunContext *ctx;
+
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- if (self->priv->task) {
- mm_dbg ("(%s/%s) requested to cancel the probing",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port));
- g_cancellable_cancel (self->priv->task->cancellable);
- return TRUE;
- }
+ if (!self->priv->task)
+ return FALSE;
- return FALSE;
+ ctx = g_task_get_task_data (self->priv->task);
+ if (g_cancellable_is_cancelled (ctx->at_probing_cancellable))
+ return FALSE;
+
+ mm_obj_dbg (self, "requested to cancel all AT probing");
+ g_cancellable_cancel (ctx->at_probing_cancellable);
+ return TRUE;
}
gboolean
-mm_port_probe_run_finish (MMPortProbe *self,
- GAsyncResult *result,
- GError **error)
+mm_port_probe_run_finish (MMPortProbe *self,
+ GAsyncResult *result,
+ GError **error)
{
- gboolean res;
-
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
-
- /* Propagate error, if any */
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
- res = FALSE;
- else
- res = g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result));
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
- /* Cleanup probing task */
- if (self->priv->task) {
- port_probe_run_task_free (self->priv->task);
- self->priv->task = NULL;
- }
- return res;
+ return g_task_propagate_boolean (G_TASK (result), error);
}
void
-mm_port_probe_run (MMPortProbe *self,
- MMPortProbeFlag flags,
- guint64 at_send_delay,
- gboolean at_remove_echo,
- gboolean at_send_lf,
+mm_port_probe_run (MMPortProbe *self,
+ MMPortProbeFlag flags,
+ guint64 at_send_delay,
+ gboolean at_remove_echo,
+ gboolean at_send_lf,
const MMPortProbeAtCommand *at_custom_probe,
- const MMAsyncMethod *at_custom_init,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ const MMAsyncMethod *at_custom_init,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- PortProbeRunTask *task;
- guint32 i;
- gchar *probe_list_str;
+ PortProbeRunContext *ctx;
+ gchar *probe_list_str;
+ guint32 i;
g_return_if_fail (MM_IS_PORT_PROBE (self));
g_return_if_fail (flags != MM_PORT_PROBE_NONE);
@@ -1315,69 +1439,125 @@ mm_port_probe_run (MMPortProbe *self,
/* Shouldn't schedule more than one probing at a time */
g_assert (self->priv->task == NULL);
+ self->priv->task = g_task_new (self, cancellable, callback, user_data);
+
+ /* Task context */
+ ctx = g_slice_new0 (PortProbeRunContext);
+ ctx->at_send_delay = at_send_delay;
+ ctx->at_remove_echo = at_remove_echo;
+ ctx->at_send_lf = at_send_lf;
+ ctx->flags = MM_PORT_PROBE_NONE;
+ ctx->at_custom_probe = at_custom_probe;
+ ctx->at_custom_init = at_custom_init ? (MMPortProbeAtCustomInit)at_custom_init->async : NULL;
+ ctx->at_custom_init_finish = at_custom_init ? (MMPortProbeAtCustomInitFinish)at_custom_init->finish : NULL;
+ ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+
+ /* The context will be owned by the task */
+ g_task_set_task_data (self->priv->task, ctx, (GDestroyNotify) port_probe_run_context_free);
+
+ /* If we're told to completely ignore the port, don't do any probing */
+ if (self->priv->is_ignored) {
+ mm_obj_dbg (self, "port probing finished: skipping for blacklisted port");
+ port_probe_task_return_boolean (self, TRUE);
+ return;
+ }
+
+ /* If this is a port flagged as a GPS port, don't do any other probing */
+ if (self->priv->is_gps) {
+ mm_obj_dbg (self, "GPS port detected");
+ mm_port_probe_set_result_at (self, FALSE);
+ mm_port_probe_set_result_qcdm (self, FALSE);
+ mm_port_probe_set_result_qmi (self, FALSE);
+ mm_port_probe_set_result_mbim (self, FALSE);
+ }
- task = g_new0 (PortProbeRunTask, 1);
- task->at_send_delay = at_send_delay;
- task->at_remove_echo = at_remove_echo;
- task->at_send_lf = at_send_lf;
- task->flags = MM_PORT_PROBE_NONE;
- task->at_custom_probe = at_custom_probe;
- task->at_custom_init = at_custom_init ? (MMPortProbeAtCustomInit)at_custom_init->async : NULL;
- task->at_custom_init_finish = at_custom_init ? (MMPortProbeAtCustomInitFinish)at_custom_init->finish : NULL;
+ /* If this is a port flagged as an audio port, don't do any other probing */
+ if (self->priv->is_audio) {
+ mm_obj_dbg (self, "audio port detected");
+ mm_port_probe_set_result_at (self, FALSE);
+ mm_port_probe_set_result_qcdm (self, FALSE);
+ mm_port_probe_set_result_qmi (self, FALSE);
+ mm_port_probe_set_result_mbim (self, FALSE);
+ }
+
+ /* If this is a port flagged as being an AT port, don't do any other probing */
+ if (self->priv->maybe_at_primary || self->priv->maybe_at_secondary || self->priv->maybe_at_ppp) {
+ mm_obj_dbg (self, "no QCDM/QMI/MBIM probing in possible AT port");
+ mm_port_probe_set_result_qcdm (self, FALSE);
+ mm_port_probe_set_result_qmi (self, FALSE);
+ mm_port_probe_set_result_mbim (self, FALSE);
+ }
- task->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_probe_run);
+ /* If this is a port flagged as being a QCDM port, don't do any other probing */
+ if (self->priv->maybe_qcdm) {
+ mm_obj_dbg (self, "no AT/QMI/MBIM probing in possible QCDM port");
+ mm_port_probe_set_result_at (self, FALSE);
+ mm_port_probe_set_result_qmi (self, FALSE);
+ mm_port_probe_set_result_mbim (self, FALSE);
+ }
+
+ /* If this is a port flagged as being a QMI port, don't do any other probing */
+ if (self->priv->maybe_qmi) {
+ mm_obj_dbg (self, "no AT/QCDM/MBIM probing in possible QMI port");
+ mm_port_probe_set_result_at (self, FALSE);
+ mm_port_probe_set_result_qcdm (self, FALSE);
+ mm_port_probe_set_result_mbim (self, FALSE);
+ }
+
+ /* If this is a port flagged as being a MBIM port, don't do any other probing */
+ if (self->priv->maybe_mbim) {
+ mm_obj_dbg (self, "no AT/QCDM/QMI probing in possible MBIM port");
+ mm_port_probe_set_result_at (self, FALSE);
+ mm_port_probe_set_result_qcdm (self, FALSE);
+ mm_port_probe_set_result_qmi (self, FALSE);
+ }
/* Check if we already have the requested probing results.
- * We will fix here the 'task->flags' so that we only request probing
+ * We will fix here the 'ctx->flags' so that we only request probing
* for the missing things. */
for (i = MM_PORT_PROBE_AT; i <= MM_PORT_PROBE_MBIM; i = (i << 1)) {
- if ((flags & i) && !(self->priv->flags & i)) {
- task->flags += i;
- }
+ if ((flags & i) && !(self->priv->flags & i))
+ ctx->flags += i;
}
- /* Store as current task. We need to keep it internally, as it will be
- * freed during _finish() when the operation is completed. */
- self->priv->task = task;
-
/* All requested probings already available? If so, we're done */
- if (!task->flags) {
- port_probe_run_task_complete (task, TRUE, NULL);
+ if (!ctx->flags) {
+ mm_obj_dbg (self, "port probing finished: no more probings needed");
+ port_probe_task_return_boolean (self, TRUE);
return;
}
- /* Setup internal cancellable */
- task->cancellable = g_cancellable_new ();
-
- probe_list_str = mm_port_probe_flag_build_string_from_mask (task->flags);
- mm_dbg ("(%s/%s) launching port probing: '%s'",
- g_udev_device_get_subsystem (self->priv->port),
- g_udev_device_get_name (self->priv->port),
- probe_list_str);
+ /* Log the probes scheduled to be run */
+ probe_list_str = mm_port_probe_flag_build_string_from_mask (ctx->flags);
+ mm_obj_dbg (self, "launching port probing: '%s'", probe_list_str);
g_free (probe_list_str);
/* If any AT probing is needed, start by opening as AT port */
- if (task->flags & MM_PORT_PROBE_AT ||
- task->flags & MM_PORT_PROBE_AT_VENDOR ||
- task->flags & MM_PORT_PROBE_AT_PRODUCT ||
- task->flags & MM_PORT_PROBE_AT_ICERA) {
- task->at_probing_cancellable = g_cancellable_new ();
- task->source_id = g_idle_add ((GSourceFunc)serial_open_at, self);
+ if (ctx->flags & MM_PORT_PROBE_AT ||
+ ctx->flags & MM_PORT_PROBE_AT_VENDOR ||
+ ctx->flags & MM_PORT_PROBE_AT_PRODUCT ||
+ ctx->flags & MM_PORT_PROBE_AT_ICERA ||
+ ctx->flags & MM_PORT_PROBE_AT_XMM) {
+ ctx->at_probing_cancellable = g_cancellable_new ();
+ /* If the main cancellable is cancelled, so will be the at-probing one */
+ if (cancellable)
+ ctx->at_probing_cancellable_linked = g_cancellable_connect (cancellable,
+ (GCallback) at_cancellable_cancel,
+ ctx,
+ NULL);
+ ctx->source_id = g_idle_add ((GSourceFunc) serial_open_at, self);
return;
}
/* If QCDM probing needed, start by opening as QCDM port */
- if (task->flags & MM_PORT_PROBE_QCDM) {
- task->source_id = g_idle_add ((GSourceFunc)serial_probe_qcdm, self);
+ if (ctx->flags & MM_PORT_PROBE_QCDM) {
+ ctx->source_id = g_idle_add ((GSourceFunc) serial_probe_qcdm, self);
return;
}
/* If QMI/MBIM probing needed, go on */
- if (task->flags & MM_PORT_PROBE_QMI || task->flags & MM_PORT_PROBE_MBIM) {
- task->source_id = g_idle_add ((GSourceFunc)wdm_probe, self);
+ if (ctx->flags & MM_PORT_PROBE_QMI || ctx->flags & MM_PORT_PROBE_MBIM) {
+ ctx->source_id = g_idle_add ((GSourceFunc) wdm_probe, self);
return;
}
@@ -1389,12 +1569,10 @@ gboolean
mm_port_probe_is_at (MMPortProbe *self)
{
const gchar *subsys;
- const gchar *name;
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
+ subsys = mm_kernel_device_get_subsystem (self->priv->port);
if (g_str_equal (subsys, "net"))
return FALSE;
@@ -1423,18 +1601,8 @@ mm_port_probe_list_has_at_port (GList *list)
gboolean
mm_port_probe_is_qcdm (MMPortProbe *self)
{
- const gchar *subsys;
- const gchar *name;
-
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
- if (g_str_equal (subsys, "net") ||
- (g_str_has_prefix (subsys, "usb") &&
- g_str_has_prefix (name, "cdc-wdm")))
- return FALSE;
-
return (self->priv->flags & MM_PORT_PROBE_QCDM ?
self->priv->is_qcdm :
FALSE);
@@ -1443,19 +1611,11 @@ mm_port_probe_is_qcdm (MMPortProbe *self)
gboolean
mm_port_probe_is_qmi (MMPortProbe *self)
{
- const gchar *subsys;
- const gchar *name;
-
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
- if (!g_str_has_prefix (subsys, "usb") ||
- !name ||
- !g_str_has_prefix (name, "cdc-wdm"))
- return FALSE;
-
- return self->priv->is_qmi;
+ return (self->priv->flags & MM_PORT_PROBE_QMI ?
+ self->priv->is_qmi :
+ FALSE);
}
gboolean
@@ -1477,19 +1637,11 @@ mm_port_probe_list_has_qmi_port (GList *list)
gboolean
mm_port_probe_is_mbim (MMPortProbe *self)
{
- const gchar *subsys;
- const gchar *name;
-
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
- if (!g_str_has_prefix (subsys, "usb") ||
- !name ||
- !g_str_has_prefix (name, "cdc-wdm"))
- return FALSE;
-
- return self->priv->is_mbim;
+ return (self->priv->flags & MM_PORT_PROBE_MBIM ?
+ self->priv->is_mbim :
+ FALSE);
}
gboolean
@@ -1512,26 +1664,22 @@ MMPortType
mm_port_probe_get_port_type (MMPortProbe *self)
{
const gchar *subsys;
- const gchar *name;
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
+ subsys = mm_kernel_device_get_subsystem (self->priv->port);
if (g_str_equal (subsys, "net"))
return MM_PORT_TYPE_NET;
#if defined WITH_QMI
- if (g_str_has_prefix (subsys, "usb") &&
- g_str_has_prefix (name, "cdc-wdm") &&
+ if (self->priv->flags & MM_PORT_PROBE_QMI &&
self->priv->is_qmi)
return MM_PORT_TYPE_QMI;
#endif
#if defined WITH_MBIM
- if (g_str_has_prefix (subsys, "usb") &&
- g_str_has_prefix (name, "cdc-wdm") &&
+ if (self->priv->flags & MM_PORT_PROBE_MBIM &&
self->priv->is_mbim)
return MM_PORT_TYPE_MBIM;
#endif
@@ -1544,6 +1692,12 @@ mm_port_probe_get_port_type (MMPortProbe *self)
self->priv->is_at)
return MM_PORT_TYPE_AT;
+ if (self->priv->is_gps)
+ return MM_PORT_TYPE_GPS;
+
+ if (self->priv->is_audio)
+ return MM_PORT_TYPE_AUDIO;
+
return MM_PORT_TYPE_UNKNOWN;
}
@@ -1563,7 +1717,7 @@ mm_port_probe_get_device (MMPortProbe *self)
return MM_DEVICE (g_object_ref (self->priv->device));
}
-GUdevDevice *
+MMKernelDevice *
mm_port_probe_peek_port (MMPortProbe *self)
{
g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
@@ -1571,29 +1725,19 @@ mm_port_probe_peek_port (MMPortProbe *self)
return self->priv->port;
};
-GUdevDevice *
+MMKernelDevice *
mm_port_probe_get_port (MMPortProbe *self)
{
g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
- return G_UDEV_DEVICE (g_object_ref (self->priv->port));
+ return MM_KERNEL_DEVICE (g_object_ref (self->priv->port));
};
const gchar *
mm_port_probe_get_vendor (MMPortProbe *self)
{
- const gchar *subsys;
- const gchar *name;
-
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
- if (g_str_equal (subsys, "net") ||
- (g_str_has_prefix (subsys, "usb") &&
- g_str_has_prefix (name, "cdc-wdm")))
- return NULL;
-
return (self->priv->flags & MM_PORT_PROBE_AT_VENDOR ?
self->priv->vendor :
NULL);
@@ -1602,18 +1746,8 @@ mm_port_probe_get_vendor (MMPortProbe *self)
const gchar *
mm_port_probe_get_product (MMPortProbe *self)
{
- const gchar *subsys;
- const gchar *name;
-
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- subsys = g_udev_device_get_subsystem (self->priv->port);
- name = g_udev_device_get_name (self->priv->port);
- if (g_str_equal (subsys, "net") ||
- (g_str_has_prefix (subsys, "usb") &&
- g_str_has_prefix (name, "cdc-wdm")))
- return NULL;
-
return (self->priv->flags & MM_PORT_PROBE_AT_PRODUCT ?
self->priv->product :
NULL);
@@ -1624,9 +1758,6 @@ mm_port_probe_is_icera (MMPortProbe *self)
{
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
- if (g_str_equal (g_udev_device_get_subsystem (self->priv->port), "net"))
- return FALSE;
-
return (self->priv->flags & MM_PORT_PROBE_AT_ICERA ?
self->priv->is_icera :
FALSE);
@@ -1646,6 +1777,29 @@ mm_port_probe_list_is_icera (GList *probes)
}
gboolean
+mm_port_probe_is_xmm (MMPortProbe *self)
+{
+ g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
+
+ return (self->priv->flags & MM_PORT_PROBE_AT_XMM ?
+ self->priv->is_xmm :
+ FALSE);
+}
+
+gboolean
+mm_port_probe_list_is_xmm (GList *probes)
+{
+ GList *l;
+
+ for (l = probes; l; l = g_list_next (l)) {
+ if (mm_port_probe_is_xmm (MM_PORT_PROBE (l->data)))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
mm_port_probe_is_ignored (MMPortProbe *self)
{
g_return_val_if_fail (MM_IS_PORT_PROBE (self), FALSE);
@@ -1658,7 +1812,7 @@ mm_port_probe_get_port_name (MMPortProbe *self)
{
g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
- return g_udev_device_get_name (self->priv->port);
+ return mm_kernel_device_get_name (self->priv->port);
}
const gchar *
@@ -1666,22 +1820,25 @@ mm_port_probe_get_port_subsys (MMPortProbe *self)
{
g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
- return g_udev_device_get_subsystem (self->priv->port);
+ return mm_kernel_device_get_subsystem (self->priv->port);
}
-const gchar *
-mm_port_probe_get_parent_path (MMPortProbe *self)
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
{
- g_return_val_if_fail (MM_IS_PORT_PROBE (self), NULL);
+ MMPortProbe *self;
- return (self->priv->parent ? g_udev_device_get_sysfs_path (self->priv->parent) : NULL);
+ self = MM_PORT_PROBE (_self);
+ return g_strdup_printf ("%s/probe", mm_kernel_device_get_name (self->priv->port));
}
/*****************************************************************************/
MMPortProbe *
-mm_port_probe_new (MMDevice *device,
- GUdevDevice *port)
+mm_port_probe_new (MMDevice *device,
+ MMKernelDevice *port)
{
return MM_PORT_PROBE (g_object_new (MM_TYPE_PORT_PROBE,
MM_PORT_PROBE_DEVICE, device,
@@ -1713,8 +1870,15 @@ set_property (GObject *object,
case PROP_PORT:
/* construct only */
self->priv->port = g_value_dup_object (value);
- self->priv->parent = g_udev_device_get_parent (self->priv->port);
- self->priv->is_ignored = g_udev_device_get_property_as_boolean (self->priv->port, "ID_MM_PORT_IGNORE");
+ self->priv->is_ignored = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_IGNORE);
+ self->priv->is_gps = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_GPS);
+ self->priv->is_audio = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AUDIO);
+ self->priv->maybe_at_primary = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AT_PRIMARY);
+ self->priv->maybe_at_secondary = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AT_SECONDARY);
+ self->priv->maybe_at_ppp = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_AT_PPP);
+ self->priv->maybe_qcdm = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_QCDM);
+ self->priv->maybe_qmi = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_QMI);
+ self->priv->maybe_mbim = mm_kernel_device_get_property_as_boolean (self->priv->port, ID_MM_PORT_TYPE_MBIM);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1765,13 +1929,18 @@ dispose (GObject *object)
/* We didn't get a reference to the device */
self->priv->device = NULL;
- g_clear_object (&self->priv->parent);
g_clear_object (&self->priv->port);
G_OBJECT_CLASS (mm_port_probe_parent_class)->dispose (object);
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_port_probe_class_init (MMPortProbeClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -1795,8 +1964,8 @@ mm_port_probe_class_init (MMPortProbeClass *klass)
properties[PROP_PORT] =
g_param_spec_object (MM_PORT_PROBE_PORT,
"Port",
- "UDev device object of the port",
- G_UDEV_TYPE_DEVICE,
+ "kernel device object of the port",
+ MM_TYPE_KERNEL_DEVICE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_PORT, properties[PROP_PORT]);
}
diff --git a/src/mm-port-probe.h b/src/mm-port-probe.h
index 0b3226a0..9c2fd893 100644
--- a/src/mm-port-probe.h
+++ b/src/mm-port-probe.h
@@ -10,7 +10,8 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2011 Aleksander Morgado <aleksander@gnu.org>
+ * Copyright (C) 2009 - 2018 Red Hat, Inc.
+ * Copyright (C) 2011 - 2018 Aleksander Morgado <aleksander@aleksander.es>
*/
#ifndef MM_PORT_PROBE_H
@@ -21,11 +22,11 @@
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
-#include <gudev/gudev.h>
#include "mm-private-boxed-types.h"
#include "mm-port-probe-at.h"
#include "mm-port-serial-at.h"
+#include "mm-kernel-device.h"
#include "mm-device.h"
#define MM_TYPE_PORT_PROBE (mm_port_probe_get_type ())
@@ -42,9 +43,10 @@ typedef enum { /*< underscore_name=mm_port_probe_flag >*/
MM_PORT_PROBE_AT_VENDOR = 1 << 1,
MM_PORT_PROBE_AT_PRODUCT = 1 << 2,
MM_PORT_PROBE_AT_ICERA = 1 << 3,
- MM_PORT_PROBE_QCDM = 1 << 4,
- MM_PORT_PROBE_QMI = 1 << 5,
- MM_PORT_PROBE_MBIM = 1 << 6
+ MM_PORT_PROBE_AT_XMM = 1 << 4,
+ MM_PORT_PROBE_QCDM = 1 << 5,
+ MM_PORT_PROBE_QMI = 1 << 6,
+ MM_PORT_PROBE_MBIM = 1 << 7,
} MMPortProbeFlag;
typedef struct _MMPortProbe MMPortProbe;
@@ -77,17 +79,17 @@ typedef gboolean (* MMPortProbeAtCustomInitFinish) (MMPortProbe *probe,
GError **error);
GType mm_port_probe_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortProbe, g_object_unref)
-MMPortProbe *mm_port_probe_new (MMDevice *device,
- GUdevDevice *port);
+MMPortProbe *mm_port_probe_new (MMDevice *device,
+ MMKernelDevice *port);
-MMDevice *mm_port_probe_peek_device (MMPortProbe *self);
-MMDevice *mm_port_probe_get_device (MMPortProbe *self);
-GUdevDevice *mm_port_probe_peek_port (MMPortProbe *self);
-GUdevDevice *mm_port_probe_get_port (MMPortProbe *self);
-const gchar *mm_port_probe_get_port_name (MMPortProbe *self);
-const gchar *mm_port_probe_get_port_subsys (MMPortProbe *self);
-const gchar *mm_port_probe_get_parent_path (MMPortProbe *self);
+MMDevice *mm_port_probe_peek_device (MMPortProbe *self);
+MMDevice *mm_port_probe_get_device (MMPortProbe *self);
+MMKernelDevice *mm_port_probe_peek_port (MMPortProbe *self);
+MMKernelDevice *mm_port_probe_get_port (MMPortProbe *self);
+const gchar *mm_port_probe_get_port_name (MMPortProbe *self);
+const gchar *mm_port_probe_get_port_subsys (MMPortProbe *self);
/* Probing result setters */
void mm_port_probe_set_result_at (MMPortProbe *self,
@@ -98,6 +100,8 @@ void mm_port_probe_set_result_at_product (MMPortProbe *self,
const gchar *at_product);
void mm_port_probe_set_result_at_icera (MMPortProbe *self,
gboolean is_icera);
+void mm_port_probe_set_result_at_xmm (MMPortProbe *self,
+ gboolean is_xmm);
void mm_port_probe_set_result_qcdm (MMPortProbe *self,
gboolean qcdm);
void mm_port_probe_set_result_qmi (MMPortProbe *self,
@@ -113,12 +117,12 @@ void mm_port_probe_run (MMPortProbe *self,
gboolean at_send_lf,
const MMPortProbeAtCommand *at_custom_probe,
const MMAsyncMethod *at_custom_init,
+ GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_port_probe_run_finish (MMPortProbe *self,
GAsyncResult *result,
GError **error);
-gboolean mm_port_probe_run_cancel (MMPortProbe *self);
gboolean mm_port_probe_run_cancel_at_probing (MMPortProbe *self);
@@ -131,6 +135,7 @@ gboolean mm_port_probe_is_mbim (MMPortProbe *self);
const gchar *mm_port_probe_get_vendor (MMPortProbe *self);
const gchar *mm_port_probe_get_product (MMPortProbe *self);
gboolean mm_port_probe_is_icera (MMPortProbe *self);
+gboolean mm_port_probe_is_xmm (MMPortProbe *self);
gboolean mm_port_probe_is_ignored (MMPortProbe *self);
/* Additional helpers */
@@ -138,5 +143,6 @@ gboolean mm_port_probe_list_has_at_port (GList *list);
gboolean mm_port_probe_list_has_qmi_port (GList *list);
gboolean mm_port_probe_list_has_mbim_port (GList *list);
gboolean mm_port_probe_list_is_icera (GList *list);
+gboolean mm_port_probe_list_is_xmm (GList *list);
#endif /* MM_PORT_PROBE_H */
diff --git a/src/mm-port-qmi.c b/src/mm-port-qmi.c
index bc5c7384..a7fbee84 100644
--- a/src/mm-port-qmi.c
+++ b/src/mm-port-qmi.c
@@ -10,9 +10,11 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*
- * Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2012-2021 Google, Inc.
+ * Copyright (C) 2021 Aleksander Morgado <aleksander@aleksander.es>
*/
+#include <config.h>
#include <stdio.h>
#include <stdlib.h>
@@ -22,46 +24,103 @@
#include <mm-errors-types.h>
#include "mm-port-qmi.h"
-#include "mm-log.h"
+#include "mm-port-net.h"
+#include "mm-port-enums-types.h"
+#include "mm-modem-helpers-qmi.h"
+#include "mm-log-object.h"
+
+#define DEFAULT_LINK_PREALLOCATED_AMOUNT 4
+
+/* as internally defined in the kernel */
+#define RMNET_MAX_PACKET_SIZE 16384
G_DEFINE_TYPE (MMPortQmi, mm_port_qmi, MM_TYPE_PORT)
+#if defined WITH_QRTR
+
+enum {
+ PROP_0,
+ PROP_NODE,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+#endif
+
typedef struct {
- QmiService service;
- QmiClient *client;
- MMPortQmiFlag flag;
+ QmiService service;
+ QmiClient *client;
+ guint flag;
} ServiceInfo;
struct _MMPortQmiPrivate {
- gboolean opening;
+ gboolean in_progress;
QmiDevice *qmi_device;
- GList *services;
+ GList *services;
+ gchar *net_driver;
+ gchar *net_sysfs_path;
+#if defined WITH_QRTR
+ QrtrNode *node;
+#endif
+
+ /* endpoint info */
+ gulong endpoint_info_signal_id;
+ QmiDataEndpointType endpoint_type;
+ gint endpoint_interface_number;
+ /* kernel data mode */
+ MMPortQmiKernelDataMode kernel_data_modes;
+ /* wda settings */
+ gboolean wda_unsupported;
+ QmiWdaLinkLayerProtocol llp;
+ QmiWdaDataAggregationProtocol dap;
+ guint max_multiplexed_links;
+ /* preallocated links */
+ MMPort *preallocated_links_master;
+ GArray *preallocated_links;
+ GList *preallocated_links_setup_pending;
};
/*****************************************************************************/
-QmiClient *
-mm_port_qmi_peek_client (MMPortQmi *self,
- QmiService service,
- MMPortQmiFlag flag)
+static QmiClient *
+lookup_client (MMPortQmi *self,
+ QmiService service,
+ guint flag,
+ gboolean steal)
{
GList *l;
for (l = self->priv->services; l; l = g_list_next (l)) {
ServiceInfo *info = l->data;
- if (info->service == service &&
- info->flag == flag)
- return info->client;
+ if (info->service == service && info->flag == flag) {
+ QmiClient *found;
+
+ found = info->client;
+ if (steal) {
+ self->priv->services = g_list_delete_link (self->priv->services, l);
+ g_free (info);
+ }
+ return found;
+ }
}
return NULL;
}
QmiClient *
-mm_port_qmi_get_client (MMPortQmi *self,
- QmiService service,
- MMPortQmiFlag flag)
+mm_port_qmi_peek_client (MMPortQmi *self,
+ QmiService service,
+ guint flag)
+{
+ return lookup_client (self, service, flag, FALSE);
+}
+
+QmiClient *
+mm_port_qmi_get_client (MMPortQmi *self,
+ QmiService service,
+ guint flag)
{
QmiClient *client;
@@ -71,22 +130,100 @@ mm_port_qmi_get_client (MMPortQmi *self,
/*****************************************************************************/
+QmiDevice *
+mm_port_qmi_peek_device (MMPortQmi *self)
+{
+ g_return_val_if_fail (MM_IS_PORT_QMI (self), NULL);
+
+ return self->priv->qmi_device;
+}
+
+/*****************************************************************************/
+
+static void
+initialize_endpoint_info (MMPortQmi *self)
+{
+ MMKernelDevice *kernel_device;
+
+ kernel_device = mm_port_peek_kernel_device (MM_PORT (self));
+
+ if (!kernel_device)
+ self->priv->endpoint_type = QMI_DATA_ENDPOINT_TYPE_UNDEFINED;
+ else
+ self->priv->endpoint_type = mm_port_subsys_to_qmi_endpoint_type (mm_port_get_subsys (MM_PORT (self)));
+
+ switch (self->priv->endpoint_type) {
+ case QMI_DATA_ENDPOINT_TYPE_HSUSB:
+ g_assert (kernel_device);
+ self->priv->endpoint_interface_number = mm_kernel_device_get_interface_number (kernel_device);
+ break;
+ case QMI_DATA_ENDPOINT_TYPE_EMBEDDED:
+ self->priv->endpoint_interface_number = 1;
+ break;
+ case QMI_DATA_ENDPOINT_TYPE_PCIE:
+ case QMI_DATA_ENDPOINT_TYPE_UNDEFINED:
+ case QMI_DATA_ENDPOINT_TYPE_HSIC:
+ case QMI_DATA_ENDPOINT_TYPE_BAM_DMUX:
+ case QMI_DATA_ENDPOINT_TYPE_UNKNOWN:
+ default:
+ self->priv->endpoint_interface_number = 0;
+ break;
+ }
+
+ mm_obj_dbg (self, "endpoint info updated: type '%s', interface number '%u'",
+ qmi_data_endpoint_type_get_string (self->priv->endpoint_type),
+ self->priv->endpoint_interface_number);
+}
+
+QmiDataEndpointType
+mm_port_qmi_get_endpoint_type (MMPortQmi *self)
+{
+ return self->priv->endpoint_type;
+}
+
+guint
+mm_port_qmi_get_endpoint_interface_number (MMPortQmi *self)
+{
+ return self->priv->endpoint_interface_number;
+}
+
+/*****************************************************************************/
+
+void
+mm_port_qmi_release_client (MMPortQmi *self,
+ QmiService service,
+ MMPortQmiFlag flag)
+{
+ QmiClient *client;
+
+ if (!self->priv->qmi_device)
+ return;
+
+ client = lookup_client (self, service, flag, TRUE);
+ if (!client)
+ return;
+
+ mm_obj_dbg (self, "explicitly releasing client for service '%s'...", qmi_service_get_string (service));
+ qmi_device_release_client (self->priv->qmi_device,
+ client,
+ QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+ 3, NULL, NULL, NULL);
+ g_object_unref (client);
+}
+
+/*****************************************************************************/
+
typedef struct {
- MMPortQmi *self;
- GSimpleAsyncResult *result;
ServiceInfo *info;
} AllocateClientContext;
static void
-allocate_client_context_complete_and_free (AllocateClientContext *ctx)
+allocate_client_context_free (AllocateClientContext *ctx)
{
- g_simple_async_result_complete (ctx->result);
if (ctx->info) {
g_assert (ctx->info->client == NULL);
g_free (ctx->info);
}
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_free (ctx);
}
@@ -95,62 +232,69 @@ mm_port_qmi_allocate_client_finish (MMPortQmi *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
allocate_client_ready (QmiDevice *qmi_device,
GAsyncResult *res,
- AllocateClientContext *ctx)
+ GTask *task)
{
+ MMPortQmi *self;
+ AllocateClientContext *ctx;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
ctx->info->client = qmi_device_allocate_client_finish (qmi_device, res, &error);
if (!ctx->info->client) {
g_prefix_error (&error,
"Couldn't create client for service '%s': ",
qmi_service_get_string (ctx->info->service));
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
} else {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
/* Move the service info to our internal list */
- ctx->self->priv->services = g_list_prepend (ctx->self->priv->services, ctx->info);
+ self->priv->services = g_list_prepend (self->priv->services, ctx->info);
ctx->info = NULL;
+ g_task_return_boolean (task, TRUE);
}
- allocate_client_context_complete_and_free (ctx);
+ g_object_unref (task);
}
void
-mm_port_qmi_allocate_client (MMPortQmi *self,
- QmiService service,
- MMPortQmiFlag flag,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_port_qmi_allocate_client (MMPortQmi *self,
+ QmiService service,
+ guint flag,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
AllocateClientContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (!mm_port_qmi_is_open (self)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Port is closed");
+ g_object_unref (task);
+ return;
+ }
if (!!mm_port_qmi_peek_client (self, service, flag)) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_EXISTS,
- "Client for service '%s' already allocated",
- qmi_service_get_string (service));
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS,
+ "Client for service '%s' already allocated",
+ qmi_service_get_string (service));
+ g_object_unref (task);
return;
}
ctx = g_new0 (AllocateClientContext, 1);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_qmi_allocate_client);
ctx->info = g_new0 (ServiceInfo, 1);
ctx->info->service = service;
ctx->info->flag = flag;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)allocate_client_context_free);
qmi_device_allocate_client (self->priv->qmi_device,
service,
@@ -158,140 +302,2227 @@ mm_port_qmi_allocate_client (MMPortQmi *self,
10,
cancellable,
(GAsyncReadyCallback)allocate_client_ready,
- ctx);
+ task);
}
/*****************************************************************************/
typedef struct {
- MMPortQmi *self;
- gboolean set_data_format;
- GSimpleAsyncResult *result;
- GCancellable *cancellable;
-} PortOpenContext;
+ gchar *link_name;
+ guint mux_id;
+ gboolean setup;
+} PreallocatedLinkInfo;
static void
-port_open_context_complete_and_free (PortOpenContext *ctx)
+preallocated_link_info_clear (PreallocatedLinkInfo *info)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- if (ctx->cancellable)
- g_object_unref (ctx->cancellable);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx);
+ g_free (info->link_name);
}
-gboolean
-mm_port_qmi_open_finish (MMPortQmi *self,
- GAsyncResult *res,
- GError **error)
+static void
+delete_preallocated_links (QmiDevice *qmi_device,
+ GArray *preallocated_links)
+{
+ guint i;
+
+ /* This link deletion cleanup may fail if the master interface is up
+ * (a limitation of qmi_wwan in some kernel versions). It's just a minor
+ * inconvenience really, if MM restarts they'll be all removed during
+ * initialization anyway */
+
+ for (i = 0; i < preallocated_links->len; i++) {
+ PreallocatedLinkInfo *info;
+
+ info = &g_array_index (preallocated_links, PreallocatedLinkInfo, i);
+ qmi_device_delete_link (qmi_device, info->link_name, info->mux_id,
+ NULL, NULL, NULL);
+ }
+}
+
+static guint
+count_preallocated_links_setup (MMPortQmi *self)
+{
+ guint i;
+ guint count = 0;
+
+ for (i = 0; self->priv->preallocated_links && (i < self->priv->preallocated_links->len); i++) {
+ PreallocatedLinkInfo *info;
+
+ info = &g_array_index (self->priv->preallocated_links, PreallocatedLinkInfo, i);
+ if (info->setup)
+ count++;
+ }
+
+ return count;
+}
+
+static gboolean
+release_preallocated_link (MMPortQmi *self,
+ const gchar *link_name,
+ guint mux_id,
+ GError **error)
+{
+ guint i;
+
+ for (i = 0; self->priv->preallocated_links && (i < self->priv->preallocated_links->len); i++) {
+ PreallocatedLinkInfo *info;
+
+ info = &g_array_index (self->priv->preallocated_links, PreallocatedLinkInfo, i);
+ if (!info->setup || (g_strcmp0 (info->link_name, link_name) != 0) || (info->mux_id != mux_id))
+ continue;
+
+ info->setup = FALSE;
+ return TRUE;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No preallocated link found to release");
+ return FALSE;
+}
+
+static gboolean
+acquire_preallocated_link (MMPortQmi *self,
+ MMPort *master,
+ gchar **link_name,
+ guint *mux_id,
+ GError **error)
+{
+ guint i;
+
+ if (!self->priv->qmi_device) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "port is closed");
+ return FALSE;
+ }
+
+ if (!self->priv->preallocated_links || !self->priv->preallocated_links_master) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No preallocated links available");
+ return FALSE;
+ }
+
+ if ((master != self->priv->preallocated_links_master) &&
+ (g_strcmp0 (mm_port_get_device (master), mm_port_get_device (self->priv->preallocated_links_master)) != 0)) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Preallocated links available in 'net/%s', not in 'net/%s'",
+ mm_port_get_device (self->priv->preallocated_links_master),
+ mm_port_get_device (master));
+ return FALSE;
+ }
+
+ for (i = 0; i < self->priv->preallocated_links->len; i++) {
+ PreallocatedLinkInfo *info;
+
+ info = &g_array_index (self->priv->preallocated_links, PreallocatedLinkInfo, i);
+ if (info->setup)
+ continue;
+
+ info->setup = TRUE;
+ *link_name = g_strdup (info->link_name);
+ *mux_id = info->mux_id;
+ return TRUE;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No more preallocated links available");
+ return FALSE;
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ QmiDevice *qmi_device;
+ MMPort *data;
+ GArray *preallocated_links;
+} InitializePreallocatedLinksContext;
+
+static void
+initialize_preallocated_links_context_free (InitializePreallocatedLinksContext *ctx)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ if (ctx->preallocated_links) {
+ delete_preallocated_links (ctx->qmi_device, ctx->preallocated_links);
+ g_array_unref (ctx->preallocated_links);
+ }
+ g_object_unref (ctx->qmi_device);
+ g_object_unref (ctx->data);
+ g_slice_free (InitializePreallocatedLinksContext, ctx);
+}
+
+static GArray *
+initialize_preallocated_links_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void initialize_preallocated_links_next (GTask *task);
+
+static void
+device_add_link_preallocated_ready (QmiDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InitializePreallocatedLinksContext *ctx;
+ GError *error = NULL;
+ PreallocatedLinkInfo info = { NULL, 0, FALSE };
+
+ ctx = g_task_get_task_data (task);
+
+ info.link_name = qmi_device_add_link_finish (device, res, &info.mux_id, &error);
+ if (!info.link_name) {
+ g_prefix_error (&error, "failed to add preallocated link (%u/%u) for device: ",
+ ctx->preallocated_links->len + 1, DEFAULT_LINK_PREALLOCATED_AMOUNT);
+ g_task_return_error (task, error);
+ return;
+ }
+
+ g_array_append_val (ctx->preallocated_links, info);
+ initialize_preallocated_links_next (task);
}
static void
-qmi_device_open_ready (QmiDevice *qmi_device,
+initialize_preallocated_links_next (GTask *task)
+{
+ MMPortQmi *self;
+ InitializePreallocatedLinksContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* if we were closed while allocating, bad thing, abort */
+ if (!self->priv->qmi_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED, "port is closed");
+ g_object_unref (task);
+ return;
+ }
+
+ if (ctx->preallocated_links->len == DEFAULT_LINK_PREALLOCATED_AMOUNT) {
+ g_task_return_pointer (task, g_steal_pointer (&ctx->preallocated_links), (GDestroyNotify)g_array_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ qmi_device_add_link (self->priv->qmi_device,
+ ctx->preallocated_links->len + 1,
+ mm_kernel_device_get_name (mm_port_peek_kernel_device (ctx->data)),
+ "ignored", /* n/a in qmi_wwan add_mux */
+ NULL,
+ (GAsyncReadyCallback) device_add_link_preallocated_ready,
+ task);
+}
+
+static void
+initialize_preallocated_links (MMPortQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InitializePreallocatedLinksContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (InitializePreallocatedLinksContext);
+ ctx->qmi_device = g_object_ref (self->priv->qmi_device);
+ ctx->data = g_object_ref (self->priv->preallocated_links_master);
+ ctx->preallocated_links = g_array_sized_new (FALSE, FALSE, sizeof (PreallocatedLinkInfo), DEFAULT_LINK_PREALLOCATED_AMOUNT);
+ g_array_set_clear_func (ctx->preallocated_links, (GDestroyNotify)preallocated_link_info_clear);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)initialize_preallocated_links_context_free);
+
+ initialize_preallocated_links_next (task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMPort *master;
+ gchar *link_name;
+ guint mux_id;
+} SetupLinkContext;
+
+static void
+setup_link_context_free (SetupLinkContext *ctx)
+{
+ g_free (ctx->link_name);
+ g_clear_object (&ctx->master);
+ g_slice_free (SetupLinkContext, ctx);
+}
+
+gchar *
+mm_port_qmi_setup_link_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ guint *mux_id,
+ GError **error)
+{
+ SetupLinkContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return NULL;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ if (mux_id)
+ *mux_id = ctx->mux_id;
+ return g_steal_pointer (&ctx->link_name);
+}
+
+static void
+device_add_link_ready (QmiDevice *device,
GAsyncResult *res,
- PortOpenContext *ctx)
+ GTask *task)
{
- GError *error = NULL;
+ SetupLinkContext *ctx;
+ GError *error = NULL;
- /* Reset the opening flag */
- ctx->self->priv->opening = FALSE;
+ ctx = g_task_get_task_data (task);
- if (!qmi_device_open_finish (qmi_device, res, &error)) {
- g_clear_object (&ctx->self->priv->qmi_device);
- g_simple_async_result_take_error (ctx->result, error);
+ ctx->link_name = qmi_device_add_link_with_flags_finish (device, res, &ctx->mux_id, &error);
+ if (!ctx->link_name) {
+ g_prefix_error (&error, "failed to add link for device: ");
+ g_task_return_error (task, error);
} else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
- port_open_context_complete_and_free (ctx);
+static void
+setup_preallocated_link (GTask *task)
+{
+ MMPortQmi *self;
+ SetupLinkContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!acquire_preallocated_link (self, ctx->master, &ctx->link_name, &ctx->mux_id, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-qmi_device_new_ready (GObject *unused,
+initialize_preallocated_links_ready (MMPortQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ g_assert (!self->priv->preallocated_links);
+ self->priv->preallocated_links = initialize_preallocated_links_finish (self, res, &error);
+ if (!self->priv->preallocated_links) {
+ /* We need to fail this task and all the additional tasks also pending */
+ g_task_return_error (task, g_error_copy (error));
+ g_object_unref (task);
+ while (self->priv->preallocated_links_setup_pending) {
+ g_task_return_error (self->priv->preallocated_links_setup_pending->data, g_error_copy (error));
+ g_object_unref (self->priv->preallocated_links_setup_pending->data);
+ self->priv->preallocated_links_setup_pending = g_list_delete_link (self->priv->preallocated_links_setup_pending,
+ self->priv->preallocated_links_setup_pending);
+ }
+ /* and reset back the master, because we're not really initialized */
+ g_clear_object (&self->priv->preallocated_links_master);
+ return;
+ }
+
+ /* Now we know preallocated links are available, complete our task and all the pending ones */
+ setup_preallocated_link (task);
+ while (self->priv->preallocated_links_setup_pending) {
+ setup_preallocated_link (self->priv->preallocated_links_setup_pending->data);
+ self->priv->preallocated_links_setup_pending = g_list_delete_link (self->priv->preallocated_links_setup_pending,
+ self->priv->preallocated_links_setup_pending);
+ }
+}
+
+void
+mm_port_qmi_setup_link (MMPortQmi *self,
+ MMPort *data,
+ const gchar *link_prefix_hint,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SetupLinkContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->qmi_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open");
+ g_object_unref (task);
+ return;
+ }
+
+ if (!(self->priv->kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Multiplex support not available in kernel");
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Aggregation not enabled");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (SetupLinkContext);
+ ctx->master = g_object_ref (data);
+ ctx->mux_id = QMI_DEVICE_MUX_ID_UNBOUND;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) setup_link_context_free);
+
+ /* When using rmnet, just try to add link in the QmiDevice */
+ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) {
+ QmiDeviceAddLinkFlags flags = QMI_DEVICE_ADD_LINK_FLAGS_NONE;
+
+ /* This may not be fully right, but it's the only way forward we know
+ * right now for the Qualcomm SoCs based on QRTR+IPA, where QMAPV4 is
+ * used and the device has checksum offload enabled by default, so we
+ * should create the link with special flags. Ideally, we would have a
+ * way to know in advance whether the checksum offload flags are needed
+ * or not.
+ */
+ if (self->priv->dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV4)
+ flags = (QMI_DEVICE_ADD_LINK_FLAGS_INGRESS_MAP_CKSUMV4 | QMI_DEVICE_ADD_LINK_FLAGS_EGRESS_MAP_CKSUMV4);
+
+ qmi_device_add_link_with_flags (self->priv->qmi_device,
+ QMI_DEVICE_MUX_ID_AUTOMATIC,
+ mm_kernel_device_get_name (mm_port_peek_kernel_device (data)),
+ link_prefix_hint,
+ flags,
+ NULL,
+ (GAsyncReadyCallback) device_add_link_ready,
+ task);
+ return;
+ }
+
+ /* For qmi_wwan, use preallocated links */
+ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN) {
+ if (self->priv->preallocated_links) {
+ setup_preallocated_link (task);
+ return;
+ }
+
+ /* We must make sure we don't run this procedure in parallel (e.g. if multiple
+ * connection attempts reach at the same time), so if we're told the preallocated
+ * links are already being initialized (master is set) but the array didn't exist,
+ * queue our task for completion once we're fully initialized */
+ if (self->priv->preallocated_links_master) {
+ self->priv->preallocated_links_setup_pending = g_list_append (self->priv->preallocated_links_setup_pending, task);
+ return;
+ }
+
+ /* Store master to flag that we're initializing preallocated links */
+ self->priv->preallocated_links_master = g_object_ref (data);
+ initialize_preallocated_links (self,
+ (GAsyncReadyCallback) initialize_preallocated_links_ready,
+ task);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_port_qmi_cleanup_link_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+device_delete_link_ready (QmiDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!qmi_device_delete_link_finish (device, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_port_qmi_cleanup_link (MMPortQmi *self,
+ const gchar *link_name,
+ guint mux_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GError *error = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->qmi_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is not open");
+ g_object_unref (task);
+ return;
+ }
+
+ if (!(self->priv->kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Multiplex support not available in kernel");
+ g_object_unref (task);
+ return;
+ }
+
+ if (!MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Aggregation not enabled");
+ g_object_unref (task);
+ return;
+ }
+
+ /* When using rmnet, just try to add link in the QmiDevice */
+ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) {
+ qmi_device_delete_link (self->priv->qmi_device,
+ link_name,
+ mux_id,
+ NULL,
+ (GAsyncReadyCallback) device_delete_link_ready,
+ task);
+ return;
+ }
+
+ /* For qmi_wwan, use preallocated links */
+ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN) {
+ if (!release_preallocated_link (self, link_name, mux_id, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ g_assert_not_reached ();
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ QmiDevice *device;
+ MMPort *data;
+} InternalResetContext;
+
+static void
+internal_reset_context_free (InternalResetContext *ctx)
+{
+ g_clear_object (&ctx->device);
+ g_clear_object (&ctx->data);
+ g_slice_free (InternalResetContext, ctx);
+}
+
+static gboolean
+internal_reset_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+delete_all_links_ready (QmiDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ InternalResetContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* link deletion not fatal, it may happen if in 802.3 already */
+ if (!qmi_device_delete_all_links_finish (device, res, &error)) {
+ mm_obj_dbg (self, "couldn't delete all links: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* expected data format only applicable to qmi_wwan */
+ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) {
+ mm_obj_dbg (self, "reseting expected kernel data format to 802.3 in data interface '%s'",
+ mm_port_get_device (MM_PORT (ctx->data)));
+ if (!qmi_device_set_expected_data_format (ctx->device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+net_link_down_ready (MMPortNet *data,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ InternalResetContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_net_link_setup_finish (data, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* first, delete all links found, if any */
+ mm_obj_dbg (self, "deleting all links in data interface '%s'",
+ mm_port_get_device (ctx->data));
+ qmi_device_delete_all_links (ctx->device,
+ mm_port_get_device (ctx->data),
+ NULL,
+ (GAsyncReadyCallback)delete_all_links_ready,
+ task);
+}
+
+static void
+internal_reset (MMPortQmi *self,
+ MMPort *data,
+ QmiDevice *device,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ InternalResetContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (InternalResetContext);
+ ctx->data = g_object_ref (data);
+ ctx->device = g_object_ref (device);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) internal_reset_context_free);
+
+ /* first, bring down master interface */
+ mm_obj_dbg (self, "bringing down data interface '%s'",
+ mm_port_get_device (ctx->data));
+ mm_port_net_link_setup (MM_PORT_NET (ctx->data),
+ FALSE,
+ MM_PORT_NET_MTU_DEFAULT,
+ NULL,
+ (GAsyncReadyCallback) net_link_down_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ QmiDevice *device;
+ MMPort *data;
+} ResetContext;
+
+static void
+reset_context_free (ResetContext *ctx)
+{
+ g_clear_object (&ctx->device);
+ g_clear_object (&ctx->data);
+ g_slice_free (ResetContext, ctx);
+}
+
+gboolean
+mm_port_qmi_reset_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+internal_reset_ready (MMPortQmi *self,
GAsyncResult *res,
- PortOpenContext *ctx)
+ GTask *task)
{
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
+ if (!internal_reset_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+reset_device_new_ready (GObject *source,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ ResetContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->device = qmi_device_new_finish (res, &error);
+ if (!ctx->device) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ internal_reset (self,
+ ctx->data,
+ ctx->device,
+ (GAsyncReadyCallback) internal_reset_ready,
+ task);
+}
+
+void
+mm_port_qmi_reset (MMPortQmi *self,
+ MMPort *data,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ ResetContext *ctx;
+ g_autoptr(GFile) file = NULL;
+ g_autofree gchar *fullpath = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->qmi_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port is already open");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (ResetContext);
+ ctx->data = g_object_ref (data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) reset_context_free);
+
+ fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self)));
+ file = g_file_new_for_path (fullpath);
+
+ qmi_device_new (file, NULL,
+ (GAsyncReadyCallback) reset_device_new_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMPortQmiKernelDataMode
+mm_port_qmi_get_kernel_data_modes (MMPortQmi *self)
+{
+ return self->priv->kernel_data_modes;
+}
+
+QmiWdaLinkLayerProtocol
+mm_port_qmi_get_link_layer_protocol (MMPortQmi *self)
+{
+ return self->priv->llp;
+}
+
+QmiWdaDataAggregationProtocol
+mm_port_qmi_get_data_aggregation_protocol (MMPortQmi *self)
+{
+ return self->priv->dap;
+}
+
+guint
+mm_port_qmi_get_max_multiplexed_links (MMPortQmi *self)
+{
+ return self->priv->max_multiplexed_links;
+}
+
+/*****************************************************************************/
+
+static MMPortQmiKernelDataMode
+load_current_kernel_data_modes (MMPortQmi *self,
+ QmiDevice *device)
+{
+ /* For BAM-DMUX based setups, raw-ip only and no multiplexing */
+ if (g_strcmp0 (self->priv->net_driver, "bam-dmux") == 0)
+ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+
+ /* For IPA based setups, always rmnet multiplexing */
+ if (g_strcmp0 (self->priv->net_driver, "ipa") == 0)
+ return MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET;
+
+ /* For USB based setups, query kernel */
+ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) {
+ switch (qmi_device_get_expected_data_format (device, NULL)) {
+ case QMI_DEVICE_EXPECTED_DATA_FORMAT_QMAP_PASS_THROUGH:
+ return MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET;
+ case QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP:
+ if (qmi_device_check_link_supported (device, NULL))
+ return (MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN);
+ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+ case QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN:
+ /* If the expected data format is unknown, it means the kernel in use
+ * doesn't have support for querying it; therefore it's 802.3 */
+ case QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3:
+ return MM_PORT_QMI_KERNEL_DATA_MODE_802_3;
+ default:
+ g_assert_not_reached ();
+ return MM_PORT_QMI_KERNEL_DATA_MODE_NONE;
+ }
+ }
+
+ /* For any driver, assume raw-ip only */
+ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+}
+
+static MMPortQmiKernelDataMode
+load_supported_kernel_data_modes (MMPortQmi *self,
+ QmiDevice *device)
+{
+ /* For BAM-DMUX based setups, raw-ip only and no multiplexing */
+ if (g_strcmp0 (self->priv->net_driver, "bam-dmux") == 0)
+ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+
+ /* For IPA based setups, always rmnet multiplexing */
+ if (g_strcmp0 (self->priv->net_driver, "ipa") == 0)
+ return MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET;
+
+ /* For USB based setups, we may have all supported */
+ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) {
+ MMPortQmiKernelDataMode supported = MM_PORT_QMI_KERNEL_DATA_MODE_802_3;
+
+ /* If raw-ip is not supported, muxing is also not supported */
+ if (qmi_device_check_expected_data_format_supported (device, QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP, NULL)) {
+ supported |= MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+
+ /* We switch to raw-ip to see if we can do link management with qmi_wwan.
+ * This switch would not truly be required, but the logic afterwards is robust
+ * enough to support this, nothing to worry about */
+ if (qmi_device_set_expected_data_format (device, QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP, NULL) &&
+ qmi_device_check_link_supported (device, NULL))
+ supported |= MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN;
+
+ if (qmi_device_check_expected_data_format_supported (device, QMI_DEVICE_EXPECTED_DATA_FORMAT_QMAP_PASS_THROUGH, NULL))
+ supported |= MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET;
+ }
+
+ return supported;
+ }
+
+ /* For any driver, assume raw-ip only */
+ return MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+}
+
+/*****************************************************************************/
+
+#define DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_SIZE 32768
+#define DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_DATAGRAMS 32
+
+typedef struct {
+ MMPortQmiKernelDataMode kernel_data_mode;
+ QmiWdaLinkLayerProtocol wda_llp;
+ QmiWdaDataAggregationProtocol wda_dap;
+} DataFormatCombination;
+
+static const DataFormatCombination data_format_combinations[] = {
+ { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5 },
+ { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV4 },
+ { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP },
+ { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5 },
+ { MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP },
+ { MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP, QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP, QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED },
+ { MM_PORT_QMI_KERNEL_DATA_MODE_802_3, QMI_WDA_LINK_LAYER_PROTOCOL_802_3, QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED },
+};
+
+typedef enum {
+ INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_WDA_CLIENT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_SUPPORTED_KERNEL_DATA_MODES,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_CURRENT_KERNEL_DATA_MODES,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_DPM_CLIENT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_DPM_OPEN,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_GET_WDA_DATA_FORMAT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_QUERY_DONE,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_CHECK_DATA_FORMAT_COMBINATION,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_WDA_DATA_FORMAT,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_SETUP_MASTER_MTU,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_KERNEL_DATA_MODE,
+ INTERNAL_SETUP_DATA_FORMAT_STEP_LAST,
+} InternalSetupDataFormatStep;
+
+typedef struct {
+ QmiDevice *device;
+ MMPort *data;
+ MMPortQmiSetupDataFormatAction action;
+
+ InternalSetupDataFormatStep step;
+ gboolean use_endpoint;
+ gint data_format_combination_i;
+
+ /* kernel data modes */
+ MMPortQmiKernelDataMode kernel_data_modes_current;
+ MMPortQmiKernelDataMode kernel_data_modes_requested;
+ MMPortQmiKernelDataMode kernel_data_modes_supported;
+
+ /* configured device data format */
+ QmiClient *wda;
+ QmiClient *dpm;
+ QmiWdaLinkLayerProtocol wda_llp_current;
+ QmiWdaLinkLayerProtocol wda_llp_requested;
+ QmiWdaDataAggregationProtocol wda_ul_dap_current;
+ QmiWdaDataAggregationProtocol wda_ul_dap_requested;
+ QmiWdaDataAggregationProtocol wda_dl_dap_current;
+ QmiWdaDataAggregationProtocol wda_dl_dap_requested;
+ guint32 wda_dl_dap_max_datagrams_current;
+ guint32 wda_dl_dap_max_size_current;
+ gboolean wda_dap_supported;
+} InternalSetupDataFormatContext;
+
+static void
+internal_setup_data_format_context_free (InternalSetupDataFormatContext *ctx)
+{
+ if (ctx->wda && ctx->device)
+ qmi_device_release_client (ctx->device,
+ ctx->wda,
+ QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+ 3, NULL, NULL, NULL);
+
+ if (ctx->dpm && ctx->device)
+ qmi_device_release_client (ctx->device,
+ ctx->dpm,
+ QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
+ 3, NULL, NULL, NULL);
+
+ g_clear_object (&ctx->wda);
+ g_clear_object (&ctx->dpm);
+ g_clear_object (&ctx->data);
+ g_clear_object (&ctx->device);
+ g_slice_free (InternalSetupDataFormatContext, ctx);
+}
+
+static gboolean
+internal_setup_data_format_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ MMPortQmiKernelDataMode *out_kernel_data_modes,
+ QmiWdaLinkLayerProtocol *out_llp,
+ QmiWdaDataAggregationProtocol *out_dap,
+ guint *out_max_multiplexed_links,
+ GError **error)
+{
+ InternalSetupDataFormatContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+ *out_kernel_data_modes = ctx->kernel_data_modes_current;
+ *out_llp = ctx->wda_llp_current;
+ g_assert (ctx->wda_dl_dap_current == ctx->wda_ul_dap_current);
+ *out_dap = ctx->wda_dl_dap_current;
+
+ if (out_max_multiplexed_links) {
+ if (!ctx->wda_dap_supported) {
+ *out_max_multiplexed_links = 0;
+ mm_obj_dbg (self, "wda data aggregation protocol unsupported: no multiplexed bearers allowed");
+ } else {
+ /* if multiplex backend may be rmnet, MAX-MIN */
+ if (ctx->kernel_data_modes_supported & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) {
+ *out_max_multiplexed_links = 1 + (QMI_DEVICE_MUX_ID_MAX - QMI_DEVICE_MUX_ID_MIN);
+ mm_obj_dbg (self, "rmnet link management supported: %u multiplexed bearers allowed",
+ *out_max_multiplexed_links);
+ }
+ /* if multiplex backend may be qmi_wwan, the max preallocated amount :/ */
+ else if (ctx->kernel_data_modes_supported & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN) {
+ *out_max_multiplexed_links = DEFAULT_LINK_PREALLOCATED_AMOUNT;
+ mm_obj_dbg (self, "qmi_wwan link management supported: %u multiplexed bearers allowed",
+ *out_max_multiplexed_links);
+ } else {
+ *out_max_multiplexed_links = 0;
+ mm_obj_dbg (self, "link management unsupported: no multiplexed bearers allowed");
+ }
+ }
+ }
+
+ return TRUE;
+}
- 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);
- port_open_context_complete_and_free (ctx);
+static void internal_setup_data_format_context_step (GTask *task);
+
+static void
+sync_kernel_data_mode (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ GError *error = NULL;
+ g_autofree gchar *kernel_data_modes_current_str = NULL;
+ g_autofree gchar *kernel_data_modes_requested_str = NULL;
+ QmiDeviceExpectedDataFormat expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ kernel_data_modes_current_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (ctx->kernel_data_modes_current);
+ kernel_data_modes_requested_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (ctx->kernel_data_modes_requested);
+
+ mm_obj_dbg (self, "Updating kernel expected data format: %s -> %s",
+ kernel_data_modes_current_str, kernel_data_modes_requested_str);
+
+ if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET)
+ expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_QMAP_PASS_THROUGH;
+ else if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)
+ expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
+ else if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP)
+ expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
+ else if (ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_802_3)
+ expected_data_format_requested = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3;
+ else
+ g_assert_not_reached ();
+
+ if (!qmi_device_set_expected_data_format (ctx->device, expected_data_format_requested, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* request reload */
+ ctx->kernel_data_modes_current = MM_PORT_QMI_KERNEL_DATA_MODE_NONE;
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+master_mtu_ready (MMPortNet *data,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_net_link_setup_finish (data, res, &error)) {
+ mm_obj_dbg (self, "failed to setup master MTU: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+setup_master_mtu (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ guint mtu = MM_PORT_NET_MTU_DEFAULT;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* qmi_wwan multiplexing logic requires master mtu set to the maximum data
+ * aggregation size */
+ if (ctx->kernel_data_modes_requested & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)) {
+ /* Load current max datagram size supported */
+ if (MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (ctx->wda_dl_dap_requested)) {
+ mtu = ctx->wda_dl_dap_max_size_current;
+ if ((ctx->kernel_data_modes_requested & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) && (mtu > RMNET_MAX_PACKET_SIZE)) {
+ mm_obj_dbg (self, "mtu limited to maximum rmnet packet size");
+ mtu = RMNET_MAX_PACKET_SIZE;
+ }
+ }
+
+ /* If no max aggregation size was specified by the modem (e.g. if we requested QMAP
+ * aggregation protocol but the modem doesn't support it), skip */
+ if (!mtu) {
+ mm_obj_dbg (self, "ignoring master mtu setup");
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+ return;
+ }
+ }
+
+ /* Master MTU change can only be changed while in 802-3 */
+ if (!(ctx->kernel_data_modes_current & MM_PORT_QMI_KERNEL_DATA_MODE_802_3)) {
+ mm_obj_dbg (self, "Updating kernel expected data format to 802-3 temporarily for master mtu setup");
+ if (!qmi_device_set_expected_data_format (ctx->device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, &error)) {
+ g_prefix_error (&error, "Failed setting up 802.3 kernel data format before master mtu change: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+ /* the sync kernel data mode step will fix this appropriately */
+ ctx->kernel_data_modes_current = MM_PORT_QMI_KERNEL_DATA_MODE_802_3;
+ }
+
+ mm_obj_dbg (self, "setting up master mtu: %u bytes", mtu);
+ mm_port_net_link_setup (MM_PORT_NET (ctx->data),
+ FALSE,
+ mtu,
+ NULL,
+ (GAsyncReadyCallback) master_mtu_ready,
+ task);
+}
+
+static void
+set_data_format_ready (QmiClientWda *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(QmiMessageWdaSetDataFormatOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_wda_set_data_format_finish (client, res, &error);
+ if (!output || !qmi_message_wda_set_data_format_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* store max aggregation size so that the master MTU logic works */
+ qmi_message_wda_set_data_format_output_get_downlink_data_aggregation_max_size (output, &ctx->wda_dl_dap_max_size_current, NULL);
+
+ /* request reload */
+ ctx->wda_llp_current = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
+ ctx->wda_ul_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+ ctx->wda_dl_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+sync_wda_data_format (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(QmiMessageWdaSetDataFormatInput) input = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->wda_llp_current != ctx->wda_llp_requested)
+ mm_obj_dbg (self, "Updating device link layer protocol: %s -> %s",
+ qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_current),
+ qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_requested));
+
+ if (ctx->wda_ul_dap_current != ctx->wda_ul_dap_requested)
+ mm_obj_dbg (self, "Updating device uplink data aggregation protocol: %s -> %s",
+ qmi_wda_data_aggregation_protocol_get_string (ctx->wda_ul_dap_current),
+ qmi_wda_data_aggregation_protocol_get_string (ctx->wda_ul_dap_requested));
+
+ if (ctx->wda_dl_dap_current != ctx->wda_dl_dap_requested)
+ mm_obj_dbg (self, "Updating device downlink data aggregation protocol: %s -> %s",
+ qmi_wda_data_aggregation_protocol_get_string (ctx->wda_dl_dap_current),
+ qmi_wda_data_aggregation_protocol_get_string (ctx->wda_dl_dap_requested));
+
+ input = qmi_message_wda_set_data_format_input_new ();
+ qmi_message_wda_set_data_format_input_set_link_layer_protocol (input, ctx->wda_llp_requested, NULL);
+ qmi_message_wda_set_data_format_input_set_uplink_data_aggregation_protocol (input, ctx->wda_ul_dap_requested, NULL);
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_protocol (input, ctx->wda_dl_dap_requested, NULL);
+ if (ctx->wda_dl_dap_requested != QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED) {
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_size (input, DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_SIZE, NULL);
+ qmi_message_wda_set_data_format_input_set_downlink_data_aggregation_max_datagrams (input, DEFAULT_DOWNLINK_DATA_AGGREGATION_MAX_DATAGRAMS, NULL);
+ }
+ if (ctx->use_endpoint)
+ qmi_message_wda_set_data_format_input_set_endpoint_info (input, self->priv->endpoint_type, self->priv->endpoint_interface_number, NULL);
+
+ qmi_client_wda_set_data_format (QMI_CLIENT_WDA (ctx->wda),
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) set_data_format_ready,
+ task);
+}
+
+static gboolean
+setup_data_format_completed (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* if aggregation enabled we require link management supported; this covers the
+ * case of old qmi_wwan drivers where add_mux/del_mux wasn't available yet */
+ if ((MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (ctx->wda_dl_dap_requested)) &&
+ (!(ctx->kernel_data_modes_current & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)))) {
+ mm_obj_dbg (self, "cannot enable data aggregation: link management unsupported");
+ return FALSE;
+ }
+
+ /* check whether the current and requested ones are the same */
+ if ((ctx->kernel_data_modes_current & ctx->kernel_data_modes_requested) &&
+ (ctx->wda_llp_current == ctx->wda_llp_requested) &&
+ (ctx->wda_ul_dap_current == ctx->wda_ul_dap_requested) &&
+ (ctx->wda_dl_dap_current == ctx->wda_dl_dap_requested)) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+check_data_format_combination (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ gboolean first_iteration;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ first_iteration = (ctx->data_format_combination_i < 0);
+ if (!first_iteration && setup_data_format_completed (task))
+ return;
+
+ /* go on to the next supported combination */
+ for (++ctx->data_format_combination_i;
+ ctx->data_format_combination_i <= (gint)G_N_ELEMENTS (data_format_combinations);
+ ctx->data_format_combination_i++) {
+ const DataFormatCombination *combination;
+ g_autofree gchar *kernel_data_mode_str = NULL;
+
+ combination = &data_format_combinations[ctx->data_format_combination_i];
+
+ if (!(ctx->kernel_data_modes_supported & combination->kernel_data_mode))
+ continue;
+
+ if ((MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (combination->wda_dap)) &&
+ ((!ctx->wda_dap_supported) ||
+ (ctx->action != MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX)))
+ continue;
+
+ kernel_data_mode_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (combination->kernel_data_mode);
+ mm_obj_dbg (self, "selected data format setup:");
+ mm_obj_dbg (self, " kernel data mode: %s", kernel_data_mode_str);
+ mm_obj_dbg (self, " link layer protocol: %s", qmi_wda_link_layer_protocol_get_string (combination->wda_llp));
+ mm_obj_dbg (self, " aggregation protocol: %s", qmi_wda_data_aggregation_protocol_get_string (combination->wda_dap));
+
+ ctx->kernel_data_modes_requested = combination->kernel_data_mode;
+ ctx->wda_llp_requested = combination->wda_llp;
+ ctx->wda_ul_dap_requested = combination->wda_dap;
+ ctx->wda_dl_dap_requested = combination->wda_dap;
+
+ if (first_iteration && setup_data_format_completed (task))
+ return;
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "No more data format combinations supported");
+ g_object_unref (task);
+}
+
+static gboolean
+process_data_format_output (MMPortQmi *self,
+ QmiMessageWdaGetDataFormatOutput *output,
+ InternalSetupDataFormatContext *ctx,
+ GError **error)
+{
+ /* Let's consider the lack o the LLP TLV a hard error; it really would be strange
+ * a module supporting WDA Get Data Format but not containing the LLP info */
+ if (!qmi_message_wda_get_data_format_output_get_link_layer_protocol (output, &ctx->wda_llp_current, error))
+ return FALSE;
+
+ /* QMAP assumed supported if both uplink and downlink TLVs are given */
+ ctx->wda_dap_supported = TRUE;
+ if (!qmi_message_wda_get_data_format_output_get_uplink_data_aggregation_protocol (output, &ctx->wda_ul_dap_current, NULL))
+ ctx->wda_dap_supported = FALSE;
+ if (!qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_protocol (output, &ctx->wda_dl_dap_current, NULL))
+ ctx->wda_dap_supported = FALSE;
+
+ ctx->wda_dl_dap_max_size_current = 0;
+ ctx->wda_dl_dap_max_datagrams_current = 0;
+ if (ctx->wda_dl_dap_current != QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED) {
+ qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_max_size (output, &ctx->wda_dl_dap_max_size_current, NULL);
+ qmi_message_wda_get_data_format_output_get_downlink_data_aggregation_max_datagrams (output, &ctx->wda_dl_dap_max_datagrams_current, NULL);
+ }
+ return TRUE;
+}
+
+static void
+get_data_format_ready (QmiClientWda *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(QmiMessageWdaGetDataFormatOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_wda_get_data_format_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_wda_get_data_format_output_get_result (output, &error) ||
+ !process_data_format_output (self, output, ctx, &error)) {
+ /* A 'missing argument' error when querying data format is seen in new
+ * devices like the Quectel RM500Q, requiring the 'endpoint info' TLV.
+ * When this happens, retry the step with the missing TLV.
+ *
+ * Note that this is not an additional step, we're still in the
+ * GET_WDA_DATA_FORMAT step.
+ */
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_MISSING_ARGUMENT) &&
+ (self->priv->endpoint_type != QMI_DATA_ENDPOINT_TYPE_UNDEFINED)) {
+ /* retry same step with endpoint info */
+ ctx->use_endpoint = TRUE;
+ internal_setup_data_format_context_step (task);
+ return;
+ }
+
+ /* otherwise, fatal */
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+dpm_open_port_ready (QmiClientDpm *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageDpmOpenPortOutput) output = NULL;
+ InternalSetupDataFormatContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_dpm_open_port_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_dpm_open_port_output_get_result (output, &error)) {
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+dpm_open_port (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+ QmiMessageDpmOpenPortInputHardwareDataPortsElement hw_port;
+ g_autoptr(GArray) hw_data_ports = NULL;
+ g_autoptr(QmiMessageDpmOpenPortInput) input = NULL;
+ g_autofree gchar *tx_sysfs_path = NULL;
+ g_autofree gchar *rx_sysfs_path = NULL;
+ g_autofree gchar *tx_sysfs_str = NULL;
+ g_autofree gchar *rx_sysfs_str = NULL;
+ guint tx_id = 0;
+ guint rx_id = 0;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ tx_sysfs_path = g_build_filename (self->priv->net_sysfs_path, "device", "modem", "tx_endpoint_id", NULL);
+ rx_sysfs_path = g_build_filename (self->priv->net_sysfs_path, "device", "modem", "rx_endpoint_id", NULL);
+
+ if (g_file_get_contents (rx_sysfs_path, &rx_sysfs_str, NULL, NULL) &&
+ g_file_get_contents (tx_sysfs_path, &tx_sysfs_str, NULL, NULL)) {
+ if (rx_sysfs_str && tx_sysfs_str) {
+ mm_get_uint_from_str (rx_sysfs_str, &rx_id);
+ mm_get_uint_from_str (tx_sysfs_str, &tx_id);
+ }
+ }
+
+ if (tx_id == 0 || rx_id == 0) {
+ mm_obj_warn (self, "Unable to read TX and RX endpoint IDs from sysfs. skipping automatic DPM port opening.");
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
return;
}
- if (ctx->set_data_format)
- flags |= (QMI_DEVICE_OPEN_FLAGS_NET_802_3 | QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER);
+ mm_obj_dbg (self, "Opening DPM port with TX ID: %u and RX ID: %u", tx_id, rx_id);
+
+ /* The modem TX endpoint connects with the IPA's RX port and the modem RX endpoint connects with the IPA's TX port. */
+ hw_port.rx_endpoint_number = tx_id;
+ hw_port.tx_endpoint_number = rx_id;
+ hw_port.endpoint_type = self->priv->endpoint_type;
+ hw_port.interface_number = self->priv->endpoint_interface_number;
+ hw_data_ports = g_array_new (FALSE, FALSE, sizeof (QmiMessageDpmOpenPortInputHardwareDataPortsElement));
+ g_array_append_val (hw_data_ports, hw_port);
+
+ input = qmi_message_dpm_open_port_input_new ();
+ qmi_message_dpm_open_port_input_set_hardware_data_ports (input,
+ hw_data_ports,
+ NULL);
+ qmi_client_dpm_open_port (QMI_CLIENT_DPM (ctx->dpm),
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) dpm_open_port_ready,
+ task);
+}
+
+static void
+allocate_client_dpm_ready (QmiDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InternalSetupDataFormatContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->dpm = qmi_device_allocate_client_finish (device, res, &error);
+ if (!ctx->dpm) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- /* Now open the QMI device */
- qmi_device_open (ctx->self->priv->qmi_device,
- flags,
- 10,
- ctx->cancellable,
- (GAsyncReadyCallback)qmi_device_open_ready,
- ctx);
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+allocate_client_wda_ready (QmiDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ InternalSetupDataFormatContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->wda = qmi_device_allocate_client_finish (device, res, &error);
+ if (!ctx->wda) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Go on to next step */
+ ctx->step++;
+ internal_setup_data_format_context_step (task);
+}
+
+static void
+internal_setup_data_format_context_step (GTask *task)
+{
+ MMPortQmi *self;
+ InternalSetupDataFormatContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST:
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_WDA_CLIENT:
+ /* Allocate new WDA client, only on first loop iteration */
+ g_assert (!ctx->wda);
+ qmi_device_allocate_client (ctx->device,
+ QMI_SERVICE_WDA,
+ QMI_CID_NONE,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) allocate_client_wda_ready,
+ task);
+ return;
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_SUPPORTED_KERNEL_DATA_MODES:
+ /* Load kernel data format capabilities, only on first loop iteration */
+ ctx->kernel_data_modes_supported = load_supported_kernel_data_modes (self, ctx->device);
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY:
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_CURRENT_KERNEL_DATA_MODES:
+ /* Only reload kernel data modes if it was updated or on first loop */
+ if (ctx->kernel_data_modes_current == MM_PORT_QMI_KERNEL_DATA_MODE_NONE)
+ ctx->kernel_data_modes_current = load_current_kernel_data_modes (self, ctx->device);
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_ALLOCATE_DPM_CLIENT:
+ /* Only allocate new DPM client on first loop */
+ if ((g_strcmp0 (self->priv->net_driver, "ipa") == 0) && (ctx->data_format_combination_i < 0)) {
+ g_assert (!ctx->dpm);
+ qmi_device_allocate_client (ctx->device,
+ QMI_SERVICE_DPM,
+ QMI_CID_NONE,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) allocate_client_dpm_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_DPM_OPEN:
+ /* Only for IPA based setups, open dpm port */
+ if (g_strcmp0 (self->priv->net_driver, "ipa") == 0) {
+ dpm_open_port (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_GET_WDA_DATA_FORMAT:
+ /* Only reload WDA data format if it was updated or on first loop */
+ if (ctx->wda_llp_current == QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN) {
+ g_autoptr(QmiMessageWdaGetDataFormatInput) input = NULL;
+
+ if (ctx->use_endpoint) {
+ input = qmi_message_wda_get_data_format_input_new ();
+ qmi_message_wda_get_data_format_input_set_endpoint_info (input,
+ self->priv->endpoint_type,
+ self->priv->endpoint_interface_number,
+ NULL);
+ }
+ qmi_client_wda_get_data_format (QMI_CLIENT_WDA (ctx->wda),
+ input,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) get_data_format_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_QUERY_DONE: {
+ g_autofree gchar *kernel_data_modes_str = NULL;
+
+ kernel_data_modes_str = mm_port_qmi_kernel_data_mode_build_string_from_mask (ctx->kernel_data_modes_current);
+ mm_obj_dbg (self, "current data format setup:");
+ mm_obj_dbg (self, " kernel data modes: %s", kernel_data_modes_str);
+ mm_obj_dbg (self, " link layer protocol: %s", qmi_wda_link_layer_protocol_get_string (ctx->wda_llp_current));
+ mm_obj_dbg (self, " aggregation protocol ul: %s", qmi_wda_data_aggregation_protocol_get_string (ctx->wda_ul_dap_current));
+ mm_obj_dbg (self, " aggregation protocol dl: %s", qmi_wda_data_aggregation_protocol_get_string (ctx->wda_dl_dap_current));
+
+ if (ctx->action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ } /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_CHECK_DATA_FORMAT_COMBINATION:
+ /* This step is the one that may complete the async operation
+ * successfully */
+ check_data_format_combination (task);
+ return;
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_WDA_DATA_FORMAT:
+ if ((ctx->wda_llp_current != ctx->wda_llp_requested) ||
+ (ctx->wda_ul_dap_current != ctx->wda_ul_dap_requested) ||
+ (ctx->wda_dl_dap_current != ctx->wda_dl_dap_requested)) {
+ sync_wda_data_format (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_SETUP_MASTER_MTU:
+ /* qmi_wwan add_mux/del_mux based logic requires master MTU set to the maximum
+ * data aggregation size reported by the modem. */
+ if (g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) {
+ setup_master_mtu (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_SYNC_KERNEL_DATA_MODE:
+ if (!(ctx->kernel_data_modes_current & ctx->kernel_data_modes_requested)) {
+ sync_kernel_data_mode (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case INTERNAL_SETUP_DATA_FORMAT_STEP_LAST:
+ /* jump back to first step to reload current state after
+ * the updates have been done */
+ ctx->step = INTERNAL_SETUP_DATA_FORMAT_STEP_RETRY;
+ internal_setup_data_format_context_step (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+internal_setup_data_format (MMPortQmi *self,
+ QmiDevice *device,
+ MMPort *data, /* may be NULL in query */
+ MMPortQmiSetupDataFormatAction action,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InternalSetupDataFormatContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Port must be open to setup data format");
+ g_object_unref (task);
+ return;
+ }
+
+ if (self->priv->wda_unsupported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Setting up data format is not supported");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (InternalSetupDataFormatContext);
+ ctx->device = g_object_ref (device);
+ ctx->data = data ? g_object_ref (data) : NULL;
+ ctx->action = action;
+ ctx->step = INTERNAL_SETUP_DATA_FORMAT_STEP_FIRST;
+ ctx->data_format_combination_i = -1;
+ ctx->kernel_data_modes_current = MM_PORT_QMI_KERNEL_DATA_MODE_NONE;
+ ctx->kernel_data_modes_requested = MM_PORT_QMI_KERNEL_DATA_MODE_NONE;
+ ctx->kernel_data_modes_supported = MM_PORT_QMI_KERNEL_DATA_MODE_NONE;
+ ctx->wda_llp_current = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
+ ctx->wda_llp_requested = QMI_WDA_LINK_LAYER_PROTOCOL_UNKNOWN;
+ ctx->wda_ul_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+ ctx->wda_ul_dap_requested = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+ ctx->wda_dl_dap_current = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+ ctx->wda_dl_dap_requested = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+
+ if (mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_QRTR)
+ ctx->use_endpoint = TRUE;
+
+ g_task_set_task_data (task, ctx, (GDestroyNotify) internal_setup_data_format_context_free);
+
+ internal_setup_data_format_context_step (task);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMPort *data;
+ QmiDevice *device;
+ MMPortQmiSetupDataFormatAction action;
+} SetupDataFormatContext;
+
+static void
+setup_data_format_context_free (SetupDataFormatContext *ctx)
+{
+ g_clear_object (&ctx->device);
+ g_clear_object (&ctx->data);
+ g_slice_free (SetupDataFormatContext, ctx);
+}
+
+gboolean
+mm_port_qmi_setup_data_format_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+internal_setup_data_format_ready (MMPortQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!internal_setup_data_format_finish (self,
+ res,
+ &self->priv->kernel_data_modes,
+ &self->priv->llp,
+ &self->priv->dap,
+ NULL, /* not expected to update */
+ &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+setup_data_format_internal_reset_ready (MMPortQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetupDataFormatContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!internal_reset_finish (self, res, &error)) {
+ g_prefix_error (&error, "Couldn't reset interface before setting up data format: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* call internal method with the already open QmiDevice */
+ internal_setup_data_format (self,
+ ctx->device,
+ ctx->data,
+ ctx->action,
+ (GAsyncReadyCallback)internal_setup_data_format_ready,
+ task);
+}
+
+static guint
+count_links_setup (MMPortQmi *self,
+ MMPort *data)
+{
+ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET) {
+ g_autoptr(GPtrArray) links = NULL;
+ g_autoptr(GError) error = NULL;
+
+ if (!qmi_device_list_links (self->priv->qmi_device,
+ mm_port_get_device (data),
+ &links,
+ &error)) {
+ mm_obj_warn (self, "couldn't list links in %s: %s",
+ mm_port_get_device (data),
+ error->message);
+ return 0;
+ }
+
+ return links->len;
+ }
+
+ if (self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)
+ return count_preallocated_links_setup (self);
+
+ return 0;
}
void
-mm_port_qmi_open (MMPortQmi *self,
- gboolean set_data_format,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GFile *file;
- gchar *fullpath;
+mm_port_qmi_setup_data_format (MMPortQmi *self,
+ MMPort *data,
+ MMPortQmiSetupDataFormatAction action,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SetupDataFormatContext *ctx;
+ GTask *task;
+
+ /* External calls are never query */
+ g_assert (action != MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY);
+ g_assert (MM_IS_PORT (data));
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!self->priv->qmi_device) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, "Port not open");
+ g_object_unref (task);
+ return;
+ }
+
+ if (self->priv->wda_unsupported) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "Setting up data format is unsupported");
+ g_object_unref (task);
+ return;
+ }
+
+ if ((action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX) &&
+ (self->priv->kernel_data_modes & (MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET | MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN)) &&
+ MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) {
+ mm_obj_dbg (self, "multiplex support already available when setting up data format");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if ((action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT) &&
+ (((self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP) && (self->priv->llp == QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP)) ||
+ ((self->priv->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_802_3) && (self->priv->llp == QMI_WDA_LINK_LAYER_PROTOCOL_802_3))) &&
+ !MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) {
+ mm_obj_dbg (self, "multiplex support already disabled when setting up data format");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* support switching from multiplex to non-multiplex, but only if there are no active
+ * links allocated */
+ if ((action == MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT) &&
+ MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP (self->priv->dap)) {
+ guint n_links_setup;
+
+ n_links_setup = count_links_setup (self, data);
+ if (n_links_setup > 0) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
+ "Cannot switch to non-multiplex setup: %u links already setup exist",
+ n_links_setup);
+ g_object_unref (task);
+ return;
+ }
+ }
+
+ ctx = g_slice_new0 (SetupDataFormatContext);
+ ctx->data = g_object_ref (data);
+ ctx->device = g_object_ref (self->priv->qmi_device);
+ ctx->action = action;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)setup_data_format_context_free);
+
+ internal_reset (self,
+ data,
+ ctx->device,
+ (GAsyncReadyCallback)setup_data_format_internal_reset_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+typedef enum {
+ PORT_OPEN_STEP_FIRST,
+ PORT_OPEN_STEP_CHECK_OPENING,
+ PORT_OPEN_STEP_CHECK_ALREADY_OPEN,
+ PORT_OPEN_STEP_DEVICE_NEW,
+ PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT,
+ PORT_OPEN_STEP_SETUP_DATA_FORMAT,
+ PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT,
+ PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT,
+ PORT_OPEN_STEP_LAST
+} PortOpenStep;
+
+typedef struct {
+ QmiDevice *device;
+ GError *error;
+ PortOpenStep step;
+ gboolean set_data_format;
+ MMPortQmiKernelDataMode kernel_data_modes;
+ gboolean ctl_raw_ip_unsupported;
+} PortOpenContext;
+
+static void
+port_open_context_free (PortOpenContext *ctx)
+{
+ g_assert (!ctx->error);
+ g_clear_object (&ctx->device);
+ g_slice_free (PortOpenContext, ctx);
+}
+
+gboolean
+mm_port_qmi_open_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void port_open_step (GTask *task);
+
+static void
+port_open_complete_with_error (GTask *task)
+{
+ MMPortQmi *self;
PortOpenContext *ctx;
- g_return_if_fail (MM_IS_PORT_QMI (self));
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
- ctx = g_new0 (PortOpenContext, 1);
- ctx->self = g_object_ref (self);
- ctx->set_data_format = set_data_format;
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_qmi_open);
- ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ g_assert (ctx->error);
+ self->priv->in_progress = FALSE;
+ g_task_return_error (task, g_steal_pointer (&ctx->error));
+ g_object_unref (task);
+}
+
+static void
+qmi_device_close_on_error_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ g_autoptr(GError) error = NULL;
- if (self->priv->opening) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "QMI device already being opened");
- port_open_context_complete_and_free (ctx);
+ self = g_task_get_source_object (task);
+
+ if (!qmi_device_close_finish (qmi_device, res, &error))
+ mm_obj_warn (self, "Couldn't close QMI device after failed open sequence: %s", error->message);
+
+ port_open_complete_with_error (task);
+}
+
+static void
+qmi_device_open_second_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ PortOpenContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_device_open_finish (qmi_device, res, &error)) {
+ /* Not all devices support raw-ip, which is the first thing we try
+ * by default. Detect this case, and retry with 802.3 if so. */
+ if ((g_strcmp0 (self->priv->net_driver, "qmi_wwan") == 0) &&
+ g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_DATA_FORMAT) &&
+ (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP)) {
+ /* switch to 802.3 right away, so that the logic can successfully go on after that */
+ qmi_device_set_expected_data_format (qmi_device, QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3, NULL);
+ ctx->ctl_raw_ip_unsupported = TRUE;
+ port_open_step (task);
+ return;
+ }
+
+ /* Otherwise, fatal */
+ ctx->error = g_steal_pointer (&error);
+ } else {
+ /* If the open with CTL data format is sucessful, update all settings
+ * that we would have received with the internal setup data format
+ * process */
+ self->priv->kernel_data_modes = ctx->kernel_data_modes;
+ if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP)
+ self->priv->llp = QMI_WDA_LINK_LAYER_PROTOCOL_RAW_IP;
+ else if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_802_3)
+ self->priv->llp = QMI_WDA_LINK_LAYER_PROTOCOL_802_3;
+ else
+ g_assert_not_reached ();
+ self->priv->dap = QMI_WDA_DATA_AGGREGATION_PROTOCOL_DISABLED;
+ self->priv->max_multiplexed_links = 0;
+ }
+
+ /* In both error and success, we go to last step */
+ ctx->step++;
+ port_open_step (task);
+}
+
+static void
+qmi_device_close_to_reopen_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMPortQmi *self;
+ PortOpenContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_device_close_finish (qmi_device, res, &ctx->error)) {
+ mm_obj_warn (self, "Couldn't close QMI device to reopen it");
+ ctx->step = PORT_OPEN_STEP_LAST;
+ } else
+ ctx->step++;
+ port_open_step (task);
+}
+
+static void
+open_internal_setup_data_format_ready (MMPortQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ PortOpenContext *ctx;
+ g_autoptr(GError) error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!internal_setup_data_format_finish (self,
+ res,
+ &self->priv->kernel_data_modes,
+ &self->priv->llp,
+ &self->priv->dap,
+ &self->priv->max_multiplexed_links,
+ &error)) {
+ /* Continue with fallback to LLP requested via CTL */
+ mm_obj_warn (self, "Couldn't setup data format: %s", error->message);
+ self->priv->wda_unsupported = TRUE;
+ ctx->step++;
+ } else {
+ /* on success, we're done */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ }
+ port_open_step (task);
+}
+
+static void
+qmi_device_open_first_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ PortOpenContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_device_open_finish (qmi_device, res, &ctx->error))
+ /* Error opening the device */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ else if (!ctx->set_data_format)
+ /* If not setting data format, we're done */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ else
+ /* Go on to next step */
+ ctx->step++;
+ port_open_step (task);
+}
+
+static void
+qmi_device_new_ready (GObject *unused,
+ GAsyncResult *res,
+ GTask *task)
+{
+ PortOpenContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ /* Store the device in the context until the operation is fully done,
+ * so that we return IN_PROGRESS errors until we finish this async
+ * operation. */
+ ctx->device = qmi_device_new_finish (res, &ctx->error);
+ if (!ctx->device)
+ /* Error creating the device */
+ ctx->step = PORT_OPEN_STEP_LAST;
+ else
+ /* Go on to next step */
+ ctx->step++;
+ port_open_step (task);
+}
+
+static void
+port_open_step (GTask *task)
+{
+ MMPortQmi *self;
+ PortOpenContext *ctx;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ switch (ctx->step) {
+ case PORT_OPEN_STEP_FIRST:
+ mm_obj_dbg (self, "Opening QMI device...");
+ ctx->step++;
+ /* Fall through */
+
+ case PORT_OPEN_STEP_CHECK_OPENING:
+ mm_obj_dbg (self, "Checking if QMI device already opening...");
+ if (self->priv->in_progress) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "QMI device open/close operation in progress");
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case PORT_OPEN_STEP_CHECK_ALREADY_OPEN:
+ mm_obj_dbg (self, "Checking if QMI device already open...");
+ if (self->priv->qmi_device) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case PORT_OPEN_STEP_DEVICE_NEW:
+ /* We flag in this point that we're opening. From now on, if we stop
+ * for whatever reason, we should clear this flag. We do this by ensuring
+ * that all callbacks go through the LAST step for completing. */
+ self->priv->in_progress = TRUE;
+
+#if defined WITH_QRTR
+ if (self->priv->node) {
+ mm_obj_info (self, "Creating QMI device from QRTR node...");
+ qmi_device_new_from_node (self->priv->node,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_new_ready,
+ task);
+ return;
+ }
+#endif
+ {
+ g_autoptr(GFile) file = NULL;
+ g_autofree gchar *fullpath = NULL;
+
+ fullpath = g_strdup_printf ("/dev/%s", mm_port_get_device (MM_PORT (self)));
+ file = g_file_new_for_path (fullpath);
+
+ mm_obj_dbg (self, "Creating QMI device...");
+ qmi_device_new (file,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_new_ready,
+ task);
+ return;
+ }
+
+ case PORT_OPEN_STEP_OPEN_WITHOUT_DATA_FORMAT:
+ if (!self->priv->wda_unsupported || !ctx->set_data_format) {
+ QmiDeviceOpenFlags open_flags;
+ g_autofree gchar *open_flags_str = NULL;
+
+ /* Now open the QMI device without any data format CTL flag */
+ open_flags = (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO | QMI_DEVICE_OPEN_FLAGS_PROXY);
+ open_flags_str = qmi_device_open_flags_build_string_from_mask (open_flags);
+ mm_obj_dbg (self, "Opening device with flags: %s...", open_flags_str);
+ qmi_device_open (ctx->device,
+ open_flags,
+ 45,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_open_first_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case PORT_OPEN_STEP_SETUP_DATA_FORMAT:
+ if (qmi_device_is_open (ctx->device)) {
+ internal_setup_data_format (self,
+ ctx->device,
+ NULL,
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY,
+ (GAsyncReadyCallback) open_internal_setup_data_format_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case PORT_OPEN_STEP_CLOSE_BEFORE_OPEN_WITH_DATA_FORMAT:
+ /* This fallback only applies when WDA unsupported */
+ if (qmi_device_is_open (ctx->device)) {
+ mm_obj_dbg (self, "Closing device to reopen it right away...");
+ qmi_device_close_async (ctx->device,
+ 5,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_close_to_reopen_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* Fall through */
+
+ case PORT_OPEN_STEP_OPEN_WITH_DATA_FORMAT: {
+ QmiDeviceOpenFlags open_flags;
+ g_autofree gchar *open_flags_str = NULL;
+
+ /* Common open flags */
+ open_flags = (QMI_DEVICE_OPEN_FLAGS_VERSION_INFO |
+ QMI_DEVICE_OPEN_FLAGS_PROXY |
+ QMI_DEVICE_OPEN_FLAGS_NET_NO_QOS_HEADER);
+
+ ctx->kernel_data_modes = load_current_kernel_data_modes (self, ctx->device);
+
+ /* Skip trying raw-ip if we already tried and it failed */
+ if (ctx->ctl_raw_ip_unsupported)
+ ctx->kernel_data_modes &= ~MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP;
+
+ /* Need to reopen setting 802.3/raw-ip using CTL */
+ if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP)
+ open_flags |= QMI_DEVICE_OPEN_FLAGS_NET_RAW_IP;
+ else if (ctx->kernel_data_modes & MM_PORT_QMI_KERNEL_DATA_MODE_802_3)
+ open_flags |= QMI_DEVICE_OPEN_FLAGS_NET_802_3;
+ else {
+ /* Set error and jump to last step, so that we cleanly close the device
+ * in case we need to reopen it right away */
+ ctx->error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unexpected kernel data mode: cannot setup using CTL");
+ ctx->step = PORT_OPEN_STEP_LAST;
+ port_open_step (task);
+ return;
+ }
+
+ open_flags_str = qmi_device_open_flags_build_string_from_mask (open_flags);
+ mm_obj_dbg (self, "Reopening device with flags: %s...", open_flags_str);
+
+ qmi_device_open (ctx->device,
+ open_flags,
+ 10,
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) qmi_device_open_second_ready,
+ task);
return;
}
- if (self->priv->qmi_device) {
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- port_open_context_complete_and_free (ctx);
+ case PORT_OPEN_STEP_LAST:
+ if (ctx->error) {
+ mm_obj_dbg (self, "QMI port open operation failed: %s", ctx->error->message);
+
+ if (ctx->device) {
+ qmi_device_close_async (ctx->device,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) qmi_device_close_on_error_ready,
+ task);
+ return;
+ }
+
+ port_open_complete_with_error (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "QMI port open operation finished successfully");
+
+ /* Store device in private info */
+ g_assert (ctx->device);
+ g_assert (!self->priv->qmi_device);
+ self->priv->qmi_device = g_object_ref (ctx->device);
+ self->priv->in_progress = FALSE;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
+
+ default:
+ g_assert_not_reached ();
}
+}
- fullpath = g_strdup_printf ("/dev/%s",
- mm_port_get_device (MM_PORT (self)));
- file = g_file_new_for_path (fullpath);
+void
+mm_port_qmi_open (MMPortQmi *self,
+ gboolean set_data_format,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ PortOpenContext *ctx;
+ GTask *task;
- self->priv->opening = TRUE;
- qmi_device_new (file,
- ctx->cancellable,
- (GAsyncReadyCallback)qmi_device_new_ready,
- ctx);
+ ctx = g_slice_new0 (PortOpenContext);
+ ctx->step = PORT_OPEN_STEP_FIRST;
+ ctx->set_data_format = set_data_format;
+ ctx->kernel_data_modes = MM_PORT_QMI_KERNEL_DATA_MODE_NONE;
- g_free (fullpath);
- g_object_unref (file);
+ task = g_task_new (self, cancellable, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)port_open_context_free);
+ port_open_step (task);
}
+/*****************************************************************************/
+
gboolean
mm_port_qmi_is_open (MMPortQmi *self)
{
@@ -300,65 +2531,223 @@ mm_port_qmi_is_open (MMPortQmi *self)
return !!self->priv->qmi_device;
}
+/*****************************************************************************/
+
void
-mm_port_qmi_close (MMPortQmi *self)
+mm_port_qmi_set_net_driver (MMPortQmi *self,
+ const gchar *net_driver)
{
- GList *l;
- GError *error = NULL;
+ g_assert (MM_IS_PORT_QMI (self));
+ g_assert (!self->priv->net_driver);
+ self->priv->net_driver = g_strdup (net_driver);
+}
+
+/*****************************************************************************/
+
+void
+mm_port_qmi_set_net_sysfs_path (MMPortQmi *self,
+ const gchar *net_sysfs_path)
+{
+ g_assert (MM_IS_PORT_QMI (self));
+ g_assert (!self->priv->net_sysfs_path);
+ self->priv->net_sysfs_path = g_strdup (net_sysfs_path);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ QmiDevice *qmi_device;
+} PortQmiCloseContext;
+
+static void
+port_qmi_close_context_free (PortQmiCloseContext *ctx)
+{
+ g_clear_object (&ctx->qmi_device);
+ g_slice_free (PortQmiCloseContext, ctx);
+}
+
+gboolean
+mm_port_qmi_close_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+qmi_device_close_ready (QmiDevice *qmi_device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ MMPortQmi *self;
+
+ self = g_task_get_source_object (task);
+
+ g_assert (!self->priv->qmi_device);
+ self->priv->in_progress = FALSE;
+
+ if (!qmi_device_close_finish (qmi_device, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_port_qmi_close (MMPortQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ PortQmiCloseContext *ctx;
+ GTask *task;
+ GList *l;
g_return_if_fail (MM_IS_PORT_QMI (self));
- if (!self->priv->qmi_device)
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (self->priv->in_progress) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "QMI device open/close operation in progress");
+ g_object_unref (task);
return;
+ }
+
+ if (!self->priv->qmi_device) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ self->priv->in_progress = TRUE;
+
+ /* Store device to close in the context */
+ ctx = g_slice_new0 (PortQmiCloseContext);
+ ctx->qmi_device = g_steal_pointer (&self->priv->qmi_device);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)port_qmi_close_context_free);
/* Release all allocated clients */
for (l = self->priv->services; l; l = g_list_next (l)) {
ServiceInfo *info = l->data;
- mm_dbg ("Releasing client for service '%s'...", qmi_service_get_string (info->service));
- qmi_device_release_client (self->priv->qmi_device,
+ mm_obj_dbg (self, "Releasing client for service '%s'...", qmi_service_get_string (info->service));
+ qmi_device_release_client (ctx->qmi_device,
info->client,
QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID,
3, NULL, NULL, NULL);
g_clear_object (&info->client);
}
- g_list_free_full (self->priv->services, (GDestroyNotify)g_free);
+ g_list_free_full (self->priv->services, g_free);
self->priv->services = NULL;
- /* Close and release the device */
- if (!qmi_device_close (self->priv->qmi_device, &error)) {
- mm_warn ("Couldn't properly close QMI device: %s",
- error->message);
- g_error_free (error);
+ /* Cleanup preallocated links, if any */
+ if (self->priv->preallocated_links) {
+ delete_preallocated_links (ctx->qmi_device, self->priv->preallocated_links);
+ g_clear_pointer (&self->priv->preallocated_links, g_array_unref);
}
+ g_clear_object (&self->priv->preallocated_links_master);
- g_clear_object (&self->priv->qmi_device);
+ qmi_device_close_async (ctx->qmi_device,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)qmi_device_close_ready,
+ task);
}
/*****************************************************************************/
MMPortQmi *
-mm_port_qmi_new (const gchar *name)
+mm_port_qmi_new (const gchar *name,
+ MMPortSubsys subsys)
{
return MM_PORT_QMI (g_object_new (MM_TYPE_PORT_QMI,
MM_PORT_DEVICE, name,
- MM_PORT_SUBSYS, MM_PORT_SUBSYS_USB,
+ MM_PORT_SUBSYS, subsys,
MM_PORT_TYPE, MM_PORT_TYPE_QMI,
NULL));
}
+#if defined WITH_QRTR
+MMPortQmi *
+mm_port_qmi_new_from_node (const gchar *name,
+ QrtrNode *node)
+{
+ return MM_PORT_QMI (g_object_new (MM_TYPE_PORT_QMI,
+ "node", node,
+ MM_PORT_DEVICE, name,
+ MM_PORT_SUBSYS, MM_PORT_SUBSYS_QRTR,
+ MM_PORT_TYPE, MM_PORT_TYPE_QMI,
+ NULL));
+}
+#endif
+
static void
mm_port_qmi_init (MMPortQmi *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_QMI, MMPortQmiPrivate);
+
+ /* load endpoint info as soon as kernel device is set */
+ self->priv->endpoint_info_signal_id = g_signal_connect (self,
+ "notify::" MM_PORT_KERNEL_DEVICE,
+ G_CALLBACK (initialize_endpoint_info),
+ NULL);
+}
+
+#if defined WITH_QRTR
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMPortQmi *self = MM_PORT_QMI (object);
+
+ switch (prop_id) {
+ case PROP_NODE:
+ g_clear_object (&self->priv->node);
+ self->priv->node = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMPortQmi *self = MM_PORT_QMI (object);
+
+ switch (prop_id) {
+ case PROP_NODE:
+ g_value_set_object (value, self->priv->node);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#endif /* defined WITH_QRTR */
+
+static void
dispose (GObject *object)
{
MMPortQmi *self = MM_PORT_QMI (object);
GList *l;
+ if (self->priv->endpoint_info_signal_id) {
+ g_signal_handler_disconnect (self, self->priv->endpoint_info_signal_id);
+ self->priv->endpoint_info_signal_id = 0;
+ }
+
/* Deallocate all clients */
for (l = self->priv->services; l; l = g_list_next (l)) {
ServiceInfo *info = l->data;
@@ -366,12 +2755,25 @@ dispose (GObject *object)
if (info->client)
g_object_unref (info->client);
}
- g_list_free_full (self->priv->services, (GDestroyNotify)g_free);
+ g_list_free_full (self->priv->services, g_free);
self->priv->services = NULL;
+ /* Cleanup preallocated links, if any */
+ if (self->priv->preallocated_links && self->priv->qmi_device)
+ delete_preallocated_links (self->priv->qmi_device, self->priv->preallocated_links);
+ g_clear_pointer (&self->priv->preallocated_links, g_array_unref);
+ g_clear_object (&self->priv->preallocated_links_master);
+
+ /* Clear node object */
+#if defined WITH_QRTR
+ g_clear_object (&self->priv->node);
+#endif
/* Clear device object */
g_clear_object (&self->priv->qmi_device);
+ g_clear_pointer (&self->priv->net_driver, g_free);
+ g_clear_pointer (&self->priv->net_sysfs_path, g_free);
+
G_OBJECT_CLASS (mm_port_qmi_parent_class)->dispose (object);
}
@@ -384,4 +2786,18 @@ mm_port_qmi_class_init (MMPortQmiClass *klass)
/* Virtual methods */
object_class->dispose = dispose;
+
+#if defined WITH_QRTR
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ properties[PROP_NODE] =
+ g_param_spec_object ("node",
+ "Qrtr Node",
+ "Qrtr node to be probed",
+ QRTR_TYPE_NODE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+#endif
}
diff --git a/src/mm-port-qmi.h b/src/mm-port-qmi.h
index 9c38b0c5..b238ede8 100644
--- a/src/mm-port-qmi.h
+++ b/src/mm-port-qmi.h
@@ -16,6 +16,8 @@
#ifndef MM_PORT_QMI_H
#define MM_PORT_QMI_H
+#include <config.h>
+
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
@@ -24,6 +26,23 @@
#include "mm-port.h"
+typedef enum { /*< underscore_name=mm_port_qmi_kernel_data_mode >*/
+ MM_PORT_QMI_KERNEL_DATA_MODE_NONE = 0,
+ /* ethernet packets over the master network interface */
+ MM_PORT_QMI_KERNEL_DATA_MODE_802_3 = 1 << 0,
+ /* raw-ip packets over the master network interface */
+ MM_PORT_QMI_KERNEL_DATA_MODE_RAW_IP = 1 << 1,
+ /* multiplexing support setup with rmnet */
+ MM_PORT_QMI_KERNEL_DATA_MODE_MUX_RMNET = 1 << 2,
+ /* multiplexing support setup with qmi_wwan add_mux/del_mux */
+ MM_PORT_QMI_KERNEL_DATA_MODE_MUX_QMIWWAN = 1 << 3,
+} MMPortQmiKernelDataMode;
+
+#define MM_PORT_QMI_DAP_IS_SUPPORTED_QMAP(dap) \
+ (dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV5 || \
+ dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAPV4 || \
+ dap == QMI_WDA_DATA_AGGREGATION_PROTOCOL_QMAP)
+
#define MM_TYPE_PORT_QMI (mm_port_qmi_get_type ())
#define MM_PORT_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PORT_QMI, MMPortQmi))
#define MM_PORT_QMI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PORT_QMI, MMPortQmiClass))
@@ -45,41 +64,118 @@ struct _MMPortQmiClass {
};
GType mm_port_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortQmi, g_object_unref)
+
+MMPortQmi *mm_port_qmi_new (const gchar *name,
+ MMPortSubsys subsys);
+#if defined WITH_QRTR
+MMPortQmi *mm_port_qmi_new_from_node (const gchar *name,
+ QrtrNode *node);
+#endif
+void mm_port_qmi_open (MMPortQmi *self,
+ gboolean set_data_format,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_open_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
+gboolean mm_port_qmi_is_open (MMPortQmi *self);
+void mm_port_qmi_close (MMPortQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_close_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
-MMPortQmi *mm_port_qmi_new (const gchar *name);
+void mm_port_qmi_set_net_driver (MMPortQmi *self,
+ const gchar *net_driver);
-void mm_port_qmi_open (MMPortQmi *self,
- gboolean set_data_format,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_port_qmi_open_finish (MMPortQmi *self,
- GAsyncResult *res,
- GError **error);
-gboolean mm_port_qmi_is_open (MMPortQmi *self);
-void mm_port_qmi_close (MMPortQmi *self);
+void mm_port_qmi_set_net_sysfs_path (MMPortQmi *self,
+ const gchar *net_sysfs_path);
typedef enum {
MM_PORT_QMI_FLAG_DEFAULT = 0,
- MM_PORT_QMI_FLAG_WDS_IPV4 = 100,
- MM_PORT_QMI_FLAG_WDS_IPV6 = 101
+ MM_PORT_QMI_FLAG_WDS_IPV4 = 1,
+ MM_PORT_QMI_FLAG_WDS_IPV6 = 2,
} MMPortQmiFlag;
-void mm_port_qmi_allocate_client (MMPortQmi *self,
- QmiService service,
- MMPortQmiFlag flag,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_port_qmi_allocate_client_finish (MMPortQmi *self,
- GAsyncResult *res,
- GError **error);
-
-QmiClient *mm_port_qmi_peek_client (MMPortQmi *self,
- QmiService service,
- MMPortQmiFlag flag);
-QmiClient *mm_port_qmi_get_client (MMPortQmi *self,
- QmiService service,
- MMPortQmiFlag flag);
+/* When using the WDS service, we may not only want to have explicit different
+ * clients for IPv4 or IPv6, but also for different mux ids as well, so that
+ * different bearer objects never attempt to use the same WDS clients. */
+#define MM_PORT_QMI_FLAG_WITH_MUX_ID(flag, mux_id) ((mux_id << 8) | (flag & 0xFF))
+
+void mm_port_qmi_allocate_client (MMPortQmi *self,
+ QmiService service,
+ guint flag,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_allocate_client_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_port_qmi_release_client (MMPortQmi *self,
+ QmiService service,
+ guint flag);
+
+QmiClient *mm_port_qmi_peek_client (MMPortQmi *self,
+ QmiService service,
+ guint flag);
+QmiClient *mm_port_qmi_get_client (MMPortQmi *self,
+ QmiService service,
+ guint flag);
+
+QmiDevice *mm_port_qmi_peek_device (MMPortQmi *self);
+
+QmiDataEndpointType mm_port_qmi_get_endpoint_type (MMPortQmi *self);
+guint mm_port_qmi_get_endpoint_interface_number (MMPortQmi *self);
+
+MMPortQmiKernelDataMode mm_port_qmi_get_kernel_data_modes (MMPortQmi *self);
+QmiWdaLinkLayerProtocol mm_port_qmi_get_link_layer_protocol (MMPortQmi *self);
+QmiWdaDataAggregationProtocol mm_port_qmi_get_data_aggregation_protocol (MMPortQmi *self);
+guint mm_port_qmi_get_max_multiplexed_links (MMPortQmi *self);
+
+typedef enum {
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_QUERY,
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_DEFAULT,
+ MM_PORT_QMI_SETUP_DATA_FORMAT_ACTION_SET_MULTIPLEX,
+} MMPortQmiSetupDataFormatAction;
+
+void mm_port_qmi_setup_data_format (MMPortQmi *self,
+ MMPort *data,
+ MMPortQmiSetupDataFormatAction action,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_setup_data_format_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_port_qmi_setup_link (MMPortQmi *self,
+ MMPort *data,
+ const gchar *link_prefix_hint,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *mm_port_qmi_setup_link_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ guint *mux_id,
+ GError **error);
+
+void mm_port_qmi_cleanup_link (MMPortQmi *self,
+ const gchar *link_name,
+ guint mux_id,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_cleanup_link_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
+
+void mm_port_qmi_reset (MMPortQmi *self,
+ MMPort *data,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_port_qmi_reset_finish (MMPortQmi *self,
+ GAsyncResult *res,
+ GError **error);
#endif /* MM_PORT_QMI_H */
diff --git a/src/mm-port-serial-at.c b/src/mm-port-serial-at.c
index 553596e8..44c0fc92 100644
--- a/src/mm-port-serial-at.c
+++ b/src/mm-port-serial-at.c
@@ -22,7 +22,7 @@
#include <string.h>
#include "mm-port-serial-at.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
G_DEFINE_TYPE (MMPortSerialAt, mm_port_serial_at, MM_TYPE_PORT_SERIAL)
@@ -116,14 +116,16 @@ mm_port_serial_at_remove_echo (GByteArray *response)
}
}
-static gboolean
+static MMPortSerialResponseType
parse_response (MMPortSerial *port,
GByteArray *response,
+ GByteArray **parsed_response,
GError **error)
{
MMPortSerialAt *self = MM_PORT_SERIAL_AT (port);
- gboolean found;
GString *string;
+ gsize parsed_len;
+ GError *inner_error = NULL;
g_return_val_if_fail (self->priv->response_parser_fn != NULL, FALSE);
@@ -131,21 +133,39 @@ parse_response (MMPortSerial *port,
if (self->priv->remove_echo)
mm_port_serial_at_remove_echo (response);
+ /* If there's no response to receive, we're done; e.g. if we only got
+ * unsolicited messages */
+ if (!response->len)
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+
/* Construct the string that AT-parsing functions expect */
string = g_string_sized_new (response->len + 1);
g_string_append_len (string, (const char *) response->data, response->len);
- /* Parse it */
- found = self->priv->response_parser_fn (self->priv->response_parser_user_data, string, error);
-
- /* And copy it back into the response array after the parser has removed
- * matches and cleaned it up.
- */
- if (response->len)
- g_byte_array_remove_range (response, 0, response->len);
- g_byte_array_append (response, (const guint8 *) string->str, string->len);
- g_string_free (string, TRUE);
- return found;
+ /* Fully cleanup the response array, we'll consider the contents we got
+ * as the full reply that the command may expect. */
+ g_byte_array_remove_range (response, 0, response->len);
+
+ /* Parse it; returns FALSE if there is nothing we can do with this
+ * response yet. */
+ if (!self->priv->response_parser_fn (self->priv->response_parser_user_data, string, self, &inner_error)) {
+ /* Copy what we got back in the response buffer. */
+ g_byte_array_append (response, (const guint8 *) string->str, string->len);
+ g_string_free (string, TRUE);
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+ }
+
+ /* If we got an error, propagate it without any further response string */
+ if (inner_error) {
+ g_string_free (string, TRUE);
+ g_propagate_error (error, inner_error);
+ return MM_PORT_SERIAL_RESPONSE_ERROR;
+ }
+
+ /* Otherwise, build a new GByteArray considered as parsed response */
+ parsed_len = string->len;
+ *parsed_response = g_byte_array_new_take ((guint8 *) g_string_free (string, FALSE), parsed_len);
+ return MM_PORT_SERIAL_RESPONSE_BUFFER;
}
/*****************************************************************************/
@@ -188,9 +208,12 @@ mm_port_serial_at_add_unsolicited_msg_handler (MMPortSerialAt *self,
if (handler->notify)
handler->notify (handler->user_data);
} else {
+ /* The new handler is always PREPENDED, so that e.g. plugins can provide
+ * more specific matches for URCs that are also handled by the generic
+ * plugin. */
handler = g_slice_new (MMAtUnsolicitedMsgHandler);
- self->priv->unsolicited_msg_handlers = g_slist_append (self->priv->unsolicited_msg_handlers, handler);
handler->regex = g_regex_ref (regex);
+ self->priv->unsolicited_msg_handlers = g_slist_prepend (self->priv->unsolicited_msg_handlers, handler);
}
handler->callback = callback;
@@ -404,6 +427,7 @@ mm_port_serial_at_command (MMPortSerialAt *self,
buf,
timeout_seconds,
allow_cached,
+ is_raw, /* raw commands always run next, never queued last */
cancellable,
(GAsyncReadyCallback)serial_command_ready,
simple);
@@ -411,10 +435,13 @@ mm_port_serial_at_command (MMPortSerialAt *self,
}
static void
-debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
+debug_log (MMPortSerial *self,
+ const gchar *prefix,
+ const gchar *buf,
+ gsize len)
{
static GString *debug = NULL;
- const char *s;
+ const char *s;
if (!debug)
debug = g_string_sized_new (256);
@@ -437,7 +464,7 @@ debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
}
g_string_append_c (debug, '\'');
- mm_dbg ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str);
+ mm_obj_dbg (self, "%s", debug->str);
g_string_truncate (debug, 0);
}
@@ -446,6 +473,8 @@ mm_port_serial_at_set_flags (MMPortSerialAt *self, MMPortSerialAtFlag flags)
{
g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_PORT_SERIAL_AT (self));
+
+ /* MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC is not expected */
g_return_if_fail (flags <= (MM_PORT_SERIAL_AT_FLAG_PRIMARY |
MM_PORT_SERIAL_AT_FLAG_SECONDARY |
MM_PORT_SERIAL_AT_FLAG_PPP |
@@ -473,7 +502,7 @@ mm_port_serial_at_run_init_sequence (MMPortSerialAt *self)
if (!self->priv->init_sequence)
return;
- mm_dbg ("(%s): running init sequence...", mm_port_get_device (MM_PORT (self)));
+ mm_obj_dbg (self, "running init sequence...");
/* Just queue the init commands, don't wait for reply */
for (i = 0; self->priv->init_sequence[i]; i++) {
@@ -500,17 +529,13 @@ config (MMPortSerial *_self)
/*****************************************************************************/
MMPortSerialAt *
-mm_port_serial_at_new (const char *name,
- MMPortSubsys subsys)
+mm_port_serial_at_new (const char *name,
+ MMPortSubsys subsys)
{
- g_return_val_if_fail (subsys == MM_PORT_SUBSYS_TTY ||
- subsys == MM_PORT_SUBSYS_USB ||
- subsys == MM_PORT_SUBSYS_UNIX, NULL);
-
return MM_PORT_SERIAL_AT (g_object_new (MM_TYPE_PORT_SERIAL_AT,
MM_PORT_DEVICE, name,
MM_PORT_SUBSYS, subsys,
- MM_PORT_TYPE, MM_PORT_TYPE_AT,
+ MM_PORT_TYPE, MM_PORT_TYPE_AT,
NULL));
}
diff --git a/src/mm-port-serial-at.h b/src/mm-port-serial-at.h
index 8e19409e..901afcba 100644
--- a/src/mm-port-serial-at.h
+++ b/src/mm-port-serial-at.h
@@ -42,20 +42,24 @@ typedef struct _MMPortSerialAtPrivate MMPortSerialAtPrivate;
* only when connecting is port 0 opened for dialing (ATD) and PPP
*/
typedef enum { /*< underscore_name=mm_port_serial_at_flag >*/
- MM_PORT_SERIAL_AT_FLAG_NONE = 0,
+ MM_PORT_SERIAL_AT_FLAG_NONE = 0,
/* This port is preferred for command and status */
- MM_PORT_SERIAL_AT_FLAG_PRIMARY = 1 << 0,
+ MM_PORT_SERIAL_AT_FLAG_PRIMARY = 1 << 0,
/* Use port for command and status if the primary port is connected */
- MM_PORT_SERIAL_AT_FLAG_SECONDARY = 1 << 1,
+ MM_PORT_SERIAL_AT_FLAG_SECONDARY = 1 << 1,
/* This port should be used for PPP */
- MM_PORT_SERIAL_AT_FLAG_PPP = 1 << 2,
+ MM_PORT_SERIAL_AT_FLAG_PPP = 1 << 2,
/* This port should be used for GPS control */
- MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL = 1 << 3,
+ MM_PORT_SERIAL_AT_FLAG_GPS_CONTROL = 1 << 3,
+ /* Helper flag to allow plugins specify that generic tags shouldn't be
+ * applied */
+ MM_PORT_SERIAL_AT_FLAG_NONE_NO_GENERIC = 1 << 4,
} MMPortSerialAtFlag;
-typedef gboolean (*MMPortSerialAtResponseParserFn) (gpointer user_data,
- GString *response,
- GError **error);
+typedef gboolean (*MMPortSerialAtResponseParserFn) (gpointer user_data,
+ GString *response,
+ gpointer log_object,
+ GError **error);
typedef void (*MMPortSerialAtUnsolicitedMsgFn) (MMPortSerialAt *port,
GMatchInfo *match_info,
@@ -76,6 +80,7 @@ struct _MMPortSerialAtClass {
};
GType mm_port_serial_at_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialAt, g_object_unref)
MMPortSerialAt *mm_port_serial_at_new (const char *name,
MMPortSubsys subsys);
diff --git a/src/mm-port-serial-gps.c b/src/mm-port-serial-gps.c
index 306df1fe..ea404994 100644
--- a/src/mm-port-serial-gps.c
+++ b/src/mm-port-serial-gps.c
@@ -19,7 +19,7 @@
#include <string.h>
#include "mm-port-serial-gps.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
G_DEFINE_TYPE (MMPortSerialGps, mm_port_serial_gps, MM_TYPE_PORT_SERIAL)
@@ -68,9 +68,10 @@ remove_eval_cb (const GMatchInfo *match_info,
return FALSE;
}
-static gboolean
+static MMPortSerialResponseType
parse_response (MMPortSerial *port,
GByteArray *response,
+ GByteArray **parsed_response,
GError **error)
{
MMPortSerialGps *self = MM_PORT_SERIAL_GPS (port);
@@ -112,7 +113,7 @@ parse_response (MMPortSerial *port,
g_match_info_free (match_info);
if (!matches)
- return FALSE;
+ return MM_PORT_SERIAL_RESPONSE_NONE;
/* Remove matches */
result_len = response->len;
@@ -122,9 +123,11 @@ parse_response (MMPortSerial *port,
0, 0,
remove_eval_cb, &result_len, NULL);
+ /* Cleanup response buffer */
g_byte_array_remove_range (response, 0, response->len);
- g_byte_array_append (response, (const guint8 *) str, result_len);
- g_free (str);
+
+ /* Build parsed response */
+ *parsed_response = g_byte_array_new_take ((guint8 *)str, result_len);
return TRUE;
}
@@ -132,10 +135,13 @@ parse_response (MMPortSerial *port,
/*****************************************************************************/
static void
-debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
+debug_log (MMPortSerial *self,
+ const gchar *prefix,
+ const gchar *buf,
+ gsize len)
{
static GString *debug = NULL;
- const char *s;
+ const gchar *s;
if (!debug)
debug = g_string_sized_new (256);
@@ -158,7 +164,7 @@ debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
}
g_string_append_c (debug, '\'');
- mm_dbg ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str);
+ mm_obj_dbg (self, "%s", debug->str);
g_string_truncate (debug, 0);
}
diff --git a/src/mm-port-serial-gps.h b/src/mm-port-serial-gps.h
index 5655b5e3..ad5a97fc 100644
--- a/src/mm-port-serial-gps.h
+++ b/src/mm-port-serial-gps.h
@@ -46,6 +46,7 @@ struct _MMPortSerialGpsClass {
};
GType mm_port_serial_gps_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialGps, g_object_unref)
MMPortSerialGps *mm_port_serial_gps_new (const char *name);
diff --git a/src/mm-port-serial-qcdm.c b/src/mm-port-serial-qcdm.c
index b63927ff..08f24303 100644
--- a/src/mm-port-serial-qcdm.c
+++ b/src/mm-port-serial-qcdm.c
@@ -26,16 +26,22 @@
#include "libqcdm/src/com.h"
#include "libqcdm/src/utils.h"
#include "libqcdm/src/errors.h"
-#include "mm-log.h"
+#include "libqcdm/src/dm-commands.h"
+#include "mm-log-object.h"
G_DEFINE_TYPE (MMPortSerialQcdm, mm_port_serial_qcdm, MM_TYPE_PORT_SERIAL)
+struct _MMPortSerialQcdmPrivate {
+ GSList *unsolicited_msg_handlers;
+};
+
/*****************************************************************************/
static gboolean
find_qcdm_start (GByteArray *response, gsize *start)
{
- int i, last = -1;
+ guint i;
+ gint last = -1;
/* Look for 3 bytes and a QCDM frame marker, ie enough data for a valid
* frame. There will usually be three cases here; (1) a QCDM frame
@@ -44,8 +50,13 @@ find_qcdm_start (GByteArray *response, gsize *start)
* uses HDLC framing (like Sierra CnS) that starts and ends with 0x7E.
*/
for (i = 0; i < response->len; i++) {
+ /* Marker found */
if (response->data[i] == 0x7E) {
- if (i > last + 3) {
+ /* If we didn't get an initial marker, count at least 3 bytes since
+ * origin; if we did get an initial marker, count at least 3 bytes
+ * since the marker.
+ */
+ if (((last == -1) && (i >= 3)) || ((last >= 0) && (i > (guint)(last + 3)))) {
/* Got a full QCDM frame; 3 non-0x7E bytes and a terminator */
if (start)
*start = last + 1;
@@ -59,10 +70,86 @@ find_qcdm_start (GByteArray *response, gsize *start)
return FALSE;
}
-static gboolean
-parse_response (MMPortSerial *port, GByteArray *response, GError **error)
+static MMPortSerialResponseType
+parse_qcdm (GByteArray *response,
+ gboolean want_log,
+ GByteArray **parsed_response,
+ GError **error)
{
- return find_qcdm_start (response, NULL);
+ gsize start = 0;
+ gsize used = 0;
+ gsize unescaped_len = 0;
+ guint8 *unescaped_buffer;
+ qcdmbool more = FALSE;
+
+ /* Get the offset into the buffer of where the QCDM frame starts */
+ if (!find_qcdm_start (response, &start)) {
+ /* Discard the unparsable data right away, we do need a QCDM
+ * start, and anything that comes before it is unknown data
+ * that we'll never use. */
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+ }
+
+ /* If there is anything before the start marker, remove it */
+ g_byte_array_remove_range (response, 0, start);
+ if (response->len == 0)
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+
+ /* Try to decapsulate the response into a buffer */
+ unescaped_buffer = g_malloc (1024);
+ if (!dm_decapsulate_buffer ((const char *)(response->data),
+ response->len,
+ (char *)unescaped_buffer,
+ 1024,
+ &unescaped_len,
+ &used,
+ &more)) {
+ /* Report an error right away. Not being able to decapsulate a QCDM
+ * packet once we got message start marker likely means that this
+ * data that we got is not a QCDM message. */
+ g_set_error (error,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_PARSE_FAILED,
+ "Failed to unescape QCDM packet");
+ g_free (unescaped_buffer);
+ return MM_PORT_SERIAL_RESPONSE_ERROR;
+ }
+
+ if (more) {
+ /* Need more data, we leave the original byte array untouched so that
+ * we can retry later when more data arrives. */
+ g_free (unescaped_buffer);
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+ }
+
+ if (want_log && unescaped_buffer[0] != DIAG_CMD_LOG) {
+ /* If we only want log items and this isn't one, don't remove this
+ * DM packet from the buffer.
+ */
+ g_free (unescaped_buffer);
+ return MM_PORT_SERIAL_RESPONSE_NONE;
+ }
+
+ /* Successfully decapsulated the DM command. We'll build a new byte array
+ * with the response, and leave the input buffer cleaned up. */
+ g_assert (unescaped_len <= 1024);
+ unescaped_buffer = g_realloc (unescaped_buffer, unescaped_len);
+ *parsed_response = g_byte_array_new_take (unescaped_buffer, unescaped_len);
+
+ /* Remove the data we used from the input buffer, leaving out any
+ * additional data that may already been received (e.g. from the following
+ * message). */
+ g_byte_array_remove_range (response, 0, used);
+ return MM_PORT_SERIAL_RESPONSE_BUFFER;
+}
+
+static MMPortSerialResponseType
+parse_response (MMPortSerial *port,
+ GByteArray *response,
+ GByteArray **parsed_response,
+ GError **error)
+{
+ return parse_qcdm (response, FALSE, parsed_response, error);
}
/*****************************************************************************/
@@ -72,88 +159,24 @@ mm_port_serial_qcdm_command_finish (MMPortSerialQcdm *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
-
- return g_byte_array_ref ((GByteArray *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
serial_command_ready (MMPortSerial *port,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
- GByteArray *response_buffer;
GByteArray *response;
GError *error = NULL;
- gsize used = 0;
- gsize start = 0;
- guint8 *unescaped_buffer = NULL;
- gboolean success = FALSE;
- qcdmbool more = FALSE;
- gsize unescaped_len = 0;
- response_buffer = mm_port_serial_command_finish (port, res, &error);
- if (!response_buffer)
- goto out;
+ response = mm_port_serial_command_finish (port, res, &error);
+ if (!response)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, response, (GDestroyNotify)g_byte_array_unref);
- /* Get the offset into the buffer of where the QCDM frame starts */
- start = 0;
- if (!find_qcdm_start (response_buffer, &start)) {
- error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_FRAME_NOT_FOUND,
- "QCDM frame start not found");
- /* Discard the unparsable data */
- used = response_buffer->len;
- goto out;
- }
-
- unescaped_buffer = g_malloc (1024);
- success = dm_decapsulate_buffer ((const char *)(response_buffer->data + start),
- response_buffer->len - start,
- (char *)unescaped_buffer,
- 1024,
- &unescaped_len,
- &used,
- &more);
- if (!success) {
- error = g_error_new_literal (MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_PARSE_FAILED,
- "Failed to unescape QCDM packet");
- g_free (unescaped_buffer);
- unescaped_buffer = NULL;
- goto out;
- }
-
- if (more) {
- /* Need more data; we shouldn't have gotten here since the parse
- * function checks for the end-of-frame marker, but whatever.
- */
- error = g_error_new_literal (MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "QCDM packet is not complete");
- g_free (unescaped_buffer);
- unescaped_buffer = NULL;
- goto out;
- }
-
- /* Successfully decapsulated the DM command */
- g_assert (error == NULL);
- g_assert (unescaped_len <= 1024);
- unescaped_buffer = g_realloc (unescaped_buffer, unescaped_len);
- response = g_byte_array_new_take (unescaped_buffer, unescaped_len);
- g_simple_async_result_set_op_res_gpointer (simple, response, (GDestroyNotify)g_byte_array_unref);
-
-out:
- if (error)
- g_simple_async_result_take_error (simple, error);
- if (start + used)
- g_byte_array_remove_range (response_buffer, 0, start + used);
- if (response_buffer)
- g_byte_array_unref (response_buffer);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
void
@@ -164,31 +187,32 @@ mm_port_serial_qcdm_command (MMPortSerialQcdm *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- GSimpleAsyncResult *simple;
+ GTask *task;
g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
g_return_if_fail (command != NULL);
- simple = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_serial_qcdm_command);
+ task = g_task_new (self, cancellable, callback, user_data);
/* 'command' is expected to be already CRC-ed and escaped */
mm_port_serial_command (MM_PORT_SERIAL (self),
command,
timeout_seconds,
FALSE, /* never cached */
+ FALSE, /* always queued last */
cancellable,
(GAsyncReadyCallback)serial_command_ready,
- simple);
+ task);
}
static void
-debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
+debug_log (MMPortSerial *self,
+ const gchar *prefix,
+ const gchar *buf,
+ gsize len)
{
static GString *debug = NULL;
- const char *s = buf;
+ const gchar *s = buf;
if (!debug)
debug = g_string_sized_new (512);
@@ -198,12 +222,117 @@ debug_log (MMPortSerial *port, const char *prefix, const char *buf, gsize len)
while (len--)
g_string_append_printf (debug, " %02x", (guint8) (*s++ & 0xFF));
- mm_dbg ("(%s): %s", mm_port_get_device (MM_PORT (port)), debug->str);
+ mm_obj_dbg (self, "%s", debug->str);
g_string_truncate (debug, 0);
}
/*****************************************************************************/
+typedef struct {
+ guint log_code;
+ MMPortSerialQcdmUnsolicitedMsgFn callback;
+ gboolean enable;
+ gpointer user_data;
+ GDestroyNotify notify;
+} MMQcdmUnsolicitedMsgHandler;
+
+static gint
+unsolicited_msg_handler_cmp (MMQcdmUnsolicitedMsgHandler *handler,
+ gpointer log_code)
+{
+ return handler->log_code - GPOINTER_TO_UINT (log_code);
+}
+
+void
+mm_port_serial_qcdm_add_unsolicited_msg_handler (MMPortSerialQcdm *self,
+ guint log_code,
+ MMPortSerialQcdmUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify)
+{
+ GSList *existing;
+ MMQcdmUnsolicitedMsgHandler *handler;
+
+ g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
+ g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16);
+
+ existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers,
+ GUINT_TO_POINTER (log_code),
+ (GCompareFunc)unsolicited_msg_handler_cmp);
+ if (existing) {
+ handler = existing->data;
+ /* We OVERWRITE any existing one, so if any context data existing, free it */
+ if (handler->notify)
+ handler->notify (handler->user_data);
+ } else {
+ handler = g_slice_new (MMQcdmUnsolicitedMsgHandler);
+ self->priv->unsolicited_msg_handlers = g_slist_append (self->priv->unsolicited_msg_handlers, handler);
+ handler->log_code = log_code;
+ }
+
+ handler->callback = callback;
+ handler->enable = TRUE;
+ handler->user_data = user_data;
+ handler->notify = notify;
+}
+
+void
+mm_port_serial_qcdm_enable_unsolicited_msg_handler (MMPortSerialQcdm *self,
+ guint log_code,
+ gboolean enable)
+{
+ GSList *existing;
+ MMQcdmUnsolicitedMsgHandler *handler;
+
+ g_return_if_fail (MM_IS_PORT_SERIAL_QCDM (self));
+ g_return_if_fail (log_code > 0 && log_code <= G_MAXUINT16);
+
+ existing = g_slist_find_custom (self->priv->unsolicited_msg_handlers,
+ GUINT_TO_POINTER (log_code),
+ (GCompareFunc)unsolicited_msg_handler_cmp);
+ if (existing) {
+ handler = existing->data;
+ handler->enable = enable;
+ }
+}
+
+static void
+parse_unsolicited (MMPortSerial *port, GByteArray *response)
+{
+ MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (port);
+ GByteArray *log_buffer = NULL;
+ GSList *iter;
+
+ if (parse_qcdm (response,
+ TRUE,
+ &log_buffer,
+ NULL) != MM_PORT_SERIAL_RESPONSE_BUFFER) {
+ return;
+ }
+
+ /* These should be guaranteed by parse_qcdm() */
+ g_return_if_fail (log_buffer);
+ g_return_if_fail (log_buffer->len > 0);
+ g_return_if_fail (log_buffer->data[0] == DIAG_CMD_LOG);
+
+ if (log_buffer->len < sizeof (DMCmdLog))
+ return;
+
+ for (iter = self->priv->unsolicited_msg_handlers; iter; iter = iter->next) {
+ MMQcdmUnsolicitedMsgHandler *handler = (MMQcdmUnsolicitedMsgHandler *) iter->data;
+ DMCmdLog *log_cmd = (DMCmdLog *) log_buffer->data;
+
+ if (!handler->enable)
+ continue;
+ if (handler->log_code != le16toh (log_cmd->log_code))
+ continue;
+ if (handler->callback)
+ handler->callback (self, log_buffer, handler->user_data);
+ }
+}
+
+/*****************************************************************************/
+
static gboolean
config_fd (MMPortSerial *port, int fd, GError **error)
{
@@ -221,11 +350,12 @@ config_fd (MMPortSerial *port, int fd, GError **error)
/*****************************************************************************/
MMPortSerialQcdm *
-mm_port_serial_qcdm_new (const char *name)
+mm_port_serial_qcdm_new (const char *name,
+ MMPortSubsys subsys)
{
return MM_PORT_SERIAL_QCDM (g_object_new (MM_TYPE_PORT_SERIAL_QCDM,
MM_PORT_DEVICE, name,
- MM_PORT_SUBSYS, MM_PORT_SUBSYS_TTY,
+ MM_PORT_SUBSYS, subsys,
MM_PORT_TYPE, MM_PORT_TYPE_QCDM,
MM_PORT_SERIAL_SEND_DELAY, (guint64) 0,
NULL));
@@ -252,14 +382,39 @@ mm_port_serial_qcdm_new_fd (int fd)
static void
mm_port_serial_qcdm_init (MMPortSerialQcdm *self)
{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_PORT_SERIAL_QCDM, MMPortSerialQcdmPrivate);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMPortSerialQcdm *self = MM_PORT_SERIAL_QCDM (object);
+
+ while (self->priv->unsolicited_msg_handlers) {
+ MMQcdmUnsolicitedMsgHandler *handler = (MMQcdmUnsolicitedMsgHandler *) self->priv->unsolicited_msg_handlers->data;
+
+ if (handler->notify)
+ handler->notify (handler->user_data);
+
+ g_slice_free (MMQcdmUnsolicitedMsgHandler, handler);
+ self->priv->unsolicited_msg_handlers = g_slist_delete_link (self->priv->unsolicited_msg_handlers,
+ self->priv->unsolicited_msg_handlers);
+ }
+
+ G_OBJECT_CLASS (mm_port_serial_qcdm_parent_class)->finalize (object);
}
static void
mm_port_serial_qcdm_class_init (MMPortSerialQcdmClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMPortSerialClass *port_class = MM_PORT_SERIAL_CLASS (klass);
+ g_type_class_add_private (object_class, sizeof (MMPortSerialQcdmPrivate));
+
/* Virtual methods */
+ object_class->finalize = finalize;
+ port_class->parse_unsolicited = parse_unsolicited;
port_class->parse_response = parse_response;
port_class->config_fd = config_fd;
port_class->debug_log = debug_log;
diff --git a/src/mm-port-serial-qcdm.h b/src/mm-port-serial-qcdm.h
index 5e3e38f5..85668a49 100644
--- a/src/mm-port-serial-qcdm.h
+++ b/src/mm-port-serial-qcdm.h
@@ -31,9 +31,11 @@
typedef struct _MMPortSerialQcdm MMPortSerialQcdm;
typedef struct _MMPortSerialQcdmClass MMPortSerialQcdmClass;
+typedef struct _MMPortSerialQcdmPrivate MMPortSerialQcdmPrivate;
struct _MMPortSerialQcdm {
MMPortSerial parent;
+ MMPortSerialQcdmPrivate *priv;
};
struct _MMPortSerialQcdmClass {
@@ -41,8 +43,10 @@ struct _MMPortSerialQcdmClass {
};
GType mm_port_serial_qcdm_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerialQcdm, g_object_unref)
-MMPortSerialQcdm *mm_port_serial_qcdm_new (const char *name);
+MMPortSerialQcdm *mm_port_serial_qcdm_new (const char *name,
+ MMPortSubsys subsys);
MMPortSerialQcdm *mm_port_serial_qcdm_new_fd (int fd);
void mm_port_serial_qcdm_command (MMPortSerialQcdm *self,
@@ -55,4 +59,18 @@ GByteArray *mm_port_serial_qcdm_command_finish (MMPortSerialQcdm *self,
GAsyncResult *res,
GError **error);
+typedef void (*MMPortSerialQcdmUnsolicitedMsgFn) (MMPortSerialQcdm *port,
+ GByteArray *log_buffer,
+ gpointer user_data);
+
+void mm_port_serial_qcdm_add_unsolicited_msg_handler (MMPortSerialQcdm *self,
+ guint log_code,
+ MMPortSerialQcdmUnsolicitedMsgFn callback,
+ gpointer user_data,
+ GDestroyNotify notify);
+
+void mm_port_serial_qcdm_enable_unsolicited_msg_handler (MMPortSerialQcdm *self,
+ guint log_code,
+ gboolean enable);
+
#endif /* MM_PORT_SERIAL_QCDM_H */
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
index 3af1b8e2..8b6e34ec 100644
--- a/src/mm-port-serial.c
+++ b/src/mm-port-serial.c
@@ -34,7 +34,8 @@
#include <mm-errors-types.h>
#include "mm-port-serial.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
+#include "mm-helper-enums-types.h"
static gboolean port_serial_queue_process (gpointer data);
static void port_serial_schedule_queue_process (MMPortSerial *self,
@@ -53,10 +54,10 @@ enum {
PROP_BITS,
PROP_PARITY,
PROP_STOPBITS,
+ PROP_FLOW_CONTROL,
PROP_SEND_DELAY,
PROP_FD,
PROP_SPEW_CONTROL,
- PROP_RTS_CTS,
PROP_FLASH_OK,
LAST_PROP
@@ -90,15 +91,14 @@ struct _MMPortSerialPrivate {
GSocket *socket;
GSource *socket_source;
- struct termios old_t;
guint baud;
guint bits;
char parity;
guint stopbits;
+ MMFlowControl flow_control;
guint64 send_delay;
gboolean spew_control;
- gboolean rts_cts;
gboolean flash_ok;
guint queue_id;
@@ -111,8 +111,8 @@ struct _MMPortSerialPrivate {
guint connected_id;
- gpointer flash_ctx;
- gpointer reopen_ctx;
+ GTask *flash_task;
+ GTask *reopen_task;
};
/*****************************************************************************/
@@ -163,6 +163,7 @@ mm_port_serial_command (MMPortSerial *self,
GByteArray *command,
guint32 timeout_seconds,
gboolean allow_cached,
+ gboolean run_next,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -203,7 +204,12 @@ mm_port_serial_command (MMPortSerial *self,
if (!allow_cached)
port_serial_set_cached_reply (self, ctx->command, NULL);
- g_queue_push_tail (self->priv->queue, ctx);
+ /* If requested to run next, push to the head of the queue so that it really is
+ * the next one sent */
+ if (run_next)
+ g_queue_push_head (self->priv->queue, ctx);
+ else
+ g_queue_push_tail (self->priv->queue, ctx);
if (g_queue_get_length (self->priv->queue) == 1)
port_serial_schedule_queue_process (self, 0);
@@ -211,95 +217,13 @@ mm_port_serial_command (MMPortSerial *self,
/*****************************************************************************/
-#if 0
-static const char *
-baud_to_string (int baud)
-{
- const char *speed = NULL;
-
- switch (baud) {
- case B0:
- speed = "0";
- break;
- case B50:
- speed = "50";
- break;
- case B75:
- speed = "75";
- break;
- case B110:
- speed = "110";
- break;
- case B150:
- speed = "150";
- break;
- case B300:
- speed = "300";
- break;
- case B600:
- speed = "600";
- break;
- case B1200:
- speed = "1200";
- break;
- case B2400:
- speed = "2400";
- break;
- case B4800:
- speed = "4800";
- break;
- case B9600:
- speed = "9600";
- break;
- case B19200:
- speed = "19200";
- break;
- case B38400:
- speed = "38400";
- break;
- case B57600:
- speed = "57600";
- break;
- case B115200:
- speed = "115200";
- break;
- case B460800:
- speed = "460800";
- break;
- default:
- break;
- }
-
- return speed;
-}
-
-void
-mm_port_serial_print_config (MMPortSerial *port,
- const char *detail)
-{
- struct termios stbuf;
- int err;
-
- err = tcgetattr (self->priv->fd, &stbuf);
- if (err) {
- mm_warn ("*** %s (%s): (%s) tcgetattr() error %d",
- __func__, detail, mm_port_get_device (MM_PORT (port)), errno);
- return;
- }
-
- mm_info ("(%s): (%s) baud rate: %d (%s)",
- detail, mm_port_get_device (MM_PORT (port)),
- stbuf.c_cflag & CBAUD,
- baud_to_string (stbuf.c_cflag & CBAUD));
-}
-#endif
-
-static int
-parse_baudrate (guint i)
+static gboolean
+parse_baudrate (guint baudrate_num,
+ guint *out_baudrate_speed)
{
- int speed;
+ guint speed;
- switch (i) {
+ switch (baudrate_num) {
case 0:
speed = B0;
break;
@@ -345,21 +269,28 @@ parse_baudrate (guint i)
case 115200:
speed = B115200;
break;
+ case 230400:
+ speed = B230400;
+ break;
case 460800:
speed = B460800;
break;
+ case 921600:
+ speed = B921600;
+ break;
default:
- mm_warn ("Invalid baudrate '%d'", i);
- speed = B9600;
+ return FALSE;
}
- return speed;
+ if (out_baudrate_speed)
+ *out_baudrate_speed = speed;
+ return TRUE;
}
static int
parse_bits (guint i)
{
- int bits;
+ int bits = -1;
switch (i) {
case 5:
@@ -375,8 +306,7 @@ parse_bits (guint i)
bits = CS8;
break;
default:
- mm_warn ("Invalid bits (%d). Valid values are 5, 6, 7, 8.", i);
- bits = CS8;
+ g_assert_not_reached ();
}
return bits;
@@ -385,7 +315,7 @@ parse_bits (guint i)
static int
parse_parity (char c)
{
- int parity;
+ int parity = -1;
switch (c) {
case 'n':
@@ -401,8 +331,7 @@ parse_parity (char c)
parity = PARENB | PARODD;
break;
default:
- mm_warn ("Invalid parity (%c). Valid values are n, e, o", c);
- parity = 0;
+ g_assert_not_reached ();
}
return parity;
@@ -411,7 +340,7 @@ parse_parity (char c)
static int
parse_stopbits (guint i)
{
- int stopbits;
+ int stopbits = -1;
switch (i) {
case 1:
@@ -421,54 +350,145 @@ parse_stopbits (guint i)
stopbits = CSTOPB;
break;
default:
- mm_warn ("Invalid stop bits (%d). Valid values are 1 and 2)", i);
- stopbits = 0;
+ g_assert_not_reached ();
}
return stopbits;
}
static gboolean
+internal_tcsetattr (MMPortSerial *self,
+ gint fd,
+ const struct termios *options,
+ GError **error)
+{
+ guint count;
+ struct termios other;
+
+#define MAX_TCSETATTR_RETRIES 4
+
+ for (count = 0; count < MAX_TCSETATTR_RETRIES; count++) {
+ /* try to set the new port attributes */
+ errno = 0;
+ if (tcsetattr (fd, TCSANOW, options) == 0)
+ break;
+
+ /* hard error if not EAGAIN */
+ if (errno != EAGAIN) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't set serial port attributes: %s", g_strerror (errno));
+ return FALSE;
+ }
+
+ /* try a few times if EAGAIN */
+ g_usleep (100000);
+ }
+
+ /* too many retries? */
+ if (count == MAX_TCSETATTR_RETRIES) {
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't set serial port attributes: too many retries (%u)", count);
+ return FALSE;
+ }
+
+ /* tcsetattr() returns 0 if any of the requested attributes could be set,
+ * so we should double-check that all were set and log if not. Just with
+ * debug level, as we're ignoring this issue all together anyway.
+ */
+ memset (&other, 0, sizeof (struct termios));
+ errno = 0;
+ if (tcgetattr (fd, &other) != 0)
+ mm_obj_dbg (self, "couldn't get serial port attributes after setting them: %s", g_strerror (errno));
+ else if (memcmp (options, &other, sizeof (struct termios)) != 0)
+ mm_obj_dbg (self, "port attributes not fully set");
+
+#undef MAX_TCSETATTR_RETRIES
+
+ return TRUE;
+}
+
+static gboolean
+set_flow_control_termios (MMPortSerial *self,
+ MMFlowControl flow_control,
+ struct termios *options)
+{
+ gboolean had_xon_xoff;
+ gboolean had_rts_cts;
+ tcflag_t iflag_orig, cflag_orig;
+
+ iflag_orig = options->c_iflag;
+ cflag_orig = options->c_cflag;
+
+ had_xon_xoff = !!(options->c_iflag & (IXON | IXOFF));
+ options->c_iflag &= ~(IXON | IXOFF | IXANY);
+
+ had_rts_cts = !!(options->c_cflag & (CRTSCTS));
+ options->c_cflag &= ~(CRTSCTS);
+
+ /* setup the requested flags */
+ switch (flow_control) {
+ case MM_FLOW_CONTROL_XON_XOFF:
+ mm_obj_dbg (self, "enabling XON/XOFF flow control");
+ options->c_iflag |= (IXON | IXOFF | IXANY);
+ break;
+ case MM_FLOW_CONTROL_RTS_CTS:
+ mm_obj_dbg (self, "enabling RTS/CTS flow control");
+ options->c_cflag |= (CRTSCTS);
+ break;
+ case MM_FLOW_CONTROL_NONE:
+ case MM_FLOW_CONTROL_UNKNOWN:
+ if (had_xon_xoff)
+ mm_obj_dbg (self, "disabling XON/XOFF flow control");
+ if (had_rts_cts)
+ mm_obj_dbg (self, "disabling RTS/CTS flow control");
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return iflag_orig != options->c_iflag || cflag_orig != options->c_cflag;
+}
+
+static gboolean
real_config_fd (MMPortSerial *self, int fd, GError **error)
{
- struct termios stbuf, other;
- int speed;
- int bits;
- int parity;
- int stopbits;
+ struct termios stbuf;
+ guint speed;
+ gint bits;
+ gint parity;
+ gint stopbits;
/* No setup if not a tty */
if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY)
return TRUE;
- speed = parse_baudrate (self->priv->baud);
+ mm_obj_dbg (self, "setting up baudrate: %u", self->priv->baud);
+ if (!parse_baudrate (self->priv->baud, &speed) || speed == B0) {
+ mm_obj_warn (self, "baudrate invalid: %u; defaulting to 57600", self->priv->baud);
+ speed = B57600;
+ }
+
bits = parse_bits (self->priv->bits);
parity = parse_parity (self->priv->parity);
stopbits = parse_stopbits (self->priv->stopbits);
memset (&stbuf, 0, sizeof (struct termios));
- if (tcgetattr (fd, &stbuf) != 0) {
- mm_warn ("(%s): tcgetattr() error: %d",
- mm_port_get_device (MM_PORT (self)),
- errno);
- }
+ if (tcgetattr (fd, &stbuf) != 0)
+ mm_obj_warn (self, "error getting serial port attributes: %s", g_strerror (errno));
- stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY );
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS);
+ stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXOFF | IXANY );
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
- stbuf.c_lflag &= ~(ICANON | XCASE | ECHO | ECHOE | ECHONL);
- stbuf.c_lflag &= ~(ECHO | ECHOE);
+ stbuf.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL);
stbuf.c_cc[VMIN] = 1;
stbuf.c_cc[VTIME] = 0;
stbuf.c_cc[VEOF] = 1;
- /* Use software handshaking and ignore parity/framing errors */
- stbuf.c_iflag |= (IXON | IXOFF | IXANY | IGNPAR);
+ /* Ignore parity/framing errors */
+ stbuf.c_iflag |= IGNPAR;
- /* Set up port speed and serial attributes; also ignore modem control
- * lines since most drivers don't implement RTS/CTS anyway.
- */
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS);
- stbuf.c_cflag |= (bits | CREAD | 0 | parity | stopbits | CLOCAL);
+ /* Set up port speed and serial attributes and enable receiver in local mode */
+ stbuf.c_cflag |= (bits | parity | stopbits | CLOCAL | CREAD);
errno = 0;
if (cfsetispeed (&stbuf, speed) != 0) {
@@ -490,36 +510,25 @@ real_config_fd (MMPortSerial *self, int fd, GError **error)
return FALSE;
}
- if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s: failed to set serial port attributes; errno %d",
- __func__, errno);
- return FALSE;
- }
+ if (self->priv->flow_control != MM_FLOW_CONTROL_UNKNOWN) {
+ gchar *str;
- /* tcsetattr() returns 0 if any of the requested attributes could be set,
- * so we should double-check that all were set and log a warning if not.
- */
- memset (&other, 0, sizeof (struct termios));
- errno = 0;
- if (tcgetattr (fd, &other) != 0) {
- mm_warn ("(%s): tcgetattr() error: %d",
- mm_port_get_device (MM_PORT (self)),
- errno);
- }
+ str = mm_flow_control_build_string_from_mask (self->priv->flow_control);
+ mm_obj_dbg (self, "flow control explicitly requested for device is: %s", str ? str : "unknown");
+ g_free (str);
+ } else
+ mm_obj_dbg (self, "no flow control explicitly requested for device");
- if (memcmp (&stbuf, &other, sizeof (other)) != 0) {
- mm_warn ("(%s): port attributes not fully set",
- mm_port_get_device (MM_PORT (self)));
- }
+ set_flow_control_termios (self, self->priv->flow_control, &stbuf);
- return TRUE;
+ return internal_tcsetattr (self, fd, &stbuf, error);
}
static void
-serial_debug (MMPortSerial *self, const char *prefix, const char *buf, gsize len)
+serial_debug (MMPortSerial *self,
+ const gchar *prefix,
+ const gchar *buf,
+ gsize len)
{
g_return_if_fail (len > 0);
@@ -551,7 +560,7 @@ port_serial_process_command (MMPortSerial *self,
/* Only print command the first time */
if (ctx->started == FALSE) {
ctx->started = TRUE;
- serial_debug (self, "-->", (const char *) ctx->command->data, ctx->command->len);
+ serial_debug (self, "-->", (const gchar *) ctx->command->data, ctx->command->len);
}
if (self->priv->send_delay == 0 || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) {
@@ -585,7 +594,8 @@ port_serial_process_command (MMPortSerial *self,
ctx->idx += written;
break;
}
- /* If written == 0, treat as EAGAIN, so fall down */
+ /* If written == 0 treat as EAGAIN */
+ /* Fall through */
case G_IO_STATUS_AGAIN:
/* We're in a non-blocking channel and therefore we're up to receive
@@ -597,10 +607,13 @@ port_serial_process_command (MMPortSerial *self,
g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
- "Sending command failed: '%s'", strerror (errno));
+ "Sending command failed: '%s'", g_strerror (errno));
return FALSE;
}
break;
+
+ default:
+ g_assert_not_reached ();
}
}
/* Socket based setup */
@@ -629,14 +642,14 @@ port_serial_process_command (MMPortSerial *self,
self->priv->n_consecutive_timeouts++;
g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_SEND_FAILED,
- "Sending command failed: '%s'", strerror (errno));
+ "Sending command failed: '%s'", g_strerror (errno));
return FALSE;
}
/* Just keep on, will retry... */
written = 0;
} else
- written = bytes_sent;
+ written = bytes_sent;
ctx->idx += written;
} else
@@ -696,9 +709,11 @@ port_serial_schedule_queue_process (MMPortSerial *self, guint timeout_ms)
static void
port_serial_got_response (MMPortSerial *self,
+ GByteArray *parsed_response,
const GError *error)
{
- CommandContext *ctx;
+ /* Either one or the other, not both */
+ g_assert ((parsed_response && !error) || (!parsed_response && error));
if (self->priv->timeout_id) {
g_source_remove (self->priv->timeout_id);
@@ -714,28 +729,36 @@ port_serial_got_response (MMPortSerial *self,
g_clear_object (&self->priv->cancellable);
- ctx = (CommandContext *) g_queue_pop_head (self->priv->queue);
- if (ctx) {
- if (error)
- g_simple_async_result_set_from_error (ctx->result, error);
- else {
- if (ctx->allow_cached && !error)
- port_serial_set_cached_reply (self, ctx->command, self->priv->response);
-
- /* Upon completion, it is a task of the caller to remove from the response
- * buffer the processed data */
- g_simple_async_result_set_op_res_gpointer (ctx->result,
- g_byte_array_ref (self->priv->response),
- (GDestroyNotify)g_byte_array_unref);
+ /* The completion of the command context may end up fully disposing the
+ * serial port object. In order to cope with that, we make sure we have
+ * our own reference to the object while the completion and follow up
+ * setup runs. */
+ g_object_ref (self);
+ {
+ CommandContext *ctx;
+
+ ctx = (CommandContext *) g_queue_pop_head (self->priv->queue);
+ if (ctx) {
+ /* Complete the command context with the appropriate result */
+ if (error)
+ g_simple_async_result_set_from_error (ctx->result, error);
+ else {
+ if (ctx->allow_cached)
+ port_serial_set_cached_reply (self, ctx->command, parsed_response);
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ g_byte_array_ref (parsed_response),
+ (GDestroyNotify) g_byte_array_unref);
+ }
+
+ /* Don't complete in idle. We need the caller remove the response range which
+ * was processed, and that must be done before processing any new queued command */
+ command_context_complete_and_free (ctx, FALSE);
}
- /* Don't complete in idle. We need the caller remove the response range which
- * was processed, and that must be done before processing any new queued command */
- command_context_complete_and_free (ctx, FALSE);
+ if (!g_queue_is_empty (self->priv->queue))
+ port_serial_schedule_queue_process (self, 0);
}
-
- if (!g_queue_is_empty (self->priv->queue))
- port_serial_schedule_queue_process (self, 0);
+ g_object_unref (self);
}
static gboolean
@@ -755,14 +778,21 @@ port_serial_timed_out (gpointer data)
error = g_error_new_literal (MM_SERIAL_ERROR,
MM_SERIAL_ERROR_RESPONSE_TIMEOUT,
"Serial command timed out");
- port_serial_got_response (self, error);
- g_error_free (error);
- /* Emit a timed out signal, used by upper layers to identify a disconnected
- * serial port */
- g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
+ /* Make sure we have a valid reference when emitting the signal */
+ g_object_ref (self);
+ {
+ port_serial_got_response (self, NULL, error);
- return FALSE;
+ /* Emit a timed out signal, used by upper layers to identify a disconnected
+ * serial port */
+ g_signal_emit (self, signals[TIMED_OUT], 0, self->priv->n_consecutive_timeouts);
+ }
+ g_object_unref (self);
+
+ g_error_free (error);
+
+ return G_SOURCE_REMOVE;
}
static void
@@ -777,10 +807,10 @@ port_serial_response_wait_cancelled (GCancellable *cancellable,
/* FIXME: This is not completely correct - if the response finally arrives and there's
* some other command waiting for response right now, the other command will
* get the output of the cancelled command. Not sure what to do here. */
- error = g_error_new_literal (MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
+ error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED,
"Waiting for the reply cancelled");
- port_serial_got_response (self, error);
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
}
@@ -795,26 +825,21 @@ port_serial_queue_process (gpointer data)
ctx = (CommandContext *) g_queue_peek_head (self->priv->queue);
if (!ctx)
- return FALSE;
+ return G_SOURCE_REMOVE;
if (ctx->allow_cached) {
const GByteArray *cached;
cached = port_serial_get_cached_reply (self, ctx->command);
if (cached) {
- /* Ensure the response array is fully empty before setting the
- * cached response. */
- if (self->priv->response->len > 0) {
- mm_warn ("(%s) response array is not empty when using cached "
- "reply, cleaning up %u bytes",
- mm_port_get_device (MM_PORT (self)),
- self->priv->response->len);
- g_byte_array_set_size (self->priv->response, 0);
- }
-
- g_byte_array_append (self->priv->response, cached->data, cached->len);
- port_serial_got_response (self, NULL);
- return FALSE;
+ GByteArray *parsed_response;
+
+ parsed_response = g_byte_array_sized_new (cached->len);
+ g_byte_array_append (parsed_response, cached->data, cached->len);
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, parsed_response, NULL);
+ g_byte_array_unref (parsed_response);
+ return G_SOURCE_REMOVE;
}
/* Cached reply wasn't found, keep on */
@@ -822,9 +847,10 @@ port_serial_queue_process (gpointer data)
/* If error, report it */
if (!port_serial_process_command (self, ctx, &error)) {
- port_serial_got_response (self, error);
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, NULL, error);
g_error_free (error);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/* Schedule the next byte of the command to be sent */
@@ -833,44 +859,87 @@ port_serial_queue_process (gpointer data)
(mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY ?
self->priv->send_delay / 1000 :
0));
- return FALSE;
+ return G_SOURCE_REMOVE;
}
/* Setup the cancellable so that we can stop waiting for a response */
if (ctx->cancellable) {
+ gulong cancellable_id;
+
self->priv->cancellable = g_object_ref (ctx->cancellable);
- self->priv->cancellable_id = (g_cancellable_connect (
- ctx->cancellable,
- (GCallback)port_serial_response_wait_cancelled,
- self,
- NULL));
- if (!self->priv->cancellable_id) {
- error = g_error_new (MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Won't wait for the reply");
- port_serial_got_response (self, error);
- g_error_free (error);
- return FALSE;
- }
+
+ /* If the GCancellable is already cancelled here, the callback will be
+ * called right away, and a GError will be propagated as response. In
+ * this case we need to completely avoid doing anything else with the
+ * MMPortSerial, as it may already be disposed.
+ * So, use an intermediate variable to store the cancellable id, and
+ * just return without further processing if we're already cancelled.
+ */
+ cancellable_id = g_cancellable_connect (ctx->cancellable,
+ (GCallback)port_serial_response_wait_cancelled,
+ self,
+ NULL);
+ if (!cancellable_id)
+ return G_SOURCE_REMOVE;
+
+ self->priv->cancellable_id = cancellable_id;
}
/* If the command is finished being sent, schedule the timeout */
self->priv->timeout_id = g_timeout_add_seconds (ctx->timeout,
port_serial_timed_out,
self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
-static gboolean
-parse_response (MMPortSerial *self,
- GByteArray *response,
- GError **error)
+static void
+parse_response_buffer (MMPortSerial *self)
{
- if (MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited)
- MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited (self, response);
+ GError *error = NULL;
+ GByteArray *parsed_response = NULL;
- g_return_val_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->parse_response, FALSE);
- return MM_PORT_SERIAL_GET_CLASS (self)->parse_response (self, response, error);
+ /* Parse unsolicited messages in the subclass.
+ *
+ * If any message found, it's processed immediately and the message is
+ * removed from the response buffer.
+ */
+ if (MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited)
+ MM_PORT_SERIAL_GET_CLASS (self)->parse_unsolicited (self,
+ self->priv->response);
+
+ /* Parse response in the subclass.
+ *
+ * Returns TRUE either if an error is provided or if we really have the
+ * response to process. The parsed string is returned already out of the
+ * response buffer, and the response buffer is cleaned up accordingly.
+ */
+ g_assert (MM_PORT_SERIAL_GET_CLASS (self)->parse_response != NULL);
+ switch (MM_PORT_SERIAL_GET_CLASS (self)->parse_response (self,
+ self->priv->response,
+ &parsed_response,
+ &error)) {
+ case MM_PORT_SERIAL_RESPONSE_BUFFER:
+ /* We have a valid response to process */
+ g_assert (parsed_response);
+ self->priv->n_consecutive_timeouts = 0;
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, parsed_response, NULL);
+ g_byte_array_unref (parsed_response);
+ break;
+ case MM_PORT_SERIAL_RESPONSE_ERROR:
+ /* We have an error to process */
+ g_assert (error);
+ self->priv->n_consecutive_timeouts = 0;
+ /* Note: may complete last operation and unref the MMPortSerial */
+ port_serial_got_response (self, NULL, error);
+ g_error_free (error);
+ break;
+ case MM_PORT_SERIAL_RESPONSE_NONE:
+ /* Nothing to do this time */
+ break;
+ default:
+ g_assert_not_reached ();
+ }
}
static gboolean
@@ -881,31 +950,30 @@ common_input_available (MMPortSerial *self,
gsize bytes_read;
GIOStatus status = G_IO_STATUS_NORMAL;
CommandContext *ctx;
- const char *device;
GError *error = NULL;
+ gboolean iterate = TRUE;
+ gboolean keep_source = G_SOURCE_CONTINUE;
if (condition & G_IO_HUP) {
- device = mm_port_get_device (MM_PORT (self));
- mm_dbg ("(%s) unexpected port hangup!", device);
-
+ mm_obj_dbg (self, "unexpected port hangup!");
if (self->priv->response->len)
g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
port_serial_close_force (self);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
if (condition & G_IO_ERR) {
if (self->priv->response->len)
g_byte_array_remove_range (self->priv->response, 0, self->priv->response->len);
- return TRUE;
+ return G_SOURCE_CONTINUE;
}
/* Don't read any input if the current command isn't done being sent yet */
ctx = g_queue_peek_nth (self->priv->queue, 0);
if (ctx && (ctx->started == TRUE) && (ctx->done == FALSE))
- return TRUE;
+ return G_SOURCE_CONTINUE;
- do {
+ while (iterate) {
bytes_read = 0;
if (self->priv->iochannel) {
@@ -915,35 +983,32 @@ common_input_available (MMPortSerial *self,
&bytes_read,
&error);
if (status == G_IO_STATUS_ERROR) {
- if (error) {
- mm_warn ("(%s): read error: %s",
- mm_port_get_device (MM_PORT (self)),
- error->message);
- }
+ if (error)
+ mm_obj_warn (self, "read error: %s", error->message);
g_clear_error (&error);
}
} else if (self->priv->socket) {
- bytes_read = g_socket_receive (self->priv->socket,
- buf,
- SERIAL_BUF_SIZE,
- NULL, /* cancellable */
- &error);
- if (bytes_read == -1) {
+ gssize sbytes_read;
+
+ sbytes_read = g_socket_receive (self->priv->socket,
+ buf,
+ SERIAL_BUF_SIZE,
+ NULL, /* cancellable */
+ &error);
+ if (sbytes_read < 0) {
bytes_read = 0;
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
status = G_IO_STATUS_AGAIN;
else
status = G_IO_STATUS_ERROR;
- mm_warn ("(%s): receive error: %s",
- mm_port_get_device (MM_PORT (self)),
- error->message);
+ mm_obj_warn (self, "receive error: %s", error->message);
g_clear_error (&error);
- } else
+ } else {
+ bytes_read = (gsize) sbytes_read;
status = G_IO_STATUS_NORMAL;
+ }
}
-
-
/* If no bytes read, just wait for more data */
if (bytes_read == 0)
break;
@@ -959,19 +1024,30 @@ common_input_available (MMPortSerial *self,
g_byte_array_remove_range (self->priv->response, 0, (SERIAL_BUF_SIZE / 2));
}
- /* Parse response. Returns TRUE either if an error is provided or if
- * we really have the response to process. */
- if (parse_response (self, self->priv->response, &error)) {
- /* Reset number of consecutive timeouts only here */
- self->priv->n_consecutive_timeouts = 0;
- /* Process response retrieved */
- port_serial_got_response (self, error);
- g_clear_error (&error);
+ /* See if we can parse anything. The response parsing may actually
+ * schedule the completion of a serial command, and that in turn may end
+ * up fully disposing this serial port object. In order to cope with
+ * that we make sure we have our own reference to the object while the
+ * response buffer operation is run, and then we check ourselves whether
+ * we should be keeping this socket/iochannel source or not. */
+ g_object_ref (self);
+ {
+ parse_response_buffer (self);
+
+ /* If we didn't end up closing the iochannel/socket in the previous
+ * operation, we keep this source. */
+ keep_source = ((self->priv->iochannel_id > 0 || self->priv->socket_source != NULL) ?
+ G_SOURCE_CONTINUE : G_SOURCE_REMOVE);
+
+ /* If we're keeping the source and we still may have bytes to read,
+ * iterate. */
+ iterate = ((keep_source == G_SOURCE_CONTINUE) &&
+ (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN));
}
- } while ( (bytes_read == SERIAL_BUF_SIZE || status == G_IO_STATUS_AGAIN)
- && (self->priv->iochannel_id > 0 || self->priv->socket_source != NULL));
+ g_object_unref (self);
+ }
- return TRUE;
+ return keep_source;
}
static gboolean
@@ -996,7 +1072,6 @@ data_watch_enable (MMPortSerial *self, gboolean enable)
if (self->priv->iochannel_id) {
if (enable)
g_warn_if_fail (self->priv->iochannel_id == 0);
-
g_source_remove (self->priv->iochannel_id);
self->priv->iochannel_id = 0;
}
@@ -1045,11 +1120,9 @@ port_connected (MMPortSerial *self, GParamSpec *pspec, gpointer user_data)
connected = mm_port_get_connected (MM_PORT (self));
if (self->priv->fd >= 0 && ioctl (self->priv->fd, (connected ? TIOCNXCL : TIOCEXCL)) < 0) {
- mm_warn ("(%s): could not %s serial port lock: (%d) %s",
- mm_port_get_device (MM_PORT (self)),
- connected ? "drop" : "re-acquire",
- errno,
- strerror (errno));
+ mm_obj_warn (self, "could not %s serial port lock: %s",
+ connected ? "drop" : "re-acquire",
+ g_strerror (errno));
if (!connected) {
// FIXME: do something here, maybe try again in a few seconds or
// close the port and error out?
@@ -1082,7 +1155,7 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
return FALSE;
}
- if (self->priv->reopen_ctx) {
+ if (self->priv->reopen_task) {
g_set_error (error,
MM_SERIAL_ERROR,
MM_SERIAL_ERROR_OPEN_FAILED,
@@ -1091,12 +1164,21 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
return FALSE;
}
+ if (mm_port_get_connected (MM_PORT (self))) {
+ g_set_error (error,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_OPEN_FAILED,
+ "Could not open serial device %s: port is connected",
+ device);
+ return FALSE;
+ }
+
if (self->priv->open_count) {
/* Already open */
goto success;
}
- mm_dbg ("(%s) opening serial port...", device);
+ mm_obj_dbg (self, "opening serial port...");
g_get_current_time (&tv_start);
@@ -1120,7 +1202,6 @@ mm_port_serial_open (MMPortSerial *self, GError **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_save));
- mm_warn ("(%s) could not open serial device (%d)", device, errno_save);
return FALSE;
}
}
@@ -1132,21 +1213,12 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
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_save));
- mm_warn ("(%s) could not lock serial device (%d)", device, errno_save);
goto error;
}
/* Flush any waiting IO */
tcflush (self->priv->fd, TCIOFLUSH);
- if (tcgetattr (self->priv->fd, &self->priv->old_t) < 0) {
- errno_save = errno;
- g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_OPEN_FAILED,
- "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;
- }
-
/* 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
* for 30 seconds when probing fails. See GNOME bug #630670.
@@ -1154,21 +1226,21 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
if (ioctl (self->priv->fd, TIOCGSERIAL, &sinfo) == 0) {
sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE;
if (ioctl (self->priv->fd, TIOCSSERIAL, &sinfo) < 0)
- mm_warn ("(%s): couldn't set serial port closing_wait to none: %s",
- device, g_strerror (errno));
+ mm_obj_warn (self, "couldn't set serial port closing_wait to none: %s", g_strerror (errno));
}
}
g_warn_if_fail (MM_PORT_SERIAL_GET_CLASS (self)->config_fd);
- if (self->priv->fd >= 0 && !MM_PORT_SERIAL_GET_CLASS (self)->config_fd (self, self->priv->fd, error)) {
- mm_dbg ("(%s) failed to configure serial device", device);
+ if (self->priv->fd >= 0 && mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_WWAN &&
+ !MM_PORT_SERIAL_GET_CLASS (self)->config_fd (self, self->priv->fd, error)) {
+ mm_obj_dbg (self, "failed to configure serial device");
goto error;
}
g_get_current_time (&tv_end);
if (tv_end.tv_sec - tv_start.tv_sec > 7)
- mm_warn ("(%s): open blocked by driver for more than 7 seconds!", device);
+ mm_obj_warn (self, "open blocked by driver for more than 7 seconds!");
if (mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_UNIX) {
/* Create new GIOChannel */
@@ -1234,7 +1306,7 @@ mm_port_serial_open (MMPortSerial *self, GError **error)
success:
self->priv->open_count++;
- mm_dbg ("(%s) device open count is %d (open)", device, self->priv->open_count);
+ mm_obj_dbg (self, "device open count is %d (open)", self->priv->open_count);
/* Run additional port config if just opened */
if (self->priv->open_count == 1 && MM_PORT_SERIAL_GET_CLASS (self)->config)
@@ -1243,10 +1315,9 @@ success:
return TRUE;
error:
- mm_warn ("(%s) failed to open serial device", device);
+ mm_obj_warn (self, "failed to open serial device");
if (self->priv->iochannel) {
- g_io_channel_shutdown (self->priv->iochannel, FALSE, NULL);
g_io_channel_unref (self->priv->iochannel);
self->priv->iochannel = NULL;
}
@@ -1277,8 +1348,7 @@ mm_port_serial_is_open (MMPortSerial *self)
static void
_close_internal (MMPortSerial *self, gboolean force)
{
- const char *device;
- int i;
+ guint i;
g_return_if_fail (MM_IS_PORT_SERIAL (self));
@@ -1289,15 +1359,16 @@ _close_internal (MMPortSerial *self, gboolean force)
self->priv->open_count--;
}
- device = mm_port_get_device (MM_PORT (self));
-
- mm_dbg ("(%s) device open count is %d (close)", device, self->priv->open_count);
+ mm_obj_dbg (self, "device open count is %d (close)", self->priv->open_count);
if (self->priv->open_count > 0)
return;
if (self->priv->connected_id) {
- g_signal_handler_disconnect (self, self->priv->connected_id);
+ /* Don't assume it's always connected, because it may be automatically connected during
+ * object disposal, and this method is also called in finalize() */
+ if (g_signal_handler_is_connected (self, self->priv->connected_id))
+ g_signal_handler_disconnect (self, self->priv->connected_id);
self->priv->connected_id = 0;
}
@@ -1307,7 +1378,7 @@ _close_internal (MMPortSerial *self, gboolean force)
GTimeVal tv_start, tv_end;
struct serial_struct sinfo = { 0 };
- mm_dbg ("(%s) closing serial port...", device);
+ mm_obj_dbg (self, "closing serial port...");
mm_port_set_connected (MM_PORT (self), FALSE);
@@ -1320,22 +1391,22 @@ _close_internal (MMPortSerial *self, gboolean force)
*/
if (ioctl (self->priv->fd, TIOCGSERIAL, &sinfo) == 0) {
if (sinfo.closing_wait != ASYNC_CLOSING_WAIT_NONE) {
- mm_warn ("(%s): serial port closing_wait was reset!", device);
+ mm_obj_warn (self, "serial port closing_wait was reset!");
sinfo.closing_wait = ASYNC_CLOSING_WAIT_NONE;
if (ioctl (self->priv->fd, TIOCSSERIAL, &sinfo) < 0)
- mm_warn ("(%s): couldn't set serial port closing_wait to none: %s",
- device, g_strerror (errno));
+ mm_obj_warn (self, "couldn't set serial port closing_wait to none: %s", g_strerror (errno));
}
}
- tcsetattr (self->priv->fd, TCSANOW, &self->priv->old_t);
tcflush (self->priv->fd, TCIOFLUSH);
}
/* Destroy channel */
if (self->priv->iochannel) {
data_watch_enable (self, FALSE);
- g_io_channel_shutdown (self->priv->iochannel, TRUE, NULL);
+ /* unref() without g_io_channel_shutdown() to destroy the channel
+ * without closing the fd. The close() is called explicitly after.
+ */
g_io_channel_unref (self->priv->iochannel);
self->priv->iochannel = NULL;
}
@@ -1356,7 +1427,7 @@ _close_internal (MMPortSerial *self, gboolean force)
g_get_current_time (&tv_end);
- mm_dbg ("(%s) serial port closed", device);
+ mm_obj_dbg (self, "serial port closed");
/* Some ports don't respond to data and when close is called
* the serial layer waits up to 30 second (closing_wait) for
@@ -1364,7 +1435,7 @@ _close_internal (MMPortSerial *self, gboolean force)
* Log that. See GNOME bug #630670 for more details.
*/
if (tv_end.tv_sec - tv_start.tv_sec > 7)
- mm_warn ("(%s): close blocked by driver for more than 7 seconds!", device);
+ mm_obj_warn (self, "close blocked by driver for more than 7 seconds!");
}
/* Clear the command queue */
@@ -1418,7 +1489,7 @@ port_serial_close_force (MMPortSerial *self)
if (self->priv->forced_close)
return;
- mm_dbg ("(%s) forced to close port", mm_port_get_device (MM_PORT (self)));
+ mm_obj_dbg (self, "forced to close port");
/* Mark as having forced the close, so that we don't warn about incorrect
* open counts */
@@ -1440,20 +1511,15 @@ port_serial_close_force (MMPortSerial *self)
/* Reopen */
typedef struct {
- MMPortSerial *self;
- GSimpleAsyncResult *result;
guint initial_open_count;
guint reopen_id;
} ReopenContext;
static void
-reopen_context_complete_and_free (ReopenContext *ctx)
+reopen_context_free (ReopenContext *ctx)
{
if (ctx->reopen_id)
g_source_remove (ctx->reopen_id);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (ReopenContext, ctx);
}
@@ -1462,56 +1528,64 @@ mm_port_serial_reopen_finish (MMPortSerial *port,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
port_serial_reopen_cancel (MMPortSerial *self)
{
- ReopenContext *ctx;
+ GTask *task;
- if (!self->priv->reopen_ctx)
+ if (!self->priv->reopen_task)
return;
- /* Recover context */
- ctx = (ReopenContext *)self->priv->reopen_ctx;
- self->priv->reopen_ctx = NULL;
+ /* Recover task */
+ task = self->priv->reopen_task;
+ self->priv->reopen_task = NULL;
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Reopen cancelled");
- reopen_context_complete_and_free (ctx);
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Reopen cancelled");
+ g_object_unref (task);
}
static gboolean
reopen_do (MMPortSerial *self)
{
+ GTask *task;
ReopenContext *ctx;
GError *error = NULL;
guint i;
- /* Recover context */
- g_assert (self->priv->reopen_ctx != NULL);
- ctx = (ReopenContext *)self->priv->reopen_ctx;
- self->priv->reopen_ctx = NULL;
+ /* Recover task */
+ g_assert (self->priv->reopen_task != NULL);
+ task = self->priv->reopen_task;
+ self->priv->reopen_task = NULL;
+ ctx = g_task_get_task_data (task);
ctx->reopen_id = 0;
for (i = 0; i < ctx->initial_open_count; i++) {
- if (!mm_port_serial_open (ctx->self, &error)) {
+ if (!mm_port_serial_open (self, &error)) {
g_prefix_error (&error, "Couldn't reopen port (%u): ", i);
break;
}
}
- if (error)
- g_simple_async_result_take_error (ctx->result, error);
- else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- reopen_context_complete_and_free (ctx);
+ if (error) {
+ /* An error during port reopening may mean that the device is
+ * already gone. Note that we won't get a HUP in the TTY when
+ * the port is gone during the reopen wait time, because there's
+ * no channel I/O monitoring in place.
+ *
+ * If we ever see this, we'll flag the port as forced close right
+ * away, because the open count would anyway be broken afterwards.
+ */
+ port_serial_close_force (self);
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void
@@ -1521,41 +1595,38 @@ mm_port_serial_reopen (MMPortSerial *self,
gpointer user_data)
{
ReopenContext *ctx;
+ GTask *task;
guint i;
g_return_if_fail (MM_IS_PORT_SERIAL (self));
/* Setup context */
ctx = g_slice_new0 (ReopenContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_serial_reopen);
ctx->initial_open_count = self->priv->open_count;
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)reopen_context_free);
+
if (self->priv->forced_close) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Serial port has been forced close.");
- reopen_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Serial port has been forced close.");
+ g_object_unref (task);
return;
}
/* If already reopening, halt */
- if (self->priv->reopen_ctx) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "Modem is already being reopened");
- reopen_context_complete_and_free (ctx);
+ if (self->priv->reopen_task) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "Modem is already being reopened");
+ g_object_unref (task);
return;
}
- mm_dbg ("(%s) reopening port (%u)",
- mm_port_get_device (MM_PORT (self)),
- ctx->initial_open_count);
+ mm_obj_dbg (self, "reopening port (%u)", ctx->initial_open_count);
for (i = 0; i < ctx->initial_open_count; i++)
mm_port_serial_close (self);
@@ -1566,7 +1637,7 @@ mm_port_serial_reopen (MMPortSerial *self,
ctx->reopen_id = g_idle_add ((GSourceFunc)reopen_do, self);
/* Store context in private info */
- self->priv->reopen_ctx = ctx;
+ self->priv->reopen_task = task;
}
static gboolean
@@ -1594,15 +1665,11 @@ static gboolean
set_speed (MMPortSerial *self, speed_t speed, GError **error)
{
struct termios options;
- int fd, count = 4;
- gboolean success = FALSE;
g_assert (self->priv->fd >= 0);
- fd = self->priv->fd;
-
memset (&options, 0, sizeof (struct termios));
- if (tcgetattr (fd, &options) != 0) {
+ if (tcgetattr (self->priv->fd, &options) != 0) {
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
@@ -1615,60 +1682,22 @@ set_speed (MMPortSerial *self, speed_t speed, GError **error)
cfsetospeed (&options, speed);
options.c_cflag |= (CLOCAL | CREAD);
- /* Configure flow control as well here */
- if (self->priv->rts_cts)
- options.c_cflag |= (CRTSCTS);
-
- while (count-- > 0) {
- if (tcsetattr (fd, TCSANOW, &options) == 0) {
- success = TRUE;
- break; /* Operation successful */
- }
-
- /* Try a few times if EAGAIN */
- if (errno == EAGAIN)
- g_usleep (100000);
- else {
- /* If not EAGAIN, hard error */
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s: tcsetattr() error %d",
- __func__, errno);
- return FALSE;
- }
- }
-
- if (!success) {
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "%s: tcsetattr() retry timeout",
- __func__);
- return FALSE;
- }
-
- return TRUE;
+ return internal_tcsetattr (self, self->priv->fd, &options, error);
}
/*****************************************************************************/
/* Flash */
typedef struct {
- GSimpleAsyncResult *result;
- MMPortSerial *self;
speed_t current_speed;
guint flash_id;
} FlashContext;
static void
-flash_context_complete_and_free (FlashContext *ctx)
+flash_context_free (FlashContext *ctx)
{
if (ctx->flash_id)
g_source_remove (ctx->flash_id);
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
g_slice_free (FlashContext, ctx);
}
@@ -1677,44 +1706,63 @@ mm_port_serial_flash_finish (MMPortSerial *port,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+flash_cancel_cb (GTask *task)
+{
+ g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Flash cancelled");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
}
void
mm_port_serial_flash_cancel (MMPortSerial *self)
{
FlashContext *ctx;
+ GTask *task;
- if (!self->priv->flash_ctx)
+ /* Do nothing if there is no flash task */
+ if (!self->priv->flash_task)
return;
- /* Recover context */
- ctx = (FlashContext *)self->priv->flash_ctx;
- self->priv->flash_ctx = NULL;
+ /* Recover task */
+ task = self->priv->flash_task;
+ self->priv->flash_task = NULL;
+
+ /* If flash operation is scheduled, unschedule it */
+ ctx = g_task_get_task_data (task);
+ if (ctx->flash_id) {
+ g_source_remove (ctx->flash_id);
+ ctx->flash_id = 0;
+ }
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_CANCELLED,
- "Flash cancelled");
- flash_context_complete_and_free (ctx);
+ /* Schedule task to be cancelled in an idle.
+ * We do NOT want this cancellation to happen right away,
+ * because the object reference in the flashing task may
+ * be the last one valid. */
+ g_idle_add ((GSourceFunc)flash_cancel_cb, task);
}
static gboolean
flash_do (MMPortSerial *self)
{
+ GTask *task;
FlashContext *ctx;
GError *error = NULL;
- /* Recover context */
- g_assert (self->priv->flash_ctx != NULL);
- ctx = (FlashContext *)self->priv->flash_ctx;
- self->priv->flash_ctx = NULL;
+ /* Recover task */
+ g_assert (self->priv->flash_task != NULL);
+ task = self->priv->flash_task;
+ self->priv->flash_task = NULL;
+ ctx = g_task_get_task_data (task);
ctx->flash_id = 0;
if (self->priv->flash_ok && mm_port_get_subsys (MM_PORT (self)) == MM_PORT_SUBSYS_TTY) {
if (ctx->current_speed) {
- if (!set_speed (ctx->self, ctx->current_speed, &error))
+ if (!set_speed (self, ctx->current_speed, &error))
g_assert (error);
} else {
error = g_error_new_literal (MM_SERIAL_ERROR,
@@ -1724,12 +1772,12 @@ flash_do (MMPortSerial *self)
}
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- flash_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
- return FALSE;
+ return G_SOURCE_REMOVE;
}
void
@@ -1740,6 +1788,7 @@ mm_port_serial_flash (MMPortSerial *self,
gpointer user_data)
{
FlashContext *ctx;
+ GTask *task;
GError *error = NULL;
gboolean success;
@@ -1747,33 +1796,31 @@ mm_port_serial_flash (MMPortSerial *self,
/* Setup context */
ctx = g_slice_new0 (FlashContext);
- ctx->self = g_object_ref (self);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_port_serial_flash);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)flash_context_free);
if (!mm_port_serial_is_open (self)) {
- g_simple_async_result_set_error (ctx->result,
- MM_SERIAL_ERROR,
- MM_SERIAL_ERROR_NOT_OPEN,
- "The serial port is not open.");
- flash_context_complete_and_free (ctx);
+ g_task_return_new_error (task,
+ MM_SERIAL_ERROR,
+ MM_SERIAL_ERROR_NOT_OPEN,
+ "The serial port is not open.");
+ g_object_unref (task);
return;
}
- if (self->priv->flash_ctx) {
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_IN_PROGRESS,
- "Modem is already being flashed.");
- flash_context_complete_and_free (ctx);
+ if (self->priv->flash_task) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "Modem is already being flashed.");
+ g_object_unref (task);
return;
}
/* Flashing only in TTY */
if (!self->priv->flash_ok || mm_port_get_subsys (MM_PORT (self)) != MM_PORT_SUBSYS_TTY) {
- self->priv->flash_ctx = ctx;
+ self->priv->flash_task = task;
ctx->flash_id = g_idle_add ((GSourceFunc)flash_do, self);
return;
}
@@ -1781,26 +1828,74 @@ mm_port_serial_flash (MMPortSerial *self,
/* Grab current speed so we can reset it after flashing */
success = get_speed (self, &ctx->current_speed, &error);
if (!success && !ignore_errors) {
- g_simple_async_result_take_error (ctx->result, error);
- flash_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_clear_error (&error);
success = set_speed (self, B0, &error);
if (!success && !ignore_errors) {
- g_simple_async_result_take_error (ctx->result, error);
- flash_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
g_clear_error (&error);
- self->priv->flash_ctx = ctx;
+ self->priv->flash_task = task;
ctx->flash_id = g_timeout_add (flash_time, (GSourceFunc)flash_do, self);
}
/*****************************************************************************/
+gboolean
+mm_port_serial_set_flow_control (MMPortSerial *self,
+ MMFlowControl flow_control,
+ GError **error)
+{
+ struct termios options;
+ gchar *flow_control_str = NULL;
+ GError *inner_error = NULL;
+
+ /* retrieve current settings */
+ memset (&options, 0, sizeof (struct termios));
+ if (tcgetattr (self->priv->fd, &options) != 0) {
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't get serial port attributes: %s", g_strerror (errno));
+ goto out;
+ }
+
+ flow_control_str = mm_flow_control_build_string_from_mask (flow_control);
+
+ /* Return if current settings are already what we want */
+ if (!set_flow_control_termios (self, flow_control, &options)) {
+ mm_obj_dbg (self, "no need to change flow control settings: already %s", flow_control_str);
+ goto out;
+ }
+
+ if (!internal_tcsetattr (self, self->priv->fd, &options, &inner_error))
+ goto out;
+
+ mm_obj_dbg (self, "flow control settings updated to %s", flow_control_str);
+
+ out:
+ g_free (flow_control_str);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+MMFlowControl
+mm_port_serial_get_flow_control (MMPortSerial *self)
+{
+ return self->priv->flow_control;
+}
+
+/*****************************************************************************/
+
MMPortSerial *
mm_port_serial_new (const char *name, MMPortType ptype)
{
@@ -1865,6 +1960,7 @@ mm_port_serial_init (MMPortSerial *self)
self->priv->bits = 8;
self->priv->parity = 'n';
self->priv->stopbits = 1;
+ self->priv->flow_control = MM_FLOW_CONTROL_UNKNOWN;
self->priv->send_delay = 1000;
self->priv->queue = g_queue_new ();
@@ -1895,15 +1991,15 @@ set_property (GObject *object,
case PROP_STOPBITS:
self->priv->stopbits = g_value_get_uint (value);
break;
+ case PROP_FLOW_CONTROL:
+ self->priv->flow_control = g_value_get_flags (value);
+ break;
case PROP_SEND_DELAY:
self->priv->send_delay = g_value_get_uint64 (value);
break;
case PROP_SPEW_CONTROL:
self->priv->spew_control = g_value_get_boolean (value);
break;
- case PROP_RTS_CTS:
- self->priv->rts_cts = g_value_get_boolean (value);
- break;
case PROP_FLASH_OK:
self->priv->flash_ok = g_value_get_boolean (value);
break;
@@ -1937,15 +2033,15 @@ get_property (GObject *object,
case PROP_STOPBITS:
g_value_set_uint (value, self->priv->stopbits);
break;
+ case PROP_FLOW_CONTROL:
+ g_value_set_flags (value, self->priv->flow_control);
+ break;
case PROP_SEND_DELAY:
g_value_set_uint64 (value, self->priv->send_delay);
break;
case PROP_SPEW_CONTROL:
g_value_set_boolean (value, self->priv->spew_control);
break;
- case PROP_RTS_CTS:
- g_value_set_boolean (value, self->priv->rts_cts);
- break;
case PROP_FLASH_OK:
g_value_set_boolean (value, self->priv->flash_ok);
break;
@@ -1956,25 +2052,24 @@ get_property (GObject *object,
}
static void
-dispose (GObject *object)
+finalize (GObject *object)
{
MMPortSerial *self = MM_PORT_SERIAL (object);
- if (self->priv->timeout_id) {
- g_source_remove (self->priv->timeout_id);
- self->priv->timeout_id = 0;
- }
-
- port_serial_close_force (MM_PORT_SERIAL (object));
+ port_serial_close_force (MM_PORT_SERIAL (object));
mm_port_serial_flash_cancel (MM_PORT_SERIAL (object));
- G_OBJECT_CLASS (mm_port_serial_parent_class)->dispose (object);
-}
+ /* These are disposed during port closing */
+ g_assert (self->priv->iochannel == NULL);
+ g_assert (self->priv->iochannel_id == 0);
+ g_assert (self->priv->socket == NULL);
+ g_assert (self->priv->socket_source == NULL);
-static void
-finalize (GObject *object)
-{
- MMPortSerial *self = MM_PORT_SERIAL (object);
+ if (self->priv->timeout_id)
+ g_source_remove (self->priv->timeout_id);
+
+ if (self->priv->queue_id)
+ g_source_remove (self->priv->queue_id);
g_hash_table_destroy (self->priv->reply_cache);
g_byte_array_unref (self->priv->response);
@@ -1993,8 +2088,7 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
/* Virtual methods */
object_class->set_property = set_property;
object_class->get_property = get_property;
- object_class->dispose = dispose;
- object_class->finalize = finalize;
+ object_class->finalize = finalize;
klass->config_fd = real_config_fd;
@@ -2040,6 +2134,15 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
G_PARAM_READWRITE));
g_object_class_install_property
+ (object_class, PROP_FLOW_CONTROL,
+ g_param_spec_flags (MM_PORT_SERIAL_FLOW_CONTROL,
+ "FlowControl",
+ "Select flow control",
+ MM_TYPE_FLOW_CONTROL,
+ MM_FLOW_CONTROL_UNKNOWN,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property
(object_class, PROP_SEND_DELAY,
g_param_spec_uint64 (MM_PORT_SERIAL_SEND_DELAY,
"SendDelay",
@@ -2056,21 +2159,13 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
G_PARAM_READWRITE));
g_object_class_install_property
- (object_class, PROP_RTS_CTS,
- g_param_spec_boolean (MM_PORT_SERIAL_RTS_CTS,
- "RTSCTS",
- "Enable RTS/CTS flow control",
- FALSE,
- G_PARAM_READWRITE));
-
- g_object_class_install_property
(object_class, PROP_FLASH_OK,
g_param_spec_boolean (MM_PORT_SERIAL_FLASH_OK,
"FlashOk",
"Flashing the port (0 baud for a short period) "
"is allowed.",
TRUE,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE));
/* Signals */
signals[BUFFER_FULL] =
diff --git a/src/mm-port-serial.h b/src/mm-port-serial.h
index 8f357ae0..16c9eee1 100644
--- a/src/mm-port-serial.h
+++ b/src/mm-port-serial.h
@@ -21,6 +21,7 @@
#include <glib-object.h>
#include <gio/gio.h>
+#include "mm-modem-helpers.h"
#include "mm-port.h"
#define MM_TYPE_PORT_SERIAL (mm_port_serial_get_type ())
@@ -34,11 +35,17 @@
#define MM_PORT_SERIAL_BITS "bits"
#define MM_PORT_SERIAL_PARITY "parity"
#define MM_PORT_SERIAL_STOPBITS "stopbits"
+#define MM_PORT_SERIAL_FLOW_CONTROL "flowcontrol"
#define MM_PORT_SERIAL_SEND_DELAY "send-delay"
-#define MM_PORT_SERIAL_RTS_CTS "rts-cts"
#define MM_PORT_SERIAL_FD "fd" /* Construct-only */
-#define MM_PORT_SERIAL_SPEW_CONTROL "spew-control" /* Construct-only */
-#define MM_PORT_SERIAL_FLASH_OK "flash-ok" /* Construct-only */
+#define MM_PORT_SERIAL_SPEW_CONTROL "spew-control"
+#define MM_PORT_SERIAL_FLASH_OK "flash-ok"
+
+typedef enum {
+ MM_PORT_SERIAL_RESPONSE_NONE,
+ MM_PORT_SERIAL_RESPONSE_BUFFER,
+ MM_PORT_SERIAL_RESPONSE_ERROR,
+} MMPortSerialResponseType;
typedef struct _MMPortSerial MMPortSerial;
typedef struct _MMPortSerialClass MMPortSerialClass;
@@ -58,16 +65,26 @@ struct _MMPortSerialClass {
*/
void (*parse_unsolicited) (MMPortSerial *self, GByteArray *response);
- /* Called to parse the device's response to a command or determine if the
- * response was an error response. If the response indicates an error, an
- * appropriate error should be returned in the 'error' argument. The
- * function should return FALSE if there is not enough data yet to determine
- * the device's reply (whether success *or* error), and should return TRUE
- * when the device's response has been recognized and parsed.
+ /*
+ * Called to parse the device's response to a command or determine if the
+ * response was an error response.
+ *
+ * If the response indicates an error, @MM_PORT_SERIAL_RESPONSE_ERROR will
+ * be returned and an appropriate GError set in @error.
+ *
+ * If the response indicates a valid response, @MM_PORT_SERIAL_RESPONSE_BUFFER
+ * will be returned, and a newly allocated GByteArray set in @parsed_response.
+ *
+ * If there is no response, @MM_PORT_SERIAL_RESPONSE_NONE will be returned,
+ * and neither @error nor @parsed_response will be set.
+ *
+ * The implementation is allowed to cleanup the @response byte array, e.g. to
+ * just remove 1 single response if more than one found.
*/
- gboolean (*parse_response) (MMPortSerial *self,
- GByteArray *response,
- GError **error);
+ MMPortSerialResponseType (*parse_response) (MMPortSerial *self,
+ GByteArray *response,
+ GByteArray **parsed_response,
+ GError **error);
/* Called to configure the serial port fd after it's opened. On error, should
* return FALSE and set 'error' as appropriate.
@@ -76,12 +93,12 @@ struct _MMPortSerialClass {
/* Called to configure the serial port after it's opened. Errors, if any,
* should get ignored. */
- void (*config) (MMPortSerial *self);
+ void (*config) (MMPortSerial *self);
void (*debug_log) (MMPortSerial *self,
- const char *prefix,
- const char *buf,
- gsize len);
+ const gchar *prefix,
+ const gchar *buf,
+ gsize len);
/* Signals */
void (*buffer_full) (MMPortSerial *port, const GByteArray *buffer);
@@ -90,6 +107,7 @@ struct _MMPortSerialClass {
};
GType mm_port_serial_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPortSerial, g_object_unref)
MMPortSerial *mm_port_serial_new (const char *name, MMPortType ptype);
@@ -127,6 +145,7 @@ void mm_port_serial_command (MMPortSerial *self,
GByteArray *command,
guint32 timeout_seconds,
gboolean allow_cached,
+ gboolean run_next,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -134,4 +153,9 @@ GByteArray *mm_port_serial_command_finish (MMPortSerial *self,
GAsyncResult *res,
GError **error);
+gboolean mm_port_serial_set_flow_control (MMPortSerial *self,
+ MMFlowControl flow_control,
+ GError **error);
+
+MMFlowControl mm_port_serial_get_flow_control (MMPortSerial *self);
#endif /* MM_PORT_SERIAL_H */
diff --git a/src/mm-port.c b/src/mm-port.c
index 0ee9a199..285c89a7 100644
--- a/src/mm-port.c
+++ b/src/mm-port.c
@@ -19,9 +19,13 @@
#include <string.h>
#include "mm-port.h"
-#include "mm-log.h"
+#include "mm-port-enums-types.h"
+#include "mm-log-object.h"
-G_DEFINE_TYPE (MMPort, mm_port, G_TYPE_OBJECT)
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMPort, mm_port, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
@@ -29,8 +33,7 @@ enum {
PROP_SUBSYS,
PROP_TYPE,
PROP_CONNECTED,
- PROP_PARENT_PATH,
-
+ PROP_KERNEL_DEVICE,
LAST_PROP
};
@@ -39,7 +42,7 @@ struct _MMPortPrivate {
MMPortSubsys subsys;
MMPortType ptype;
gboolean connected;
- gchar *parent_path;
+ MMKernelDevice *kernel_device;
};
/*****************************************************************************/
@@ -89,19 +92,29 @@ mm_port_set_connected (MMPort *self, gboolean connected)
if (self->priv->connected != connected) {
self->priv->connected = connected;
g_object_notify (G_OBJECT (self), MM_PORT_CONNECTED);
-
- mm_dbg ("(%s): port now %s",
- self->priv->device,
- connected ? "connected" : "disconnected");
+ mm_obj_dbg (self, "port now %s", connected ? "connected" : "disconnected");
}
}
-const gchar *
-mm_port_get_parent_path (MMPort *self)
+MMKernelDevice *
+mm_port_peek_kernel_device (MMPort *self)
{
g_return_val_if_fail (MM_IS_PORT (self), NULL);
- return self->priv->parent_path;
+ return self->priv->kernel_device;
+}
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ MMPort *self;
+
+ self = MM_PORT (_self);
+ return g_strdup_printf ("%s/%s",
+ mm_port_get_device (self),
+ mm_port_type_get_string (mm_port_get_port_type (self)));
}
/*****************************************************************************/
@@ -136,9 +149,10 @@ set_property (GObject *object,
case PROP_CONNECTED:
self->priv->connected = g_value_get_boolean (value);
break;
- case PROP_PARENT_PATH:
- g_free (self->priv->parent_path);
- self->priv->parent_path = g_value_dup_string (value);
+ case PROP_KERNEL_DEVICE:
+ /* Not construct only, but only set once */
+ g_assert (!self->priv->kernel_device);
+ self->priv->kernel_device = g_value_dup_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -165,8 +179,8 @@ get_property (GObject *object, guint prop_id,
case PROP_CONNECTED:
g_value_set_boolean (value, self->priv->connected);
break;
- case PROP_PARENT_PATH:
- g_value_set_string (value, self->priv->parent_path);
+ case PROP_KERNEL_DEVICE:
+ g_value_set_object (value, self->priv->kernel_device);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -180,12 +194,27 @@ finalize (GObject *object)
MMPort *self = MM_PORT (object);
g_free (self->priv->device);
- g_free (self->priv->parent_path);
G_OBJECT_CLASS (mm_port_parent_class)->finalize (object);
}
static void
+dispose (GObject *object)
+{
+ MMPort *self = MM_PORT (object);
+
+ g_clear_object (&self->priv->kernel_device);
+
+ G_OBJECT_CLASS (mm_port_parent_class)->dispose (object);
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_port_class_init (MMPortClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
@@ -196,6 +225,7 @@ mm_port_class_init (MMPortClass *klass)
object_class->set_property = set_property;
object_class->get_property = get_property;
object_class->finalize = finalize;
+ object_class->dispose = dispose;
g_object_class_install_property
(object_class, PROP_DEVICE,
@@ -234,10 +264,10 @@ mm_port_class_init (MMPortClass *klass)
G_PARAM_READWRITE));
g_object_class_install_property
- (object_class, PROP_PARENT_PATH,
- g_param_spec_string (MM_PORT_PARENT_PATH,
- "Parent path",
- "sysfs path of the parent device",
- NULL,
+ (object_class, PROP_KERNEL_DEVICE,
+ g_param_spec_object (MM_PORT_KERNEL_DEVICE,
+ "Kernel device",
+ "kernel device object",
+ MM_TYPE_KERNEL_DEVICE,
G_PARAM_READWRITE));
}
diff --git a/src/mm-port.h b/src/mm-port.h
index 8ac6829a..b5d58706 100644
--- a/src/mm-port.h
+++ b/src/mm-port.h
@@ -20,14 +20,18 @@
#include <glib.h>
#include <glib-object.h>
+#include "mm-kernel-device.h"
+
typedef enum { /*< underscore_name=mm_port_subsys >*/
MM_PORT_SUBSYS_UNKNOWN = 0x0,
MM_PORT_SUBSYS_TTY,
MM_PORT_SUBSYS_NET,
- MM_PORT_SUBSYS_USB,
+ MM_PORT_SUBSYS_USBMISC,
MM_PORT_SUBSYS_UNIX,
-
- MM_PORT_SUBSYS_LAST = MM_PORT_SUBSYS_UNIX /*< skip >*/
+ MM_PORT_SUBSYS_QRTR,
+ MM_PORT_SUBSYS_RPMSG,
+ MM_PORT_SUBSYS_WWAN,
+ MM_PORT_SUBSYS_LAST = MM_PORT_SUBSYS_WWAN /*< skip >*/
} MMPortSubsys;
typedef enum { /*< underscore_name=mm_port_type >*/
@@ -39,7 +43,8 @@ typedef enum { /*< underscore_name=mm_port_type >*/
MM_PORT_TYPE_GPS,
MM_PORT_TYPE_QMI,
MM_PORT_TYPE_MBIM,
- MM_PORT_TYPE_LAST = MM_PORT_TYPE_MBIM /*< skip >*/
+ MM_PORT_TYPE_AUDIO,
+ MM_PORT_TYPE_LAST = MM_PORT_TYPE_AUDIO /*< skip >*/
} MMPortType;
#define MM_TYPE_PORT (mm_port_get_type ())
@@ -49,11 +54,11 @@ 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_CONNECTED "connected"
-#define MM_PORT_PARENT_PATH "parent-path"
+#define MM_PORT_DEVICE "device"
+#define MM_PORT_SUBSYS "subsys"
+#define MM_PORT_TYPE "type"
+#define MM_PORT_CONNECTED "connected"
+#define MM_PORT_KERNEL_DEVICE "kernel-device"
typedef struct _MMPort MMPort;
typedef struct _MMPortClass MMPortClass;
@@ -69,12 +74,13 @@ struct _MMPortClass {
};
GType mm_port_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMPort, g_object_unref)
-const gchar *mm_port_get_device (MMPort *self);
-MMPortSubsys mm_port_get_subsys (MMPort *self);
-MMPortType mm_port_get_port_type (MMPort *self);
-const gchar *mm_port_get_parent_path (MMPort *self);
-gboolean mm_port_get_connected (MMPort *self);
-void mm_port_set_connected (MMPort *self, gboolean connected);
+const gchar *mm_port_get_device (MMPort *self);
+MMPortSubsys mm_port_get_subsys (MMPort *self);
+MMPortType mm_port_get_port_type (MMPort *self);
+gboolean mm_port_get_connected (MMPort *self);
+void mm_port_set_connected (MMPort *self, gboolean connected);
+MMKernelDevice *mm_port_peek_kernel_device (MMPort *self);
#endif /* MM_PORT_H */
diff --git a/src/mm-private-boxed-types.c b/src/mm-private-boxed-types.c
index 465649b5..1c510bb8 100644
--- a/src/mm-private-boxed-types.c
+++ b/src/mm-private-boxed-types.c
@@ -37,18 +37,18 @@ uint16_array_copy (guint16 *array)
GType
mm_uint16_array_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile)) {
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
GType g_define_type_id =
g_boxed_type_register_static (g_intern_static_string ("MMUint16Array"),
(GBoxedCopyFunc) uint16_array_copy,
(GBoxedFreeFunc) g_free);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
}
static mm_uint16_pair *
@@ -72,18 +72,18 @@ uint16_pair_array_copy (mm_uint16_pair *array)
GType
mm_uint16_pair_array_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile)) {
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
GType g_define_type_id =
g_boxed_type_register_static (g_intern_static_string ("MMUint16PairArray"),
(GBoxedCopyFunc) uint16_pair_array_copy,
(GBoxedFreeFunc) g_free);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
}
static void
@@ -122,18 +122,18 @@ str_pair_array_copy (mm_str_pair *array)
GType
mm_str_pair_array_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile)) {
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
GType g_define_type_id =
g_boxed_type_register_static (g_intern_static_string ("MMStrPairArray"),
(GBoxedCopyFunc) str_pair_array_copy,
(GBoxedFreeFunc) str_pair_array_free);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
}
static gpointer *
@@ -157,18 +157,47 @@ pointer_array_copy (gpointer *array)
GType
mm_pointer_array_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile)) {
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
GType g_define_type_id =
g_boxed_type_register_static (g_intern_static_string ("MMPointerArray"),
(GBoxedCopyFunc) pointer_array_copy,
(GBoxedFreeFunc) g_free);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
+}
+
+static GPtrArray *
+object_array_copy (GPtrArray *object_array)
+{
+ return g_ptr_array_ref (object_array);
+}
+
+static void
+object_array_free (GPtrArray *object_array)
+{
+ g_ptr_array_unref (object_array);
+}
+
+GType
+mm_object_array_get_type (void)
+{
+ static gsize g_define_type_id_initialized = 0;
+
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
+ GType g_define_type_id =
+ g_boxed_type_register_static (g_intern_static_string ("MMObjectArray"),
+ (GBoxedCopyFunc) object_array_copy,
+ (GBoxedFreeFunc) object_array_free);
+
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
+ }
+
+ return g_define_type_id_initialized;
}
static void
@@ -194,16 +223,16 @@ async_method_copy (MMAsyncMethod *original)
GType
mm_async_method_get_type (void)
{
- static volatile gsize g_define_type_id__volatile = 0;
+ static gsize g_define_type_id_initialized = 0;
- if (g_once_init_enter (&g_define_type_id__volatile)) {
+ if (g_once_init_enter (&g_define_type_id_initialized)) {
GType g_define_type_id =
g_boxed_type_register_static (g_intern_static_string ("MMAsyncMethod"),
(GBoxedCopyFunc) async_method_copy,
(GBoxedFreeFunc) async_method_free);
- g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+ g_once_init_leave (&g_define_type_id_initialized, g_define_type_id);
}
- return g_define_type_id__volatile;
+ return g_define_type_id_initialized;
}
diff --git a/src/mm-private-boxed-types.h b/src/mm-private-boxed-types.h
index fcdcbb10..8a7d5fcb 100644
--- a/src/mm-private-boxed-types.h
+++ b/src/mm-private-boxed-types.h
@@ -34,6 +34,9 @@ GType mm_str_pair_array_get_type (void) G_GNUC_CONST;
GType mm_pointer_array_get_type (void) G_GNUC_CONST;
#define MM_TYPE_POINTER_ARRAY (mm_pointer_array_get_type ())
+GType mm_object_array_get_type (void) G_GNUC_CONST;
+#define MM_TYPE_OBJECT_ARRAY (mm_object_array_get_type ())
+
typedef struct {
GCallback async;
GCallback finish;
diff --git a/src/mm-qrtr-bus-watcher.c b/src/mm-qrtr-bus-watcher.c
new file mode 100644
index 00000000..ee0cb28b
--- /dev/null
+++ b/src/mm-qrtr-bus-watcher.c
@@ -0,0 +1,354 @@
+/* -*- 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 2020 Google LLC
+ */
+
+#include <ModemManager.h>
+#include <libqrtr-glib.h>
+#include <libqmi-glib.h>
+
+#include "mm-utils.h"
+
+#include "mm-qrtr-bus-watcher.h"
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMQrtrBusWatcher, mm_qrtr_bus_watcher, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+struct _MMQrtrBusWatcherPrivate {
+ QrtrBus *qrtr_bus;
+ guint node_added_id;
+ guint node_removed_id;
+
+ /* Map of NodeNumber -> QRTR nodes available */
+ GHashTable *nodes;
+};
+
+enum {
+ QRTR_DEVICE_ADDED,
+ QRTR_DEVICE_REMOVED,
+ LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("qrtr-bus-watcher");
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ MMQrtrBusWatcher *self;
+ QrtrNode *node;
+} DeviceContext;
+
+static void
+device_context_free (DeviceContext *ctx)
+{
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->node);
+ g_slice_free (DeviceContext, ctx);
+}
+
+static void
+qrtr_node_services_ready (QrtrNode *node,
+ GAsyncResult *res,
+ DeviceContext *ctx)
+{
+ guint32 node_id;
+
+ node_id = qrtr_node_get_id (node);
+ if (!qrtr_node_wait_for_services_finish (node, res, NULL)) {
+ mm_obj_dbg (ctx->self,
+ "qrtr node %u doesn't have required services to be considered a control node",
+ node_id);
+ g_hash_table_remove (ctx->self->priv->nodes, GUINT_TO_POINTER (node_id));
+ device_context_free (ctx);
+ return;
+ }
+
+ mm_obj_info (ctx->self, "qrtr services ready for node %u", node_id);
+ g_signal_emit (ctx->self, signals[QRTR_DEVICE_ADDED], 0, node_id);
+ device_context_free (ctx);
+}
+
+static void
+handle_qrtr_node_added (QrtrBus *qrtr_bus,
+ guint32 node_id,
+ MMQrtrBusWatcher *self)
+{
+ g_autoptr(QrtrNode) node = NULL;
+ g_autoptr(GArray) services = NULL;
+ DeviceContext *ctx;
+ static const QmiService required_services[] = {
+ QMI_SERVICE_WDS,
+ QMI_SERVICE_NAS,
+ QMI_SERVICE_DMS
+ };
+
+ mm_obj_dbg (self, "qrtr node %u added", node_id);
+
+ node = qrtr_bus_get_node (qrtr_bus, node_id);
+ if (!node) {
+ mm_obj_warn (self, "cannot find node %u", node_id);
+ return;
+ }
+
+ if (g_hash_table_contains (self->priv->nodes, GUINT_TO_POINTER (node_id))) {
+ mm_obj_warn (self, "qrtr node %u was previously added", node_id);
+ return;
+ }
+
+ /* a full node reference now owned by the hash table */
+ g_hash_table_insert (self->priv->nodes, GUINT_TO_POINTER (node_id), g_object_ref (node));
+
+ mm_obj_dbg (self, "waiting for modem services on node %u", node_id);
+
+ /* Check if the node provides services to be sure the node represents a
+ * modem. */
+ services = g_array_sized_new (FALSE, FALSE, sizeof (QmiService), G_N_ELEMENTS (required_services));
+ g_array_append_vals (services, required_services, G_N_ELEMENTS (required_services));
+
+ /* Setup command context */
+ ctx = g_slice_new0 (DeviceContext);
+ ctx->self = g_object_ref (self);
+ ctx->node = g_object_ref (node);
+
+ qrtr_node_wait_for_services (node,
+ services,
+ 1000, /* ms */
+ NULL,
+ (GAsyncReadyCallback) qrtr_node_services_ready,
+ ctx);
+}
+
+static void
+handle_qrtr_node_removed (QrtrBus *qrtr_bus,
+ guint32 node_id,
+ MMQrtrBusWatcher *self)
+{
+ QrtrNode *node;
+
+ node = qrtr_bus_get_node (qrtr_bus, node_id);
+ if (!node) {
+ mm_obj_warn (self, "cannot find node %u", node_id);
+ return;
+ }
+
+ g_hash_table_remove (self->priv->nodes, GUINT_TO_POINTER (node_id));
+ mm_obj_info (self, "qrtr node %u removed", node_id);
+
+ g_signal_emit (self, signals[QRTR_DEVICE_REMOVED], 0, node_id);
+}
+
+/*****************************************************************************/
+
+QrtrNode *
+mm_qrtr_bus_watcher_peek_node (MMQrtrBusWatcher *self,
+ guint32 node_id)
+{
+ g_assert (MM_IS_QRTR_BUS_WATCHER (self));
+
+ return g_hash_table_lookup (self->priv->nodes, GUINT_TO_POINTER (node_id));
+}
+
+/*****************************************************************************/
+
+gboolean
+mm_qrtr_bus_watcher_start_finish (MMQrtrBusWatcher *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+typedef struct {
+ MMQrtrBusWatcher *self;
+ QrtrNode *node;
+} ProcessExistingNodes;
+
+static gboolean
+process_existing_nodes_idle (ProcessExistingNodes *ctx)
+{
+ handle_qrtr_node_added (
+ ctx->self->priv->qrtr_bus, qrtr_node_get_id (ctx->node), ctx->self);
+
+ g_object_unref (ctx->self);
+ g_object_unref (ctx->node);
+ g_slice_free (ProcessExistingNodes, ctx);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+process_existing_nodes (MMQrtrBusWatcher *self)
+{
+ GList *nodes, *l;
+ QrtrNode *node;
+ ProcessExistingNodes *ctx;
+
+ nodes = qrtr_bus_peek_nodes (self->priv->qrtr_bus);
+ for (l = nodes; l; l = g_list_next (l)) {
+ node = l->data;
+ ctx = g_slice_new (ProcessExistingNodes);
+ ctx->self = g_object_ref (self);
+ ctx->node = g_object_ref (node);
+ g_idle_add ((GSourceFunc) process_existing_nodes_idle, ctx);
+ }
+}
+
+static void
+qrtr_bus_ready (GObject *source,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMQrtrBusWatcher *self;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ self->priv->qrtr_bus = qrtr_bus_new_finish (res, &error);
+ if (!self->priv->qrtr_bus) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Listen for bus events */
+ self->priv->node_added_id = g_signal_connect (self->priv->qrtr_bus,
+ QRTR_BUS_SIGNAL_NODE_ADDED,
+ G_CALLBACK (handle_qrtr_node_added),
+ self);
+ self->priv->node_removed_id = g_signal_connect (self->priv->qrtr_bus,
+ QRTR_BUS_SIGNAL_NODE_REMOVED,
+ G_CALLBACK (handle_qrtr_node_removed),
+ self);
+
+ process_existing_nodes (self);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_qrtr_bus_watcher_start (MMQrtrBusWatcher *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ qrtr_bus_new (0, /* disable initial lookup wait */
+ NULL,
+ (GAsyncReadyCallback)qrtr_bus_ready,
+ task);
+}
+
+/*****************************************************************************/
+
+MMQrtrBusWatcher *
+mm_qrtr_bus_watcher_new (void)
+{
+ return MM_QRTR_BUS_WATCHER (g_object_new (MM_TYPE_QRTR_BUS_WATCHER, NULL));
+}
+
+static void
+mm_qrtr_bus_watcher_init (MMQrtrBusWatcher *self)
+{
+ /* Initialize opaque pointer to private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_QRTR_BUS_WATCHER,
+ MMQrtrBusWatcherPrivate);
+ /* Setup internal lists of device and node objects */
+ self->priv->nodes = g_hash_table_new_full (g_direct_hash,
+ g_direct_equal,
+ NULL,
+ (GDestroyNotify) g_object_unref);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMQrtrBusWatcher *self = MM_QRTR_BUS_WATCHER (object);
+
+ g_hash_table_destroy (self->priv->nodes);
+
+ if (self->priv->node_added_id)
+ g_signal_handler_disconnect (self->priv->qrtr_bus, self->priv->node_added_id);
+ if (self->priv->node_removed_id)
+ g_signal_handler_disconnect (self->priv->qrtr_bus, self->priv->node_removed_id);
+ g_clear_object (&self->priv->qrtr_bus);
+
+ G_OBJECT_CLASS (mm_qrtr_bus_watcher_parent_class)->finalize (object);
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
+mm_qrtr_bus_watcher_class_init (MMQrtrBusWatcherClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMQrtrBusWatcherPrivate));
+
+ /* Virtual methods */
+ object_class->finalize = finalize;
+
+ /**
+ * QrtrBusWatcher::qrtr-device-added:
+ * @self: the #QrtrBusWatcher
+ * @node: the node ID of the modem that is added
+ *
+ * The ::qrtr-device-added signal is emitted when a new qrtr modem is
+ * available on the QRTR bus.
+ */
+ signals[QRTR_DEVICE_ADDED] = g_signal_new (MM_QRTR_BUS_WATCHER_DEVICE_ADDED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMQrtrBusWatcherClass, qrtr_device_added),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+
+ /**
+ * QrtrBusWatcher::qrtr-device-removed:
+ * @self: the #QrtrBusWatcher
+ * @node: the node ID of the modem that is removed
+ *
+ * The ::qrtr-device-removed signal is emitted when a qrtr modem deregisters
+ * all services from the QRTR bus.
+ */
+ signals[QRTR_DEVICE_REMOVED] = g_signal_new (MM_QRTR_BUS_WATCHER_DEVICE_REMOVED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (MMQrtrBusWatcherClass, qrtr_device_removed),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE,
+ 1,
+ G_TYPE_UINT);
+}
diff --git a/src/mm-qrtr-bus-watcher.h b/src/mm-qrtr-bus-watcher.h
new file mode 100644
index 00000000..b7275447
--- /dev/null
+++ b/src/mm-qrtr-bus-watcher.h
@@ -0,0 +1,69 @@
+/* -*- 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 2020 Google LLC
+ */
+
+#ifndef MM_QRTR_BUS_WATCHER_H
+#define MM_QRTR_BUS_WATCHER_H
+
+#include <glib-object.h>
+#include <glib.h>
+
+#include <libqrtr-glib.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_QRTR_BUS_WATCHER (mm_qrtr_bus_watcher_get_type ())
+#define MM_QRTR_BUS_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcher))
+#define MM_QRTR_BUS_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcherClass))
+#define MM_IS_QRTR_BUS_WATCHER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_QRTR_BUS_WATCHER))
+#define MM_IS_QRTR_BUS_WATCHER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_QRTR_BUS_WATCHER))
+#define MM_QRTR_BUS_WATCHER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_QRTR_BUS_WATCHER, MMQrtrBusWatcherClass))
+
+#define MM_QRTR_BUS_WATCHER_DEVICE_ADDED "qrtr-device-added"
+#define MM_QRTR_BUS_WATCHER_DEVICE_REMOVED "qrtr-device-removed"
+
+typedef struct _MMQrtrBusWatcher MMQrtrBusWatcher;
+typedef struct _MMQrtrBusWatcherClass MMQrtrBusWatcherClass;
+typedef struct _MMQrtrBusWatcherPrivate MMQrtrBusWatcherPrivate;
+
+struct _MMQrtrBusWatcher {
+ GObject parent;
+ MMQrtrBusWatcherPrivate *priv;
+};
+
+struct _MMQrtrBusWatcherClass {
+ GObjectClass parent;
+
+ void (* qrtr_device_added) (MMQrtrBusWatcher *bus_watcher);
+ void (* qrtr_device_removed) (MMQrtrBusWatcher *bus_watcher);
+};
+
+GType mm_qrtr_bus_watcher_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMQrtrBusWatcher, g_object_unref)
+
+MMQrtrBusWatcher *mm_qrtr_bus_watcher_new (void);
+
+void mm_qrtr_bus_watcher_start (MMQrtrBusWatcher *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_qrtr_bus_watcher_start_finish (MMQrtrBusWatcher *self,
+ GAsyncResult *res,
+ GError **error);
+
+QrtrNode *mm_qrtr_bus_watcher_peek_node (MMQrtrBusWatcher *self,
+ guint32 node_id);
+
+G_END_DECLS
+
+#endif /* MM_QRTR_BUS_WATCHER_H */
diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c
index b7bb3087..0b60d607 100644
--- a/src/mm-serial-parsers.c
+++ b/src/mm-serial-parsers.c
@@ -19,7 +19,7 @@
#include "mm-error-helpers.h"
#include "mm-serial-parsers.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
/* Clean up the response by removing control characters like <CR><LF> etc */
static void
@@ -108,16 +108,16 @@ mm_serial_parser_v1_new (void)
parser = g_slice_new (MMSerialParserV1);
- parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+$", flags, 0, NULL);
+ parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+", flags, 0, NULL);
parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL);
parser->regex_sms = g_regex_new ("\\r\\n>\\s*$", flags, 0, NULL);
- parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR:\\s*(\\d+)\\r\\n$", flags, 0, NULL);
- parser->regex_cms_error = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*(\\d+)\\r\\n$", flags, 0, NULL);
- parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR:\\s*([^\\n\\r]+)\\r\\n$", flags, 0, NULL);
- parser->regex_cms_error_str = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*([^\\n\\r]+)\\r\\n$", flags, 0, NULL);
- parser->regex_ezx_error = g_regex_new ("\\r\\n\\MODEM ERROR:\\s*(\\d+)\\r\\n$", flags, 0, NULL);
- parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL);
- parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL);
+ parser->regex_cme_error = g_regex_new ("\\r\\n\\+CME ERROR:\\s*(\\d+)\\r\\n", flags, 0, NULL);
+ parser->regex_cms_error = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*(\\d+)\\r\\n", flags, 0, NULL);
+ parser->regex_cme_error_str = g_regex_new ("\\r\\n\\+CME ERROR:\\s*([^\\n\\r]+)\\r\\n", flags, 0, NULL);
+ parser->regex_cms_error_str = g_regex_new ("\\r\\n\\+CMS ERROR:\\s*([^\\n\\r]+)\\r\\n", flags, 0, NULL);
+ parser->regex_ezx_error = g_regex_new ("\\r\\n\\MODEM ERROR:\\s*(\\d+)\\r\\n", flags, 0, NULL);
+ parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n", flags, 0, NULL);
+ parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n", flags, 0, NULL);
/* Samsung Z810 may reply "NA" to report a not-available error */
parser->regex_na = g_regex_new ("\\r\\nNA\\r\\n", flags, 0, NULL);
@@ -161,9 +161,10 @@ mm_serial_parser_v1_add_filter (gpointer data,
}
gboolean
-mm_serial_parser_v1_parse (gpointer data,
- GString *response,
- GError **error)
+mm_serial_parser_v1_parse (gpointer data,
+ GString *response,
+ gpointer log_object,
+ GError **error)
{
MMSerialParserV1 *parser = (MMSerialParserV1 *) data;
GMatchInfo *match_info;
@@ -188,7 +189,7 @@ mm_serial_parser_v1_parse (gpointer data,
response,
&local_error)) {
g_assert (local_error != NULL);
- mm_dbg ("Got response filtered in serial port: %s", local_error->message);
+ mm_obj_dbg (log_object, "response filtered in serial port: %s", local_error->message);
g_propagate_error (error, local_error);
response_clean (response);
return TRUE;
@@ -238,7 +239,7 @@ mm_serial_parser_v1_parse (gpointer data,
if (found) {
str = g_match_info_fetch (match_info, 1);
g_assert (str);
- local_error = mm_mobile_equipment_error_for_code (atoi (str));
+ local_error = mm_mobile_equipment_error_for_code (atoi (str), log_object);
goto done;
}
g_match_info_free (match_info);
@@ -251,7 +252,7 @@ mm_serial_parser_v1_parse (gpointer data,
if (found) {
str = g_match_info_fetch (match_info, 1);
g_assert (str);
- local_error = mm_mobile_equipment_error_for_code (atoi (str));
+ local_error = mm_mobile_equipment_error_for_code (atoi (str), log_object);
goto done;
}
g_match_info_free (match_info);
@@ -263,7 +264,7 @@ mm_serial_parser_v1_parse (gpointer data,
if (found) {
str = g_match_info_fetch (match_info, 1);
g_assert (str);
- local_error = mm_message_error_for_code (atoi (str));
+ local_error = mm_message_error_for_code (atoi (str), log_object);
goto done;
}
g_match_info_free (match_info);
@@ -275,7 +276,7 @@ mm_serial_parser_v1_parse (gpointer data,
if (found) {
str = g_match_info_fetch (match_info, 1);
g_assert (str);
- local_error = mm_mobile_equipment_error_for_string (str);
+ local_error = mm_mobile_equipment_error_for_string (str, log_object);
goto done;
}
g_match_info_free (match_info);
@@ -287,7 +288,7 @@ mm_serial_parser_v1_parse (gpointer data,
if (found) {
str = g_match_info_fetch (match_info, 1);
g_assert (str);
- local_error = mm_message_error_for_string (str);
+ local_error = mm_message_error_for_string (str, log_object);
goto done;
}
g_match_info_free (match_info);
@@ -299,7 +300,7 @@ mm_serial_parser_v1_parse (gpointer data,
if (found) {
str = g_match_info_fetch (match_info, 1);
g_assert (str);
- local_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN);
+ local_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, log_object);
goto done;
}
g_match_info_free (match_info);
@@ -309,7 +310,7 @@ mm_serial_parser_v1_parse (gpointer data,
response->str, response->len,
0, 0, &match_info, NULL);
if (found) {
- local_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN);
+ local_error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN, log_object);
goto done;
}
g_match_info_free (match_info);
@@ -337,7 +338,7 @@ mm_serial_parser_v1_parse (gpointer data,
code = MM_CONNECTION_ERROR_NO_CARRIER;
}
- local_error = mm_connection_error_for_code (code);
+ local_error = mm_connection_error_for_code (code, log_object);
goto done;
}
g_match_info_free (match_info);
@@ -361,7 +362,7 @@ done:
response_clean (response);
if (local_error) {
- mm_dbg ("Got failure code %d: %s", local_error->code, local_error->message);
+ mm_obj_dbg (log_object, "operation failure: %d (%s)", local_error->code, local_error->message);
g_propagate_error (error, local_error);
}
diff --git a/src/mm-serial-parsers.h b/src/mm-serial-parsers.h
index 641c5e0f..523597b9 100644
--- a/src/mm-serial-parsers.h
+++ b/src/mm-serial-parsers.h
@@ -24,6 +24,7 @@ void mm_serial_parser_v1_set_custom_regex (gpointer data,
GRegex *error);
gboolean mm_serial_parser_v1_parse (gpointer parser,
GString *response,
+ gpointer log_object,
GError **error);
void mm_serial_parser_v1_destroy (gpointer parser);
gboolean mm_serial_parser_v1_is_known_error (const GError *error);
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
new file mode 100644
index 00000000..cb932aee
--- /dev/null
+++ b/src/mm-shared-qmi.c
@@ -0,0 +1,6936 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include <libqmi-glib.h>
+
+#include "mm-log-object.h"
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-location.h"
+#include "mm-sim-qmi.h"
+#include "mm-shared-qmi.h"
+#include "mm-modem-helpers-qmi.h"
+
+/* Default session id to use in LOC operations */
+#define DEFAULT_LOC_SESSION_ID 0x10
+
+/* Default description for the default configuration of the firmware */
+#define DEFAULT_CONFIG_DESCRIPTION "default"
+
+/*****************************************************************************/
+/* Private data context */
+
+#define PRIVATE_TAG "shared-qmi-private-tag"
+static GQuark private_quark;
+
+typedef enum {
+ FEATURE_UNKNOWN,
+ FEATURE_UNSUPPORTED,
+ FEATURE_SUPPORTED,
+} Feature;
+
+typedef struct {
+ GArray *id;
+ QmiPdcConfigurationType config_type;
+ guint32 token;
+ guint32 version;
+ gchar *description;
+ guint32 total_size;
+} ConfigInfo;
+
+static void
+config_info_clear (ConfigInfo *config_info)
+{
+ g_array_unref (config_info->id);
+ g_free (config_info->description);
+}
+
+typedef struct {
+ /* Capabilities & modes helpers */
+ MMModemCapability current_capabilities;
+ GArray *supported_radio_interfaces;
+ Feature feature_nas_tp;
+ Feature feature_nas_ssp;
+ Feature feature_nas_ssp_extended_lte_band_preference;
+ Feature feature_nas_ssp_acquisition_order_preference;
+ GArray *feature_nas_ssp_acquisition_order_preference_array;
+ gboolean disable_4g_only_mode;
+ GArray *supported_bands;
+
+ /* Location helpers */
+ MMIfaceModemLocation *iface_modem_location_parent;
+ MMModemLocationSource enabled_sources;
+ QmiClient *pds_client;
+ gulong pds_location_event_report_indication_id;
+ QmiClient *loc_client;
+ gulong loc_location_nmea_indication_id;
+ gchar **loc_assistance_data_servers;
+ guint32 loc_assistance_data_max_file_size;
+ guint32 loc_assistance_data_max_part_size;
+
+ /* Carrier config helpers */
+ gboolean config_active_default;
+ GArray *config_list;
+ gint config_active_i;
+
+ /* Slot status monitoring */
+ QmiClient *uim_client;
+ gulong uim_slot_status_indication_id;
+ GArray *slots_status;
+ gulong uim_refresh_indication_id;
+ guint uim_refresh_start_timeout_id;
+} Private;
+
+static void
+private_free (Private *priv)
+{
+ if (priv->config_list)
+ g_array_unref (priv->config_list);
+ if (priv->supported_bands)
+ g_array_unref (priv->supported_bands);
+ if (priv->supported_radio_interfaces)
+ g_array_unref (priv->supported_radio_interfaces);
+ if (priv->pds_location_event_report_indication_id)
+ g_signal_handler_disconnect (priv->pds_client, priv->pds_location_event_report_indication_id);
+ if (priv->pds_client)
+ g_object_unref (priv->pds_client);
+ if (priv->loc_location_nmea_indication_id)
+ g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id);
+ if (priv->loc_client)
+ g_object_unref (priv->loc_client);
+ if (priv->uim_slot_status_indication_id)
+ g_signal_handler_disconnect (priv->uim_client, priv->uim_slot_status_indication_id);
+ if (priv->slots_status)
+ g_array_unref (priv->slots_status);
+ if (priv->uim_refresh_indication_id)
+ g_signal_handler_disconnect (priv->uim_client, priv->uim_refresh_indication_id);
+ if (priv->uim_client)
+ g_object_unref (priv->uim_client);
+ if (priv->uim_refresh_start_timeout_id)
+ g_source_remove (priv->uim_refresh_start_timeout_id);
+ if (priv->feature_nas_ssp_acquisition_order_preference_array)
+ g_array_unref (priv->feature_nas_ssp_acquisition_order_preference_array);
+ g_strfreev (priv->loc_assistance_data_servers);
+ g_slice_free (Private, priv);
+}
+
+static Private *
+get_private (MMSharedQmi *self)
+{
+ Private *priv;
+
+ if (G_UNLIKELY (!private_quark))
+ private_quark = g_quark_from_static_string (PRIVATE_TAG);
+
+ priv = g_object_get_qdata (G_OBJECT (self), private_quark);
+ if (!priv) {
+ priv = g_slice_new0 (Private);
+
+ priv->feature_nas_tp = FEATURE_UNKNOWN;
+ priv->feature_nas_ssp = FEATURE_UNKNOWN;
+ priv->feature_nas_ssp_extended_lte_band_preference = FEATURE_UNKNOWN;
+ priv->feature_nas_ssp_acquisition_order_preference = FEATURE_UNKNOWN;
+ priv->config_active_i = -1;
+
+ /* Setup parent class' MMIfaceModemLocation */
+ g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface);
+ priv->iface_modem_location_parent = MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface (self);
+
+ g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
+ }
+
+ return priv;
+}
+
+/*****************************************************************************/
+/* Register in network (3GPP interface) */
+
+/* wait this amount of time at most if we don't get the serving system
+ * indication earlier */
+#define REGISTER_IN_NETWORK_TIMEOUT_SECS 25
+
+typedef struct {
+ guint timeout_id;
+ gulong serving_system_indication_id;
+ GCancellable *cancellable;
+ gulong cancellable_id;
+ QmiClientNas *client;
+} RegisterInNetworkContext;
+
+static void
+register_in_network_context_free (RegisterInNetworkContext *ctx)
+{
+ g_assert (!ctx->cancellable_id);
+ g_assert (!ctx->timeout_id);
+ if (ctx->client) {
+ g_assert (!ctx->serving_system_indication_id);
+ g_object_unref (ctx->client);
+ }
+ g_clear_object (&ctx->cancellable);
+ g_slice_free (RegisterInNetworkContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+register_in_network_cancelled (GCancellable *cancellable,
+ GTask *task)
+{
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->cancellable);
+ g_assert (ctx->cancellable_id);
+ ctx->cancellable_id = 0;
+
+ g_assert (ctx->timeout_id);
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ g_assert (ctx->client);
+ g_assert (ctx->serving_system_indication_id);
+ g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id);
+ ctx->serving_system_indication_id = 0;
+
+ g_task_return_error_if_cancelled (task);
+ g_object_unref (task);
+}
+
+static gboolean
+register_in_network_timeout (GTask *task)
+{
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ g_assert (ctx->client);
+ g_assert (ctx->serving_system_indication_id);
+ g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id);
+ ctx->serving_system_indication_id = 0;
+
+ g_assert (!ctx->cancellable || ctx->cancellable_id);
+ g_cancellable_disconnect (ctx->cancellable, ctx->cancellable_id);
+ ctx->cancellable_id = 0;
+
+ /* the 3GPP interface will take care of checking if the registration is
+ * the one we asked for */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+register_in_network_ready (GTask *task,
+ QmiIndicationNasServingSystemOutput *output)
+{
+ RegisterInNetworkContext *ctx;
+ QmiNasRegistrationState registration_state;
+
+ /* ignore indication updates reporting "searching" */
+ qmi_indication_nas_serving_system_output_get_serving_system (
+ output,
+ &registration_state,
+ NULL, /* cs_attach_state */
+ NULL, /* ps_attach_state */
+ NULL, /* selected_network */
+ NULL, /* radio_interfaces */
+ NULL);
+ if (registration_state == QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED_SEARCHING)
+ return;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->client);
+ g_assert (ctx->serving_system_indication_id);
+ g_signal_handler_disconnect (ctx->client, ctx->serving_system_indication_id);
+ ctx->serving_system_indication_id = 0;
+
+ g_assert (ctx->timeout_id);
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ g_assert (!ctx->cancellable || ctx->cancellable_id);
+ g_cancellable_disconnect (ctx->cancellable, ctx->cancellable_id);
+ ctx->cancellable_id = 0;
+
+ /* the 3GPP interface will take care of checking if the registration is
+ * the one we asked for */
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+initiate_network_register_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ QmiMessageNasInitiateNetworkRegisterOutput *output;
+ RegisterInNetworkContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_initiate_network_register_finish (client, res, &error);
+ if (!output || !qmi_message_nas_initiate_network_register_output_get_result (output, &error)) {
+ /* No effect would mean we're already in the desired network */
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_task_return_boolean (task, TRUE);
+ g_error_free (error);
+ } else {
+ g_prefix_error (&error, "Couldn't initiate network register: ");
+ g_task_return_error (task, error);
+ }
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* Registration attempt started, now we need to monitor "serving system" indications
+ * to get notified when the registration changed. Note that we won't need to process
+ * the indication, because we already have that logic setup (and it runs before this
+ * new signal handler), we just need to get notified of when it happens. We will also
+ * setup a maximum operation timeuot plus a cancellability point, as this operation
+ * may be explicitly cancelled by the 3GPP interface if a new registration request
+ * arrives while the current one is being processed.
+ *
+ * Task is shared among cancellable, indication and timeout. The first one triggered
+ * will cancel the others.
+ */
+
+ if (ctx->cancellable)
+ ctx->cancellable_id = g_cancellable_connect (ctx->cancellable,
+ G_CALLBACK (register_in_network_cancelled),
+ task,
+ NULL);
+
+ ctx->serving_system_indication_id = g_signal_connect_swapped (client,
+ "serving-system",
+ G_CALLBACK (register_in_network_ready),
+ task);
+
+ ctx->timeout_id = g_timeout_add_seconds (REGISTER_IN_NETWORK_TIMEOUT_SECS,
+ (GSourceFunc) register_in_network_timeout,
+ task);
+
+out:
+
+ if (output)
+ qmi_message_nas_initiate_network_register_output_unref (output);
+}
+
+static void
+register_in_network_inr (GTask *task,
+ QmiClient *client,
+ GCancellable *cancellable,
+ guint16 mcc,
+ guint16 mnc,
+ gboolean mnc_pcs_digit)
+{
+ QmiMessageNasInitiateNetworkRegisterInput *input;
+
+ input = qmi_message_nas_initiate_network_register_input_new ();
+
+ if (mcc) {
+ /* If the user sent a specific network to use, lock it in. */
+ qmi_message_nas_initiate_network_register_input_set_action (
+ input,
+ QMI_NAS_NETWORK_REGISTER_TYPE_MANUAL,
+ NULL);
+ qmi_message_nas_initiate_network_register_input_set_manual_registration_info_3gpp (
+ input,
+ mcc,
+ mnc,
+ QMI_NAS_RADIO_INTERFACE_UNKNOWN, /* don't change radio interface */
+ NULL);
+ if (mnc_pcs_digit && mnc < 100)
+ qmi_message_nas_initiate_network_register_input_set_mnc_pcs_digit_include_status (
+ input,
+ mnc_pcs_digit,
+ NULL
+ );
+ } else {
+ /* Otherwise, automatic registration */
+ qmi_message_nas_initiate_network_register_input_set_action (
+ input,
+ QMI_NAS_NETWORK_REGISTER_TYPE_AUTOMATIC,
+ NULL);
+ }
+
+ qmi_client_nas_initiate_network_register (
+ QMI_CLIENT_NAS (client),
+ input,
+ 120,
+ cancellable,
+ (GAsyncReadyCallback)initiate_network_register_ready,
+ task);
+
+ qmi_message_nas_initiate_network_register_input_unref (input);
+}
+
+static void
+set_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ QmiMessageNasSetSystemSelectionPreferenceOutput *output;
+
+ output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
+ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_prefix_error (&error, "Couldn't set network selection preference: ");
+ g_task_return_error (task, error);
+ goto out;
+ }
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
+
+out:
+ g_object_unref (task);
+
+ if (output)
+ qmi_message_nas_set_system_selection_preference_output_unref (output);
+}
+
+static void
+register_in_network_sssp (GTask *task,
+ QmiClient *client,
+ GCancellable *cancellable,
+ guint16 mcc,
+ guint16 mnc,
+ gboolean mnc_pcs_digit)
+{
+ QmiMessageNasSetSystemSelectionPreferenceInput *input;
+
+ input = qmi_message_nas_set_system_selection_preference_input_new ();
+
+ qmi_message_nas_set_system_selection_preference_input_set_network_selection_preference (
+ input,
+ mcc ? QMI_NAS_NETWORK_SELECTION_PREFERENCE_MANUAL : QMI_NAS_NETWORK_SELECTION_PREFERENCE_AUTOMATIC,
+ mcc,
+ mnc,
+ NULL);
+
+ if (mnc_pcs_digit && mnc < 100)
+ qmi_message_nas_set_system_selection_preference_input_set_mnc_pcs_digit_include_status (
+ input,
+ mnc_pcs_digit,
+ NULL
+ );
+
+ qmi_client_nas_set_system_selection_preference (
+ QMI_CLIENT_NAS (client),
+ input,
+ 120,
+ cancellable,
+ (GAsyncReadyCallback)set_system_selection_preference_ready,
+ task);
+
+ qmi_message_nas_set_system_selection_preference_input_unref (input);
+}
+
+void
+mm_shared_qmi_3gpp_register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ RegisterInNetworkContext *ctx;
+ guint16 mcc = 0;
+ guint16 mnc = 0;
+ gboolean mnc_pcs_digit = FALSE;
+ QmiClient *client = NULL;
+ GError *error = NULL;
+ Private *priv = NULL;
+
+ /* Get NAS client */
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ ctx = g_slice_new0 (RegisterInNetworkContext);
+ ctx->client = QMI_CLIENT_NAS (g_object_ref (client));
+ ctx->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free);
+
+ /* Parse input MCC/MNC */
+ if (operator_id && !mm_3gpp_parse_operator_id (operator_id, &mcc, &mnc, &mnc_pcs_digit, &error)) {
+ g_assert (error != NULL);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ priv = get_private (MM_SHARED_QMI (self));
+ if (priv->feature_nas_ssp == FEATURE_SUPPORTED)
+ register_in_network_sssp (task, client, cancellable, mcc, mnc, mnc_pcs_digit);
+ else
+ register_in_network_inr (task, client, cancellable, mcc, mnc, mnc_pcs_digit);
+}
+
+/*****************************************************************************/
+/* Current capabilities setting (Modem interface) */
+
+typedef enum {
+ SET_CURRENT_CAPABILITIES_STEP_FIRST,
+ SET_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE,
+ SET_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE,
+ SET_CURRENT_CAPABILITIES_STEP_RESET,
+ SET_CURRENT_CAPABILITIES_STEP_LAST,
+} SetCurrentCapabilitiesStep;
+
+typedef struct {
+ QmiClientNas *client;
+ MMModemCapability capabilities;
+ gboolean capabilities_updated;
+ SetCurrentCapabilitiesStep step;
+} SetCurrentCapabilitiesContext;
+
+static void
+set_current_capabilities_context_free (SetCurrentCapabilitiesContext *ctx)
+{
+ g_object_unref (ctx->client);
+ g_slice_free (SetCurrentCapabilitiesContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_set_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void set_current_capabilities_step (GTask *task);
+
+static void
+set_current_capabilities_reset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_shared_qmi_reset_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_current_capabilities_step (task);
+}
+
+static void
+set_current_capabilities_set_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_set_technology_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_technology_preference_output_get_result (output, &error)) {
+ /* A no-effect error here is not a real error */
+ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+ /* no effect, just end operation without reset */
+ g_clear_error (&error);
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_LAST;
+ set_current_capabilities_step (task);
+ goto out;
+ }
+
+ /* success! */
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_RESET;
+ set_current_capabilities_step (task);
+
+out:
+ if (output)
+ qmi_message_nas_set_technology_preference_output_unref (output);
+}
+
+static void
+set_current_capabilities_technology_preference (GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceInput *input;
+ QmiNasRadioTechnologyPreference pref;
+
+ ctx = g_task_get_task_data (task);
+
+ pref = mm_modem_capability_to_qmi_radio_technology_preference (ctx->capabilities);
+ if (!pref) {
+ gchar *str;
+
+ str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled capabilities setting: '%s'",
+ str);
+ g_object_unref (task);
+ g_free (str);
+ return;
+ }
+
+ input = qmi_message_nas_set_technology_preference_input_new ();
+ qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_technology_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)set_current_capabilities_set_technology_preference_ready,
+ task);
+ qmi_message_nas_set_technology_preference_input_unref (input);
+}
+
+static void
+set_current_capabilities_set_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* success! */
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_RESET;
+ set_current_capabilities_step (task);
+
+out:
+ if (output)
+ qmi_message_nas_set_system_selection_preference_output_unref (output);
+}
+
+static void
+set_current_capabilities_system_selection_preference (GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceInput *input;
+ QmiNasRatModePreference pref;
+
+ ctx = g_task_get_task_data (task);
+
+ pref = mm_modem_capability_to_qmi_rat_mode_preference (ctx->capabilities);
+ if (!pref) {
+ gchar *str;
+
+ str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled capabilities setting: '%s'",
+ str);
+ g_object_unref (task);
+ g_free (str);
+ return;
+ }
+
+ input = qmi_message_nas_set_system_selection_preference_input_new ();
+ qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
+ qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_system_selection_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)set_current_capabilities_set_system_selection_preference_ready,
+ task);
+ qmi_message_nas_set_system_selection_preference_input_unref (input);
+}
+
+static void
+set_current_capabilities_step (GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ SetCurrentCapabilitiesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_CURRENT_CAPABILITIES_STEP_FIRST:
+ /* Error out early if both unsupported */
+ if ((priv->feature_nas_ssp != FEATURE_SUPPORTED) &&
+ (priv->feature_nas_tp != FEATURE_SUPPORTED)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Setting capabilities is not supported by this device");
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case SET_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE:
+ if (priv->feature_nas_ssp == FEATURE_SUPPORTED) {
+ set_current_capabilities_system_selection_preference (task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case SET_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE:
+ if (priv->feature_nas_tp == FEATURE_SUPPORTED) {
+ set_current_capabilities_technology_preference (task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case SET_CURRENT_CAPABILITIES_STEP_RESET:
+ mm_shared_qmi_reset (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)set_current_capabilities_reset_ready,
+ task);
+ return;
+
+ case SET_CURRENT_CAPABILITIES_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+mm_shared_qmi_set_current_capabilities (MMIfaceModem *self,
+ MMModemCapability capabilities,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ SetCurrentCapabilitiesContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->feature_nas_tp != FEATURE_UNKNOWN);
+ g_assert (priv->feature_nas_ssp != FEATURE_UNKNOWN);
+
+ ctx = g_slice_new0 (SetCurrentCapabilitiesContext);
+ ctx->client = QMI_CLIENT_NAS (g_object_ref (client));
+ ctx->capabilities = capabilities;
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_capabilities_context_free);
+
+ set_current_capabilities_step (task);
+}
+
+/*****************************************************************************/
+/* Current capabilities (Modem interface) */
+
+typedef enum {
+ LOAD_CURRENT_CAPABILITIES_STEP_FIRST,
+ LOAD_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE,
+ LOAD_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE,
+ LOAD_CURRENT_CAPABILITIES_STEP_DMS_GET_CAPABILITIES,
+ LOAD_CURRENT_CAPABILITIES_STEP_LAST,
+} LoadCurrentCapabilitiesStep;
+
+typedef struct {
+ QmiClientNas *nas_client;
+ QmiClientDms *dms_client;
+ LoadCurrentCapabilitiesStep step;
+ MMQmiCapabilitiesContext capabilities_context;
+} LoadCurrentCapabilitiesContext;
+
+MMModemCapability
+mm_shared_qmi_load_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_CAPABILITY_NONE;
+ }
+ return (MMModemCapability)value;
+}
+
+static void
+load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx)
+{
+ g_object_unref (ctx->nas_client);
+ g_object_unref (ctx->dms_client);
+ g_slice_free (LoadCurrentCapabilitiesContext, ctx);
+}
+
+static void load_current_capabilities_step (GTask *task);
+
+static void
+load_current_capabilities_get_capabilities_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+ QmiMessageDmsGetCapabilitiesOutput *output = NULL;
+ GError *error = NULL;
+ guint i;
+ GArray *radio_interface_list;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_dms_get_capabilities_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get Capabilities: ");
+ goto out;
+ }
+
+ qmi_message_dms_get_capabilities_output_get_info (
+ output,
+ NULL, /* info_max_tx_channel_rate */
+ NULL, /* info_max_rx_channel_rate */
+ NULL, /* info_data_service_capability */
+ NULL, /* info_sim_capability */
+ &radio_interface_list,
+ NULL);
+
+ /* Cache supported radio interfaces */
+ g_assert (!priv->supported_radio_interfaces);
+ priv->supported_radio_interfaces = g_array_ref (radio_interface_list);
+
+ for (i = 0; i < radio_interface_list->len; i++)
+ ctx->capabilities_context.dms_capabilities |=
+ mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list, QmiDmsRadioInterface, i), self);
+
+out:
+ if (output)
+ qmi_message_dms_get_capabilities_output_unref (output);
+
+ /* Failure in DMS Get Capabilities is fatal */
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ load_current_capabilities_step (task);
+}
+
+static void
+load_current_capabilities_get_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+ QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_get_technology_preference_finish (client, res, &error);
+ if (!output) {
+ mm_obj_dbg (self, "QMI operation failed: %s", error->message);
+ g_error_free (error);
+ priv->feature_nas_tp = FEATURE_UNSUPPORTED;
+ } else if (!qmi_message_nas_get_technology_preference_output_get_result (output, &error)) {
+ mm_obj_dbg (self, "couldn't get technology preference: %s", error->message);
+ g_error_free (error);
+ priv->feature_nas_tp = FEATURE_UNSUPPORTED;
+ } else {
+ qmi_message_nas_get_technology_preference_output_get_active (
+ output,
+ &ctx->capabilities_context.nas_tp_mask,
+ NULL, /* duration */
+ NULL);
+ priv->feature_nas_tp = FEATURE_SUPPORTED;
+ }
+
+ if (output)
+ qmi_message_nas_get_technology_preference_output_unref (output);
+
+ ctx->step++;
+ load_current_capabilities_step (task);
+}
+
+static void
+load_current_capabilities_get_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+ QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ priv->feature_nas_ssp = FEATURE_UNSUPPORTED;
+ priv->feature_nas_ssp_extended_lte_band_preference = FEATURE_UNSUPPORTED;
+ priv->feature_nas_ssp_acquisition_order_preference = FEATURE_UNSUPPORTED;
+
+ output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
+ if (!output) {
+ mm_obj_dbg (self, "QMI operation failed: %s", error->message);
+ g_error_free (error);
+ } else if (!qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
+ mm_obj_dbg (self, "couldn't get system selection preference: %s", error->message);
+ g_error_free (error);
+ } else {
+ GArray *acquisition_order_preference_array = NULL;
+
+ /* SSP is supported, perform feature checks */
+ priv->feature_nas_ssp = FEATURE_SUPPORTED;
+ if (qmi_message_nas_get_system_selection_preference_output_get_extended_lte_band_preference (output, NULL, NULL, NULL, NULL, NULL))
+ priv->feature_nas_ssp_extended_lte_band_preference = FEATURE_SUPPORTED;
+ if (qmi_message_nas_get_system_selection_preference_output_get_acquisition_order_preference (output, &acquisition_order_preference_array, NULL) &&
+ acquisition_order_preference_array &&
+ acquisition_order_preference_array->len) {
+ priv->feature_nas_ssp_acquisition_order_preference = FEATURE_SUPPORTED;
+ priv->feature_nas_ssp_acquisition_order_preference_array = g_array_ref (acquisition_order_preference_array);
+ }
+
+ qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
+ output,
+ &ctx->capabilities_context.nas_ssp_mode_preference_mask,
+ NULL);
+ }
+
+ if (output)
+ qmi_message_nas_get_system_selection_preference_output_unref (output);
+
+ ctx->step++;
+ load_current_capabilities_step (task);
+}
+
+static void
+load_current_capabilities_step (GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case LOAD_CURRENT_CAPABILITIES_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE:
+ qmi_client_nas_get_system_selection_preference (
+ ctx->nas_client, NULL, 5, NULL,
+ (GAsyncReadyCallback)load_current_capabilities_get_system_selection_preference_ready,
+ task);
+ return;
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE:
+ qmi_client_nas_get_technology_preference (
+ ctx->nas_client, NULL, 5, NULL,
+ (GAsyncReadyCallback)load_current_capabilities_get_technology_preference_ready,
+ task);
+ return;
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_DMS_GET_CAPABILITIES:
+ qmi_client_dms_get_capabilities (
+ ctx->dms_client, NULL, 5, NULL,
+ (GAsyncReadyCallback)load_current_capabilities_get_capabilities_ready,
+ task);
+ return;
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_LAST:
+ g_assert (priv->feature_nas_tp != FEATURE_UNKNOWN);
+ g_assert (priv->feature_nas_ssp != FEATURE_UNKNOWN);
+ priv->current_capabilities = mm_modem_capability_from_qmi_capabilities_context (&ctx->capabilities_context, self);
+ g_task_return_int (task, priv->current_capabilities);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+mm_shared_qmi_load_current_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadCurrentCapabilitiesContext *ctx;
+ GTask *task;
+ QmiClient *nas_client = NULL;
+ QmiClient *dms_client = NULL;
+ Private *priv;
+
+ /*
+ * We assume that DMS Get Capabilities reports always the same result,
+ * that will include all capabilities supported by the device regardless
+ * of which ones are configured at the moment. E.g. for the Load Supported
+ * Capabilities we base the logic exclusively on this method's output.
+ *
+ * We then consider 3 different cases:
+ * a) If the device supports NAS System Selection Preference, we use the
+ * "mode preference" TLV to select currently enabled capabilities.
+ * b) If the device supports NAS Technology Preference (older devices),
+ * we use this method to select currently enabled capabilities.
+ * c) If none of those messages is supported we don't allow swiching
+ * capabilities.
+ */
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &nas_client,
+ callback, user_data))
+ return;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &dms_client,
+ callback, user_data))
+ return;
+
+ /* Current capabilities is the first thing run, and will only be run once per modem,
+ * so we should here check support for the optional features. */
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->feature_nas_tp == FEATURE_UNKNOWN);
+ g_assert (priv->feature_nas_ssp == FEATURE_UNKNOWN);
+
+ ctx = g_slice_new0 (LoadCurrentCapabilitiesContext);
+ ctx->nas_client = QMI_CLIENT_NAS (g_object_ref (nas_client));
+ ctx->dms_client = QMI_CLIENT_DMS (g_object_ref (dms_client));
+ ctx->step = LOAD_CURRENT_CAPABILITIES_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_current_capabilities_context_free);
+
+ load_current_capabilities_step (task);
+}
+
+/*****************************************************************************/
+/* Supported capabilities (Modem interface) */
+
+GArray *
+mm_shared_qmi_load_supported_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+mm_shared_qmi_load_supported_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+ MMModemCapability mask;
+ MMModemCapability single;
+ GArray *supported_combinations;
+ guint i;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* List of radio interfaces preloaded in current capabilities */
+ priv = get_private (MM_SHARED_QMI (self));
+ if (!priv->supported_radio_interfaces) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "cannot load current capabilities without radio interface information");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Build mask with all supported capabilities */
+ mask = MM_MODEM_CAPABILITY_NONE;
+ for (i = 0; i < priv->supported_radio_interfaces->len; i++)
+ mask |= mm_modem_capability_from_qmi_radio_interface (g_array_index (priv->supported_radio_interfaces, QmiDmsRadioInterface, i), self);
+
+ supported_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 3);
+
+ /* Add all possible supported capability combinations.
+ * In order to avoid unnecessary modem reboots, we will only implement capabilities
+ * switching only when switching GSM/UMTS+CDMA/EVDO multimode devices, and only if
+ * we have support for the commands doing it.
+ */
+ if (priv->feature_nas_tp == FEATURE_SUPPORTED || priv->feature_nas_ssp == FEATURE_SUPPORTED) {
+ if (mask == (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO)) {
+ /* Multimode GSM/UMTS+CDMA/EVDO device switched to GSM/UMTS only */
+ single = MM_MODEM_CAPABILITY_GSM_UMTS;
+ g_array_append_val (supported_combinations, single);
+ /* Multimode GSM/UMTS+CDMA/EVDO device switched to CDMA/EVDO only */
+ single = MM_MODEM_CAPABILITY_CDMA_EVDO;
+ g_array_append_val (supported_combinations, single);
+ } else if (mask == (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE)) {
+ /* Multimode GSM/UMTS+CDMA/EVDO+LTE device switched to GSM/UMTS+LTE only */
+ single = MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE;
+ g_array_append_val (supported_combinations, single);
+ /* Multimode GSM/UMTS+CDMA/EVDO+LTE device switched to CDMA/EVDO+LTE only */
+ single = MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE;
+ g_array_append_val (supported_combinations, single);
+ /*
+ * Multimode GSM/UMTS+CDMA/EVDO+LTE device switched to LTE only.
+ *
+ * This case is required because we use the same methods and operations to
+ * switch capabilities and modes. For the LTE capability there is a direct
+ * related 4G mode, and so we cannot select a '4G only' mode in this device
+ * because we wouldn't be able to know the full list of current capabilities
+ * if the device was rebooted, as we would only see LTE capability. So,
+ * handle this special case so that the LTE/4G-only mode can exclusively be
+ * selected as capability switching in this kind of devices.
+ */
+ priv->disable_4g_only_mode = TRUE;
+ single = MM_MODEM_CAPABILITY_LTE;
+ g_array_append_val (supported_combinations, single);
+ }
+ }
+
+ /* Add the full mask itself */
+ single = mask;
+ g_array_append_val (supported_combinations, single);
+
+ g_task_return_pointer (task, supported_combinations, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load model (Modem interface) */
+
+gchar *
+mm_shared_qmi_load_model_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+dms_get_model_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageDmsGetModelOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_dms_get_model_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_dms_get_model_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get Model: ");
+ g_task_return_error (task, error);
+ } else {
+ const gchar *str;
+
+ qmi_message_dms_get_model_output_get_model (output, &str, NULL);
+ g_task_return_pointer (task, g_strdup (str), g_free);
+ }
+
+ if (output)
+ qmi_message_dms_get_model_output_unref (output);
+
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_load_model (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ mm_obj_dbg (self, "loading model...");
+ qmi_client_dms_get_model (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_get_model_ready,
+ g_task_new (self, NULL, callback, user_data));
+}
+
+/*****************************************************************************/
+/* Allowed modes setting (Modem interface) */
+
+typedef struct {
+ QmiClientNas *client;
+ MMModemMode allowed;
+ MMModemMode preferred;
+} SetCurrentModesContext;
+
+static void
+set_current_modes_context_free (SetCurrentModesContext *ctx)
+{
+ g_object_unref (ctx->client);
+ g_slice_free (SetCurrentModesContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_current_modes_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_nas_set_technology_preference_finish (client, res, &error);
+ if (!output ||
+ (!qmi_message_nas_set_technology_preference_output_get_result (output, &error) &&
+ !g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT))) {
+ g_task_return_error (task, error);
+ } else {
+ g_clear_error (&error);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+
+ if (output)
+ qmi_message_nas_set_technology_preference_output_unref (output);
+}
+
+static void
+set_current_modes_technology_preference (GTask *task)
+{
+ MMIfaceModem *self;
+ SetCurrentModesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceInput *input;
+ QmiNasRadioTechnologyPreference pref;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->preferred != MM_MODEM_MODE_NONE) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot set specific preferred mode");
+ g_object_unref (task);
+ return;
+ }
+
+ pref = mm_modem_mode_to_qmi_radio_technology_preference (ctx->allowed, mm_iface_modem_is_cdma (self));
+ if (!pref) {
+ gchar *str;
+
+ str = mm_modem_mode_build_string_from_mask (ctx->allowed);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled allowed mode setting: '%s'",
+ str);
+ g_object_unref (task);
+ g_free (str);
+ return;
+ }
+
+ input = qmi_message_nas_set_technology_preference_input_new ();
+ qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_technology_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)set_current_modes_technology_preference_ready,
+ task);
+ qmi_message_nas_set_technology_preference_input_unref (input);
+}
+
+static void
+set_current_modes_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ if (output)
+ qmi_message_nas_set_system_selection_preference_output_unref (output);
+}
+
+static void
+set_current_modes_system_selection_preference (GTask *task)
+{
+ MMIfaceModem *self;
+ Private *priv;
+ SetCurrentModesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceInput *input;
+ QmiNasRatModePreference pref;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_nas_set_system_selection_preference_input_new ();
+ qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+
+ /* Preferred modes */
+
+ if (ctx->preferred != MM_MODEM_MODE_NONE) {
+ if (priv->feature_nas_ssp_acquisition_order_preference == FEATURE_SUPPORTED) {
+ GArray *array;
+
+ /* Acquisition order array */
+ array = mm_modem_mode_to_qmi_acquisition_order_preference (ctx->allowed,
+ ctx->preferred,
+ priv->feature_nas_ssp_acquisition_order_preference_array);
+ g_assert (array);
+ qmi_message_nas_set_system_selection_preference_input_set_acquisition_order_preference (input, array, NULL);
+ g_array_unref (array);
+ }
+
+ /* Only set GSM/WCDMA acquisition order preference if both 2G and 3G given as allowed */
+ if (mm_iface_modem_is_3gpp (self) && ((ctx->allowed & (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G))) {
+ QmiNasGsmWcdmaAcquisitionOrderPreference order;
+
+ order = mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (ctx->preferred, self);
+ qmi_message_nas_set_system_selection_preference_input_set_gsm_wcdma_acquisition_order_preference (input, order, NULL);
+ }
+ }
+
+ /* Allowed modes */
+ pref = mm_modem_mode_to_qmi_rat_mode_preference (ctx->allowed,
+ mm_iface_modem_is_cdma (self),
+ mm_iface_modem_is_3gpp (self));
+ qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
+
+ qmi_client_nas_set_system_selection_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)set_current_modes_system_selection_preference_ready,
+ task);
+ qmi_message_nas_set_system_selection_preference_input_unref (input);
+}
+
+void
+mm_shared_qmi_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SetCurrentModesContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+ Private *priv;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ ctx = g_slice_new0 (SetCurrentModesContext);
+ ctx->client = QMI_CLIENT_NAS (g_object_ref (client));
+
+ if (allowed == MM_MODEM_MODE_ANY && ctx->preferred == MM_MODEM_MODE_NONE) {
+ ctx->allowed = MM_MODEM_MODE_NONE;
+ if (mm_iface_modem_is_2g (self))
+ ctx->allowed |= MM_MODEM_MODE_2G;
+ if (mm_iface_modem_is_3g (self))
+ ctx->allowed |= MM_MODEM_MODE_3G;
+ if (mm_iface_modem_is_4g (self))
+ ctx->allowed |= MM_MODEM_MODE_4G;
+ ctx->preferred = MM_MODEM_MODE_NONE;
+ } else {
+ ctx->allowed = allowed;
+ ctx->preferred = preferred;
+ }
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free);
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ if (priv->feature_nas_ssp == FEATURE_SUPPORTED) {
+ set_current_modes_system_selection_preference (task);
+ return;
+ }
+
+ if (priv->feature_nas_tp == FEATURE_SUPPORTED) {
+ set_current_modes_technology_preference (task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Setting allowed modes is not supported by this device");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load current modes (Modem interface) */
+
+typedef struct {
+ QmiClientNas *client;
+} LoadCurrentModesContext;
+
+typedef struct {
+ MMModemMode allowed;
+ MMModemMode preferred;
+} LoadCurrentModesResult;
+
+static void
+load_current_modes_context_free (LoadCurrentModesContext *ctx)
+{
+ g_object_unref (ctx->client);
+ g_free (ctx);
+}
+
+gboolean
+mm_shared_qmi_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ LoadCurrentModesResult *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ *allowed = result->allowed;
+ *preferred = result->preferred;
+ g_free (result);
+ return TRUE;
+}
+
+static void
+get_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadCurrentModesResult *result = NULL;
+ QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+ MMModemMode allowed;
+ QmiNasRadioTechnologyPreference preference_mask;
+
+ output = qmi_client_nas_get_technology_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_get_technology_preference_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ qmi_message_nas_get_technology_preference_output_get_active (
+ output,
+ &preference_mask,
+ NULL, /* duration */
+ NULL);
+ allowed = mm_modem_mode_from_qmi_radio_technology_preference (preference_mask);
+ if (allowed == MM_MODEM_MODE_NONE) {
+ gchar *str;
+
+ str = qmi_nas_radio_technology_preference_build_string_from_mask (preference_mask);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported modes reported: '%s'", str);
+ g_free (str);
+ goto out;
+ }
+
+ /* We got a valid value from here */
+ result = g_new (LoadCurrentModesResult, 1);
+ result->allowed = allowed;
+ result->preferred = MM_MODEM_MODE_NONE;
+ g_task_return_pointer (task, result, g_free);
+
+out:
+ if (output)
+ qmi_message_nas_get_technology_preference_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+load_current_modes_technology_preference (GTask *task)
+{
+ LoadCurrentModesContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ qmi_client_nas_get_technology_preference (
+ ctx->client,
+ NULL, /* no input */
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_technology_preference_ready,
+ task);
+}
+
+static void
+load_current_modes_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentModesResult *result = NULL;
+ QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+ QmiNasRatModePreference mode_preference_mask = 0;
+ MMModemMode allowed;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ if (!qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
+ output,
+ &mode_preference_mask,
+ NULL)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Mode preference not reported in system selection preference");
+ goto out;
+ }
+
+ allowed = mm_modem_mode_from_qmi_rat_mode_preference (mode_preference_mask);
+ if (allowed == MM_MODEM_MODE_NONE) {
+ gchar *str;
+
+ str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference_mask);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported modes reported: '%s'", str);
+ g_free (str);
+ goto out;
+ }
+
+ /* We got a valid value from here */
+ result = g_new (LoadCurrentModesResult, 1);
+ result->allowed = allowed;
+ result->preferred = MM_MODEM_MODE_NONE;
+
+ /* If acquisition order preference is available, always use that first */
+ if (priv->feature_nas_ssp_acquisition_order_preference == FEATURE_SUPPORTED) {
+ GArray *array;
+
+ if (qmi_message_nas_get_system_selection_preference_output_get_acquisition_order_preference (output, &array, NULL) &&
+ array->len > 0) {
+ guint i;
+
+ /* The array of preference contains the preference of the full list of supported
+ * access technologies, regardless of whether they're enabled or not. So, look for
+ * the first one that is flagged as enabled, not just the first one in the array.
+ */
+ for (i = 0; i < array->len; i++) {
+ MMModemMode mode;
+
+ mode = mm_modem_mode_from_qmi_nas_radio_interface (g_array_index (array, QmiNasRadioInterface, i));
+ if (allowed == mode)
+ break;
+ if (allowed & mode) {
+ result->preferred = mode;
+ break;
+ }
+ }
+ }
+ }
+ /* For 2G+3G only rely on the GSM/WCDMA acquisition order preference TLV */
+ else if (mode_preference_mask == (QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS)) {
+ QmiNasGsmWcdmaAcquisitionOrderPreference gsm_or_wcdma;
+
+ if (qmi_message_nas_get_system_selection_preference_output_get_gsm_wcdma_acquisition_order_preference (
+ output,
+ &gsm_or_wcdma,
+ NULL))
+ result->preferred = mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (gsm_or_wcdma, self);
+ }
+
+ g_task_return_pointer (task, result, g_free);
+
+out:
+ if (output)
+ qmi_message_nas_get_system_selection_preference_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+load_current_modes_system_selection_preference (GTask *task)
+{
+ LoadCurrentModesContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ qmi_client_nas_get_system_selection_preference (
+ ctx->client,
+ NULL, /* no input */
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)load_current_modes_system_selection_preference_ready,
+ task);
+}
+
+void
+mm_shared_qmi_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ LoadCurrentModesContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ ctx = g_new0 (LoadCurrentModesContext, 1);
+ ctx->client = QMI_CLIENT_NAS (g_object_ref (client));
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_current_modes_context_free);
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ if (priv->feature_nas_ssp != FEATURE_UNSUPPORTED) {
+ load_current_modes_system_selection_preference (task);
+ return;
+ }
+
+ if (priv->feature_nas_tp != FEATURE_UNSUPPORTED) {
+ load_current_modes_technology_preference (task);
+ return;
+ }
+
+ /* Default to supported */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Loading current modes is not supported by this device");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Supported modes (Modem interface) */
+
+GArray *
+mm_shared_qmi_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+mm_shared_qmi_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GArray *combinations;
+ MMModemModeCombination mode;
+ Private *priv;
+ MMModemMode mask_all;
+ guint i;
+ GArray *all;
+ GArray *filtered;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->supported_radio_interfaces);
+
+ /* Build all, based on the supported radio interfaces */
+ mask_all = MM_MODEM_MODE_NONE;
+ for (i = 0; i < priv->supported_radio_interfaces->len; i++)
+ mask_all |= mm_modem_mode_from_qmi_radio_interface (g_array_index (priv->supported_radio_interfaces, QmiDmsRadioInterface, i), self);
+ mode.allowed = mask_all;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ g_array_append_val (all, mode);
+
+ /* If SSP and TP are not supported, ignore supported mode management */
+ if (priv->feature_nas_ssp == FEATURE_UNSUPPORTED && priv->feature_nas_tp == FEATURE_UNSUPPORTED) {
+ g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ combinations = g_array_new (FALSE, FALSE, sizeof (MMModemModeCombination));
+
+#define ADD_MODE_PREFERENCE(MODE1, MODE2, MODE3, MODE4) do { \
+ mode.allowed = MODE1; \
+ if (MODE2 != MM_MODEM_MODE_NONE) { \
+ mode.allowed |= MODE2; \
+ if (MODE3 != MM_MODEM_MODE_NONE) { \
+ mode.allowed |= MODE3; \
+ if (MODE4 != MM_MODEM_MODE_NONE) \
+ mode.allowed |= MODE4; \
+ } \
+ if (priv->feature_nas_ssp != FEATURE_UNSUPPORTED) { \
+ if (MODE3 != MM_MODEM_MODE_NONE) { \
+ if (MODE4 != MM_MODEM_MODE_NONE) { \
+ mode.preferred = MODE4; \
+ g_array_append_val (combinations, mode); \
+ } \
+ mode.preferred = MODE3; \
+ g_array_append_val (combinations, mode); \
+ } \
+ mode.preferred = MODE2; \
+ g_array_append_val (combinations, mode); \
+ mode.preferred = MODE1; \
+ g_array_append_val (combinations, mode); \
+ } else { \
+ mode.preferred = MM_MODEM_MODE_NONE; \
+ g_array_append_val (combinations, mode); \
+ } \
+ } else { \
+ mode.allowed = MODE1; \
+ mode.preferred = MM_MODEM_MODE_NONE; \
+ g_array_append_val (combinations, mode); \
+ } \
+ } while (0)
+
+ /* 2G-only, 3G-only */
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+
+ /* 4G-only mode is not possible in multimode GSM/UMTS+CDMA/EVDO+LTE
+ * devices. This configuration may be selected as "LTE only" capability
+ * instead. */
+ if (!priv->disable_4g_only_mode)
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+
+ /* 2G, 3G, 4G combinations */
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE);
+
+ /* 5G related mode combinations are only supported when NAS SSP is supported,
+ * as there is no 5G support in NAS TP. */
+ if (priv->feature_nas_ssp != FEATURE_UNSUPPORTED) {
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_4G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_4G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_5G, MM_MODEM_MODE_NONE);
+ ADD_MODE_PREFERENCE (MM_MODEM_MODE_2G, MM_MODEM_MODE_3G, MM_MODEM_MODE_4G, MM_MODEM_MODE_5G);
+ }
+
+ /* Filter out unsupported modes */
+ filtered = mm_filter_supported_modes (all, combinations, self);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load supported bands (Modem interface) */
+
+GArray *
+mm_shared_qmi_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+dms_get_band_capabilities_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessageDmsGetBandCapabilitiesOutput *output;
+ GError *error = NULL;
+ GArray *mm_bands = NULL;
+ QmiDmsBandCapability qmi_bands = 0;
+ QmiDmsLteBandCapability qmi_lte_bands = 0;
+ GArray *extended_qmi_lte_bands = NULL;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ output = qmi_client_dms_get_band_capabilities_finish (client, res, &error);
+ if (!output || !qmi_message_dms_get_band_capabilities_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get band capabilities: ");
+ goto out;
+ }
+
+ qmi_message_dms_get_band_capabilities_output_get_band_capability (
+ output,
+ &qmi_bands,
+ NULL);
+ qmi_message_dms_get_band_capabilities_output_get_lte_band_capability (
+ output,
+ &qmi_lte_bands,
+ NULL);
+ qmi_message_dms_get_band_capabilities_output_get_extended_lte_band_capability (
+ output,
+ &extended_qmi_lte_bands,
+ NULL);
+
+ mm_bands = mm_modem_bands_from_qmi_band_capabilities (qmi_bands, qmi_lte_bands, extended_qmi_lte_bands, self);
+ if (mm_bands->len == 0) {
+ g_clear_pointer (&mm_bands, g_array_unref);
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse the list of supported bands");
+ goto out;
+ }
+
+ /* Cache the result */
+ g_clear_pointer (&priv->supported_bands, g_array_unref);
+ priv->supported_bands = g_array_ref (mm_bands);
+
+ out:
+ if (output)
+ qmi_message_dms_get_band_capabilities_output_unref (output);
+
+ if (error)
+ g_task_return_error (task, error);
+ else if (mm_bands)
+ g_task_return_pointer (task, mm_bands, (GDestroyNotify)g_array_unref);
+ else
+ g_assert_not_reached ();
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ qmi_client_dms_get_band_capabilities (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_get_band_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Load current bands (Modem interface) */
+
+GArray *
+mm_shared_qmi_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return (GArray *) g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_bands_get_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+ GArray *mm_bands = NULL;
+ QmiNasBandPreference band_preference_mask = 0;
+ QmiNasLteBandPreference lte_band_preference_mask = 0;
+ guint64 extended_lte_band_preference[4] = { 0 };
+ guint extended_lte_band_preference_size = 0;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get system selection preference: ");
+ goto out;
+ }
+
+ qmi_message_nas_get_system_selection_preference_output_get_band_preference (
+ output,
+ &band_preference_mask,
+ NULL);
+
+ qmi_message_nas_get_system_selection_preference_output_get_lte_band_preference (
+ output,
+ &lte_band_preference_mask,
+ NULL);
+
+ if ((priv->feature_nas_ssp_extended_lte_band_preference == FEATURE_SUPPORTED) &&
+ qmi_message_nas_get_system_selection_preference_output_get_extended_lte_band_preference (
+ output,
+ &extended_lte_band_preference[0],
+ &extended_lte_band_preference[1],
+ &extended_lte_band_preference[2],
+ &extended_lte_band_preference[3],
+ NULL))
+ extended_lte_band_preference_size = G_N_ELEMENTS (extended_lte_band_preference);
+
+ mm_bands = mm_modem_bands_from_qmi_band_preference (band_preference_mask,
+ lte_band_preference_mask,
+ extended_lte_band_preference_size ? extended_lte_band_preference : NULL,
+ extended_lte_band_preference_size,
+ self);
+
+ if (mm_bands->len == 0) {
+ g_clear_pointer (&mm_bands, g_array_unref);
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't parse the list of current bands");
+ }
+
+ out:
+
+ if (output)
+ qmi_message_nas_get_system_selection_preference_output_unref (output);
+
+ if (error)
+ g_task_return_error (task, error);
+ else if (mm_bands)
+ g_task_return_pointer (task, mm_bands, (GDestroyNotify)g_array_unref);
+ else
+ g_assert_not_reached ();
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ qmi_client_nas_get_system_selection_preference (
+ QMI_CLIENT_NAS (client),
+ NULL, /* no input */
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)load_bands_get_system_selection_preference_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set current bands (Modem interface) */
+
+gboolean
+mm_shared_qmi_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+bands_set_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't set system selection preference: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (output)
+ qmi_message_nas_set_system_selection_preference_output_unref (output);
+
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiMessageNasSetSystemSelectionPreferenceInput *input;
+ Private *priv;
+ GTask *task;
+ QmiClient *client = NULL;
+ QmiNasBandPreference qmi_bands = 0;
+ QmiNasLteBandPreference qmi_lte_bands = 0;
+ guint64 extended_qmi_lte_bands[4] = { 0 };
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ /* Handle ANY separately */
+ if (bands_array->len == 1 && g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) {
+ if (!priv->supported_bands) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Cannot handle 'ANY' if supported bands are unknown");
+ g_object_unref (task);
+ return;
+ }
+ bands_array = priv->supported_bands;
+ }
+
+ mm_modem_bands_to_qmi_band_preference (bands_array,
+ &qmi_bands,
+ &qmi_lte_bands,
+ priv->feature_nas_ssp_extended_lte_band_preference == FEATURE_SUPPORTED ? extended_qmi_lte_bands : NULL,
+ G_N_ELEMENTS (extended_qmi_lte_bands),
+ self);
+
+ input = qmi_message_nas_set_system_selection_preference_input_new ();
+ qmi_message_nas_set_system_selection_preference_input_set_band_preference (input, qmi_bands, NULL);
+ if (mm_iface_modem_is_3gpp_lte (self)) {
+ if (priv->feature_nas_ssp_extended_lte_band_preference == FEATURE_SUPPORTED)
+ qmi_message_nas_set_system_selection_preference_input_set_extended_lte_band_preference (
+ input,
+ extended_qmi_lte_bands[0],
+ extended_qmi_lte_bands[1],
+ extended_qmi_lte_bands[2],
+ extended_qmi_lte_bands[3],
+ NULL);
+ else
+ qmi_message_nas_set_system_selection_preference_input_set_lte_band_preference (input, qmi_lte_bands, NULL);
+ }
+ qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_system_selection_preference (
+ QMI_CLIENT_NAS (client),
+ input,
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)bands_set_system_selection_preference_ready,
+ task);
+ qmi_message_nas_set_system_selection_preference_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Reset (Modem interface) */
+
+gboolean
+mm_shared_qmi_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+reset_set_operating_mode_reset_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ QmiMessageDmsSetOperatingModeOutput *output;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
+ if (!output || !qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ } else {
+ mm_obj_info (self, "rebooting now");
+ g_task_return_boolean (task, TRUE);
+ }
+
+ if (output)
+ qmi_message_dms_set_operating_mode_output_unref (output);
+
+ g_object_unref (task);
+}
+
+static void
+reset_set_operating_mode_offline_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageDmsSetOperatingModeInput *input;
+ QmiMessageDmsSetOperatingModeOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_dms_set_operating_mode_finish (client, res, &error);
+ if (!output) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_dms_set_operating_mode_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_dms_set_operating_mode_output_unref (output);
+ return;
+ }
+
+ qmi_message_dms_set_operating_mode_output_unref (output);
+
+ /* Now, go into reset mode. This will fully reboot the modem, and the current
+ * modem object should get disposed. */
+ input = qmi_message_dms_set_operating_mode_input_new ();
+ qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_RESET, NULL);
+ qmi_client_dms_set_operating_mode (client,
+ input,
+ 20,
+ NULL,
+ (GAsyncReadyCallback)reset_set_operating_mode_reset_ready,
+ task);
+ qmi_message_dms_set_operating_mode_input_unref (input);
+}
+
+void
+mm_shared_qmi_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiMessageDmsSetOperatingModeInput *input;
+ GTask *task;
+ QmiClient *client;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Now, go into offline mode */
+ input = qmi_message_dms_set_operating_mode_input_new ();
+ qmi_message_dms_set_operating_mode_input_set_mode (input, QMI_DMS_OPERATING_MODE_OFFLINE, NULL);
+ qmi_client_dms_set_operating_mode (QMI_CLIENT_DMS (client),
+ input,
+ 20,
+ NULL,
+ (GAsyncReadyCallback)reset_set_operating_mode_offline_ready,
+ task);
+ qmi_message_dms_set_operating_mode_input_unref (input);
+}
+
+/*****************************************************************************/
+/* Factory reset (Modem interface) */
+
+gboolean
+mm_shared_qmi_factory_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+dms_restore_factory_defaults_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageDmsRestoreFactoryDefaultsOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_dms_restore_factory_defaults_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_dms_restore_factory_defaults_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't restore factory defaults: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (output)
+ qmi_message_dms_restore_factory_defaults_output_unref (output);
+
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_factory_reset (MMIfaceModem *self,
+ const gchar *code,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiMessageDmsRestoreFactoryDefaultsInput *input;
+ GTask *task;
+ QmiClient *client = NULL;
+ GError *error = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ input = qmi_message_dms_restore_factory_defaults_input_new ();
+ if (!qmi_message_dms_restore_factory_defaults_input_set_service_programming_code (
+ input,
+ code,
+ &error)) {
+ qmi_message_dms_restore_factory_defaults_input_unref (input);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "performing a factory reset...");
+ qmi_client_dms_restore_factory_defaults (QMI_CLIENT_DMS (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)dms_restore_factory_defaults_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup carrier config (Modem interface) */
+
+#define SETUP_CARRIER_CONFIG_STEP_TIMEOUT_SECS 10
+#define GENERIC_CONFIG_FALLBACK "generic"
+
+typedef enum {
+ SETUP_CARRIER_CONFIG_STEP_FIRST,
+ SETUP_CARRIER_CONFIG_STEP_FIND_REQUESTED,
+ SETUP_CARRIER_CONFIG_STEP_CHECK_CHANGE_NEEDED,
+ SETUP_CARRIER_CONFIG_STEP_UPDATE_CURRENT,
+ SETUP_CARRIER_CONFIG_STEP_ACTIVATE_CURRENT,
+ SETUP_CARRIER_CONFIG_STEP_LAST,
+} SetupCarrierConfigStep;
+
+
+typedef struct {
+ SetupCarrierConfigStep step;
+ QmiClientPdc *client;
+ GKeyFile *keyfile;
+ gchar *imsi;
+
+ gint config_requested_i;
+ gchar *config_requested;
+
+ guint token;
+ guint timeout_id;
+ gulong set_selected_config_indication_id;
+ gulong activate_config_indication_id;
+} SetupCarrierConfigContext;
+
+/* Allow to cleanup action setup right away, without being tied
+ * to the lifecycle of the GTask */
+static void
+setup_carrier_config_context_cleanup_action (SetupCarrierConfigContext *ctx)
+{
+ if (ctx->activate_config_indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->activate_config_indication_id);
+ ctx->activate_config_indication_id = 0;
+ }
+ if (ctx->set_selected_config_indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->set_selected_config_indication_id);
+ ctx->set_selected_config_indication_id = 0;
+ }
+ if (ctx->timeout_id) {
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+ }
+}
+
+static void
+setup_carrier_config_context_free (SetupCarrierConfigContext *ctx)
+{
+ setup_carrier_config_context_cleanup_action (ctx);
+
+ g_free (ctx->config_requested);
+ g_free (ctx->imsi);
+ g_key_file_unref (ctx->keyfile);
+ g_clear_object (&ctx->client);
+ g_slice_free (SetupCarrierConfigContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_setup_carrier_config_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void setup_carrier_config_step (GTask *task);
+
+static void
+setup_carrier_config_abort (GTask *task,
+ GError *error)
+{
+ SetupCarrierConfigContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ setup_carrier_config_context_cleanup_action (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static gboolean
+setup_carrier_config_timeout_no_error (GTask *task)
+{
+ SetupCarrierConfigContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ setup_carrier_config_context_cleanup_action (ctx);
+ ctx->step++;
+ setup_carrier_config_step (task);
+
+ return G_SOURCE_REMOVE;
+}
+
+static gboolean
+setup_carrier_config_timeout (GTask *task)
+{
+ SetupCarrierConfigContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Operation timed out"));
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+activate_config_indication (QmiClientPdc *client,
+ QmiIndicationPdcActivateConfigOutput *output,
+ GTask *task)
+{
+ SetupCarrierConfigContext *ctx;
+ GError *error = NULL;
+ guint16 error_code = 0;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_pdc_activate_config_output_get_indication_result (output, &error_code, &error)) {
+ setup_carrier_config_abort (task, error);
+ return;
+ }
+
+ if (error_code != 0) {
+ setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't activate config: %s",
+ qmi_protocol_error_get_string ((QmiProtocolError) error_code)));
+ return;
+ }
+
+ /* Go on */
+ setup_carrier_config_context_cleanup_action (ctx);
+ ctx->step++;
+ setup_carrier_config_step (task);
+}
+
+static void
+activate_config_ready (QmiClientPdc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdcActivateConfigOutput *output;
+ SetupCarrierConfigContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_pdc_activate_config_finish (client, res, &error);
+ if (!output || !qmi_message_pdc_activate_config_output_get_result (output, &error)) {
+ setup_carrier_config_abort (task, error);
+ goto out;
+ }
+
+ /* When we activate the config, if the operation is successful, we'll just
+ * see the modem going away completely. So, do not consider an error the timeout
+ * waiting for the Activate Config indication, as that is actually a good
+ * thing.
+ */
+ ctx->timeout_id = g_timeout_add_seconds (SETUP_CARRIER_CONFIG_STEP_TIMEOUT_SECS,
+ (GSourceFunc) setup_carrier_config_timeout_no_error,
+ task);
+ ctx->activate_config_indication_id = g_signal_connect (ctx->client,
+ "activate-config",
+ G_CALLBACK (activate_config_indication),
+ task);
+out:
+ if (output)
+ qmi_message_pdc_activate_config_output_unref (output);
+}
+
+static void
+set_selected_config_indication (QmiClientPdc *client,
+ QmiIndicationPdcSetSelectedConfigOutput *output,
+ GTask *task)
+{
+ SetupCarrierConfigContext *ctx;
+ GError *error = NULL;
+ guint16 error_code = 0;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_pdc_set_selected_config_output_get_indication_result (output, &error_code, &error)) {
+ setup_carrier_config_abort (task, error);
+ return;
+ }
+
+ if (error_code != 0) {
+ setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't set selected config: %s",
+ qmi_protocol_error_get_string ((QmiProtocolError) error_code)));
+ return;
+ }
+
+ /* Go on */
+ setup_carrier_config_context_cleanup_action (ctx);
+ ctx->step++;
+ setup_carrier_config_step (task);
+}
+
+static void
+set_selected_config_ready (QmiClientPdc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdcSetSelectedConfigOutput *output;
+ SetupCarrierConfigContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_pdc_set_selected_config_finish (client, res, &error);
+ if (!output || !qmi_message_pdc_set_selected_config_output_get_result (output, &error)) {
+ setup_carrier_config_abort (task, error);
+ goto out;
+ }
+
+ ctx->timeout_id = g_timeout_add_seconds (SETUP_CARRIER_CONFIG_STEP_TIMEOUT_SECS,
+ (GSourceFunc) setup_carrier_config_timeout,
+ task);
+ ctx->set_selected_config_indication_id = g_signal_connect (ctx->client,
+ "set-selected-config",
+ G_CALLBACK (set_selected_config_indication),
+ task);
+out:
+ if (output)
+ qmi_message_pdc_set_selected_config_output_unref (output);
+}
+
+static gint
+select_newest_carrier_config (MMSharedQmi *self,
+ gint config_a_i,
+ gint config_b_i)
+{
+ Private *priv;
+ ConfigInfo *config_a;
+ ConfigInfo *config_b;
+
+ priv = get_private (self);
+ config_a = &g_array_index (priv->config_list, ConfigInfo, config_a_i);
+ config_b = &g_array_index (priv->config_list, ConfigInfo, config_b_i);
+
+ g_assert (!g_strcmp0 (config_a->description, config_b->description));
+
+ if (config_a->version > config_b->version)
+ return config_a_i;
+ if (config_b->version > config_a->version)
+ return config_b_i;
+ /* if both are equal, return the first one found always */
+ return config_a_i;
+}
+
+static void
+find_requested_carrier_config (GTask *task)
+{
+ SetupCarrierConfigContext *ctx;
+ MMSharedQmi *self;
+ Private *priv;
+ gchar mccmnc[7];
+ gchar *group;
+ gint config_fallback_i = -1;
+ gchar *config_fallback = NULL;
+
+ ctx = g_task_get_task_data (task);
+ self = MM_SHARED_QMI (g_task_get_source_object (task));
+ priv = get_private (self);
+
+ /* Only one group expected per file, so get the start one */
+ group = g_key_file_get_start_group (ctx->keyfile);
+
+ /* Match generic configuration */
+ config_fallback = g_key_file_get_string (ctx->keyfile, group, GENERIC_CONFIG_FALLBACK, NULL);
+ mm_obj_dbg (self, "fallback carrier configuration %sfound in group '%s'", config_fallback ? "" : "not ", group);
+
+ /* First, try to match 6 MCCMNC digits (3-digit MNCs) */
+ strncpy (mccmnc, ctx->imsi, 6);
+ mccmnc[6] = '\0';
+ ctx->config_requested = g_key_file_get_string (ctx->keyfile, group, mccmnc, NULL);
+ if (!ctx->config_requested) {
+ /* If not found, try to match 5 MCCMNC digits (2-digit MNCs) */
+ mccmnc[5] = '\0';
+ ctx->config_requested = g_key_file_get_string (ctx->keyfile, group, mccmnc, NULL);
+ }
+ mm_obj_dbg (self, "requested carrier configuration %sfound for '%s' in group '%s': %s",
+ ctx->config_requested ? "" : "not ", mccmnc, group, ctx->config_requested ? ctx->config_requested : "n/a");
+
+ if (!ctx->config_requested && !config_fallback) {
+ setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND,
+ "no valid configuration found in group '%s'", group));
+ goto out;
+ }
+
+ /* Now, look for the configurations among the ones available in the device */
+ if (priv->config_list) {
+ guint i;
+
+ for (i = 0; i < priv->config_list->len; i++) {
+ ConfigInfo *config;
+
+ config = &g_array_index (priv->config_list, ConfigInfo, i);
+ if (ctx->config_requested && !g_strcmp0 (ctx->config_requested, config->description)) {
+ mm_obj_dbg (self, "requested carrier configuration '%s' is available (version 0x%08x, size %u bytes)",
+ config->description, config->version, config->total_size);
+ if (ctx->config_requested_i < 0)
+ ctx->config_requested_i = i;
+ else
+ ctx->config_requested_i = select_newest_carrier_config (self, ctx->config_requested_i, i);
+ }
+ if (config_fallback && !g_strcmp0 (config_fallback, config->description)) {
+ mm_obj_dbg (self, "fallback carrier configuration '%s' is available (version 0x%08x, size %u bytes)",
+ config->description, config->version, config->total_size);
+ if (config_fallback_i < 0)
+ config_fallback_i = i;
+ else
+ config_fallback_i = select_newest_carrier_config (self, config_fallback_i, i);
+ }
+ }
+ }
+
+ /* Fail operation if we didn't find the one we want */
+ if ((ctx->config_requested_i < 0) && (config_fallback_i < 0)) {
+ setup_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "carrier configurations (requested '%s', fallback '%s') are not available",
+ ctx->config_requested, config_fallback));
+ goto out;
+ }
+
+ /* If the mapping expects a given config, but the config isn't installed,
+ * we fallback to generic */
+ if (ctx->config_requested_i < 0) {
+ ConfigInfo *config;
+
+ g_assert (config_fallback_i >= 0);
+
+ config = &g_array_index (priv->config_list, ConfigInfo, config_fallback_i);
+ mm_obj_info (self, "using fallback carrier configuration '%s' (version 0x%08x, size %u bytes)",
+ config->description, config->version, config->total_size);
+
+ g_free (ctx->config_requested);
+ ctx->config_requested = config_fallback;
+ ctx->config_requested_i = config_fallback_i;
+ config_fallback = NULL;
+ } else {
+ ConfigInfo *config;
+
+ config = &g_array_index (priv->config_list, ConfigInfo, ctx->config_requested_i);
+ mm_obj_dbg (self, "using requested carrier configuration '%s' (version 0x%08x, size %u bytes)",
+ config->description, config->version, config->total_size);
+ }
+
+ ctx->step++;
+ setup_carrier_config_step (task);
+
+out:
+ g_free (config_fallback);
+ g_free (group);
+}
+
+static void
+setup_carrier_config_step (GTask *task)
+{
+ MMSharedQmi *self;
+ SetupCarrierConfigContext *ctx;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ priv = get_private (self);
+
+ switch (ctx->step) {
+ case SETUP_CARRIER_CONFIG_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case SETUP_CARRIER_CONFIG_STEP_FIND_REQUESTED:
+ find_requested_carrier_config (task);
+ return;
+
+ case SETUP_CARRIER_CONFIG_STEP_CHECK_CHANGE_NEEDED:
+ g_assert (ctx->config_requested_i >= 0);
+ g_assert (priv->config_active_i >= 0 || priv->config_active_default);
+ if (ctx->config_requested_i == priv->config_active_i) {
+ mm_obj_info (self, "carrier config switching not needed: already using '%s'", ctx->config_requested);
+ ctx->step = SETUP_CARRIER_CONFIG_STEP_LAST;
+ setup_carrier_config_step (task);
+ return;
+ }
+
+ ctx->step++;
+ /* fall-through */
+
+ case SETUP_CARRIER_CONFIG_STEP_UPDATE_CURRENT: {
+ QmiMessagePdcSetSelectedConfigInput *input;
+ ConfigInfo *requested_config;
+ ConfigInfo *active_config;
+ QmiConfigTypeAndId type_and_id;
+
+ requested_config = &g_array_index (priv->config_list, ConfigInfo, ctx->config_requested_i);
+ active_config = (priv->config_active_default ? NULL : &g_array_index (priv->config_list, ConfigInfo, priv->config_active_i));
+ mm_obj_warn (self, "carrier config switching needed: '%s' -> '%s'",
+ active_config ? active_config->description : DEFAULT_CONFIG_DESCRIPTION, requested_config->description);
+
+ type_and_id.config_type = requested_config->config_type;;
+ type_and_id.id = requested_config->id;
+
+ input = qmi_message_pdc_set_selected_config_input_new ();
+ qmi_message_pdc_set_selected_config_input_set_type_with_id (input, &type_and_id, NULL);
+ qmi_message_pdc_set_selected_config_input_set_token (input, ctx->token++, NULL);
+ qmi_client_pdc_set_selected_config (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)set_selected_config_ready,
+ task);
+ qmi_message_pdc_set_selected_config_input_unref (input);
+ return;
+ }
+
+ case SETUP_CARRIER_CONFIG_STEP_ACTIVATE_CURRENT: {
+ QmiMessagePdcActivateConfigInput *input;
+ ConfigInfo *requested_config;
+
+ requested_config = &g_array_index (priv->config_list, ConfigInfo, ctx->config_requested_i);
+
+ input = qmi_message_pdc_activate_config_input_new ();
+ qmi_message_pdc_activate_config_input_set_config_type (input, requested_config->config_type, NULL);
+ qmi_message_pdc_activate_config_input_set_token (input, ctx->token++, NULL);
+ qmi_client_pdc_activate_config (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) activate_config_ready,
+ task);
+ qmi_message_pdc_activate_config_input_unref (input);
+ return;
+ }
+
+ case SETUP_CARRIER_CONFIG_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+mm_shared_qmi_setup_carrier_config (MMIfaceModem *self,
+ const gchar *imsi,
+ const gchar *carrier_config_mapping,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SetupCarrierConfigContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+ GError *error = NULL;
+
+ g_assert (imsi);
+ g_assert (carrier_config_mapping);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (SetupCarrierConfigContext);
+ ctx->step = SETUP_CARRIER_CONFIG_STEP_FIRST;
+ ctx->imsi = g_strdup (imsi);
+ ctx->keyfile = g_key_file_new ();
+ ctx->config_requested_i = -1;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)setup_carrier_config_context_free);
+
+ /* Load mapping keyfile */
+ if (!g_key_file_load_from_file (ctx->keyfile,
+ carrier_config_mapping,
+ G_KEY_FILE_NONE,
+ &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Load PDC client */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (!client) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "QMI PDC not supported");
+ g_object_unref (task);
+ return;
+ }
+ ctx->client = QMI_CLIENT_PDC (g_object_ref (client));
+
+ setup_carrier_config_step (task);
+}
+
+/*****************************************************************************/
+/* Load carrier config (Modem interface) */
+
+#define LOAD_CARRIER_CONFIG_STEP_TIMEOUT_SECS 5
+
+typedef enum {
+ LOAD_CARRIER_CONFIG_STEP_FIRST,
+ LOAD_CARRIER_CONFIG_STEP_LIST_CONFIGS,
+ LOAD_CARRIER_CONFIG_STEP_QUERY_CURRENT,
+ LOAD_CARRIER_CONFIG_STEP_LAST,
+} LoadCarrierConfigStep;
+
+typedef struct {
+ LoadCarrierConfigStep step;
+
+ QmiClientPdc *client;
+
+ GArray *config_list;
+ guint configs_loaded;
+ gboolean config_active_default;
+ gint config_active_i;
+
+ guint token;
+ guint timeout_id;
+ gulong list_configs_indication_id;
+ gulong get_selected_config_indication_id;
+ gulong get_config_info_indication_id;
+} LoadCarrierConfigContext;
+
+/* Allow to cleanup action load right away, without being tied
+ * to the lifecycle of the GTask */
+static void
+load_carrier_config_context_cleanup_action (LoadCarrierConfigContext *ctx)
+{
+ if (ctx->get_selected_config_indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->get_selected_config_indication_id);
+ ctx->get_selected_config_indication_id = 0;
+ }
+ if (ctx->get_config_info_indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->get_config_info_indication_id);
+ ctx->get_config_info_indication_id = 0;
+ }
+ if (ctx->list_configs_indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->list_configs_indication_id);
+ ctx->list_configs_indication_id = 0;
+ }
+ if (ctx->timeout_id) {
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+ }
+}
+
+static void
+load_carrier_config_context_free (LoadCarrierConfigContext *ctx)
+{
+ load_carrier_config_context_cleanup_action (ctx);
+
+ if (ctx->config_list)
+ g_array_unref (ctx->config_list);
+ g_clear_object (&ctx->client);
+ g_slice_free (LoadCarrierConfigContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_load_carrier_config_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ gchar **carrier_config_name,
+ gchar **carrier_config_revision,
+ GError **error)
+{
+ Private *priv;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->config_active_i >= 0 || priv->config_active_default);
+
+ if (priv->config_active_i >= 0) {
+ ConfigInfo *config;
+
+ config = &g_array_index (priv->config_list, ConfigInfo, priv->config_active_i);
+ *carrier_config_name = g_strdup (config->description);
+ *carrier_config_revision = g_strdup_printf ("%08X", config->version);
+ } else if (priv->config_active_default) {
+ *carrier_config_name = g_strdup (DEFAULT_CONFIG_DESCRIPTION);
+ *carrier_config_revision = NULL;
+ } else
+ g_assert_not_reached ();
+
+ return TRUE;
+}
+
+static void load_carrier_config_step (GTask *task);
+
+static void
+load_carrier_config_abort (GTask *task,
+ GError *error)
+{
+ LoadCarrierConfigContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ load_carrier_config_context_cleanup_action (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+}
+
+static gboolean
+load_carrier_config_timeout (GTask *task)
+{
+ LoadCarrierConfigContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Operation timed out"));
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+get_selected_config_indication (QmiClientPdc *client,
+ QmiIndicationPdcGetSelectedConfigOutput *output,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ LoadCarrierConfigContext *ctx;
+ GArray *active_id = NULL;
+ GError *error = NULL;
+ guint16 error_code = 0;
+ guint i;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_pdc_get_selected_config_output_get_indication_result (output, &error_code, &error)) {
+ load_carrier_config_abort (task, error);
+ return;
+ }
+
+ if (error_code != 0 &&
+ error_code != QMI_PROTOCOL_ERROR_NOT_PROVISIONED) { /* No configs active */
+ load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't get selected config: %s",
+ qmi_protocol_error_get_string ((QmiProtocolError) error_code)));
+ return;
+ }
+
+ qmi_indication_pdc_get_selected_config_output_get_active_id (output, &active_id, NULL);
+ if (!active_id) {
+ mm_obj_dbg (self, "no carrier config currently selected (default in use)");
+ ctx->config_active_default = TRUE;
+ goto next;
+ }
+
+ g_assert (ctx->config_list);
+ g_assert (ctx->config_list->len);
+
+ for (i = 0; i < ctx->config_list->len; i++) {
+ ConfigInfo *config;
+
+ config = &g_array_index (ctx->config_list, ConfigInfo, i);
+ if ((config->id->len == active_id->len) &&
+ !memcmp (config->id->data, active_id->data, active_id->len)) {
+ ctx->config_active_i = i;
+ break;
+ }
+ }
+
+ if (i == ctx->config_list->len) {
+ load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't find currently selected config"));
+ return;
+ }
+
+ next:
+
+ /* Go on */
+ load_carrier_config_context_cleanup_action (ctx);
+ ctx->step++;
+ load_carrier_config_step (task);
+}
+
+static void
+get_selected_config_ready (QmiClientPdc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdcGetSelectedConfigOutput *output;
+ LoadCarrierConfigContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_pdc_get_selected_config_finish (client, res, &error);
+ if (!output || !qmi_message_pdc_get_selected_config_output_get_result (output, &error)) {
+ load_carrier_config_abort (task, error);
+ goto out;
+ }
+
+ ctx->timeout_id = g_timeout_add_seconds (LOAD_CARRIER_CONFIG_STEP_TIMEOUT_SECS,
+ (GSourceFunc) load_carrier_config_timeout,
+ task);
+ ctx->get_selected_config_indication_id = g_signal_connect (ctx->client,
+ "get-selected-config",
+ G_CALLBACK (get_selected_config_indication),
+ task);
+
+out:
+ if (output)
+ qmi_message_pdc_get_selected_config_output_unref (output);
+}
+
+static void
+get_config_info_indication (QmiClientPdc *client,
+ QmiIndicationPdcGetConfigInfoOutput *output,
+ GTask *task)
+{
+ LoadCarrierConfigContext *ctx;
+ GError *error = NULL;
+ ConfigInfo *current_config = NULL;
+ guint32 token;
+ const gchar *description;
+ guint i;
+ guint16 error_code = 0;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_pdc_get_config_info_output_get_indication_result (output, &error_code, &error)) {
+ load_carrier_config_abort (task, error);
+ return;
+ }
+
+ if (error_code != 0) {
+ load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't get config info: %s",
+ qmi_protocol_error_get_string ((QmiProtocolError) error_code)));
+ return;
+ }
+
+ if (!qmi_indication_pdc_get_config_info_output_get_token (output, &token, &error)) {
+ load_carrier_config_abort (task, error);
+ return;
+ }
+
+ /* Look for the current config in the list, match by token */
+ for (i = 0; i < ctx->config_list->len; i++) {
+ current_config = &g_array_index (ctx->config_list, ConfigInfo, i);
+ if (current_config->token == token)
+ break;
+ }
+
+ /* Ignore if not found in the list */
+ if (i == ctx->config_list->len)
+ return;
+
+ /* Ignore if already set */
+ if (current_config->description)
+ return;
+
+ /* Store total size, version and description of the current config */
+ if (!qmi_indication_pdc_get_config_info_output_get_total_size (output, &current_config->total_size, &error) ||
+ !qmi_indication_pdc_get_config_info_output_get_version (output, &current_config->version, &error) ||
+ !qmi_indication_pdc_get_config_info_output_get_description (output, &description, &error)) {
+ load_carrier_config_abort (task, error);
+ return;
+ }
+
+ current_config->description = g_strdup (description);
+ ctx->configs_loaded++;
+
+ /* If not all loaded, wait for more */
+ if (ctx->configs_loaded < ctx->config_list->len)
+ return;
+
+ /* Go on */
+ load_carrier_config_context_cleanup_action (ctx);
+ ctx->step++;
+ load_carrier_config_step (task);
+}
+
+static void
+list_configs_indication (QmiClientPdc *client,
+ QmiIndicationPdcListConfigsOutput *output,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ LoadCarrierConfigContext *ctx;
+ GError *error = NULL;
+ GArray *configs = NULL;
+ guint i;
+ guint16 error_code = 0;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_pdc_list_configs_output_get_indication_result (output, &error_code, &error)) {
+ load_carrier_config_abort (task, error);
+ return;
+ }
+
+ if (error_code != 0) {
+ load_carrier_config_abort (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't list configs: %s",
+ qmi_protocol_error_get_string ((QmiProtocolError) error_code)));
+ return;
+ }
+
+ if (!qmi_indication_pdc_list_configs_output_get_configs (output, &configs, &error)) {
+ load_carrier_config_abort (task, error);
+ return;
+ }
+
+ /* If no configs are installed, the module is running with the default one */
+ if (!configs || !configs->len) {
+ ctx->config_active_default = TRUE;
+ ctx->step = LOAD_CARRIER_CONFIG_STEP_LAST;
+ load_carrier_config_step (task);
+ return;
+ }
+
+ /* Preallocate config list and request details for each */
+ mm_obj_dbg (self, "found %u carrier configurations...", configs->len);
+ ctx->config_list = g_array_sized_new (FALSE, TRUE, sizeof (ConfigInfo), configs->len);
+ g_array_set_size (ctx->config_list, configs->len);
+ g_array_set_clear_func (ctx->config_list, (GDestroyNotify) config_info_clear);
+
+ ctx->get_config_info_indication_id = g_signal_connect (ctx->client,
+ "get-config-info",
+ G_CALLBACK (get_config_info_indication),
+ task);
+
+ for (i = 0; i < configs->len; i++) {
+ ConfigInfo *current_info;
+ QmiIndicationPdcListConfigsOutputConfigsElement *element;
+ QmiConfigTypeAndId type_with_id;
+ QmiMessagePdcGetConfigInfoInput *input;
+
+ element = &g_array_index (configs, QmiIndicationPdcListConfigsOutputConfigsElement, i);
+
+ current_info = &g_array_index (ctx->config_list, ConfigInfo, i);
+ current_info->token = ctx->token++;
+ current_info->id = g_array_ref (element->id);
+ current_info->config_type = element->config_type;
+
+ input = qmi_message_pdc_get_config_info_input_new ();
+ type_with_id.config_type = element->config_type;
+ type_with_id.id = current_info->id;
+ qmi_message_pdc_get_config_info_input_set_type_with_id (input, &type_with_id, NULL);
+ qmi_message_pdc_get_config_info_input_set_token (input, current_info->token, NULL);
+ qmi_client_pdc_get_config_info (ctx->client, input, 10, NULL, NULL, NULL); /* ignore response! */
+ qmi_message_pdc_get_config_info_input_unref (input);
+ }
+}
+
+static void
+list_configs_ready (QmiClientPdc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdcListConfigsOutput *output;
+ LoadCarrierConfigContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_pdc_list_configs_finish (client, res, &error);
+ if (!output || !qmi_message_pdc_list_configs_output_get_result (output, &error)) {
+ load_carrier_config_abort (task, error);
+ goto out;
+ }
+
+ ctx->timeout_id = g_timeout_add_seconds (LOAD_CARRIER_CONFIG_STEP_TIMEOUT_SECS,
+ (GSourceFunc) load_carrier_config_timeout,
+ task);
+ ctx->list_configs_indication_id = g_signal_connect (ctx->client,
+ "list-configs",
+ G_CALLBACK (list_configs_indication),
+ task);
+out:
+ if (output)
+ qmi_message_pdc_list_configs_output_unref (output);
+}
+
+static void
+load_carrier_config_step (GTask *task)
+{
+ LoadCarrierConfigContext *ctx;
+ Private *priv;
+
+ ctx = g_task_get_task_data (task);
+ priv = get_private (g_task_get_source_object (task));
+
+ switch (ctx->step) {
+ case LOAD_CARRIER_CONFIG_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case LOAD_CARRIER_CONFIG_STEP_LIST_CONFIGS: {
+ QmiMessagePdcListConfigsInput *input;
+
+ input = qmi_message_pdc_list_configs_input_new ();
+ qmi_message_pdc_list_configs_input_set_config_type (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, NULL);
+ qmi_message_pdc_list_configs_input_set_token (input, ctx->token++, NULL);
+ qmi_client_pdc_list_configs (ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)list_configs_ready,
+ task);
+ qmi_message_pdc_list_configs_input_unref (input);
+ return;
+ }
+
+ case LOAD_CARRIER_CONFIG_STEP_QUERY_CURRENT: {
+ QmiMessagePdcGetSelectedConfigInput *input;
+
+ input = qmi_message_pdc_get_selected_config_input_new ();
+ qmi_message_pdc_get_selected_config_input_set_config_type (input, QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, NULL);
+ qmi_message_pdc_get_selected_config_input_set_token (input, ctx->token++, NULL);
+ qmi_client_pdc_get_selected_config (ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)get_selected_config_ready,
+ task);
+ qmi_message_pdc_get_selected_config_input_unref (input);
+ return;
+ }
+
+ case LOAD_CARRIER_CONFIG_STEP_LAST:
+ /* We will now store the loaded information so that we can later on use it
+ * if needed during the automatic carrier config switching operation */
+ g_assert (priv->config_active_i < 0 && !priv->config_active_default);
+ g_assert (ctx->config_active_i >= 0 || ctx->config_active_default);
+ priv->config_list = ctx->config_list ? g_array_ref (ctx->config_list) : NULL;
+ priv->config_active_i = ctx->config_active_i;
+ priv->config_active_default = ctx->config_active_default;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+mm_shared_qmi_load_carrier_config (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadCarrierConfigContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (LoadCarrierConfigContext);
+ ctx->step = LOAD_CARRIER_CONFIG_STEP_FIRST;
+ ctx->config_active_i = -1;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_carrier_config_context_free);
+
+ /* Load PDC client */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (!client) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "QMI PDC not supported");
+ g_object_unref (task);
+ return;
+ }
+ ctx->client = QMI_CLIENT_PDC (g_object_ref (client));
+
+ load_carrier_config_step (task);
+}
+
+/*****************************************************************************/
+/* Load SIM slots (modem interface) */
+
+typedef struct {
+ QmiClientUim *client_uim;
+ GPtrArray *sim_slots;
+ guint active_slot_number;
+ guint active_logical_id;
+} LoadSimSlotsContext;
+
+static void
+load_sim_slots_context_free (LoadSimSlotsContext *ctx)
+{
+ g_clear_pointer (&ctx->sim_slots, g_ptr_array_unref);
+ g_clear_object (&ctx->client_uim);
+ g_slice_free (LoadSimSlotsContext, ctx);
+}
+
+static void
+sim_slot_free (MMBaseSim *sim)
+{
+ if (sim)
+ g_object_unref (sim);
+}
+
+gboolean
+mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GPtrArray **sim_slots,
+ guint *primary_sim_slot,
+ GError **error)
+{
+ LoadSimSlotsContext *ctx;
+
+ if (!g_task_propagate_boolean (G_TASK (res), error))
+ return FALSE;
+
+ ctx = g_task_get_task_data (G_TASK (res));
+
+ if (sim_slots)
+ *sim_slots = g_steal_pointer (&ctx->sim_slots);
+ if (primary_sim_slot)
+ *primary_sim_slot = ctx->active_slot_number;
+ return TRUE;
+}
+
+static void
+uim_get_slot_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL;
+ LoadSimSlotsContext *ctx;
+ MMIfaceModem *self;
+ Private *priv;
+ GError *error = NULL;
+ GArray *physical_slots = NULL;
+ GArray *ext_information = NULL;
+ GArray *slot_eids = NULL;
+ guint i;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ output = qmi_client_uim_get_slot_status_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_uim_get_slot_status_output_get_result (output, &error) ||
+ !qmi_message_uim_get_slot_status_output_get_physical_slot_status (output, &physical_slots, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Store the slot status before loading all sim slots.
+ * We will use this to check if a hotswap requires a reprobe. */
+ if (priv->slots_status)
+ g_array_unref (priv->slots_status);
+ priv->slots_status = g_array_ref (physical_slots);
+
+ /* It's fine if we don't have EID information, but it should be well-formed if present. If it's malformed,
+ * there is probably a modem firmware bug. */
+ if (qmi_message_uim_get_slot_status_output_get_physical_slot_information (output, &ext_information, NULL) &&
+ qmi_message_uim_get_slot_status_output_get_slot_eid_information (output, &slot_eids, NULL) &&
+ (ext_information->len != physical_slots->len || slot_eids->len != physical_slots->len)) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "UIM Get Slot Status returned malformed response");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->sim_slots = g_ptr_array_new_full (physical_slots->len, (GDestroyNotify) sim_slot_free);
+
+ for (i = 0; i < physical_slots->len; i++) {
+ QmiPhysicalSlotStatusSlot *slot_status;
+ QmiPhysicalSlotInformationSlot *slot_info;
+ MMBaseSim *sim;
+ g_autofree gchar *raw_iccid = NULL;
+ g_autofree gchar *iccid = NULL;
+ g_autofree gchar *eid = NULL;
+ g_autoptr(GError) inner_error = NULL;
+ gboolean sim_active = FALSE;
+
+ /* Store active slot info */
+ slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i);
+ if (slot_status->physical_slot_status == QMI_UIM_SLOT_STATE_ACTIVE) {
+ sim_active = TRUE;
+ ctx->active_logical_id = slot_status->logical_slot;
+ ctx->active_slot_number = i + 1;
+ }
+
+ if (!slot_status->iccid->len) {
+ mm_obj_dbg (self, "not creating SIM object: no SIM in slot %u", i + 1);
+ g_ptr_array_add (ctx->sim_slots, NULL);
+ continue;
+ }
+
+ /* This is supposed to be BCD, but some carriers have non-spec-compliant ICCIDs that use
+ * A-F characters as part of the operator-specific part of the ICCID. Parse it as hex and
+ * let mm_3gpp_parse_iccid take care of handling the A-F characters properly. */
+ raw_iccid = mm_utils_bin2hexstr ((const guint8 *)slot_status->iccid->data, slot_status->iccid->len);
+ if (!raw_iccid) {
+ mm_obj_warn (self, "not creating SIM object: failed to convert ICCID from BCD");
+ g_ptr_array_add (ctx->sim_slots, NULL);
+ continue;
+ }
+
+ iccid = mm_3gpp_parse_iccid (raw_iccid, &inner_error);
+ if (!iccid) {
+ mm_obj_warn (self, "not creating SIM object: couldn't parse SIM iccid: %s", inner_error->message);
+ g_ptr_array_add (ctx->sim_slots, NULL);
+ continue;
+ }
+
+ if (ext_information && slot_eids) {
+ slot_info = &g_array_index (ext_information, QmiPhysicalSlotInformationSlot, i);
+ if (slot_info->is_euicc) {
+ GArray *slot_eid;
+
+ slot_eid = g_array_index (slot_eids, GArray *, i);
+ if (slot_eid->len)
+ eid = mm_decode_eid (slot_eid->data, slot_eid->len);
+ if (!eid)
+ mm_obj_dbg (self, "SIM in slot %d is marked as eUICC, but has malformed EID", i + 1);
+ }
+ }
+
+ sim = mm_sim_qmi_new_initialized (MM_BASE_MODEM (self),
+ TRUE, /* consider DMS UIM deprecated if we're creating SIM slots */
+ i + 1, /* slot number is the array index starting at 1 */
+ sim_active,
+ iccid,
+ NULL, /* imsi unknown */
+ eid, /* may be NULL, which is fine */
+ NULL, /* operator id unknown */
+ NULL, /* operator name unknown */
+ NULL); /* emergency numbers unknown */
+ g_ptr_array_add (ctx->sim_slots, sim);
+ }
+ g_assert_cmpuint (ctx->sim_slots->len, ==, physical_slots->len);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_load_sim_slots (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadSimSlotsContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (LoadSimSlotsContext);
+ ctx->client_uim = QMI_CLIENT_UIM (g_object_ref (client));
+ g_task_set_task_data (task, ctx, (GDestroyNotify) load_sim_slots_context_free);
+
+ qmi_client_uim_get_slot_status (ctx->client_uim,
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_get_slot_status_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set Primary SIM slot (modem interface) */
+
+gboolean
+mm_shared_qmi_set_primary_sim_slot_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+uim_switch_slot_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimSwitchSlotOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+ MMIfaceModem *self;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_uim_switch_slot_finish (client, res, &error);
+ if (!output || !qmi_message_uim_switch_slot_output_get_result (output, &error)) {
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT))
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS,
+ "SIM slot switch operation not needed");
+ else
+ g_task_return_error (task, g_steal_pointer (&error));
+ } else {
+ mm_obj_info (self, "SIM slot switch operation request successful");
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+uim_switch_get_slot_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL;
+ g_autoptr(QmiMessageUimSwitchSlotInput) input = NULL;
+ MMIfaceModem *self;
+ GError *error = NULL;
+ GArray *physical_slots = NULL;
+ guint i;
+ guint active_logical_id = 0;
+ guint active_slot_number;
+ guint slot_number;
+
+ self = g_task_get_source_object (task);
+ slot_number = GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ output = qmi_client_uim_get_slot_status_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_uim_get_slot_status_output_get_result (output, &error) ||
+ !qmi_message_uim_get_slot_status_output_get_physical_slot_status (output, &physical_slots, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ for (i = 0; i < physical_slots->len; i++) {
+ QmiPhysicalSlotStatusSlot *slot_status;
+
+ /* We look for the currently ACTIVE SIM card only! */
+ slot_status = &g_array_index (physical_slots, QmiPhysicalSlotStatusSlot, i);
+ if (slot_status->physical_slot_status != QMI_UIM_SLOT_STATE_ACTIVE)
+ continue;
+
+ active_logical_id = slot_status->logical_slot;
+ active_slot_number = i + 1;
+ }
+
+ if (!active_logical_id) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "couldn't find active slot logical ID");
+ g_object_unref (task);
+ return;
+ }
+
+ if (active_slot_number == slot_number) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_EXISTS,
+ "SIM slot switch operation not needed");
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "requesting active logical id %d switch to SIM slot %u", active_logical_id, slot_number);
+
+ input = qmi_message_uim_switch_slot_input_new ();
+ qmi_message_uim_switch_slot_input_set_logical_slot (input, (guint8) active_logical_id, NULL);
+ qmi_message_uim_switch_slot_input_set_physical_slot (input, slot_number, NULL);
+ qmi_client_uim_switch_slot (client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_switch_slot_ready,
+ task);
+}
+
+void
+mm_shared_qmi_set_primary_sim_slot (MMIfaceModem *self,
+ guint sim_slot,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (sim_slot), NULL);
+
+ qmi_client_uim_get_slot_status (QMI_CLIENT_UIM (client),
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_switch_get_slot_status_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* SIM hot swap detection */
+
+#define REFRESH_START_TIMEOUT_SECS 3
+
+gboolean
+mm_shared_qmi_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+uim_refresh_complete (QmiClientUim *client,
+ QmiUimSessionType session_type)
+{
+ g_autoptr(QmiMessageUimRefreshCompleteInput) refresh_complete_input = NULL;
+ GArray *dummy_aid;
+
+ dummy_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
+
+ refresh_complete_input = qmi_message_uim_refresh_complete_input_new ();
+ qmi_message_uim_refresh_complete_input_set_session (
+ refresh_complete_input,
+ session_type,
+ dummy_aid, /* ignored */
+ NULL);
+ qmi_message_uim_refresh_complete_input_set_info (
+ refresh_complete_input,
+ TRUE,
+ NULL);
+
+ qmi_client_uim_refresh_complete (
+ client,
+ refresh_complete_input,
+ 10,
+ NULL,
+ NULL,
+ NULL);
+ g_array_unref (dummy_aid);
+}
+
+static gboolean
+uim_start_refresh_timeout (MMSharedQmi *self)
+{
+ Private *priv;
+
+ priv = get_private (self);
+ priv->uim_refresh_start_timeout_id = 0;
+
+ mm_obj_dbg (self, "refresh start timed out; trigger SIM change check");
+
+ mm_iface_modem_check_for_sim_swap (MM_IFACE_MODEM (self), 0, NULL, NULL, NULL);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+uim_refresh_indication_cb (QmiClientUim *client,
+ QmiIndicationUimRefreshOutput *output,
+ MMSharedQmi *self)
+{
+ QmiUimRefreshStage stage;
+ QmiUimRefreshMode mode;
+ QmiUimSessionType session_type;
+ Private *priv;
+ g_autoptr(GError) error = NULL;
+
+ priv = get_private (self);
+
+ if (!qmi_indication_uim_refresh_output_get_event (output,
+ &stage,
+ &mode,
+ &session_type,
+ NULL,
+ NULL,
+ &error)) {
+ mm_obj_warn (self, "couldn't process UIM refresh indication: %s", error->message);
+ return;
+ }
+
+ mm_obj_dbg (self, "refresh indication received: session type '%s', stage '%s', mode '%s'",
+ qmi_uim_session_type_get_string (session_type),
+ qmi_uim_refresh_stage_get_string (stage),
+ qmi_uim_refresh_mode_get_string (mode));
+
+ /* Support only the first slot for now. Primary GW provisioning is used in old modems. */
+ if (session_type != QMI_UIM_SESSION_TYPE_CARD_SLOT_1 &&
+ session_type != QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING) {
+ mm_obj_warn (self, "refresh session type not supported: %s", qmi_uim_session_type_get_string (session_type));
+ return;
+ }
+
+ /* Currently we handle only UICC Reset type refresh, which can be used
+ * in profile switch scenarios. In other cases we just trigger 'refresh
+ * complete' during start phase. Signal to notify about potential SIM
+ * profile switch is triggered when the refresh is ending. If it were
+ * triggered in start phase, reading SIM files seems to fail with
+ * an internal error.
+ *
+ * It's possible that 'end-with-success' stage never appears. For that,
+ * we start a timer at 'start' stage and if it expires, the SIM change
+ * check is triggered anyway. */
+ if (stage == QMI_UIM_REFRESH_STAGE_START) {
+ if (mode == QMI_UIM_REFRESH_MODE_RESET) {
+ if (!priv->uim_refresh_start_timeout_id)
+ priv->uim_refresh_start_timeout_id = g_timeout_add_seconds (REFRESH_START_TIMEOUT_SECS,
+ (GSourceFunc)uim_start_refresh_timeout,
+ self);
+ } else
+ uim_refresh_complete (client, session_type);
+ } else if (stage == QMI_UIM_REFRESH_STAGE_END_WITH_SUCCESS) {
+ if (mode == QMI_UIM_REFRESH_MODE_RESET) {
+ if (priv->uim_refresh_start_timeout_id) {
+ g_source_remove (priv->uim_refresh_start_timeout_id);
+ priv->uim_refresh_start_timeout_id = 0;
+ }
+ mm_iface_modem_check_for_sim_swap (MM_IFACE_MODEM (self), 0, NULL, NULL, NULL);
+ }
+ }
+}
+
+/* Modifies the sim at slot == index+1, based on the content of slot_status.
+ * Primarily used when a hotswap occurs on the inactive slot */
+static void
+update_sim_from_slot_status (MMSharedQmi *self,
+ QmiPhysicalSlotStatusSlot *slot_status,
+ guint slot_index)
+{
+ g_autoptr(MMBaseSim) sim = NULL;
+ g_autofree gchar *raw_iccid = NULL;
+ g_autofree gchar *iccid = NULL;
+ g_autoptr(GError) inner_error = NULL;
+
+ mm_obj_dbg (self, "Updating sim at slot %d", slot_index + 1);
+ /* If sim is not present, ask mm_iface_modem to clear the sim object */
+ if (slot_status->physical_card_status != QMI_UIM_PHYSICAL_CARD_STATE_PRESENT) {
+ mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), slot_index, NULL);
+ return;
+ }
+
+ raw_iccid = mm_utils_bin2hexstr ((const guint8 *)slot_status->iccid->data, slot_status->iccid->len);
+ if (!raw_iccid) {
+ mm_obj_warn (self, "not creating SIM object: failed to convert ICCID from BCD");
+ return;
+ }
+ iccid = mm_3gpp_parse_iccid (raw_iccid, &inner_error);
+ if (!iccid) {
+ mm_obj_warn (self, "not creating SIM object: couldn't parse SIM iccid: %s", inner_error->message);
+ return;
+ }
+ sim = mm_sim_qmi_new_initialized (MM_BASE_MODEM (self),
+ TRUE, /* consider DMS UIM deprecated if we're creating SIM slots */
+ slot_index + 1, /* slot number is the array index starting at 1 */
+ slot_status->physical_slot_status, /* is_active */
+ iccid,
+ NULL, /* imsi unknown */
+ NULL, /* eid unknown */
+ NULL, /* operator id unknown */
+ NULL, /* operator name unknown */
+ NULL); /* emergency numbers unknown */
+
+ mm_iface_modem_modify_sim (MM_IFACE_MODEM (self), slot_index, sim);
+}
+
+/* Checks for equality of two slots. */
+static gboolean
+slot_status_equal (QmiPhysicalSlotStatusSlot *slot_a,
+ QmiPhysicalSlotStatusSlot *slot_b)
+{
+ guint j;
+
+ if (slot_a->physical_slot_status != slot_b->physical_slot_status)
+ return FALSE;
+ if (slot_a->physical_card_status != slot_b->physical_card_status)
+ return FALSE;
+ if (slot_a->iccid->len != slot_b->iccid->len)
+ return FALSE;
+ for (j = 0; j < slot_a->iccid->len; j++) {
+ if (g_array_index (slot_a->iccid, guint8, j) != g_array_index (slot_b->iccid, guint8, j))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Checks for equality of QmiPhysicalSlotStatusSlot arrays.
+ * The number of elements in each array is expected to be the number of sim slots in the modem.*/
+static gboolean
+slot_array_status_equal (GArray *slots_status1,
+ GArray *slots_status2,
+ gboolean check_active_slots_only)
+{
+ guint i;
+
+ if (!slots_status1 && !slots_status2)
+ return TRUE;
+ if (!slots_status1 || !slots_status2 || slots_status1->len != slots_status2->len)
+ return FALSE;
+ for (i = 0; i < slots_status1->len; i++) {
+ /* Compare slot at index i from slots_status1 and slots_status2 */
+ QmiPhysicalSlotStatusSlot *slot_a;
+ QmiPhysicalSlotStatusSlot *slot_b;
+
+ slot_a = &g_array_index (slots_status1, QmiPhysicalSlotStatusSlot, i);
+ slot_b = &g_array_index (slots_status2, QmiPhysicalSlotStatusSlot, i);
+
+ /* Check that slot_a and slot_b have the same slot status (i.e. active or inactive) */
+ if (slot_a->physical_slot_status != slot_b->physical_slot_status)
+ return FALSE;
+ /* Once slot_a and slot_b are confirmed to have the same physical slot status,
+ * we will ignore inactive slots if check_active_slots_only is set. */
+ if (check_active_slots_only && slot_a->physical_slot_status != QMI_UIM_SLOT_STATE_ACTIVE) {
+ g_assert (slot_a->physical_slot_status == slot_b->physical_slot_status);
+ continue;
+ }
+ if (!slot_status_equal (slot_a, slot_b))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void
+uim_slot_status_indication_cb (QmiClientUim *client,
+ QmiIndicationUimSlotStatusOutput *output,
+ MMSharedQmi *self)
+{
+ GArray *new_slots_status = NULL;
+ Private *priv;
+ guint i;
+ g_autoptr(GError) error = NULL;
+
+ priv = get_private (self);
+ mm_obj_dbg (self, "received slot status indication");
+
+ if (!qmi_indication_uim_slot_status_output_get_physical_slot_status (output,
+ &new_slots_status,
+ &error)) {
+ mm_obj_warn (self, "could not process slot status indication: %s", error->message);
+ return;
+ }
+
+ /* A slot status indication means that
+ * 1) The physical slot to logical slot mapping has changed as a
+ * result of switching the slot. or,
+ * 2) A card has been removed from, or inserted to, the physical slot. or,
+ * 3) A physical slot is powered up or down. */
+
+ /* Reprobe if the active slot changed or the information in an
+ * active slot's status changed */
+ if (!slot_array_status_equal (priv->slots_status, new_slots_status, TRUE)) {
+ mm_obj_dbg (self, "An active slot had a status change, will reprobe the modem");
+ mm_base_modem_process_sim_event (MM_BASE_MODEM (self));
+ return;
+ }
+
+ /* An inactive slot changed, we won't be reprobing the modem.
+ * Instead, we will ask mm_iface_modem to update the sim object.
+ * Iterate over each slot to identify the slots that changed state.*/
+ for (i = 0; i < priv->slots_status->len; i++) {
+ QmiPhysicalSlotStatusSlot *old_slot;
+ QmiPhysicalSlotStatusSlot *new_slot;
+
+ old_slot = &g_array_index (priv->slots_status, QmiPhysicalSlotStatusSlot, i);
+ new_slot = &g_array_index (new_slots_status, QmiPhysicalSlotStatusSlot, i);
+
+ if (!slot_status_equal (old_slot, new_slot)) {
+ mm_obj_dbg (self, "Slot %d (inactive) had a status change. Will update sims, but not reprobe", i + 1);
+ update_sim_from_slot_status (self, new_slot, i);
+ }
+ }
+
+ g_clear_pointer (&priv->slots_status, g_array_unref);
+ priv->slots_status = g_array_ref (new_slots_status);
+}
+
+static void
+uim_refresh_register_iccid_change_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ g_autoptr(QmiMessageUimRefreshRegisterOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ output = qmi_client_uim_refresh_register_finish (client, res, &error);
+ if (!output || !qmi_message_uim_refresh_register_output_get_result (output, &error)) {
+ mm_obj_dbg (self, "refresh registration using 'refresh register' failed: %s", error->message);
+ g_clear_object (&priv->uim_client);
+ g_task_return_new_error (task, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_SUPPORTED,
+ "SIM hot swap detection not supported by modem");
+ } else {
+ mm_obj_dbg (self, "registered for SIM refresh events using 'refresh register'");
+ priv->uim_refresh_indication_id =
+ g_signal_connect (client,
+ "refresh",
+ G_CALLBACK (uim_refresh_indication_cb),
+ self);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+/* This is the last resort if 'refresh register all' does not work. It works
+ * on some older modems. Those modems may not also support QMI_UIM_SESSION_TYPE_CARD_SLOT_1
+ * so we'll use QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING */
+static void
+uim_refresh_register_iccid_change (GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessageUimRefreshRegisterInputInfoFilesElement file_element;
+ guint8 val;
+ g_autoptr(QmiMessageUimRefreshRegisterInput) refresh_register_input = NULL;
+ g_autoptr(GArray) dummy_aid = NULL;
+ g_autoptr(GArray) file = NULL;
+ g_autoptr(GArray) file_element_path = NULL;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ mm_obj_dbg (self, "register for refresh file indication");
+
+ dummy_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
+
+ file = g_array_sized_new (FALSE, FALSE, sizeof (QmiMessageUimRefreshRegisterInputInfoFilesElement), 1);
+
+ file_element_path = g_array_sized_new (FALSE, FALSE, sizeof (guint8), 2);
+ val = 0x00;
+ g_array_append_val (file_element_path, val);
+ val = 0x3F;
+ g_array_append_val (file_element_path, val);
+
+
+ memset (&file_element, 0, sizeof (file_element));
+ file_element.file_id = 0x2FE2; /* ICCID */
+ file_element.path = file_element_path;
+ g_array_append_val (file, file_element);
+
+ refresh_register_input = qmi_message_uim_refresh_register_input_new ();
+ qmi_message_uim_refresh_register_input_set_info (refresh_register_input,
+ TRUE,
+ FALSE,
+ file,
+ NULL);
+ qmi_message_uim_refresh_register_input_set_session (refresh_register_input,
+ QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
+ dummy_aid,
+ NULL);
+
+ qmi_client_uim_refresh_register (QMI_CLIENT_UIM (priv->uim_client),
+ refresh_register_input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_refresh_register_iccid_change_ready,
+ task);
+}
+
+/* Refresh registration and event handling.
+ * This is used only as fallback in case slot status indications do not work
+ * in the particular modem (determined by UIM Get Slot Status failing) for
+ * detecting ICCID changing due to a profile switch.
+ *
+ * We assume that devices not supporting UIM Get Slot Status only have a
+ * single slot, for which we register refresh events.
+ */
+
+static void
+uim_refresh_register_all_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimRefreshRegisterAllOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+ MMIfaceModem *self;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ output = qmi_client_uim_refresh_register_all_finish (client, res, &error);
+ if (!output || !qmi_message_uim_refresh_register_all_output_get_result (output, &error)) {
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED) ||
+ g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+ /* As last resort, if 'refresh register all' fails, try a plain 'refresh register'.
+ * Some older modems may not support 'refresh register all'. */
+ uim_refresh_register_iccid_change (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "refresh register all operation failed: %s", error->message);
+ g_clear_object (&priv->uim_client);
+ g_task_return_error (task, g_steal_pointer (&error));
+ } else {
+ mm_obj_dbg (self, "registered for all SIM refresh events");
+ priv->uim_refresh_indication_id =
+ g_signal_connect (client,
+ "refresh",
+ G_CALLBACK (uim_refresh_indication_cb),
+ self);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+}
+
+static void
+uim_slot_status_not_supported (GTask *task)
+{
+ MMIfaceModem *self;
+ Private *priv;
+ g_autoptr(QmiMessageUimRefreshRegisterAllInput) refresh_register_all_input = NULL;
+ g_autoptr(GArray) dummy_aid = NULL;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ g_assert (!priv->uim_refresh_indication_id);
+
+ mm_obj_dbg (self, "slot status not supported by modem: register for refresh indications");
+
+ dummy_aid = g_array_new (FALSE, FALSE, sizeof (guint8));
+ refresh_register_all_input = qmi_message_uim_refresh_register_all_input_new ();
+
+ qmi_message_uim_refresh_register_all_input_set_info (refresh_register_all_input,
+ TRUE,
+ NULL);
+ qmi_message_uim_refresh_register_all_input_set_session (refresh_register_all_input,
+ QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
+ dummy_aid,
+ NULL);
+
+ qmi_client_uim_refresh_register_all (QMI_CLIENT_UIM (priv->uim_client),
+ refresh_register_all_input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_refresh_register_all_ready,
+ task);
+}
+
+static void
+uim_check_get_slot_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimGetSlotStatusOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+ MMIfaceModem *self;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ output = qmi_client_uim_get_slot_status_finish (client, res, &error);
+ if (!output || !qmi_message_uim_get_slot_status_output_get_result (output, &error)) {
+ if (priv->uim_slot_status_indication_id) {
+ g_signal_handler_disconnect (client, priv->uim_slot_status_indication_id);
+ priv->uim_slot_status_indication_id = 0;
+ }
+
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED) ||
+ g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
+ uim_slot_status_not_supported (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "slot status retrieval failed: %s", error->message);
+ g_clear_object (&priv->uim_client);
+ g_task_return_error (task, g_steal_pointer (&error));
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "slot status retrieval succeeded: monitoring slot status indications");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+uim_register_events_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimRegisterEventsOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+ MMIfaceModem *self;
+ Private *priv;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ /* If event registration fails, go on with initialization. In that case
+ * we cannot use slot status indications to detect eUICC profile switches. */
+ output = qmi_client_uim_register_events_finish (client, res, &error);
+ if (output && qmi_message_uim_register_events_output_get_result (output, &error)) {
+ g_assert (!priv->uim_slot_status_indication_id);
+ priv->uim_slot_status_indication_id = g_signal_connect (priv->uim_client,
+ "slot-status",
+ G_CALLBACK (uim_slot_status_indication_cb),
+ self);
+ mm_obj_dbg (self, "registered for slot status indications");
+
+ /* Successful registration does not mean that the modem actually sends
+ * physical slot status indications; invoke Get Slot Status to find out if
+ * the modem really supports slot status. */
+ qmi_client_uim_get_slot_status (client,
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_check_get_slot_status_ready,
+ task);
+ return;
+ }
+
+ mm_obj_dbg (self, "not registered for slot status indications: %s", error->message);
+ uim_slot_status_not_supported (task);
+}
+
+void
+mm_shared_qmi_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(QmiMessageUimRegisterEventsInput) register_events_input = NULL;
+ GTask *task;
+ QmiClient *client = NULL;
+ Private *priv;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_UIM, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ g_assert (!priv->uim_slot_status_indication_id);
+ g_assert (!priv->uim_client);
+ priv->uim_client = g_object_ref (client);
+
+ register_events_input = qmi_message_uim_register_events_input_new ();
+ qmi_message_uim_register_events_input_set_event_registration_mask (register_events_input,
+ QMI_UIM_EVENT_REGISTRATION_FLAG_PHYSICAL_SLOT_STATUS,
+ NULL);
+ qmi_client_uim_register_events (QMI_CLIENT_UIM (priv->uim_client),
+ register_events_input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) uim_register_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* FCC unlock (Modem interface) */
+
+gboolean
+mm_shared_qmi_fcc_unlock_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+dms_set_fcc_authentication_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ g_autoptr(QmiMessageDmsSetFccAuthenticationOutput) output = NULL;
+
+ output = qmi_client_dms_set_fcc_authentication_finish (client, res, &error);
+ if (!output || !qmi_message_dms_set_fcc_authentication_output_get_result (output, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_fcc_unlock (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ qmi_client_dms_set_fcc_authentication (QMI_CLIENT_DMS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)dms_set_fcc_authentication_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Location: Set SUPL server */
+
+typedef struct {
+ QmiClient *client;
+ gchar *supl;
+ glong indication_id;
+ guint timeout_id;
+} SetSuplServerContext;
+
+static void
+set_supl_server_context_free (SetSuplServerContext *ctx)
+{
+ if (ctx->client) {
+ if (ctx->timeout_id)
+ g_source_remove (ctx->timeout_id);
+ if (ctx->indication_id)
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ g_object_unref (ctx->client);
+ }
+ g_slice_free (SetSuplServerContext, ctx);
+}
+
+static GArray *
+parse_as_utf16_url (const gchar *supl)
+{
+ GArray *url;
+ gchar *utf16;
+ gsize utf16_len;
+
+ utf16 = g_convert (supl, -1, "UTF-16BE", "UTF-8", NULL, &utf16_len, NULL);
+ url = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), utf16_len),
+ utf16, utf16_len);
+ g_free (utf16);
+ return url;
+}
+
+gboolean
+mm_shared_qmi_location_set_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+pds_set_agps_config_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdsSetAgpsConfigOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_pds_set_agps_config_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_set_agps_config_output_get_result (output, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ qmi_message_pds_set_agps_config_output_unref (output);
+}
+
+static void
+pds_set_supl_server (GTask *task)
+{
+ MMSharedQmi *self;
+ SetSuplServerContext *ctx;
+ QmiMessagePdsSetAgpsConfigInput *input;
+ guint32 ip;
+ guint16 port;
+ GArray *url;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_pds_set_agps_config_input_new ();
+
+ /* For multimode devices, prefer UMTS by default */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
+ qmi_message_pds_set_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_UMTS, NULL);
+ else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
+ qmi_message_pds_set_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_CDMA, NULL);
+
+ if (mm_parse_supl_address (ctx->supl, NULL, &ip, &port, NULL))
+ qmi_message_pds_set_agps_config_input_set_location_server_address (input, ip, port, NULL);
+ else {
+ url = parse_as_utf16_url (ctx->supl);
+ qmi_message_pds_set_agps_config_input_set_location_server_url (input, url, NULL);
+ g_array_unref (url);
+ }
+
+ qmi_client_pds_set_agps_config (
+ QMI_CLIENT_PDS (ctx->client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_set_agps_config_ready,
+ task);
+ qmi_message_pds_set_agps_config_input_unref (input);
+}
+
+static gboolean
+loc_location_set_server_indication_timed_out (GTask *task)
+{
+ SetSuplServerContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->timeout_id = 0;
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Failed to receive indication with the server update result");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+loc_location_set_server_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocSetServerOutput *output,
+ GTask *task)
+{
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+
+ if (!qmi_indication_loc_set_server_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ mm_error_from_qmi_loc_indication_status (status, &error);
+
+out:
+ if (error)
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+loc_set_server_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetSuplServerContext *ctx;
+ QmiMessageLocSetServerOutput *output;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_set_server_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_set_server_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_set_server_output_unref (output);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "set-server",
+ G_CALLBACK (loc_location_set_server_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_set_server_indication_timed_out,
+ task);
+
+ qmi_message_loc_set_server_output_unref (output);
+}
+
+static void
+loc_set_supl_server (GTask *task)
+{
+ MMSharedQmi *self;
+ SetSuplServerContext *ctx;
+ QmiMessageLocSetServerInput *input;
+ guint32 ip;
+ guint16 port;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_loc_set_server_input_new ();
+
+ /* For multimode devices, prefer UMTS by default */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
+ qmi_message_loc_set_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_UMTS_SLP, NULL);
+ else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
+ qmi_message_loc_set_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_CDMA_PDE, NULL);
+
+ if (mm_parse_supl_address (ctx->supl, NULL, &ip, &port, NULL))
+ qmi_message_loc_set_server_input_set_ipv4 (input, ip, (guint32) port, NULL);
+ else
+ qmi_message_loc_set_server_input_set_url (input, ctx->supl, NULL);
+
+ qmi_client_loc_set_server (
+ QMI_CLIENT_LOC (ctx->client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)loc_set_server_ready,
+ task);
+ qmi_message_loc_set_server_input_unref (input);
+}
+
+void
+mm_shared_qmi_location_set_supl_server (MMIfaceModemLocation *self,
+ const gchar *supl,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ SetSuplServerContext *ctx;
+ QmiClient *client = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (SetSuplServerContext);
+ ctx->supl = g_strdup (supl);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_supl_server_context_free);
+
+ /* Prefer PDS */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ ctx->client = g_object_ref (client);
+ pds_set_supl_server (task);
+ return;
+ }
+
+ /* Otherwise LOC */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_LOC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ ctx->client = g_object_ref (client);
+ loc_set_supl_server (task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find any PDS/LOC client");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: Load SUPL server */
+
+typedef struct {
+ QmiClient *client;
+ glong indication_id;
+ guint timeout_id;
+} LoadSuplServerContext;
+
+static void
+load_supl_server_context_free (LoadSuplServerContext *ctx)
+{
+ if (ctx->client) {
+ if (ctx->timeout_id)
+ g_source_remove (ctx->timeout_id);
+ if (ctx->indication_id)
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ g_object_unref (ctx->client);
+ }
+ g_slice_free (LoadSuplServerContext, ctx);
+}
+
+gchar *
+mm_shared_qmi_location_load_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+pds_get_agps_config_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdsGetAgpsConfigOutput *output;
+ GError *error = NULL;
+ guint32 ip = 0;
+ guint32 port = 0;
+ GArray *url = NULL;
+ gchar *str = NULL;
+
+ output = qmi_client_pds_get_agps_config_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ if (!qmi_message_pds_get_agps_config_output_get_result (output, &error))
+ goto out;
+
+ /* Prefer IP/PORT to URL */
+ if (qmi_message_pds_get_agps_config_output_get_location_server_address (
+ output,
+ &ip,
+ &port,
+ NULL) &&
+ ip != 0 &&
+ port != 0) {
+ struct in_addr a = { .s_addr = ip };
+ gchar buf[INET_ADDRSTRLEN + 1];
+
+ memset (buf, 0, sizeof (buf));
+
+ if (!inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)) {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot convert numeric IP address (%u) to string", ip);
+ goto out;
+ }
+
+ str = g_strdup_printf ("%s:%u", buf, port);
+ goto out;
+ }
+
+ if (qmi_message_pds_get_agps_config_output_get_location_server_url (
+ output,
+ &url,
+ NULL) &&
+ url->len > 0) {
+ str = g_convert (url->data, url->len, "UTF-8", "UTF-16BE", NULL, NULL, NULL);
+ }
+
+ if (!str)
+ str = g_strdup ("");
+
+out:
+ if (error)
+ g_task_return_error (task, error);
+ else {
+ g_assert (str);
+ g_task_return_pointer (task, str, g_free);
+ }
+ g_object_unref (task);
+
+ if (output)
+ qmi_message_pds_get_agps_config_output_unref (output);
+}
+
+static void
+pds_load_supl_server (GTask *task)
+{
+ MMSharedQmi *self;
+ LoadSuplServerContext *ctx;
+ QmiMessagePdsGetAgpsConfigInput *input;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_pds_get_agps_config_input_new ();
+
+ /* For multimode devices, prefer UMTS by default */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
+ qmi_message_pds_get_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_UMTS, NULL);
+ else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
+ qmi_message_pds_get_agps_config_input_set_network_mode (input, QMI_PDS_NETWORK_MODE_CDMA, NULL);
+
+ qmi_client_pds_get_agps_config (
+ QMI_CLIENT_PDS (ctx->client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_get_agps_config_ready,
+ task);
+ qmi_message_pds_get_agps_config_input_unref (input);
+}
+
+static gboolean
+loc_location_get_server_indication_timed_out (GTask *task)
+{
+ LoadSuplServerContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->timeout_id = 0;
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Failed to receive indication with the current server settings");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+loc_location_get_server_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocGetServerOutput *output,
+ GTask *task)
+{
+ QmiLocIndicationStatus status;
+ const gchar *url = NULL;
+ guint32 ipv4_address = 0;
+ guint16 ipv4_port = 0;
+ GError *error = NULL;
+ gchar *str = NULL;
+
+ if (!qmi_indication_loc_get_server_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ if (!mm_error_from_qmi_loc_indication_status (status, &error))
+ goto out;
+
+ /* Prefer IP/PORT to URL */
+
+ if (qmi_indication_loc_get_server_output_get_ipv4 (
+ output,
+ &ipv4_address,
+ &ipv4_port,
+ NULL) &&
+ ipv4_address != 0 && ipv4_port != 0) {
+ struct in_addr a = { .s_addr = ipv4_address };
+ gchar buf[INET_ADDRSTRLEN + 1];
+
+ memset (buf, 0, sizeof (buf));
+
+ if (!inet_ntop (AF_INET, &a, buf, sizeof (buf) - 1)) {
+ error = g_error_new (MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot convert numeric IP address (%u) to string", ipv4_address);
+ goto out;
+ }
+
+ str = g_strdup_printf ("%s:%u", buf, ipv4_port);
+ goto out;
+ }
+
+ if (qmi_indication_loc_get_server_output_get_url (
+ output,
+ &url,
+ NULL) &&
+ url && url [0]) {
+ str = g_strdup (url);
+ }
+
+ if (!str)
+ str = g_strdup ("");
+
+out:
+ if (error)
+ g_task_return_error (task, error);
+ else {
+ g_assert (str);
+ g_task_return_pointer (task, str, g_free);
+ }
+ g_object_unref (task);
+
+}
+
+static void
+loc_get_server_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadSuplServerContext *ctx;
+ QmiMessageLocGetServerOutput *output;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_get_server_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_get_server_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_get_server_output_unref (output);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "get-server",
+ G_CALLBACK (loc_location_get_server_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_get_server_indication_timed_out,
+ task);
+
+ qmi_message_loc_get_server_output_unref (output);
+}
+
+static void
+loc_load_supl_server (GTask *task)
+{
+ MMSharedQmi *self;
+ LoadSuplServerContext *ctx;
+ QmiMessageLocGetServerInput *input;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_loc_get_server_input_new ();
+
+ /* For multimode devices, prefer UMTS by default */
+ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self)))
+ qmi_message_loc_get_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_UMTS_SLP, NULL);
+ else if (mm_iface_modem_is_cdma (MM_IFACE_MODEM (self)))
+ qmi_message_loc_get_server_input_set_server_type (input, QMI_LOC_SERVER_TYPE_CDMA_PDE, NULL);
+
+ qmi_message_loc_get_server_input_set_server_address_type (
+ input,
+ (QMI_LOC_SERVER_ADDRESS_TYPE_IPV4 | QMI_LOC_SERVER_ADDRESS_TYPE_URL),
+ NULL);
+
+ qmi_client_loc_get_server (
+ QMI_CLIENT_LOC (ctx->client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)loc_get_server_ready,
+ task);
+ qmi_message_loc_get_server_input_unref (input);
+}
+
+void
+mm_shared_qmi_location_load_supl_server (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client;
+ GTask *task;
+ LoadSuplServerContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (LoadSuplServerContext);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_supl_server_context_free);
+
+ /* Prefer PDS */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ ctx->client = g_object_ref (client);
+ pds_load_supl_server (task);
+ return;
+ }
+
+ /* Otherwise LOC */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_LOC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ ctx->client = g_object_ref (client);
+ loc_load_supl_server (task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find any PDS/LOC client");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: internal helper: stop gps engine */
+
+static gboolean
+stop_gps_engine_finish (MMSharedQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+pds_gps_service_state_stop_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessagePdsSetGpsServiceStateOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_pds_set_gps_service_state_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_set_gps_service_state_output_get_result (output, &error)) {
+ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_prefix_error (&error, "Couldn't set GPS service state: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_pds_set_gps_service_state_output_unref (output);
+ return;
+ }
+
+ g_error_free (error);
+ }
+
+ qmi_message_pds_set_gps_service_state_output_unref (output);
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ if (priv->pds_client) {
+ if (priv->pds_location_event_report_indication_id != 0) {
+ g_signal_handler_disconnect (priv->pds_client, priv->pds_location_event_report_indication_id);
+ priv->pds_location_event_report_indication_id = 0;
+ }
+ g_clear_object (&priv->pds_client);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+loc_stop_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessageLocStopOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_loc_stop_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_stop_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't stop GPS engine: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_stop_output_unref (output);
+ return;
+ }
+
+ qmi_message_loc_stop_output_unref (output);
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ if (priv->loc_client) {
+ if (priv->loc_location_nmea_indication_id != 0) {
+ g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id);
+ priv->loc_location_nmea_indication_id = 0;
+ }
+ g_clear_object (&priv->loc_client);
+ }
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+stop_gps_engine (MMSharedQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ priv = get_private (self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (priv->pds_client) {
+ QmiMessagePdsSetGpsServiceStateInput *input;
+
+ input = qmi_message_pds_set_gps_service_state_input_new ();
+ qmi_message_pds_set_gps_service_state_input_set_state (input, FALSE, NULL);
+ qmi_client_pds_set_gps_service_state (
+ QMI_CLIENT_PDS (priv->pds_client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_gps_service_state_stop_ready,
+ task);
+ qmi_message_pds_set_gps_service_state_input_unref (input);
+ return;
+ }
+
+ if (priv->loc_client) {
+ QmiMessageLocStopInput *input;
+
+ input = qmi_message_loc_stop_input_new ();
+ qmi_message_loc_stop_input_set_session_id (input, DEFAULT_LOC_SESSION_ID, NULL);
+ qmi_client_loc_stop (QMI_CLIENT_LOC (priv->loc_client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) loc_stop_ready,
+ task);
+ qmi_message_loc_stop_input_unref (input);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find any PDS/LOC client");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: internal helpers: NMEA indication callbacks */
+
+static void
+pds_location_event_report_indication_cb (QmiClientPds *client,
+ QmiIndicationPdsEventReportOutput *output,
+ MMSharedQmi *self)
+{
+ QmiPdsPositionSessionStatus session_status;
+ const gchar *nmea;
+
+ if (qmi_indication_pds_event_report_output_get_position_session_status (
+ output,
+ &session_status,
+ NULL)) {
+ mm_obj_dbg (self, "[GPS] session status changed: '%s'",
+ qmi_pds_position_session_status_get_string (session_status));
+ }
+
+ if (qmi_indication_pds_event_report_output_get_nmea_position (
+ output,
+ &nmea,
+ NULL)) {
+ mm_obj_dbg (self, "[NMEA] %s", nmea);
+ mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
+ }
+}
+
+static void
+loc_location_nmea_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocNmeaOutput *output,
+ MMSharedQmi *self)
+{
+ const gchar *nmea = NULL;
+
+ qmi_indication_loc_nmea_output_get_nmea_string (output, &nmea, NULL);
+ if (!nmea)
+ return;
+
+ mm_obj_dbg (self, "[NMEA] %s", nmea);
+ mm_iface_modem_location_gps_update (MM_IFACE_MODEM_LOCATION (self), nmea);
+}
+
+/*****************************************************************************/
+/* Location: internal helper: setup minimum required NMEA traces */
+
+typedef struct {
+ QmiClientLoc *client;
+ guint timeout_id;
+ gulong indication_id;
+} SetupRequiredNmeaTracesContext;
+
+static void
+setup_required_nmea_traces_cleanup_action (SetupRequiredNmeaTracesContext *ctx)
+{
+ if (ctx->indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ ctx->indication_id = 0;
+ }
+ if (ctx->timeout_id) {
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+ }
+}
+
+static void
+setup_required_nmea_traces_context_free (SetupRequiredNmeaTracesContext *ctx)
+{
+ setup_required_nmea_traces_cleanup_action (ctx);
+ g_clear_object (&ctx->client);
+ g_slice_free (SetupRequiredNmeaTracesContext, ctx);
+}
+
+static gboolean
+setup_required_nmea_traces_finish (MMSharedQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+setup_required_nmea_traces_timeout (GTask *task)
+{
+ SetupRequiredNmeaTracesContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->timeout_id);
+ setup_required_nmea_traces_cleanup_action (ctx);
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Operation timed out");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+loc_set_nmea_types_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocSetNmeaTypesOutput *output,
+ GTask *task)
+{
+ SetupRequiredNmeaTracesContext *ctx;
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->indication_id);
+ setup_required_nmea_traces_cleanup_action (ctx);
+
+ if (!qmi_indication_loc_set_nmea_types_output_get_indication_status (output, &status, &error) ||
+ !mm_error_from_qmi_loc_indication_status (status, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+loc_set_nmea_types_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetupRequiredNmeaTracesContext *ctx;
+ GError *error = NULL;
+ g_autoptr(QmiMessageLocSetNmeaTypesOutput) output = NULL;
+
+ output = qmi_client_loc_set_nmea_types_finish (client, res, &error);
+ if (!output || !qmi_message_loc_set_nmea_types_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx = g_task_get_task_data (task);
+ g_assert (!ctx->indication_id);
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "set-nmea-types",
+ G_CALLBACK (loc_set_nmea_types_indication_cb),
+ task);
+ g_assert (!ctx->timeout_id);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)setup_required_nmea_traces_timeout,
+ task);
+}
+
+static void
+loc_get_nmea_types_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocGetNmeaTypesOutput *output,
+ GTask *task)
+{
+ SetupRequiredNmeaTracesContext *ctx;
+ QmiLocIndicationStatus status;
+ QmiLocNmeaType nmea_types_mask = 0;
+ QmiLocNmeaType desired_nmea_types_mask = (QMI_LOC_NMEA_TYPE_GGA | QMI_LOC_NMEA_TYPE_GSA | QMI_LOC_NMEA_TYPE_GSV);
+ GError *error = NULL;
+ g_autoptr(QmiMessageLocSetNmeaTypesInput) input = NULL;
+
+ ctx = g_task_get_task_data (task);
+ g_assert (ctx->indication_id);
+ setup_required_nmea_traces_cleanup_action (ctx);
+
+ if (!qmi_indication_loc_get_nmea_types_output_get_indication_status (output, &status, &error) ||
+ !mm_error_from_qmi_loc_indication_status (status, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ qmi_indication_loc_get_nmea_types_output_get_nmea_types (output, &nmea_types_mask, NULL);
+
+ /* If the configured NMEA types already include GGA, GSV and GSA, we're fine. For raw
+ * GPS sources GGA is the only required one, the other two are given for completeness */
+ if ((nmea_types_mask & desired_nmea_types_mask) == desired_nmea_types_mask) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ input = qmi_message_loc_set_nmea_types_input_new ();
+ qmi_message_loc_set_nmea_types_input_set_nmea_types (input, (nmea_types_mask | desired_nmea_types_mask), NULL);
+ qmi_client_loc_set_nmea_types (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)loc_set_nmea_types_ready,
+ task);
+}
+
+static void
+loc_get_nmea_types_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetupRequiredNmeaTracesContext *ctx;
+ GError *error = NULL;
+ g_autoptr(QmiMessageLocGetNmeaTypesOutput) output = NULL;
+
+ output = qmi_client_loc_get_nmea_types_finish (client, res, &error);
+ if (!output || !qmi_message_loc_get_nmea_types_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx = g_task_get_task_data (task);
+ g_assert (!ctx->indication_id);
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "get-nmea-types",
+ G_CALLBACK (loc_get_nmea_types_indication_cb),
+ task);
+ g_assert (!ctx->timeout_id);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)setup_required_nmea_traces_timeout,
+ task);
+}
+
+static void
+setup_required_nmea_traces (MMSharedQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If using PDS, no further setup required */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Otherwise LOC */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_LOC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ SetupRequiredNmeaTracesContext *ctx;
+
+ ctx = g_slice_new0 (SetupRequiredNmeaTracesContext);
+ ctx->client = QMI_CLIENT_LOC (g_object_ref (client));
+ g_task_set_task_data (task, ctx, (GDestroyNotify)setup_required_nmea_traces_context_free);
+
+ qmi_client_loc_get_nmea_types (ctx->client,
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)loc_get_nmea_types_ready,
+ task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find any PDS/LOC client");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: internal helper: start gps engine */
+
+static gboolean
+start_gps_engine_finish (MMSharedQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+pds_ser_location_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessagePdsSetEventReportOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_pds_set_event_report_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_set_event_report_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't set event report: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_pds_set_event_report_output_unref (output);
+ return;
+ }
+
+ qmi_message_pds_set_event_report_output_unref (output);
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ g_assert (!priv->pds_client);
+ g_assert (priv->pds_location_event_report_indication_id == 0);
+ priv->pds_client = QMI_CLIENT (g_object_ref (client));
+ priv->pds_location_event_report_indication_id =
+ g_signal_connect (priv->pds_client,
+ "event-report",
+ G_CALLBACK (pds_location_event_report_indication_cb),
+ self);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+pds_auto_tracking_state_start_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdsSetEventReportInput *input;
+ QmiMessagePdsSetAutoTrackingStateOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_pds_set_auto_tracking_state_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_set_auto_tracking_state_output_get_result (output, &error)) {
+ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_prefix_error (&error, "Couldn't set auto-tracking state: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_pds_set_auto_tracking_state_output_unref (output);
+ return;
+ }
+ g_error_free (error);
+ }
+
+ qmi_message_pds_set_auto_tracking_state_output_unref (output);
+
+ /* Only gather standard NMEA traces */
+ input = qmi_message_pds_set_event_report_input_new ();
+ qmi_message_pds_set_event_report_input_set_nmea_position_reporting (input, TRUE, NULL);
+ qmi_client_pds_set_event_report (
+ client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)pds_ser_location_ready,
+ task);
+ qmi_message_pds_set_event_report_input_unref (input);
+}
+
+static void
+pds_gps_service_state_start_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessagePdsSetAutoTrackingStateInput *input;
+ QmiMessagePdsSetGpsServiceStateOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_pds_set_gps_service_state_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_set_gps_service_state_output_get_result (output, &error)) {
+ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_prefix_error (&error, "Couldn't set GPS service state: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_pds_set_gps_service_state_output_unref (output);
+ return;
+ }
+ g_error_free (error);
+ }
+
+ qmi_message_pds_set_gps_service_state_output_unref (output);
+
+ /* Enable auto-tracking for a continuous fix */
+ input = qmi_message_pds_set_auto_tracking_state_input_new ();
+ qmi_message_pds_set_auto_tracking_state_input_set_state (input, TRUE, NULL);
+ qmi_client_pds_set_auto_tracking_state (
+ client,
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_auto_tracking_state_start_ready,
+ task);
+ qmi_message_pds_set_auto_tracking_state_input_unref (input);
+}
+
+static void
+loc_register_events_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessageLocRegisterEventsOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_loc_register_events_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_register_events_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't not register tracking events: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_register_events_output_unref (output);
+ return;
+ }
+
+ qmi_message_loc_register_events_output_unref (output);
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ g_assert (!priv->loc_client);
+ g_assert (!priv->loc_location_nmea_indication_id);
+ priv->loc_client = QMI_CLIENT (g_object_ref (client));
+ priv->loc_location_nmea_indication_id =
+ g_signal_connect (client,
+ "nmea",
+ G_CALLBACK (loc_location_nmea_indication_cb),
+ self);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+loc_start_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageLocRegisterEventsInput *input;
+ QmiMessageLocStartOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_loc_start_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_start_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't start GPS engine: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_start_output_unref (output);
+ return;
+ }
+
+ qmi_message_loc_start_output_unref (output);
+
+ input = qmi_message_loc_register_events_input_new ();
+ qmi_message_loc_register_events_input_set_event_registration_mask (
+ input, QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA, NULL);
+ qmi_client_loc_register_events (client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) loc_register_events_ready,
+ task);
+ qmi_message_loc_register_events_input_unref (input);
+}
+
+static void
+start_gps_engine (MMSharedQmi *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Prefer PDS */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ QmiMessagePdsSetGpsServiceStateInput *input;
+
+ input = qmi_message_pds_set_gps_service_state_input_new ();
+ qmi_message_pds_set_gps_service_state_input_set_state (input, TRUE, NULL);
+ qmi_client_pds_set_gps_service_state (
+ QMI_CLIENT_PDS (client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_gps_service_state_start_ready,
+ task);
+ qmi_message_pds_set_gps_service_state_input_unref (input);
+ return;
+ }
+
+ /* Otherwise LOC */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_LOC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ QmiMessageLocStartInput *input;
+
+ input = qmi_message_loc_start_input_new ();
+ qmi_message_loc_start_input_set_session_id (input, DEFAULT_LOC_SESSION_ID, NULL);
+ qmi_message_loc_start_input_set_intermediate_report_state (input, QMI_LOC_INTERMEDIATE_REPORT_STATE_DISABLE, NULL);
+ qmi_message_loc_start_input_set_minimum_interval_between_position_reports (input, 1000, NULL);
+ qmi_message_loc_start_input_set_fix_recurrence_type (input, QMI_LOC_FIX_RECURRENCE_TYPE_REQUEST_PERIODIC_FIXES, NULL);
+ qmi_client_loc_start (QMI_CLIENT_LOC (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) loc_start_ready,
+ task);
+ qmi_message_loc_start_input_unref (input);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find any PDS/LOC client");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: internal helper: select operation mode (msa/msb/standalone) */
+
+typedef enum {
+ GPS_OPERATION_MODE_UNKNOWN,
+ GPS_OPERATION_MODE_STANDALONE,
+ GPS_OPERATION_MODE_AGPS_MSA,
+ GPS_OPERATION_MODE_AGPS_MSB,
+} GpsOperationMode;
+
+typedef struct {
+ QmiClient *client;
+ GpsOperationMode mode;
+ glong indication_id;
+ guint timeout_id;
+} SetGpsOperationModeContext;
+
+static void
+set_gps_operation_mode_context_free (SetGpsOperationModeContext *ctx)
+{
+ if (ctx->client) {
+ if (ctx->timeout_id)
+ g_source_remove (ctx->timeout_id);
+ if (ctx->indication_id)
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ g_object_unref (ctx->client);
+ }
+ g_slice_free (SetGpsOperationModeContext, ctx);
+}
+
+static gboolean
+set_gps_operation_mode_finish (MMSharedQmi *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+pds_set_default_tracking_session_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ SetGpsOperationModeContext *ctx;
+ QmiMessagePdsSetDefaultTrackingSessionOutput *output;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_pds_set_default_tracking_session_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_set_default_tracking_session_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't set default tracking session: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_pds_set_default_tracking_session_output_unref (output);
+ return;
+ }
+
+ qmi_message_pds_set_default_tracking_session_output_unref (output);
+
+ switch (ctx->mode) {
+ case GPS_OPERATION_MODE_AGPS_MSA:
+ mm_obj_dbg (self, "MSA A-GPS operation mode enabled");
+ break;
+ case GPS_OPERATION_MODE_AGPS_MSB:
+ mm_obj_dbg (self, "MSB A-GPS operation mode enabled");
+ break;
+ case GPS_OPERATION_MODE_STANDALONE:
+ mm_obj_dbg (self, "standalone mode enabled (A-GPS disabled)");
+ break;
+ case GPS_OPERATION_MODE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+pds_get_default_tracking_session_ready (QmiClientPds *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ SetGpsOperationModeContext *ctx;
+ QmiMessagePdsSetDefaultTrackingSessionInput *input;
+ QmiMessagePdsGetDefaultTrackingSessionOutput *output;
+ GError *error = NULL;
+ QmiPdsOperatingMode session_operation;
+ guint8 data_timeout;
+ guint32 interval;
+ guint32 accuracy_threshold;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_pds_get_default_tracking_session_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_pds_get_default_tracking_session_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get default tracking session: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_pds_get_default_tracking_session_output_unref (output);
+ return;
+ }
+
+ qmi_message_pds_get_default_tracking_session_output_get_info (
+ output,
+ &session_operation,
+ &data_timeout,
+ &interval,
+ &accuracy_threshold,
+ NULL);
+
+ qmi_message_pds_get_default_tracking_session_output_unref (output);
+
+ if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSA) {
+ if (session_operation == QMI_PDS_OPERATING_MODE_MS_ASSISTED) {
+ mm_obj_dbg (self, "MSA A-GPS already enabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "need to enable MSA A-GPS");
+ session_operation = QMI_PDS_OPERATING_MODE_MS_ASSISTED;
+ } else if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSB) {
+ if (session_operation == QMI_PDS_OPERATING_MODE_MS_BASED) {
+ mm_obj_dbg (self, "MSB A-GPS already enabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "need to enable MSB A-GPS");
+ session_operation = QMI_PDS_OPERATING_MODE_MS_BASED;
+ } else if (ctx->mode == GPS_OPERATION_MODE_STANDALONE) {
+ if (session_operation == QMI_PDS_OPERATING_MODE_STANDALONE) {
+ mm_obj_dbg (self, "A-GPS already disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "need to disable A-GPS");
+ session_operation = QMI_PDS_OPERATING_MODE_STANDALONE;
+ } else
+ g_assert_not_reached ();
+
+ input = qmi_message_pds_set_default_tracking_session_input_new ();
+ qmi_message_pds_set_default_tracking_session_input_set_info (
+ input,
+ session_operation,
+ data_timeout,
+ interval,
+ accuracy_threshold,
+ NULL);
+ qmi_client_pds_set_default_tracking_session (
+ client,
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_set_default_tracking_session_ready,
+ task);
+ qmi_message_pds_set_default_tracking_session_input_unref (input);
+}
+
+static gboolean
+loc_location_operation_mode_indication_timed_out (GTask *task)
+{
+ SetGpsOperationModeContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->timeout_id = 0;
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Failed to receive operation mode indication");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+loc_location_set_operation_mode_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocSetOperationModeOutput *output,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ SetGpsOperationModeContext *ctx;
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_loc_set_operation_mode_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!mm_error_from_qmi_loc_indication_status (status, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ switch (ctx->mode) {
+ case GPS_OPERATION_MODE_AGPS_MSA:
+ mm_obj_dbg (self, "MSA A-GPS operation mode enabled");
+ break;
+ case GPS_OPERATION_MODE_AGPS_MSB:
+ mm_obj_dbg (self, "MSB A-GPS operation mode enabled");
+ break;
+ case GPS_OPERATION_MODE_STANDALONE:
+ mm_obj_dbg (self, "standalone mode enabled (A-GPS disabled)");
+ break;
+ case GPS_OPERATION_MODE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+loc_set_operation_mode_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetGpsOperationModeContext *ctx;
+ QmiMessageLocSetOperationModeOutput *output;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_set_operation_mode_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_set_operation_mode_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_set_operation_mode_output_unref (output);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "set-operation-mode",
+ G_CALLBACK (loc_location_set_operation_mode_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_operation_mode_indication_timed_out,
+ task);
+
+ qmi_message_loc_set_operation_mode_output_unref (output);
+}
+
+static void
+loc_location_get_operation_mode_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocGetOperationModeOutput *output,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ SetGpsOperationModeContext *ctx;
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+ QmiLocOperationMode mode = QMI_LOC_OPERATION_MODE_DEFAULT;
+ QmiMessageLocSetOperationModeInput *input;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (!qmi_indication_loc_get_operation_mode_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!mm_error_from_qmi_loc_indication_status (status, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ qmi_indication_loc_get_operation_mode_output_get_operation_mode (output, &mode, NULL);
+
+ if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSA) {
+ if (mode == QMI_LOC_OPERATION_MODE_MSA) {
+ mm_obj_dbg (self, "MSA A-GPS already enabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "need to enable MSA A-GPS");
+ mode = QMI_LOC_OPERATION_MODE_MSA;
+ } else if (ctx->mode == GPS_OPERATION_MODE_AGPS_MSB) {
+ if (mode == QMI_LOC_OPERATION_MODE_MSB) {
+ mm_obj_dbg (self, "MSB A-GPS already enabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "need to enable MSB A-GPS");
+ mode = QMI_LOC_OPERATION_MODE_MSB;
+ } else if (ctx->mode == GPS_OPERATION_MODE_STANDALONE) {
+ if (mode == QMI_LOC_OPERATION_MODE_STANDALONE) {
+ mm_obj_dbg (self, "A-GPS already disabled");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+ mm_obj_dbg (self, "need to disable A-GPS");
+ mode = QMI_LOC_OPERATION_MODE_STANDALONE;
+ } else
+ g_assert_not_reached ();
+
+ if (ctx->timeout_id) {
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+ }
+
+ if (ctx->indication_id) {
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ ctx->indication_id = 0;
+ }
+
+ input = qmi_message_loc_set_operation_mode_input_new ();
+ qmi_message_loc_set_operation_mode_input_set_operation_mode (input, mode, NULL);
+ qmi_client_loc_set_operation_mode (
+ QMI_CLIENT_LOC (ctx->client),
+ input,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)loc_set_operation_mode_ready,
+ task);
+ qmi_message_loc_set_operation_mode_input_unref (input);
+}
+
+static void
+loc_get_operation_mode_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetGpsOperationModeContext *ctx;
+ QmiMessageLocGetOperationModeOutput *output;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_get_operation_mode_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_get_operation_mode_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_get_operation_mode_output_unref (output);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "get-operation-mode",
+ G_CALLBACK (loc_location_get_operation_mode_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_operation_mode_indication_timed_out,
+ task);
+
+ qmi_message_loc_get_operation_mode_output_unref (output);
+}
+
+static void
+set_gps_operation_mode (MMSharedQmi *self,
+ GpsOperationMode mode,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SetGpsOperationModeContext *ctx;
+ GTask *task;
+ QmiClient *client;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (SetGpsOperationModeContext);
+ ctx->mode = mode;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_gps_operation_mode_context_free);
+
+ /* Prefer PDS */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_PDS,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ ctx->client = g_object_ref (client);
+ qmi_client_pds_get_default_tracking_session (
+ QMI_CLIENT_PDS (ctx->client),
+ NULL,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)pds_get_default_tracking_session_ready,
+ task);
+ return;
+ }
+
+ /* Otherwise LOC */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_LOC,
+ MM_PORT_QMI_FLAG_DEFAULT,
+ NULL);
+ if (client) {
+ ctx->client = g_object_ref (client);
+ qmi_client_loc_get_operation_mode (
+ QMI_CLIENT_LOC (ctx->client),
+ NULL,
+ 10,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)loc_get_operation_mode_ready,
+ task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't find any PDS/LOC client");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: disable */
+
+gboolean
+mm_shared_qmi_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+stop_gps_engine_ready (MMSharedQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ Private *priv;
+ GError *error = NULL;
+
+ if (!stop_gps_engine_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv = get_private (self);
+
+ priv->enabled_sources &= ~source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+set_gps_operation_mode_standalone_ready (MMSharedQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ Private *priv;
+ GError *error = NULL;
+
+ if (!set_gps_operation_mode_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv = get_private (self);
+
+ priv->enabled_sources &= ~source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_disable_location_gathering (MMIfaceModemLocation *_self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ GTask *task;
+ MMModemLocationSource tmp;
+
+ self = MM_SHARED_QMI (_self);
+ priv = get_private (self);
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ /* NOTE: no parent disable_location_gathering() implementation */
+
+ if (!(source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ g_assert (!(priv->pds_client && priv->loc_client));
+
+ /* Disable A-GPS? */
+ if (source == MM_MODEM_LOCATION_SOURCE_AGPS_MSA || source == MM_MODEM_LOCATION_SOURCE_AGPS_MSB) {
+ set_gps_operation_mode (self,
+ GPS_OPERATION_MODE_STANDALONE,
+ (GAsyncReadyCallback)set_gps_operation_mode_standalone_ready,
+ task);
+ return;
+ }
+
+ /* If no more GPS sources enabled, stop GPS */
+ tmp = priv->enabled_sources;
+ tmp &= ~source;
+ if (!(tmp & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) {
+ stop_gps_engine (self,
+ (GAsyncReadyCallback)stop_gps_engine_ready,
+ task);
+ return;
+ }
+
+ /* Otherwise, we have more GPS sources enabled, we shouldn't stop GPS, just
+ * return */
+ priv->enabled_sources &= ~source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: enable */
+
+gboolean
+mm_shared_qmi_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+start_gps_engine_ready (MMSharedQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ Private *priv;
+ GError *error = NULL;
+
+ if (!start_gps_engine_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv = get_private (self);
+
+ priv->enabled_sources |= source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+setup_required_nmea_traces_ready (MMSharedQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(GError) error = NULL;
+
+ /* don't treat this error as fatal */
+ if (!setup_required_nmea_traces_finish (self, res, &error))
+ mm_obj_warn (self, "couldn't setup required NMEA traces: %s", error->message);
+
+ start_gps_engine (self,
+ (GAsyncReadyCallback)start_gps_engine_ready,
+ task);
+}
+
+static void
+set_gps_operation_mode_agps_ready (MMSharedQmi *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource source;
+ Private *priv;
+ GError *error = NULL;
+
+ if (!set_gps_operation_mode_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+ priv = get_private (self);
+
+ priv->enabled_sources |= source;
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_enable_location_gathering_ready (MMIfaceModemLocation *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self = MM_SHARED_QMI (_self);
+ Private *priv;
+ MMModemLocationSource source;
+ GError *error = NULL;
+
+ priv = get_private (self);
+
+ if (!priv->iface_modem_location_parent->enable_location_gathering_finish (_self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ source = (MMModemLocationSource) GPOINTER_TO_UINT (g_task_get_task_data (task));
+
+ /* We only consider GPS related sources in this shared QMI implementation */
+ if (!(source & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB))) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Enabling MSA A-GPS? */
+ if (source == MM_MODEM_LOCATION_SOURCE_AGPS_MSA) {
+ set_gps_operation_mode (self,
+ GPS_OPERATION_MODE_AGPS_MSA,
+ (GAsyncReadyCallback)set_gps_operation_mode_agps_ready,
+ task);
+ return;
+ }
+
+ /* Enabling MSB A-GPS? */
+ if (source == MM_MODEM_LOCATION_SOURCE_AGPS_MSB) {
+ set_gps_operation_mode (self,
+ GPS_OPERATION_MODE_AGPS_MSB,
+ (GAsyncReadyCallback)set_gps_operation_mode_agps_ready,
+ task);
+ return;
+ }
+
+ /* Only setup NMEA traces and start GPS engine if not done already */
+ if (!(priv->enabled_sources & (MM_MODEM_LOCATION_SOURCE_GPS_NMEA | MM_MODEM_LOCATION_SOURCE_GPS_RAW))) {
+ setup_required_nmea_traces (self,
+ (GAsyncReadyCallback)setup_required_nmea_traces_ready,
+ task);
+ return;
+ }
+
+ /* GPS already started, we're done */
+ priv->enabled_sources |= source;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_enable_location_gathering (MMIfaceModemLocation *self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, GUINT_TO_POINTER (source), NULL);
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering);
+ g_assert (priv->iface_modem_location_parent->enable_location_gathering_finish);
+
+ /* Chain up parent's gathering enable */
+ priv->iface_modem_location_parent->enable_location_gathering (
+ self,
+ source,
+ (GAsyncReadyCallback)parent_enable_location_gathering_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Location: load capabilities */
+
+MMModemLocationSource
+mm_shared_qmi_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_SOURCE_NONE;
+ }
+ return (MMModemLocationSource)value;
+}
+
+static void
+parent_load_capabilities_ready (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMModemLocationSource sources;
+ GError *error = NULL;
+ Private *priv;
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ sources = priv->iface_modem_location_parent->load_capabilities_finish (self, res, &error);
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Now our own checks */
+
+ /* If we have support for the PDS or LOC client, GPS and A-GPS location is supported */
+ if ((mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_PDS, MM_PORT_QMI_FLAG_DEFAULT, NULL)) ||
+ (mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL)))
+ sources |= (MM_MODEM_LOCATION_SOURCE_GPS_NMEA |
+ MM_MODEM_LOCATION_SOURCE_GPS_RAW |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSA |
+ MM_MODEM_LOCATION_SOURCE_AGPS_MSB);
+
+ /* So we're done, complete */
+ g_task_return_int (task, sources);
+ g_object_unref (task);
+}
+
+void
+mm_shared_qmi_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->iface_modem_location_parent);
+ g_assert (priv->iface_modem_location_parent->load_capabilities);
+ g_assert (priv->iface_modem_location_parent->load_capabilities_finish);
+
+ priv->iface_modem_location_parent->load_capabilities (self,
+ (GAsyncReadyCallback)parent_load_capabilities_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Location: load supported assistance data */
+
+gchar **
+mm_shared_qmi_location_load_assistance_data_servers_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+mm_shared_qmi_location_load_assistance_data_servers (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ GTask *task;
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_return_pointer (task, g_strdupv (priv->loc_assistance_data_servers), (GDestroyNotify) g_strfreev);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Location: load supported assistance data */
+
+typedef struct {
+ QmiClientLoc *client;
+ glong indication_id;
+ guint timeout_id;
+} LoadSupportedAssistanceDataContext;
+
+static void
+load_supported_assistance_data_context_free (LoadSupportedAssistanceDataContext *ctx)
+{
+ if (ctx->client) {
+ if (ctx->timeout_id)
+ g_source_remove (ctx->timeout_id);
+ if (ctx->indication_id)
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ g_object_unref (ctx->client);
+ }
+ g_slice_free (LoadSupportedAssistanceDataContext, ctx);
+}
+
+MMModemLocationAssistanceDataType
+mm_shared_qmi_location_load_supported_assistance_data_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE;
+ }
+ return (MMModemLocationAssistanceDataType)value;
+}
+
+static gboolean
+loc_location_get_predicted_orbits_data_source_indication_timed_out (GTask *task)
+{
+ LoadSupportedAssistanceDataContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->timeout_id = 0;
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Failed to receive indication with the predicted orbits data source");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocGetPredictedOrbitsDataSourceOutput *output,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+ GArray *server_list = NULL;
+ gboolean supported = FALSE;
+
+ if (!qmi_indication_loc_get_predicted_orbits_data_source_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ if (!mm_error_from_qmi_loc_indication_status (status, &error))
+ goto out;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+
+ if (qmi_indication_loc_get_predicted_orbits_data_source_output_get_server_list (
+ output,
+ &server_list,
+ NULL) &&
+ server_list->len > 0) {
+ guint i;
+ GPtrArray *tmp;
+
+ tmp = g_ptr_array_sized_new (server_list->len + 1);
+ for (i = 0; i < server_list->len; i++) {
+ const gchar *server;
+
+ server = g_array_index (server_list, gchar *, i);
+ g_ptr_array_add (tmp, g_strdup (server));
+ }
+ g_ptr_array_add (tmp, NULL);
+
+ g_assert (!priv->loc_assistance_data_servers);
+ priv->loc_assistance_data_servers = (gchar **) g_ptr_array_free (tmp, FALSE);
+
+ supported = TRUE;
+ }
+
+ if (qmi_indication_loc_get_predicted_orbits_data_source_output_get_allowed_sizes (
+ output,
+ &priv->loc_assistance_data_max_file_size,
+ &priv->loc_assistance_data_max_part_size,
+ NULL) &&
+ priv->loc_assistance_data_max_file_size > 0 &&
+ priv->loc_assistance_data_max_part_size > 0) {
+ supported = TRUE;
+ }
+
+out:
+ if (error)
+ g_task_return_error (task, error);
+ else if (!supported)
+ g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
+ else
+ g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA);
+ g_object_unref (task);
+}
+
+static void
+loc_location_get_predicted_orbits_data_source_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadSupportedAssistanceDataContext *ctx;
+ QmiMessageLocGetPredictedOrbitsDataSourceOutput *output;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_get_predicted_orbits_data_source_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!qmi_message_loc_get_predicted_orbits_data_source_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ qmi_message_loc_get_predicted_orbits_data_source_output_unref (output);
+ return;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "get-predicted-orbits-data-source",
+ G_CALLBACK (loc_location_get_predicted_orbits_data_source_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_get_predicted_orbits_data_source_indication_timed_out,
+ task);
+
+ qmi_message_loc_get_predicted_orbits_data_source_output_unref (output);
+}
+
+void
+mm_shared_qmi_location_load_supported_assistance_data (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadSupportedAssistanceDataContext *ctx;
+ GTask *task;
+ QmiClient *client;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* If no LOC client, no assistance data right away */
+ client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), QMI_SERVICE_LOC, MM_PORT_QMI_FLAG_DEFAULT, NULL);
+ if (!client) {
+ g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_slice_new0 (LoadSupportedAssistanceDataContext);
+ ctx->client = QMI_CLIENT_LOC (g_object_ref (client));
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_supported_assistance_data_context_free);
+
+ qmi_client_loc_get_predicted_orbits_data_source (ctx->client,
+ NULL,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)loc_location_get_predicted_orbits_data_source_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Location: inject assistance data */
+
+#define MAX_BYTES_PER_REQUEST 1024
+
+typedef struct {
+ QmiClientLoc *client;
+ guint8 *data;
+ goffset data_size;
+ gulong total_parts;
+ guint32 part_size;
+ glong indication_id;
+ guint timeout_id;
+ goffset i;
+ gulong n_part;
+} InjectAssistanceDataContext;
+
+static void
+inject_assistance_data_context_free (InjectAssistanceDataContext *ctx)
+{
+ if (ctx->client) {
+ if (ctx->timeout_id)
+ g_source_remove (ctx->timeout_id);
+ if (ctx->indication_id)
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ g_object_unref (ctx->client);
+ }
+ g_free (ctx->data);
+ g_slice_free (InjectAssistanceDataContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_location_inject_assistance_data_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static gboolean
+loc_location_inject_data_indication_timed_out (GTask *task)
+{
+ InjectAssistanceDataContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ ctx->timeout_id = 0;
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
+ "Failed to receive indication with the server update result");
+ g_object_unref (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void inject_xtra_data_next (GTask *task);
+
+static void
+loc_location_inject_xtra_data_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocInjectXtraDataOutput *output,
+ GTask *task)
+{
+ InjectAssistanceDataContext *ctx;
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+
+ if (!qmi_indication_loc_inject_xtra_data_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ mm_error_from_qmi_loc_indication_status (status, &error);
+
+out:
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ ctx->indication_id = 0;
+
+ inject_xtra_data_next (task);
+}
+
+static void
+inject_xtra_data_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageLocInjectXtraDataOutput *output;
+ InjectAssistanceDataContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_inject_xtra_data_finish (client, res, &error);
+ if (!output || !qmi_message_loc_inject_xtra_data_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "inject-xtra-data",
+ G_CALLBACK (loc_location_inject_xtra_data_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_inject_data_indication_timed_out,
+ task);
+out:
+ if (output)
+ qmi_message_loc_inject_xtra_data_output_unref (output);
+}
+
+static void
+inject_xtra_data_next (GTask *task)
+{
+ MMSharedQmi *self;
+ QmiMessageLocInjectXtraDataInput *input;
+ InjectAssistanceDataContext *ctx;
+ goffset total_bytes_left;
+ gsize count;
+ GArray *data;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->data_size >= ctx->i);
+ total_bytes_left = ctx->data_size - ctx->i;
+ if (total_bytes_left == 0) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->n_part++;
+ count = (total_bytes_left >= ctx->part_size) ? ctx->part_size : total_bytes_left;
+
+ input = qmi_message_loc_inject_xtra_data_input_new ();
+ qmi_message_loc_inject_xtra_data_input_set_total_size (
+ input,
+ (guint32)ctx->data_size,
+ NULL);
+ qmi_message_loc_inject_xtra_data_input_set_total_parts (
+ input,
+ (guint16)ctx->total_parts,
+ NULL);
+ qmi_message_loc_inject_xtra_data_input_set_part_number (
+ input,
+ (guint16)ctx->n_part,
+ NULL);
+ data = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), count), &(ctx->data[ctx->i]), count);
+ qmi_message_loc_inject_xtra_data_input_set_part_data (
+ input,
+ data,
+ NULL);
+ g_array_unref (data);
+
+ ctx->i += count;
+
+ mm_obj_info (self, "injecting xtra data: %" G_GSIZE_FORMAT " bytes (%u/%u)",
+ count, (guint) ctx->n_part, (guint) ctx->total_parts);
+ qmi_client_loc_inject_xtra_data (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) inject_xtra_data_ready,
+ task);
+
+ qmi_message_loc_inject_xtra_data_input_unref (input);
+}
+
+static void
+inject_xtra_data (GTask *task)
+{
+ InjectAssistanceDataContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->timeout_id == 0);
+ g_assert (ctx->indication_id == 0);
+
+ ctx->n_part = 0;
+ ctx->i = 0;
+
+ inject_xtra_data_next (task);
+}
+
+static void inject_assistance_data_next (GTask *task);
+
+static void
+loc_location_inject_predicted_orbits_data_indication_cb (QmiClientLoc *client,
+ QmiIndicationLocInjectPredictedOrbitsDataOutput *output,
+ GTask *task)
+{
+ InjectAssistanceDataContext *ctx;
+ QmiLocIndicationStatus status;
+ GError *error = NULL;
+
+ if (!qmi_indication_loc_inject_predicted_orbits_data_output_get_indication_status (output, &status, &error)) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ mm_error_from_qmi_loc_indication_status (status, &error);
+
+out:
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx = g_task_get_task_data (task);
+
+ g_source_remove (ctx->timeout_id);
+ ctx->timeout_id = 0;
+
+ g_signal_handler_disconnect (ctx->client, ctx->indication_id);
+ ctx->indication_id = 0;
+
+ inject_assistance_data_next (task);
+}
+
+static void
+inject_predicted_orbits_data_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageLocInjectPredictedOrbitsDataOutput *output;
+ InjectAssistanceDataContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_loc_inject_predicted_orbits_data_finish (client, res, &error);
+ if (!output || !qmi_message_loc_inject_predicted_orbits_data_output_get_result (output, &error)) {
+ /* Try with InjectXtra if InjectPredictedOrbits is unsupported */
+ if (g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NOT_SUPPORTED)) {
+ g_error_free (error);
+ inject_xtra_data (task);
+ goto out;
+ }
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* The task ownership is shared between signal and timeout; the one which is
+ * scheduled first will cancel the other. */
+ ctx->indication_id = g_signal_connect (ctx->client,
+ "inject-predicted-orbits-data",
+ G_CALLBACK (loc_location_inject_predicted_orbits_data_indication_cb),
+ task);
+ ctx->timeout_id = g_timeout_add_seconds (10,
+ (GSourceFunc)loc_location_inject_data_indication_timed_out,
+ task);
+out:
+ if (output)
+ qmi_message_loc_inject_predicted_orbits_data_output_unref (output);
+}
+
+static void
+inject_assistance_data_next (GTask *task)
+{
+ MMSharedQmi *self;
+ QmiMessageLocInjectPredictedOrbitsDataInput *input;
+ InjectAssistanceDataContext *ctx;
+ goffset total_bytes_left;
+ gsize count;
+ GArray *data;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ g_assert (ctx->data_size >= ctx->i);
+ total_bytes_left = ctx->data_size - ctx->i;
+ if (total_bytes_left == 0) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->n_part++;
+ count = (total_bytes_left >= ctx->part_size) ? ctx->part_size : total_bytes_left;
+
+ input = qmi_message_loc_inject_predicted_orbits_data_input_new ();
+ qmi_message_loc_inject_predicted_orbits_data_input_set_format_type (
+ input,
+ QMI_LOC_PREDICTED_ORBITS_DATA_FORMAT_XTRA,
+ NULL);
+ qmi_message_loc_inject_predicted_orbits_data_input_set_total_size (
+ input,
+ (guint32)ctx->data_size,
+ NULL);
+ qmi_message_loc_inject_predicted_orbits_data_input_set_total_parts (
+ input,
+ (guint16)ctx->total_parts,
+ NULL);
+ qmi_message_loc_inject_predicted_orbits_data_input_set_part_number (
+ input,
+ (guint16)ctx->n_part,
+ NULL);
+ data = g_array_append_vals (g_array_sized_new (FALSE, FALSE, sizeof (guint8), count), &(ctx->data[ctx->i]), count);
+ qmi_message_loc_inject_predicted_orbits_data_input_set_part_data (
+ input,
+ data,
+ NULL);
+ g_array_unref (data);
+
+ ctx->i += count;
+
+ mm_obj_info (self, "injecting predicted orbits data: %" G_GSIZE_FORMAT " bytes (%u/%u)",
+ count, (guint) ctx->n_part, (guint) ctx->total_parts);
+ qmi_client_loc_inject_predicted_orbits_data (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) inject_predicted_orbits_data_ready,
+ task);
+
+ qmi_message_loc_inject_predicted_orbits_data_input_unref (input);
+}
+
+void
+mm_shared_qmi_location_inject_assistance_data (MMIfaceModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InjectAssistanceDataContext *ctx;
+ QmiClient *client;
+ GTask *task;
+ Private *priv;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_LOC, &client,
+ callback, user_data))
+ return;
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_slice_new0 (InjectAssistanceDataContext);
+ ctx->client = QMI_CLIENT_LOC (g_object_ref (client));
+ ctx->data = g_memdup (data, data_size);
+ ctx->data_size = data_size;
+ ctx->part_size = ((priv->loc_assistance_data_max_part_size > 0) ? priv->loc_assistance_data_max_part_size : MAX_BYTES_PER_REQUEST);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) inject_assistance_data_context_free);
+
+ if ((ctx->data_size > (G_MAXUINT16 * ctx->part_size)) ||
+ ((priv->loc_assistance_data_max_file_size > 0) && (ctx->data_size > priv->loc_assistance_data_max_file_size))) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY,
+ "Assistance data file is too big");
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->total_parts = (ctx->data_size / ctx->part_size);
+ if (ctx->data_size % ctx->part_size)
+ ctx->total_parts++;
+ g_assert (ctx->total_parts <= G_MAXUINT16);
+
+ mm_obj_dbg (self, "injecting gpsOneXTRA data (%" G_GOFFSET_FORMAT " bytes)...", ctx->data_size);
+
+ inject_assistance_data_next (task);
+}
+
+/*****************************************************************************/
+
+QmiClient *
+mm_shared_qmi_peek_client (MMSharedQmi *self,
+ QmiService service,
+ MMPortQmiFlag flag,
+ GError **error)
+{
+ g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_client);
+ return MM_SHARED_QMI_GET_INTERFACE (self)->peek_client (self, service, flag, error);
+}
+
+gboolean
+mm_shared_qmi_ensure_client (MMSharedQmi *self,
+ QmiService service,
+ QmiClient **o_client,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ QmiClient *client;
+
+ client = mm_shared_qmi_peek_client (self, service, MM_PORT_QMI_FLAG_DEFAULT, &error);
+ if (!client) {
+ g_task_report_error (self, callback, user_data, mm_shared_qmi_ensure_client, error);
+ return FALSE;
+ }
+
+ *o_client = client;
+ return TRUE;
+}
+
+static void
+shared_qmi_init (gpointer g_iface)
+{
+}
+
+GType
+mm_shared_qmi_get_type (void)
+{
+ static GType shared_qmi_type = 0;
+
+ if (!G_UNLIKELY (shared_qmi_type)) {
+ static const GTypeInfo info = {
+ sizeof (MMSharedQmi), /* class_size */
+ shared_qmi_init, /* base_init */
+ NULL, /* base_finalize */
+ };
+
+ shared_qmi_type = g_type_register_static (G_TYPE_INTERFACE, "MMSharedQmi", &info, 0);
+ g_type_interface_add_prerequisite (shared_qmi_type, MM_TYPE_IFACE_MODEM);
+ g_type_interface_add_prerequisite (shared_qmi_type, MM_TYPE_IFACE_MODEM_3GPP);
+ g_type_interface_add_prerequisite (shared_qmi_type, MM_TYPE_IFACE_MODEM_LOCATION);
+ }
+
+ return shared_qmi_type;
+}
diff --git a/src/mm-shared-qmi.h b/src/mm-shared-qmi.h
new file mode 100644
index 00000000..5ffa85c0
--- /dev/null
+++ b/src/mm-shared-qmi.h
@@ -0,0 +1,257 @@
+/* -*- 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) 2018 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SHARED_QMI_H
+#define MM_SHARED_QMI_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+
+#include <libqmi-glib.h>
+
+#include "mm-iface-modem.h"
+#include "mm-iface-modem-3gpp.h"
+#include "mm-iface-modem-location.h"
+#include "mm-port-qmi.h"
+
+#define MM_TYPE_SHARED_QMI (mm_shared_qmi_get_type ())
+#define MM_SHARED_QMI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SHARED_QMI, MMSharedQmi))
+#define MM_IS_SHARED_QMI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SHARED_QMI))
+#define MM_SHARED_QMI_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_SHARED_QMI, MMSharedQmi))
+
+typedef struct _MMSharedQmi MMSharedQmi;
+
+struct _MMSharedQmi {
+ GTypeInterface g_iface;
+
+ QmiClient * (* peek_client) (MMSharedQmi *self,
+ QmiService service,
+ MMPortQmiFlag flag,
+ GError **error);
+
+ /* Peek location interface of the parent class of the object */
+ MMIfaceModemLocation * (* peek_parent_location_interface) (MMSharedQmi *self);
+};
+
+GType mm_shared_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSharedQmi, g_object_unref)
+
+QmiClient *mm_shared_qmi_peek_client (MMSharedQmi *self,
+ QmiService service,
+ MMPortQmiFlag flag,
+ GError **error);
+
+gboolean mm_shared_qmi_ensure_client (MMSharedQmi *self,
+ QmiService service,
+ QmiClient **o_client,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+/* Shared QMI 3GPP operations */
+
+void mm_shared_qmi_3gpp_register_in_network (MMIfaceModem3gpp *self,
+ const gchar *operator_id,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shared QMI device management support */
+
+void mm_shared_qmi_load_supported_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_qmi_load_supported_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_current_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemCapability mm_shared_qmi_load_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_set_current_capabilities (MMIfaceModem *self,
+ MMModemCapability capabilities,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_set_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_model (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *mm_shared_qmi_load_model_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_qmi_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error);
+void mm_shared_qmi_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_supported_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_qmi_load_supported_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_current_bands (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_qmi_load_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_set_current_bands (MMIfaceModem *self,
+ GArray *bands_array,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_set_current_bands_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_factory_reset (MMIfaceModem *self,
+ const gchar *code,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_factory_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_carrier_config (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_load_carrier_config_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ gchar **carrier_config_name,
+ gchar **carrier_config_revision,
+ GError **error);
+void mm_shared_qmi_setup_carrier_config (MMIfaceModem *self,
+ const gchar *imsi,
+ const gchar *carrier_config_mapping,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_setup_carrier_config_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_sim_slots (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_load_sim_slots_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GPtrArray **sim_slots,
+ guint *primary_sim_slot,
+ GError **error);
+void mm_shared_qmi_set_primary_sim_slot (MMIfaceModem *self,
+ guint sim_slot,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_set_primary_sim_slot_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_setup_sim_hot_swap (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_setup_sim_hot_swap_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_fcc_unlock (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_fcc_unlock_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+
+/* Shared QMI location support */
+
+void mm_shared_qmi_location_load_capabilities (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemLocationSource mm_shared_qmi_location_load_capabilities_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_enable_location_gathering (MMIfaceModemLocation *_self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_enable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_disable_location_gathering (MMIfaceModemLocation *_self,
+ MMModemLocationSource source,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_disable_location_gathering_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_location_load_supl_server (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar *mm_shared_qmi_location_load_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_location_set_supl_server (MMIfaceModemLocation *self,
+ const gchar *supl,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_location_set_supl_server_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_location_load_supported_assistance_data (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemLocationAssistanceDataType mm_shared_qmi_location_load_supported_assistance_data_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_location_inject_assistance_data (MMIfaceModemLocation *self,
+ const guint8 *data,
+ gsize data_size,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_location_inject_assistance_data_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_location_load_assistance_data_servers (MMIfaceModemLocation *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gchar **mm_shared_qmi_location_load_assistance_data_servers_finish (MMIfaceModemLocation *self,
+ GAsyncResult *res,
+ GError **error);
+
+#endif /* MM_SHARED_QMI_H */
diff --git a/src/mm-shared.h b/src/mm-shared.h
new file mode 100644
index 00000000..4fc7cef5
--- /dev/null
+++ b/src/mm-shared.h
@@ -0,0 +1,35 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#ifndef MM_SHARED_H
+#define MM_SHARED_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define MM_SHARED_MAJOR_VERSION 1
+#define MM_SHARED_MINOR_VERSION 0
+
+#if defined (G_HAVE_GNUC_VISIBILITY)
+#define VISIBILITY __attribute__((visibility("protected")))
+#else
+#define VISIBILITY
+#endif
+
+#define MM_SHARED_DEFINE_MAJOR_VERSION VISIBILITY int mm_shared_major_version = MM_SHARED_MAJOR_VERSION;
+#define MM_SHARED_DEFINE_MINOR_VERSION VISIBILITY int mm_shared_minor_version = MM_SHARED_MINOR_VERSION;
+#define MM_SHARED_DEFINE_NAME(NAME) VISIBILITY const char *mm_shared_name = #NAME;
+
+#endif /* MM_SHARED_H */
diff --git a/src/mm-sim-mbim.c b/src/mm-sim-mbim.c
index 8d7f3f85..bf662b5b 100644
--- a/src/mm-sim-mbim.c
+++ b/src/mm-sim-mbim.c
@@ -24,8 +24,11 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-broadband-modem-mbim.h"
#include "mm-error-helpers.h"
-#include "mm-log.h"
+#include "mm-iface-modem.h"
+#include "mm-log-object.h"
+#include "mm-modem-helpers-mbim.h"
#include "mm-sim-mbim.h"
G_DEFINE_TYPE (MMSimMbim, mm_sim_mbim, MM_TYPE_BASE_SIM)
@@ -46,16 +49,17 @@ peek_device (gpointer self,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
- port = mm_base_modem_peek_port_mbim (modem);
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem));
g_object_unref (modem);
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek MBIM port");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ peek_device,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek MBIM port");
return FALSE;
}
@@ -71,40 +75,42 @@ load_sim_identifier_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return g_strdup ((gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
simid_subscriber_ready_state_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
- gchar *sim_iccid;
+ gchar *sim_iccid = NULL;
+ g_autofree gchar *raw_iccid = NULL;
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_subscriber_ready_status_response_parse (
response,
NULL, /* ready_state */
NULL, /* subscriber_id */
- &sim_iccid,
+ &raw_iccid,
NULL, /* ready_info */
NULL, /* telephone_numbers_count */
NULL, /* telephone_numbers */
&error))
- g_simple_async_result_set_op_res_gpointer (simple, sim_iccid, (GDestroyNotify)g_free);
+ sim_iccid = mm_3gpp_parse_iccid (raw_iccid, &error);
+
+ if (error)
+ g_task_return_error (task, error);
else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_pointer (task, sim_iccid, g_free);
+
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -114,12 +120,12 @@ load_sim_identifier (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_sim_identifier);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_subscriber_ready_status_query_new (NULL);
mbim_device_command (device,
@@ -127,7 +133,7 @@ load_sim_identifier (MMBaseSim *self,
10,
NULL,
(GAsyncReadyCallback)simid_subscriber_ready_state_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -136,18 +142,16 @@ load_sim_identifier (MMBaseSim *self,
static gchar *
load_imsi_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+ GAsyncResult *res,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
- return g_strdup ((gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
imsi_subscriber_ready_state_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -155,7 +159,7 @@ imsi_subscriber_ready_state_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_subscriber_ready_status_response_parse (
response,
NULL, /* ready_state */
@@ -165,14 +169,13 @@ 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, (GDestroyNotify)g_free);
+ g_task_return_pointer (task, subscriber_id, g_free);
else
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -182,12 +185,12 @@ load_imsi (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_imsi);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_subscriber_ready_status_query_new (NULL);
mbim_device_command (device,
@@ -195,11 +198,275 @@ load_imsi (MMBaseSim *self,
10,
NULL,
(GAsyncReadyCallback)imsi_subscriber_ready_state_ready,
- result);
+ task);
mbim_message_unref (message);
}
/*****************************************************************************/
+/* Load EID */
+
+#define UICC_STATUS_OK 144
+#define EID_APDU_HEADER 5
+
+typedef enum {
+ ESIM_CHECK_STEP_FIRST,
+ ESIM_CHECK_STEP_UICC_OPEN_CHANNEL,
+ ESIM_CHECK_STEP_UICC_GET_APDU,
+ ESIM_CHECK_STEP_UICC_CLOSE_CHANNEL,
+ ESIM_CHECK_STEP_LAST
+} EsimCheckStep;
+
+typedef struct {
+ EsimCheckStep step;
+ guint32 channel;
+ guint32 channel_grp;
+ gchar *eid;
+ GError *saved_error;
+} EsimCheckContext;
+
+static void
+esim_check_context_free (EsimCheckContext *ctx)
+{
+ g_assert (!ctx->saved_error);
+ g_free (ctx->eid);
+ g_free (ctx);
+}
+
+static gchar *
+load_eid_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void esim_check_step (MbimDevice *device,
+ GTask *task);
+
+static void
+check_uicc_close_channel_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ g_autoptr(GError) error = NULL;
+ guint32 status;
+ EsimCheckContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_uicc_low_level_access_close_channel_response_parse (response, &status, &error)) {
+ /* if we have a saved error, prefer that one */
+ if (!ctx->saved_error)
+ ctx->saved_error = g_steal_pointer (&error);
+ } else if (status != UICC_STATUS_OK) {
+ /* if we have a saved error, prefer that one */
+ if (!ctx->saved_error)
+ ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "UICC close channel failed");
+ }
+
+ /* go on to next step */
+ ctx->step++;
+ esim_check_step (device, task);
+}
+
+static void
+check_uicc_apdu_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ guint32 status;
+ guint32 apdu_response_size;
+ const guint8 *apdu_response = NULL;
+ EsimCheckContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_uicc_low_level_access_apdu_response_parse (
+ response,
+ &status,
+ &apdu_response_size,
+ &apdu_response,
+ &error))
+ ctx->saved_error = error;
+ else
+ ctx->eid = mm_decode_eid ((const gchar *)(apdu_response + EID_APDU_HEADER),
+ apdu_response_size - EID_APDU_HEADER);
+
+ /* always go on to the close channel step, even on error */
+ ctx->step++;
+ esim_check_step (device, task);
+}
+
+static void
+check_uicc_open_channel_ready (MbimDevice *device,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(MbimMessage) response = NULL;
+ GError *error = NULL;
+ guint32 status;
+ guint32 channel;
+ EsimCheckContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ response = mbim_device_command_finish (device, res, &error);
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_ms_uicc_low_level_access_open_channel_response_parse (
+ response,
+ &status,
+ &channel,
+ NULL,
+ NULL,
+ &error)) {
+ ctx->saved_error = error;
+ ctx->step = ESIM_CHECK_STEP_LAST;
+ } else if (status != UICC_STATUS_OK) {
+ ctx->saved_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "UICC open channel failed");;
+ ctx->step = ESIM_CHECK_STEP_LAST;
+ } else {
+ /* channel is open, from now on we'll need to always explicitly close,
+ * even on errors */
+ ctx->channel = channel;
+ ctx->step++;
+ }
+
+ esim_check_step (device, task);
+}
+
+static void
+esim_check_step (MbimDevice *device,
+ GTask *task)
+{
+ MMSimMbim *self;
+ EsimCheckContext *ctx;
+ g_autoptr(MbimMessage) message = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ /* Don't run new steps if we're cancelled */
+ if (g_task_return_error_if_cancelled (task)) {
+ g_object_unref (task);
+ return;
+ }
+
+ switch (ctx->step) {
+ case ESIM_CHECK_STEP_FIRST:
+ ctx->step++;
+ /* fall through */
+
+ case ESIM_CHECK_STEP_UICC_OPEN_CHANNEL: {
+ const guint8 app_id[] = {0xa0, 0x00, 0x00, 0x05, 0x59, 0x10, 0x10, 0xff,
+ 0xff, 0xff, 0xff, 0x89, 0x00, 0x00, 0x01, 0x00};
+
+ /* Channel group is used to bundle all logical channels opened and for
+ * future reference to close */
+ ctx->channel_grp = 1;
+
+ mm_obj_dbg (self, "opening UICC channel...");
+ message = mbim_message_ms_uicc_low_level_access_open_channel_set_new (
+ sizeof (app_id),
+ app_id,
+ 4, /* SelectP2Arg: Return File Control Parameters(FCP) template.
+ * Refer 11.1.13 of of the ETSI TS 102 221 technical specification.
+ */
+ ctx->channel_grp,
+ NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)check_uicc_open_channel_ready,
+ task);
+ return;
+ }
+
+ case ESIM_CHECK_STEP_UICC_GET_APDU: {
+ const guint8 apdu_cmd[] = {0x81, 0xe2, 0x91, 0x00, 0x06, 0xbf,
+ 0x3e, 0x03, 0x5c, 0x01, 0x5a, 0x00};
+
+ mm_obj_dbg (self, "reading EID...");
+ message = mbim_message_ms_uicc_low_level_access_apdu_set_new (
+ ctx->channel,
+ MBIM_UICC_SECURE_MESSAGING_NONE,
+ MBIM_UICC_CLASS_BYTE_TYPE_EXTENDED,
+ sizeof (apdu_cmd),
+ apdu_cmd,
+ NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)check_uicc_apdu_ready,
+ task);
+ return;
+ }
+
+ case ESIM_CHECK_STEP_UICC_CLOSE_CHANNEL:
+ mm_obj_dbg (self, "closing UICC channel...");
+ message = mbim_message_ms_uicc_low_level_access_close_channel_set_new (
+ ctx->channel,
+ ctx->channel_grp,
+ NULL);
+ mbim_device_command (device,
+ message,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)check_uicc_close_channel_ready,
+ task);
+ return;
+
+ case ESIM_CHECK_STEP_LAST:
+ if (ctx->saved_error)
+ g_task_return_error (task, g_steal_pointer (&ctx->saved_error));
+ else if (ctx->eid)
+ g_task_return_pointer (task, g_steal_pointer (&ctx->eid), g_free);
+ else
+ g_assert_not_reached ();
+ g_object_unref (task);
+ return;
+
+ default:
+ break;
+ }
+
+ g_assert_not_reached ();
+}
+
+static void
+load_eid (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ EsimCheckContext *ctx;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_new0 (EsimCheckContext, 1);
+ ctx->step = ESIM_CHECK_STEP_FIRST;
+ g_task_set_task_data (task, ctx, (GDestroyNotify)esim_check_context_free);
+ esim_check_step (device, task);
+}
+
+/*****************************************************************************/
/* Load operator identifier */
static gchar *
@@ -207,19 +474,13 @@ load_operator_identifier_finish (MMBaseSim *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);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_operator_identifier_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -227,19 +488,19 @@ load_operator_identifier_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &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);
+ &error)) {
+ g_task_return_pointer (task, g_strdup (provider->provider_id), g_free);
+ mbim_provider_free (provider);
+ } else
+ g_task_return_error (task, error);
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -249,20 +510,20 @@ load_operator_identifier (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_operator_identifier);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_home_provider_query_new (NULL);
mbim_device_command (device,
message,
- 10,
+ 30,
NULL,
(GAsyncReadyCallback)load_operator_identifier_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -274,19 +535,13 @@ load_operator_name_finish (MMBaseSim *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);
+ return g_task_propagate_pointer (G_TASK (res), error);
}
static void
load_operator_name_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
MbimMessage *response;
GError *error = NULL;
@@ -294,19 +549,19 @@ load_operator_name_ready (MbimDevice *device,
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &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);
+ &error)) {
+ g_task_return_pointer (task, g_strdup (provider->provider_name), g_free);
+ mbim_provider_free (provider);
+ } else
+ g_task_return_error (task, error);
+ g_object_unref (task);
if (response)
mbim_message_unref (response);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
}
static void
@@ -316,20 +571,20 @@ load_operator_name (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, load_operator_name);
+ task = g_task_new (self, NULL, callback, user_data);
message = mbim_message_home_provider_query_new (NULL);
mbim_device_command (device,
message,
- 10,
+ 30,
NULL,
(GAsyncReadyCallback)load_operator_name_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -341,48 +596,53 @@ send_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
pin_set_enter_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
+ MMSimMbim *self;
GError *error = NULL;
MbimMessage *response;
+ gboolean success;
MbimPinType pin_type;
MbimPinState pin_state;
+ guint32 remaining_attempts;
+
+ self = g_task_get_source_object (task);
response = mbim_device_command_finish (device, res, &error);
- if (response &&
- !mbim_message_command_done_get_result (response, &error)) {
- /* Sending PIN failed, build a better error to report */
- if (mbim_message_pin_response_parse (
- response,
- &pin_type,
- &pin_state,
- NULL,
- NULL)) {
- /* Create the errors ourselves */
- if (pin_type == MBIM_PIN_TYPE_PIN1 && pin_state == MBIM_PIN_STATE_LOCKED) {
- g_error_free (error);
- error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD);
- } else if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) {
- g_error_free (error);
- error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK);
+ if (response) {
+ success = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
+
+ if (mbim_message_pin_response_parse (response,
+ &pin_type,
+ &pin_state,
+ &remaining_attempts,
+ NULL)) {
+ if (!success) {
+ /* Sending PIN failed, build a better error to report */
+ if (pin_type == MBIM_PIN_TYPE_PIN1 && pin_state == MBIM_PIN_STATE_LOCKED) {
+ g_error_free (error);
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, self);
+ } else if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) {
+ g_error_free (error);
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_PUK, self);
+ }
}
}
+
+ mbim_message_unref (response);
}
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- if (response)
- mbim_message_unref (response);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -393,15 +653,15 @@ send_pin (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, send_pin);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("Sending PIN...");
+ mm_obj_dbg (self, "sending PIN...");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PIN1,
MBIM_PIN_OPERATION_ENTER,
@@ -409,9 +669,8 @@ send_pin (MMBaseSim *self,
"",
&error));
if (!message) {
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -420,7 +679,7 @@ send_pin (MMBaseSim *self,
10,
NULL,
(GAsyncReadyCallback)pin_set_enter_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -432,49 +691,53 @@ send_puk_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
puk_set_enter_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
+ MMSimMbim *self;
GError *error = NULL;
MbimMessage *response;
+ gboolean success;
MbimPinType pin_type;
MbimPinState pin_state;
guint32 remaining_attempts;
+ self = g_task_get_source_object (task);
+
response = mbim_device_command_finish (device, res, &error);
- if (response &&
- !mbim_message_command_done_get_result (response, &error)) {
- /* Sending PUK failed, build a better error to report */
- if (mbim_message_pin_response_parse (
- response,
- &pin_type,
- &pin_state,
- &remaining_attempts,
- NULL)) {
- /* Create the errors ourselves */
- if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) {
- g_error_free (error);
- if (remaining_attempts == 0)
- error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG);
- else
- error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD);
+ if (response) {
+ success = mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
+
+ if (mbim_message_pin_response_parse (response,
+ &pin_type,
+ &pin_state,
+ &remaining_attempts,
+ NULL)) {
+ if (!success) {
+ /* Sending PUK failed, build a better error to report */
+ if (pin_type == MBIM_PIN_TYPE_PUK1 && pin_state == MBIM_PIN_STATE_LOCKED) {
+ g_error_free (error);
+ if (remaining_attempts == 0)
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG, self);
+ else
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, self);
+ }
}
}
+
+ mbim_message_unref (response);
}
if (error)
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
- if (response)
- mbim_message_unref (response);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -486,15 +749,15 @@ send_puk (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, send_puk);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("Sending PUK...");
+ mm_obj_dbg (self, "sending PUK...");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PUK1,
MBIM_PIN_OPERATION_ENTER,
@@ -502,9 +765,8 @@ send_puk (MMBaseSim *self,
new_pin,
&error));
if (!message) {
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -513,7 +775,7 @@ send_puk (MMBaseSim *self,
10,
NULL,
(GAsyncReadyCallback)puk_set_enter_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -525,20 +787,20 @@ enable_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
pin_set_enable_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
MbimMessage *response;
response = mbim_device_command_finish (device, res, &error);
if (response) {
- mbim_message_command_done_get_result (response, &error);
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
mbim_message_unref (response);
}
@@ -550,11 +812,10 @@ pin_set_enable_ready (MbimDevice *device,
"Need to be unlocked to allow enabling/disabling PIN");
}
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -566,15 +827,15 @@ enable_pin (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, send_puk);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("%s PIN ...", enabled ? "Enabling" : "Disabling");
+ mm_obj_dbg (self, "%s PIN ...", enabled ? "enabling" : "disabling");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PIN1,
enabled ? MBIM_PIN_OPERATION_ENABLE : MBIM_PIN_OPERATION_DISABLE,
@@ -582,9 +843,8 @@ enable_pin (MMBaseSim *self,
"",
&error));
if (!message) {
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -593,7 +853,7 @@ enable_pin (MMBaseSim *self,
10,
NULL,
(GAsyncReadyCallback)pin_set_enable_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -605,20 +865,20 @@ change_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static void
pin_set_change_ready (MbimDevice *device,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
GError *error = NULL;
MbimMessage *response;
response = mbim_device_command_finish (device, res, &error);
if (response) {
- mbim_message_command_done_get_result (response, &error);
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error);
mbim_message_unref (response);
}
@@ -630,11 +890,10 @@ pin_set_change_ready (MbimDevice *device,
"Need to be unlocked to allow changing PIN");
}
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
@@ -646,15 +905,15 @@ change_pin (MMBaseSim *self,
{
MbimDevice *device;
MbimMessage *message;
- GSimpleAsyncResult *result;
+ GTask *task;
GError *error = NULL;
if (!peek_device (self, &device, callback, user_data))
return;
- result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, send_puk);
+ task = g_task_new (self, NULL, callback, user_data);
- mm_dbg ("Changing PIN");
+ mm_obj_dbg (self, "changing PIN...");
message = (mbim_message_pin_set_new (
MBIM_PIN_TYPE_PIN1,
MBIM_PIN_OPERATION_CHANGE,
@@ -662,9 +921,8 @@ change_pin (MMBaseSim *self,
new_pin,
&error));
if (!message) {
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -673,7 +931,7 @@ change_pin (MMBaseSim *self,
10,
NULL,
(GAsyncReadyCallback)pin_set_change_ready,
- result);
+ task);
mbim_message_unref (message);
}
@@ -711,9 +969,38 @@ mm_sim_mbim_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ "active", TRUE, /* by default always active */
NULL);
}
+MMBaseSim *
+mm_sim_mbim_new_initialized (MMBaseModem *modem,
+ guint slot_number,
+ gboolean active,
+ const gchar *sim_identifier,
+ const gchar *imsi,
+ const gchar *eid,
+ const gchar *operator_identifier,
+ const gchar *operator_name,
+ const GStrv emergency_numbers)
+{
+ MMBaseSim *sim;
+
+ sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_MBIM,
+ MM_BASE_SIM_MODEM, modem,
+ MM_BASE_SIM_SLOT_NUMBER, slot_number,
+ "active", active,
+ "sim-identifier", sim_identifier,
+ "eid", eid,
+ "operator-identifier", operator_identifier,
+ "operator-name", operator_name,
+ "emergency-numbers", emergency_numbers,
+ NULL));
+
+ mm_base_sim_export (sim);
+ return sim;
+}
+
static void
mm_sim_mbim_init (MMSimMbim *self)
{
@@ -728,6 +1015,8 @@ mm_sim_mbim_class_init (MMSimMbimClass *klass)
base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
base_sim_class->load_imsi = load_imsi;
base_sim_class->load_imsi_finish = load_imsi_finish;
+ base_sim_class->load_eid = load_eid;
+ base_sim_class->load_eid_finish = load_eid_finish;
base_sim_class->load_operator_identifier = load_operator_identifier;
base_sim_class->load_operator_identifier_finish = load_operator_identifier_finish;
base_sim_class->load_operator_name = load_operator_name;
diff --git a/src/mm-sim-mbim.h b/src/mm-sim-mbim.h
index f7bf6ab1..f6926840 100644
--- a/src/mm-sim-mbim.h
+++ b/src/mm-sim-mbim.h
@@ -40,6 +40,7 @@ struct _MMSimMbimClass {
};
GType mm_sim_mbim_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimMbim, g_object_unref)
void mm_sim_mbim_new (MMBaseModem *modem,
GCancellable *cancellable,
@@ -47,5 +48,13 @@ void mm_sim_mbim_new (MMBaseModem *modem,
gpointer user_data);
MMBaseSim *mm_sim_mbim_new_finish (GAsyncResult *res,
GError **error);
-
+MMBaseSim *mm_sim_mbim_new_initialized (MMBaseModem *modem,
+ guint slot_number,
+ gboolean active,
+ const gchar *sim_identifier,
+ const gchar *imsi,
+ const gchar *eid,
+ const gchar *operator_identifier,
+ const gchar *operator_name,
+ const GStrv emergency_numbers);
#endif /* MM_SIM_MBIM_H */
diff --git a/src/mm-sim-qmi.c b/src/mm-sim-qmi.c
index 4eb1b24f..7c9fe7f5 100644
--- a/src/mm-sim-qmi.c
+++ b/src/mm-sim-qmi.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2012 Google, Inc.
+ * Copyright (C) 2016 Aleksander Morgado <aleksander@aleksander.es>
*/
#include <config.h>
@@ -24,19 +25,32 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
-#include "mm-log.h"
+#include "mm-broadband-modem-qmi.h"
+#include "mm-log-object.h"
#include "mm-sim-qmi.h"
+#include "mm-modem-helpers-qmi.h"
G_DEFINE_TYPE (MMSimQmi, mm_sim_qmi, MM_TYPE_BASE_SIM)
+enum {
+ PROP_0,
+ PROP_DMS_UIM_DEPRECATED,
+ PROP_LAST
+};
+
+static GParamSpec *properties[PROP_LAST];
+
+struct _MMSimQmiPrivate {
+ gboolean dms_uim_deprecated;
+};
+
/*****************************************************************************/
static gboolean
-ensure_qmi_client (MMSimQmi *self,
- QmiService service,
- QmiClient **o_client,
- GAsyncReadyCallback callback,
- gpointer user_data)
+ensure_qmi_client (GTask *task,
+ MMSimQmi *self,
+ QmiService service,
+ QmiClient **o_client)
{
MMBaseModem *modem = NULL;
QmiClient *client;
@@ -47,16 +61,15 @@ ensure_qmi_client (MMSimQmi *self,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
- port = mm_base_modem_peek_port_qmi (modem);
+ port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (modem));
g_object_unref (modem);
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek QMI port");
+ if (task) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't peek QMI port");
+ g_object_unref (task);
+ }
return FALSE;
}
@@ -64,13 +77,12 @@ ensure_qmi_client (MMSimQmi *self,
service,
MM_PORT_QMI_FLAG_DEFAULT);
if (!client) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek client for service '%s'",
- qmi_service_get_string (service));
+ if (task) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't peek client for service '%s'",
+ qmi_service_get_string (service));
+ g_object_unref (task);
+ }
return FALSE;
}
@@ -79,27 +91,292 @@ ensure_qmi_client (MMSimQmi *self,
}
/*****************************************************************************/
+/* Wait for SIM ready */
+
+#define SIM_READY_CHECKS_MAX 5
+#define SIM_READY_CHECKS_TIMEOUT_SECS 1
+
+typedef struct {
+ QmiClient *client_uim;
+ guint ready_checks_n;
+} WaitSimReadyContext;
+
+static void
+wait_sim_ready_context_free (WaitSimReadyContext *ctx)
+{
+ g_clear_object (&ctx->client_uim);
+ g_slice_free (WaitSimReadyContext, ctx);
+}
+
+static gboolean
+wait_sim_ready_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void sim_ready_check (GTask *task);
+
+static gboolean
+sim_ready_retry_cb (GTask *task)
+{
+ sim_ready_check (task);
+ return G_SOURCE_REMOVE;
+}
+
+static void
+sim_ready_retry (GTask *task)
+{
+ g_timeout_add_seconds (SIM_READY_CHECKS_TIMEOUT_SECS, (GSourceFunc) sim_ready_retry_cb, task);
+}
+
+static void
+uim_get_card_status_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ g_autoptr(QmiMessageUimGetCardStatusOutput) output = NULL;
+ g_autoptr(GError) error = NULL;
+ MMSimQmi *self;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_uim_get_card_status_finish (client, res, &error);
+ if (!output ||
+ !qmi_message_uim_get_card_status_output_get_result (output, &error) ||
+ (!mm_qmi_uim_get_card_status_output_parse (self, output, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &error) &&
+ (g_error_matches (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
+ g_error_matches (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY)))) {
+ mm_obj_dbg (self, "sim not yet considered ready... retrying");
+ sim_ready_retry (task);
+ return;
+ }
+
+ /* SIM is considered ready now */
+ mm_obj_dbg (self, "sim is ready");
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+sim_ready_check (GTask *task)
+{
+ WaitSimReadyContext *ctx;
+ MMSimQmi *self;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->ready_checks_n++;
+ if (ctx->ready_checks_n == SIM_READY_CHECKS_MAX) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "failed waiting for SIM readiness");
+ g_object_unref (task);
+ return;
+ }
+
+ mm_obj_dbg (self, "checking SIM readiness");
+ qmi_client_uim_get_card_status (QMI_CLIENT_UIM (ctx->client_uim),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) uim_get_card_status_ready,
+ task);
+}
+
+static void
+wait_sim_ready (MMBaseSim *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ QmiClient *client;
+ MMSimQmi *self;
+ GTask *task;
+ WaitSimReadyContext *ctx;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "waiting for SIM to be ready...");
+ if (!self->priv->dms_uim_deprecated) {
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+
+ if (!ensure_qmi_client (task, self, QMI_SERVICE_UIM, &client))
+ return;
+
+ ctx = g_slice_new0 (WaitSimReadyContext);
+ ctx->client_uim = g_object_ref (client);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) wait_sim_ready_context_free);
+
+ sim_ready_check (task);
+}
+
+/*****************************************************************************/
/* Load SIM ID (ICCID) */
+static GArray *
+uim_read_finish (QmiClientUim *client,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+uim_read_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageUimReadTransparentOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_uim_read_transparent_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_uim_read_transparent_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't read data from UIM: ");
+ g_task_return_error (task, error);
+ } else {
+ GArray *read_result = NULL;
+
+ qmi_message_uim_read_transparent_output_get_read_result (output, &read_result, NULL);
+ if (read_result)
+ g_task_return_pointer (task,
+ g_array_ref (read_result),
+ (GDestroyNotify) g_array_unref);
+ else
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Read malformed data from UIM");
+ }
+
+ if (output)
+ qmi_message_uim_read_transparent_output_unref (output);
+
+ g_object_unref (task);
+}
+
+static void
+uim_read (MMSimQmi *self,
+ guint16 file_id,
+ const guint16 *file_path,
+ gsize file_path_len,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+ GArray *file_path_bytes;
+ gsize i;
+ QmiMessageUimReadTransparentInput *input;
+ GArray *aid;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ if (!ensure_qmi_client (task,
+ self,
+ QMI_SERVICE_UIM, &client))
+ return;
+
+ file_path_bytes = g_array_sized_new (FALSE, FALSE, 1, file_path_len * 2);
+ for (i = 0; i < file_path_len; ++i) {
+ guint8 byte;
+
+ byte = file_path[i] & 0xFF;
+ g_array_append_val (file_path_bytes, byte);
+ byte = (file_path[i] >> 8) & 0xFF;
+ g_array_append_val (file_path_bytes, byte);
+ }
+
+ input = qmi_message_uim_read_transparent_input_new ();
+ aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
+ qmi_message_uim_read_transparent_input_set_session (
+ input,
+ QMI_UIM_SESSION_TYPE_PRIMARY_GW_PROVISIONING,
+ aid,
+ NULL);
+ g_array_unref (aid);
+ qmi_message_uim_read_transparent_input_set_file (input,
+ file_id,
+ file_path_bytes,
+ NULL);
+ qmi_message_uim_read_transparent_input_set_read_information (input,
+ 0,
+ 0,
+ NULL);
+ g_array_unref (file_path_bytes);
+
+ qmi_client_uim_read_transparent (QMI_CLIENT_UIM (client),
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)uim_read_ready,
+ task);
+ qmi_message_uim_read_transparent_input_unref (input);
+}
+
static gchar *
-load_sim_identifier_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+load_sim_identifier_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
{
- gchar *sim_identifier;
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
+static void
+uim_get_iccid_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GArray *read_result;
+ g_autofree gchar *raw_iccid = NULL;
+ gchar *iccid;
+
+ read_result = uim_read_finish (client, res, &error);
+ if (!read_result) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
- sim_identifier = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded SIM identifier: %s", sim_identifier);
- return sim_identifier;
+ raw_iccid = mm_utils_bin2hexstr ((const guint8 *) read_result->data, read_result->len);
+ g_assert (raw_iccid);
+ iccid = mm_3gpp_parse_iccid (raw_iccid, &error);
+ if (!iccid) {
+ g_task_return_error (task, error);
+ } else {
+ g_task_return_pointer (task, iccid, g_free);
+ }
+ g_object_unref (task);
+
+ g_array_unref (read_result);
+}
+
+static void
+uim_get_iccid (MMSimQmi *self,
+ GTask *task)
+{
+ static const guint16 file_path[] = { 0x3F00 };
+
+ uim_read (self,
+ 0x2FE2,
+ file_path,
+ G_N_ELEMENTS (file_path),
+ (GAsyncReadyCallback)uim_get_iccid_ready,
+ task);
}
static void
dms_uim_get_iccid_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsUimGetIccidOutput *output = NULL;
GError *error = NULL;
@@ -107,75 +384,125 @@ dms_uim_get_iccid_ready (QmiClientDms *client,
output = qmi_client_dms_uim_get_iccid_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_uim_get_iccid_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get UIM ICCID: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else {
const gchar *str = NULL;
qmi_message_dms_uim_get_iccid_output_get_iccid (output, &str, NULL);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ g_task_return_pointer (task, g_strdup (str), g_free);
}
if (output)
qmi_message_dms_uim_get_iccid_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-load_sim_identifier (MMBaseSim *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_get_iccid (MMSimQmi *self,
+ GTask *task)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_SIM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!ensure_qmi_client (task,
+ self,
+ QMI_SERVICE_DMS, &client))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_sim_identifier);
-
- mm_dbg ("loading SIM identifier...");
qmi_client_dms_uim_get_iccid (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_uim_get_iccid_ready,
- result);
+ task);
+}
+
+static void
+load_sim_identifier (MMBaseSim *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMSimQmi *self;
+ GTask *task;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "loading SIM identifier...");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_get_iccid (self, task);
+ else
+ uim_get_iccid (self, task);
}
/*****************************************************************************/
/* Load IMSI */
static gchar *
-load_imsi_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+load_imsi_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+uim_get_imsi_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GArray *read_result;
gchar *imsi;
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
- return NULL;
+ read_result = uim_read_finish (client, res, &error);
+ if (!read_result) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ imsi = mm_bcd_to_string ((const guint8 *) read_result->data, read_result->len,
+ TRUE /* low_nybble_first */);
+ g_assert (imsi);
+ if (strlen (imsi) < 3)
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "IMSI is malformed");
+ else
+ /* EFimsi contains a length byte, follwed by a nibble for parity,
+ * and then followed by the actual IMSI in BCD. After converting
+ * the BCD into a decimal string, we simply skip the first 3
+ * decimal digits to obtain the IMSI. */
+ g_task_return_pointer (task, g_strdup (imsi + 3), g_free);
+ g_object_unref (task);
+
+ g_free (imsi);
+ g_array_unref (read_result);
+}
- imsi = g_strdup (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
- mm_dbg ("loaded IMSI: %s", imsi);
- return imsi;
+static void
+uim_get_imsi (MMSimQmi *self,
+ GTask *task)
+{
+ static const guint16 file_path[] = { 0x3F00, 0x7FFF };
+
+ uim_read (self,
+ 0x6F07,
+ file_path,
+ G_N_ELEMENTS (file_path),
+ (GAsyncReadyCallback)uim_get_imsi_ready,
+ task);
}
static void
dms_uim_get_imsi_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsUimGetImsiOutput *output = NULL;
GError *error = NULL;
@@ -183,51 +510,551 @@ dms_uim_get_imsi_ready (QmiClientDms *client,
output = qmi_client_dms_uim_get_imsi_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_uim_get_imsi_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't get UIM IMSI: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else {
const gchar *str = NULL;
qmi_message_dms_uim_get_imsi_output_get_imsi (output, &str, NULL);
- g_simple_async_result_set_op_res_gpointer (simple,
- g_strdup (str),
- (GDestroyNotify)g_free);
+ g_task_return_pointer (task, g_strdup (str), g_free);
}
if (output)
qmi_message_dms_uim_get_imsi_output_unref (output);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-load_imsi (MMBaseSim *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_get_imsi (MMSimQmi *self,
+ GTask *task)
{
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_SIM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!ensure_qmi_client (task,
+ self,
+ QMI_SERVICE_DMS, &client))
return;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_imsi);
-
- mm_dbg ("loading IMSI...");
qmi_client_dms_uim_get_imsi (QMI_CLIENT_DMS (client),
NULL,
5,
NULL,
(GAsyncReadyCallback)dms_uim_get_imsi_ready,
- result);
+ task);
+}
+
+static void
+load_imsi (MMBaseSim *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMSimQmi *self;
+ GTask *task;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ mm_obj_dbg (self, "loading IMSI...");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_get_imsi (self, task);
+ else
+ uim_get_imsi (self, task);
+}
+
+/*****************************************************************************/
+/* Load operator identifier */
+
+static gboolean
+get_home_network (QmiClientNas *client,
+ GAsyncResult *res,
+ guint16 *out_mcc,
+ guint16 *out_mnc,
+ gboolean *out_mnc_with_pcs,
+ gchar **out_operator_name,
+ GError **error)
+{
+ QmiMessageNasGetHomeNetworkOutput *output = NULL;
+ gboolean success = FALSE;
+
+ output = qmi_client_nas_get_home_network_finish (client, res, error);
+ if (!output) {
+ g_prefix_error (error, "QMI operation failed: ");
+ } else if (!qmi_message_nas_get_home_network_output_get_result (output, error)) {
+ g_prefix_error (error, "Couldn't get home network: ");
+ } else {
+ const gchar *name = NULL;
+
+ qmi_message_nas_get_home_network_output_get_home_network (
+ output,
+ out_mcc,
+ out_mnc,
+ &name,
+ NULL);
+ if (out_operator_name)
+ *out_operator_name = g_strdup (name);
+
+ if (out_mnc_with_pcs) {
+ gboolean is_3gpp;
+ gboolean mnc_includes_pcs_digit;
+
+ if (qmi_message_nas_get_home_network_output_get_home_network_3gpp_mnc (
+ output,
+ &is_3gpp,
+ &mnc_includes_pcs_digit,
+ NULL) &&
+ is_3gpp &&
+ mnc_includes_pcs_digit) {
+ /* MNC should include PCS digit */
+ *out_mnc_with_pcs = TRUE;
+ } else {
+ /* We default to NO PCS digit, unless of course the MNC is already > 99 */
+ *out_mnc_with_pcs = FALSE;
+ }
+ }
+
+ success = TRUE;
+ }
+
+ if (output)
+ qmi_message_nas_get_home_network_output_unref (output);
+
+ return success;
+}
+
+static gchar *
+load_operator_identifier_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_operator_identifier_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ guint16 mcc, mnc;
+ gboolean mnc_with_pcs;
+ GError *error = NULL;
+ GString *aux;
+
+ if (!get_home_network (client, res, &mcc, &mnc, &mnc_with_pcs, NULL, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ aux = g_string_new ("");
+ /* MCC always 3 digits */
+ g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mcc);
+ /* Guess about MNC, if < 100 assume it's 2 digits, no PCS info here */
+ if (mnc >= 100 || mnc_with_pcs)
+ g_string_append_printf (aux, "%.3" G_GUINT16_FORMAT, mnc);
+ else
+ g_string_append_printf (aux, "%.2" G_GUINT16_FORMAT, mnc);
+ g_task_return_pointer (task, g_string_free (aux, FALSE), g_free);
+ g_object_unref (task);
+}
+
+static void
+load_operator_identifier (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (!ensure_qmi_client (task,
+ MM_SIM_QMI (self),
+ QMI_SERVICE_NAS, &client))
+ return;
+
+ mm_obj_dbg (self, "loading SIM operator identifier...");
+ qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)load_operator_identifier_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Load operator name */
+
+static gchar *
+load_operator_name_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_operator_name_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ gchar *operator_name = NULL;
+ GError *error = NULL;
+
+ if (!get_home_network (client, res, NULL, NULL, NULL, &operator_name, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, operator_name, g_free);
+ g_object_unref (task);
+}
+
+static void
+load_operator_name (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (!ensure_qmi_client (task,
+ MM_SIM_QMI (self),
+ QMI_SERVICE_NAS, &client))
+ return;
+
+ mm_obj_dbg (self, "loading SIM operator name...");
+ qmi_client_nas_get_home_network (QMI_CLIENT_NAS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)load_operator_name_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Load preferred networks */
+
+static GList *
+load_preferred_networks_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static GList *
+parse_get_preferred_networks (QmiMessageNasGetPreferredNetworksOutput *output)
+{
+ GList *result = NULL;
+ GArray *preferred_nets_array = NULL;
+ GArray *preferred_nets_mnc_pcs_digit_array = NULL;
+ guint i;
+
+ if (qmi_message_nas_get_preferred_networks_output_get_preferred_networks (output,
+ &preferred_nets_array,
+ NULL)) {
+ qmi_message_nas_get_preferred_networks_output_get_mnc_pcs_digit_include_status (output,
+ &preferred_nets_mnc_pcs_digit_array,
+ NULL);
+ for (i = 0; i < preferred_nets_array->len; i++) {
+ QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement *net;
+ QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement *mnc_pcs_digit = NULL;
+ MMSimPreferredNetwork *new_item;
+ g_autofree gchar *operator_code = NULL;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ net = &g_array_index (preferred_nets_array,
+ QmiMessageNasGetPreferredNetworksOutputPreferredNetworksElement, i);
+ if (preferred_nets_mnc_pcs_digit_array && i < preferred_nets_mnc_pcs_digit_array->len)
+ mnc_pcs_digit = &g_array_index (preferred_nets_mnc_pcs_digit_array,
+ QmiMessageNasGetPreferredNetworksOutputMncPcsDigitIncludeStatusElement, i);
+
+ new_item = mm_sim_preferred_network_new ();
+
+ if (net->mnc > 99 || (mnc_pcs_digit != NULL && mnc_pcs_digit->includes_pcs_digit))
+ operator_code = g_strdup_printf ("%03d%03d", net->mcc, net->mnc);
+ else
+ operator_code = g_strdup_printf ("%03d%02d", net->mcc, net->mnc);
+ mm_sim_preferred_network_set_operator_code (new_item, operator_code);
+
+ if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM;
+ if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM_COMPACT)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT;
+ if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UTRAN)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_EUTRAN)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ if (net->radio_access_technology & QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_NGRAN)
+ act |= MM_MODEM_ACCESS_TECHNOLOGY_5GNR;
+ mm_sim_preferred_network_set_access_technology (new_item, act);
+
+ result = g_list_append (result, new_item);
+ }
+ }
+
+ return result;
+}
+
+static void
+load_preferred_networks_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageNasGetPreferredNetworksOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_nas_get_preferred_networks_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_nas_get_preferred_networks_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get preferred networks: ");
+ g_task_return_error (task, error);
+ } else
+ g_task_return_pointer (task, parse_get_preferred_networks (output), (GDestroyNotify) mm_sim_preferred_network_list_free);
+
+ if (output)
+ qmi_message_nas_get_preferred_networks_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+load_preferred_networks (MMBaseSim *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiClient *client = NULL;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (!ensure_qmi_client (task,
+ MM_SIM_QMI (self),
+ QMI_SERVICE_NAS, &client))
+ return;
+
+ mm_obj_dbg (self, "loading preferred network list...");
+ qmi_client_nas_get_preferred_networks (QMI_CLIENT_NAS (client),
+ NULL,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)load_preferred_networks_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Set preferred networks */
+
+typedef struct {
+ /* Preferred network list to be set, used for after-check comparison */
+ GList *set_list;
+} SetPreferredNetworksContext;
+
+static void
+set_preferred_network_context_free (SetPreferredNetworksContext *ctx)
+{
+ g_list_free_full (ctx->set_list, (GDestroyNotify) mm_sim_preferred_network_free);
+ g_slice_free (SetPreferredNetworksContext, ctx);
+}
+
+static gboolean
+set_preferred_networks_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_preferred_networks_reload_ready (MMBaseSim *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+ GList *loaded_list;
+ GList *loaded_iter;
+ GList *set_iter;
+ SetPreferredNetworksContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ loaded_list = load_preferred_networks_finish (self, res, &error);
+ if (error) {
+ mm_obj_warn (self, "couldn't reload list of preferred networks: %s", error->message);
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Compare the set and loaded network list for differences */
+ loaded_iter = loaded_list;
+ set_iter = ctx->set_list;
+
+ while (loaded_iter && set_iter) {
+ const gchar *loaded_op_code;
+ const gchar *set_op_code;
+ MMModemAccessTechnology loaded_act;
+ MMModemAccessTechnology set_act;
+
+ loaded_op_code = mm_sim_preferred_network_get_operator_code (loaded_iter->data);
+ set_op_code = mm_sim_preferred_network_get_operator_code (set_iter->data);
+ loaded_act = mm_sim_preferred_network_get_access_technology (loaded_iter->data);
+ set_act = mm_sim_preferred_network_get_access_technology (set_iter->data);
+
+ /* Operator code mismatch is never expected, but check it just in case */
+ if (g_strcmp0 (loaded_op_code, set_op_code)) {
+ mm_obj_warn (self, "operator code mismatch, expected '%s' loaded '%s'",
+ set_op_code, loaded_op_code);
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Mismatch in requested and set operator code");
+ break;
+ }
+ /* Check if there are access technology bits requested but unset */
+ if ((loaded_act & set_act) != set_act) {
+ MMModemAccessTechnology unset = set_act & ~loaded_act;
+
+ mm_obj_warn (self, "access technologies '%s' not set for operator code '%s'",
+ mm_modem_access_technology_build_string_from_mask (unset),
+ set_op_code);
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Access technology unsupported by modem or SIM");
+ break;
+ }
+ loaded_iter = g_list_next (loaded_iter);
+ set_iter = g_list_next (set_iter);
+ }
+ if (!error && loaded_iter == NULL && set_iter != NULL) {
+ /* Not all networks were written; some modems silently discard networks
+ * that exceed the SIM card capacity.
+ */
+ error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY,
+ "Too many networks; %u networks written",
+ g_list_length (loaded_list));
+ }
+
+ if (error) {
+ /* Update the PreferredNetworks property to real SIM contents */
+ mm_gdbus_sim_set_preferred_networks (MM_GDBUS_SIM (self),
+ mm_sim_preferred_network_list_get_variant (loaded_list));
+ g_task_return_error (task, error);
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ g_list_free_full (loaded_list, (GDestroyNotify) mm_sim_preferred_network_free);
+ g_object_unref (task);
+}
+
+static void
+set_preferred_networks_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageNasSetPreferredNetworksOutput *output;
+ GError *error = NULL;
+ MMBaseSim *self;
+
+ self = g_task_get_source_object (task);
+
+ output = qmi_client_nas_set_preferred_networks_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_nas_set_preferred_networks_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't set preferred networks: ");
+ g_task_return_error (task, error);
+ } else {
+ /* Reload the networks from modem to check whether everything was written */
+ load_preferred_networks (self, (GAsyncReadyCallback) set_preferred_networks_reload_ready, task);
+ qmi_message_nas_set_preferred_networks_output_unref (output);
+ return;
+ }
+
+ if (output)
+ qmi_message_nas_set_preferred_networks_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+set_preferred_networks (MMBaseSim *self,
+ GList *preferred_network_list,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ QmiMessageNasSetPreferredNetworksInput *input;
+ QmiClient *client = NULL;
+ GArray *preferred_nets_array;
+ GArray *preferred_nets_mnc_pcs_digit_array;
+ SetPreferredNetworksContext *ctx;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ if (!ensure_qmi_client (task,
+ MM_SIM_QMI (self),
+ QMI_SERVICE_NAS, &client))
+ return;
+
+ ctx = g_slice_new0 (SetPreferredNetworksContext);
+ ctx->set_list = mm_sim_preferred_network_list_copy (preferred_network_list);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) set_preferred_network_context_free);
+
+ mm_obj_dbg (self, "setting preferred networks...");
+
+ input = qmi_message_nas_set_preferred_networks_input_new ();
+
+ preferred_nets_array = g_array_new (FALSE, TRUE, sizeof (QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement));
+ preferred_nets_mnc_pcs_digit_array = g_array_new (FALSE, TRUE, sizeof (QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement));
+
+ while (preferred_network_list) {
+ QmiMessageNasSetPreferredNetworksInputPreferredNetworksElement preferred_nets_element;
+ QmiMessageNasSetPreferredNetworksInputMncPcsDigitIncludeStatusElement pcs_digit_element;
+ const gchar *operator_code;
+ MMModemAccessTechnology act;
+
+ memset (&preferred_nets_element, 0, sizeof (preferred_nets_element));
+ memset (&pcs_digit_element, 0, sizeof (pcs_digit_element));
+
+ operator_code = mm_sim_preferred_network_get_operator_code (preferred_network_list->data);
+ act = mm_sim_preferred_network_get_access_technology (preferred_network_list->data);
+ if (mm_3gpp_parse_operator_id (operator_code, &preferred_nets_element.mcc, &preferred_nets_element.mnc,
+ &pcs_digit_element.includes_pcs_digit, NULL)) {
+ pcs_digit_element.mcc = preferred_nets_element.mcc;
+ pcs_digit_element.mnc = preferred_nets_element.mnc;
+
+ preferred_nets_element.radio_access_technology = QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UNSPECIFIED;
+ if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM)
+ preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM;
+ if (act & MM_MODEM_ACCESS_TECHNOLOGY_GSM_COMPACT)
+ preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_GSM_COMPACT;
+ if (act & MM_MODEM_ACCESS_TECHNOLOGY_UMTS)
+ preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_UTRAN;
+ if (act & MM_MODEM_ACCESS_TECHNOLOGY_LTE)
+ preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_EUTRAN;
+ if (act & MM_MODEM_ACCESS_TECHNOLOGY_5GNR)
+ preferred_nets_element.radio_access_technology |= QMI_NAS_PLMN_ACCESS_TECHNOLOGY_IDENTIFIER_NGRAN;
+
+ g_array_append_val (preferred_nets_array, preferred_nets_element);
+ g_array_append_val (preferred_nets_mnc_pcs_digit_array, pcs_digit_element);
+ }
+
+ preferred_network_list = g_list_next (preferred_network_list);
+ }
+
+ qmi_message_nas_set_preferred_networks_input_set_preferred_networks (input, preferred_nets_array, NULL);
+ qmi_message_nas_set_preferred_networks_input_set_mnc_pcs_digit_include_status (input, preferred_nets_mnc_pcs_digit_array, NULL);
+ /* Always clear any pre-existing networks */
+ qmi_message_nas_set_preferred_networks_input_set_clear_previous_preferred_networks (input, TRUE, NULL);
+
+ qmi_client_nas_set_preferred_networks (QMI_CLIENT_NAS (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)set_preferred_networks_ready,
+ task);
+
+ qmi_message_nas_set_preferred_networks_input_unref (input);
+ g_array_unref (preferred_nets_array);
+ g_array_unref (preferred_nets_mnc_pcs_digit_array);
}
/*****************************************************************************/
@@ -261,17 +1088,75 @@ pin_qmi_error_to_mobile_equipment_error (GError *qmi_error)
}
static gboolean
-send_pin_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+send_pin_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+uim_verify_pin_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageUimVerifyPinOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_uim_verify_pin_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_uim_verify_pin_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't verify PIN: ");
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (output)
+ qmi_message_uim_verify_pin_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+uim_verify_pin (MMSimQmi *self,
+ GTask *task)
+{
+ QmiMessageUimVerifyPinInput *input;
+ QmiClient *client = NULL;
+ GArray *aid;
+
+ if (!ensure_qmi_client (task,
+ self,
+ QMI_SERVICE_UIM, &client))
+ return;
+
+ input = qmi_message_uim_verify_pin_input_new ();
+ qmi_message_uim_verify_pin_input_set_info (
+ input,
+ QMI_UIM_PIN_ID_PIN1,
+ g_task_get_task_data (task),
+ NULL);
+ aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
+ qmi_message_uim_verify_pin_input_set_session (
+ input,
+ QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
+ aid,
+ NULL);
+ g_array_unref (aid);
+ qmi_client_uim_verify_pin (QMI_CLIENT_UIM (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) uim_verify_pin_ready,
+ task);
+ qmi_message_uim_verify_pin_input_unref (input);
}
static void
dms_uim_verify_pin_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsUimVerifyPinOutput *output = NULL;
GError *error = NULL;
@@ -279,73 +1164,161 @@ dms_uim_verify_pin_ready (QmiClientDms *client,
output = qmi_client_dms_uim_verify_pin_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_uim_verify_pin_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't verify PIN: ");
- g_simple_async_result_take_error (simple,
- pin_qmi_error_to_mobile_equipment_error (error));
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
if (output)
qmi_message_dms_uim_verify_pin_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-send_pin (MMBaseSim *self,
- const gchar *pin,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_verify_pin (MMSimQmi *self,
+ GTask *task)
{
QmiMessageDmsUimVerifyPinInput *input;
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
- if (!ensure_qmi_client (MM_SIM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ if (!ensure_qmi_client (NULL,
+ self,
+ QMI_SERVICE_DMS, &client)) {
+ /* Very unlikely that this will ever happen, but anyway, try with
+ * UIM service instead */
+ uim_verify_pin (self, task);
return;
+ }
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- send_pin);
-
- mm_dbg ("Sending PIN...");
+ mm_obj_dbg (self, "sending PIN...");
input = qmi_message_dms_uim_verify_pin_input_new ();
qmi_message_dms_uim_verify_pin_input_set_info (
input,
QMI_DMS_UIM_PIN_ID_PIN,
- pin,
+ g_task_get_task_data (task),
NULL);
qmi_client_dms_uim_verify_pin (QMI_CLIENT_DMS (client),
input,
5,
NULL,
- (GAsyncReadyCallback)dms_uim_verify_pin_ready,
- result);
+ (GAsyncReadyCallback) dms_uim_verify_pin_ready,
+ task);
qmi_message_dms_uim_verify_pin_input_unref (input);
}
+static void
+send_pin (MMBaseSim *_self,
+ const gchar *pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ MMSimQmi *self;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ g_task_set_task_data (task, g_strdup (pin), g_free);
+
+ mm_obj_dbg (self, "verifying PIN...");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_verify_pin (self, task);
+ else
+ uim_verify_pin (self, task);
+}
+
/*****************************************************************************/
/* Send PUK */
+typedef struct {
+ gchar *puk;
+ gchar *new_pin;
+} UnblockPinContext;
+
+static void
+unblock_pin_context_free (UnblockPinContext *ctx)
+{
+ g_free (ctx->puk);
+ g_free (ctx->new_pin);
+ g_slice_free (UnblockPinContext, ctx);
+}
+
static gboolean
-send_puk_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+send_puk_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+uim_unblock_pin_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageUimUnblockPinOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_uim_unblock_pin_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_uim_unblock_pin_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't unblock PIN: ");
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (output)
+ qmi_message_uim_unblock_pin_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+uim_unblock_pin (MMSimQmi *self,
+ GTask *task)
+{
+ QmiMessageUimUnblockPinInput *input;
+ QmiClient *client = NULL;
+ UnblockPinContext *ctx;
+ GArray *aid;
+
+ if (!ensure_qmi_client (task,
+ self,
+ QMI_SERVICE_UIM, &client))
+ return;
+
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_uim_unblock_pin_input_new ();
+ qmi_message_uim_unblock_pin_input_set_info (
+ input,
+ QMI_UIM_PIN_ID_PIN1,
+ ctx->puk,
+ ctx->new_pin,
+ NULL);
+ aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
+ qmi_message_uim_unblock_pin_input_set_session (
+ input,
+ QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
+ aid,
+ NULL);
+ g_array_unref (aid);
+ qmi_client_uim_unblock_pin (QMI_CLIENT_UIM (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) uim_unblock_pin_ready,
+ task);
+ qmi_message_uim_unblock_pin_input_unref (input);
}
static void
dms_uim_unblock_pin_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsUimUnblockPinOutput *output = NULL;
GError *error = NULL;
@@ -353,76 +1326,169 @@ dms_uim_unblock_pin_ready (QmiClientDms *client,
output = qmi_client_dms_uim_unblock_pin_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_uim_unblock_pin_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't unblock PIN: ");
- g_simple_async_result_take_error (simple,
- pin_qmi_error_to_mobile_equipment_error (error));
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
if (output)
qmi_message_dms_uim_unblock_pin_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-send_puk (MMBaseSim *self,
- const gchar *puk,
- const gchar *new_pin,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_unblock_pin (MMSimQmi *self,
+ GTask *task)
{
QmiMessageDmsUimUnblockPinInput *input;
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_SIM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ UnblockPinContext *ctx;
+
+ if (!ensure_qmi_client (NULL,
+ self,
+ QMI_SERVICE_DMS, &client)) {
+ /* Very unlikely that this will ever happen, but anyway, try with
+ * UIM service instead */
+ uim_unblock_pin (self, task);
return;
+ }
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- send_puk);
-
- mm_dbg ("Sending PUK...");
+ ctx = g_task_get_task_data (task);
input = qmi_message_dms_uim_unblock_pin_input_new ();
qmi_message_dms_uim_unblock_pin_input_set_info (
input,
QMI_DMS_UIM_PIN_ID_PIN,
- puk,
- new_pin,
+ ctx->puk,
+ ctx->new_pin,
NULL);
qmi_client_dms_uim_unblock_pin (QMI_CLIENT_DMS (client),
input,
5,
NULL,
(GAsyncReadyCallback)dms_uim_unblock_pin_ready,
- result);
+ task);
qmi_message_dms_uim_unblock_pin_input_unref (input);
}
+static void
+send_puk (MMBaseSim *_self,
+ const gchar *puk,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ UnblockPinContext *ctx;
+ MMSimQmi *self;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new (UnblockPinContext);
+ ctx->puk = g_strdup (puk);
+ ctx->new_pin = g_strdup (new_pin);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) unblock_pin_context_free);
+
+ mm_obj_dbg (self, "unblocking PIN...");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_unblock_pin (self, task);
+ else
+ uim_unblock_pin (self, task);
+}
+
/*****************************************************************************/
/* Change PIN */
+typedef struct {
+ gchar *old_pin;
+ gchar *new_pin;
+} ChangePinContext;
+
+static void
+change_pin_context_free (ChangePinContext *ctx)
+{
+ g_free (ctx->old_pin);
+ g_free (ctx->new_pin);
+ g_slice_free (ChangePinContext, ctx);
+}
+
static gboolean
-change_pin_finish (MMBaseSim *self,
- GAsyncResult *res,
- GError **error)
+change_pin_finish (MMBaseSim *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+uim_change_pin_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageUimChangePinOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_uim_change_pin_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_uim_change_pin_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't change PIN: ");
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (output)
+ qmi_message_uim_change_pin_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+uim_change_pin (MMSimQmi *self,
+ GTask *task)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ QmiMessageUimChangePinInput *input;
+ QmiClient *client = NULL;
+ ChangePinContext *ctx;
+ GArray *aid;
+
+ if (!ensure_qmi_client (task,
+ self,
+ QMI_SERVICE_UIM, &client))
+ return;
+
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_uim_change_pin_input_new ();
+ qmi_message_uim_change_pin_input_set_info (
+ input,
+ QMI_UIM_PIN_ID_PIN1,
+ ctx->old_pin,
+ ctx->new_pin,
+ NULL);
+ aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
+ qmi_message_uim_change_pin_input_set_session (
+ input,
+ QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
+ aid,
+ NULL);
+ g_array_unref (aid);
+ qmi_client_uim_change_pin (QMI_CLIENT_UIM (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback) uim_change_pin_ready,
+ task);
+ qmi_message_uim_change_pin_input_unref (input);
}
static void
dms_uim_change_pin_ready (QmiClientDms *client,
- GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GAsyncResult *res,
+ GTask *task)
{
QmiMessageDmsUimChangePinOutput *output = NULL;
GError *error = NULL;
@@ -430,76 +1496,168 @@ dms_uim_change_pin_ready (QmiClientDms *client,
output = qmi_client_dms_uim_change_pin_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_uim_change_pin_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't change PIN: ");
- g_simple_async_result_take_error (simple,
- pin_qmi_error_to_mobile_equipment_error (error));
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
if (output)
qmi_message_dms_uim_change_pin_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-change_pin (MMBaseSim *self,
- const gchar *old_pin,
- const gchar *new_pin,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_change_pin (MMSimQmi *self,
+ GTask *task)
{
QmiMessageDmsUimChangePinInput *input;
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_SIM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ ChangePinContext *ctx;
+
+ if (!ensure_qmi_client (NULL,
+ self,
+ QMI_SERVICE_DMS, &client)) {
+ /* Very unlikely that this will ever happen, but anyway, try with
+ * UIM service instead */
+ uim_change_pin (self, task);
return;
+ }
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- change_pin);
-
- mm_dbg ("Changing PIN...");
+ ctx = g_task_get_task_data (task);
input = qmi_message_dms_uim_change_pin_input_new ();
qmi_message_dms_uim_change_pin_input_set_info (
input,
QMI_DMS_UIM_PIN_ID_PIN,
- old_pin,
- new_pin,
+ ctx->old_pin,
+ ctx->new_pin,
NULL);
qmi_client_dms_uim_change_pin (QMI_CLIENT_DMS (client),
input,
5,
NULL,
- (GAsyncReadyCallback)dms_uim_change_pin_ready,
- result);
+ (GAsyncReadyCallback) dms_uim_change_pin_ready,
+ task);
qmi_message_dms_uim_change_pin_input_unref (input);
}
+static void
+change_pin (MMBaseSim *_self,
+ const gchar *old_pin,
+ const gchar *new_pin,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ ChangePinContext *ctx;
+ MMSimQmi *self;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new (ChangePinContext);
+ ctx->old_pin = g_strdup (old_pin);
+ ctx->new_pin = g_strdup (new_pin);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) change_pin_context_free);
+
+ mm_obj_dbg (self, "changing PIN...");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_change_pin (self, task);
+ else
+ uim_change_pin (self, task);
+}
+
/*****************************************************************************/
/* Enable PIN */
+typedef struct {
+ gchar *pin;
+ gboolean enabled;
+} EnablePinContext;
+
+static void
+enable_pin_context_free (EnablePinContext *ctx)
+{
+ g_free (ctx->pin);
+ g_slice_free (EnablePinContext, ctx);
+}
+
static gboolean
enable_pin_finish (MMBaseSim *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+uim_set_pin_protection_ready (QmiClientUim *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ QmiMessageUimSetPinProtectionOutput *output = NULL;
+ GError *error = NULL;
+
+ output = qmi_client_uim_set_pin_protection_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_task_return_error (task, error);
+ } else if (!qmi_message_uim_set_pin_protection_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't enable PIN: ");
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
+
+ if (output)
+ qmi_message_uim_set_pin_protection_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+uim_enable_pin (MMSimQmi *self,
+ GTask *task)
+{
+ QmiMessageUimSetPinProtectionInput *input;
+ QmiClient *client = NULL;
+ EnablePinContext *ctx;
+ GArray *aid;
+
+ if (!ensure_qmi_client (task,
+ MM_SIM_QMI (self),
+ QMI_SERVICE_UIM, &client))
+ return;
+
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_uim_set_pin_protection_input_new ();
+ qmi_message_uim_set_pin_protection_input_set_info (
+ input,
+ QMI_UIM_PIN_ID_PIN1,
+ ctx->enabled,
+ ctx->pin,
+ NULL);
+ aid = g_array_new (FALSE, FALSE, sizeof (guint8)); /* empty AID */
+ qmi_message_uim_set_pin_protection_input_set_session (
+ input,
+ QMI_UIM_SESSION_TYPE_CARD_SLOT_1,
+ aid,
+ NULL);
+ g_array_unref (aid);
+ qmi_client_uim_set_pin_protection (QMI_CLIENT_UIM (client),
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)uim_set_pin_protection_ready,
+ task);
+ qmi_message_uim_set_pin_protection_input_unref (input);
}
static void
dms_uim_set_pin_protection_ready (QmiClientDms *client,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ GTask *task)
{
QmiMessageDmsUimSetPinProtectionOutput *output = NULL;
GError *error = NULL;
@@ -507,62 +1665,79 @@ dms_uim_set_pin_protection_ready (QmiClientDms *client,
output = qmi_client_dms_uim_set_pin_protection_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (simple, error);
+ g_task_return_error (task, error);
} else if (!qmi_message_dms_uim_set_pin_protection_output_get_result (output, &error)) {
g_prefix_error (&error, "Couldn't enable PIN: ");
- g_simple_async_result_take_error (simple,
- pin_qmi_error_to_mobile_equipment_error (error));
- } else {
- g_simple_async_result_set_op_res_gboolean (simple, TRUE);
- }
+ g_task_return_error (task, pin_qmi_error_to_mobile_equipment_error (error));
+ } else
+ g_task_return_boolean (task, TRUE);
if (output)
qmi_message_dms_uim_set_pin_protection_output_unref (output);
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ g_object_unref (task);
}
static void
-enable_pin (MMBaseSim *self,
- const gchar *pin,
- gboolean enabled,
- GAsyncReadyCallback callback,
- gpointer user_data)
+dms_uim_enable_pin (MMSimQmi *self,
+ GTask *task)
{
QmiMessageDmsUimSetPinProtectionInput *input;
- GSimpleAsyncResult *result;
QmiClient *client = NULL;
-
- if (!ensure_qmi_client (MM_SIM_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
+ EnablePinContext *ctx;
+
+ if (!ensure_qmi_client (NULL,
+ MM_SIM_QMI (self),
+ QMI_SERVICE_DMS, &client)) {
+ /* Very unlikely that this will ever happen, but anyway, try with
+ * UIM service instead */
+ uim_enable_pin (self, task);
return;
+ }
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- enable_pin);
-
- mm_dbg ("%s PIN...",
- enabled ? "Enabling" : "Disabling");
+ ctx = g_task_get_task_data (task);
input = qmi_message_dms_uim_set_pin_protection_input_new ();
qmi_message_dms_uim_set_pin_protection_input_set_info (
input,
QMI_DMS_UIM_PIN_ID_PIN,
- enabled,
- pin,
+ ctx->enabled,
+ ctx->pin,
NULL);
qmi_client_dms_uim_set_pin_protection (QMI_CLIENT_DMS (client),
input,
5,
NULL,
(GAsyncReadyCallback)dms_uim_set_pin_protection_ready,
- result);
+ task);
qmi_message_dms_uim_set_pin_protection_input_unref (input);
}
+static void
+enable_pin (MMBaseSim *_self,
+ const gchar *pin,
+ gboolean enabled,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ EnablePinContext *ctx;
+ MMSimQmi *self;
+
+ self = MM_SIM_QMI (_self);
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new (EnablePinContext);
+ ctx->pin = g_strdup (pin);
+ ctx->enabled = enabled;
+ g_task_set_task_data (task, ctx, (GDestroyNotify) enable_pin_context_free);
+
+ mm_obj_dbg (self, "%s PIN...", enabled ? "enabling" : "disabling");
+ if (!self->priv->dms_uim_deprecated)
+ dms_uim_enable_pin (self, task);
+ else
+ uim_enable_pin (self, task);
+}
+
/*****************************************************************************/
MMBaseSim *
@@ -586,10 +1761,11 @@ mm_sim_qmi_new_finish (GAsyncResult *res,
}
void
-mm_sim_qmi_new (MMBaseModem *modem,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+mm_sim_qmi_new (MMBaseModem *modem,
+ gboolean dms_uim_deprecated,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
g_async_initable_new_async (MM_TYPE_SIM_QMI,
G_PRIORITY_DEFAULT,
@@ -597,27 +1773,114 @@ mm_sim_qmi_new (MMBaseModem *modem,
callback,
user_data,
MM_BASE_SIM_MODEM, modem,
+ MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated,
+ "active", TRUE, /* by default always active */
NULL);
}
+MMBaseSim *
+mm_sim_qmi_new_initialized (MMBaseModem *modem,
+ gboolean dms_uim_deprecated,
+ guint slot_number,
+ gboolean active,
+ const gchar *sim_identifier,
+ const gchar *imsi,
+ const gchar *eid,
+ const gchar *operator_identifier,
+ const gchar *operator_name,
+ const GStrv emergency_numbers)
+{
+ MMBaseSim *sim;
+
+ sim = MM_BASE_SIM (g_object_new (MM_TYPE_SIM_QMI,
+ MM_BASE_SIM_MODEM, modem,
+ MM_SIM_QMI_DMS_UIM_DEPRECATED, dms_uim_deprecated,
+ MM_BASE_SIM_SLOT_NUMBER, slot_number,
+ "active", active,
+ "sim-identifier", sim_identifier,
+ "imsi", imsi,
+ "eid", eid,
+ "operator-identifier", operator_identifier,
+ "operator-name", operator_name,
+ "emergency-numbers", emergency_numbers,
+ NULL));
+
+ mm_base_sim_export (sim);
+ return sim;
+}
+
+/*****************************************************************************/
+
static void
mm_sim_qmi_init (MMSimQmi *self)
{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_SIM_QMI,
+ MMSimQmiPrivate);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ MMSimQmi *self = MM_SIM_QMI (object);
+
+ switch (prop_id) {
+ case PROP_DMS_UIM_DEPRECATED:
+ self->priv->dms_uim_deprecated = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ MMSimQmi *self = MM_SIM_QMI (object);
+
+ switch (prop_id) {
+ case PROP_DMS_UIM_DEPRECATED:
+ g_value_set_boolean (value, self->priv->dms_uim_deprecated);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
mm_sim_qmi_class_init (MMSimQmiClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBaseSimClass *base_sim_class = MM_BASE_SIM_CLASS (klass);
+ g_type_class_add_private (object_class, sizeof (MMSimQmiPrivate));
+
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ base_sim_class->wait_sim_ready = wait_sim_ready;
+ base_sim_class->wait_sim_ready_finish = wait_sim_ready_finish;
base_sim_class->load_sim_identifier = load_sim_identifier;
base_sim_class->load_sim_identifier_finish = load_sim_identifier_finish;
base_sim_class->load_imsi = load_imsi;
base_sim_class->load_imsi_finish = load_imsi_finish;
- base_sim_class->load_operator_identifier = NULL;
- base_sim_class->load_operator_identifier_finish = NULL;
- base_sim_class->load_operator_name = NULL;
- base_sim_class->load_operator_name_finish = NULL;
+ base_sim_class->load_operator_identifier = load_operator_identifier;
+ base_sim_class->load_operator_identifier_finish = load_operator_identifier_finish;
+ base_sim_class->load_operator_name = load_operator_name;
+ base_sim_class->load_operator_name_finish = load_operator_name_finish;
+ base_sim_class->load_preferred_networks = load_preferred_networks;
+ base_sim_class->load_preferred_networks_finish = load_preferred_networks_finish;
+ base_sim_class->set_preferred_networks = set_preferred_networks;
+ base_sim_class->set_preferred_networks_finish = set_preferred_networks_finish;
base_sim_class->send_pin = send_pin;
base_sim_class->send_pin_finish = send_pin_finish;
base_sim_class->send_puk = send_puk;
@@ -626,4 +1889,12 @@ mm_sim_qmi_class_init (MMSimQmiClass *klass)
base_sim_class->change_pin_finish = change_pin_finish;
base_sim_class->enable_pin = enable_pin;
base_sim_class->enable_pin_finish = enable_pin_finish;
+
+ properties[PROP_DMS_UIM_DEPRECATED] =
+ g_param_spec_boolean (MM_SIM_QMI_DMS_UIM_DEPRECATED,
+ "DMS UIM deprecated",
+ "Whether DMS UIM commands should be skipped",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+ g_object_class_install_property (object_class, PROP_DMS_UIM_DEPRECATED, properties[PROP_DMS_UIM_DEPRECATED]);
}
diff --git a/src/mm-sim-qmi.h b/src/mm-sim-qmi.h
index 070f98f6..c0fe91b9 100644
--- a/src/mm-sim-qmi.h
+++ b/src/mm-sim-qmi.h
@@ -30,9 +30,13 @@
typedef struct _MMSimQmi MMSimQmi;
typedef struct _MMSimQmiClass MMSimQmiClass;
+typedef struct _MMSimQmiPrivate MMSimQmiPrivate;
+
+#define MM_SIM_QMI_DMS_UIM_DEPRECATED "dms-uim-deprecated"
struct _MMSimQmi {
MMBaseSim parent;
+ MMSimQmiPrivate *priv;
};
struct _MMSimQmiClass {
@@ -40,12 +44,25 @@ struct _MMSimQmiClass {
};
GType mm_sim_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSimQmi, g_object_unref)
+
+void mm_sim_qmi_new (MMBaseModem *modem,
+ gboolean dms_uim_deprecated,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMBaseSim *mm_sim_qmi_new_finish (GAsyncResult *res,
+ GError **error);
-void mm_sim_qmi_new (MMBaseModem *modem,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-MMBaseSim *mm_sim_qmi_new_finish (GAsyncResult *res,
- GError **error);
+MMBaseSim *mm_sim_qmi_new_initialized (MMBaseModem *modem,
+ gboolean dms_uim_deprecated,
+ guint slot_number,
+ gboolean active,
+ const gchar *sim_identifier,
+ const gchar *imsi,
+ const gchar *eid,
+ const gchar *operator_identifier,
+ const gchar *operator_name,
+ const GStrv emergency_numbers);
#endif /* MM_SIM_QMI_H */
diff --git a/src/mm-sleep-monitor.c b/src/mm-sleep-monitor.c
new file mode 100644
index 00000000..d3d181c1
--- /dev/null
+++ b/src/mm-sleep-monitor.c
@@ -0,0 +1,265 @@
+/* -*- 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2012 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+
+#include "mm-log-object.h"
+#include "mm-utils.h"
+#include "mm-sleep-monitor.h"
+
+#define SD_NAME "org.freedesktop.login1"
+#define SD_PATH "/org/freedesktop/login1"
+#define SD_INTERFACE "org.freedesktop.login1.Manager"
+
+
+struct _MMSleepMonitor {
+ GObject parent_instance;
+
+ GDBusProxy *sd_proxy;
+ gint inhibit_fd;
+};
+
+struct _MMSleepMonitorClass {
+ GObjectClass parent_class;
+
+ void (*sleeping) (MMSleepMonitor *monitor);
+ void (*resuming) (MMSleepMonitor *monitor);
+};
+
+
+enum {
+ SLEEPING,
+ RESUMING,
+ LAST_SIGNAL,
+};
+static guint signals[LAST_SIGNAL] = {0};
+
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMSleepMonitor, mm_sleep_monitor, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
+
+/*****************************************************************************/
+
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("sleep-monitor");
+}
+
+/********************************************************************/
+
+static gboolean
+drop_inhibitor (MMSleepMonitor *self)
+{
+ if (self->inhibit_fd >= 0) {
+ mm_obj_dbg (self, "dropping systemd sleep inhibitor");
+ close (self->inhibit_fd);
+ self->inhibit_fd = -1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+inhibit_done (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusProxy *sd_proxy = G_DBUS_PROXY (source);
+ MMSleepMonitor *self = user_data;
+ GError *error = NULL;
+ GVariant *res;
+ GUnixFDList *fd_list;
+
+ res = g_dbus_proxy_call_with_unix_fd_list_finish (sd_proxy, &fd_list, result, &error);
+ if (!res) {
+ mm_obj_warn (self, "inhibit failed: %s", error->message);
+ g_error_free (error);
+ } else {
+ if (!fd_list || g_unix_fd_list_get_length (fd_list) != 1)
+ mm_obj_warn (self, "didn't get a single fd back");
+
+ self->inhibit_fd = g_unix_fd_list_get (fd_list, 0, NULL);
+
+ mm_obj_dbg (self, "inhibitor fd is %d", self->inhibit_fd);
+ g_object_unref (fd_list);
+ g_variant_unref (res);
+ }
+}
+
+static void
+take_inhibitor (MMSleepMonitor *self)
+{
+ g_assert (self->inhibit_fd == -1);
+
+ mm_obj_dbg (self, "taking systemd sleep inhibitor");
+ g_dbus_proxy_call_with_unix_fd_list (self->sd_proxy,
+ "Inhibit",
+ g_variant_new ("(ssss)",
+ "sleep",
+ "ModemManager",
+ _("ModemManager needs to reset devices"),
+ "delay"),
+ 0,
+ G_MAXINT,
+ NULL,
+ NULL,
+ inhibit_done,
+ self);
+}
+
+static void
+signal_cb (GDBusProxy *proxy,
+ const gchar *sendername,
+ const gchar *signalname,
+ GVariant *args,
+ gpointer data)
+{
+ MMSleepMonitor *self = data;
+ gboolean is_about_to_suspend;
+
+ if (strcmp (signalname, "PrepareForSleep") != 0)
+ return;
+
+ g_variant_get (args, "(b)", &is_about_to_suspend);
+
+ if (is_about_to_suspend) {
+ mm_obj_info (self, "system is about to suspend");
+ g_signal_emit (self, signals[SLEEPING], 0);
+ drop_inhibitor (self);
+ } else {
+ mm_obj_info (self, "system is resuming");
+ take_inhibitor (self);
+ g_signal_emit (self, signals[RESUMING], 0);
+ }
+}
+
+static void
+name_owner_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GDBusProxy *proxy = G_DBUS_PROXY (object);
+ MMSleepMonitor *self = MM_SLEEP_MONITOR (user_data);
+ char *owner;
+
+ g_assert (proxy == self->sd_proxy);
+
+ owner = g_dbus_proxy_get_name_owner (proxy);
+ if (owner)
+ take_inhibitor (self);
+ else
+ drop_inhibitor (self);
+ g_free (owner);
+}
+
+static void
+on_proxy_acquired (GObject *object,
+ GAsyncResult *res,
+ MMSleepMonitor *self)
+{
+ GError *error = NULL;
+ char *owner;
+
+ self->sd_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+ if (!self->sd_proxy) {
+ mm_obj_warn (self, "failed to acquire logind proxy: %s", error->message);
+ g_clear_error (&error);
+ return;
+ }
+
+ g_signal_connect (self->sd_proxy, "notify::g-name-owner", G_CALLBACK (name_owner_cb), self);
+ g_signal_connect (self->sd_proxy, "g-signal", G_CALLBACK (signal_cb), self);
+
+ owner = g_dbus_proxy_get_name_owner (self->sd_proxy);
+ if (owner)
+ take_inhibitor (self);
+ g_free (owner);
+}
+
+static void
+mm_sleep_monitor_init (MMSleepMonitor *self)
+{
+ self->inhibit_fd = -1;
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ SD_NAME, SD_PATH, SD_INTERFACE,
+ NULL,
+ (GAsyncReadyCallback) on_proxy_acquired, self);
+}
+
+static void
+finalize (GObject *object)
+{
+ MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
+
+ drop_inhibitor (self);
+ if (self->sd_proxy)
+ g_object_unref (self->sd_proxy);
+
+ if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
+}
+
+static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
+mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->finalize = finalize;
+
+ signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
+ MM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
+ MM_TYPE_SLEEP_MONITOR,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (MMSleepMonitorClass, resuming),
+ NULL, /* accumulator */
+ NULL, /* accumulator data */
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+MM_DEFINE_SINGLETON_GETTER (MMSleepMonitor, mm_sleep_monitor_get, MM_TYPE_SLEEP_MONITOR);
+
+/* ---------------------------------------------------------------------------------------------------- */
diff --git a/src/mm-sleep-monitor.h b/src/mm-sleep-monitor.h
new file mode 100644
index 00000000..2fac5517
--- /dev/null
+++ b/src/mm-sleep-monitor.h
@@ -0,0 +1,43 @@
+/* -*- 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.
+ *
+ * (C) Copyright 2012 Red Hat, Inc.
+ * Author: Matthias Clasen <mclasen@redhat.com>
+ * Original code imported from NetworkManager.
+ */
+
+#ifndef __MM_SLEEP_MONITOR_H__
+#define __MM_SLEEP_MONITOR_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MM_TYPE_SLEEP_MONITOR (mm_sleep_monitor_get_type ())
+#define MM_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), MM_TYPE_SLEEP_MONITOR, MMSleepMonitor))
+#define MM_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), MM_TYPE_SLEEP_MONITOR, MMSleepMonitorClass))
+#define MM_SLEEP_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MM_TYPE_SLEEP_MONITOR, MMSleepMonitorClass))
+#define MM_IS_SLEEP_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MM_TYPE_SLEEP_MONITOR))
+#define MM_IS_SLEEP_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), MM_TYPE_SLEEP_MONITOR))
+
+#define MM_SLEEP_MONITOR_SLEEPING "sleeping"
+#define MM_SLEEP_MONITOR_RESUMING "resuming"
+
+typedef struct _MMSleepMonitor MMSleepMonitor;
+typedef struct _MMSleepMonitorClass MMSleepMonitorClass;
+
+GType mm_sleep_monitor_get_type (void) G_GNUC_CONST;
+MMSleepMonitor *mm_sleep_monitor_get (void);
+
+G_END_DECLS
+
+#endif /* __MM_SLEEP_MONITOR_H__ */
diff --git a/src/mm-sms-list.c b/src/mm-sms-list.c
index e98bd8c2..1a832216 100644
--- a/src/mm-sms-list.c
+++ b/src/mm-sms-list.c
@@ -27,9 +27,12 @@
#include "mm-iface-modem-messaging.h"
#include "mm-sms-list.h"
#include "mm-base-sms.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
-G_DEFINE_TYPE (MMSmsList, mm_sms_list, G_TYPE_OBJECT);
+static void log_object_iface_init (MMLogObjectInterface *iface);
+
+G_DEFINE_TYPE_EXTENDED (MMSmsList, mm_sms_list, G_TYPE_OBJECT, 0,
+ G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
enum {
PROP_0,
@@ -113,28 +116,12 @@ mm_sms_list_get_paths (MMSmsList *self)
/*****************************************************************************/
-typedef struct {
- MMSmsList *self;
- GSimpleAsyncResult *result;
- gchar *path;
-} DeleteSmsContext;
-
-static void
-delete_sms_context_complete_and_free (DeleteSmsContext *ctx)
-{
- g_simple_async_result_complete (ctx->result);
- g_object_unref (ctx->result);
- g_object_unref (ctx->self);
- g_free (ctx->path);
- g_free (ctx);
-}
-
gboolean
mm_sms_list_delete_sms_finish (MMSmsList *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
static guint
@@ -147,25 +134,29 @@ cmp_sms_by_path (MMBaseSms *sms,
static void
delete_ready (MMBaseSms *sms,
GAsyncResult *res,
- DeleteSmsContext *ctx)
+ GTask *task)
{
+ MMSmsList *self;
+ const gchar *path;
GError *error = NULL;
GList *l;
if (!mm_base_sms_delete_finish (sms, res, &error)) {
/* We report the error */
- g_simple_async_result_take_error (ctx->result, error);
- delete_sms_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ path = g_task_get_task_data (task);
/* The SMS was properly deleted, we now remove it from our list */
- l = g_list_find_custom (ctx->self->priv->list,
- ctx->path,
+ l = g_list_find_custom (self->priv->list,
+ path,
(GCompareFunc)cmp_sms_by_path);
if (l) {
g_object_unref (MM_BASE_SMS (l->data));
- ctx->self->priv->list = g_list_delete_link (ctx->self->priv->list, l);
+ self->priv->list = g_list_delete_link (self->priv->list, l);
}
/* We don't need to unref the SMS any more, but we can use the
@@ -173,12 +164,12 @@ delete_ready (MMBaseSms *sms,
* during the async operation. */
mm_base_sms_unexport (sms);
- g_signal_emit (ctx->self,
+ g_signal_emit (self,
signals[SIGNAL_DELETED], 0,
- ctx->path);
+ path);
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- delete_sms_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
void
@@ -187,35 +178,31 @@ mm_sms_list_delete_sms (MMSmsList *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- DeleteSmsContext *ctx;
GList *l;
+ GTask *task;
l = g_list_find_custom (self->priv->list,
(gpointer)sms_path,
(GCompareFunc)cmp_sms_by_path);
if (!l) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_NOT_FOUND,
- "No SMS found with path '%s'",
- sms_path);
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ mm_sms_list_delete_sms,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_NOT_FOUND,
+ "No SMS found with path '%s'",
+ sms_path);
return;
}
/* Delete all SMS parts */
- ctx = g_new0 (DeleteSmsContext, 1);
- ctx->self = g_object_ref (self);
- ctx->path = g_strdup (sms_path);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- mm_sms_list_delete_sms);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, g_strdup (sms_path), g_free);
mm_base_sms_delete (MM_BASE_SMS (l->data),
(GAsyncReadyCallback)delete_ready,
- ctx);
+ task);
}
/*****************************************************************************/
@@ -294,9 +281,11 @@ take_multipart (MMSmsList *self,
l = g_list_find_custom (self->priv->list,
GUINT_TO_POINTER (concat_reference),
(GCompareFunc)cmp_sms_by_concat_reference);
- if (l)
+ if (l) {
/* Try to take the part */
+ mm_obj_dbg (self, "found existing multipart SMS object with reference '%u': adding new part", concat_reference);
return mm_base_sms_multipart_take_part (MM_BASE_SMS (l->data), part, error);
+ }
/* Create new Multipart */
sms = mm_base_sms_multipart_new (self->priv->modem,
@@ -309,6 +298,9 @@ take_multipart (MMSmsList *self,
if (!sms)
return FALSE;
+ mm_obj_dbg (self, "creating new multipart SMS object: need to receive %u parts with reference '%u'",
+ mm_sms_part_get_concat_max (part),
+ concat_reference);
self->priv->list = g_list_prepend (self->priv->list, sms);
g_signal_emit (self, signals[SIGNAL_ADDED], 0,
mm_base_sms_get_path (sms),
@@ -344,11 +336,6 @@ mm_sms_list_take_part (MMSmsList *self,
MMSmsStorage storage,
GError **error)
{
- PartIndexAndStorage ctx;
-
- ctx.part_index = mm_sms_part_get_index (part);
- ctx.storage = storage;
-
/* Ensure we don't have already taken a part with the same index */
if (mm_sms_list_has_part (self,
storage,
@@ -364,31 +351,41 @@ mm_sms_list_take_part (MMSmsList *self,
/* Did we just get a part of a multi-part SMS? */
if (mm_sms_part_should_concat (part)) {
if (mm_sms_part_get_index (part) != SMS_PART_INVALID_INDEX)
- mm_dbg ("SMS part at '%s/%u' is from a multipart SMS (reference: '%u', sequence: '%u')",
- mm_sms_storage_get_string (storage),
- mm_sms_part_get_index (part),
- mm_sms_part_get_concat_reference (part),
- mm_sms_part_get_concat_sequence (part));
+ mm_obj_dbg (self, "SMS part at '%s/%u' is from a multipart SMS (reference: '%u', sequence: '%u/%u')",
+ mm_sms_storage_get_string (storage),
+ mm_sms_part_get_index (part),
+ mm_sms_part_get_concat_reference (part),
+ mm_sms_part_get_concat_sequence (part),
+ mm_sms_part_get_concat_max (part));
else
- mm_dbg ("SMS part (not stored) is from a multipart SMS (reference: '%u', sequence: '%u')",
- mm_sms_part_get_concat_reference (part),
- mm_sms_part_get_concat_sequence (part));
+ mm_obj_dbg (self, "SMS part (not stored) is from a multipart SMS (reference: '%u', sequence: '%u/%u')",
+ mm_sms_part_get_concat_reference (part),
+ mm_sms_part_get_concat_sequence (part),
+ mm_sms_part_get_concat_max (part));
return take_multipart (self, part, state, storage, error);
}
/* Otherwise, we build a whole new single-part MMSms just from this part */
if (mm_sms_part_get_index (part) != SMS_PART_INVALID_INDEX)
- mm_dbg ("SMS part at '%s/%u' is from a singlepart SMS",
- mm_sms_storage_get_string (storage),
- mm_sms_part_get_index (part));
+ mm_obj_dbg (self, "SMS part at '%s/%u' is from a singlepart SMS",
+ mm_sms_storage_get_string (storage),
+ mm_sms_part_get_index (part));
else
- mm_dbg ("SMS part (not stored) is from a singlepart SMS");
+ mm_obj_dbg (self, "SMS part (not stored) is from a singlepart SMS");
return take_singlepart (self, part, state, storage, error);
}
/*****************************************************************************/
+static gchar *
+log_object_build_id (MMLogObject *_self)
+{
+ return g_strdup ("sms-list");
+}
+
+/*****************************************************************************/
+
MMSmsList *
mm_sms_list_new (MMBaseModem *modem)
{
@@ -410,6 +407,10 @@ set_property (GObject *object,
case PROP_MODEM:
g_clear_object (&self->priv->modem);
self->priv->modem = g_value_dup_object (value);
+ if (self->priv->modem) {
+ /* Set owner ID */
+ mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
+ }
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -450,12 +451,19 @@ dispose (GObject *object)
MMSmsList *self = MM_SMS_LIST (object);
g_clear_object (&self->priv->modem);
- g_list_free_full (self->priv->list, (GDestroyNotify)g_object_unref);
+ g_list_free_full (self->priv->list, g_object_unref);
+ self->priv->list = NULL;
G_OBJECT_CLASS (mm_sms_list_parent_class)->dispose (object);
}
static void
+log_object_iface_init (MMLogObjectInterface *iface)
+{
+ iface->build_id = log_object_build_id;
+}
+
+static void
mm_sms_list_class_init (MMSmsListClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
diff --git a/src/mm-sms-list.h b/src/mm-sms-list.h
index 29370657..8a77d6ca 100644
--- a/src/mm-sms-list.h
+++ b/src/mm-sms-list.h
@@ -55,6 +55,7 @@ struct _MMSmsListClass {
};
GType mm_sms_list_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsList, g_object_unref)
MMSmsList *mm_sms_list_new (MMBaseModem *modem);
diff --git a/src/mm-sms-mbim.c b/src/mm-sms-mbim.c
index 3bdd68a9..acbbee44 100644
--- a/src/mm-sms-mbim.c
+++ b/src/mm-sms-mbim.c
@@ -24,11 +24,12 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-broadband-modem-mbim.h"
#include "mm-modem-helpers-mbim.h"
#include "mm-iface-modem-messaging.h"
#include "mm-sms-mbim.h"
#include "mm-base-modem.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
#include "mm-sms-part-3gpp.h"
G_DEFINE_TYPE (MMSmsMbim, mm_sms_mbim, MM_TYPE_BASE_SMS)
@@ -51,14 +52,15 @@ peek_device (gpointer self,
if (o_device) {
MMPortMbim *port;
- port = mm_base_modem_peek_port_mbim (modem);
+ port = mm_broadband_modem_mbim_peek_port_mbim (MM_BROADBAND_MODEM_MBIM (modem));
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek MBIM port");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ peek_device,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek MBIM port");
g_object_unref (modem);
return FALSE;
}
@@ -74,21 +76,16 @@ peek_device (gpointer self,
/* Send the SMS */
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
MbimDevice *device;
- GSimpleAsyncResult *result;
GList *current;
} SmsSendContext;
static void
-sms_send_context_complete_and_free (SmsSendContext *ctx)
+sms_send_context_free (SmsSendContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->device);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (SmsSendContext, ctx);
}
@@ -97,23 +94,25 @@ sms_send_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void sms_send_next_part (SmsSendContext *ctx);
+static void sms_send_next_part (GTask *task);
static void
sms_send_set_ready (MbimDevice *device,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ SmsSendContext *ctx;
MbimMessage *response;
GError *error = NULL;
guint32 message_reference;
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error) &&
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
mbim_message_sms_send_response_parse (
response,
&message_reference,
@@ -127,19 +126,21 @@ sms_send_set_ready (MbimDevice *device,
if (error) {
g_prefix_error (&error, "Couldn't send SMS part: ");
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on with next part */
ctx->current = g_list_next (ctx->current);
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
static void
-sms_send_next_part (SmsSendContext *ctx)
+sms_send_next_part (GTask *task)
{
+ MMSmsMbim *self;
+ SmsSendContext *ctx;
MbimMessage *message;
guint8 *pdu;
guint pdulen = 0;
@@ -147,18 +148,21 @@ sms_send_next_part (SmsSendContext *ctx)
GError *error = NULL;
MbimSmsPduSendRecord send_record;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (!ctx->current) {
/* Done we are */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Get PDU */
- pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, &error);
+ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, self, &error);
if (!pdu) {
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -171,10 +175,10 @@ sms_send_next_part (SmsSendContext *ctx)
NULL);
mbim_device_command (ctx->device,
message,
- 30,
+ MM_BASE_SMS_DEFAULT_SEND_TIMEOUT,
NULL,
(GAsyncReadyCallback)sms_send_set_ready,
- ctx);
+ task);
mbim_message_unref (message);
g_free (pdu);
}
@@ -186,45 +190,39 @@ sms_send (MMBaseSms *self,
{
SmsSendContext *ctx;
MbimDevice *device;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
/* Setup the context */
ctx = g_slice_new0 (SmsSendContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_send);
- ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
g_object_get (self,
MM_BASE_SMS_MODEM, &ctx->modem,
NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free);
+
ctx->current = mm_base_sms_get_parts (self);;
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
/*****************************************************************************/
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
MbimDevice *device;
- GSimpleAsyncResult *result;
GList *current;
guint n_failed;
} SmsDeletePartsContext;
static void
-sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx)
+sms_delete_parts_context_free (SmsDeletePartsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->device);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (SmsDeletePartsContext, ctx);
}
@@ -233,22 +231,27 @@ sms_delete_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void delete_next_part (SmsDeletePartsContext *ctx);
+static void delete_next_part (GTask *task);
static void
sms_delete_set_ready (MbimDevice *device,
GAsyncResult *res,
- SmsDeletePartsContext *ctx)
+ GTask *task)
{
+ MMSmsMbim *self;
+ SmsDeletePartsContext *ctx;
MbimMessage *response;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
response = mbim_device_command_finish (device, res, &error);
if (response &&
- mbim_message_command_done_get_result (response, &error))
+ mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error))
mbim_message_sms_delete_response_parse (response, &error);
if (response)
@@ -256,9 +259,9 @@ sms_delete_set_ready (MbimDevice *device,
if (error) {
ctx->n_failed++;
- mm_dbg ("Couldn't delete SMS part with index %u: '%s'",
- mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
- error->message);
+ mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s",
+ mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
+ error->message);
g_error_free (error);
}
@@ -266,14 +269,16 @@ sms_delete_set_ready (MbimDevice *device,
mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX);
ctx->current = g_list_next (ctx->current);
- delete_next_part (ctx);
+ delete_next_part (task);
}
static void
-delete_next_part (SmsDeletePartsContext *ctx)
+delete_next_part (GTask *task)
{
+ SmsDeletePartsContext *ctx;
MbimMessage *message;
+ ctx = g_task_get_task_data (task);
/* Skip non-stored parts */
while (ctx->current &&
mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX)
@@ -282,15 +287,15 @@ delete_next_part (SmsDeletePartsContext *ctx)
/* If all removed, we're done */
if (!ctx->current) {
if (ctx->n_failed > 0)
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't delete %u parts from this SMS",
- ctx->n_failed);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't delete %u parts from this SMS",
+ ctx->n_failed);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
- sms_delete_parts_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
@@ -302,7 +307,7 @@ delete_next_part (SmsDeletePartsContext *ctx)
10,
NULL,
(GAsyncReadyCallback)sms_delete_set_ready,
- ctx);
+ task);
mbim_message_unref (message);
}
@@ -314,24 +319,23 @@ sms_delete (MMBaseSms *self,
{
SmsDeletePartsContext *ctx;
MbimDevice *device;
+ GTask *task;
if (!peek_device (self, &device, callback, user_data))
return;
ctx = g_slice_new0 (SmsDeletePartsContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_delete);
- ctx->self = g_object_ref (self);
ctx->device = g_object_ref (device);
g_object_get (self,
MM_BASE_SMS_MODEM, &ctx->modem,
NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free);
+
/* Go on deleting parts */
ctx->current = mm_base_sms_get_parts (self);
- delete_next_part (ctx);
+ delete_next_part (task);
}
/*****************************************************************************/
diff --git a/src/mm-sms-mbim.h b/src/mm-sms-mbim.h
index f2f6f3ac..5743f262 100644
--- a/src/mm-sms-mbim.h
+++ b/src/mm-sms-mbim.h
@@ -43,6 +43,7 @@ struct _MMSmsMbimClass {
};
GType mm_sms_mbim_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsMbim, g_object_unref)
MMBaseSms *mm_sms_mbim_new (MMBaseModem *modem);
diff --git a/src/mm-sms-part-3gpp.c b/src/mm-sms-part-3gpp.c
index b305be59..bfae03a8 100644
--- a/src/mm-sms-part-3gpp.c
+++ b/src/mm-sms-part-3gpp.c
@@ -23,6 +23,8 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-common-helpers.h"
+#include "mm-helper-enums-types.h"
#include "mm-sms-part-3gpp.h"
#include "mm-charsets.h"
#include "mm-log.h"
@@ -119,23 +121,26 @@ sms_string_to_bcd_semi_octets (guint8 *buf, gsize buflen, const char *string)
}
/* len is in semi-octets */
-static char *
-sms_decode_address (const guint8 *address, int len)
+static gchar *
+sms_decode_address (const guint8 *address,
+ gint len,
+ GError **error)
{
guint8 addrtype, addrplan;
- char *utf8;
+ gchar *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);
+ g_autoptr(GByteArray) unpacked_array = NULL;
+ guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+
+ unpacked = mm_charset_gsm_unpack (address, (len * 4) / 7, 0, &unpacked_len);
+ unpacked_array = g_byte_array_new_take (unpacked, unpacked_len);
+ utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
} else if (addrtype == SMS_NUMBER_TYPE_INTL &&
addrplan == SMS_NUMBER_PLAN_TELEPHONE) {
/* International telphone number, format as "+1234567890" */
@@ -155,25 +160,26 @@ sms_decode_address (const guint8 *address, int len)
return utf8;
}
-static char *
+static gchar *
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);
+ /* ISO8601 format: YYYY-MM-DDTHH:MM:SS+HHMM */
+ guint year, month, day, hour, minute, second;
+ gint quarters, offset_minutes;
+
+ year = 2000 + ((timestamp[0] & 0xf) * 10) + ((timestamp[0] >> 4) & 0xf);
+ month = ((timestamp[1] & 0xf) * 10) + ((timestamp[1] >> 4) & 0xf);
+ day = ((timestamp[2] & 0xf) * 10) + ((timestamp[2] >> 4) & 0xf);
+ hour = ((timestamp[3] & 0xf) * 10) + ((timestamp[3] >> 4) & 0xf);
+ minute = ((timestamp[4] & 0xf) * 10) + ((timestamp[4] >> 4) & 0xf);
+ second = ((timestamp[5] & 0xf) * 10) + ((timestamp[5] >> 4) & 0xf);
quarters = ((timestamp[6] & 0x7) * 10) + ((timestamp[6] >> 4) & 0xf);
- hours = quarters / 4;
+ offset_minutes = quarters * 15;
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;
+ offset_minutes = -1 * offset_minutes;
+
+ return mm_new_iso8601_time (year, month, day, hour,
+ minute, second, TRUE, offset_minutes);
}
static MMSmsEncoding
@@ -190,14 +196,16 @@ sms_encoding_type (int dcs)
scheme = MM_SMS_ENCODING_UCS2;
break;
case 0x00:
- /* fallthrough */
/* reserved - spec says to treat it as default alphabet */
+ /* Fall through */
case 0x0c:
scheme = MM_SMS_ENCODING_GSM7;
break;
case 0x04:
scheme = MM_SMS_ENCODING_8BIT;
break;
+ default:
+ g_assert_not_reached ();
}
break;
@@ -221,6 +229,8 @@ sms_encoding_type (int dcs)
case 0x04:
scheme = MM_SMS_ENCODING_8BIT;
break;
+ default:
+ g_assert_not_reached ();
}
break;
@@ -233,29 +243,44 @@ sms_encoding_type (int dcs)
return scheme;
}
-static char *
-sms_decode_text (const guint8 *text, int len, MMSmsEncoding encoding, int bit_offset)
+static gchar *
+sms_decode_text (const guint8 *text,
+ int len,
+ MMSmsEncoding encoding,
+ int bit_offset,
+ gpointer log_object,
+ GError **error)
{
- 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 ("");
+ g_autoptr(GByteArray) unpacked_array = NULL;
+ guint8 *unpacked = NULL;
+ guint32 unpacked_len;
+ gchar *utf8;
+
+ unpacked = mm_charset_gsm_unpack ((const guint8 *) text, len, bit_offset, &unpacked_len);
+ unpacked_array = g_byte_array_new_take (unpacked, unpacked_len);
+ utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_array, MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (utf8)
+ mm_obj_dbg (log_object, "converted SMS part text from GSM-7 to UTF-8: %s", utf8);
+ return utf8;
}
- return utf8;
+ /* Always assume UTF-16 instead of UCS-2! */
+ if (encoding == MM_SMS_ENCODING_UCS2) {
+ g_autoptr(GByteArray) bytearray = NULL;
+ gchar *utf8;
+
+ bytearray = g_byte_array_append (g_byte_array_sized_new (len), (const guint8 *)text, len);
+ utf8 = mm_modem_charset_bytearray_to_utf8 (bytearray, MM_MODEM_CHARSET_UTF16, FALSE, error);
+ if (utf8)
+ mm_obj_dbg (log_object, "converted SMS part text from UTF-16BE to UTF-8: %s", utf8);
+ return utf8;
+ }
+
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't convert SMS part contents from %s to UTF-8",
+ mm_sms_encoding_get_string (encoding));
+ return NULL;
}
static guint
@@ -316,35 +341,31 @@ validity_to_relative (guint validity)
}
MMSmsPart *
-mm_sms_part_3gpp_new_from_pdu (guint index,
- const gchar *hexpdu,
- GError **error)
+mm_sms_part_3gpp_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ gpointer log_object,
+ GError **error)
{
- gsize pdu_len;
- guint8 *pdu;
- MMSmsPart *part;
+ g_autofree guint8 *pdu = NULL;
+ gsize pdu_len;
/* Convert PDU from hex to binary */
- pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
+ pdu = mm_utils_hexstr2bin (hexpdu, -1, &pdu_len, error);
if (!pdu) {
- g_set_error_literal (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't convert 3GPP PDU from hex to binary");
+ g_prefix_error (error, "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;
+ return mm_sms_part_3gpp_new_from_binary_pdu (index, pdu, pdu_len, log_object, FALSE, error);
}
MMSmsPart *
-mm_sms_part_3gpp_new_from_binary_pdu (guint index,
- const guint8 *pdu,
- gsize pdu_len,
- GError **error)
+mm_sms_part_3gpp_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ gpointer log_object,
+ gboolean transfer_route,
+ GError **error)
{
MMSmsPart *sms_part;
guint8 pdu_type;
@@ -360,14 +381,15 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
guint tp_dcs_offset = 0;
guint tp_user_data_len_offset = 0;
MMSmsEncoding user_data_encoding = MM_SMS_ENCODING_UNKNOWN;
+ gchar *address;
/* 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);
+ mm_obj_dbg (log_object, "parsing PDU (%u)...", index);
else
- mm_dbg ("Parsing PDU...");
+ mm_obj_dbg (log_object, "parsing PDU...");
#define PDU_SIZE_CHECK(required_size, check_descr_str) \
if (pdu_len < required_size) { \
@@ -384,20 +406,28 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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;
+ if (!transfer_route) {
+ /* ---------------------------------------------------------------------- */
+ /* 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 */
+ address = sms_decode_address (&pdu[1], 2 * (smsc_addr_size_bytes - 1), error);
+ if (!address) {
+ g_prefix_error (error, "Couldn't read SMSC address: ");
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+ mm_sms_part_take_smsc (sms_part, g_steal_pointer (&address));
+ mm_obj_dbg (log_object, " SMSC address parsed: '%s'", mm_sms_part_get_smsc (sms_part));
+ offset += smsc_addr_size_bytes;
+ } else
+ mm_obj_dbg (log_object, " no SMSC address given");
} else
- mm_dbg (" No SMSC address given");
+ mm_obj_dbg (log_object, " This is a transfer-route message");
/* ---------------------------------------------------------------------- */
@@ -407,15 +437,15 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
pdu_type = (pdu[offset] & SMS_TP_MTI_MASK);
switch (pdu_type) {
case SMS_TP_MTI_SMS_DELIVER:
- mm_dbg (" Deliver type PDU detected");
+ mm_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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_obj_dbg (log_object, " status report type PDU detected");
mm_sms_part_set_pdu_type (sms_part, MM_SMS_PDU_TYPE_STATUS_REPORT);
break;
default:
@@ -448,7 +478,7 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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_obj_dbg (log_object, " message reference: %u", (guint)pdu[offset]);
mm_sms_part_set_message_reference (sms_part, pdu[offset]);
offset++;
}
@@ -465,10 +495,14 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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));
+ address = sms_decode_address (&pdu[offset], tp_addr_size_digits, error);
+ if (!address) {
+ g_prefix_error (error, "Couldn't read address: ");
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+ mm_sms_part_take_number (sms_part, g_steal_pointer (&address));
+ mm_obj_dbg (log_object, " number parsed: %s", mm_sms_part_get_number (sms_part));
offset += (1 + tp_addr_size_bytes); /* +1 due to the Type of Address byte */
/* ---------------------------------------------------------------------- */
@@ -504,26 +538,26 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
if (validity_format) {
switch (validity_format) {
case 0x10:
- mm_dbg (" validity available, format relative");
+ mm_obj_dbg (log_object, " 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)");
+ mm_obj_dbg (log_object, " 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)");
+ mm_obj_dbg (log_object, " 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();
+ g_assert_not_reached ();
}
}
@@ -547,7 +581,7 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
offset += 7;
/* ----- TP-STATUS (1 byte) ------ */
- mm_dbg (" delivery state: %u", (guint)pdu[offset]);
+ mm_obj_dbg (log_object, " delivery state: %u", (guint)pdu[offset]);
mm_sms_part_set_delivery_state (sms_part, pdu[offset]);
offset++;
@@ -572,7 +606,7 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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]);
+ mm_obj_dbg (log_object, " PID: %u", (guint)pdu[tp_pid_offset]);
}
/* Grab user data encoding and message class */
@@ -580,20 +614,23 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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]);
+ 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");
+ mm_obj_dbg (log_object, " user data encoding is GSM7");
break;
case MM_SMS_ENCODING_UCS2:
- mm_dbg (" user data encoding is UCS2");
+ mm_obj_dbg (log_object, " user data encoding is UCS2");
break;
case MM_SMS_ENCODING_8BIT:
- mm_dbg (" user data encoding is 8bit");
+ mm_obj_dbg (log_object, " user data encoding is 8bit");
break;
- default:
- mm_dbg (" user data encoding is unknown");
+ case MM_SMS_ENCODING_UNKNOWN:
+ mm_obj_dbg (log_object, " user data encoding is unknown");
break;
+ default:
+ g_assert_not_reached ();
+
}
mm_sms_part_set_encoding (sms_part, user_data_encoding);
@@ -611,13 +648,13 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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);
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " 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");
@@ -666,6 +703,8 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
mm_sms_part_set_concat_max (sms_part,pdu[offset + 2]);
mm_sms_part_set_concat_sequence (sms_part, pdu[offset + 3]);
break;
+ default:
+ break;
}
offset += ie_len;
@@ -691,21 +730,31 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
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;
-
+ {
+ gchar *text;
+
+ /* Otherwise if it's 7-bit or UCS2 we can decode it */
+ mm_obj_dbg (log_object, "decoding SMS text with %u elements", tp_user_data_size_elements);
+ text = sms_decode_text (&pdu[tp_user_data_offset],
+ tp_user_data_size_elements,
+ user_data_encoding,
+ bit_offset,
+ log_object,
+ error);
+ if (!text) {
+ mm_sms_part_free (sms_part);
+ return NULL;
+ }
+ mm_sms_part_take_text (sms_part, text);
+ break;
+ }
+ case MM_SMS_ENCODING_8BIT:
+ case MM_SMS_ENCODING_UNKNOWN:
default:
{
GByteArray *raw;
- mm_dbg ("Skipping SMS text: Unknown encoding (0x%02X)", user_data_encoding);
+ mm_obj_dbg (log_object, "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");
@@ -729,7 +778,7 @@ mm_sms_part_3gpp_new_from_binary_pdu (guint index,
* @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,
+ * @is_smsc: if %TRUE encode size as number of octets of address information,
* otherwise if %FALSE encode size as number of digits of @address
*
* Returns: the size in bytes of the data added to @buf
@@ -783,12 +832,14 @@ guint8 *
mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
guint *out_pdulen,
guint *out_msgstart,
+ gpointer log_object,
GError **error)
{
guint8 *pdu;
guint len, offset = 0;
guint shift = 0;
guint8 *udl_ptr;
+ MMSmsEncoding encoding;
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);
@@ -802,13 +853,13 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
return NULL;
}
- mm_dbg ("Creating PDU for part...");
+ mm_obj_dbg (log_object, "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...");
+ mm_obj_dbg (log_object, " 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,
@@ -831,13 +882,13 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
/* TP-VP present; format RELATIVE */
if (mm_sms_part_get_validity_relative (part) > 0) {
- mm_dbg (" adding validity to PDU...");
+ mm_obj_dbg (log_object, " 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...");
+ mm_obj_dbg (log_object, " adding UDHI to PDU...");
pdu[offset] |= 0x40; /* UDHI */
}
@@ -846,7 +897,7 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
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...");
+ mm_obj_dbg (log_object, " requesting delivery report...");
pdu[offset] |= 0x20;
}
@@ -878,22 +929,26 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
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));
+ mm_obj_dbg (log_object, " 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)) {
+ encoding = mm_sms_part_get_encoding (part);
+
+ switch (encoding) {
case MM_SMS_ENCODING_UCS2:
- mm_dbg (" using UCS2 encoding...");
+ mm_obj_dbg (log_object, " using UCS2 encoding...");
pdu[offset] |= SMS_DCS_CODING_UCS2;
break;
case MM_SMS_ENCODING_GSM7:
- mm_dbg (" using GSM7 encoding...");
+ mm_obj_dbg (log_object, " using GSM7 encoding...");
pdu[offset] |= SMS_DCS_CODING_DEFAULT; /* GSM */
break;
+ case MM_SMS_ENCODING_8BIT:
+ case MM_SMS_ENCODING_UNKNOWN:
default:
- mm_dbg (" using 8bit encoding...");
+ mm_obj_dbg (log_object, " using 8bit encoding...");
pdu[offset] |= SMS_DCS_CODING_8BIT;
break;
}
@@ -912,7 +967,7 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
/* Build UDH */
if (mm_sms_part_get_concat_sequence (part)) {
- mm_dbg (" adding UDH header in PDU... (reference: %u, max: %u, sequence: %u)",
+ mm_obj_dbg (log_object, " 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));
@@ -934,13 +989,16 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
shift = 1;
}
- if (mm_sms_part_get_encoding (part) == MM_SMS_ENCODING_GSM7) {
- guint8 *unpacked, *packed;
- guint32 unlen = 0, packlen = 0;
+ if (encoding == MM_SMS_ENCODING_GSM7) {
+ g_autoptr(GByteArray) unpacked = NULL;
+ g_autofree guint8 *packed = NULL;
+ guint32 packlen = 0;
+
+ unpacked = mm_modem_charset_bytearray_from_utf8 (mm_sms_part_get_text (part), MM_MODEM_CHARSET_GSM, FALSE, error);
+ if (!unpacked)
+ goto error;
- unpacked = mm_charset_utf8_to_unpacked_gsm (mm_sms_part_get_text (part), &unlen);
- if (!unpacked || unlen == 0) {
- g_free (unpacked);
+ if (unpacked->len == 0) {
g_set_error_literal (error,
MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
@@ -951,15 +1009,13 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
/* 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");
+ *udl_ptr = mm_sms_part_get_concat_sequence (part) ? (7 + unpacked->len) : unpacked->len;
+ mm_obj_dbg (log_object, " 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);
+ packed = mm_charset_gsm_pack (unpacked->data, unpacked->len, shift, &packlen);
if (!packed || packlen == 0) {
- g_free (packed);
g_set_error_literal (error,
MM_MESSAGE_ERROR,
MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
@@ -968,19 +1024,19 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
}
memcpy (&pdu[offset], packed, packlen);
- g_free (packed);
offset += packlen;
- } else if (mm_sms_part_get_encoding (part) == MM_SMS_ENCODING_UCS2) {
- GByteArray *array;
+ } else if (encoding == MM_SMS_ENCODING_UCS2) {
+ g_autoptr(GByteArray) array = NULL;
+ g_autoptr(GError) inner_error = NULL;
- /* 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");
+ /* Always assume UTF-16 instead of UCS-2! */
+ array = mm_modem_charset_bytearray_from_utf8 (mm_sms_part_get_text (part), MM_MODEM_CHARSET_UTF16, FALSE, &inner_error);
+ if (!array) {
+ g_set_error (error,
+ MM_MESSAGE_ERROR,
+ MM_MESSAGE_ERROR_INVALID_PDU_PARAMETER,
+ "Failed to convert message text to UTF-16: %s",
+ inner_error->message);
goto error;
}
@@ -988,13 +1044,12 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
* 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");
+ mm_obj_dbg (log_object, " 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;
@@ -1004,7 +1059,7 @@ mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
* 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)",
+ mm_obj_dbg (log_object, " binary user data length is %u octets (%s UDH)",
*udl_ptr,
mm_sms_part_get_concat_sequence (part) ? "with" : "without");
@@ -1022,22 +1077,109 @@ error:
return NULL;
}
-gchar **
-mm_sms_part_3gpp_util_split_text (const gchar *text,
- MMSmsEncoding *encoding)
+static gchar **
+util_split_text_gsm7 (const gchar *text,
+ gsize text_len,
+ gpointer log_object)
{
- guint gsm_unsupported = 0;
gchar **out;
- guint n_chunks;
- guint i;
- guint j;
- gsize in_len;
+ guint n_chunks;
+ guint i;
+ guint j;
+
+ /* No splitting needed? */
+ if (text_len <= 160) {
+ out = g_new0 (gchar *, 2);
+ out[0] = g_strdup (text);
+ return out;
+ }
+ /* Compute number of chunks needed */
+ n_chunks = text_len / 153;
+ if (text_len % 153 != 0)
+ n_chunks++;
+
+ /* Fill in all 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;
+}
+
+static gchar **
+util_split_text_utf16_or_ucs2 (const gchar *text,
+ gsize text_len,
+ gpointer log_object)
+{
+ g_autoptr(GPtrArray) chunks = NULL;
+ const gchar *walker;
+ const gchar *chunk_start;
+ glong encoded_chunk_length;
+ glong total_encoded_chunk_length;
+
+ chunks = g_ptr_array_new_with_free_func ((GDestroyNotify)g_free);
+
+ walker = text;
+ chunk_start = text;
+ encoded_chunk_length = 0;
+ total_encoded_chunk_length = 0;
+ while (walker && *walker) {
+ g_autofree gunichar2 *unichar2 = NULL;
+ glong unichar2_written = 0;
+ glong unichar2_written_bytes = 0;
+ gunichar single;
+
+ single = g_utf8_get_char (walker);
+ unichar2 = g_ucs4_to_utf16 (&single, 1, NULL, &unichar2_written, NULL);
+ g_assert (unichar2_written > 0);
+
+ /* When splitting for UCS-2 encoding, only one single unichar2 will be
+ * written, because all codepoints represented in UCS2 fit in the BMP.
+ * When splitting for UTF-16, though, we may end up writing one or two
+ * unichar2 (without or with surrogate pairs), because UTF-16 covers the
+ * whole Unicode spectrum. */
+ unichar2_written_bytes = (unichar2_written * sizeof (gunichar2));
+ if ((encoded_chunk_length + unichar2_written_bytes) > 134) {
+ g_ptr_array_add (chunks, g_strndup (chunk_start, walker - chunk_start));
+ chunk_start = walker;
+ encoded_chunk_length = unichar2_written_bytes;
+ } else
+ encoded_chunk_length += unichar2_written_bytes;
+
+ total_encoded_chunk_length += unichar2_written_bytes;
+ walker = g_utf8_next_char (walker);
+ }
+
+ /* We have split the original string in chunks, where each chunk
+ * does not require more than 134 bytes when encoded in UTF-16.
+ * As a special case now, we consider the case that no splitting
+ * is necessary, i.e. if the total amount of bytes after encoding
+ * in UTF-16 is less or equal than 140. */
+ if (total_encoded_chunk_length <= 140) {
+ gchar **out;
+
+ out = g_new0 (gchar *, 2);
+ out[0] = g_strdup (text);
+ return out;
+ }
+
+ /* Otherwise, we do need the splitted chunks. Add the last one
+ * with contents plus the last trailing NULL */
+ g_ptr_array_add (chunks, g_strndup (chunk_start, walker - chunk_start));
+ g_ptr_array_add (chunks, NULL);
+
+ return (gchar **) g_ptr_array_free (g_steal_pointer (&chunks), FALSE);
+}
+
+gchar **
+mm_sms_part_3gpp_util_split_text (const gchar *text,
+ MMSmsEncoding *encoding,
+ gpointer log_object)
+{
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:
@@ -1052,81 +1194,25 @@ mm_sms_part_3gpp_util_split_text (const gchar *text,
* 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.
+ * 3) If we're using UTF-16 encoding (instead of UCS2), the amount of
+ * characters we can pack is variable, depends on how the characters
+ * are encoded in UTF-16 (e.g. if there are characters out of the BMP
+ * we'll need surrogate pairs and a single character will need 4 bytes
+ * instead of 2).
*
* 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 */
+ if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_GSM)) {
*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 util_split_text_gsm7 (text, strlen (text), log_object);
}
- return out;
+ /* Otherwise fallback to report UCS-2 and split supporting UTF-16 */
+ *encoding = MM_SMS_ENCODING_UCS2;
+ return util_split_text_utf16_or_ucs2 (text, strlen (text), log_object);
}
GByteArray **
diff --git a/src/mm-sms-part-3gpp.h b/src/mm-sms-part-3gpp.h
index 82709a2f..c6f4cf3f 100644
--- a/src/mm-sms-part-3gpp.h
+++ b/src/mm-sms-part-3gpp.h
@@ -18,37 +18,36 @@
#define MM_SMS_PART_3GPP_H
#include <glib.h>
-#include <ModemManager-enums.h>
+#include <ModemManager.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);
+MMSmsPart *mm_sms_part_3gpp_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ gpointer log_object,
+ GError **error);
+MMSmsPart *mm_sms_part_3gpp_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ gpointer log_object,
+ gboolean transfer_route,
+ GError **error);
+guint8 *mm_sms_part_3gpp_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ guint *out_msgstart,
+ gpointer log_object,
+ 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);
+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,
+ gpointer log_object);
+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
index 3aa6b455..d79a0f91 100644
--- a/src/mm-sms-part-cdma.c
+++ b/src/mm-sms-part-cdma.c
@@ -24,7 +24,7 @@
#include "mm-charsets.h"
#include "mm-sms-part-cdma.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
/*
* Documentation that you may want to have around:
@@ -273,8 +273,10 @@ cause_code_to_delivery_state (guint8 error_class,
return MM_SMS_DELIVERY_STATE_COMPLETED_RECEIVED;
case ERROR_CLASS_TEMPORARY:
delivery_state += 0x300;
+ break;
case ERROR_CLASS_PERMANENT:
delivery_state += 0x200;
+ break;
default:
return MM_SMS_DELIVERY_STATE_UNKNOWN;
}
@@ -296,7 +298,7 @@ cause_code_to_delivery_state (guint8 error_class,
else if (cause_code == 101)
/* 101 reserved */
delivery_state += CAUSE_CODE_GENERAL_PROBLEM_OTHER;
- else if (cause_code >= 108 && cause_code <= 255)
+ else if (cause_code >= 108) /* cause_code <= 255 is always true */
/* 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;
@@ -310,28 +312,22 @@ cause_code_to_delivery_state (guint8 error_class,
/*****************************************************************************/
MMSmsPart *
-mm_sms_part_cdma_new_from_pdu (guint index,
- const gchar *hexpdu,
- GError **error)
+mm_sms_part_cdma_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ gpointer log_object,
+ GError **error)
{
- gsize pdu_len;
- guint8 *pdu;
- MMSmsPart *part;
+ g_autofree guint8 *pdu = NULL;
+ gsize pdu_len;
/* Convert PDU from hex to binary */
- pdu = (guint8 *) mm_utils_hexstr2bin (hexpdu, &pdu_len);
+ pdu = mm_utils_hexstr2bin (hexpdu, -1, &pdu_len, error);
if (!pdu) {
- g_set_error_literal (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't convert CDMA PDU from hex to binary");
+ g_prefix_error (error, "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;
+ return mm_sms_part_cdma_new_from_binary_pdu (index, pdu, pdu_len, log_object, error);
}
struct Parameter {
@@ -341,16 +337,17 @@ struct Parameter {
} __attribute__((packed));
static void
-read_teleservice_id (MMSmsPart *sms_part,
- const struct Parameter *parameter)
+read_teleservice_id (MMSmsPart *sms_part,
+ const struct Parameter *parameter,
+ gpointer log_object)
{
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);
+ mm_obj_dbg (log_object, " invalid teleservice ID length found (%u != 2): ignoring",
+ parameter->parameter_len);
return;
}
@@ -368,29 +365,30 @@ read_teleservice_id (MMSmsPart *sms_part,
case MM_SMS_CDMA_TELESERVICE_ID_CATPT:
break;
default:
- mm_dbg (" invalid teleservice ID found (%u): ignoring", teleservice_id);
+ mm_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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)
+read_service_category (MMSmsPart *sms_part,
+ const struct Parameter *parameter,
+ gpointer log_object)
{
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);
+ mm_obj_dbg (log_object, " invalid service category length found (%u != 2): ignoring",
+ parameter->parameter_len);
return;
}
@@ -436,20 +434,21 @@ read_service_category (MMSmsPart *sms_part,
case MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST:
break;
default:
- mm_dbg (" invalid service category found (%u): ignoring", service_category);
+ mm_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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);
+ (MMSmsCdmaServiceCategory)service_category);
}
static guint8
-dtmf_to_ascii (guint8 dtmf)
+dtmf_to_ascii (guint8 dtmf,
+ gpointer log_object)
{
static const gchar dtmf_to_ascii_digits[13] = {
'\0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '*', '#' };
@@ -457,13 +456,14 @@ dtmf_to_ascii (guint8 dtmf)
if (dtmf > 0 && dtmf < 13)
return dtmf_to_ascii_digits[dtmf];
- mm_dbg (" invalid dtmf digit: %u", dtmf);
+ mm_obj_dbg (log_object, " invalid dtmf digit: %u", dtmf);
return '\0';
}
static void
-read_address (MMSmsPart *sms_part,
- const struct Parameter *parameter)
+read_address (MMSmsPart *sms_part,
+ const struct Parameter *parameter,
+ gpointer log_object)
{
guint8 digit_mode;
guint8 number_mode;
@@ -485,9 +485,9 @@ read_address (MMSmsPart *sms_part,
#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); \
+ mm_obj_dbg (log_object, " cannot read address, need at least %u bytes (got %u)", \
+ required_size, \
+ parameter->parameter_len); \
return; \
}
@@ -500,10 +500,10 @@ read_address (MMSmsPart *sms_part,
g_assert (digit_mode <= 1);
switch (digit_mode) {
case DIGIT_MODE_DTMF:
- mm_dbg (" digit mode: dtmf");
+ mm_obj_dbg (log_object, " digit mode: dtmf");
break;
case DIGIT_MODE_ASCII:
- mm_dbg (" digit mode: ascii");
+ mm_obj_dbg (log_object, " digit mode: ascii");
break;
default:
g_assert_not_reached ();
@@ -514,10 +514,10 @@ read_address (MMSmsPart *sms_part,
OFFSETS_UPDATE (1);
switch (number_mode) {
case NUMBER_MODE_DIGIT:
- mm_dbg (" number mode: digit");
+ mm_obj_dbg (log_object, " number mode: digit");
break;
case NUMBER_MODE_DATA_NETWORK_ADDRESS:
- mm_dbg (" number mode: data network address");
+ mm_obj_dbg (log_object, " number mode: data network address");
break;
default:
g_assert_not_reached ();
@@ -530,25 +530,25 @@ read_address (MMSmsPart *sms_part,
OFFSETS_UPDATE (3);
switch (number_type) {
case NUMBER_TYPE_UNKNOWN:
- mm_dbg (" number type: unknown");
+ mm_obj_dbg (log_object, " number type: unknown");
break;
case NUMBER_TYPE_INTERNATIONAL:
- mm_dbg (" number type: international");
+ mm_obj_dbg (log_object, " number type: international");
break;
case NUMBER_TYPE_NATIONAL:
- mm_dbg (" number type: national");
+ mm_obj_dbg (log_object, " number type: national");
break;
case NUMBER_TYPE_NETWORK_SPECIFIC:
- mm_dbg (" number type: specific");
+ mm_obj_dbg (log_object, " number type: specific");
break;
case NUMBER_TYPE_SUBSCRIBER:
- mm_dbg (" number type: subscriber");
+ mm_obj_dbg (log_object, " number type: subscriber");
break;
case NUMBER_TYPE_ABBREVIATED:
- mm_dbg (" number type: abbreviated");
+ mm_obj_dbg (log_object, " number type: abbreviated");
break;
default:
- mm_dbg (" number type unknown (%u)", number_type);
+ mm_obj_dbg (log_object, " number type unknown (%u)", number_type);
break;
}
} else
@@ -562,22 +562,22 @@ read_address (MMSmsPart *sms_part,
OFFSETS_UPDATE (4);
switch (numbering_plan) {
case NUMBERING_PLAN_UNKNOWN:
- mm_dbg (" numbering plan: unknown");
+ mm_obj_dbg (log_object, " numbering plan: unknown");
break;
case NUMBERING_PLAN_ISDN:
- mm_dbg (" numbering plan: isdn");
+ mm_obj_dbg (log_object, " numbering plan: isdn");
break;
case NUMBERING_PLAN_DATA:
- mm_dbg (" numbering plan: data");
+ mm_obj_dbg (log_object, " numbering plan: data");
break;
case NUMBERING_PLAN_TELEX:
- mm_dbg (" numbering plan: telex");
+ mm_obj_dbg (log_object, " numbering plan: telex");
break;
case NUMBERING_PLAN_PRIVATE:
- mm_dbg (" numbering plan: private");
+ mm_obj_dbg (log_object, " numbering plan: private");
break;
default:
- mm_dbg (" numbering plan unknown (%u)", numbering_plan);
+ mm_obj_dbg (log_object, " numbering plan unknown (%u)", numbering_plan);
break;
}
} else
@@ -587,7 +587,7 @@ read_address (MMSmsPart *sms_part,
PARAMETER_SIZE_CHECK (byte_offset + 2);
num_fields = read_bits (&parameter->parameter_value[byte_offset], bit_offset, 8);
OFFSETS_UPDATE (8);
- mm_dbg (" num fields: %u", num_fields);
+ mm_obj_dbg (log_object, " num fields: %u", num_fields);
/* Address string */
@@ -596,7 +596,7 @@ read_address (MMSmsPart *sms_part,
PARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 4)) / 8));
number = g_malloc (num_fields + 1);
for (i = 0; i < num_fields; i++) {
- number[i] = dtmf_to_ascii (read_bits (&parameter->parameter_value[byte_offset], bit_offset, 4));
+ number[i] = dtmf_to_ascii (read_bits (&parameter->parameter_value[byte_offset], bit_offset, 4), log_object);
OFFSETS_UPDATE (4);
}
number[i] = '\0';
@@ -632,9 +632,9 @@ read_address (MMSmsPart *sms_part,
}
number = g_string_free (str, FALSE);
} else
- mm_dbg (" data network address number type unknown (%u)", number_type);
+ mm_obj_dbg (log_object, " data network address number type unknown (%u)", number_type);
- mm_dbg (" address: %s", number);
+ mm_obj_dbg (log_object, " address: %s", number);
mm_sms_part_set_number (sms_part, number);
g_free (number);
@@ -644,28 +644,30 @@ read_address (MMSmsPart *sms_part,
}
static void
-read_bearer_reply_option (MMSmsPart *sms_part,
- const struct Parameter *parameter)
+read_bearer_reply_option (MMSmsPart *sms_part,
+ const struct Parameter *parameter,
+ gpointer log_object)
{
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",
+ mm_obj_dbg (log_object, " invalid bearer reply option length found (%u != 1): ignoring",
parameter->parameter_len);
return;
}
sequence = read_bits (&parameter->parameter_value[0], 0, 6);
- mm_dbg (" sequence: %u", sequence);
+ mm_obj_dbg (log_object, " sequence: %u", sequence);
mm_sms_part_set_message_reference (sms_part, sequence);
}
static void
-read_cause_codes (MMSmsPart *sms_part,
- const struct Parameter *parameter)
+read_cause_codes (MMSmsPart *sms_part,
+ const struct Parameter *parameter,
+ gpointer log_object)
{
guint8 sequence;
guint8 error_class;
@@ -675,38 +677,39 @@ read_cause_codes (MMSmsPart *sms_part,
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",
+ mm_obj_dbg (log_object, " invalid cause codes length found (%u): ignoring",
parameter->parameter_len);
return;
}
sequence = read_bits (&parameter->parameter_value[0], 0, 6);
- mm_dbg (" sequence: %u", sequence);
+ mm_obj_dbg (log_object, " sequence: %u", sequence);
error_class = read_bits (&parameter->parameter_value[0], 6, 2);
- mm_dbg (" error class: %u", error_class);
+ mm_obj_dbg (log_object, " 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",
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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)
+read_bearer_data_message_identifier (MMSmsPart *sms_part,
+ const struct Parameter *subparameter,
+ gpointer log_object)
{
guint8 message_type;
guint16 message_id;
@@ -715,7 +718,7 @@ read_bearer_data_message_identifier (MMSmsPart *sms_part,
g_assert (subparameter->parameter_id == SUBPARAMETER_ID_MESSAGE_ID);
if (subparameter->parameter_len != 3) {
- mm_dbg (" invalid message identifier length found (%u): ignoring",
+ mm_obj_dbg (log_object, " invalid message identifier length found (%u): ignoring",
subparameter->parameter_len);
return;
}
@@ -723,49 +726,50 @@ read_bearer_data_message_identifier (MMSmsPart *sms_part,
message_type = read_bits (&subparameter->parameter_value[0], 0, 4);
switch (message_type) {
case TELESERVICE_MESSAGE_TYPE_UNKNOWN:
- mm_dbg (" message type: unknown");
+ mm_obj_dbg (log_object, " message type: unknown");
break;
case TELESERVICE_MESSAGE_TYPE_DELIVER:
- mm_dbg (" message type: deliver");
+ mm_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " message id: %u", (guint) message_id);
header_ind = read_bits (&subparameter->parameter_value[2], 4, 1);
- mm_dbg (" header indicator: %u", header_ind);
+ mm_obj_dbg (log_object, " header indicator: %u", header_ind);
}
static void
-read_bearer_data_user_data (MMSmsPart *sms_part,
- const struct Parameter *subparameter)
+read_bearer_data_user_data (MMSmsPart *sms_part,
+ const struct Parameter *subparameter,
+ gpointer log_object)
{
guint8 message_encoding;
guint8 message_type = 0;
@@ -783,7 +787,7 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
#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)", \
+ mm_obj_dbg (log_object, " cannot read user data, need at least %u bytes (got %u)", \
required_size, \
subparameter->parameter_len); \
return; \
@@ -795,21 +799,21 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
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));
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " num fields: %u", num_fields);
/* Now, process actual text or data */
switch (message_encoding) {
@@ -826,7 +830,7 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
OFFSETS_UPDATE (8);
}
- mm_dbg (" data: (%u bytes)", num_fields);
+ mm_obj_dbg (log_object, " data: (%u bytes)", num_fields);
mm_sms_part_take_data (sms_part, data);
break;
}
@@ -835,7 +839,7 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
gchar *text;
guint i;
- SUBPARAMETER_SIZE_CHECK (byte_offset + 1 + ((bit_offset + (num_fields * 7)) / 8));
+ SUBPARAMETER_SIZE_CHECK (byte_offset + ((bit_offset + (num_fields * 7)) / 8));
text = g_malloc (num_fields + 1);
for (i = 0; i < num_fields; i++) {
@@ -844,7 +848,7 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
}
text[i] = '\0';
- mm_dbg (" text: '%s'", text);
+ mm_obj_dbg (log_object, " text: '%s'", text);
mm_sms_part_take_text (sms_part, text);
break;
}
@@ -863,11 +867,11 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
}
latin[i] = '\0';
- text = g_convert (latin, -1, "UTF-8", "ISO−8859−1", NULL, NULL, NULL);
+ 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)");
+ mm_obj_dbg (log_object, " text/data: ignored (latin to UTF-8 conversion error)");
} else {
- mm_dbg (" text: '%s'", text);
+ mm_obj_dbg (log_object, " text: '%s'", text);
mm_sms_part_take_text (sms_part, text);
}
@@ -894,9 +898,9 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
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)");
+ mm_obj_dbg (log_object, " text/data: ignored (UTF-16 to UTF-8 conversion error)");
} else {
- mm_dbg (" text: '%s'", text);
+ mm_obj_dbg (log_object, " text: '%s'", text);
mm_sms_part_take_text (sms_part, text);
}
@@ -905,7 +909,7 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
}
default:
- mm_dbg (" text/data: ignored (unsupported encoding)");
+ mm_obj_dbg (log_object, " text/data: ignored (unsupported encoding)");
}
#undef OFFSETS_UPDATE
@@ -913,16 +917,17 @@ read_bearer_data_user_data (MMSmsPart *sms_part,
}
static void
-read_bearer_data (MMSmsPart *sms_part,
- const struct Parameter *parameter)
+read_bearer_data (MMSmsPart *sms_part,
+ const struct Parameter *parameter,
+ gpointer log_object)
{
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); \
+ mm_obj_dbg (log_object, " cannot read bearer data, need at least %u bytes (got %u)", \
+ required_size, \
+ parameter->parameter_len); \
return; \
}
@@ -939,81 +944,81 @@ read_bearer_data (MMSmsPart *sms_part,
switch (subparameter->parameter_id) {
case SUBPARAMETER_ID_MESSAGE_ID:
- mm_dbg (" reading message ID...");
- read_bearer_data_message_identifier (sms_part, subparameter);
+ mm_obj_dbg (log_object, " reading message ID...");
+ read_bearer_data_message_identifier (sms_part, subparameter, log_object);
break;
case SUBPARAMETER_ID_USER_DATA:
- mm_dbg (" reading user data...");
- read_bearer_data_user_data (sms_part, subparameter);
+ mm_obj_dbg (log_object, " reading user data...");
+ read_bearer_data_user_data (sms_part, subparameter, log_object);
break;
case SUBPARAMETER_ID_USER_RESPONSE_CODE:
- mm_dbg (" skipping user response code...");
+ mm_obj_dbg (log_object, " skipping user response code...");
break;
case SUBPARAMETER_ID_MESSAGE_CENTER_TIME_STAMP:
- mm_dbg (" skipping message center timestamp...");
+ mm_obj_dbg (log_object, " skipping message center timestamp...");
break;
case SUBPARAMETER_ID_VALIDITY_PERIOD_ABSOLUTE:
- mm_dbg (" skipping absolute validity period...");
+ mm_obj_dbg (log_object, " skipping absolute validity period...");
break;
case SUBPARAMETER_ID_VALIDITY_PERIOD_RELATIVE:
- mm_dbg (" skipping relative validity period...");
+ mm_obj_dbg (log_object, " skipping relative validity period...");
break;
case SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_ABSOLUTE:
- mm_dbg (" skipping absolute deferred delivery time...");
+ mm_obj_dbg (log_object, " skipping absolute deferred delivery time...");
break;
case SUBPARAMETER_ID_DEFERRED_DELIVERY_TIME_RELATIVE:
- mm_dbg (" skipping relative deferred delivery time...");
+ mm_obj_dbg (log_object, " skipping relative deferred delivery time...");
break;
case SUBPARAMETER_ID_PRIORITY_INDICATOR:
- mm_dbg (" skipping priority indicator...");
+ mm_obj_dbg (log_object, " skipping priority indicator...");
break;
case SUBPARAMETER_ID_PRIVACY_INDICATOR:
- mm_dbg (" skipping privacy indicator...");
+ mm_obj_dbg (log_object, " skipping privacy indicator...");
break;
case SUBPARAMETER_ID_REPLY_OPTION:
- mm_dbg (" skipping reply option...");
+ mm_obj_dbg (log_object, " skipping reply option...");
break;
case SUBPARAMETER_ID_NUMBER_OF_MESSAGES:
- mm_dbg (" skipping number of messages...");
+ mm_obj_dbg (log_object, " skipping number of messages...");
break;
case SUBPARAMETER_ID_ALERT_ON_MESSAGE_DELIVERY:
- mm_dbg (" skipping alert on message delivery...");
+ mm_obj_dbg (log_object, " skipping alert on message delivery...");
break;
case SUBPARAMETER_ID_LANGUAGE_INDICATOR:
- mm_dbg (" skipping language indicator...");
+ mm_obj_dbg (log_object, " skipping language indicator...");
break;
case SUBPARAMETER_ID_CALL_BACK_NUMBER:
- mm_dbg (" skipping call back number...");
+ mm_obj_dbg (log_object, " skipping call back number...");
break;
case SUBPARAMETER_ID_MESSAGE_DISPLAY_MODE:
- mm_dbg (" skipping message display mode...");
+ mm_obj_dbg (log_object, " skipping message display mode...");
break;
case SUBPARAMETER_ID_MULTIPLE_ENCODING_USER_DATA:
- mm_dbg (" skipping multiple encoding user data...");
+ mm_obj_dbg (log_object, " skipping multiple encoding user data...");
break;
case SUBPARAMETER_ID_MESSAGE_DEPOSIT_INDEX:
- mm_dbg (" skipping message deposit index...");
+ mm_obj_dbg (log_object, " skipping message deposit index...");
break;
case SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_DATA:
- mm_dbg (" skipping service category program data...");
+ mm_obj_dbg (log_object, " skipping service category program data...");
break;
case SUBPARAMETER_ID_SERVICE_CATEGORY_PROGRAM_RESULT:
- mm_dbg (" skipping service category program result...");
+ mm_obj_dbg (log_object, " skipping service category program result...");
break;
case SUBPARAMETER_ID_MESSAGE_STATUS:
- mm_dbg (" skipping message status...");
+ mm_obj_dbg (log_object, " skipping message status...");
break;
case SUBPARAMETER_ID_TP_FAILURE_CAUSE:
- mm_dbg (" skipping TP failure case...");
+ mm_obj_dbg (log_object, " skipping TP failure case...");
break;
case SUBPARAMETER_ID_ENHANCED_VMN:
- mm_dbg (" skipping enhanced vmn...");
+ mm_obj_dbg (log_object, " skipping enhanced vmn...");
break;
case SUBPARAMETER_ID_ENHANCED_VMN_ACK:
- mm_dbg (" skipping enhanced vmn ack...");
+ mm_obj_dbg (log_object, " skipping enhanced vmn ack...");
break;
default:
- mm_dbg (" unknown subparameter found: '%u' (ignoring)",
+ mm_obj_dbg (log_object, " unknown subparameter found: '%u' (ignoring)",
subparameter->parameter_id);
break;
}
@@ -1023,10 +1028,11 @@ read_bearer_data (MMSmsPart *sms_part,
}
MMSmsPart *
-mm_sms_part_cdma_new_from_binary_pdu (guint index,
- const guint8 *pdu,
- gsize pdu_len,
- GError **error)
+mm_sms_part_cdma_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ gpointer log_object,
+ GError **error)
{
MMSmsPart *sms_part;
guint offset;
@@ -1036,9 +1042,9 @@ mm_sms_part_cdma_new_from_binary_pdu (guint index,
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);
+ mm_obj_dbg (log_object, "parsing CDMA PDU (%u)...", index);
else
- mm_dbg ("Parsing CDMA PDU...");
+ mm_obj_dbg (log_object, "parsing CDMA PDU...");
#define PDU_SIZE_CHECK(required_size, check_descr_str) \
if (pdu_len < required_size) { \
@@ -1086,47 +1092,47 @@ mm_sms_part_cdma_new_from_binary_pdu (guint index,
switch (parameter->parameter_id) {
case PARAMETER_ID_TELESERVICE_ID:
- mm_dbg (" reading teleservice ID...");
- read_teleservice_id (sms_part, parameter);
+ mm_obj_dbg (log_object, " reading teleservice ID...");
+ read_teleservice_id (sms_part, parameter, log_object);
break;
case PARAMETER_ID_SERVICE_CATEGORY:
- mm_dbg (" reading service category...");
- read_service_category (sms_part, parameter);
+ mm_obj_dbg (log_object, " reading service category...");
+ read_service_category (sms_part, parameter, log_object);
break;
case PARAMETER_ID_ORIGINATING_ADDRESS:
- mm_dbg (" reading originating address...");
+ mm_obj_dbg (log_object, " reading originating address...");
if (mm_sms_part_get_number (sms_part))
- mm_dbg (" cannot read originating address; an address field was already read");
+ mm_obj_dbg (log_object, " cannot read originating address; an address field was already read");
else
- read_address (sms_part, parameter);
+ read_address (sms_part, parameter, log_object);
break;
case PARAMETER_ID_ORIGINATING_SUBADDRESS:
- mm_dbg (" skipping originating subaddress...");
+ mm_obj_dbg (log_object, " skipping originating subaddress...");
break;
case PARAMETER_ID_DESTINATION_ADDRESS:
- mm_dbg (" reading destination address...");
+ mm_obj_dbg (log_object, " reading destination address...");
if (mm_sms_part_get_number (sms_part))
- mm_dbg (" cannot read destination address; an address field was already read");
+ mm_obj_dbg (log_object, " cannot read destination address; an address field was already read");
else
- read_address (sms_part, parameter);
+ read_address (sms_part, parameter, log_object);
break;
case PARAMETER_ID_DESTINATION_SUBADDRESS:
- mm_dbg (" skipping destination subaddress...");
+ mm_obj_dbg (log_object, " skipping destination subaddress...");
break;
case PARAMETER_ID_BEARER_REPLY_OPTION:
- mm_dbg (" reading bearer reply option...");
- read_bearer_reply_option (sms_part, parameter);
+ mm_obj_dbg (log_object, " reading bearer reply option...");
+ read_bearer_reply_option (sms_part, parameter, log_object);
break;
case PARAMETER_ID_CAUSE_CODES:
- mm_dbg (" reading cause codes...");
- read_cause_codes (sms_part, parameter);
+ mm_obj_dbg (log_object, " reading cause codes...");
+ read_cause_codes (sms_part, parameter, log_object);
break;
case PARAMETER_ID_BEARER_DATA:
- mm_dbg (" reading bearer data...");
- read_bearer_data (sms_part, parameter);
+ mm_obj_dbg (log_object, " reading bearer data...");
+ read_bearer_data (sms_part, parameter, log_object);
break;
default:
- mm_dbg (" unknown parameter found: '%u' (ignoring)",
+ mm_obj_dbg (log_object, " unknown parameter found: '%u' (ignoring)",
parameter->parameter_id);
break;
}
@@ -1136,15 +1142,17 @@ mm_sms_part_cdma_new_from_binary_pdu (guint index,
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");
+ mm_obj_dbg (log_object, " 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");
+ mm_obj_dbg (log_object, " 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");
+ mm_obj_dbg (log_object, " mandatory parameter missing: cause codes not found or invalid in acknowledge message");
+ break;
+ default:
break;
}
@@ -1193,7 +1201,8 @@ write_bits (guint8 *bytes,
/*****************************************************************************/
static guint8
-dtmf_from_ascii (guint8 ascii)
+dtmf_from_ascii (guint8 ascii,
+ gpointer log_object)
{
if (ascii >= '1' && ascii <= '9')
return ascii - '0';
@@ -1204,19 +1213,20 @@ dtmf_from_ascii (guint8 ascii)
if (ascii == '#')
return 12;
- mm_dbg (" invalid ascii digit in dtmf conversion: %c", ascii);
+ mm_obj_dbg (log_object, " invalid ascii digit in dtmf conversion: %c", ascii);
return 0;
}
static gboolean
-write_teleservice_id (MMSmsPart *part,
- guint8 *pdu,
- guint *absolute_offset,
- GError **error)
+write_teleservice_id (MMSmsPart *part,
+ guint8 *pdu,
+ guint *absolute_offset,
+ gpointer log_object,
+ GError **error)
{
guint16 aux16;
- mm_dbg (" writing teleservice ID...");
+ mm_obj_dbg (log_object, " writing teleservice ID...");
if (mm_sms_part_get_cdma_teleservice_id (part) != MM_SMS_CDMA_TELESERVICE_ID_WMT) {
g_set_error (error,
@@ -1228,9 +1238,9 @@ write_teleservice_id (MMSmsPart *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);
+ mm_obj_dbg (log_object, " 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;
@@ -1243,10 +1253,11 @@ write_teleservice_id (MMSmsPart *part,
}
static gboolean
-write_destination_address (MMSmsPart *part,
- guint8 *pdu,
- guint *absolute_offset,
- GError **error)
+write_destination_address (MMSmsPart *part,
+ guint8 *pdu,
+ guint *absolute_offset,
+ gpointer log_object,
+ GError **error)
{
const gchar *number;
guint bit_offset;
@@ -1254,7 +1265,7 @@ write_destination_address (MMSmsPart *part,
guint n_digits;
guint i;
- mm_dbg (" writing destination address...");
+ mm_obj_dbg (log_object, " writing destination address...");
#define OFFSETS_UPDATE(n_bits) do { \
bit_offset += n_bits; \
@@ -1274,12 +1285,12 @@ write_destination_address (MMSmsPart *part,
bit_offset = 0;
/* Digit mode: DTMF always */
- mm_dbg (" digit mode: dtmf");
+ mm_obj_dbg (log_object, " 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");
+ mm_obj_dbg (log_object, " number mode: digit");
write_bits (&pdu[byte_offset], bit_offset, 1, NUMBER_MODE_DIGIT);
OFFSETS_UPDATE (1);
@@ -1294,16 +1305,16 @@ write_destination_address (MMSmsPart *part,
n_digits);
return FALSE;
}
- mm_dbg (" num fields: %u", n_digits);
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " address: %s", number);
for (i = 0; i < n_digits; i++) {
guint8 dtmf;
- dtmf = dtmf_from_ascii (number[i]);
+ dtmf = dtmf_from_ascii (number[i], log_object);
if (!dtmf) {
g_set_error (error,
MM_CORE_ERROR,
@@ -1335,15 +1346,16 @@ write_destination_address (MMSmsPart *part,
}
static gboolean
-write_bearer_data_message_identifier (MMSmsPart *part,
- guint8 *pdu,
- guint *parameter_offset,
- GError **error)
+write_bearer_data_message_identifier (MMSmsPart *part,
+ guint8 *pdu,
+ guint *parameter_offset,
+ gpointer log_object,
+ GError **error)
{
pdu[0] = SUBPARAMETER_ID_MESSAGE_ID;
pdu[1] = 3; /* subparameter_len, always 3 */
- mm_dbg (" writing message identifier: submit");
+ mm_obj_dbg (log_object, " writing message identifier: submit");
/* Message type */
write_bits (&pdu[2], 0, 4, TELESERVICE_MESSAGE_TYPE_SUBMIT);
@@ -1356,70 +1368,57 @@ write_bearer_data_message_identifier (MMSmsPart *part,
return TRUE;
}
-static void
+static GByteArray *
decide_best_encoding (const gchar *text,
- GByteArray **out,
- guint *num_fields,
- guint *num_bits_per_field,
- Encoding *encoding)
+ gpointer log_object,
+ guint *num_fields,
+ guint *num_bits_per_field,
+ Encoding *encoding,
+ GError **error)
{
- guint latin_unsupported = 0;
- guint ascii_unsupported = 0;
- guint i;
- guint len;
+ g_autoptr(GByteArray) barray = NULL;
+ MMModemCharset target_charset = MM_MODEM_CHARSET_UNKNOWN;
+ 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 (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_IRA))
+ target_charset = MM_MODEM_CHARSET_IRA;
+ else if (mm_charset_can_convert_to (text, MM_MODEM_CHARSET_8859_1))
+ target_charset = MM_MODEM_CHARSET_8859_1;
+ else
+ target_charset = MM_MODEM_CHARSET_UCS2;
+
+ barray = mm_modem_charset_bytearray_from_utf8 (text, target_charset, FALSE, error);
+ if (!barray) {
+ g_prefix_error (error, "Couldn't decide best encoding: ");
+ return NULL;
}
- /* 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);
+ if (target_charset == MM_MODEM_CHARSET_IRA) {
*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;
+ } else if (target_charset == MM_MODEM_CHARSET_8859_1) {
+ *num_fields = barray->len;
*num_bits_per_field = 8;
*encoding = ENCODING_LATIN;
- return;
- }
+ } else if (target_charset == MM_MODEM_CHARSET_UCS2) {
+ *num_fields = barray->len / 2;
+ *num_bits_per_field = 16;
+ *encoding = ENCODING_UNICODE;
+ } else
+ g_assert_not_reached ();
- /* 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;
+ return g_steal_pointer (&barray);
}
static gboolean
-write_bearer_data_user_data (MMSmsPart *part,
- guint8 *pdu,
- guint *parameter_offset,
- GError **error)
+write_bearer_data_user_data (MMSmsPart *part,
+ guint8 *pdu,
+ guint *parameter_offset,
+ gpointer log_object,
+ GError **error)
{
const gchar *text;
const GByteArray *data;
@@ -1433,7 +1432,7 @@ write_bearer_data_user_data (MMSmsPart *part,
const GByteArray *aux;
guint num_bits_per_iter;
- mm_dbg (" writing user data...");
+ mm_obj_dbg (log_object, " writing user data...");
#define OFFSETS_UPDATE(n_bits) do { \
bit_offset += n_bits; \
@@ -1455,11 +1454,14 @@ write_bearer_data_user_data (MMSmsPart *part,
/* Text or Data */
if (text) {
- decide_best_encoding (text,
- &converted,
- &num_fields,
- &num_bits_per_field,
- &encoding);
+ converted = decide_best_encoding (text,
+ log_object,
+ &num_fields,
+ &num_bits_per_field,
+ &encoding,
+ error);
+ if (!converted)
+ return FALSE;
aux = (const GByteArray *)converted;
} else {
aux = data;
@@ -1469,7 +1471,7 @@ write_bearer_data_user_data (MMSmsPart *part,
}
/* Message encoding*/
- mm_dbg (" message encoding: %s", encoding_to_string (encoding));
+ mm_obj_dbg (log_object, " message encoding: %s", encoding_to_string (encoding));
write_bits (&pdu[byte_offset], bit_offset, 5, encoding);
OFFSETS_UPDATE (5);
@@ -1484,16 +1486,16 @@ write_bearer_data_user_data (MMSmsPart *part,
num_fields);
return FALSE;
}
- mm_dbg (" num fields: %u", num_fields);
+ mm_obj_dbg (log_object, " 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);
+ mm_obj_dbg (log_object, " text: '%s'", text);
else
- mm_dbg (" data: (%u bytes)", num_fields);
+ mm_obj_dbg (log_object, " 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]);
@@ -1522,24 +1524,25 @@ write_bearer_data_user_data (MMSmsPart *part,
}
static gboolean
-write_bearer_data (MMSmsPart *part,
- guint8 *pdu,
- guint *absolute_offset,
- GError **error)
+write_bearer_data (MMSmsPart *part,
+ guint8 *pdu,
+ guint *absolute_offset,
+ gpointer log_object,
+ GError **error)
{
GError *inner_error = NULL;
guint offset = 0;
- mm_dbg (" writing bearer data...");
+ mm_obj_dbg (log_object, " 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 (!write_bearer_data_message_identifier (part, &pdu[offset], &offset, log_object, &inner_error))
+ mm_obj_dbg (log_object, "error writing message identifier: %s", inner_error->message);
+ else if (!write_bearer_data_user_data (part, &pdu[offset], &offset, log_object, &inner_error))
+ mm_obj_dbg (log_object, "error writing user data: %s", inner_error->message);
if (inner_error) {
g_propagate_error (error, inner_error);
@@ -1564,9 +1567,10 @@ write_bearer_data (MMSmsPart *part,
}
guint8 *
-mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
- guint *out_pdulen,
- GError **error)
+mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ gpointer log_object,
+ GError **error)
{
GError *inner_error = NULL;
guint offset = 0;
@@ -1584,7 +1588,7 @@ mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
return NULL;
}
- mm_dbg ("Creating PDU for part...");
+ mm_obj_dbg (log_object, "creating PDU for part...");
/* Current max size estimations:
* Message type: 1 byte
@@ -1597,12 +1601,12 @@ mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
/* 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 (!write_teleservice_id (part, &pdu[offset], &offset, log_object, &inner_error))
+ mm_obj_dbg (log_object, "error writing teleservice ID: %s", inner_error->message);
+ else if (!write_destination_address (part, &pdu[offset], &offset, log_object, &inner_error))
+ mm_obj_dbg (log_object, "error writing destination address: %s", inner_error->message);
+ else if (!write_bearer_data (part, &pdu[offset], &offset, log_object, &inner_error))
+ mm_obj_dbg (log_object, "error writing bearer data: %s", inner_error->message);
if (inner_error) {
g_propagate_error (error, inner_error);
diff --git a/src/mm-sms-part-cdma.h b/src/mm-sms-part-cdma.h
index 14c2c1df..804f67b1 100644
--- a/src/mm-sms-part-cdma.h
+++ b/src/mm-sms-part-cdma.h
@@ -17,21 +17,22 @@
#define MM_SMS_PART_CDMA_H
#include <glib.h>
-#include <ModemManager-enums.h>
+#include <ModemManager.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);
+MMSmsPart *mm_sms_part_cdma_new_from_pdu (guint index,
+ const gchar *hexpdu,
+ gpointer log_object,
+ GError **error);
+MMSmsPart *mm_sms_part_cdma_new_from_binary_pdu (guint index,
+ const guint8 *pdu,
+ gsize pdu_len,
+ gpointer log_object,
+ GError **error);
+guint8 *mm_sms_part_cdma_get_submit_pdu (MMSmsPart *part,
+ guint *out_pdulen,
+ gpointer log_object,
+ GError **error);
#endif /* MM_SMS_PART_CDMA_H */
diff --git a/src/mm-sms-part.h b/src/mm-sms-part.h
index 96262956..133d2e08 100644
--- a/src/mm-sms-part.h
+++ b/src/mm-sms-part.h
@@ -18,13 +18,29 @@
#define MM_SMS_PART_H
#include <glib.h>
-#include <ModemManager-enums.h>
-
-typedef enum {
+#include <ModemManager.h>
+
+/* Despite 3GPP TS 23.038 specifies that Unicode SMS messages are
+ * encoded in UCS-2, UTF-16 encoding is commonly used instead on many
+ * modern platforms to allow encoding code points that fall outside the
+ * Basic Multilingual Plane (BMP), such as Emoji. Most of the UCS-2
+ * code points are identical to their equivalent UTF-16 code points.
+ * In UTF-16, non-BMP code points are encoded in a pair of surrogate
+ * code points (i.e. a high surrogate in 0xD800..0xDBFF, followed by a
+ * low surrogate in 0xDC00..0xDFFF). An isolated surrogate code point
+ * has no general interpretation in UTF-16, but could be a valid
+ * (though unmapped) code point in UCS-2.
+ *
+ * The current implementation in ModemManager just assumes that whenever
+ * possible (i.e. when parsing received PDUs or when creating submit
+ * PDUs) UTF-16 will be used instead of plain UCS-2 (even if the PDUs
+ * report the encoding as UCS-2).
+ */
+typedef enum { /*< underscore_name=mm_sms_encoding >*/
MM_SMS_ENCODING_UNKNOWN = 0x0,
MM_SMS_ENCODING_GSM7,
MM_SMS_ENCODING_8BIT,
- MM_SMS_ENCODING_UCS2
+ MM_SMS_ENCODING_UCS2,
} MMSmsEncoding;
typedef struct _MMSmsPart MMSmsPart;
@@ -43,6 +59,8 @@ MMSmsPart *mm_sms_part_new (guint index,
MMSmsPduType type);
void mm_sms_part_free (MMSmsPart *part);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsPart, mm_sms_part_free)
+
guint mm_sms_part_get_index (MMSmsPart *part);
void mm_sms_part_set_index (MMSmsPart *part,
guint index);
diff --git a/src/mm-sms-qmi.c b/src/mm-sms-qmi.c
index 6f604844..3db0512e 100644
--- a/src/mm-sms-qmi.c
+++ b/src/mm-sms-qmi.c
@@ -24,6 +24,7 @@
#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
+#include "mm-broadband-modem-qmi.h"
#include "mm-modem-helpers-qmi.h"
#include "mm-iface-modem.h"
#include "mm-iface-modem-messaging.h"
@@ -31,7 +32,7 @@
#include "mm-base-modem.h"
#include "mm-sms-part-3gpp.h"
#include "mm-sms-part-cdma.h"
-#include "mm-log.h"
+#include "mm-log-object.h"
G_DEFINE_TYPE (MMSmsQmi, mm_sms_qmi, MM_TYPE_BASE_SMS)
@@ -53,16 +54,17 @@ ensure_qmi_client (MMSmsQmi *self,
NULL);
g_assert (MM_IS_BASE_MODEM (modem));
- port = mm_base_modem_peek_port_qmi (modem);
+ port = mm_broadband_modem_qmi_peek_port_qmi (MM_BROADBAND_MODEM_QMI (modem));
g_object_unref (modem);
if (!port) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek QMI port");
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ ensure_qmi_client,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek QMI port");
return FALSE;
}
@@ -70,13 +72,14 @@ ensure_qmi_client (MMSmsQmi *self,
service,
MM_PORT_QMI_FLAG_DEFAULT);
if (!client) {
- g_simple_async_report_error_in_idle (G_OBJECT (self),
- callback,
- user_data,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't peek client for service '%s'",
- qmi_service_get_string (service));
+ g_task_report_new_error (self,
+ callback,
+ user_data,
+ ensure_qmi_client,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't peek client for service '%s'",
+ qmi_service_get_string (service));
return FALSE;
}
@@ -115,22 +118,17 @@ check_sms_type_support (MMSmsQmi *self,
/* Store the SMS */
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
QmiClientWms *client;
- GSimpleAsyncResult *result;
MMSmsStorage storage;
GList *current;
} SmsStoreContext;
static void
-sms_store_context_complete_and_free (SmsStoreContext *ctx)
+sms_store_context_free (SmsStoreContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->client);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (SmsStoreContext, ctx);
}
@@ -139,16 +137,18 @@ sms_store_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void sms_store_next_part (SmsStoreContext *ctx);
+static void sms_store_next_part (GTask *task);
static void
store_ready (QmiClientWms *client,
GAsyncResult *res,
- SmsStoreContext *ctx)
+ GTask *task)
{
+ MMBaseSms *self;
+ SmsStoreContext *ctx;
QmiMessageWmsRawWriteOutput *output = NULL;
GError *error = NULL;
GList *parts;
@@ -157,19 +157,22 @@ store_ready (QmiClientWms *client,
output = qmi_client_wms_raw_write_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
if (!qmi_message_wms_raw_write_output_get_result (output, &error)) {
qmi_message_wms_raw_write_output_unref (output);
g_prefix_error (&error, "Couldn't write SMS part: ");
- g_simple_async_result_take_error (ctx->result, error);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
qmi_message_wms_raw_write_output_get_memory_index (
output,
&idx,
@@ -177,17 +180,19 @@ store_ready (QmiClientWms *client,
qmi_message_wms_raw_write_output_unref (output);
/* Set the index in the part we hold */
- parts = mm_base_sms_get_parts (ctx->self);
+ parts = mm_base_sms_get_parts (self);
mm_sms_part_set_index ((MMSmsPart *)parts->data, (guint)idx);
/* Go on with next one */
ctx->current = g_list_next (ctx->current);
- sms_store_next_part (ctx);
+ sms_store_next_part (task);
}
static void
-sms_store_next_part (SmsStoreContext *ctx)
+sms_store_next_part (GTask *task)
{
+ MMSmsQmi *self;
+ SmsStoreContext *ctx;
QmiMessageWmsRawWriteInput *input;
guint8 *pdu = NULL;
guint pdulen = 0;
@@ -195,31 +200,35 @@ sms_store_next_part (SmsStoreContext *ctx)
GArray *array;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
if (!ctx->current) {
/* Done we are */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_store_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Get PDU */
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);
+ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, self, &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);
+ pdu = mm_sms_part_cdma_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, self, &error);
if (!pdu) {
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, 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);
+ g_task_return_new_error (task,
+ 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)));
+
+ g_object_unref (task);
return;
}
@@ -244,7 +253,7 @@ sms_store_next_part (SmsStoreContext *ctx)
5,
NULL,
(GAsyncReadyCallback)store_ready,
- ctx);
+ task);
qmi_message_wms_raw_write_input_unref (input);
g_array_unref (array);
}
@@ -258,6 +267,7 @@ sms_store (MMBaseSms *self,
SmsStoreContext *ctx;
QmiClient *client = NULL;
GError *error = NULL;
+ GTask *task;
/* Ensure WMS client */
if (!ensure_qmi_client (MM_SMS_QMI (self),
@@ -267,12 +277,7 @@ sms_store (MMBaseSms *self,
/* Setup the context */
ctx = g_slice_new0 (SmsStoreContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_store);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx->client = QMI_CLIENT_WMS (g_object_ref (client));
ctx->storage = storage;
g_object_get (self,
MM_BASE_SMS_MODEM, &ctx->modem,
@@ -280,37 +285,35 @@ sms_store (MMBaseSms *self,
ctx->current = mm_base_sms_get_parts (self);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free);
+
/* 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
/* Go on */
- sms_store_next_part (ctx);
+ sms_store_next_part (task);
}
/*****************************************************************************/
/* Send the SMS */
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
QmiClientWms *client;
- GSimpleAsyncResult *result;
gboolean from_storage;
GList *current;
} SmsSendContext;
static void
-sms_send_context_complete_and_free (SmsSendContext *ctx)
+sms_send_context_free (SmsSendContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->client);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (SmsSendContext, ctx);
}
@@ -319,25 +322,29 @@ sms_send_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void sms_send_next_part (SmsSendContext *ctx);
+static void sms_send_next_part (GTask *task);
static void
send_generic_ready (QmiClientWms *client,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ MMSmsQmi *self;
+ SmsSendContext *ctx;
QmiMessageWmsRawSendOutput *output = NULL;
GError *error = NULL;
guint16 message_id;
+ self = g_task_get_source_object (task);
+
output = qmi_client_wms_raw_send_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -350,20 +357,22 @@ send_generic_ready (QmiClientWms *client,
&rp_cause,
&tp_cause,
NULL)) {
- mm_warn ("Couldn't send SMS; RP cause (%u): '%s'; TP cause (%u): '%s'",
- rp_cause,
- qmi_wms_gsm_umts_rp_cause_get_string (rp_cause),
- tp_cause,
- qmi_wms_gsm_umts_tp_cause_get_string (tp_cause));
+ mm_obj_warn (self, "couldn't send SMS; RP cause (%u): %s; TP cause (%u): %s",
+ rp_cause,
+ qmi_wms_gsm_umts_rp_cause_get_string (rp_cause),
+ tp_cause,
+ qmi_wms_gsm_umts_tp_cause_get_string (tp_cause));
}
qmi_message_wms_raw_send_output_unref (output);
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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
+ ctx = g_task_get_task_data (task);
+
if (qmi_message_wms_raw_send_output_get_message_id (output, &message_id, NULL))
mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data,
message_id);
@@ -372,12 +381,14 @@ send_generic_ready (QmiClientWms *client,
/* Go on with next part */
ctx->current = g_list_next (ctx->current);
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
static void
-sms_send_generic (SmsSendContext *ctx)
+sms_send_generic (GTask *task)
{
+ MMSmsQmi *self;
+ SmsSendContext *ctx;
QmiMessageWmsRawSendInput *input;
guint8 *pdu = NULL;
guint pdulen = 0;
@@ -385,24 +396,27 @@ sms_send_generic (SmsSendContext *ctx)
GArray *array;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Get PDU */
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);
+ pdu = mm_sms_part_3gpp_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, &msgstart, self, &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);
+ pdu = mm_sms_part_cdma_get_submit_pdu ((MMSmsPart *)ctx->current->data, &pdulen, self, &error);
if (!pdu) {
if (error)
- g_simple_async_result_take_error (ctx->result, error);
+ g_task_return_error (task, 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);
+ g_task_return_new_error (task,
+ 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)));
+ g_object_unref (task);
return;
}
@@ -423,10 +437,10 @@ sms_send_generic (SmsSendContext *ctx)
qmi_client_wms_raw_send (ctx->client,
input,
- 30,
+ MM_BASE_SMS_DEFAULT_SEND_TIMEOUT,
NULL,
(GAsyncReadyCallback)send_generic_ready,
- ctx);
+ task);
qmi_message_wms_raw_send_input_unref (input);
g_array_unref (array);
}
@@ -434,29 +448,33 @@ sms_send_generic (SmsSendContext *ctx)
static void
send_from_storage_ready (QmiClientWms *client,
GAsyncResult *res,
- SmsSendContext *ctx)
+ GTask *task)
{
+ MMSmsQmi *self;
+ SmsSendContext *ctx;
QmiMessageWmsSendFromMemoryStorageOutput *output = NULL;
GError *error = NULL;
guint16 message_id;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_wms_send_from_memory_storage_finish (client, res, &error);
if (!output) {
if (g_error_matches (error,
QMI_CORE_ERROR,
QMI_CORE_ERROR_UNSUPPORTED)) {
- mm_dbg ("Couldn't send SMS from storage: '%s'; trying generic send...",
- error->message);
+ mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message);
g_error_free (error);
ctx->from_storage = FALSE;
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
return;
}
/* Fatal error */
g_prefix_error (&error, "QMI operation failed: ");
- g_simple_async_result_take_error (ctx->result, error);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
@@ -464,11 +482,10 @@ send_from_storage_ready (QmiClientWms *client,
if (g_error_matches (error,
QMI_PROTOCOL_ERROR,
QMI_PROTOCOL_ERROR_INVALID_QMI_COMMAND)) {
- mm_dbg ("Couldn't send SMS from storage: '%s'; trying generic send...",
- error->message);
+ mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message);
g_error_free (error);
ctx->from_storage = FALSE;
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
} else {
QmiWmsGsmUmtsRpCause rp_cause;
QmiWmsGsmUmtsTpCause tp_cause;
@@ -480,34 +497,34 @@ send_from_storage_ready (QmiClientWms *client,
&rp_cause,
&tp_cause,
NULL)) {
- mm_warn ("Couldn't send SMS; RP cause (%u): '%s'; TP cause (%u): '%s'",
- rp_cause,
- qmi_wms_gsm_umts_rp_cause_get_string (rp_cause),
- tp_cause,
- qmi_wms_gsm_umts_tp_cause_get_string (tp_cause));
+ mm_obj_warn (self, "couldn't send SMS; RP cause (%u): %s; TP cause (%u): %s",
+ rp_cause,
+ qmi_wms_gsm_umts_rp_cause_get_string (rp_cause),
+ tp_cause,
+ 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));
+ mm_obj_warn (self, "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));
+ mm_obj_warn (self, "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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
}
qmi_message_wms_send_from_memory_storage_output_unref (output);
@@ -522,19 +539,23 @@ send_from_storage_ready (QmiClientWms *client,
/* Go on with next part */
ctx->current = g_list_next (ctx->current);
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
static void
-sms_send_from_storage (SmsSendContext *ctx)
+sms_send_from_storage (GTask *task)
{
+ MMBaseSms *self;
+ SmsSendContext *ctx;
QmiMessageWmsSendFromMemoryStorageInput *input;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
input = qmi_message_wms_send_from_memory_storage_input_new ();
qmi_message_wms_send_from_memory_storage_input_set_information (
input,
- mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (ctx->self)),
+ mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (self)),
mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
(MM_SMS_PART_IS_3GPP ((MMSmsPart *)ctx->current->data) ?
QMI_WMS_MESSAGE_MODE_GSM_WCDMA :
@@ -544,28 +565,32 @@ sms_send_from_storage (SmsSendContext *ctx)
qmi_client_wms_send_from_memory_storage (
ctx->client,
input,
- 30,
+ MM_BASE_SMS_DEFAULT_SEND_TIMEOUT,
NULL,
(GAsyncReadyCallback)send_from_storage_ready,
- ctx);
+ task);
qmi_message_wms_send_from_memory_storage_input_unref (input);
}
static void
-sms_send_next_part (SmsSendContext *ctx)
+sms_send_next_part (GTask *task)
{
+ SmsSendContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
if (!ctx->current) {
/* Done we are */
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
- sms_send_context_complete_and_free (ctx);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
return;
}
/* Send from storage? */
if (ctx->from_storage)
- sms_send_from_storage (ctx);
+ sms_send_from_storage (task);
else
- sms_send_generic (ctx);
+ sms_send_generic (task);
}
static void
@@ -576,6 +601,7 @@ sms_send (MMBaseSms *self,
SmsSendContext *ctx;
QmiClient *client = NULL;
GError *error = NULL;
+ GTask *task;
/* Ensure WMS client */
if (!ensure_qmi_client (MM_SMS_QMI (self),
@@ -585,12 +611,7 @@ sms_send (MMBaseSms *self,
/* Setup the context */
ctx = g_slice_new0 (SmsSendContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_send);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx->client = QMI_CLIENT_WMS (g_object_ref (client));
g_object_get (self,
MM_BASE_SMS_MODEM, &ctx->modem,
NULL);
@@ -600,35 +621,33 @@ sms_send (MMBaseSms *self,
ctx->current = mm_base_sms_get_parts (self);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free);
+
/* 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);
+ g_task_return_error (task, error);
+ g_object_unref (task);
return;
}
- sms_send_next_part (ctx);
+ sms_send_next_part (task);
}
/*****************************************************************************/
typedef struct {
- MMBaseSms *self;
MMBaseModem *modem;
QmiClientWms *client;
- GSimpleAsyncResult *result;
GList *current;
guint n_failed;
} SmsDeletePartsContext;
static void
-sms_delete_parts_context_complete_and_free (SmsDeletePartsContext *ctx)
+sms_delete_parts_context_free (SmsDeletePartsContext *ctx)
{
- g_simple_async_result_complete_in_idle (ctx->result);
- g_object_unref (ctx->result);
g_object_unref (ctx->client);
g_object_unref (ctx->modem);
- g_object_unref (ctx->self);
g_slice_free (SmsDeletePartsContext, ctx);
}
@@ -637,31 +656,36 @@ sms_delete_finish (MMBaseSms *self,
GAsyncResult *res,
GError **error)
{
- return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+ return g_task_propagate_boolean (G_TASK (res), error);
}
-static void delete_next_part (SmsDeletePartsContext *ctx);
+static void delete_next_part (GTask *task);
static void
delete_part_ready (QmiClientWms *client,
GAsyncResult *res,
- SmsDeletePartsContext *ctx)
+ GTask *task)
{
+ MMSmsQmi *self;
+ SmsDeletePartsContext *ctx;
QmiMessageWmsDeleteOutput *output = NULL;
GError *error = NULL;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
output = qmi_client_wms_delete_finish (client, res, &error);
if (!output) {
ctx->n_failed++;
- mm_dbg ("QMI operation failed: Couldn't delete SMS part with index %u: '%s'",
- mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
- error->message);
+ mm_obj_dbg (self, "QMI operation failed: couldn't delete SMS part with index %u: %s",
+ mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
+ error->message);
g_error_free (error);
} else if (!qmi_message_wms_delete_output_get_result (output, &error)) {
ctx->n_failed++;
- mm_dbg ("Couldn't delete SMS part with index %u: '%s'",
- mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
- error->message);
+ mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s",
+ mm_sms_part_get_index ((MMSmsPart *)ctx->current->data),
+ error->message);
g_error_free (error);
}
@@ -672,14 +696,19 @@ delete_part_ready (QmiClientWms *client,
mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX);
ctx->current = g_list_next (ctx->current);
- delete_next_part (ctx);
+ delete_next_part (task);
}
static void
-delete_next_part (SmsDeletePartsContext *ctx)
+delete_next_part (GTask *task)
{
+ MMBaseSms *self;
+ SmsDeletePartsContext *ctx;
QmiMessageWmsDeleteInput *input;
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
/* Skip non-stored parts */
while (ctx->current &&
mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX)
@@ -688,22 +717,22 @@ delete_next_part (SmsDeletePartsContext *ctx)
/* If all removed, we're done */
if (!ctx->current) {
if (ctx->n_failed > 0)
- g_simple_async_result_set_error (ctx->result,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Couldn't delete %u parts from this SMS",
- ctx->n_failed);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Couldn't delete %u parts from this SMS",
+ ctx->n_failed);
else
- g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ g_task_return_boolean (task, TRUE);
- sms_delete_parts_context_complete_and_free (ctx);
+ g_object_unref (task);
return;
}
input = qmi_message_wms_delete_input_new ();
qmi_message_wms_delete_input_set_memory_storage (
input,
- mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (ctx->self)),
+ mm_sms_storage_to_qmi_storage_type (mm_base_sms_get_storage (self)),
NULL);
qmi_message_wms_delete_input_set_memory_index (
input,
@@ -720,7 +749,7 @@ delete_next_part (SmsDeletePartsContext *ctx)
5,
NULL,
(GAsyncReadyCallback)delete_part_ready,
- ctx);
+ task);
qmi_message_wms_delete_input_unref (input);
}
@@ -731,6 +760,7 @@ sms_delete (MMBaseSms *self,
{
SmsDeletePartsContext *ctx;
QmiClient *client = NULL;
+ GTask *task;
/* Ensure WMS client */
if (!ensure_qmi_client (MM_SMS_QMI (self),
@@ -739,19 +769,17 @@ sms_delete (MMBaseSms *self,
return;
ctx = g_slice_new0 (SmsDeletePartsContext);
- ctx->result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- sms_delete);
- ctx->self = g_object_ref (self);
- ctx->client = g_object_ref (client);
+ ctx->client = QMI_CLIENT_WMS (g_object_ref (client));
g_object_get (self,
MM_BASE_SMS_MODEM, &ctx->modem,
NULL);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free);
+
/* Go on deleting parts */
ctx->current = mm_base_sms_get_parts (self);
- delete_next_part (ctx);
+ delete_next_part (task);
}
/*****************************************************************************/
diff --git a/src/mm-sms-qmi.h b/src/mm-sms-qmi.h
index 64a2f300..af60a7f5 100644
--- a/src/mm-sms-qmi.h
+++ b/src/mm-sms-qmi.h
@@ -45,6 +45,7 @@ struct _MMSmsQmiClass {
};
GType mm_sms_qmi_get_type (void);
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsQmi, g_object_unref)
MMBaseSms *mm_sms_qmi_new (MMBaseModem *modem);
diff --git a/src/mm-utils.h b/src/mm-utils.h
new file mode 100644
index 00000000..43fcbfef
--- /dev/null
+++ b/src/mm-utils.h
@@ -0,0 +1,83 @@
+/* -*- 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:
+ *
+ * Singleton support imported from NetworkManager.
+ * (C) Copyright 2014 Red Hat, Inc.
+ *
+ * GPtrArray lookup with GEqualFunc imported from GLib 2.48
+ */
+
+#ifndef MM_UTILS_H
+#define MM_UTILS_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mm-log-object.h"
+
+/*****************************************************************************/
+
+#define MM_DEFINE_SINGLETON_INSTANCE(TYPE) \
+ static TYPE *singleton_instance;
+
+#define MM_DEFINE_SINGLETON_WEAK_REF(TYPE) \
+ static void \
+ _singleton_instance_weak_ref_cb (gpointer data, \
+ GObject *where_the_object_was) \
+ { \
+ mm_obj_dbg (singleton_instance, "singleton disposed"); \
+ singleton_instance = NULL; \
+ } \
+ static inline void \
+ mm_singleton_instance_weak_ref_register (void) \
+ { \
+ g_object_weak_ref (G_OBJECT (singleton_instance), _singleton_instance_weak_ref_cb, NULL); \
+ }
+
+#define MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE) \
+ static void __attribute__((destructor)) \
+ _singleton_destructor (void) \
+ { \
+ if (singleton_instance) { \
+ if (G_OBJECT (singleton_instance)->ref_count > 1) \
+ mm_obj_dbg (singleton_instance, "singleton disowned"); \
+ g_object_unref (singleton_instance); \
+ } \
+ }
+
+/* By default, the getter will assert that the singleton will be created only once. You can
+ * change this by redefining MM_DEFINE_SINGLETON_ALLOW_MULTIPLE. */
+#ifndef MM_DEFINE_SINGLETON_ALLOW_MULTIPLE
+#define MM_DEFINE_SINGLETON_ALLOW_MULTIPLE FALSE
+#endif
+
+#define MM_DEFINE_SINGLETON_GETTER(TYPE, GETTER, GTYPE, ...) \
+ MM_DEFINE_SINGLETON_INSTANCE (TYPE) \
+ MM_DEFINE_SINGLETON_WEAK_REF (TYPE) \
+ TYPE * \
+ GETTER (void) \
+ { \
+ if (G_UNLIKELY (!singleton_instance)) { \
+ static char _already_created = FALSE; \
+ \
+ g_assert (!_already_created || (MM_DEFINE_SINGLETON_ALLOW_MULTIPLE)); \
+ _already_created = TRUE; \
+ singleton_instance = (g_object_new (GTYPE, ##__VA_ARGS__, NULL)); \
+ g_assert (singleton_instance); \
+ mm_singleton_instance_weak_ref_register (); \
+ mm_obj_dbg (singleton_instance, "singleton created"); \
+ } \
+ return singleton_instance; \
+ } \
+ MM_DEFINE_SINGLETON_DESTRUCTOR(TYPE)
+
+#endif /* MM_UTILS_H */
diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
index c52e4d1d..26a05fa3 100644
--- a/src/tests/Makefile.am
+++ b/src/tests/Makefile.am
@@ -1,187 +1,67 @@
-include $(top_srcdir)/gtester.make
-
-noinst_PROGRAMS = \
- test-modem-helpers \
- test-charsets \
- test-qcdm-serial-port \
- test-at-serial-port \
- test-sms-part-3gpp \
- test-sms-part-cdma
-
-if WITH_QMI
-noinst_PROGRAMS += test-modem-helpers-qmi
-endif
-
-TEST_PROGS += $(noinst_PROGRAMS)
-
-################
-
-test_modem_helpers_SOURCES = \
- test-modem-helpers.c
-
-test_modem_helpers_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_modem_helpers_LDADD = \
- $(top_builddir)/src/libmodem-helpers.la \
- $(MM_LIBS)
-
-if WITH_QMI
-test_modem_helpers_CPPFLAGS += $(QMI_CFLAGS)
-test_modem_helpers_LDADD += $(QMI_LIBS)
-endif
-
-################
-
-if WITH_QMI
-test_modem_helpers_qmi_SOURCES = \
- test-modem-helpers-qmi.c
-
-test_modem_helpers_qmi_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 \
- $(QMI_CFLAGS)
-
-test_modem_helpers_qmi_LDADD = \
- $(top_builddir)/src/libmodem-helpers.la \
- $(MM_LIBS) \
- $(QMI_LIBS)
-endif
-
-################
-
-test_charsets_SOURCES = \
- test-charsets.c
-
-test_charsets_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_charsets_LDADD = \
- $(top_builddir)/src/libmodem-helpers.la \
- $(MM_LIBS)
-
-if WITH_QMI
-test_charsets_CPPFLAGS += $(QMI_CFLAGS)
-test_charsets_LDADD += $(QMI_LIBS)
-endif
-
-################
+include $(top_srcdir)/gtester.make
-test_qcdm_serial_port_SOURCES = \
- test-qcdm-serial-port.c
+################################################################################
+# common
+################################################################################
-test_qcdm_serial_port_CPPFLAGS = \
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
$(MM_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
-I$(top_srcdir) \
- -I$(top_srcdir)/src \
-I$(top_srcdir)/include \
-I$(top_builddir)/include \
+ -I$(top_srcdir)/libqcdm/src \
-I$(top_srcdir)/libmm-glib \
- -I$(top_srcdir)/libmm-glib/generated \
- -I$(top_builddir)/libmm-glib/generated
-
-test_qcdm_serial_port_LDADD = \
- $(MM_LIBS) \
+ -I${top_builddir}/libmm-glib/generated \
+ -I${top_srcdir}/src/ \
+ -I${top_builddir}/src/ \
+ -I${top_srcdir}/src/kerneldevice \
+ -DTESTUDEVRULESDIR=\"${top_srcdir}/src/\" \
+ $(NULL)
+
+LDADD = \
+ $(top_builddir)/src/libhelpers.la \
$(top_builddir)/src/libport.la \
- $(top_builddir)/src/libmodem-helpers.la \
- $(top_builddir)/libqcdm/src/libqcdm.la \
- -lutil
-
-if WITH_QMI
-test_qcdm_serial_port_CPPFLAGS += $(QMI_CFLAGS)
-test_qcdm_serial_port_LDADD += $(QMI_LIBS)
-endif
-
-################
-
-test_at_serial_port_SOURCES = \
- test-at-serial-port.c
+ $(top_builddir)/src/libkerneldevice.la \
+ $(NULL)
-test_at_serial_port_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_at_serial_port_LDADD = \
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
$(MM_LIBS) \
- $(top_builddir)/src/libport.la \
- $(top_builddir)/src/libmodem-helpers.la \
- -lutil
+ $(CODE_COVERAGE_LDFLAGS) \
+ -lutil \
+ $(NULL)
if WITH_QMI
-test_at_serial_port_CPPFLAGS += $(QMI_CFLAGS)
-test_at_serial_port_LDADD += $(QMI_LIBS)
+AM_CFLAGS += $(QMI_CFLAGS)
+AM_LDFLAGS += $(QMI_LIBS)
endif
-################
-
-test_sms_part_3gpp_SOURCES = \
- test-sms-part-3gpp.c
-
-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
-
-test_sms_part_3gpp_LDADD = \
- $(top_builddir)/src/libmodem-helpers.la \
- $(MM_LIBS)
-
-if WITH_QMI
-test_sms_part_3gpp_CPPFLAGS += $(QMI_CFLAGS)
-test_sms_part_3gpp_LDADD += $(QMI_LIBS)
+if WITH_MBIM
+AM_CFLAGS += $(MBIM_CFLAGS)
+AM_LDFLAGS += $(MBIM_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
+################################################################################
+# tests
+# note: we abuse AM_LDFLAGS to include the libraries being tested
+################################################################################
-test_sms_part_cdma_LDADD = \
- $(top_builddir)/src/libmodem-helpers.la \
- $(MM_LIBS)
+noinst_PROGRAMS = \
+ test-modem-helpers \
+ test-charsets \
+ test-qcdm-serial-port \
+ test-at-serial-port \
+ test-sms-part-3gpp \
+ test-sms-part-cdma \
+ test-udev-rules \
+ test-error-helpers \
+ $(NULL)
if WITH_QMI
-test_sms_part_cdma_CPPFLAGS += $(QMI_CFLAGS)
-test_sms_part_cdma_LDADD += $(QMI_LIBS)
+noinst_PROGRAMS += test-modem-helpers-qmi
endif
+
+TEST_PROGS += $(noinst_PROGRAMS)
diff --git a/src/tests/meson.build b/src/tests/meson.build
new file mode 100644
index 00000000..c059ae2d
--- /dev/null
+++ b/src/tests/meson.build
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+test_units = {
+ 'at-serial-port': libport_dep,
+ 'charsets': libhelpers_dep,
+ 'error-helpers': libhelpers_dep,
+ 'modem-helpers': libhelpers_dep,
+ 'sms-part-3gpp': libhelpers_dep,
+ 'sms-part-cdma': libhelpers_dep,
+ 'udev-rules': libkerneldevice_dep,
+}
+
+deps = [
+ libport_dep,
+ libqcdm_dep,
+ util_dep,
+]
+
+test_units += {'qcdm-serial-port': deps}
+
+if enable_qmi
+ test_units += {'modem-helpers-qmi': libkerneldevice_dep}
+endif
+
+foreach test_unit, test_deps: test_units
+ test_name = 'test-' + test_unit
+
+ exe = executable(
+ test_name,
+ sources: test_name + '.c',
+ include_directories: top_inc,
+ dependencies: test_deps,
+ c_args: '-DTESTUDEVRULESDIR="@0@"'.format(src_dir)
+ )
+
+ test(test_name, exe)
+endforeach
diff --git a/src/tests/test-at-serial-port.c b/src/tests/test-at-serial-port.c
index c0be5fa1..5372c478 100644
--- a/src/tests/test-at-serial-port.c
+++ b/src/tests/test-at-serial-port.c
@@ -18,13 +18,20 @@
#include <glib.h>
#include "mm-port-serial-at.h"
-#include "mm-log.h"
+#include "mm-serial-parsers.h"
+#include "mm-log-test.h"
typedef struct {
- gchar *original;
- gchar *without_echo;
+ const gchar *original;
+ const gchar *without_echo;
} EchoRemovalTest;
+typedef struct {
+ const gchar *response;
+ const gboolean found;
+ const gboolean expected_error;
+} ParseResponseTest;
+
static const EchoRemovalTest echo_removal_tests[] = {
{ "\r\n", "\r\n" },
{ "\r", "\r" },
@@ -41,6 +48,41 @@ static const EchoRemovalTest echo_removal_tests[] = {
{ "\r\nthis is valid\r\nand so is this\r\n", "\r\nthis is valid\r\nand so is this\r\n" },
};
+static const ParseResponseTest parse_ok_tests[] = {
+ { "\r\nOK\r\n", TRUE, FALSE},
+ { "\r\nOK\r\n\r\n+CMTI: \"ME\",1\r\n", TRUE, FALSE},
+ { "\r\nOK\r\n\r\n+CIEV: 7,1\r\n\r\n+CRING: VOICE\r\n\r\n+CLIP: \"+0123456789\",145,,,,0\r\n", TRUE, FALSE},
+ { "\r\nUNKNOWN COMMAND\r\n", FALSE, FALSE}
+};
+
+static const ParseResponseTest parse_error_tests[] = {
+ { "\r\nUNKNOWN COMMAND\r\n", FALSE, FALSE},
+ { "\r\nERROR\r\n", TRUE, TRUE},
+ { "\r\nERROR\r\n\r\noooops\r\n", TRUE, TRUE},
+ { "\r\n+CME ERROR: raspberry\r\n", TRUE, TRUE},
+ { "\r\n+CME ERROR: 123\r\n", TRUE, TRUE},
+ { "\r\n+CME ERROR: \r\n", TRUE, TRUE},
+ { "\r\n+CME ERROR:\r\n", FALSE, FALSE},
+ { "\r\n+CMS ERROR: bananas\r\n", TRUE, TRUE},
+ { "\r\n+CMS ERROR: 456\r\n", TRUE, TRUE},
+ { "\r\n+CMS ERROR: \r\n", TRUE, TRUE},
+ { "\r\n+CMS ERROR:\r\n", FALSE, FALSE},
+ { "\r\nMODEM ERROR: 5\r\n", TRUE, TRUE},
+ { "\r\nMODEM ERROR: apple\r\n", FALSE, FALSE},
+ { "\r\nMODEM ERROR: \r\n", FALSE, FALSE},
+ { "\r\nMODEM ERROR:\r\n", FALSE, FALSE},
+ { "\r\nCOMMAND NOT SUPPORT\r\n", TRUE, TRUE},
+ { "\r\nCOMMAND NOT SUPPORT\r\n\r\nSomething extra\r\n", TRUE, TRUE},
+ { "\r\nNO CARRIER\r\n", TRUE, TRUE},
+ { "\r\nNO CARRIER\r\n\r\nSomething extra\r\n", TRUE, TRUE},
+ { "\r\nBUSY\r\n", TRUE, TRUE},
+ { "\r\nBUSY\r\n\r\nSomething extra\r\n", TRUE, TRUE},
+ { "\r\nNO ANSWER\r\n", TRUE, TRUE},
+ { "\r\nNO ANSWER\r\n\r\nSomething extra\r\n", TRUE, TRUE},
+ { "\r\nNO DIALTONE\r\n", TRUE, TRUE},
+ { "\r\nNO DIALTONE\r\n\r\nSomething extra\r\n", TRUE, TRUE}
+};
+
static void
at_serial_echo_removal (void)
{
@@ -64,32 +106,56 @@ at_serial_echo_removal (void)
}
}
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+static void
+_run_parse_test (const ParseResponseTest tests[], guint number_of_tests)
+{
+ guint i;
+ gpointer parser;
+ GError *error = NULL;
+ gboolean found = FALSE;
+ GString *response;
+
+ for (i = 0; i < number_of_tests; i++) {
+ parser = mm_serial_parser_v1_new ();
+ response = g_string_new (tests[i].response);
+ found = mm_serial_parser_v1_parse (parser, response, NULL, &error);
+
+ /* Verify if we expect a match or not */
+ g_assert_cmpint (found, ==, tests[i].found);
+
+ /* Error expected */
+ if (tests[i].expected_error) {
+ g_assert (error != NULL);
+ }
+ /* No error expected */
+ else {
+ g_assert_no_error (error);
+ }
+
+ g_string_free (response, TRUE);
+ error = NULL ;
+ }
+}
+
+static void
+at_serial_parse_ok (void)
+{
+ _run_parse_test (parse_ok_tests, G_N_ELEMENTS(parse_ok_tests));
+}
+
+static void
+at_serial_parse_error (void)
{
-#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
+ _run_parse_test (parse_error_tests, G_N_ELEMENTS(parse_error_tests));
}
int main (int argc, char **argv)
{
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/ModemManager/AT-serial/echo-removal", at_serial_echo_removal);
+ g_test_add_func ("/ModemManager/AT-serial/parse-ok", at_serial_parse_ok);
+ g_test_add_func ("/ModemManager/AT-serial/parse-error", at_serial_parse_error);
return g_test_run ();
}
diff --git a/src/tests/test-charsets.c b/src/tests/test-charsets.c
index 1c314133..8735fd22 100644
--- a/src/tests/test-charsets.c
+++ b/src/tests/test-charsets.c
@@ -15,90 +15,99 @@
#include <glib.h>
#include <string.h>
+#include <locale.h>
#include "mm-modem-helpers.h"
+#include "mm-log-test.h"
static void
-test_def_chars (void *f, gpointer d)
+common_test_gsm7 (const gchar *in_utf8)
{
- /* Test that a string with all the characters in the GSM 03.38 charset
- * are converted from UTF-8 to GSM and back to UTF-8 successfully.
- */
- static const char *s = "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";
- guint8 *gsm, *utf8;
- guint32 len = 0;
+ guint32 packed_gsm_len = 0;
+ guint32 unpacked_gsm_len_2 = 0;
+ g_autoptr(GByteArray) unpacked_gsm = NULL;
+ g_autofree guint8 *packed_gsm = NULL;
+ guint8 *unpacked_gsm_2 = NULL;
+ g_autoptr(GByteArray) unpacked_gsm_2_array = NULL;
+ g_autofree gchar *built_utf8 = NULL;
+ g_autoptr(GError) error = NULL;
/* Convert to GSM */
- gsm = mm_charset_utf8_to_unpacked_gsm (s, &len);
- g_assert (gsm);
- g_assert_cmpint (len, ==, 127);
+ unpacked_gsm = mm_modem_charset_bytearray_from_utf8 (in_utf8, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_nonnull (unpacked_gsm);
+ g_assert_no_error (error);
+
+ /* Pack */
+ packed_gsm = mm_charset_gsm_pack (unpacked_gsm->data, unpacked_gsm->len, 0, &packed_gsm_len);
+ g_assert_nonnull (packed_gsm);
+ g_assert_cmpuint (packed_gsm_len, <=, unpacked_gsm->len);
+
+#if 0
+ {
+ g_autofree gchar *hex_packed = NULL;
+
+ /* Print */
+ hex_packed = mm_utils_bin2hexstr (packed_gsm, packed_gsm_len);
+ g_print ("----------\n");
+ g_print ("input string: '%s'\n", in_utf8);
+ g_print ("gsm-7 encoded string: %s\n", hex_packed);
+ }
+#endif
+
+ /* Unpack */
+ unpacked_gsm_2 = mm_charset_gsm_unpack (packed_gsm, packed_gsm_len * 8 / 7, 0, &unpacked_gsm_len_2);
+ g_assert_nonnull (unpacked_gsm_2);
+ unpacked_gsm_2_array = g_byte_array_new_take (unpacked_gsm_2, unpacked_gsm_len_2);
/* And back to UTF-8 */
- utf8 = mm_charset_gsm_unpacked_to_utf8 (gsm, len);
- g_assert (utf8);
- g_assert_cmpstr (s, ==, (const char *) utf8);
+ built_utf8 = mm_modem_charset_bytearray_to_utf8 (unpacked_gsm_2_array, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_nonnull (built_utf8);
+ g_assert_no_error (error);
+ g_assert_cmpstr (built_utf8, ==, in_utf8);
+}
- g_free (gsm);
- g_free (utf8);
+static void
+test_gsm7_default_chars (void)
+{
+ /* Test that a string with all the characters in the GSM 03.38 charset
+ * are converted from UTF-8 to GSM and back to UTF-8 successfully.
+ */
+ static const char *s = "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà";
+
+ common_test_gsm7 (s);
}
static void
-test_esc_chars (void *f, gpointer d)
+test_gsm7_extended_chars (void)
{
/* Test that a string with all the characters in the extended GSM 03.38
* charset are converted from UTF-8 to GSM and back to UTF-8 successfully.
*/
static const char *s = "\f^{}\\[~]|€";
- guint8 *gsm, *utf8;
- guint32 len = 0;
-
- /* Convert to GSM */
- gsm = mm_charset_utf8_to_unpacked_gsm (s, &len);
- g_assert (gsm);
- g_assert_cmpint (len, ==, 20);
-
- /* And back to UTF-8 */
- utf8 = mm_charset_gsm_unpacked_to_utf8 (gsm, len);
- g_assert (utf8);
- g_assert_cmpstr (s, ==, (const char *) utf8);
- g_free (gsm);
- g_free (utf8);
+ common_test_gsm7 (s);
}
static void
-test_mixed_chars (void *f, gpointer d)
+test_gsm7_mixed_chars (void)
{
/* Test that a string with a mix of GSM 03.38 default and extended characters
* is converted from UTF-8 to GSM and back to UTF-8 successfully.
*/
static const char *s = "@£$¥èéùìø\fΩΠΨΣΘ{ΞÆæß(})789\\:;<=>[?¡QRS]TUÖ|Ñܧ¿abpqrstuvöñüà€";
- guint8 *gsm, *utf8;
- guint32 len = 0;
-
- /* Convert to GSM */
- gsm = mm_charset_utf8_to_unpacked_gsm (s, &len);
- g_assert (gsm);
- g_assert_cmpint (len, ==, 69);
-
- /* And back to UTF-8 */
- utf8 = mm_charset_gsm_unpacked_to_utf8 (gsm, len);
- g_assert (utf8);
- g_assert_cmpstr (s, ==, (const char *) utf8);
- g_free (gsm);
- g_free (utf8);
+ common_test_gsm7 (s);
}
static void
-test_unpack_gsm7 (void *f, gpointer d)
+test_gsm7_unpack_basic (void)
{
static const guint8 gsm[] = { 0xC8, 0xF7, 0x1D, 0x14, 0x96, 0x97, 0x41, 0xF9, 0x77, 0xFD, 0x07 };
static const guint8 expected[] = { 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f };
guint8 *unpacked;
guint32 unpacked_len = 0;
- unpacked = gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len);
+ unpacked = mm_charset_gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len);
g_assert (unpacked);
g_assert_cmpint (unpacked_len, ==, sizeof (expected));
g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0);
@@ -107,7 +116,7 @@ test_unpack_gsm7 (void *f, gpointer d)
}
static void
-test_unpack_gsm7_7_chars (void *f, gpointer d)
+test_gsm7_unpack_7_chars (void)
{
static const guint8 gsm[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 };
static const guint8 expected[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75};
@@ -119,7 +128,7 @@ test_unpack_gsm7_7_chars (void *f, gpointer d)
* we expect to get the number of characters that were specified.
*/
- unpacked = gsm_unpack (gsm, 7 , 0, &unpacked_len);
+ unpacked = mm_charset_gsm_unpack (gsm, 7 , 0, &unpacked_len);
g_assert (unpacked);
g_assert_cmpint (unpacked_len, ==, sizeof (expected));
g_assert_cmpint (memcmp (unpacked, expected, unpacked_len), ==, 0);
@@ -128,7 +137,7 @@ test_unpack_gsm7_7_chars (void *f, gpointer d)
}
static void
-test_unpack_gsm7_all_chars (void *f, gpointer d)
+test_gsm7_unpack_all_chars (void)
{
/* Packed array of all chars in GSM default and extended charset */
static const guint8 gsm[] = {
@@ -152,7 +161,7 @@ test_unpack_gsm7_all_chars (void *f, gpointer d)
guint32 unpacked_len = 0;
int i;
- unpacked = gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len);
+ unpacked = mm_charset_gsm_unpack (gsm, (sizeof (gsm) * 8) / 7, 0, &unpacked_len);
g_assert (unpacked);
g_assert_cmpint (unpacked_len, ==, 148);
@@ -167,14 +176,14 @@ test_unpack_gsm7_all_chars (void *f, gpointer d)
}
static void
-test_pack_gsm7 (void *f, gpointer d)
+test_gsm7_pack_basic (void)
{
static const guint8 unpacked[] = { 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f };
static const guint8 expected[] = { 0xC8, 0xF7, 0x1D, 0x14, 0x96, 0x97, 0x41, 0xF9, 0x77, 0xFD, 0x07 };
guint8 *packed;
guint32 packed_len = 0;
- packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
+ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
g_assert (packed);
g_assert_cmpint (packed_len, ==, sizeof (expected));
g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0);
@@ -183,7 +192,7 @@ test_pack_gsm7 (void *f, gpointer d)
}
static void
-test_pack_gsm7_7_chars (void *f, gpointer d)
+test_gsm7_pack_7_chars (void)
{
static const guint8 unpacked[] = { 0x71, 0x77, 0x65, 0x72, 0x74, 0x79, 0x75 };
static const guint8 expected[] = { 0xF1, 0x7B, 0x59, 0x4E, 0xCF, 0xD7, 0x01 };
@@ -196,7 +205,7 @@ test_pack_gsm7_7_chars (void *f, gpointer d)
* the intended message to remove it when required.
*/
- packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
+ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
g_assert (packed);
g_assert_cmpint (packed_len, ==, sizeof (expected));
g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0);
@@ -204,27 +213,8 @@ test_pack_gsm7_7_chars (void *f, gpointer d)
g_free (packed);
}
-#if 0
static void
-print_array (const guint8 *array, guint32 len)
-{
- int col;
- guint8 c;
-
- g_print ("\n");
- for (c = 0, col = 0; c < len; c++) {
- g_print ("0x%02X, ", array[c] & 0xFF);
- if (col++ == 11) {
- col = 0;
- g_print ("\n");
- }
- }
- g_print ("\n");
-}
-#endif
-
-static void
-test_pack_gsm7_all_chars (void *f, gpointer d)
+test_gsm7_pack_all_chars (void)
{
/* Packed array of all chars in GSM default and extended charset */
static const guint8 expected[] = {
@@ -254,7 +244,7 @@ test_pack_gsm7_all_chars (void *f, gpointer d)
for (c = 0; c < sizeof (ext); c++)
g_byte_array_append (unpacked, &ext[c], 1);
- packed = gsm_pack (unpacked->data, unpacked->len, 0, &packed_len);
+ packed = mm_charset_gsm_pack (unpacked->data, unpacked->len, 0, &packed_len);
g_assert (packed);
g_assert_cmpint (packed_len, ==, sizeof (expected));
g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0);
@@ -264,7 +254,7 @@ test_pack_gsm7_all_chars (void *f, gpointer d)
}
static void
-test_pack_gsm7_24_chars (void *f, gpointer d)
+test_gsm7_pack_24_chars (void)
{
static const guint8 unpacked[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
@@ -277,7 +267,7 @@ test_pack_gsm7_24_chars (void *f, gpointer d)
* are packed into an exact number of bytes.
*/
- packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
+ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
g_assert (packed);
g_assert_cmpint (packed_len, ==, 21);
@@ -285,7 +275,7 @@ test_pack_gsm7_24_chars (void *f, gpointer d)
}
static void
-test_pack_gsm7_last_septet_alone (void *f, gpointer d)
+test_gsm7_pack_last_septet_alone (void)
{
static const guint8 unpacked[] = {
0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x61, 0x6C,
@@ -303,7 +293,7 @@ test_pack_gsm7_last_septet_alone (void *f, gpointer d)
* septet will be in an octet by itself) packs correctly.
*/
- packed = gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
+ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 0, &packed_len);
g_assert (packed);
g_assert_cmpint (packed_len, ==, sizeof (expected));
@@ -311,14 +301,14 @@ test_pack_gsm7_last_septet_alone (void *f, gpointer d)
}
static void
-test_pack_gsm7_7_chars_offset (void *f, gpointer d)
+test_gsm7_pack_7_chars_offset (void)
{
static const guint8 unpacked[] = { 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x10, 0x2F };
static const guint8 expected[] = { 0x00, 0x5D, 0x66, 0xB3, 0xDF, 0x90, 0x17 };
guint8 *packed;
guint32 packed_len = 0;
- packed = gsm_pack (unpacked, sizeof (unpacked), 5, &packed_len);
+ packed = mm_charset_gsm_pack (unpacked, sizeof (unpacked), 5, &packed_len);
g_assert (packed);
g_assert_cmpint (packed_len, ==, sizeof (expected));
g_assert_cmpint (memcmp (packed, expected, packed_len), ==, 0);
@@ -327,75 +317,159 @@ test_pack_gsm7_7_chars_offset (void *f, gpointer d)
}
static void
-test_take_convert_ucs2_hex_utf8 (void *f, gpointer d)
+test_str_ucs2_to_from_utf8 (void)
{
- gchar *src, *converted;
-
- /* Ensure hex-encoded UCS-2 works */
- src = g_strdup ("0054002d004d006f00620069006c0065");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
- g_assert_cmpstr (converted, ==, "T-Mobile");
- g_free (converted);
+ const gchar *src = "0054002D004D006F00620069006C0065";
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *dst = NULL;
+ g_autoptr(GError) error = NULL;
+
+ utf8 = mm_modem_charset_str_to_utf8 (src, -1, MM_MODEM_CHARSET_UCS2, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (utf8, ==, "T-Mobile");
+
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_UCS2, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (dst, ==, src);
}
static void
-test_take_convert_ucs2_bad_ascii (void *f, gpointer d)
+test_str_gsm_to_from_utf8 (void)
{
- gchar *src, *converted;
+ const gchar *src = "T-Mobile";
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *dst = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* Note: as long as the GSM string doesn't contain the '@' character, str_to_utf8()
+ * and str_from_utf8() can safely be used */
- /* Test that something mostly ASCII returns most of the original string */
- src = g_strdup ("Orange\241");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
- g_assert_cmpstr (converted, ==, "Orange");
- g_free (converted);
+ utf8 = mm_modem_charset_str_to_utf8 (src, -1, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (utf8, ==, src);
+
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (dst, ==, src);
}
static void
-test_take_convert_ucs2_bad_ascii2 (void *f, gpointer d)
+test_str_gsm_to_from_utf8_with_at (void)
{
- gchar *src, *converted;
-
- /* Ensure something completely screwed up doesn't crash */
- src = g_strdup ("\241\255\254\250\244\234");
- converted = mm_charset_take_and_convert_to_utf8 (src, MM_MODEM_CHARSET_UCS2);
- g_assert (converted == NULL);
+ /* The NULs are '@' chars, except for the trailing one which is always taken as end-of-string */
+ const gchar src[] = { 'T', '-', 'M', 0x00, 'o', 'b', 'i', 0x00, 'l', 'e', 0x00 };
+ const gchar *utf8_expected = "T-M@obi@le";
+ const gchar *src_translit = "T-M?obi?le";
+ g_autofree gchar *utf8 = NULL;
+ g_autofree gchar *dst = NULL;
+ g_autoptr(GError) error = NULL;
+
+ /* Note: as long as the GSM string doesn't contain the '@' character, str_to_utf8()
+ * and str_from_utf8() can safely be used */
+
+ utf8 = mm_modem_charset_str_to_utf8 (src, G_N_ELEMENTS (src), MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (utf8, ==, utf8_expected);
+
+ /* if charset conversion from UTF-8 contains '@' chars, running without transliteration
+ * will return an error */
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, FALSE, &error);
+ g_assert_nonnull (error);
+ g_assert_null (dst);
+ g_clear_error (&error);
+
+ /* with transliteration, '@'->'?' */
+ dst = mm_modem_charset_str_from_utf8 (utf8, MM_MODEM_CHARSET_GSM, TRUE, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (dst, ==, src_translit);
}
+struct charset_can_convert_to_test_s {
+ const char *utf8;
+ gboolean to_gsm;
+ gboolean to_ira;
+ gboolean to_8859_1;
+ gboolean to_ucs2;
+ gboolean to_utf16;
+ gboolean to_pccp437;
+ gboolean to_pcdn;
+};
-typedef GTestFixtureFunc TCFunc;
-
-#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
+static void
+test_charset_can_covert_to (void)
+{
+ static const struct charset_can_convert_to_test_s charset_can_convert_to_test[] = {
+ {
+ .utf8 = "",
+ .to_gsm = TRUE, .to_ira = TRUE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE,
+ },
+ {
+ .utf8 = " ",
+ .to_gsm = TRUE, .to_ira = TRUE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE,
+ },
+ {
+ .utf8 = "some basic ascii",
+ .to_gsm = TRUE, .to_ira = TRUE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE,
+ },
+ {
+ .utf8 = "ホモ・サピエンス 喂人类 katakana, chinese, english: UCS2 takes it all",
+ .to_gsm = FALSE, .to_ira = FALSE, .to_8859_1 = FALSE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = FALSE, .to_pcdn = FALSE,
+ },
+ {
+ .utf8 = "Some from the GSM7 basic set: a % Ψ Ω ñ ö è æ",
+ .to_gsm = TRUE, .to_ira = FALSE, .to_8859_1 = FALSE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = FALSE, .to_pcdn = FALSE,
+ },
+ {
+ .utf8 = "More from the GSM7 extended set: {} [] ~ € |",
+ .to_gsm = TRUE, .to_ira = FALSE, .to_8859_1 = FALSE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = FALSE, .to_pcdn = FALSE,
+ },
+ {
+ .utf8 = "patín cannot be encoded in GSM7 or IRA, but is valid UCS2, ISO-8859-1, CP437 and CP850",
+ .to_gsm = FALSE, .to_ira = FALSE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE,
+ },
+ {
+ .utf8 = "ècole can be encoded in multiple ways, but not in IRA",
+ .to_gsm = TRUE, .to_ira = FALSE, .to_8859_1 = TRUE, .to_ucs2 = TRUE, .to_utf16 = TRUE, .to_pccp437 = TRUE, .to_pcdn = TRUE,
+ },
+ };
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (charset_can_convert_to_test); i++) {
+ g_debug ("testing charset conversion: '%s'", charset_can_convert_to_test[i].utf8);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_GSM) == charset_can_convert_to_test[i].to_gsm);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_IRA) == charset_can_convert_to_test[i].to_ira);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_8859_1) == charset_can_convert_to_test[i].to_8859_1);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_UCS2) == charset_can_convert_to_test[i].to_ucs2);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_UTF16) == charset_can_convert_to_test[i].to_utf16);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_PCCP437) == charset_can_convert_to_test[i].to_pccp437);
+ g_assert (mm_charset_can_convert_to (charset_can_convert_to_test[i].utf8, MM_MODEM_CHARSET_PCDN) == charset_can_convert_to_test[i].to_pcdn);
+ }
+}
int main (int argc, char **argv)
{
- GTestSuite *suite;
- gint result;
+ setlocale (LC_ALL, "");
g_test_init (&argc, &argv, NULL);
- suite = g_test_get_root ();
-
- g_test_suite_add (suite, TESTCASE (test_def_chars, NULL));
- g_test_suite_add (suite, TESTCASE (test_esc_chars, NULL));
- g_test_suite_add (suite, TESTCASE (test_mixed_chars, NULL));
-
- g_test_suite_add (suite, TESTCASE (test_unpack_gsm7, NULL));
- g_test_suite_add (suite, TESTCASE (test_unpack_gsm7_7_chars, NULL));
- g_test_suite_add (suite, TESTCASE (test_unpack_gsm7_all_chars, NULL));
-
- g_test_suite_add (suite, TESTCASE (test_pack_gsm7, NULL));
- g_test_suite_add (suite, TESTCASE (test_pack_gsm7_7_chars, NULL));
- g_test_suite_add (suite, TESTCASE (test_pack_gsm7_all_chars, NULL));
- g_test_suite_add (suite, TESTCASE (test_pack_gsm7_24_chars, NULL));
- g_test_suite_add (suite, TESTCASE (test_pack_gsm7_last_septet_alone, NULL));
-
- g_test_suite_add (suite, TESTCASE (test_pack_gsm7_7_chars_offset, NULL));
-
- g_test_suite_add (suite, TESTCASE (test_take_convert_ucs2_hex_utf8, NULL));
- g_test_suite_add (suite, TESTCASE (test_take_convert_ucs2_bad_ascii, NULL));
- g_test_suite_add (suite, TESTCASE (test_take_convert_ucs2_bad_ascii2, NULL));
-
- result = g_test_run ();
-
- return result;
+ g_test_add_func ("/MM/charsets/gsm7/default-chars", test_gsm7_default_chars);
+ g_test_add_func ("/MM/charsets/gsm7/extended-chars", test_gsm7_extended_chars);
+ g_test_add_func ("/MM/charsets/gsm7/mixed-chars", test_gsm7_mixed_chars);
+ g_test_add_func ("/MM/charsets/gsm7/unpack/basic", test_gsm7_unpack_basic);
+ g_test_add_func ("/MM/charsets/gsm7/unpack/7-chars", test_gsm7_unpack_7_chars);
+ g_test_add_func ("/MM/charsets/gsm7/unpack/all-chars", test_gsm7_unpack_all_chars);
+ g_test_add_func ("/MM/charsets/gsm7/pack/basic", test_gsm7_pack_basic);
+ g_test_add_func ("/MM/charsets/gsm7/pack/7-chars", test_gsm7_pack_7_chars);
+ g_test_add_func ("/MM/charsets/gsm7/pack/all-chars", test_gsm7_pack_all_chars);
+ g_test_add_func ("/MM/charsets/gsm7/pack/24-chars", test_gsm7_pack_24_chars);
+ g_test_add_func ("/MM/charsets/gsm7/pack/last-septet-alone", test_gsm7_pack_last_septet_alone);
+ g_test_add_func ("/MM/charsets/gsm7/pack/7-chars-offset", test_gsm7_pack_7_chars_offset);
+
+ g_test_add_func ("/MM/charsets/str-from-to/ucs2", test_str_ucs2_to_from_utf8);
+ g_test_add_func ("/MM/charsets/str-from-to/gsm", test_str_gsm_to_from_utf8);
+ g_test_add_func ("/MM/charsets/str-from-to/gsm-with-at", test_str_gsm_to_from_utf8_with_at);
+
+ g_test_add_func ("/MM/charsets/can-convert-to", test_charset_can_covert_to);
+
+ return g_test_run ();
}
diff --git a/src/tests/test-error-helpers.c b/src/tests/test-error-helpers.c
new file mode 100644
index 00000000..03bcab98
--- /dev/null
+++ b/src/tests/test-error-helpers.c
@@ -0,0 +1,151 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+#include "mm-error-helpers.h"
+#include "mm-log.h"
+
+#define TEST_ERROR_HELPER(ERROR_CAPS,ERROR_SMALL,ERROR_CAMEL) \
+ static void \
+ test_error_helpers_## ERROR_SMALL (void) \
+ { \
+ GError *error; \
+ gint i; \
+ GEnumClass *enum_class; \
+ \
+ enum_class = g_type_class_ref (MM_TYPE_ ## ERROR_CAPS); \
+ for (i = enum_class->minimum; i <= enum_class->maximum; i++) { \
+ GEnumValue *enum_value; \
+ \
+ enum_value = g_enum_get_value (enum_class, i); \
+ if (enum_value) { \
+ error = mm_## ERROR_SMALL ## _for_code ((MM##ERROR_CAMEL)i, NULL); \
+ g_assert_error (error, MM_ ## ERROR_CAPS, i); \
+ g_error_free (error); \
+ } \
+ } \
+ g_type_class_unref (enum_class); \
+ }
+
+TEST_ERROR_HELPER (CONNECTION_ERROR, connection_error, ConnectionError)
+TEST_ERROR_HELPER (MOBILE_EQUIPMENT_ERROR, mobile_equipment_error, MobileEquipmentError)
+TEST_ERROR_HELPER (MESSAGE_ERROR, message_error, MessageError)
+
+/*****************************************************************************/
+
+static void
+test_error_helpers_mobile_equipment_error_for_code (void)
+{
+ GError *error = NULL;
+
+ /* first */
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE, NULL);
+ g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_PHONE_FAILURE);
+ g_clear_error (&error);
+
+ /* last */
+ error = mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED, NULL);
+ g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_WIRELINE_ACCESS_AREA_NOT_ALLOWED);
+ g_clear_error (&error);
+
+ /* other > 255 */
+ error = mm_mobile_equipment_error_for_code (256, NULL);
+ g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN);
+ g_clear_error (&error);
+}
+
+static void
+test_error_helpers_message_error_for_code (void)
+{
+ GError *error = NULL;
+
+ /* first */
+ error = mm_message_error_for_code (MM_MESSAGE_ERROR_ME_FAILURE, NULL);
+ g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_ME_FAILURE);
+ g_clear_error (&error);
+
+ /* last */
+ error = mm_message_error_for_code (MM_MESSAGE_ERROR_UNKNOWN, NULL);
+ g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN);
+ g_clear_error (&error);
+
+ /* other < 300 */
+ error = mm_message_error_for_code (299, NULL);
+ g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN);
+ g_clear_error (&error);
+
+ /* other > 500 */
+ error = mm_message_error_for_code (501, NULL);
+ g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN);
+ g_clear_error (&error);
+}
+
+/*****************************************************************************/
+
+static void
+test_error_helpers_mobile_equipment_error_for_string (void)
+{
+ GError *error = NULL;
+
+ error = mm_mobile_equipment_error_for_string ("operationnotallowed", NULL);
+ g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_NOT_ALLOWED);
+ g_clear_error (&error);
+
+ error = mm_mobile_equipment_error_for_string ("arratsaldeon", NULL);
+ g_assert_error (error, MM_MOBILE_EQUIPMENT_ERROR, MM_MOBILE_EQUIPMENT_ERROR_UNKNOWN);
+ g_clear_error (&error);
+}
+
+static void
+test_error_helpers_message_error_for_string (void)
+{
+ GError *error = NULL;
+
+ error = mm_message_error_for_string ("operationnotallowed", NULL);
+ g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_NOT_ALLOWED);
+ g_clear_error (&error);
+
+ error = mm_message_error_for_string ("arratsaldeon", NULL);
+ g_assert_error (error, MM_MESSAGE_ERROR, MM_MESSAGE_ERROR_UNKNOWN);
+ g_clear_error (&error);
+}
+
+/*****************************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/error-helpers/connection-error", test_error_helpers_connection_error);
+ g_test_add_func ("/MM/error-helpers/mobile-equipment-error", test_error_helpers_mobile_equipment_error);
+ g_test_add_func ("/MM/error-helpers/message-error", test_error_helpers_message_error);
+
+ g_test_add_func ("/MM/error-helpers/mobile-equipment-error/for-code", test_error_helpers_mobile_equipment_error_for_code);
+ g_test_add_func ("/MM/error-helpers/message-error/for-code", test_error_helpers_message_error_for_code);
+
+ g_test_add_func ("/MM/error-helpers/mobile-equipment-error/for-string", test_error_helpers_mobile_equipment_error_for_string);
+ g_test_add_func ("/MM/error-helpers/message-error/for-string", test_error_helpers_message_error_for_string);
+
+ return g_test_run ();
+}
diff --git a/src/tests/test-modem-helpers-qmi.c b/src/tests/test-modem-helpers-qmi.c
index 0f020f39..fbd6cdba 100644
--- a/src/tests/test-modem-helpers-qmi.c
+++ b/src/tests/test-modem-helpers-qmi.c
@@ -21,7 +21,7 @@
#include "mm-enums-types.h"
#include "mm-modem-helpers-qmi.h"
-#include "mm-log.h"
+#include "mm-log-test.h"
static void
test_capabilities_expected (MMQmiCapabilitiesContext *ctx,
@@ -31,7 +31,7 @@ test_capabilities_expected (MMQmiCapabilitiesContext *ctx,
gchar *expected_str;
gchar *built_str;
- built = mm_modem_capability_from_qmi_capabilities_context (ctx);
+ built = mm_modem_capability_from_qmi_capabilities_context (ctx, NULL);
expected_str = mm_modem_capability_build_string_from_mask (expected);
built_str = mm_modem_capability_build_string_from_mask (built);
@@ -309,31 +309,10 @@ test_gobi3k_cdma (void)
/*****************************************************************************/
-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/QMI/Current-Capabilities/UML290", test_uml290);
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index b493e6bc..1f040e8f 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -17,16 +17,228 @@
#include <glib-object.h>
#include <string.h>
#include <stdlib.h>
+#include <math.h>
+#define _LIBMM_INSIDE_MM
#include <libmm-glib.h>
#include "mm-modem-helpers.h"
-#include "mm-log.h"
+#include "mm-log-test.h"
-#if defined ENABLE_TEST_MESSAGE_TRACES
-#define trace(message, ...) g_print (message, ##__VA_ARGS__)
-#else
-#define trace(...)
-#endif
+#define g_assert_cmpfloat_tolerance(val1, val2, tolerance) \
+ g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
+
+/*****************************************************************************/
+/* Test IFC=? responses */
+
+static void
+test_ifc_response (const gchar *str,
+ const MMFlowControl expected)
+{
+ MMFlowControl mask;
+ GError *error = NULL;
+
+ mask = mm_parse_ifc_test_response (str, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_cmpuint (mask, ==, expected);
+}
+
+static void
+test_ifc_response_all_simple (void)
+{
+ test_ifc_response ("+IFC (0,1,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_all_groups (void)
+{
+ test_ifc_response ("+IFC (0-2),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_none_only (void)
+{
+ test_ifc_response ("+IFC (0),(0)", MM_FLOW_CONTROL_NONE);
+}
+
+static void
+test_ifc_response_xon_xoff_only (void)
+{
+ test_ifc_response ("+IFC (1),(1)", MM_FLOW_CONTROL_XON_XOFF);
+}
+
+static void
+test_ifc_response_rts_cts_only (void)
+{
+ test_ifc_response ("+IFC (2),(2)", MM_FLOW_CONTROL_RTS_CTS);
+}
+
+static void
+test_ifc_response_no_xon_xoff (void)
+{
+ test_ifc_response ("+IFC (0,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_no_xon_xoff_in_ta (void)
+{
+ test_ifc_response ("+IFC (0,1,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_no_xon_xoff_in_te (void)
+{
+ test_ifc_response ("+IFC (0,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_no_rts_cts_simple (void)
+{
+ test_ifc_response ("+IFC (0,1),(0,1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF));
+}
+
+static void
+test_ifc_response_no_rts_cts_groups (void)
+{
+ test_ifc_response ("+IFC (0-1),(0-1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF));
+}
+
+static void
+test_ifc_response_all_simple_and_unknown (void)
+{
+ test_ifc_response ("+IFC (0,1,2,3),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+static void
+test_ifc_response_all_groups_and_unknown (void)
+{
+ test_ifc_response ("+IFC (0-3),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
+}
+
+/*****************************************************************************/
+/* Test WS46=? responses */
+
+static void
+test_ws46_response (const gchar *str,
+ const MMModemMode *expected,
+ guint n_expected)
+{
+ guint i;
+ GArray *modes;
+ GError *error = NULL;
+
+ modes = mm_3gpp_parse_ws46_test_response (str, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (modes != NULL);
+ g_assert_cmpuint (modes->len, ==, n_expected);
+
+ for (i = 0; i < n_expected; i++) {
+ guint j;
+
+ for (j = 0; j < modes->len; j++) {
+ if (expected[i] == g_array_index (modes, MMModemMode, j))
+ break;
+ }
+ g_assert_cmpuint (j, !=, modes->len);
+ }
+ g_array_unref (modes);
+}
+
+static void
+test_ws46_response_generic_2g3g4g (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_2G,
+ MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ };
+ const gchar *str = "+WS46: (12,22,25,28,29)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
+
+static void
+test_ws46_response_generic_2g3g (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_2G,
+ MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ };
+ const gchar *str = "+WS46: (12,22,25)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
+
+static void
+test_ws46_response_generic_2g3g_v2 (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_2G,
+ MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ };
+ const gchar *str = "+WS46: (12,22,29)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
+
+static void
+test_ws46_response_cinterion (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_2G,
+ MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ };
+ const gchar *str = "(12,22,25,28,29)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
+
+static void
+test_ws46_response_telit_le866 (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_4G,
+ };
+ const gchar *str = "(28)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
+
+static void
+test_ws46_response_range_1 (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ };
+ const gchar *str = "+WS46: (29-31)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
+
+static void
+test_ws46_response_range_2 (void)
+{
+ static const MMModemMode expected[] = {
+ MM_MODEM_MODE_2G,
+ MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_3G,
+ MM_MODEM_MODE_2G | MM_MODEM_MODE_4G,
+ MM_MODEM_MODE_3G | MM_MODEM_MODE_4G,
+ };
+ const gchar *str = "+WS46: (12,22,25,28-31)";
+
+ test_ws46_response (str, expected, G_N_ELEMENTS (expected));
+}
/*****************************************************************************/
/* Test CMGL responses */
@@ -79,7 +291,7 @@ test_cmgl_response_generic (void *f, gpointer d)
{
.index = 0,
.status = 1,
- .pdu = "07914306073011F00405812261F700003130916191314095C27"
+ .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27"
"4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F"
"77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731"
"0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B"
@@ -114,7 +326,7 @@ test_cmgl_response_generic_multiple (void *f, gpointer d)
{
.index = 0,
.status = 1,
- .pdu = "07914306073011F00405812261F700003130916191314095C27"
+ .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27"
"4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F"
"77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731"
"0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B"
@@ -123,7 +335,7 @@ test_cmgl_response_generic_multiple (void *f, gpointer d)
{
.index = 1,
.status = 1,
- .pdu = "07914306073011F00405812261F700003130916191314095C27"
+ .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27"
"4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F"
"77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731"
"0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B"
@@ -132,7 +344,7 @@ test_cmgl_response_generic_multiple (void *f, gpointer d)
{
.index = 2,
.status = 1,
- .pdu = "07914306073011F00405812261F700003130916191314095C27"
+ .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27"
"4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F"
"77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731"
"0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B"
@@ -153,7 +365,7 @@ test_cmgl_response_pantech (void *f, gpointer d)
{
.index = 17,
.status = 3,
- .pdu = "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
+ .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
}
};
@@ -173,22 +385,22 @@ test_cmgl_response_pantech_multiple (void *f, gpointer d)
{
.index = 17,
.status = 3,
- .pdu = "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
+ .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
},
{
.index = 15,
.status = 3,
- .pdu = "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
+ .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
},
{
.index = 13,
.status = 3,
- .pdu = "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
+ .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300001954747A0E4ACF41F27298CDCE83C6EF371B0402814020"
},
{
.index = 11,
.status = 3,
- .pdu = "079100F40D1101000F001000B917118336058F300"
+ .pdu = (gchar *) "079100F40D1101000F001000B917118336058F300"
}
};
@@ -196,11 +408,75 @@ test_cmgl_response_pantech_multiple (void *f, gpointer d)
}
/*****************************************************************************/
+/* Test CMGR responses */
+
+static void
+test_cmgr_response (const gchar *str,
+ const MM3gppPduInfo *expected)
+{
+ MM3gppPduInfo *info;
+ GError *error = NULL;
+
+ info = mm_3gpp_parse_cmgr_read_response (str, 0, &error);
+ g_assert_no_error (error);
+ g_assert (info != NULL);
+
+ /* Ignore index, it is not included in CMGR response */
+ g_assert_cmpint (info->status, ==, expected->status);
+ g_assert_cmpstr (info->pdu, ==, expected->pdu);
+
+ mm_3gpp_pdu_info_free (info);
+}
+
+static void
+test_cmgr_response_generic (void *f, gpointer d)
+{
+ const gchar *str =
+ "+CMGR: 1,,147 07914306073011F00405812261F700003130916191314095C27"
+ "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F"
+ "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731"
+ "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B"
+ "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07";
+
+ const MM3gppPduInfo expected = {
+ .index = 0,
+ .status = 1,
+ .pdu = (gchar *) "07914306073011F00405812261F700003130916191314095C27"
+ "4D96D2FBBD3E437280CB2BEC961F3DB5D76818EF2F0381D9E83E06F39A8CC2E9FD372F"
+ "77BEE0249CBE37A594E0E83E2F532085E2F93CB73D0B93CA7A7DFEEB01C447F93DF731"
+ "0BD3E07CDCB727B7A9C7ECF41E432C8FC96B7C32079189E26874179D0F8DD7E93C3A0B"
+ "21B246AA641D637396C7EBBCB22D0FD7E77B5D376B3AB3C07"
+ };
+
+ test_cmgr_response (str, &expected);
+}
+
+/* Telit HE910 places empty quotation marks in the <alpha> field and a CR+LF
+ * before the PDU */
+static void
+test_cmgr_response_telit (void *f, gpointer d)
+{
+ const gchar *str =
+ "+CMGR: 0,\"\",50\r\n07916163838428F9040B916121021021F7000051905141642"
+ "20A23C4B0BCFD5E8740C4B0BCFD5E83C26E3248196687C9A0301D440DBBC3677918";
+
+ const MM3gppPduInfo expected = {
+ .index = 0,
+ .status = 0,
+ .pdu = (gchar *) "07916163838428F9040B916121021021F7000051905141642"
+ "20A23C4B0BCFD5E8740C4B0BCFD5E83C26E3248196687C9A0301D440DBBC3677918"
+ };
+
+ test_cmgr_response (str, &expected);
+}
+
+/*****************************************************************************/
/* Test COPS responses */
static void
test_cops_results (const gchar *desc,
const gchar *reply,
+ MMModemCharset cur_charset,
MM3gppNetworkInfo *expected_results,
guint32 expected_results_len)
{
@@ -208,9 +484,9 @@ test_cops_results (const gchar *desc,
GError *error = NULL;
GList *results;
- trace ("\nTesting %s +COPS response...\n", desc);
+ g_debug ("Testing %s +COPS response...", desc);
- results = mm_3gpp_parse_cops_test_response (reply, &error);
+ results = mm_3gpp_parse_cops_test_response (reply, cur_charset, NULL, &error);
g_assert (results);
g_assert_no_error (error);
g_assert_cmpuint (g_list_length (results), ==, expected_results_len);
@@ -219,6 +495,7 @@ test_cops_results (const gchar *desc,
MM3gppNetworkInfo *info = l->data;
gboolean found = FALSE;
guint i;
+ gchar *access_tech_str;
for (i = 0; !found && i < expected_results_len; i++) {
MM3gppNetworkInfo *expected;
@@ -239,11 +516,18 @@ test_cops_results (const gchar *desc,
g_assert_cmpstr (info->operator_short, ==, expected->operator_short);
else
g_assert (info->operator_short == NULL);
-
- g_debug ("info: %s, expected: %s", info->operator_code, expected->operator_code);
}
}
+ access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech);
+ g_debug ("info: [%s,%s,%s,%s] %sfound in expected results",
+ info->operator_long ? info->operator_long : "",
+ info->operator_short ? info->operator_short : "",
+ info->operator_code,
+ access_tech_str,
+ found ? "" : "not ");
+ g_free (access_tech_str);
+
g_assert (found == TRUE);
}
@@ -255,12 +539,12 @@ test_cops_response_tm506 (void *f, gpointer d)
{
const gchar *reply = "+COPS: (2,\"\",\"T-Mobile\",\"31026\",0),(2,\"T - Mobile\",\"T - Mobile\",\"310260\"),2),(1,\"AT&T\",\"AT&T\",\"310410\"),0)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, "T-Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T - Mobile", "T - Mobile", "310260", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", (gchar *) "T - Mobile", (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
};
- test_cops_results ("TM-506", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("TM-506", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -268,11 +552,11 @@ test_cops_response_gt3gplus (void *f, gpointer d)
{
const char *reply = "+COPS: (1,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"Cingular\",\"Cingular\",\"310410\",0),,(0, 1, 3),(0-2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "T-Mobile US", "TMO US", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "Cingular", "Cingular", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", (gchar *) "Cingular", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("GlobeTrotter 3G+ (nozomi)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -280,12 +564,12 @@ test_cops_response_ac881 (void *f, gpointer d)
{
const char *reply = "+COPS: (1,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "T-Mobile", "TMO", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Sierra AirCard 881", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Sierra AirCard 881", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -293,12 +577,12 @@ test_cops_response_gtmax36 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0, 1,)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile US", "TMO US", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Option GlobeTrotter MAX 3.6", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -306,12 +590,12 @@ test_cops_response_ac860 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"Cingular\",\"Cinglr\",\"310410\",2),(1,\"Cingular\",\"Cinglr\",\"310410\",0),,)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "TMO", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "Cingular", "Cinglr", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "Cingular", "Cinglr", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", (gchar *) "Cinglr", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", (gchar *) "Cinglr", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Sierra AirCard 860", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Sierra AirCard 860", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -319,12 +603,12 @@ test_cops_response_gtm378 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0, 1, 3),(0-2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "T-Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Option GTM378", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Option GTM378", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -332,35 +616,37 @@ test_cops_response_motoc (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"\",\"310260\"),(0,\"Cingular Wireless\",\"\",\"310410\")";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", NULL, "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN, "Cingular Wireless", NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", NULL, (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN, (gchar *) "Cingular Wireless", NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("BUSlink SCWi275u (Motorola C-series)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
test_cops_response_mf627a (void *f, gpointer d)
{
- const char *reply = "+COPS: (2,\"AT&T@\",\"AT&TD\",\"310410\",0),(3,\"Voicestream Wireless Corporation\",\"VSTREAM\",\"31026\",0),";
+ /* The '@' in this string is ASCII 0x40, and 0x40 is a valid GSM-7 char: '¡' (which is 0xc2,0xa1 in UTF-8) */
+ const char *reply = "+COPS: (2,\"AT&T@\",\"AT&TD\",\"310410\",0),(3,\"Vstream Wireless\",\"VSTREAM\",\"31026\",0),";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "AT&T@", "AT&TD", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, "Voicestream Wireless Corporation", "VSTREAM", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&T¡", (gchar *) "AT&TD", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, (gchar *) "Vstream Wireless", (gchar *) "VSTREAM", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("ZTE MF627 (A)", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("ZTE MF627 (A)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
test_cops_response_mf627b (void *f, gpointer d)
{
+ /* The '@' in this string is ASCII 0x40, and 0x40 is a valid GSM-7 char: '¡' (which is 0xc2,0xa1 in UTF-8) */
const char *reply = "+COPS: (2,\"AT&Tp\",\"AT&T@\",\"310410\",0),(3,\"\",\"\",\"31026\",0),";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "AT&Tp", "AT&T@", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&Tp", (gchar *) "AT&T¡", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("ZTE MF627 (B)", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("ZTE MF627 (B)", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -368,11 +654,11 @@ test_cops_response_e160g (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "TMO", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Huawei E160G", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Huawei E160G", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -380,12 +666,12 @@ test_cops_response_mercury (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"\",\"\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),(1,\"T-Mobile\",\"TMO\",\"31026\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "T-Mobile", "TMO", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Sierra AT&T USBConnect Mercury", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -393,12 +679,12 @@ test_cops_response_quicksilver (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"AT&T\",\"\",\"310410\",0),(2,\"\",\"\",\"3104100\",2),(1,\"AT&T\",\"\",\"310260\",0),,(0-4),(0-2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "AT&T", NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, NULL, "3104100", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", NULL, "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "AT&T", NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, NULL, NULL, (gchar *) "3104100", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", NULL, (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Option AT&T Quicksilver", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Option AT&T Quicksilver", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -406,11 +692,11 @@ test_cops_response_icon225 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0, 1, 3),(0-2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile US", "TMO US", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Option iCON 225", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Option iCON 225", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -418,13 +704,13 @@ test_cops_response_icon452 (void *f, gpointer d)
{
const char *reply = "+COPS: (1,\"T-Mobile US\",\"TMO US\",\"31026\",0),(2,\"T-Mobile\",\"T-Mobile\",\"310260\",2),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "T-Mobile US", "TMO US", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "T-Mobile", "310260", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
};
- test_cops_results ("Option iCON 452", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Option iCON 452", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -432,12 +718,12 @@ test_cops_response_f3507g (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T - Mobile\",\"T - Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T - Mobile", "T - Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", (gchar *) "T - Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS }
};
- test_cops_results ("Ericsson F3507g", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Ericsson F3507g", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -445,12 +731,12 @@ test_cops_response_f3607gw (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T - Mobile\",\"T - Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\"),2),(1,\"AT&T\",\"AT&T\",\"310410\"),0)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T - Mobile", "T - Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", (gchar *) "T - Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
};
- test_cops_results ("Ericsson F3607gw", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Ericsson F3607gw", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -458,12 +744,12 @@ test_cops_response_mc8775 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "T-Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM }
};
- test_cops_results ("Sierra MC8775", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Sierra MC8775", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -471,12 +757,12 @@ test_cops_response_n80 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T - Mobile\",,\"31026\"),(1,\"Einstein PCS\",,\"31064\"),(1,\"Cingular\",,\"31041\"),,(0,1,3),(0,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T - Mobile", NULL, "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "Einstein PCS", NULL, "31064", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "Cingular", NULL, "31041", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Einstein PCS", NULL, (gchar *) "31064", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Cingular", NULL, (gchar *) "31041", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Nokia N80", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Nokia N80", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -484,11 +770,11 @@ test_cops_response_e1550 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"TMO\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "TMO", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Huawei E1550", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Huawei E1550", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -496,11 +782,11 @@ test_cops_response_mf622 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"\",\"\",\"310410\",0),";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "T-Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("ZTE MF622", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("ZTE MF622", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -508,12 +794,12 @@ test_cops_response_e226 (void *f, gpointer d)
{
const char *reply = "+COPS: (1,\"\",\"\",\"31026\",0),(1,\"\",\"\",\"310410\",2),(1,\"\",\"\",\"310410\",0),,(0,1,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, NULL, NULL, (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Huawei E226", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Huawei E226", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -521,12 +807,12 @@ test_cops_response_xu870 (void *f, gpointer d)
{
const char *reply = "+COPS: (0,\"AT&T MicroCell\",\"AT&T MicroCell\",\"310410\",2)\r\n+COPS: (1,\"AT&T MicroCell\",\"AT&T MicroCell\",\"310410\",0)\r\n+COPS: (1,\"T-Mobile\",\"TMO\",\"31026\",0)\r\n";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN, "AT&T MicroCell", "AT&T MicroCell", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T MicroCell", "AT&T MicroCell", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "T-Mobile", "TMO", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_UNKNOWN, (gchar *) "AT&T MicroCell", (gchar *) "AT&T MicroCell", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T MicroCell", (gchar *) "AT&T MicroCell", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile", (gchar *) "TMO", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Novatel XU870", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Novatel XU870", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -534,12 +820,12 @@ test_cops_response_gtultraexpress (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile US\",\"TMO US\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile US", "TMO US", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile US", (gchar *) "TMO US", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Option GlobeTrotter Ultra Express", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -547,11 +833,11 @@ test_cops_response_n2720 (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T - Mobile\",,\"31026\",0),\r\n(1,\"AT&T\",,\"310410\",0),,(0,1,3),(0,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T - Mobile", NULL, "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", NULL, "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T - Mobile", NULL, (gchar *)"31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", NULL, (gchar *)"310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Nokia 2720", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Nokia 2720", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -559,12 +845,12 @@ test_cops_response_gobi (void *f, gpointer d)
{
const char *reply = "+COPS: (2,\"T-Mobile\",\"T-Mobile\",\"31026\",0),(1,\"AT&T\",\"AT&T\",\"310410\",2),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "T-Mobile", "T-Mobile", "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "T-Mobile", (gchar *) "T-Mobile", (gchar *) "31026", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Qualcomm Gobi", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Qualcomm Gobi", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -576,16 +862,16 @@ test_cops_response_sek600i (void *f, gpointer d)
*/
const char *reply = "+COPS: (2,\"blau\",\"\",\"26203\"),(2,\"blau\",\"\",\"26203\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26202\"),(3,\"\",\"\",\"26207\"),(3,\"\",\"\",\"26201\"),(3,\"\",\"\",\"26207\")";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "blau", NULL, "26203", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, "blau", NULL, "26203", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, "26201", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, "26202", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, "26207", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, "26201", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, "26207", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "blau", NULL, (gchar *) "26203", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "blau", NULL, (gchar *) "26203", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26201", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26202", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26207", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26201", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_FORBIDDEN, NULL, NULL, (gchar *) "26207", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Sony-Ericsson K600i", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Sony-Ericsson K600i", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -594,11 +880,35 @@ test_cops_response_samsung_z810 (void *f, gpointer d)
/* Ensure commas within quotes don't trip up the parser */
const char *reply = "+COPS: (1,\"T-Mobile USA, In\",\"T-Mobile\",\"310260\",0),(1,\"AT&T\",\"AT&T\",\"310410\",0),,(0,1,2,3,4),(0,1,2)";
static MM3gppNetworkInfo expected[] = {
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "T-Mobile USA, In", "T-Mobile", "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
- { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, "AT&T", "AT&T", "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "T-Mobile USA, In", (gchar *) "T-Mobile", (gchar *) "310260", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "AT&T", (gchar *) "AT&T", (gchar *) "310410", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
};
- test_cops_results ("Samsung Z810", reply, &expected[0], G_N_ELEMENTS (expected));
+ test_cops_results ("Samsung Z810", reply, MM_MODEM_CHARSET_GSM, &expected[0], G_N_ELEMENTS (expected));
+}
+
+static void
+test_cops_response_ublox_lara (void *f, gpointer d)
+{
+ /* strings in UCS2 */
+ const char *reply =
+ "+COPS: "
+ "(2,\"004D006F007600690073007400610072\",\"004D006F007600690073007400610072\",\"00320031003400300037\",7),"
+ "(1,\"0059004F00490047004F\",\"0059004F00490047004F\",\"00320031003400300034\",7),"
+ "(1,\"0076006F006400610066006F006E0065002000450053\",\"0076006F00640061002000450053\",\"00320031003400300031\",7),"
+ "(1,\"004F00720061006E00670065002000530050\",\"00450053005000520054\",\"00320031003400300033\",0),"
+ "(1,\"0076006F006400610066006F006E0065002000450053\",\"0076006F00640061002000450053\",\"00320031003400300031\",0),"
+ "(1,\"004F00720061006E00670065002000530050\",\"00450053005000520054\",\"00320031003400300033\",7)";
+ static MM3gppNetworkInfo expected[] = {
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_CURRENT, (gchar *) "Movistar", (gchar *) "Movistar", (gchar *) "21407", MM_MODEM_ACCESS_TECHNOLOGY_LTE },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "YOIGO", (gchar *) "YOIGO", (gchar *) "21404", MM_MODEM_ACCESS_TECHNOLOGY_LTE },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "vodafone ES", (gchar *) "voda ES", (gchar *) "21401", MM_MODEM_ACCESS_TECHNOLOGY_LTE },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Orange SP", (gchar *) "ESPRT", (gchar *) "21403", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "vodafone ES", (gchar *) "voda ES", (gchar *) "21401", MM_MODEM_ACCESS_TECHNOLOGY_GSM },
+ { MM_MODEM_3GPP_NETWORK_AVAILABILITY_AVAILABLE, (gchar *) "Orange SP", (gchar *) "ESPRT", (gchar *) "21403", MM_MODEM_ACCESS_TECHNOLOGY_LTE },
+ };
+
+ test_cops_results ("u-blox LARA", reply, MM_MODEM_CHARSET_UCS2, &expected[0], G_N_ELEMENTS (expected));
}
static void
@@ -608,7 +918,7 @@ test_cops_response_gsm_invalid (void *f, gpointer d)
GList *results;
GError *error = NULL;
- results = mm_3gpp_parse_cops_test_response (reply, &error);
+ results = mm_3gpp_parse_cops_test_response (reply, MM_MODEM_CHARSET_GSM, NULL, &error);
g_assert (results == NULL);
g_assert_no_error (error);
}
@@ -620,12 +930,137 @@ test_cops_response_umts_invalid (void *f, gpointer d)
GList *results;
GError *error = NULL;
- results = mm_3gpp_parse_cops_test_response (reply, &error);
+ results = mm_3gpp_parse_cops_test_response (reply, MM_MODEM_CHARSET_GSM, NULL, &error);
g_assert (results == NULL);
g_assert_no_error (error);
}
/*****************************************************************************/
+/* Test COPS? responses */
+
+typedef struct {
+ const gchar *str;
+ guint mode;
+ guint format;
+ const gchar *operator;
+ MMModemAccessTechnology act;
+} CopsQueryData;
+
+static void
+test_cops_query_data (const CopsQueryData *item)
+{
+ gboolean result;
+ GError *error = NULL;
+ guint mode = G_MAXUINT;
+ guint format = G_MAXUINT;
+ gchar *operator = NULL;
+ MMModemAccessTechnology act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+
+ result = mm_3gpp_parse_cops_read_response (item->str,
+ &mode,
+ &format,
+ &operator,
+ &act,
+ &error);
+ g_assert_no_error (error);
+ g_assert (result);
+ g_assert_cmpuint (mode, ==, item->mode);
+ g_assert_cmpuint (format, ==, item->format);
+ g_assert_cmpstr (operator, ==, item->operator);
+ g_assert_cmpuint (act, ==, item->act);
+
+ g_free (operator);
+}
+
+static const CopsQueryData cops_query_data[] = {
+ {
+ .str = "+COPS: 1,0,\"CHINA MOBILE\"",
+ .mode = 1,
+ .format = 0,
+ .operator = "CHINA MOBILE",
+ .act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN
+ },
+ {
+ .str = "+COPS: 1,0,\"CHINA MOBILE\",7",
+ .mode = 1,
+ .format = 0,
+ .operator = "CHINA MOBILE",
+ .act = MM_MODEM_ACCESS_TECHNOLOGY_LTE
+ },
+ {
+ .str = "+COPS: 1,2,\"46000\"",
+ .mode = 1,
+ .format = 2,
+ .operator = "46000",
+ .act = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN
+ },
+ {
+ .str = "+COPS: 1,2,\"46000\",7",
+ .mode = 1,
+ .format = 2,
+ .operator = "46000",
+ .act = MM_MODEM_ACCESS_TECHNOLOGY_LTE
+ },
+};
+
+static void
+test_cops_query (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cops_query_data); i++)
+ test_cops_query_data (&cops_query_data[i]);
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ const gchar *input;
+ MMModemCharset charset;
+ const gchar *normalized;
+} NormalizeOperatorTest;
+
+static const NormalizeOperatorTest normalize_operator_tests[] = {
+ /* charset unknown */
+ { "Verizon", MM_MODEM_CHARSET_UNKNOWN, "Verizon" },
+ { "311480", MM_MODEM_CHARSET_UNKNOWN, "311480" },
+ /* charset configured as IRA (ASCII) */
+ { "Verizon", MM_MODEM_CHARSET_IRA, "Verizon" },
+ { "311480", MM_MODEM_CHARSET_IRA, "311480" },
+ /* charset configured as GSM7 */
+ { "Verizon", MM_MODEM_CHARSET_GSM, "Verizon" },
+ { "311480", MM_MODEM_CHARSET_GSM, "311480" },
+ /* charset configured as UCS2 */
+ { "0056006500720069007A006F006E", MM_MODEM_CHARSET_UCS2, "Verizon" },
+ { "003300310031003400380030", MM_MODEM_CHARSET_UCS2, "311480" },
+ { "Verizon", MM_MODEM_CHARSET_UCS2, "Verizon" },
+ { "311480", MM_MODEM_CHARSET_UCS2, "311480" },
+};
+
+static void
+common_test_normalize_operator (const NormalizeOperatorTest *t)
+{
+ gchar *str;
+
+ str = g_strdup (t->input);
+ mm_3gpp_normalize_operator (&str, t->charset, NULL);
+ if (!t->normalized)
+ g_assert (!str);
+ else
+ g_assert_cmpstr (str, ==, t->normalized);
+ g_free (str);
+}
+
+static void
+test_normalize_operator (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (normalize_operator_tests); i++)
+ common_test_normalize_operator (&normalize_operator_tests[i]);
+}
+
+/*****************************************************************************/
/* Test CREG/CGREG responses and unsolicited messages */
typedef struct {
@@ -661,6 +1096,7 @@ typedef struct {
guint regex_num;
gboolean cgreg;
gboolean cereg;
+ gboolean c5greg;
} CregResult;
static void
@@ -670,13 +1106,13 @@ test_creg_match (const char *test,
RegTestData *data,
const CregResult *result)
{
- int i;
+ guint i;
GMatchInfo *info = NULL;
MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN;
MMModemAccessTechnology access_tech = MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
gulong lac = 0, ci = 0;
GError *error = NULL;
- gboolean success, cgreg = FALSE, cereg = FALSE;
+ gboolean success, cgreg = FALSE, cereg = FALSE, c5greg = FALSE;
guint regex_num = 0;
GPtrArray *array;
@@ -685,44 +1121,43 @@ test_creg_match (const char *test,
g_assert (data);
g_assert (result);
- trace ("\nTesting '%s' +C%sREG %s response...\n",
- test,
- result->cgreg ? "G" : "",
- solicited ? "solicited" : "unsolicited");
+ g_debug ("Testing '%s' +C%sREG %s response...",
+ test,
+ result->cgreg ? "G" : "",
+ solicited ? "solicited" : "unsolicited");
array = solicited ? data->solicited_creg : data->unsolicited_creg;
for (i = 0; i < array->len; i++) {
GRegex *r = g_ptr_array_index (array, i);
if (g_regex_match (r, reply, 0, &info)) {
- trace (" matched with %d\n", i);
- regex_num = i + 1;
+ g_debug (" matched with %d", i);
+ regex_num = i;
break;
}
g_match_info_free (info);
info = NULL;
}
- trace (" regex_num (%u) == result->regex_num (%u)\n",
- regex_num,
- result->regex_num);
+ g_debug (" regex_num (%u) == result->regex_num (%u)",
+ regex_num,
+ result->regex_num);
g_assert (info != NULL);
g_assert_cmpuint (regex_num, ==, result->regex_num);
- success = mm_3gpp_parse_creg_response (info, &state, &lac, &ci, &access_tech, &cgreg, &cereg, &error);
+ success = mm_3gpp_parse_creg_response (info, NULL, &state, &lac, &ci, &access_tech, &cgreg, &cereg, &c5greg, &error);
+
g_match_info_free (info);
g_assert (success);
g_assert_no_error (error);
g_assert_cmpuint (state, ==, result->state);
- g_assert (lac == result->lac);
- g_assert (ci == result->ci);
-
- trace (" access_tech (%d) == result->act (%d)\n",
- access_tech, result->act);
+ g_assert_cmpuint (lac, ==, result->lac);
+ g_assert_cmpuint (ci, ==, result->ci);
g_assert_cmpuint (access_tech, ==, result->act);
g_assert_cmpuint (cgreg, ==, result->cgreg);
g_assert_cmpuint (cereg, ==, result->cereg);
+ g_assert_cmpuint (c5greg, ==, result->c5greg);
}
static void
@@ -730,7 +1165,7 @@ test_creg1_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CREG: 1,3";
- const CregResult result = { 3, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 2, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, FALSE, FALSE };
test_creg_match ("CREG=1", TRUE, reply, data, &result);
}
@@ -740,7 +1175,7 @@ test_creg1_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 3\r\n";
- const CregResult result = { 3, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 1, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, FALSE, FALSE };
test_creg_match ("CREG=1", FALSE, reply, data, &result);
}
@@ -749,8 +1184,8 @@ static void
test_creg2_mercury_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
- const char *reply = "+CREG: 0,1,84CD,00D30173";
- const CregResult result = { 1, 0x84cd, 0xd30173, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 4, FALSE, FALSE };
+ const char *reply = "+CREG: 1,1,84CD,00D30173";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x84cd, 0xd30173, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("Sierra Mercury CREG=2", TRUE, reply, data, &result);
}
@@ -760,7 +1195,7 @@ test_creg2_mercury_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 1,84CD,00D30156\r\n";
- const CregResult result = { 1, 0x84cd, 0xd30156, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 3, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x84cd, 0xd30156, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE };
test_creg_match ("Sierra Mercury CREG=2", FALSE, reply, data, &result);
}
@@ -770,7 +1205,7 @@ test_creg2_sek850i_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CREG: 2,1,\"CE00\",\"01CEAD8F\"";
- const CregResult result = { 1, 0xce00, 0x01cead8f, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 4, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0xce00, 0x01cead8f, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("Sony Ericsson K850i CREG=2", TRUE, reply, data, &result);
}
@@ -780,7 +1215,7 @@ test_creg2_sek850i_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 1,\"CE00\",\"00005449\"\r\n";
- const CregResult result = { 1, 0xce00, 0x5449, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 3, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0xce00, 0x5449, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE };
test_creg_match ("Sony Ericsson K850i CREG=2", FALSE, reply, data, &result);
}
@@ -790,7 +1225,7 @@ test_creg2_e160g_solicited_unregistered (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CREG: 2,0,00,0";
- const CregResult result = { 0, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 4, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("Huawei E160G unregistered CREG=2", TRUE, reply, data, &result);
}
@@ -800,7 +1235,7 @@ test_creg2_e160g_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CREG: 2,1,8BE3,2BAF";
- const CregResult result = { 1, 0x8be3, 0x2baf, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 4, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8be3, 0x2baf, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("Huawei E160G CREG=2", TRUE, reply, data, &result);
}
@@ -809,8 +1244,8 @@ static void
test_creg2_e160g_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
- const char *reply = "\r\n+CREG: 2,8BE3,2BAF\r\n";
- const CregResult result = { 2, 0x8be3, 0x2baf, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 3, FALSE, FALSE };
+ const char *reply = "\r\n+CREG: 1,8BE3,2BAF\r\n";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8be3, 0x2baf, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE };
test_creg_match ("Huawei E160G CREG=2", FALSE, reply, data, &result);
}
@@ -820,7 +1255,7 @@ test_creg2_tm506_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CREG: 2,1,\"8BE3\",\"00002BAF\"";
- const CregResult result = { 1, 0x8BE3, 0x2BAF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 4, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8BE3, 0x2BAF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
/* Test leading zeros in the CI */
test_creg_match ("Sony Ericsson TM-506 CREG=2", TRUE, reply, data, &result);
@@ -831,7 +1266,7 @@ test_creg2_xu870_unsolicited_unregistered (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 2,,\r\n";
- const CregResult result = { 2, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 3, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, FALSE, FALSE, FALSE };
test_creg_match ("Novatel XU870 unregistered CREG=2", FALSE, reply, data, &result);
}
@@ -841,7 +1276,7 @@ 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, 5, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x18D8, 0xFFFF, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE, FALSE };
test_creg_match ("Iridium, CREG=2", TRUE, reply, data, &result);
}
@@ -851,7 +1286,7 @@ 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 };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("solicited CREG=2 with no leading zeros in integer fields", TRUE, reply, data, &result);
}
@@ -861,7 +1296,7 @@ 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 };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE, FALSE };
test_creg_match ("solicited CREG=2 with leading zeros in integer fields", TRUE, reply, data, &result);
}
@@ -871,7 +1306,7 @@ 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 };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 5, FALSE, FALSE, FALSE };
test_creg_match ("unsolicited CREG=2 with no leading zeros in integer fields", FALSE, reply, data, &result);
}
@@ -881,17 +1316,37 @@ 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 };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0001, 0x0010, MM_MODEM_ACCESS_TECHNOLOGY_GSM, 6, FALSE, FALSE, FALSE };
test_creg_match ("unsolicited CREG=2 with leading zeros in integer fields", FALSE, reply, data, &result);
}
static void
+test_creg2_ublox_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const gchar *reply = "\r\n+CREG: 2,6,\"8B37\",\"0A265185\",7\r\n";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY, 0x8B37, 0x0A265185, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 7, FALSE, FALSE, FALSE };
+
+ test_creg_match ("Ublox Toby-L2 solicited while on LTE", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_ublox_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const gchar *reply = "\r\n+CREG: 6,\"8B37\",\"0A265185\",7\r\n";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY, 0x8B37, 0x0A265185, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, FALSE, FALSE };
+
+ test_creg_match ("Ublox Toby-L2 unsolicited while on LTE", FALSE, reply, data, &result);
+}
+
+static void
test_cgreg1_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CGREG: 1,3";
- const CregResult result = { 3, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 2, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, TRUE, FALSE, FALSE };
test_creg_match ("CGREG=1", TRUE, reply, data, &result);
}
@@ -901,7 +1356,7 @@ test_cgreg1_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CGREG: 3\r\n";
- const CregResult result = { 3, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 1, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, TRUE, FALSE, FALSE };
test_creg_match ("CGREG=1", FALSE, reply, data, &result);
}
@@ -911,7 +1366,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, 8, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 7, TRUE, FALSE, FALSE };
test_creg_match ("Ericsson F3607gw CGREG=2", TRUE, reply, data, &result);
}
@@ -921,7 +1376,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, 6, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x8BE3, 0x2B5D, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 5, TRUE, FALSE, FALSE };
test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result);
}
@@ -931,7 +1386,7 @@ test_creg2_md400_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 2,5,\"0502\",\"0404736D\"\r\n";
- const CregResult result = { 5, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 4, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("Sony-Ericsson MD400 CREG=2", FALSE, reply, data, &result);
}
@@ -941,7 +1396,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, 6, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING, 0x0502, 0x0404736D, MM_MODEM_ACCESS_TECHNOLOGY_UMTS, 5, TRUE, FALSE, FALSE };
test_creg_match ("Sony-Ericsson MD400 CGREG=2", FALSE, reply, data, &result);
}
@@ -951,7 +1406,7 @@ test_creg_cgreg_multi_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 5\r\n\r\n+CGREG: 0\r\n";
- const CregResult result = { 5, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, FALSE, FALSE };
test_creg_match ("Multi CREG/CGREG", FALSE, reply, data, &result);
}
@@ -961,7 +1416,7 @@ test_creg_cgreg_multi2_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CGREG: 0\r\n\r\n+CREG: 5\r\n";
- const CregResult result = { 0, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, TRUE, FALSE, FALSE };
test_creg_match ("Multi CREG/CGREG #2", FALSE, reply, data, &result);
}
@@ -971,7 +1426,7 @@ test_cgreg2_x220_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CGREG: 2,1, 81ED, 1A9CEB\r\n";
- const CregResult result = { 1, 0x81ED, 0x1A9CEB, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x81ED, 0x1A9CEB, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, TRUE, FALSE, FALSE };
/* Tests random spaces in response */
test_creg_match ("Alcatel One-Touch X220D CGREG=2", FALSE, reply, data, &result);
@@ -982,7 +1437,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, 9, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x000B, 0x2816, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 8, FALSE, FALSE, FALSE };
test_creg_match ("Samsung Wave S8500 CREG=2", FALSE, reply, data, &result);
}
@@ -992,7 +1447,7 @@ test_creg2_gobi_weird_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CREG: 2,1, 0 5, 2715\r\n";
- const CregResult result = { 1, 0x0000, 0x2715, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 4, FALSE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0000, 0x2715, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, FALSE, FALSE, FALSE };
test_creg_match ("Qualcomm Gobi 1000 CREG=2", TRUE, reply, data, &result);
}
@@ -1002,7 +1457,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, 10, TRUE, FALSE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1422, 0x0142, MM_MODEM_ACCESS_TECHNOLOGY_EDGE, 9, TRUE, FALSE, FALSE };
test_creg_match ("CGREG=2 with RAC", FALSE, reply, data, &result);
}
@@ -1012,7 +1467,7 @@ test_cereg1_solicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "+CEREG: 1,3";
- const CregResult result = { 3, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 2, FALSE, TRUE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, TRUE, FALSE };
test_creg_match ("CEREG=1", TRUE, reply, data, &result);
}
@@ -1022,7 +1477,7 @@ test_cereg1_unsolicited (void *f, gpointer d)
{
RegTestData *data = (RegTestData *) d;
const char *reply = "\r\n+CEREG: 3\r\n";
- const CregResult result = { 3, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN , 1, FALSE, TRUE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, TRUE, FALSE };
test_creg_match ("CEREG=1", FALSE, reply, data, &result);
}
@@ -1032,7 +1487,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, 8, FALSE, TRUE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 7, FALSE, TRUE, FALSE };
test_creg_match ("CEREG=2", TRUE, reply, data, &result);
}
@@ -1042,7 +1497,7 @@ 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, 6, FALSE, TRUE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, TRUE, FALSE };
test_creg_match ("CEREG=2", FALSE, reply, data, &result);
}
@@ -1052,7 +1507,7 @@ 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 };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, 0x0001, 0x00000100, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 7, FALSE, TRUE, FALSE };
test_creg_match ("Altair LTE CEREG=2", FALSE, reply, data, &result);
}
@@ -1062,7 +1517,7 @@ 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 };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING, 0x0001, 0x00000100, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 5, FALSE, TRUE, FALSE };
test_creg_match ("Altair LTE CEREG=2", FALSE, reply, data, &result);
}
@@ -1072,7 +1527,7 @@ 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, 12, FALSE, TRUE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 11, FALSE, TRUE, FALSE };
test_creg_match ("Novatel LTE E362 CEREG=2", TRUE, reply, data, &result);
}
@@ -1082,11 +1537,71 @@ 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, 11, FALSE, TRUE };
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_LTE, 10, FALSE, TRUE, FALSE };
test_creg_match ("Novatel LTE E362 CEREG=2", FALSE, reply, data, &result);
}
+static void
+test_cgreg2_thuraya_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "+CGREG: 2, 1, \"0426\", \"F00F\"";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0426, 0xF00F, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 3, TRUE, FALSE, FALSE };
+
+ test_creg_match ("Thuraya solicited CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg2_thuraya_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+CGREG: 1, \"0426\", \"F00F\"\r\n";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x0426, 0xF00F, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 2, TRUE, FALSE, FALSE };
+
+ test_creg_match ("Thuraya unsolicited CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_c5greg1_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "+C5GREG: 1,3";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 1, FALSE, FALSE, TRUE };
+
+ test_creg_match ("C5GREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_c5greg1_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+C5GREG: 3\r\n";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_DENIED, 0, 0, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, 0, FALSE, FALSE, TRUE };
+
+ test_creg_match ("C5GREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_c5greg2_solicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "+C5GREG: 2,1,1F00,79D903,11,6,ABCDEF";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_5GNR, 13, FALSE, FALSE, TRUE };
+
+ test_creg_match ("C5GREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_c5greg2_unsolicited (void *f, gpointer d)
+{
+ RegTestData *data = (RegTestData *) d;
+ const char *reply = "\r\n+C5GREG: 1,1F00,79D903,11,6,ABCDEF\r\n";
+ const CregResult result = { MM_MODEM_3GPP_REGISTRATION_STATE_HOME, 0x1F00, 0x79D903, MM_MODEM_ACCESS_TECHNOLOGY_5GNR, 12, FALSE, FALSE, TRUE };
+
+ test_creg_match ("C5GREG=2", FALSE, reply, data, &result);
+}
+
/*****************************************************************************/
/* Test CSCS responses */
@@ -1155,8 +1670,8 @@ test_cscs_blackberry_support_response (void *f, gpointer d)
/* Test Device Identifier builders */
typedef struct {
- char *devid;
- char *desc;
+ const char *devid;
+ const char *desc;
guint vid;
guint pid;
const char *ati;
@@ -1433,9 +1948,10 @@ test_devid_item (void *f, gpointer d)
DevidItem *item = (DevidItem *) d;
char *devid;
- trace ("%s... ", item->desc);
+ g_debug ("%s... ", item->desc);
devid = mm_create_device_identifier (item->vid,
item->pid,
+ NULL,
item->ati,
item->ati1,
item->gsn,
@@ -1450,6 +1966,80 @@ test_devid_item (void *f, gpointer d)
}
/*****************************************************************************/
+/* Test CMER test responses */
+
+static void
+test_cmer_response (const gchar *str,
+ MM3gppCmerMode expected_modes,
+ MM3gppCmerInd expected_inds)
+{
+ gboolean ret;
+ MM3gppCmerMode modes = MM_3GPP_CMER_MODE_NONE;
+ MM3gppCmerInd inds = MM_3GPP_CMER_IND_NONE;
+ GError *error = NULL;
+
+ ret = mm_3gpp_parse_cmer_test_response (str, NULL, &modes, &inds, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_assert_cmpuint (modes, ==, expected_modes);
+ g_assert_cmpuint (inds, ==, expected_inds);
+}
+
+static void
+test_cmer_response_cinterion_pls8 (void)
+{
+ static const gchar *str = "+CMER: (0-3),(0),(0),(0-1),(0-1)";
+ static const MM3gppCmerMode expected_modes = ( \
+ MM_3GPP_CMER_MODE_DISCARD_URCS | \
+ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED | \
+ MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED | \
+ MM_3GPP_CMER_MODE_FORWARD_URCS);
+ static const MM3gppCmerInd expected_inds = ( \
+ MM_3GPP_CMER_IND_DISABLE | \
+ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND);
+
+ test_cmer_response (str, expected_modes, expected_inds);
+}
+
+static void
+test_cmer_response_sierra_em7345 (void)
+{
+ static const gchar *str = "+CMER: 1,0,0,(0-1),0";
+ static const MM3gppCmerMode expected_modes = ( \
+ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED);
+ static const MM3gppCmerInd expected_inds = ( \
+ MM_3GPP_CMER_IND_DISABLE | \
+ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND);
+
+ test_cmer_response (str, expected_modes, expected_inds);
+}
+
+static void
+test_cmer_response_cinterion_ehs5 (void)
+{
+ static const gchar *str = "+CMER: (1,2),0,0,(0-1),0";
+ static const MM3gppCmerMode expected_modes = ( \
+ MM_3GPP_CMER_MODE_DISCARD_URCS_IF_LINK_RESERVED | \
+ MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED);
+ static const MM3gppCmerInd expected_inds = ( \
+ MM_3GPP_CMER_IND_DISABLE | \
+ MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND);
+
+ test_cmer_response (str, expected_modes, expected_inds);
+}
+
+static void
+test_cmer_request_cinterion_ehs5 (void)
+{
+ gchar *str;
+
+ str = mm_3gpp_build_cmer_set_request (MM_3GPP_CMER_MODE_BUFFER_URCS_IF_LINK_RESERVED, MM_3GPP_CMER_IND_ENABLE_NOT_CAUSED_BY_CIND);
+ g_assert_cmpstr (str, ==, "+CMER=2,0,0,1");
+ g_free (str);
+}
+
+/*****************************************************************************/
/* Test CIND responses */
typedef struct {
@@ -1468,7 +2058,7 @@ test_cind_results (const char *desc,
GError *error = NULL;
GHashTable *results;
- trace ("\nTesting %s +CIND response...\n", desc);
+ g_debug ("Testing %s +CIND response...", desc);
results = mm_3gpp_parse_cind_test_response (reply, &error);
g_assert (results);
@@ -1525,6 +2115,162 @@ test_cind_response_moto_v3m (void *f, gpointer d)
}
/*****************************************************************************/
+/* Test +CGEV indication parsing */
+
+typedef struct {
+ const gchar *str;
+ MM3gppCgev expected_type;
+ const gchar *expected_pdp_type;
+ const gchar *expected_pdp_addr;
+ guint expected_cid;
+ guint expected_parent_cid;
+ guint expected_event_type;
+} CgevIndicationTest;
+
+static const CgevIndicationTest cgev_indication_tests[] = {
+ { "+CGEV: REJECT IP, 123.123.123.123", MM_3GPP_CGEV_REJECT, "IP", "123.123.123.123", 0, 0, 0 },
+ { "REJECT IP, 123.123.123.123", MM_3GPP_CGEV_REJECT, "IP", "123.123.123.123", 0, 0, 0 },
+
+ { "+CGEV: NW REACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 0, 0, 0 },
+ { "NW REACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 0, 0, 0 },
+ { "+CGEV: NW REACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 1, 0, 0 },
+
+ { "NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 },
+ { "+CGEV: NW DEACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 },
+ { "NW DEACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 },
+ { "+CGEV: NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 },
+ { "NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 },
+
+ { "ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 },
+ { "+CGEV: ME DEACT IP, 123.123.123.123", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 },
+ { "ME DEACT IP, 123.123.123.123", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 },
+ { "+CGEV: ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 },
+ { "ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 },
+
+ { "ME PDN ACT 2", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "+CGEV: ME PDN ACT 2", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ /* with ,<reason>[,<cid_other>]] */
+ { "ME PDN ACT 2, 3", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "+CGEV: ME PDN ACT 2, 3", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "ME PDN ACT 2, 3, 4", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "+CGEV: ME PDN ACT 2, 3, 4", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+
+ { "ME PDN DEACT 2", MM_3GPP_CGEV_ME_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "+CGEV: ME PDN DEACT 2", MM_3GPP_CGEV_ME_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+
+ { "ME ACT 3, 2, 1", MM_3GPP_CGEV_ME_ACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+ { "+CGEV: ME ACT 3, 2, 1", MM_3GPP_CGEV_ME_ACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+
+ { "ME DEACT 3, 2, 1", MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+ { "+CGEV: ME DEACT 3, 2, 1", MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+
+ { "NW PDN ACT 2", MM_3GPP_CGEV_NW_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "+CGEV: NW PDN ACT 2", MM_3GPP_CGEV_NW_ACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+
+ { "NW PDN DEACT 2", MM_3GPP_CGEV_NW_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+ { "+CGEV: NW PDN DEACT 2", MM_3GPP_CGEV_NW_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 },
+
+ { "NW ACT 3, 2, 1", MM_3GPP_CGEV_NW_ACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+ { "+CGEV: NW ACT 3, 2, 1", MM_3GPP_CGEV_NW_ACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+
+ { "NW DEACT 3, 2, 1", MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+ { "+CGEV: NW DEACT 3, 2, 1", MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 },
+
+ { "+CGEV: NW DETACH", MM_3GPP_CGEV_NW_DETACH, NULL, NULL, 0, 0, 0 },
+ { "NW DETACH", MM_3GPP_CGEV_NW_DETACH, NULL, NULL, 0, 0, 0 },
+ { "+CGEV: ME DETACH", MM_3GPP_CGEV_ME_DETACH, NULL, NULL, 0, 0, 0 },
+ { "ME DETACH", MM_3GPP_CGEV_ME_DETACH, NULL, NULL, 0, 0, 0 },
+
+ { "+CGEV: NW CLASS A", MM_3GPP_CGEV_NW_CLASS, NULL, NULL, 0, 0, 0 },
+ { "NW CLASS A", MM_3GPP_CGEV_NW_CLASS, NULL, NULL, 0, 0, 0 },
+ { "+CGEV: ME CLASS A", MM_3GPP_CGEV_ME_CLASS, NULL, NULL, 0, 0, 0 },
+ { "ME CLASS A", MM_3GPP_CGEV_ME_CLASS, NULL, NULL, 0, 0, 0 },
+};
+
+static void
+test_cgev_indication (const CgevIndicationTest *t)
+{
+ guint i;
+ GError *error = NULL;
+ gboolean ret;
+
+ for (i = 0; i < G_N_ELEMENTS (cgev_indication_tests); i++) {
+ const CgevIndicationTest *test = &cgev_indication_tests[i];
+ MM3gppCgev type;
+
+ type = mm_3gpp_parse_cgev_indication_action (test->str);
+ g_assert_cmpuint (type, ==, test->expected_type);
+
+ g_debug ("[%u] type: %u", i, type);
+
+ switch (type) {
+ case MM_3GPP_CGEV_UNKNOWN:
+ case MM_3GPP_CGEV_NW_DETACH:
+ case MM_3GPP_CGEV_ME_DETACH:
+ break;
+ case MM_3GPP_CGEV_NW_ACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_ACT_PRIMARY:
+ case MM_3GPP_CGEV_NW_DEACT_PRIMARY:
+ case MM_3GPP_CGEV_ME_DEACT_PRIMARY: {
+ guint cid;
+
+ g_debug ("[%u] parsing as primary", i);
+ ret = mm_3gpp_parse_cgev_indication_primary (test->str, type, &cid, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpuint (cid, ==, test->expected_cid);
+ break;
+ }
+ case MM_3GPP_CGEV_NW_ACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_ACT_SECONDARY:
+ case MM_3GPP_CGEV_NW_DEACT_SECONDARY:
+ case MM_3GPP_CGEV_ME_DEACT_SECONDARY: {
+ guint p_cid;
+ guint cid;
+ guint event_type;
+
+ g_debug ("[%u] parsing as secondary", i);
+ ret = mm_3gpp_parse_cgev_indication_secondary (test->str, type, &p_cid, &cid, &event_type, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpuint (cid, ==, test->expected_cid);
+ g_assert_cmpuint (p_cid, ==, test->expected_parent_cid);
+ g_assert_cmpuint (event_type, ==, test->expected_event_type);
+ break;
+ }
+ case MM_3GPP_CGEV_NW_DEACT_PDP:
+ case MM_3GPP_CGEV_ME_DEACT_PDP:
+ case MM_3GPP_CGEV_REJECT:
+ case MM_3GPP_CGEV_NW_REACT: {
+ gchar *pdp_type;
+ gchar *pdp_addr;
+ guint cid;
+
+ g_debug ("[%u] parsing as pdp", i);
+ ret = mm_3gpp_parse_cgev_indication_pdp (test->str, type, &pdp_type, &pdp_addr, &cid, &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+ g_assert_cmpstr (pdp_type, ==, test->expected_pdp_type);
+ g_assert_cmpstr (pdp_addr, ==, test->expected_pdp_addr);
+ g_assert_cmpuint (cid, ==, test->expected_cid);
+
+ g_free (pdp_type);
+ g_free (pdp_addr);
+ break;
+ }
+ case MM_3GPP_CGEV_NW_CLASS:
+ case MM_3GPP_CGEV_ME_CLASS:
+ case MM_3GPP_CGEV_NW_MODIFY:
+ case MM_3GPP_CGEV_ME_MODIFY:
+ /* ignore */
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/*****************************************************************************/
/* Test ICCID parsing */
static void
@@ -1570,6 +2316,20 @@ test_iccid_parse_unquoted_unswapped_19_digit (void *f, gpointer d)
}
static void
+test_iccid_parse_unquoted_unswapped_19_digit_no_f (void *f, gpointer d)
+{
+ const char *raw_iccid = "8944200053671052499";
+ 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);
+ g_free (parsed);
+}
+
+static void
test_iccid_parse_quoted_unswapped_20_digit (void *f, gpointer d)
{
const char *raw_iccid = "\"89324102234690160476\"";
@@ -1584,6 +2344,34 @@ test_iccid_parse_quoted_unswapped_20_digit (void *f, gpointer d)
}
static void
+test_iccid_parse_quoted_unswapped_hex_account (void *f, gpointer d)
+{
+ const char *raw_iccid = "\"898602F9091830030220\"";
+ const char *expected = "898602F9091830030220";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (parsed, ==, expected);
+ g_free (parsed);
+}
+
+static void
+test_iccid_parse_quoted_unswapped_hex_account_2 (void *f, gpointer d)
+{
+ const char *raw_iccid = "\"898602C0123456789012\"";
+ const char *expected = "898602C0123456789012";
+ char *parsed;
+ GError *error = NULL;
+
+ parsed = mm_3gpp_parse_iccid (raw_iccid, &error);
+ g_assert_no_error (error);
+ g_assert_cmpstr (parsed, ==, expected);
+ g_free (parsed);
+}
+
+static void
test_iccid_parse_short (void *f, gpointer d)
{
const char *raw_iccid = "982314203264096";
@@ -1599,7 +2387,7 @@ test_iccid_parse_short (void *f, gpointer d)
static void
test_iccid_parse_invalid_chars (void *f, gpointer d)
{
- const char *raw_iccid = "98231420326ab9614067";
+ const char *raw_iccid = "98231420326pl9614067";
char *parsed;
GError *error = NULL;
@@ -1636,6 +2424,48 @@ test_iccid_parse_unquoted_invalid_mii (void *f, gpointer d)
}
/*****************************************************************************/
+/* Test APN cmp */
+
+typedef struct {
+ const gchar *existing;
+ const gchar *requested;
+ gboolean match_expected;
+} TestApnCmp;
+
+static const TestApnCmp test_apn_cmp[] = {
+ { "", "", TRUE },
+ { NULL, "", TRUE },
+ { "", NULL, TRUE },
+ { NULL, NULL, TRUE },
+ { "m2m.com.attz", "m2m.com.attz", TRUE },
+ { "m2m.com.attz", "M2M.COM.ATTZ", TRUE },
+ { "M2M.COM.ATTZ", "m2m.com.attz", TRUE },
+ { "m2m.com.attz.mnc170.mcc310.gprs", "m2m.com.attz", TRUE },
+ { "ac.vodafone.es.MNC001.MCC214.GPRS", "ac.vodafone.es", TRUE },
+ { "", "m2m.com.attz", FALSE },
+ { "m2m.com.attz", "", FALSE },
+ { "m2m.com.attz", "m2m.com.attz.mnc170.mcc310.gprs", FALSE },
+ { "ac.vodafone.es", "ac.vodafone.es.MNC001.MCC214.GPRS", FALSE },
+ { "internet.test", "internet", FALSE },
+ { "internet.test", "INTERNET", FALSE },
+ { "internet.test", "internet.tes", FALSE },
+};
+
+static void
+test_cmp_apn_name (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_apn_cmp); i++) {
+ g_debug ("Comparing requested '%s' vs existing '%s': %s match",
+ test_apn_cmp[i].requested,
+ test_apn_cmp[i].existing,
+ test_apn_cmp[i].match_expected ? "should" : "shouldn't");
+ g_assert (mm_3gpp_cmp_apn_name (test_apn_cmp[i].requested, test_apn_cmp[i].existing) == test_apn_cmp[i].match_expected);
+ }
+}
+
+/*****************************************************************************/
/* Test CGDCONT test responses */
static void
@@ -1648,9 +2478,9 @@ test_cgdcont_test_results (const gchar *desc,
GError *error = NULL;
GList *results;
- trace ("\nTesting %s +CGDCONT test response...\n", desc);
+ g_debug ("Testing %s +CGDCONT test response...", desc);
- results = mm_3gpp_parse_cgdcont_test_response (reply, &error);
+ results = mm_3gpp_parse_cgdcont_test_response (reply, NULL, &error);
g_assert (results);
g_assert_no_error (error);
g_assert_cmpuint (g_list_length (results), ==, expected_results_len);
@@ -1735,6 +2565,34 @@ test_cgdcont_test_response_single_context (void *f, gpointer d)
test_cgdcont_test_results ("Single Context", reply, &expected[0], G_N_ELEMENTS (expected));
}
+static void
+test_cgdcont_test_response_thuraya (void *f, gpointer d)
+{
+ const gchar *reply =
+ "+CGDCONT: ( 1 ) , \"IP\" ,,, (0-2),(0-3)\r\n"
+ "+CGDCONT: , \"PPP\" ,,, (0-2),(0-3)\r\n";
+ static MM3gppPdpContextFormat expected[] = {
+ { 1, 1, MM_BEARER_IP_FAMILY_IPV4 }
+ };
+
+ test_cgdcont_test_results ("Thuraya", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+
+static void
+test_cgdcont_test_response_cinterion_phs8 (void *f, gpointer d)
+{
+ const gchar *reply =
+ "+CGDCONT: (1-17,101-116),\"IP\",,,(0),(0-4)\r\n";
+ static MM3gppPdpContextFormat expected[] = {
+ { 1, 17, MM_BEARER_IP_FAMILY_IPV4 }
+ };
+
+ test_cgdcont_test_results ("Cinterion PHS8-USA REVISION 03.001", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+
+
/*****************************************************************************/
/* Test CGDCONT read responses */
@@ -1748,7 +2606,7 @@ test_cgdcont_read_results (const gchar *desc,
GError *error = NULL;
GList *results;
- trace ("\nTesting %s +CGDCONT response...\n", desc);
+ g_debug ("Testing %s +CGDCONT response...", desc);
results = mm_3gpp_parse_cgdcont_read_response (reply, &error);
g_assert (results);
@@ -1797,15 +2655,287 @@ test_cgdcont_read_response_samsung (void *f, gpointer d)
"+CGDCONT: 2,\"IP\",\"epc.tmobile.com\",\"\",0,0\r\n"
"+CGDCONT: 3,\"IP\",\"MAXROAM.com\",\"\",0,0\r\n";
static MM3gppPdpContext expected[] = {
- { 1, MM_BEARER_IP_FAMILY_IPV4, "nate.sktelecom.com" },
- { 2, MM_BEARER_IP_FAMILY_IPV4, "epc.tmobile.com" },
- { 3, MM_BEARER_IP_FAMILY_IPV4, "MAXROAM.com" }
+ { 1, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "nate.sktelecom.com" },
+ { 2, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "epc.tmobile.com" },
+ { 3, MM_BEARER_IP_FAMILY_IPV4, (gchar *) "MAXROAM.com" }
};
test_cgdcont_read_results ("Samsung", reply, &expected[0], G_N_ELEMENTS (expected));
}
/*****************************************************************************/
+/* Test CGDCONT read responses */
+
+static void
+test_cgact_read_results (const gchar *desc,
+ const gchar *reply,
+ MM3gppPdpContextActive *expected_results,
+ guint32 expected_results_len)
+{
+ GList *l;
+ GError *error = NULL;
+ GList *results;
+
+ g_debug ("Testing %s +CGACT response...", desc);
+
+ results = mm_3gpp_parse_cgact_read_response (reply, &error);
+ g_assert_no_error (error);
+ if (expected_results_len) {
+ g_assert (results);
+ g_assert_cmpuint (g_list_length (results), ==, expected_results_len);
+ }
+
+ for (l = results; l; l = g_list_next (l)) {
+ MM3gppPdpContextActive *pdp = l->data;
+ gboolean found = FALSE;
+ guint i;
+
+ for (i = 0; !found && i < expected_results_len; i++) {
+ MM3gppPdpContextActive *expected;
+
+ expected = &expected_results[i];
+ if (pdp->cid == expected->cid) {
+ found = TRUE;
+ g_assert_cmpuint (pdp->active, ==, expected->active);
+ }
+ }
+
+ g_assert (found == TRUE);
+ }
+
+ mm_3gpp_pdp_context_active_list_free (results);
+}
+
+static void
+test_cgact_read_response_none (void)
+{
+ test_cgact_read_results ("none", "", NULL, 0);
+}
+
+static void
+test_cgact_read_response_single_inactive (void)
+{
+ const gchar *reply = "+CGACT: 1,0\r\n";
+ static MM3gppPdpContextActive expected[] = {
+ { 1, FALSE },
+ };
+
+ test_cgact_read_results ("single inactive", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+static void
+test_cgact_read_response_single_active (void)
+{
+ const gchar *reply = "+CGACT: 1,1\r\n";
+ static MM3gppPdpContextActive expected[] = {
+ { 1, TRUE },
+ };
+
+ test_cgact_read_results ("single active", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+static void
+test_cgact_read_response_multiple (void)
+{
+ const gchar *reply =
+ "+CGACT: 1,0\r\n"
+ "+CGACT: 4,1\r\n"
+ "+CGACT: 5,0\r\n";
+ static MM3gppPdpContextActive expected[] = {
+ { 1, FALSE },
+ { 4, TRUE },
+ { 5, FALSE },
+ };
+
+ test_cgact_read_results ("multiple", reply, &expected[0], G_N_ELEMENTS (expected));
+}
+
+/*****************************************************************************/
+/* CID selection logic */
+
+typedef struct {
+ const gchar *apn;
+ MMBearerIpFamily ip_family;
+ const gchar *cgdcont_test;
+ const gchar *cgdcont_query;
+ gint expected_profile_id;
+ gboolean expected_profile_id_reused;
+ gboolean expected_profile_id_overwritten;
+} ProfileSelectionTest;
+
+static const ProfileSelectionTest profile_selection_tests[] = {
+ /* Test: exact APN match */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 2,\"IP\",\"ac.vodafone.es\",\"\",0,0\r\n"
+ "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 2,
+ .expected_profile_id_reused = TRUE,
+ .expected_profile_id_overwritten = FALSE
+ },
+ /* Test: exact APN match reported as activated */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 2,\"IP\",\"ac.vodafone.es.MNC001.MCC214.GPRS\",\"\",0,0\r\n"
+ "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 2,
+ .expected_profile_id_reused = TRUE,
+ .expected_profile_id_overwritten = FALSE
+ },
+ /* Test: first empty slot in between defined contexts */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 10,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 2,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = FALSE
+ },
+ /* Test: first empty slot in between defined contexts, different PDP types */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IPV6\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 10,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 2,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = FALSE
+ },
+ /* Test: first empty slot after last context found */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 2,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 3,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = FALSE
+ },
+ /* Test: first empty slot after last context found, different PDP types */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-10),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-10),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 2,\"IPV6\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 3,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = FALSE
+ },
+ /* Test: no empty slot, rewrite context with empty APN */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-3),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-3),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-3),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 2,\"IP\",\"\",\"\",0,0\r\n"
+ "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 2,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = TRUE
+ },
+ /* Test: no empty slot, rewrite last context found */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = "+CGDCONT: (1-3),\"IP\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-3),\"IPV6\",,,(0,1),(0,1)\r\n"
+ "+CGDCONT: (1-3),\"IPV4V6\",,,(0,1),(0,1)\r\n",
+ .cgdcont_query = "+CGDCONT: 1,\"IP\",\"telefonica.es\",\"\",0,0\r\n"
+ "+CGDCONT: 2,\"IP\",\"vzwinternet\",\"\",0,0\r\n"
+ "+CGDCONT: 3,\"IP\",\"inet.es\",\"\",0,0\r\n",
+ .expected_profile_id = 3,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = TRUE
+ },
+ /* Test: CGDCONT? and CGDCONT=? failures, fallback to CID=1 (a.g. some Android phones) */
+ {
+ .apn = "ac.vodafone.es",
+ .ip_family = MM_BEARER_IP_FAMILY_IPV4,
+ .cgdcont_test = NULL,
+ .cgdcont_query = NULL,
+ .expected_profile_id = 1,
+ .expected_profile_id_reused = FALSE,
+ .expected_profile_id_overwritten = TRUE
+ },
+};
+
+static void
+test_profile_selection (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (profile_selection_tests); i++) {
+ const ProfileSelectionTest *test;
+ GList *context_format_list;
+ GList *context_list;
+ GList *profile_list;
+ gint profile_id;
+ guint min_allowed_cid = 1;
+ guint max_allowed_cid = G_MAXINT - 1;
+ g_autoptr(MM3gppProfile) requested = NULL;
+ g_autoptr(MM3gppProfile) reused = NULL;
+ gboolean profile_id_overwritten;
+
+ test = &profile_selection_tests[i];
+
+ requested = mm_3gpp_profile_new ();
+ mm_3gpp_profile_set_apn (requested, test->apn);
+ mm_3gpp_profile_set_ip_type (requested, test->ip_family);
+
+ context_format_list = test->cgdcont_test ? mm_3gpp_parse_cgdcont_test_response (test->cgdcont_test, NULL, NULL) : NULL;
+ mm_3gpp_pdp_context_format_list_find_range (context_format_list, test->ip_family, &min_allowed_cid, &max_allowed_cid);
+
+ context_list = test->cgdcont_query ? mm_3gpp_parse_cgdcont_read_response (test->cgdcont_query, NULL) : NULL;
+ profile_list = mm_3gpp_profile_list_new_from_pdp_context_list (context_list);
+
+ profile_id = mm_3gpp_profile_list_find_best (profile_list,
+ requested,
+ (GEqualFunc)mm_3gpp_cmp_apn_name,
+ (MM_3GPP_PROFILE_CMP_FLAGS_NO_PROFILE_ID |
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_AUTH |
+ MM_3GPP_PROFILE_CMP_FLAGS_NO_APN_TYPE),
+ min_allowed_cid,
+ max_allowed_cid,
+ NULL, /* log_object */
+ &reused,
+ &profile_id_overwritten);
+
+ g_assert_cmpuint (profile_id, ==, test->expected_profile_id);
+ g_assert_cmpuint (!!reused, ==, test->expected_profile_id_reused);
+ g_assert_cmpuint (profile_id_overwritten, ==, test->expected_profile_id_overwritten);
+
+ mm_3gpp_profile_list_free (profile_list);
+ mm_3gpp_pdp_context_format_list_free (context_format_list);
+ mm_3gpp_pdp_context_list_free (context_list);
+ }
+}
+
+/*****************************************************************************/
/* Test CPMS responses */
static gboolean
@@ -1830,18 +2960,20 @@ test_cpms_response_cinterion (void *f, gpointer d)
GArray *mem1 = NULL;
GArray *mem2 = NULL;
GArray *mem3 = NULL;
+ GError *error = NULL;
- trace ("\nTesting Cinterion +CPMS=? response...\n");
+ g_debug ("Testing Cinterion +CPMS=? response...");
- g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3));
- g_assert (mem1->len == 2);
+ g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 2);
g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME));
g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT));
- g_assert (mem2->len == 3);
+ g_assert_cmpuint (mem2->len, ==, 3);
g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME));
g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_SM));
g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_MT));
- g_assert (mem3->len == 2);
+ g_assert_cmpuint (mem3->len, ==, 2);
g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM));
g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_MT));
@@ -1850,6 +2982,174 @@ test_cpms_response_cinterion (void *f, gpointer d)
g_array_unref (mem3);
}
+static void
+test_cpms_response_huawei_mu609 (void *f, gpointer d)
+{
+ /* Use different sets for each on purpose, even if weird */
+ const gchar *reply = "+CPMS: \"ME\",\"MT\",\"SM\"";
+ GArray *mem1 = NULL;
+ GArray *mem2 = NULL;
+ GArray *mem3 = NULL;
+ GError *error = NULL;
+
+ g_debug ("Testing Huawei MU609 +CPMS=? response...");
+
+ g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 1);
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME));
+ g_assert_cmpuint (mem2->len, ==, 1);
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_MT));
+ g_assert_cmpuint (mem3->len, ==, 1);
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM));
+
+ g_array_unref (mem1);
+ g_array_unref (mem2);
+ g_array_unref (mem3);
+}
+
+static void
+test_cpms_response_nokia_c6 (void *f, gpointer d)
+{
+ /* Use different sets for each on purpose, even if weird */
+ const gchar *reply = "+CPMS: (),(),()";
+ GArray *mem1 = NULL;
+ GArray *mem2 = NULL;
+ GArray *mem3 = NULL;
+ GError *error = NULL;
+
+ g_debug ("Testing Nokia C6 response...");
+
+ g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 0);
+ g_assert_cmpuint (mem2->len, ==, 0);
+ g_assert_cmpuint (mem3->len, ==, 0);
+
+ g_array_unref (mem1);
+ g_array_unref (mem2);
+ g_array_unref (mem3);
+}
+
+static void
+test_cpms_response_mixed (void *f, gpointer d)
+{
+ /*
+ * First: ("ME","MT") 2-item group
+ * Second: "ME" 1 item
+ * Third: ("SM") 1-item group
+ */
+ const gchar *reply = "+CPMS: (\"ME\",\"MT\"),\"ME\",(\"SM\")";
+ GArray *mem1 = NULL;
+ GArray *mem2 = NULL;
+ GArray *mem3 = NULL;
+ GError *error = NULL;
+
+ g_debug ("Testing mixed +CPMS=? response...");
+
+ g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 2);
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME));
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT));
+ g_assert_cmpuint (mem2->len, ==, 1);
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME));
+ g_assert_cmpuint (mem3->len, ==, 1);
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM));
+
+ g_array_unref (mem1);
+ g_array_unref (mem2);
+ g_array_unref (mem3);
+}
+
+static void
+test_cpms_response_mixed_spaces (void *f, gpointer d)
+{
+ /* Test with whitespaces here and there */
+ const gchar *reply = "+CPMS: ( \"ME\" , \"MT\" ) , \"ME\" , ( \"SM\" )";
+ GArray *mem1 = NULL;
+ GArray *mem2 = NULL;
+ GArray *mem3 = NULL;
+ GError *error = NULL;
+
+ g_debug ("Testing mixed +CPMS=? response with spaces...");
+
+ g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 2);
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_ME));
+ g_assert (is_storage_supported (mem1, MM_SMS_STORAGE_MT));
+ g_assert_cmpuint (mem2->len, ==, 1);
+ g_assert (is_storage_supported (mem2, MM_SMS_STORAGE_ME));
+ g_assert_cmpuint (mem3->len, ==, 1);
+ g_assert (is_storage_supported (mem3, MM_SMS_STORAGE_SM));
+
+ g_array_unref (mem1);
+ g_array_unref (mem2);
+ g_array_unref (mem3);
+}
+
+static void
+test_cpms_response_empty_fields (void *f, gpointer d)
+{
+ /*
+ * First: () Empty group
+ * Second: Empty item
+ * Third: ( ) Empty group with spaces
+ */
+ const gchar *reply = "+CPMS: (),,( )";
+ GArray *mem1 = NULL;
+ GArray *mem2 = NULL;
+ GArray *mem3 = NULL;
+ GError *error = NULL;
+
+ g_debug ("Testing mixed +CPMS=? response...");
+
+ g_assert (mm_3gpp_parse_cpms_test_response (reply, &mem1, &mem2, &mem3, &error));
+ g_assert_no_error (error);
+ g_assert_cmpuint (mem1->len, ==, 0);
+ g_assert_cmpuint (mem2->len, ==, 0);
+ g_assert_cmpuint (mem3->len, ==, 0);
+
+ g_array_unref (mem1);
+ g_array_unref (mem2);
+ g_array_unref (mem3);
+}
+
+typedef struct {
+ const gchar *query;
+ MMSmsStorage mem1_want;
+ MMSmsStorage mem2_want;
+} CpmsQueryTest;
+
+CpmsQueryTest cpms_query_test[] = {
+ {"+CPMS: \"ME\",1,100,\"MT\",5,100,\"TA\",1,100", 2, 3},
+ {"+CPMS: \"SM\",100,100,\"SR\",5,10,\"TA\",1,100", 1, 4},
+ {"+CPMS: \"XX\",100,100,\"BM\",5,10,\"TA\",1,100", 0, 5},
+ {"+CPMS: \"XX\",100,100,\"YY\",5,10,\"TA\",1,100", 0, 0},
+ {NULL, 0, 0}
+};
+
+static void
+test_cpms_query_response (void *f, gpointer d) {
+ MMSmsStorage mem1;
+ MMSmsStorage mem2;
+ gboolean ret;
+ GError *error = NULL;
+ int i;
+
+ for (i = 0; cpms_query_test[i].query != NULL; i++){
+ ret = mm_3gpp_parse_cpms_query_response (cpms_query_test[i].query,
+ &mem1,
+ &mem2,
+ &error);
+ g_assert (ret);
+ g_assert_no_error (error);
+ g_assert_cmpuint (cpms_query_test[i].mem1_want, ==, mem1);
+ g_assert_cmpuint (cpms_query_test[i].mem2_want, ==, mem2);
+ }
+}
+
/*****************************************************************************/
/* Test CNUM responses */
@@ -1859,14 +3159,12 @@ test_cnum_results (const gchar *desc,
const GStrv expected)
{
GStrv results;
- GError *error = NULL;
guint i;
- trace ("\nTesting +CNUM response (%s)...\n", desc);
+ g_debug ("Testing +CNUM response (%s)...", desc);
- results = mm_3gpp_parse_cnum_exec_response (reply, &error);
+ results = mm_3gpp_parse_cnum_exec_response (reply);
g_assert (results);
- g_assert_no_error (error);
g_assert_cmpuint (g_strv_length (results), ==, g_strv_length (expected));
for (i = 0; results[i]; i++) {
@@ -1956,27 +3254,29 @@ static void
common_parse_operator_id (const gchar *operator_id,
gboolean expected_success,
guint16 expected_mcc,
- guint16 expected_mnc)
+ guint16 expected_mnc,
+ gboolean expected_three_digit_mnc)
{
guint16 mcc;
guint16 mnc;
+ gboolean three_digit_mnc;
gboolean result;
GError *error = NULL;
if (expected_mcc) {
- trace ("\nParsing Operator ID '%s' "
- "(%" G_GUINT16_FORMAT ", %" G_GUINT16_FORMAT ")...\n",
- operator_id, expected_mcc, expected_mnc);
- result = mm_3gpp_parse_operator_id (operator_id, &mcc, &mnc, &error);
+ g_debug ("Parsing Operator ID '%s' "
+ "(%" G_GUINT16_FORMAT ", %" G_GUINT16_FORMAT ", %s)...",
+ operator_id, expected_mcc, expected_mnc, expected_three_digit_mnc ? "TRUE" : "FALSE");
+ result = mm_3gpp_parse_operator_id (operator_id, &mcc, &mnc, &three_digit_mnc, &error);
} else {
- trace ("\nValidating Operator ID '%s'...\n", operator_id);
- result = mm_3gpp_parse_operator_id (operator_id, NULL, NULL, &error);
+ g_debug ("Validating Operator ID '%s'...", operator_id);
+ result = mm_3gpp_parse_operator_id (operator_id, NULL, NULL, NULL, &error);
}
if (error)
- trace ("\tGot %s error: %s...\n",
- expected_success ? "unexpected" : "expected",
- error->message);
+ g_debug ("Got %s error: %s...",
+ expected_success ? "unexpected" : "expected",
+ error->message);
g_assert (result == expected_success);
@@ -1985,6 +3285,7 @@ common_parse_operator_id (const gchar *operator_id,
if (expected_mcc) {
g_assert_cmpuint (expected_mcc, ==, mcc);
g_assert_cmpuint (expected_mnc, ==, mnc);
+ g_assert_cmpint (expected_three_digit_mnc, ==, three_digit_mnc);
}
} else {
g_assert (error != NULL);
@@ -1996,28 +3297,27 @@ common_parse_operator_id (const gchar *operator_id,
static void
test_parse_operator_id (void *f, gpointer d)
{
- trace ("\n");
/* Valid MCC+MNC(2) */
- common_parse_operator_id ("41201", TRUE, 412, 1);
- common_parse_operator_id ("41201", TRUE, 0, 0);
+ common_parse_operator_id ("41201", TRUE, 412, 1, FALSE);
+ common_parse_operator_id ("41201", TRUE, 0, 0, FALSE);
/* Valid MCC+MNC(3) */
- common_parse_operator_id ("342600", TRUE, 342, 600);
- common_parse_operator_id ("342600", TRUE, 0, 0);
+ common_parse_operator_id ("342600", TRUE, 342, 600, TRUE);
+ common_parse_operator_id ("342600", TRUE, 0, 0, FALSE);
/* Valid MCC+MNC(2, == 0) */
- common_parse_operator_id ("72400", TRUE, 724, 0);
- common_parse_operator_id ("72400", TRUE, 0, 0);
+ common_parse_operator_id ("72400", TRUE, 724, 0, FALSE);
+ common_parse_operator_id ("72400", TRUE, 0, 0, FALSE);
/* Valid MCC+MNC(3, == 0) */
- common_parse_operator_id ("724000", TRUE, 724, 0);
- common_parse_operator_id ("724000", TRUE, 0, 0);
+ common_parse_operator_id ("724000", TRUE, 724, 0, TRUE);
+ common_parse_operator_id ("724000", TRUE, 0, 0, FALSE);
/* Invalid MCC=0 */
- common_parse_operator_id ("000600", FALSE, 0, 0);
+ common_parse_operator_id ("000600", FALSE, 0, 0, FALSE);
/* Invalid, non-digits */
- common_parse_operator_id ("000Z00", FALSE, 0, 0);
+ common_parse_operator_id ("000Z00", FALSE, 0, 0, FALSE);
/* Invalid, short */
- common_parse_operator_id ("123", FALSE, 0, 0);
+ common_parse_operator_id ("123", FALSE, 0, 0, FALSE);
/* Invalid, long */
- common_parse_operator_id ("1234567", FALSE, 0, 0);
+ common_parse_operator_id ("1234567", FALSE, 0, 0, FALSE);
}
/*****************************************************************************/
@@ -2198,7 +3498,7 @@ test_supported_mode_filter (void *f, gpointer d)
/* Only 2G supported */
all = build_mode_all (MM_MODEM_MODE_2G);
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, NULL);
g_assert_cmpuint (filtered->len, ==, 1);
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE));
g_array_unref (filtered);
@@ -2206,7 +3506,7 @@ test_supported_mode_filter (void *f, gpointer d)
/* Only 3G supported */
all = build_mode_all (MM_MODEM_MODE_3G);
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, NULL);
g_assert_cmpuint (filtered->len, ==, 1);
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE));
g_array_unref (filtered);
@@ -2214,7 +3514,7 @@ test_supported_mode_filter (void *f, gpointer d)
/* 2G and 3G supported */
all = build_mode_all (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, NULL);
g_assert_cmpuint (filtered->len, ==, 3);
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE));
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE));
@@ -2224,7 +3524,7 @@ test_supported_mode_filter (void *f, gpointer d)
/* 3G and 4G supported */
all = build_mode_all (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, NULL);
g_assert_cmpuint (filtered->len, ==, 3);
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE));
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_4G, MM_MODEM_MODE_NONE));
@@ -2234,7 +3534,7 @@ test_supported_mode_filter (void *f, gpointer d)
/* 2G, 3G and 4G supported */
all = build_mode_all (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- filtered = mm_filter_supported_modes (all, combinations);
+ filtered = mm_filter_supported_modes (all, combinations, NULL);
g_assert_cmpuint (filtered->len, ==, 6);
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_2G, MM_MODEM_MODE_NONE));
g_assert (find_mode_combination (filtered, MM_MODEM_MODE_3G, MM_MODEM_MODE_NONE));
@@ -2249,119 +3549,1053 @@ test_supported_mode_filter (void *f, gpointer d)
}
/*****************************************************************************/
+/* Test +CCLK responses */
-static gboolean
-find_capability_combination (GArray *capabilities,
- MMModemCapability capability)
+typedef struct {
+ const gchar *str;
+ gboolean ret;
+ gboolean test_iso8601;
+ gboolean test_tz;
+ const gchar *iso8601;
+ gint32 offset;
+} CclkTest;
+
+static const CclkTest cclk_tests[] = {
+ { "+CCLK: \"14/08/05,04:00:21\"", TRUE, TRUE, FALSE,
+ "2014-08-05T04:00:21Z", 0 },
+ { "+CCLK: \"14/08/05,04:00:21\"", TRUE, FALSE, TRUE,
+ "2014-08-05T04:00:21+00:00", 0 },
+ { "+CCLK: \"14/08/05,04:00:21\"", TRUE, TRUE, TRUE,
+ "2014-08-05T04:00:21Z", 0 },
+
+ { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, FALSE,
+ "2014-08-05T04:00:21+10", 600 },
+ { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, FALSE, TRUE,
+ "2014-08-05T04:00:21+10:00", 600 },
+ { "+CCLK: \"14/08/05,04:00:21+40\"", TRUE, TRUE, TRUE,
+ "2014-08-05T04:00:21+10", 600 },
+
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE,
+ "2015-02-28T20:30:40-08", -480 },
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE,
+ "2015-02-28T20:30:40-08", -480 },
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE,
+ "2015-02-28T20:30:40-08", -480 },
+
+ { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, FALSE,
+ "2017-07-26T11:42:15+00:15", 15 },
+ { "+CCLK: 17/07/26,11:42:15+01", TRUE, FALSE, TRUE,
+ "2017-07-26T11:42:15+00:15", 15 },
+ { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, TRUE,
+ "2017-07-26T11:42:15+00:15", 15 },
+
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, FALSE,
+ "2015-02-28T20:30:40-08", -480 },
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, FALSE, TRUE,
+ "2015-02-28T20:30:40-08:00", -480 },
+ { "+CCLK: \"15/02/28,20:30:40-32\"", TRUE, TRUE, TRUE,
+ "2015-02-28T20:30:40-08", -480 },
+
+ { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, FALSE,
+ "2017-07-26T11:42:15+00:15", 15 },
+ { "+CCLK: 17/07/26,11:42:15+01", TRUE, FALSE, TRUE,
+ "2017-07-26T11:42:15+00:15", 15 },
+ { "+CCLK: 17/07/26,11:42:15+01", TRUE, TRUE, TRUE,
+ "2017-07-26T11:42:15+00:15", 15 },
+
+ { "+CCLK: \"XX/XX/XX,XX:XX:XX+XX\"", FALSE, TRUE, FALSE,
+ NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN },
+
+ { NULL, FALSE, FALSE, FALSE, NULL, MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN }
+};
+
+static void
+test_cclk_response (void)
{
guint i;
- for (i = 0; i < capabilities->len; i++) {
- MMModemCapability capability_i;
+ for (i = 0; cclk_tests[i].str; i++) {
+ GError *error = NULL;
+ gchar *iso8601 = NULL;
+ MMNetworkTimezone *tz = NULL;
+ gboolean ret;
- capability_i = g_array_index (capabilities, MMModemCapability, i);
- if (capability_i == capability)
- return TRUE;
+ ret = mm_parse_cclk_response (cclk_tests[i].str,
+ cclk_tests[i].test_iso8601 ? &iso8601 : NULL,
+ cclk_tests[i].test_tz ? &tz : NULL,
+ &error);
+
+ g_assert (ret == cclk_tests[i].ret);
+ g_assert (ret == (error ? FALSE : TRUE));
+
+ g_clear_error (&error);
+
+ if (cclk_tests[i].test_iso8601)
+ g_assert_cmpstr (cclk_tests[i].iso8601, ==, iso8601);
+
+ if (cclk_tests[i].test_tz) {
+ g_assert (mm_network_timezone_get_offset (tz) == cclk_tests[i].offset);
+ g_assert (mm_network_timezone_get_dst_offset (tz) == MM_NETWORK_TIMEZONE_OFFSET_UNKNOWN);
+ g_assert (mm_network_timezone_get_leap_seconds (tz) == MM_NETWORK_TIMEZONE_LEAP_SECONDS_UNKNOWN);
+ }
+
+ g_free (iso8601);
+
+ if (tz)
+ g_object_unref (tz);
}
+}
- return FALSE;
+
+/*****************************************************************************/
+/* Test +CRSM responses */
+
+typedef struct {
+ const gchar *str;
+ gboolean ret;
+ guint sw1;
+ guint sw2;
+ const gchar *hex;
+} CrsmTest;
+
+static const CrsmTest crsm_tests[] = {
+ { "+CRSM: 144, 0, 0054485552415941FFFFFFFFFFFFFFFFFF", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" },
+ { "+CRSM: 144, 0,0054485552415941FFFFFFFFFFFFFFFFFF", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" },
+ { "+CRSM: 144, 0, \"0054485552415941FFFFFFFFFFFFFFFFFF\"", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" },
+ { "+CRSM: 144, 0,\"0054485552415941FFFFFFFFFFFFFFFFFF\"", TRUE, 144, 0, "0054485552415941FFFFFFFFFFFFFFFFFF" },
+ { NULL, FALSE, 0, 0, NULL }
+};
+
+static void
+test_crsm_response (void)
+{
+ guint i;
+
+ for (i = 0; crsm_tests[i].str; i++) {
+ GError *error = NULL;
+ guint sw1 = 0;
+ guint sw2 = 0;
+ gchar *hex = 0;
+ gboolean ret;
+
+ ret = mm_3gpp_parse_crsm_response (crsm_tests[i].str,
+ &sw1,
+ &sw2,
+ &hex,
+ &error);
+
+ g_assert (ret == crsm_tests[i].ret);
+ g_assert (ret == (error ? FALSE : TRUE));
+
+ g_clear_error (&error);
+
+ g_assert (sw1 == crsm_tests[i].sw1);
+ g_assert (sw2 == crsm_tests[i].sw2);
+
+ g_assert_cmpstr (crsm_tests[i].hex, ==, hex);
+
+ g_free (hex);
+ }
+}
+
+/*****************************************************************************/
+/* Test CGCONTRDP=N responses */
+
+typedef struct {
+ const gchar *str;
+ guint cid;
+ guint bearer_id;
+ const gchar *apn;
+ const gchar *local_address;
+ const gchar *subnet;
+ const gchar *gateway_address;
+ const gchar *dns_primary_address;
+ const gchar *dns_secondary_address;
+} CgcontrdpResponseTest;
+
+static const CgcontrdpResponseTest cgcontrdp_response_tests[] = {
+ /* Post TS 27.007 v9.4.0 format */
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\",\"10.206.56.132\",\"0.0.0.0\",\"0.0.0.0\",0",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ .gateway_address = "2.197.17.49",
+ .dns_primary_address = "10.207.43.46",
+ .dns_secondary_address = "10.206.56.132",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ .gateway_address = "2.197.17.49",
+ .dns_primary_address = "10.207.43.46",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\",\"2.197.17.49\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ .gateway_address = "2.197.17.49",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49.255.255.255.255\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ },
+ /* Pre TS 27.007 v9.4.0 format */
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\",\"10.206.56.132\",\"0.0.0.0\",\"0.0.0.0\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ .gateway_address = "2.197.17.49",
+ .dns_primary_address = "10.207.43.46",
+ .dns_secondary_address = "10.206.56.132",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\",\"10.207.43.46\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ .gateway_address = "2.197.17.49",
+ .dns_primary_address = "10.207.43.46",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\",\"2.197.17.49\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ .gateway_address = "2.197.17.49",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\",\"255.255.255.255\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ .subnet = "255.255.255.255",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\",\"2.197.17.49\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ .local_address = "2.197.17.49",
+ },
+ /* Common */
+ {
+ .str = "+CGCONTRDP: 4,5,\"ibox.tim.it.mnc001.mcc222.gprs\"",
+ .cid = 4,
+ .bearer_id = 5,
+ .apn = "ibox.tim.it.mnc001.mcc222.gprs",
+ },
+ {
+ .str = "+CGCONTRDP: 4,5,\"\"",
+ .cid = 4,
+ .bearer_id = 5,
+ },
+};
+
+static void
+test_cgcontrdp_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cgcontrdp_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint cid = G_MAXUINT;
+ guint bearer_id = G_MAXUINT;
+ gchar *apn = NULL;
+ gchar *local_address = NULL;
+ gchar *subnet = NULL;
+ gchar *gateway_address = NULL;
+ gchar *dns_primary_address = NULL;
+ gchar *dns_secondary_address = NULL;
+
+ success = mm_3gpp_parse_cgcontrdp_response (cgcontrdp_response_tests[i].str,
+ &cid,
+ &bearer_id,
+ &apn,
+ &local_address,
+ &subnet,
+ &gateway_address,
+ &dns_primary_address,
+ &dns_secondary_address,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cgcontrdp_response_tests[i].cid, ==, cid);
+ g_assert_cmpuint (cgcontrdp_response_tests[i].bearer_id, ==, bearer_id);
+ g_assert_cmpstr (cgcontrdp_response_tests[i].apn, ==, apn);
+ g_assert_cmpstr (cgcontrdp_response_tests[i].local_address, ==, local_address);
+ g_assert_cmpstr (cgcontrdp_response_tests[i].subnet, ==, subnet);
+ g_assert_cmpstr (cgcontrdp_response_tests[i].gateway_address, ==, gateway_address);
+ g_assert_cmpstr (cgcontrdp_response_tests[i].dns_primary_address, ==, dns_primary_address);
+ g_assert_cmpstr (cgcontrdp_response_tests[i].dns_secondary_address, ==, dns_secondary_address);
+
+ g_free (apn);
+ g_free (local_address);
+ g_free (subnet);
+ g_free (gateway_address);
+ g_free (dns_primary_address);
+ g_free (dns_secondary_address);
+ }
}
+/*****************************************************************************/
+/* Test CFUN? response */
+
+typedef struct {
+ const gchar *str;
+ guint state;
+} CfunQueryTest;
+
+static const CfunQueryTest cfun_query_tests[] = {
+ { "+CFUN: 1", 1 },
+ { "+CFUN: 1,0", 1 },
+ { "+CFUN: 0", 0 },
+ { "+CFUN: 0,0", 0 },
+ { "+CFUN: 19", 19 },
+ { "+CFUN: 19,0", 19 },
+};
+
static void
-test_supported_capability_filter (void *f, gpointer d)
+test_cfun_response (void)
{
- MMModemCapability capability;
- GArray *combinations;
- GArray *filtered;
+ guint i;
- combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 6);
-
- /* GSM/UMTS only */
- capability = MM_MODEM_CAPABILITY_GSM_UMTS;
- g_array_append_val (combinations, capability);
- /* CDMA/EVDO only */
- capability = MM_MODEM_CAPABILITY_CDMA_EVDO;
- g_array_append_val (combinations, capability);
- /* GSM/UMTS and CDMA/EVDO */
- capability = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_GSM_UMTS);
- g_array_append_val (combinations, capability);
- /* GSM/UMTS+LTE */
- capability = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (combinations, capability);
- /* CDMA/EVDO+LTE */
- capability = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (combinations, capability);
- /* GSM/UMTS+CDMA/EVDO+LTE */
- capability = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (combinations, capability);
-
- /* Only GSM-UMTS supported */
- filtered = mm_filter_supported_capabilities (MM_MODEM_CAPABILITY_GSM_UMTS, combinations);
- g_assert_cmpuint (filtered->len, ==, 1);
- g_assert (find_capability_combination (filtered, MM_MODEM_CAPABILITY_GSM_UMTS));
- g_array_unref (filtered);
+ for (i = 0; i < G_N_ELEMENTS (cfun_query_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint state = G_MAXUINT;
- /* Only CDMA-EVDO supported */
- filtered = mm_filter_supported_capabilities (MM_MODEM_CAPABILITY_CDMA_EVDO, combinations);
- g_assert_cmpuint (filtered->len, ==, 1);
- g_assert (find_capability_combination (filtered, MM_MODEM_CAPABILITY_CDMA_EVDO));
- g_array_unref (filtered);
+ success = mm_3gpp_parse_cfun_query_response (cfun_query_tests[i].str, &state, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cfun_query_tests[i].state, ==, state);
+ }
+}
- /* GSM-UMTS and CDMA-EVDO supported */
- filtered = mm_filter_supported_capabilities ((MM_MODEM_CAPABILITY_CDMA_EVDO |
- MM_MODEM_CAPABILITY_GSM_UMTS),
- combinations);
- g_assert_cmpuint (filtered->len, ==, 3);
- g_assert (find_capability_combination (filtered, MM_MODEM_CAPABILITY_CDMA_EVDO));
- g_assert (find_capability_combination (filtered, MM_MODEM_CAPABILITY_GSM_UMTS));
- g_assert (find_capability_combination (filtered, (MM_MODEM_CAPABILITY_GSM_UMTS |
- MM_MODEM_CAPABILITY_CDMA_EVDO)));
- g_array_unref (filtered);
+/*****************************************************************************/
+/* Test +CESQ responses */
- /* GSM-UMTS, CDMA-EVDO and LTE supported */
- filtered = mm_filter_supported_capabilities ((MM_MODEM_CAPABILITY_CDMA_EVDO |
- MM_MODEM_CAPABILITY_GSM_UMTS |
- MM_MODEM_CAPABILITY_LTE),
- combinations);
- g_assert_cmpuint (filtered->len, ==, 6);
- g_assert (find_capability_combination (filtered, MM_MODEM_CAPABILITY_CDMA_EVDO));
- g_assert (find_capability_combination (filtered, MM_MODEM_CAPABILITY_GSM_UMTS));
- g_assert (find_capability_combination (filtered, (MM_MODEM_CAPABILITY_GSM_UMTS |
- MM_MODEM_CAPABILITY_CDMA_EVDO)));
- g_assert (find_capability_combination (filtered, (MM_MODEM_CAPABILITY_GSM_UMTS |
- MM_MODEM_CAPABILITY_LTE)));
- g_assert (find_capability_combination (filtered, (MM_MODEM_CAPABILITY_CDMA_EVDO |
- MM_MODEM_CAPABILITY_LTE)));
- g_assert (find_capability_combination (filtered, (MM_MODEM_CAPABILITY_GSM_UMTS |
- MM_MODEM_CAPABILITY_CDMA_EVDO |
- MM_MODEM_CAPABILITY_LTE)));
- g_array_unref (filtered);
+typedef struct {
+ const gchar *str;
+
+ gboolean gsm_info;
+ guint rxlev;
+ gdouble rssi;
+ guint ber;
+
+ gboolean umts_info;
+ guint rscp_level;
+ gdouble rscp;
+ guint ecn0_level;
+ gdouble ecio;
+
+ gboolean lte_info;
+ guint rsrq_level;
+ gdouble rsrq;
+ guint rsrp_level;
+ gdouble rsrp;
+} CesqResponseTest;
+
+static const CesqResponseTest cesq_response_tests[] = {
+ {
+ .str = "+CESQ: 99,99,255,255,20,80",
+ .gsm_info = FALSE, .rxlev = 99, .ber = 99,
+ .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255,
+ .lte_info = TRUE, .rsrq_level = 20, .rsrq = -10.0, .rsrp_level = 80, .rsrp = -61.0,
+ },
+ {
+ .str = "+CESQ: 99,99,95,40,255,255",
+ .gsm_info = FALSE, .rxlev = 99, .ber = 99,
+ .umts_info = TRUE, .rscp_level = 95, .rscp = -26.0, .ecn0_level = 40, .ecio = -4.5,
+ .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255,
+ },
+ {
+ .str = "+CESQ: 10,6,255,255,255,255",
+ .gsm_info = TRUE, .rxlev = 10, .rssi = -101.0, .ber = 6,
+ .umts_info = FALSE, .rscp_level = 255, .ecn0_level = 255,
+ .lte_info = FALSE, .rsrq_level = 255, .rsrp_level = 255,
+ }
+};
- g_array_unref (combinations);
+static void
+test_cesq_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cesq_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ guint rxlev = G_MAXUINT;
+ guint ber = G_MAXUINT;
+ guint rscp = G_MAXUINT;
+ guint ecn0 = G_MAXUINT;
+ guint rsrq = G_MAXUINT;
+ guint rsrp = G_MAXUINT;
+
+ success = mm_3gpp_parse_cesq_response (cesq_response_tests[i].str,
+ &rxlev, &ber,
+ &rscp, &ecn0,
+ &rsrq, &rsrp,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_assert_cmpuint (cesq_response_tests[i].rxlev, ==, rxlev);
+ g_assert_cmpuint (cesq_response_tests[i].ber, ==, ber);
+ g_assert_cmpuint (cesq_response_tests[i].rscp_level, ==, rscp);
+ g_assert_cmpuint (cesq_response_tests[i].ecn0_level, ==, ecn0);
+ g_assert_cmpuint (cesq_response_tests[i].rsrq_level, ==, rsrq);
+ g_assert_cmpuint (cesq_response_tests[i].rsrp_level, ==, rsrp);
+ }
+}
+
+static void
+test_cesq_response_to_signal (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cesq_response_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMSignal *gsm = NULL;
+ MMSignal *umts = NULL;
+ MMSignal *lte = NULL;
+
+ success = mm_3gpp_cesq_response_to_signal_info (cesq_response_tests[i].str,
+ NULL,
+ &gsm, &umts, &lte,
+ &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ if (cesq_response_tests[i].gsm_info) {
+ g_assert (gsm);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rssi (gsm), cesq_response_tests[i].rssi, 0.1);
+ g_object_unref (gsm);
+ } else
+ g_assert (!gsm);
+
+ if (cesq_response_tests[i].umts_info) {
+ g_assert (umts);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rscp (umts), cesq_response_tests[i].rscp, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_ecio (umts), cesq_response_tests[i].ecio, 0.1);
+ g_object_unref (umts);
+ } else
+ g_assert (!umts);
+
+ if (cesq_response_tests[i].lte_info) {
+ g_assert (lte);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rsrq (lte), cesq_response_tests[i].rsrq, 0.1);
+ g_assert_cmpfloat_tolerance (mm_signal_get_rsrp (lte), cesq_response_tests[i].rsrp, 0.1);
+ g_object_unref (lte);
+ } else
+ g_assert (!lte);
+ }
+}
+
+typedef struct {
+ const gchar *str;
+ MMModemPowerState state;
+} CfunQueryGenericTest;
+
+static const CfunQueryGenericTest cfun_query_generic_tests[] = {
+ { "+CFUN: 1", MM_MODEM_POWER_STATE_ON },
+ { "+CFUN: 1,0", MM_MODEM_POWER_STATE_ON },
+ { "+CFUN: 0", MM_MODEM_POWER_STATE_OFF },
+ { "+CFUN: 0,0", MM_MODEM_POWER_STATE_OFF },
+ { "+CFUN: 4", MM_MODEM_POWER_STATE_LOW },
+ { "+CFUN: 4,0", MM_MODEM_POWER_STATE_LOW },
+};
+
+static void
+test_cfun_generic_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (cfun_query_generic_tests); i++) {
+ GError *error = NULL;
+ gboolean success;
+ MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN;
+
+ success = mm_3gpp_parse_cfun_query_generic_response (cfun_query_generic_tests[i].str, &state, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpuint (cfun_query_generic_tests[i].state, ==, state);
+ }
+}
+
+typedef struct {
+ const gchar *response;
+ gint result;
+ const gchar *error_message;
+} CSIMResponseTest;
+
+static CSIMResponseTest csim_response_test_list [] = {
+ /* The parser expects that 2nd arg contains
+ * substring "63Cx" where x is an HEX string
+ * representing the retry value */
+ {"+CSIM:8,\"000063C1\"", 1, NULL},
+ {"+CSIM:8,\"000063CA\"", 10, NULL},
+ {"+CSIM:8,\"000063CF\"", 15, NULL},
+ /* The parser accepts spaces */
+ {"+CSIM:8, \"000063C1\"", 1, NULL},
+ {"+CSIM: 8, \"000063C1\"", 1, NULL},
+ {"+CSIM: 8, \"000063C1\"", 1, NULL},
+ /* the parser expects an int as first argument (2nd arg's length),
+ * but does not check if it is correct */
+ {"+CSIM: 10, \"63CF\"", 15, NULL},
+ /* Valid +CSIM Error codes */
+ {"+CSIM: 4, \"6300\"", -1, "SIM verification failed"},
+ {"+CSIM: 4, \"6983\"", -1, "SIM authentication method blocked"},
+ {"+CSIM: 4, \"6984\"", -1, "SIM reference data invalidated"},
+ {"+CSIM: 4, \"6A86\"", -1, "Incorrect parameters in SIM request"},
+ {"+CSIM: 4, \"6A88\"", -1, "SIM reference data not found"},
+ /* Test error: missing first argument */
+ {"+CSIM:000063CF\"", -1, "Could not recognize +CSIM response '+CSIM:000063CF\"'"},
+ /* Test error: missing quotation mark */
+ {"+CSIM: 8, 000063CF", -1, "Could not recognize +CSIM response '+CSIM: 8, 000063CF'"},
+ /* Test generic error */
+ {"+CSIM: 4, \"63BF\"", -1, "Unknown error returned '0x63bf'"},
+ {"+CSIM: 4, \"63D0\"", -1, "Unknown error returned '0x63d0'"}
+};
+
+static void
+test_csim_response (void)
+{
+ guint i;
+ gint res;
+ GError* error = NULL;
+
+ for (i = 0; i < G_N_ELEMENTS (csim_response_test_list); i++) {
+ res = mm_parse_csim_response (csim_response_test_list[i].response, &error);
+
+ if (csim_response_test_list[i].error_message == NULL) {
+ g_assert_no_error (error);
+ } else {
+ g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED);
+ g_assert_cmpstr (error->message, ==, csim_response_test_list[i].error_message);
+ g_clear_error (&error);
+ }
+
+ g_assert_cmpint (res, ==, csim_response_test_list[i].result);
+ }
+}
+
+/*****************************************************************************/
+/* +CLIP URC */
+
+typedef struct {
+ const gchar *str;
+ const gchar *number;
+ guint type;
+} ClipUrcTest;
+
+static const ClipUrcTest clip_urc_tests[] = {
+ { "\r\n+CLIP: \"123456789\",129\r\n", "123456789", 129 },
+ { "\r\n+CLIP: \"123456789\",129,,,,0\r\n", "123456789", 129 },
+};
+
+static void
+test_clip_indication (void)
+{
+ GRegex *r;
+ guint i;
+
+ r = mm_voice_clip_regex_get ();
+
+ for (i = 0; i < G_N_ELEMENTS (clip_urc_tests); i++) {
+ GMatchInfo *match_info = NULL;
+ gchar *number;
+ guint type;
+
+ g_assert (g_regex_match (r, clip_urc_tests[i].str, 0, &match_info));
+ g_assert (g_match_info_matches (match_info));
+
+ number = mm_get_string_unquoted_from_match_info (match_info, 1);
+ g_assert_cmpstr (number, ==, clip_urc_tests[i].number);
+
+ g_assert (mm_get_uint_from_match_info (match_info, 2, &type));
+ g_assert_cmpuint (type, ==, clip_urc_tests[i].type);
+
+ g_free (number);
+ g_match_info_free (match_info);
+ }
+
+ g_regex_unref (r);
+}
+
+/*****************************************************************************/
+/* +CCWA URC */
+
+typedef struct {
+ const gchar *str;
+ const gchar *number;
+ guint type;
+ guint class;
+} CcwaUrcTest;
+
+static const CcwaUrcTest ccwa_urc_tests[] = {
+ { "\r\n+CCWA: \"123456789\",129,1\r\n", "123456789", 129, 1 },
+ { "\r\n+CCWA: \"123456789\",129,1,,0\r\n", "123456789", 129, 1 },
+ { "\r\n+CCWA: \"123456789\",129,1,,0,,,\r\n", "123456789", 129, 1 },
+};
+
+static void
+test_ccwa_indication (void)
+{
+ GRegex *r;
+ guint i;
+
+ r = mm_voice_ccwa_regex_get ();
+
+ for (i = 0; i < G_N_ELEMENTS (ccwa_urc_tests); i++) {
+ GMatchInfo *match_info = NULL;
+ gchar *number;
+ guint type;
+ guint class;
+
+ g_assert (g_regex_match (r, ccwa_urc_tests[i].str, 0, &match_info));
+ g_assert (g_match_info_matches (match_info));
+
+ number = mm_get_string_unquoted_from_match_info (match_info, 1);
+ g_assert_cmpstr (number, ==, ccwa_urc_tests[i].number);
+
+ g_assert (mm_get_uint_from_match_info (match_info, 2, &type));
+ g_assert_cmpuint (type, ==, ccwa_urc_tests[i].type);
+
+ g_assert (mm_get_uint_from_match_info (match_info, 3, &class));
+ g_assert_cmpuint (class, ==, ccwa_urc_tests[i].class);
+
+ g_free (number);
+ g_match_info_free (match_info);
+ }
+
+ g_regex_unref (r);
+}
+
+/*****************************************************************************/
+/* +CCWA service query response testing */
+
+static void
+common_test_ccwa_response (const gchar *response,
+ gboolean expected_status,
+ gboolean expected_error)
+{
+ gboolean status = FALSE;
+ GError *error = NULL;
+ gboolean result;
+
+ result = mm_3gpp_parse_ccwa_service_query_response (response, NULL, &status, &error);
+
+ if (expected_error) {
+ g_assert (!result);
+ g_assert (error);
+ g_error_free (error);
+ } else {
+ g_assert (result);
+ g_assert_no_error (error);
+ g_assert_cmpuint (status, ==, expected_status);
+ }
+}
+
+typedef struct {
+ const gchar *response;
+ gboolean expected_status;
+ gboolean expected_error;
+} TestCcwa;
+
+static TestCcwa test_ccwa[] = {
+ { "+CCWA: 0,255", FALSE, FALSE }, /* all disabled */
+ { "+CCWA: 1,255", TRUE, FALSE }, /* all enabled */
+ { "+CCWA: 0,1\r\n"
+ "+CCWA: 0,4\r\n", FALSE, FALSE }, /* voice and fax disabled */
+ { "+CCWA: 1,1\r\n"
+ "+CCWA: 1,4\r\n", TRUE, FALSE }, /* voice and fax enabled */
+ { "+CCWA: 0,2\r\n"
+ "+CCWA: 0,4\r\n"
+ "+CCWA: 0,8\r\n", FALSE, TRUE }, /* data, fax, sms disabled, voice not given */
+ { "+CCWA: 1,2\r\n"
+ "+CCWA: 1,4\r\n"
+ "+CCWA: 1,8\r\n", FALSE, TRUE }, /* data, fax, sms enabled, voice not given */
+ { "+CCWA: 2,1\r\n"
+ "+CCWA: 2,4\r\n", FALSE, TRUE }, /* voice and fax enabled but unexpected state */
+};
+
+static void
+test_ccwa_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_ccwa); i++)
+ common_test_ccwa_response (test_ccwa[i].response, test_ccwa[i].expected_status, test_ccwa[i].expected_error);
+}
+
+/*****************************************************************************/
+/* Test +CLCC URCs */
+
+static void
+common_test_clcc_response (const gchar *str,
+ const MMCallInfo *expected_call_info_list,
+ guint expected_call_info_list_size)
+{
+ GError *error = NULL;
+ gboolean result;
+ GList *call_info_list = NULL;
+ GList *l;
+
+ result = mm_3gpp_parse_clcc_response (str, NULL, &call_info_list, &error);
+ g_assert_no_error (error);
+ g_assert (result);
+
+ g_debug ("found %u calls", g_list_length (call_info_list));
+
+ if (expected_call_info_list) {
+ g_assert (call_info_list);
+ g_assert_cmpuint (g_list_length (call_info_list), ==, expected_call_info_list_size);
+ } else
+ g_assert (!call_info_list);
+
+ for (l = call_info_list; l; l = g_list_next (l)) {
+ const MMCallInfo *call_info = (const MMCallInfo *)(l->data);
+ gboolean found = FALSE;
+ guint i;
+
+ g_debug ("call at index %u: direction %s, state %s, number %s",
+ call_info->index,
+ mm_call_direction_get_string (call_info->direction),
+ mm_call_state_get_string (call_info->state),
+ call_info->number ? call_info->number : "n/a");
+
+ for (i = 0; !found && i < expected_call_info_list_size; i++)
+ found = ((call_info->index == expected_call_info_list[i].index) &&
+ (call_info->direction == expected_call_info_list[i].direction) &&
+ (call_info->state == expected_call_info_list[i].state) &&
+ (g_strcmp0 (call_info->number, expected_call_info_list[i].number) == 0));
+
+ g_assert (found);
+ }
+
+ mm_3gpp_call_info_list_free (call_info_list);
+}
+
+static void
+test_clcc_response_empty (void)
+{
+ const gchar *response = "";
+
+ common_test_clcc_response (response, NULL, 0);
+}
+
+static void
+test_clcc_response_single (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" }
+ };
+
+ const gchar *response =
+ "+CLCC: 1,1,0,0,0,\"123456789\",161";
+
+ common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_clcc_response_single_long (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "123456789" }
+ };
+
+ /* NOTE: priority field is EMPTY */
+ const gchar *response =
+ "+CLCC: 1,1,4,0,0,\"123456789\",129,\"\",,0";
+
+ common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_clcc_response_multiple (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, NULL },
+ { 2, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "123456789" },
+ { 3, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "987654321" },
+ { 4, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_ACTIVE, (gchar *) "000000000" },
+ { 5, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_WAITING, (gchar *) "555555555" },
+ };
+
+ const gchar *response =
+ "+CLCC: 1,1,0,0,1\r\n" /* number unknown */
+ "+CLCC: 2,1,0,0,1,\"123456789\",161\r\n"
+ "+CLCC: 3,1,0,0,1,\"987654321\",161,\"Alice\"\r\n"
+ "+CLCC: 4,1,0,0,1,\"000000000\",161,\"Bob\",1\r\n"
+ "+CLCC: 5,1,5,0,0,\"555555555\",161,\"Mallory\",2,0\r\n";
+
+ common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+static void
+test_clcc_response_ignore_non_voice (void)
+{
+ static const MMCallInfo expected_call_info_list[] = {
+ { 1, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "987654321" },
+ { 4, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "111111111" },
+ { 5, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "222222222" },
+ { 6, MM_CALL_DIRECTION_INCOMING, MM_CALL_STATE_RINGING_IN, (gchar *) "333333333" },
+ };
+
+ const gchar *response =
+ "+CLCC: 1,1,4,0,0,\"987654321\",161\r\n" /* voice mode */
+ "+CLCC: 2,1,4,1,0,\"123456789\",161\r\n" /* data mode, skip */
+ "+CLCC: 3,1,4,2,0,\"000000000\",161\r\n" /* fax data, skip */
+ "+CLCC: 4,1,4,3,0,\"111111111\",161\r\n" /* voice followed by data, voice mode */
+ "+CLCC: 5,1,4,4,0,\"222222222\",161\r\n" /* alternating voice/data, voice mode */
+ "+CLCC: 6,1,4,5,0,\"333333333\",161\r\n" /* alternating voice/fax, voice mode */
+ "+CLCC: 7,1,4,6,0,\"444444444\",161\r\n" /* voice followed by data, data mode, skip */
+ "+CLCC: 8,1,4,7,0,\"555555555\",161\r\n" /* alternating voice/data, data mode, skip */
+ "+CLCC: 9,1,4,8,0,\"666666666\",161\r\n" /* alternating voice/fax, fax mode, skip */
+ "+CLCC: 10,1,4,9,0,\"777777777\",161\r\n"; /* unknown mode, skip */
+
+ common_test_clcc_response (response, expected_call_info_list, G_N_ELEMENTS (expected_call_info_list));
+}
+
+/*****************************************************************************/
+/* Test +CRSM EF_ECC read data parsing */
+
+#define MAX_EMERGENCY_NUMBERS 5
+typedef struct {
+ const gchar *raw;
+ guint n_numbers;
+ const gchar *numbers[MAX_EMERGENCY_NUMBERS];
+} EmergencyNumbersTest;
+
+static const EmergencyNumbersTest emergency_numbers_tests[] = {
+ { "", 0 },
+ { "FFF", 0 },
+ { "FFFFFF" "FFFFFF" "FFFFFF" "FFFFFF" "FFFFFF", 0 },
+ { "00F0FF" "11F2FF" "88F8FF", 3, { "000", "112", "888" } },
+ { "00F0FF" "11F2FF" "88F8FF" "FFFFFF" "FFFFFF", 3, { "000", "112", "888" } },
+ { "00F0FF" "11F2FF" "88F8FF" "214365" "08FFFF", 5, { "000", "112", "888", "123456", "80" } },
+};
+
+static void
+test_emergency_numbers (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (emergency_numbers_tests); i++) {
+ GStrv numbers;
+ GError *error = NULL;
+ guint j;
+
+ g_debug (" testing %s...", emergency_numbers_tests[i].raw);
+
+ numbers = mm_3gpp_parse_emergency_numbers (emergency_numbers_tests[i].raw, &error);
+ if (!emergency_numbers_tests[i].n_numbers) {
+ g_assert (error);
+ g_assert (!numbers);
+ continue;
+ }
+
+ g_assert_no_error (error);
+ g_assert (numbers);
+
+ g_assert_cmpuint (emergency_numbers_tests[i].n_numbers, ==, g_strv_length (numbers));
+ for (j = 0; j < emergency_numbers_tests[i].n_numbers; j++)
+ g_assert_cmpstr (emergency_numbers_tests[i].numbers[j], ==, numbers[j]);
+
+ g_strfreev (numbers);
+ }
+}
+
+/*****************************************************************************/
+
+typedef struct {
+ const gchar *str;
+ gint expected_number_list[9];
+} TestParseNumberList;
+
+static const TestParseNumberList test_parse_number_list_item [] = {
+ { "1-6", { 1, 2, 3, 4, 5, 6, -1 } },
+ { "0,1,2,4,6", { 0, 1, 2, 4, 6, -1 } },
+ { "1,3-5,7,9-11", { 1, 3, 4, 5, 7, 9, 10, 11, -1 } },
+ { "9-11,7,3-5", { 3, 4, 5, 7, 9, 10, 11, -1 } },
+ { "", { -1 } },
+ { NULL, { -1 } },
+};
+
+static void
+test_parse_uint_list (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_parse_number_list_item); i++) {
+ GArray *array;
+ GError *error = NULL;
+ guint j;
+
+ array = mm_parse_uint_list (test_parse_number_list_item[i].str, &error);
+ g_assert_no_error (error);
+ if (test_parse_number_list_item[i].expected_number_list[0] == -1) {
+ g_assert (!array);
+ continue;
+ }
+
+ g_assert (array);
+ for (j = 0; j < array->len; j++) {
+ g_assert_cmpint (test_parse_number_list_item[i].expected_number_list[j], !=, -1);
+ g_assert_cmpuint (test_parse_number_list_item[i].expected_number_list[j], ==, g_array_index (array, guint, j));
+ }
+ g_assert_cmpint (test_parse_number_list_item[i].expected_number_list[array->len], ==, -1);
+ g_array_unref (array);
+ }
}
/*****************************************************************************/
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+typedef struct {
+ const guint8 bcd[10];
+ gsize bcd_len;
+ const gchar *low_nybble_first_str;
+ const gchar *high_nybble_first_str;
+} BcdToStringTest;
+
+static const BcdToStringTest bcd_to_string_tests[] = {
+ { { }, 0, "", "" },
+ { { 0x01 }, 1, "10", "01" },
+ { { 0x1F }, 1, "", "1" },
+ { { 0xE2 }, 1, "2", "" },
+ { { 0xD3 }, 1, "3", "" },
+ { { 0xC4 }, 1, "4", "" },
+ { { 0xB1, 0x23 }, 2, "1", "" },
+ { { 0x01, 0x2A }, 2, "10", "012" },
+ { { 0x01, 0x23, 0x45, 0x67 }, 4, "10325476", "01234567" },
+ { { 0x01, 0x23, 0x45, 0xA7 }, 4, "1032547", "012345" },
+ { { 0x01, 0x23, 0x45, 0x67 }, 2, "1032", "0123" },
+};
+
+static void
+test_bcd_to_string (void *f, gpointer d)
{
-#if defined ENABLE_TEST_MESSAGE_TRACES
- /* Dummy log function */
- va_list args;
- gchar *msg;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (bcd_to_string_tests); i++) {
+ gchar *str;
- va_start (args, fmt);
- msg = g_strdup_vprintf (fmt, args);
- va_end (args);
- g_print ("%s\n", msg);
- g_free (msg);
-#endif
+ str = mm_bcd_to_string (bcd_to_string_tests[i].bcd,
+ bcd_to_string_tests[i].bcd_len,
+ TRUE /* low_nybble_first */);
+ g_assert_cmpstr (str, ==, bcd_to_string_tests[i].low_nybble_first_str);
+ g_free (str);
+
+ str = mm_bcd_to_string (bcd_to_string_tests[i].bcd,
+ bcd_to_string_tests[i].bcd_len,
+ FALSE /* low_nybble_first */);
+ g_assert_cmpstr (str, ==, bcd_to_string_tests[i].high_nybble_first_str);
+ g_free (str);
+ }
}
+/*****************************************************************************/
+
+typedef struct {
+ const gchar *response;
+ gboolean expected_error;
+ guint expected_index;
+ const gchar *expected_operator_code;
+ gboolean expected_gsm_act;
+ gboolean expected_gsm_compact_act;
+ gboolean expected_utran_act;
+ gboolean expected_eutran_act;
+ gboolean expected_ngran_act;
+ guint expected_act_count;
+} TestCpol;
+
+static const TestCpol test_cpol[] = {
+ { "+CPOL: 1,2,\"24412\",0,0,0,0", FALSE, 1, "24412", FALSE, FALSE, FALSE, FALSE, FALSE, 4 },
+ { "+CPOL: 1,2,24412,0,0,0,0", FALSE, 1, "24412", FALSE, FALSE, FALSE, FALSE, FALSE, 4 },
+ { "+CPOL: 1", TRUE },
+ { "+CPOL: 1,2", TRUE },
+ { "+CPOL: 1,1,\"Test\"", TRUE },
+ { "+CPOL: 5,2,123456,0,0,0,0,1", FALSE, 5, "123456", FALSE, FALSE, FALSE, FALSE, TRUE, 5 },
+ { "+CPOL: 99,2,\"63423\",1,0,1,0,1", FALSE, 99, "63423", TRUE, FALSE, TRUE, FALSE, TRUE, 5 },
+ { "+CPOL: 101,2,\"63423\",1,1,1", FALSE, 101, "63423", TRUE, TRUE, TRUE, FALSE, FALSE, 3 },
+ { "+CPOL: 101,2,\"24491\"", FALSE, 101, "24491", FALSE, FALSE, FALSE, FALSE, FALSE, 0 },
+ { "+CPOL: X,2,\"24491\"", TRUE },
+ { "+CPOL:1, 2, 12345, 1, 1, 1, 1, 1", FALSE, 1, "12345", TRUE, TRUE, TRUE, TRUE, TRUE, 5 }
+};
+
+static void
+test_cpol_response (void)
+{
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (test_cpol); i++) {
+ guint index;
+ gchar *operator_code = NULL;
+ gboolean gsm_act;
+ gboolean gsm_compact_act;
+ gboolean utran_act;
+ gboolean eutran_act;
+ gboolean ngran_act;
+ guint act_count;
+ gboolean result;
+ GError *error = NULL;
+
+ result = mm_sim_parse_cpol_query_response (test_cpol[i].response,
+ &index,
+ &operator_code,
+ &gsm_act,
+ &gsm_compact_act,
+ &utran_act,
+ &eutran_act,
+ &ngran_act,
+ &act_count,
+ &error);
+ if (test_cpol[i].expected_error) {
+ g_assert (!result);
+ g_assert (error);
+ g_error_free (error);
+ } else {
+ g_assert (result);
+ g_assert_no_error (error);
+ g_assert_cmpuint (index, ==, test_cpol[i].expected_index);
+ g_assert_cmpstr (operator_code, ==, test_cpol[i].expected_operator_code);
+ g_assert_cmpuint (gsm_act, ==, test_cpol[i].expected_gsm_act);
+ g_assert_cmpuint (gsm_compact_act, ==, test_cpol[i].expected_gsm_compact_act);
+ g_assert_cmpuint (utran_act, ==, test_cpol[i].expected_utran_act);
+ g_assert_cmpuint (eutran_act, ==, test_cpol[i].expected_eutran_act);
+ g_assert_cmpuint (ngran_act, ==, test_cpol[i].expected_ngran_act);
+ g_assert_cmpuint (act_count, ==, test_cpol[i].expected_act_count);
+ }
+ g_free (operator_code);
+ }
+}
+
+/*****************************************************************************/
+
#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL)
int main (int argc, char **argv)
@@ -2371,12 +4605,32 @@ int main (int argc, char **argv)
gint result;
DevidItem *item = &devids[0];
- g_type_init ();
g_test_init (&argc, &argv, NULL);
suite = g_test_get_root ();
reg_data = reg_test_data_new ();
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_none_only, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_xon_xoff_only, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_rts_cts_only, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_ta, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_te, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_simple, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_groups, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple_and_unknown, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups_and_unknown, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g4g, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g_v2, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_cinterion, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_telit_le866, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_range_1, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ws46_response_range_2, NULL));
+
g_test_suite_add (suite, TESTCASE (test_cops_response_tm506, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gt3gplus, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_ac881, NULL));
@@ -2404,10 +4658,15 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cops_response_gobi, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_sek600i, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_samsung_z810, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cops_response_ublox_lara, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gsm_invalid, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_umts_invalid, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cops_query, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_normalize_operator, NULL));
+
g_test_suite_add (suite, TESTCASE (test_creg1_solicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_creg1_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_creg2_mercury_solicited, reg_data));
@@ -2427,6 +4686,8 @@ int main (int argc, char **argv)
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_creg2_ublox_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_ublox_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));
@@ -2435,6 +4696,8 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cgreg2_md400_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cgreg2_x220_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cgreg2_unsolicited_with_rac, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_thuraya_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_thuraya_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cereg1_solicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_cereg1_unsolicited, reg_data));
@@ -2445,6 +4708,11 @@ int main (int argc, char **argv)
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));
+ g_test_suite_add (suite, TESTCASE (test_c5greg1_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_c5greg1_unsolicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_c5greg2_solicited, reg_data));
+ g_test_suite_add (suite, TESTCASE (test_c5greg2_unsolicited, reg_data));
+
g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi_unsolicited, reg_data));
g_test_suite_add (suite, TESTCASE (test_creg_cgreg_multi2_unsolicited, reg_data));
@@ -2453,13 +4721,24 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cscs_buslink_support_response, NULL));
g_test_suite_add (suite, TESTCASE (test_cscs_blackberry_support_response, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cmer_response_cinterion_pls8, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cmer_response_sierra_em7345, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cmer_response_cinterion_ehs5, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cmer_request_cinterion_ehs5, NULL));
+
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_cgev_indication, 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_unquoted_unswapped_19_digit_no_f, NULL));
g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_20_digit, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_hex_account, NULL));
+ g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_unswapped_hex_account_2, 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));
@@ -2470,16 +4749,33 @@ int main (int argc, char **argv)
item++;
}
- g_test_suite_add (suite, TESTCASE (test_cpms_response_cinterion, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_cinterion, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_huawei_mu609, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_nokia_c6, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_mixed, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_mixed_spaces, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_response_empty_fields, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cpms_query_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cmp_apn_name, NULL));
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_test_response_thuraya, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cgdcont_test_response_cinterion_phs8, 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));
+ g_test_suite_add (suite, TESTCASE (test_profile_selection, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cgact_read_response_none, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cgact_read_response_single_inactive, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cgact_read_response_single_active, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cgact_read_response_multiple, NULL));
+
g_test_suite_add (suite, TESTCASE (test_cnum_response_generic, NULL));
g_test_suite_add (suite, TESTCASE (test_cnum_response_generic_without_detail, NULL));
g_test_suite_add (suite, TESTCASE (test_cnum_response_generic_detail_unquoted, NULL));
@@ -2497,9 +4793,42 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cmgl_response_pantech, NULL));
g_test_suite_add (suite, TESTCASE (test_cmgl_response_pantech_multiple, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cmgr_response_generic, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cmgr_response_telit, NULL));
+
g_test_suite_add (suite, TESTCASE (test_supported_mode_filter, NULL));
- g_test_suite_add (suite, TESTCASE (test_supported_capability_filter, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cclk_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_crsm_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cgcontrdp_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cfun_response, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cfun_generic_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_csim_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cesq_response, NULL));
+ g_test_suite_add (suite, TESTCASE (test_cesq_response_to_signal, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_clip_indication, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ccwa_indication, NULL));
+ g_test_suite_add (suite, TESTCASE (test_ccwa_response, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_clcc_response_empty, NULL));
+ g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL));
+ g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL));
+ g_test_suite_add (suite, TESTCASE (test_clcc_response_multiple, NULL));
+ g_test_suite_add (suite, TESTCASE (test_clcc_response_ignore_non_voice, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_emergency_numbers, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_parse_uint_list, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_bcd_to_string, NULL));
+
+ g_test_suite_add (suite, TESTCASE (test_cpol_response, NULL));
result = g_test_run ();
diff --git a/src/tests/test-qcdm-serial-port.c b/src/tests/test-qcdm-serial-port.c
index 1f88fad9..2a9e1c47 100644
--- a/src/tests/test-qcdm-serial-port.c
+++ b/src/tests/test-qcdm-serial-port.c
@@ -34,7 +34,7 @@
#include "libqcdm/src/utils.h"
#include "libqcdm/src/com.h"
#include "libqcdm/src/errors.h"
-#include "mm-log.h"
+#include "mm-log-test.h"
typedef struct {
int master;
@@ -54,7 +54,7 @@ wait_for_child (TestData *d, guint32 timeout)
status = 0;
ret = waitpid (d->child, &status, WNOHANG);
g_get_current_time (&now);
- if (d->child && (now.tv_sec - start.tv_sec > timeout)) {
+ if (d->child && (now.tv_sec - start.tv_sec > (glong)timeout)) {
/* Kill it */
if (g_test_verbose ())
g_message ("Killing running child process %d", d->child);
@@ -72,7 +72,7 @@ wait_for_child (TestData *d, guint32 timeout)
static void
print_buf (const char *detail, const char *buf, gsize len)
{
- int i = 0;
+ guint i = 0;
gboolean newline = FALSE;
g_print ("%s (%zu) ", detail, len);
@@ -116,7 +116,7 @@ server_wait_request (int fd, char *buf, gsize len)
struct timeval timeout = { 1, 0 };
char readbuf[1024];
ssize_t bytes_read;
- int total = 0, retries = 0;
+ guint total = 0, retries = 0;
gsize decap_len = 0;
FD_ZERO (&in);
@@ -213,9 +213,6 @@ qcdm_test_child (int fd, GAsyncReadyCallback cb)
gboolean success;
GError *error = NULL;
- /* In the child */
- g_type_init ();
-
loop = g_main_loop_new (NULL, FALSE);
port = mm_port_serial_qcdm_new_fd (fd);
@@ -440,26 +437,6 @@ test_pty_cleanup (TestData *d)
}
}
-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
-}
-
typedef void (*TCFunc) (TestData *, gconstpointer);
#define TESTCASE_PTY(s, t) g_test_add (s, TestData, NULL, (TCFunc)test_pty_create, (TCFunc)t, (TCFunc)test_pty_cleanup);
diff --git a/src/tests/test-sms-part-3gpp.c b/src/tests/test-sms-part-3gpp.c
index 21822f09..4da299e7 100644
--- a/src/tests/test-sms-part-3gpp.c
+++ b/src/tests/test-sms-part-3gpp.c
@@ -24,24 +24,7 @@
#include <libmm-glib.h>
#include "mm-sms-part-3gpp.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
+#include "mm-log-test.h"
/********************* PDU PARSER TESTS *********************/
@@ -58,7 +41,7 @@ common_test_part_from_hexpdu (const gchar *hexpdu,
MMSmsPart *part;
GError *error = NULL;
- part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, NULL, &error);
g_assert_no_error (error);
g_assert (part != NULL);
@@ -136,7 +119,7 @@ test_pdu1 (void)
pdu, sizeof (pdu),
"+12404492164", /* smsc */
"+16175927198", /* number */
- "110228115050-05", /* timestamp */
+ "2011-02-28T11:50:50-05", /* timestamp */
FALSE,
"Here's a longer message [{with some extended characters}] "
"thrown in, such as £ and ΩΠΨ and §¿ as well.", /* text */
@@ -157,7 +140,7 @@ test_pdu2 (void)
pdu, sizeof (pdu),
"+79037011111", /* smsc */
"InternetSMS", /* number */
- "110329192004+04", /* timestamp */
+ "2011-03-29T19:20:04+04", /* timestamp */
FALSE,
"тест", /* text */
NULL, 0);
@@ -177,7 +160,7 @@ test_pdu3 (void)
pdu, sizeof (pdu),
"+12345678901", /* smsc */
"+18005551212", /* number */
- "110101123456+00", /* timestamp */
+ "2011-01-01T12:34:56Z", /* timestamp */
FALSE,
"hellohello", /* text */
NULL, 0);
@@ -198,7 +181,7 @@ test_pdu3_nzpid (void)
pdu, sizeof (pdu),
"+12345678901", /* smsc */
"+18005551212", /* number */
- "110101123456+00", /* timestamp */
+ "2011-01-01T12:34:56Z", /* timestamp */
FALSE,
"hellohello", /* text */
NULL, 0);
@@ -219,7 +202,7 @@ test_pdu3_mms (void)
pdu, sizeof (pdu),
"+12345678901", /* smsc */
"+18005551212", /* number */
- "110101123456+00", /* timestamp */
+ "2011-01-01T12:34:56Z", /* timestamp */
FALSE,
"hellohello", /* text */
NULL, 0);
@@ -240,7 +223,7 @@ test_pdu3_natl (void)
pdu, sizeof (pdu),
"+12345678901", /* smsc */
"18005551212", /* number, no plus */
- "110101123456+00", /* timestamp */
+ "2011-01-01T12:34:56Z", /* timestamp */
FALSE,
"hellohello", /* text */
NULL, 0);
@@ -262,7 +245,7 @@ test_pdu3_8bit (void)
pdu, sizeof (pdu),
"+12345678901", /* smsc */
"+18005551212", /* number */
- "110101123456+00", /* timestamp */
+ "2011-01-01T12:34:56Z", /* timestamp */
FALSE,
NULL, /* text */
expected_data, /* data */
@@ -310,7 +293,7 @@ test_pdu_dcsf1 (void)
pdu, sizeof (pdu),
"+33609001390", /* smsc */
"1800", /* number */
- "110624130815+02", /* timestamp */
+ "2011-06-24T13:08:15+02", /* timestamp */
FALSE,
"Info SFR - Confidentiel, à ne jamais transmettre -\r\n"
"Voici votre nouveau mot de passe : sw2ced pour gérer "
@@ -334,7 +317,7 @@ test_pdu_dcsf_8bit (void)
pdu, sizeof (pdu),
"+12345678901", /* smsc */
"+18005551212", /* number */
- "110101123456+00", /* timestamp */
+ "2011-01-01T12:34:56Z", /* timestamp */
FALSE,
NULL, /* text */
expected_data, /* data */
@@ -356,7 +339,7 @@ test_pdu_insufficient_data (void)
};
hexpdu = mm_utils_bin2hexstr (pdu, sizeof (pdu));
- part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, &error);
+ part = mm_sms_part_3gpp_new_from_pdu (0, hexpdu, NULL, &error);
g_assert (part == NULL);
/* We don't care for the specific error type */
g_assert (error != NULL);
@@ -380,7 +363,7 @@ test_pdu_udhi (void)
hexpdu,
"+31653131316", /* smsc */
"1002", /* number */
- "110629233219+02", /* timestamp */
+ "2011-06-29T23:32:19+02", /* timestamp */
TRUE,
"Welkom, bel om uw Voicemail te beluisteren naar +31612001233"
" (PrePay: *100*1233#). Voicemail ontvangen is altijd gratis."
@@ -405,7 +388,7 @@ test_pdu_multipart (void)
hexpdu1,
"+12063130025", /* smsc */
"+16175046925", /* number */
- "120425195650-04", /* timestamp */
+ "2012-04-25T19:56:50-04", /* timestamp */
TRUE, /* multipart! */
"This is a very long test designed to exercise multi part capability. It should "
"show up as one message, not as two, as the underlying encoding represents ", /* text */
@@ -415,7 +398,7 @@ test_pdu_multipart (void)
hexpdu2,
"+12063130026", /* smsc */
"+16175046925", /* number */
- "120425195651-04", /* timestamp */
+ "2012-04-25T19:56:51-04", /* timestamp */
TRUE, /* multipart! */
"that the parts are related to one another. ", /* text */
NULL, 0);
@@ -448,7 +431,7 @@ test_pdu_not_stored (void)
hexpdu1,
"+34656000311", /* smsc */
"639337937", /* number */
- "120911074036+02", /* timestamp */
+ "2012-09-11T07:40:36+02", /* timestamp */
FALSE, /* multipart! */
NULL, /* text */
NULL, 0);
@@ -509,6 +492,21 @@ test_address_encode_unknown (void)
/********************* PDU CREATOR TESTS *********************/
static void
+trace_pdu (const guint8 *pdu,
+ guint len)
+{
+ 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");
+}
+
+static void
common_test_create_pdu (const gchar *smsc,
const gchar *number,
const gchar *text,
@@ -533,7 +531,7 @@ common_test_create_pdu (const gchar *smsc,
MMSmsEncoding encoding = MM_SMS_ENCODING_UNKNOWN;
/* Detect best encoding */
- out = mm_sms_part_3gpp_util_split_text (text, &encoding);
+ out = mm_sms_part_3gpp_util_split_text (text, &encoding, NULL);
g_strfreev (out);
mm_sms_part_set_text (part, text);
mm_sms_part_set_encoding (part, encoding);
@@ -546,15 +544,16 @@ common_test_create_pdu (const gchar *smsc,
pdu = mm_sms_part_3gpp_get_submit_pdu (part,
&len,
&msgstart,
+ NULL,
&error);
mm_sms_part_free (part);
- trace_pdu (pdu, len);
+ if (g_test_verbose ())
+ 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_assert_cmpmem (pdu, len, expected, expected_size);
g_assert_cmpint (msgstart, ==, expected_msgstart);
g_free (pdu);
@@ -720,7 +719,7 @@ common_test_text_split (const gchar *text,
MMSmsEncoding out_encoding = MM_SMS_ENCODING_UNKNOWN;
guint i;
- out = mm_sms_part_3gpp_util_split_text (text, &out_encoding);
+ out = mm_sms_part_3gpp_util_split_text (text, &out_encoding, NULL);
g_assert (out != NULL);
g_assert (out_encoding != MM_SMS_ENCODING_UNKNOWN);
@@ -735,7 +734,7 @@ common_test_text_split (const gchar *text,
}
static void
-test_text_split_short (void)
+test_text_split_short_gsm7 (void)
{
const gchar *text = "Hello";
const gchar *expected [] = {
@@ -749,7 +748,7 @@ test_text_split_short (void)
static void
test_text_split_short_ucs2 (void)
{
- const gchar *text = "你好";
+ const gchar *text = "你好"; /* (UTF-8) e4 bd a0 e5 a5 bd */
const gchar *expected [] = {
"你好",
NULL
@@ -759,7 +758,19 @@ test_text_split_short_ucs2 (void)
}
static void
-test_text_split_max_single_pdu (void)
+test_text_split_short_utf16 (void)
+{
+ const gchar *text = "😉"; /* U+1F609, winking face */
+ const gchar *expected [] = {
+ "😉",
+ NULL
+ };
+
+ common_test_text_split (text, expected, MM_SMS_ENCODING_UCS2);
+}
+
+static void
+test_text_split_max_single_pdu_gsm7 (void)
{
const gchar *text =
"0123456789012345678901234567890123456789"
@@ -798,7 +809,23 @@ test_text_split_max_single_pdu_ucs2 (void)
}
static void
-test_text_split_two_pdu (void)
+test_text_split_max_single_pdu_utf16 (void)
+{
+ /* NOTE: this string contains 35 Bhaiksuki characters, each of
+ * them requiring 4 bytes both in UTF-8 and in UTF-16 (140 bytes
+ * in total). */
+ const gchar *text =
+ "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡𑰢𑰣";
+ const gchar *expected [] = {
+ "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡𑰢𑰣",
+ NULL
+ };
+
+ common_test_text_split (text, expected, MM_SMS_ENCODING_UCS2);
+}
+
+static void
+test_text_split_two_pdu_gsm7 (void)
{
const gchar *text =
"0123456789012345678901234567890123456789"
@@ -839,33 +866,36 @@ test_text_split_two_pdu_ucs2 (void)
common_test_text_split (text, expected, MM_SMS_ENCODING_UCS2);
}
-/************************************************************/
-
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+static void
+test_text_split_two_pdu_utf16 (void)
{
-#if defined ENABLE_TEST_MESSAGE_TRACES
- /* Dummy log function */
- va_list args;
- gchar *msg;
+ /* NOTE: this string contains 35 Bhaiksuki characters, each of
+ * them requiring 4 bytes both in UTF-8 and in UTF-16 (140 bytes
+ * in total) plus one ASCII char (encoded with 1 byte in UTF-8 and
+ * 2 bytes in UTF-16), making it a total of 142 bytes when in
+ * UTF-16 (so not fitting in one single PDU)
+ *
+ * When split in chunks, the last chunk will hold 2 Bhaiksuki
+ * characters plus the last ASCII one (9 bytes in UTF-16) so that
+ * the first chunk contains the leading 33 Bhaiksuki characters
+ * (132 characters, less than 134) */
+ const gchar *text =
+ "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡𑰢𑰣a";
+ const gchar *expected [] = {
+ "𑰀𑰁𑰂𑰃𑰄𑰅𑰆𑰇𑰈𑰊𑰋𑰌𑰍𑰎𑰏𑰐𑰑𑰒𑰓𑰔𑰕𑰖𑰗𑰘𑰙𑰚𑰛𑰜𑰝𑰞𑰟𑰠𑰡",
+ "𑰢𑰣a",
+ NULL
+ };
- va_start (args, fmt);
- msg = g_strdup_vprintf (fmt, args);
- va_end (args);
- g_print ("%s\n", msg);
- g_free (msg);
-#endif
+ common_test_text_split (text, expected, MM_SMS_ENCODING_UCS2);
}
+/************************************************************/
+
int main (int argc, char **argv)
{
setlocale (LC_ALL, "");
- g_type_init ();
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/MM/SMS/3GPP/PDU-Parser/pdu1", test_pdu1);
@@ -895,12 +925,15 @@ int main (int argc, char **argv)
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);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/gsm7/short", test_text_split_short_gsm7);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/ucs2/short", test_text_split_short_ucs2);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/utf16/short", test_text_split_short_utf16);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/gsm7/max-single-pdu", test_text_split_max_single_pdu_gsm7);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/ucs2/max-single-pdu", test_text_split_max_single_pdu_ucs2);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/utf16/max-single-pdu", test_text_split_max_single_pdu_utf16);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/gsm7/two-pdu", test_text_split_two_pdu_gsm7);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/ucs2/two-pdu", test_text_split_two_pdu_ucs2);
+ g_test_add_func ("/MM/SMS/3GPP/Text-Split/utf16/two-pdu", test_text_split_two_pdu_utf16);
return g_test_run ();
}
diff --git a/src/tests/test-sms-part-cdma.c b/src/tests/test-sms-part-cdma.c
index 17d78966..eb095f34 100644
--- a/src/tests/test-sms-part-cdma.c
+++ b/src/tests/test-sms-part-cdma.c
@@ -23,24 +23,7 @@
#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
+#include "mm-log-test.h"
/********************* PDU PARSER TESTS *********************/
@@ -55,8 +38,7 @@ common_test_part_from_hexpdu (const gchar *hexpdu,
MMSmsPart *part;
GError *error = NULL;
- mm_dbg (" ");
- part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, &error);
+ part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, NULL, &error);
g_assert_no_error (error);
g_assert (part != NULL);
@@ -105,8 +87,7 @@ 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);
+ part = mm_sms_part_cdma_new_from_pdu (0, hexpdu, NULL, &error);
g_assert (part == NULL);
/* We don't care for the specific error type */
g_assert (error != NULL);
@@ -370,6 +351,21 @@ test_unicode_encoding (void)
/********************* PDU CREATOR TESTS *********************/
static void
+trace_pdu (const guint8 *pdu,
+ guint len)
+{
+ 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");
+}
+
+static void
common_test_create_pdu (MMSmsCdmaTeleserviceId teleservice_id,
const gchar *number,
const gchar *text,
@@ -398,10 +394,11 @@ common_test_create_pdu (MMSmsCdmaTeleserviceId teleservice_id,
mm_sms_part_take_data (part, data_bytearray);
}
- pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, &error);
+ pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, NULL, &error);
mm_sms_part_free (part);
- trace_pdu (pdu, len);
+ if (g_test_verbose ())
+ trace_pdu (pdu, len);
g_assert_no_error (error);
g_assert (pdu != NULL);
@@ -503,33 +500,50 @@ test_create_pdu_text_unicode_encoding (void)
expected, sizeof (expected));
}
-/************************************************************/
-
-void
-_mm_log (const char *loc,
- const char *func,
- guint32 level,
- const char *fmt,
- ...)
+static void
+test_create_parse_pdu_text_ascii_encoding (void)
{
-#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 MAX_TEXT_LEN 100
+ guint i;
+ gchar text[MAX_TEXT_LEN + 1];
+
+ memset (text, 0, sizeof (text));
+
+ for (i = 0; i < MAX_TEXT_LEN; i++) {
+ MMSmsPart *part;
+ guint8 *pdu;
+ guint len = 0;
+ GError *error = NULL;
+
+ text[i]='A';
+
+ part = mm_sms_part_new (0, MM_SMS_PDU_TYPE_CDMA_SUBMIT);
+ mm_sms_part_set_cdma_teleservice_id (part, MM_SMS_CDMA_TELESERVICE_ID_WMT);
+ mm_sms_part_set_number (part, "123456789");
+ mm_sms_part_set_text (part, text);
+ pdu = mm_sms_part_cdma_get_submit_pdu (part, &len, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (pdu != NULL);
+ mm_sms_part_free (part);
+
+ part = mm_sms_part_cdma_new_from_binary_pdu (0, pdu, len, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (part != NULL);
+ g_assert_cmpuint (MM_SMS_CDMA_TELESERVICE_ID_WMT, ==, mm_sms_part_get_cdma_teleservice_id (part));
+ g_assert_cmpstr ("123456789", ==, mm_sms_part_get_number (part));
+ g_assert_cmpstr (text, ==, mm_sms_part_get_text (part));
+ mm_sms_part_free (part);
+
+ g_free (pdu);
+ }
}
+/************************************************************/
+
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);
@@ -544,5 +558,7 @@ int main (int argc, char **argv)
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);
+ g_test_add_func ("/MM/SMS/CDMA/PDU-Creator-Parser/ascii-encoding", test_create_parse_pdu_text_ascii_encoding);
+
return g_test_run ();
}
diff --git a/src/tests/test-udev-rules.c b/src/tests/test-udev-rules.c
new file mode 100644
index 00000000..cdc962e0
--- /dev/null
+++ b/src/tests/test-udev-rules.c
@@ -0,0 +1,55 @@
+/* -*- 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) 2016 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#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-kernel-device-generic-rules.h"
+#include "mm-log-test.h"
+
+/************************************************************/
+
+static void
+test_load_cleanup_core (void)
+{
+ GArray *rules;
+ GError *error = NULL;
+
+ rules = mm_kernel_device_generic_rules_load (TESTUDEVRULESDIR, &error);
+ g_assert_no_error (error);
+ g_assert (rules);
+ g_assert (rules->len > 0);
+
+ g_array_unref (rules);
+}
+
+/************************************************************/
+
+int main (int argc, char **argv)
+{
+ setlocale (LC_ALL, "");
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/MM/test-udev-rules/load-cleanup-core", test_load_cleanup_core);
+
+ return g_test_run ();
+}
diff --git a/test/Makefile.am b/test/Makefile.am
index f0bfd188..bf54f0af 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,9 +1,131 @@
-noinst_PROGRAMS = lsudev
+noinst_PROGRAMS =
+EXTRA_DIST =
-lsudev_SOURCES = lsudev.c
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(NULL)
+
+################################################################################
+# lsudev
+################################################################################
+
+if WITH_UDEV
+
+noinst_PROGRAMS += lsudev
+
+lsudev_SOURCES = lsudev.c
lsudev_CPPFLAGS = $(GUDEV_CFLAGS)
-lsudev_LDADD = $(GUDEV_LIBS)
+lsudev_LDADD = $(GUDEV_LIBS)
+
+endif
+
+################################################################################
+# mmtty
+################################################################################
+
+noinst_PROGRAMS += mmtty
+
+mmtty_SOURCES = mmtty.c
+
+mmtty_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/kerneldevice \
+ -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
+ $(NULL)
+
+mmtty_LDADD = \
+ $(MM_LIBS) \
+ $(top_builddir)/src/libport.la \
+ $(NULL)
+
+################################################################################
+# mmrules
+################################################################################
+
+noinst_PROGRAMS += mmrules
+
+mmrules_SOURCES = mmrules.c
+
+mmrules_CPPFLAGS = \
+ $(MM_CFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/kerneldevice \
+ -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
+ $(NULL)
+
+mmrules_LDADD = \
+ $(MM_LIBS) \
+ $(top_builddir)/src/libkerneldevice.la \
+ $(NULL)
+
+################################################################################
+# mmsmspdu
+################################################################################
+
+noinst_PROGRAMS += mmsmspdu
+
+mmsmspdu_SOURCES = mmsmspdu.c
+
+mmsmspdu_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
+ $(NULL)
+
+mmsmspdu_LDADD = \
+ $(MM_LIBS) \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+################################################################################
+# mmsmsmonitor
+################################################################################
+
+noinst_PROGRAMS += mmsmsmonitor
+
+mmsmsmonitor_SOURCES = mmsmsmonitor.c
+
+mmsmsmonitor_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
+ $(NULL)
+
+mmsmsmonitor_LDADD = \
+ $(MM_LIBS) \
+ $(top_builddir)/src/libhelpers.la \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+################################################################################
+# mmcli-test-sms
+################################################################################
-EXTRA_DIST = \
- mmcli-test-sms
+EXTRA_DIST += mmcli-test-sms
diff --git a/test/lsudev.c b/test/lsudev.c
index b4ff790b..4579fd23 100644
--- a/test/lsudev.c
+++ b/test/lsudev.c
@@ -30,94 +30,93 @@ static GMainLoop *loop = NULL;
static void
signal_handler (int signo)
{
- if (signo == SIGINT || signo == SIGTERM) {
- g_message ("Caught signal %d, shutting down...", signo);
- g_main_loop_quit (loop);
- }
+ if (signo == SIGINT || signo == SIGTERM) {
+ g_message ("Caught signal %d, shutting down...", signo);
+ g_main_loop_quit (loop);
+ }
}
static void
setup_signals (void)
{
- struct sigaction action;
- sigset_t mask;
-
- sigemptyset (&mask);
- action.sa_handler = signal_handler;
- action.sa_mask = mask;
- action.sa_flags = 0;
- sigaction (SIGTERM, &action, NULL);
- sigaction (SIGINT, &action, NULL);
+ struct sigaction action;
+ sigset_t mask;
+
+ sigemptyset (&mask);
+ action.sa_handler = signal_handler;
+ action.sa_mask = mask;
+ action.sa_flags = 0;
+ sigaction (SIGTERM, &action, NULL);
+ sigaction (SIGINT, &action, NULL);
}
+static void println (guint indent, const char *fmt, ...) __attribute__((__format__ (__printf__, 2, 3)));
+
static void
println (guint indent, const char *fmt, ...)
{
- va_list args;
- GString *output;
- int i;
+ va_list args;
+ GString *output;
+ guint i;
- g_return_if_fail (fmt != NULL);
+ g_return_if_fail (fmt != NULL);
- output = g_string_sized_new (250);
+ output = g_string_sized_new (250);
- for (i = 0; i < indent; i++)
- g_string_append_c (output, ' ');
+ for (i = 0; i < indent; i++)
+ g_string_append_c (output, ' ');
- va_start (args, fmt);
- g_string_append_vprintf (output, fmt, args);
- va_end (args);
+ va_start (args, fmt);
+ g_string_append_vprintf (output, fmt, args);
+ va_end (args);
- g_print ("%s\n", output->str);
- g_string_free (output, TRUE);
+ g_print ("%s\n", output->str);
+ g_string_free (output, TRUE);
}
static void
dump_device_and_parent (GUdevDevice *device, guint indent)
{
- const char **list, **iter;
- GUdevDevice *parent;
- char propstr[500];
- guint32 namelen = 0, i;
-
- println (indent, "------------------------------------------------------");
- println (indent, "Name: %s", g_udev_device_get_name (device));
- println (indent, "Type: %s", g_udev_device_get_devtype (device));
- println (indent, "Subsys: %s", g_udev_device_get_subsystem (device));
- println (indent, "Number: %s", g_udev_device_get_number (device));
- println (indent, "Path: %s", g_udev_device_get_sysfs_path (device));
- println (indent, "Driver: %s", g_udev_device_get_driver (device));
- println (indent, "Action: %s", g_udev_device_get_action (device));
- println (indent, "Seq Num: %lu", g_udev_device_get_seqnum (device));
- println (indent, "Dev File: %s", g_udev_device_get_device_file (device));
-
- println (indent, "");
- println (indent, "Properties:");
-
- /* Get longest property name length for alignment */
- list = (const char **) g_udev_device_get_property_keys (device);
- for (iter = list; iter && *iter; iter++) {
- if (strlen (*iter) > namelen)
- namelen = strlen (*iter);
- }
- namelen++;
-
- for (iter = list; iter && *iter; iter++) {
- strcpy (propstr, *iter);
- strcat (propstr, ":");
- for (i = 0; i < namelen - strlen (*iter); i++)
- strcat (propstr, " ");
- strcat (propstr, g_udev_device_get_property (device, *iter));
- println (indent + 2, "%s", propstr);
- }
-
- println (indent, "");
-
- parent = g_udev_device_get_parent (device);
- if (parent) {
- dump_device_and_parent (parent, indent + 4);
- g_object_unref (parent);
- }
+ const char **list, **iter;
+ GUdevDevice *parent;
+ char propstr[500];
+ guint32 namelen = 0, i;
+
+ println (indent, "------------------------------------------------------");
+ println (indent, "Name: %s", g_udev_device_get_name (device));
+ println (indent, "Type: %s", g_udev_device_get_devtype (device));
+ println (indent, "Subsys: %s", g_udev_device_get_subsystem (device));
+ println (indent, "Number: %s", g_udev_device_get_number (device));
+ println (indent, "Path: %s", g_udev_device_get_sysfs_path (device));
+ println (indent, "Driver: %s", g_udev_device_get_driver (device));
+ println (indent, "Action: %s", g_udev_device_get_action (device));
+ println (indent, "Seq Num: %" G_GUINT64_FORMAT, g_udev_device_get_seqnum (device));
+ println (indent, "Dev File: %s", g_udev_device_get_device_file (device));
+ println (indent, "-----------");
+ println (indent, "Properties:");
+
+ /* Get longest property name length for alignment */
+ list = (const char **) g_udev_device_get_property_keys (device);
+ for (iter = list; iter && *iter; iter++) {
+ if (strlen (*iter) > namelen)
+ namelen = strlen (*iter);
+ }
+ namelen++;
+
+ for (iter = list; iter && *iter; iter++) {
+ strcpy (propstr, *iter);
+ strcat (propstr, ":");
+ for (i = 0; i < namelen - strlen (*iter); i++)
+ strcat (propstr, " ");
+ strcat (propstr, g_udev_device_get_property (device, *iter));
+ println (indent + 2, "%s", propstr);
+ }
+
+ parent = g_udev_device_get_parent (device);
+ if (parent) {
+ dump_device_and_parent (parent, indent + 4);
+ g_object_unref (parent);
+ }
}
static void
@@ -126,55 +125,52 @@ handle_uevent (GUdevClient *client,
GUdevDevice *device,
gpointer user_data)
{
- const char *expected_subsys = user_data;
- const char *subsys;
+ const char *expected_subsys = user_data;
+ const char *subsys;
- g_return_if_fail (client != NULL);
- g_return_if_fail (action != NULL);
- g_return_if_fail (device != NULL);
+ g_return_if_fail (client != NULL);
+ g_return_if_fail (action != NULL);
+ g_return_if_fail (device != NULL);
- /* A bit paranoid */
- subsys = g_udev_device_get_subsystem (device);
- g_return_if_fail (subsys != NULL);
+ /* A bit paranoid */
+ subsys = g_udev_device_get_subsystem (device);
+ g_return_if_fail (subsys != NULL);
- g_return_if_fail (!strcmp (subsys, expected_subsys));
+ g_return_if_fail (!strcmp (subsys, expected_subsys));
- g_print ("---- (EVENT: %s) ----\n", action);
- dump_device_and_parent (device, 0);
- g_print ("\n");
+ g_print ("---- (EVENT: %s) ----\n", action);
+ dump_device_and_parent (device, 0);
+ g_print ("\n");
}
int
main (int argc, char *argv[])
{
- GUdevClient *client;
- const char *subsys[2] = { NULL, NULL };
- GList *list, *iter;
-
- if (argc != 2) {
- g_warning ("Usage: %s [subsystem]", argv[0]);
- return 1;
- }
+ GUdevClient *client;
+ const char *subsys[2] = { NULL, NULL };
+ GList *list, *iter;
- g_type_init ();
+ if (argc != 2) {
+ g_warning ("Usage: %s [subsystem]", argv[0]);
+ return 1;
+ }
- loop = g_main_loop_new (NULL, FALSE);
+ loop = g_main_loop_new (NULL, FALSE);
- setup_signals ();
+ setup_signals ();
- subsys[0] = argv[1];
- client = g_udev_client_new (subsys);
- g_signal_connect (client, "uevent", G_CALLBACK (handle_uevent), (gpointer) subsys[0]);
+ subsys[0] = argv[1];
+ client = g_udev_client_new (subsys);
+ g_signal_connect (client, "uevent", G_CALLBACK (handle_uevent), (gpointer) subsys[0]);
- list = g_udev_client_query_by_subsystem (client, subsys[0]);
- for (iter = list; iter; iter = g_list_next (iter)) {
- dump_device_and_parent (G_UDEV_DEVICE (iter->data), 0);
- g_print ("\n");
- g_object_unref (G_UDEV_DEVICE (iter->data));
- }
+ list = g_udev_client_query_by_subsystem (client, subsys[0]);
+ for (iter = list; iter; iter = g_list_next (iter)) {
+ dump_device_and_parent (G_UDEV_DEVICE (iter->data), 0);
+ g_print ("\n");
+ g_object_unref (G_UDEV_DEVICE (iter->data));
+ }
- g_main_loop_run (loop);
+ g_main_loop_run (loop);
- return 0;
+ return 0;
}
-
diff --git a/test/meson.build b/test/meson.build
new file mode 100644
index 00000000..3e66eed7
--- /dev/null
+++ b/test/meson.build
@@ -0,0 +1,22 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+test_units = {
+ 'mmrules': libkerneldevice_dep,
+ 'mmsmsmonitor': libhelpers_dep,
+ 'mmsmspdu': libhelpers_dep,
+ 'mmtty': libport_dep,
+}
+
+if gudev_dep.found()
+ test_units += {'lsudev': gudev_dep}
+endif
+
+foreach test_unit, test_deps: test_units
+ executable(
+ test_unit,
+ test_unit + '.c',
+ include_directories: top_inc,
+ dependencies: test_deps,
+ )
+endforeach
diff --git a/test/mmrules.c b/test/mmrules.c
new file mode 100644
index 00000000..3538f44d
--- /dev/null
+++ b/test/mmrules.c
@@ -0,0 +1,153 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <mm-log-test.h>
+#include <mm-kernel-device-generic-rules.h>
+
+#define PROGRAM_NAME "mmrules"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+/* Context */
+static gchar *path;
+static gboolean verbose_flag;
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+ { "path", 'p', 0, G_OPTION_ARG_FILENAME, &path,
+ "Specify path to udev rules directory",
+ "[PATH]"
+ },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
+ "Run action with verbose logs",
+ NULL
+ },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ { NULL }
+};
+
+static void
+print_version_and_exit (void)
+{
+ g_print ("\n"
+ PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (2015) Aleksander Morgado\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n"
+ "\n");
+ exit (EXIT_SUCCESS);
+}
+
+static void
+print_rule (MMUdevRule *rule)
+{
+ /* Process conditions */
+ if (rule->conditions) {
+ guint i;
+
+ for (i = 0; i < rule->conditions->len; i++) {
+ MMUdevRuleMatch *rule_match;
+
+ rule_match = &g_array_index (rule->conditions, MMUdevRuleMatch, i);
+ switch (rule_match->type) {
+ case MM_UDEV_RULE_MATCH_TYPE_EQUAL:
+ g_print (" [condition %u] %s == %s\n",
+ i, rule_match->parameter, rule_match->value);
+ break;
+ case MM_UDEV_RULE_MATCH_TYPE_NOT_EQUAL:
+ g_print (" [condition %u] %s != %s\n",
+ i, rule_match->parameter, rule_match->value);
+ break;
+ case MM_UDEV_RULE_MATCH_TYPE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+ }
+ }
+
+ /* Process result */
+ switch (rule->result.type) {
+ case MM_UDEV_RULE_RESULT_TYPE_LABEL:
+ g_print (" [result] label %s\n", rule->result.content.tag);
+ break;
+ case MM_UDEV_RULE_RESULT_TYPE_GOTO_INDEX:
+ g_print (" [result] jump to rule %u\n", rule->result.content.index);
+ break;
+ case MM_UDEV_RULE_RESULT_TYPE_PROPERTY:
+ g_print (" [result] set property %s = %s\n",
+ rule->result.content.property.name, rule->result.content.property.value);
+ break;
+ case MM_UDEV_RULE_RESULT_TYPE_GOTO_TAG:
+ case MM_UDEV_RULE_RESULT_TYPE_UNKNOWN:
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GArray *rules;
+ guint i;
+ GError *error = NULL;
+
+ setlocale (LC_ALL, "");
+
+ /* Setup option context, process it and destroy it */
+ context = g_option_context_new ("- ModemManager udev rules testing");
+ g_option_context_add_main_entries (context, main_entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (version_flag)
+ print_version_and_exit ();
+
+ /* No device path given? */
+ if (!path) {
+ g_printerr ("error: no path specified\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Load rules from directory */
+ rules = mm_kernel_device_generic_rules_load (path, &error);
+ if (!rules) {
+ g_printerr ("error: couldn't load rules: %s", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Print loaded rules */
+ for (i = 0; i < rules->len; i++) {
+ g_print ("-----------------------------------------\n");
+ g_print ("rule [%u]:\n", i);
+ print_rule (&g_array_index (rules, MMUdevRule, i));
+ }
+
+ g_array_unref (rules);
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/mmsmsmonitor.c b/test/mmsmsmonitor.c
new file mode 100644
index 00000000..de895214
--- /dev/null
+++ b/test/mmsmsmonitor.c
@@ -0,0 +1,195 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib-unix.h>
+#include <gio/gio.h>
+#include <libmm-glib.h>
+
+#define PROGRAM_NAME "mmsmsmonitor"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+/* Globals */
+static GMainLoop *loop;
+static GList *monitored_sms_list;
+
+/* Context */
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ { NULL }
+};
+
+static gboolean
+signals_handler (void)
+{
+ if (loop && g_main_loop_is_running (loop)) {
+ g_printerr ("%s\n",
+ "cancelling the main loop...\n");
+ g_main_loop_quit (loop);
+ }
+ return TRUE;
+}
+
+static void
+print_version_and_exit (void)
+{
+ g_print ("\n"
+ PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (2019) Aleksander Morgado\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n"
+ "\n");
+ exit (EXIT_SUCCESS);
+}
+
+static void
+sms_state_updated (MMSms *sms)
+{
+ g_print ("[%s] sms updated: %s\n",
+ mm_sms_get_path (sms),
+ mm_sms_state_get_string (mm_sms_get_state (sms)));
+}
+
+static gboolean
+sms_added (MMModemMessaging *modem_messaging,
+ const gchar *sms_path,
+ gboolean received)
+{
+ GList *sms_list;
+ GList *l;
+ MMSms *new_sms = NULL;
+
+ sms_list = mm_modem_messaging_list_sync (modem_messaging, NULL, NULL);
+ for (l = sms_list; l && !new_sms; l = g_list_next (l)) {
+ MMSms *l_sms = MM_SMS (l->data);
+
+ if (g_strcmp0 (mm_sms_get_path (l_sms), sms_path) == 0)
+ new_sms = l_sms;
+ }
+ g_assert (new_sms);
+
+ g_print ("[%s] new sms: %s\n",
+ mm_sms_get_path (new_sms),
+ mm_sms_state_get_string (mm_sms_get_state (new_sms)));
+ g_signal_connect (new_sms, "notify::state", G_CALLBACK (sms_state_updated), NULL);
+ monitored_sms_list = g_list_append (monitored_sms_list, g_object_ref (new_sms));
+
+ g_list_free_full (sms_list, g_object_unref);
+ return TRUE;
+}
+
+static void
+list_all_sms_found (MMModemMessaging *modem_messaging)
+{
+ GList *sms_list;
+ GList *l;
+
+ sms_list = mm_modem_messaging_list_sync (modem_messaging, NULL, NULL);
+ for (l = sms_list; l; l = g_list_next (l)) {
+ MMSms *l_sms = MM_SMS (l->data);
+
+ g_print ("[%s] sms found: %s\n",
+ mm_sms_get_path (l_sms),
+ mm_sms_state_get_string (mm_sms_get_state (l_sms)));
+ g_signal_connect (l_sms, "notify::state", G_CALLBACK (sms_state_updated), NULL);
+ monitored_sms_list = g_list_append (monitored_sms_list, g_object_ref (l_sms));
+ }
+ g_list_free_full (sms_list, g_object_unref);
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GDBusConnection *connection;
+ MMManager *manager;
+ GList *modem_list;
+ GList *l;
+ gchar *name_owner;
+ GError *error = NULL;
+
+ setlocale (LC_ALL, "");
+
+ /* Setup option context, process it and destroy it */
+ context = g_option_context_new ("- ModemManager SMS monitor");
+ g_option_context_add_main_entries (context, main_entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (version_flag)
+ print_version_and_exit ();
+
+ /* Setup dbus connection to use */
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!connection) {
+ g_printerr ("error: couldn't get bus: %s\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ manager = mm_manager_new_sync (connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START,
+ NULL,
+ &error);
+ if (!manager) {
+ g_printerr ("error: couldn't create manager: %s\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ name_owner = g_dbus_object_manager_client_get_name_owner (G_DBUS_OBJECT_MANAGER_CLIENT (manager));
+ if (!name_owner) {
+ g_printerr ("error: couldn't find the ModemManager process in the bus\n");
+ exit (EXIT_FAILURE);
+ }
+ g_free (name_owner);
+
+ modem_list = g_dbus_object_manager_get_objects (G_DBUS_OBJECT_MANAGER (manager));
+ for (l = modem_list; l; l = g_list_next (l)) {
+ MMObject *obj;
+ MMModemMessaging *modem_messaging;
+
+ obj = MM_OBJECT (l->data);
+ modem_messaging = MM_MODEM_MESSAGING (mm_object_peek_modem_messaging (obj));
+
+ g_signal_connect (modem_messaging, "added", G_CALLBACK (sms_added), NULL);
+ list_all_sms_found (modem_messaging);
+ }
+
+ g_unix_signal_add (SIGINT, (GSourceFunc) signals_handler, NULL);
+ g_unix_signal_add (SIGHUP, (GSourceFunc) signals_handler, NULL);
+ g_unix_signal_add (SIGTERM, (GSourceFunc) signals_handler, NULL);
+
+ /* Setup main loop and shedule start in idle */
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+
+ /* Cleanup */
+ g_main_loop_unref (loop);
+ g_list_free_full (modem_list, g_object_unref);
+ g_object_unref (manager);
+ return EXIT_SUCCESS;
+}
diff --git a/test/mmsmspdu.c b/test/mmsmspdu.c
new file mode 100644
index 00000000..3a56ffc5
--- /dev/null
+++ b/test/mmsmspdu.c
@@ -0,0 +1,214 @@
+/* -*- 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) 2019 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <ModemManager.h>
+#define _LIBMM_INSIDE_MM
+#include <libmm-glib.h>
+#include "mm-log-test.h"
+#include "mm-sms-part-3gpp.h"
+
+#define PROGRAM_NAME "mmsmspdu"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+/* Context */
+static gchar *pdu;
+static gboolean verbose_flag;
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+ { "pdu", 'p', 0, G_OPTION_ARG_STRING, &pdu,
+ "PDU contents",
+ "[0123456789ABCDEF..]"
+ },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
+ "Run action with verbose logs",
+ NULL
+ },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ { NULL }
+};
+
+static void
+show_part_info (MMSmsPart *part)
+{
+ MMSmsPduType pdu_type;
+ const gchar *smsc;
+ const gchar *number;
+ const gchar *timestamp;
+ const gchar *text;
+ MMSmsEncoding encoding;
+ gint class;
+ guint validity_relative;
+ gboolean delivery_report_request;
+ guint concat_reference;
+ guint concat_max;
+ guint concat_sequence;
+ const GByteArray *data;
+
+ pdu_type = mm_sms_part_get_pdu_type (part);
+ g_print ("pdu type: %s\n", mm_sms_pdu_type_get_string (pdu_type));
+
+ smsc = mm_sms_part_get_smsc (part);
+ g_print ("smsc: %s\n", smsc ? smsc : "n/a");
+
+ number = mm_sms_part_get_number (part);
+ g_print ("number: %s\n", number ? number : "n/a");
+
+ timestamp = mm_sms_part_get_timestamp (part);
+ g_print ("timestamp: %s\n", timestamp ? timestamp : "n/a");
+
+ encoding = mm_sms_part_get_encoding (part);
+ switch (encoding) {
+ case MM_SMS_ENCODING_GSM7:
+ g_print ("encoding: GSM7\n");
+ break;
+ case MM_SMS_ENCODING_UCS2:
+ g_print ("encoding: UCS2\n");
+ break;
+ case MM_SMS_ENCODING_8BIT:
+ g_print ("encoding: 8BIT\n");
+ break;
+ case MM_SMS_ENCODING_UNKNOWN:
+ default:
+ g_print ("encoding: unknown (0x%x)\n", encoding);
+ break;
+ }
+
+ text = mm_sms_part_get_text (part);
+ g_print ("text: %s\n", text ? text : "n/a");
+
+ data = mm_sms_part_get_data (part);
+ if (data) {
+ gchar *data_str;
+
+ data_str = mm_utils_bin2hexstr (data->data, data->len);
+ g_print ("data: %s\n", data_str);
+ g_free (data_str);
+ } else
+ g_print ("data: n/a\n");
+
+ class = mm_sms_part_get_class (part);
+ if (class != -1)
+ g_print ("class: %d\n", class);
+ else
+ g_print ("class: n/a\n");
+
+ validity_relative = mm_sms_part_get_validity_relative (part);
+ if (validity_relative != 0)
+ g_print ("validity relative: %d\n", validity_relative);
+ else
+ g_print ("validity relative: n/a\n");
+
+ delivery_report_request = mm_sms_part_get_delivery_report_request (part);
+ g_print ("delivery report request: %s\n", delivery_report_request ? "yes" : "no");
+
+ concat_reference = mm_sms_part_get_concat_reference (part);
+ g_print ("concat reference: %d\n", concat_reference);
+
+ concat_max = mm_sms_part_get_concat_max (part);
+ g_print ("concat max: %d\n", concat_max);
+
+ concat_sequence = mm_sms_part_get_concat_sequence (part);
+ g_print ("concat sequence: %d\n", concat_sequence);
+
+ if (mm_sms_part_get_pdu_type (part) == MM_SMS_PDU_TYPE_STATUS_REPORT) {
+ const gchar *discharge_timestamp;
+ guint message_reference;
+ guint delivery_state;
+
+ message_reference = mm_sms_part_get_message_reference (part);
+ g_print ("message reference: %d\n", message_reference);
+
+ discharge_timestamp = mm_sms_part_get_discharge_timestamp (part);
+ g_print ("discharge timestamp: %s\n", discharge_timestamp ? discharge_timestamp : "n/a");
+
+ delivery_state = mm_sms_part_get_delivery_state (part);
+ g_print ("delivery state: %s\n", mm_sms_delivery_state_get_string_extended (delivery_state));
+ }
+
+ if (MM_SMS_PART_IS_CDMA (part)) {
+ MMSmsCdmaTeleserviceId teleservice_id;
+ MMSmsCdmaServiceCategory service_category;
+
+ teleservice_id = mm_sms_part_get_cdma_teleservice_id (part);
+ g_print ("teleservice id: %s\n", mm_sms_cdma_teleservice_id_get_string (teleservice_id));
+
+ service_category = mm_sms_part_get_cdma_service_category (part);
+ g_print ("service category: %s\n", mm_sms_cdma_service_category_get_string (service_category));
+ }
+}
+
+static void
+print_version_and_exit (void)
+{
+ g_print ("\n"
+ PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (2019) Aleksander Morgado\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n"
+ "\n");
+ exit (EXIT_SUCCESS);
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+ GError *error = NULL;
+ MMSmsPart *part;
+
+ setlocale (LC_ALL, "");
+
+ /* Setup option context, process it and destroy it */
+ context = g_option_context_new ("- ModemManager SMS PDU parser");
+ g_option_context_add_main_entries (context, main_entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (version_flag)
+ print_version_and_exit ();
+
+ /* No pdu given? */
+ if (!pdu) {
+ g_printerr ("error: no PDU specified\n");
+ exit (EXIT_FAILURE);
+ }
+
+ part = mm_sms_part_3gpp_new_from_pdu (0, pdu, NULL, &error);
+ if (!part) {
+ g_printerr ("error: couldn't parse PDU: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ show_part_info (part);
+
+ mm_sms_part_free (part);
+ g_free (pdu);
+
+ return EXIT_SUCCESS;
+}
diff --git a/test/mmtty.c b/test/mmtty.c
new file mode 100644
index 00000000..5e8e16b0
--- /dev/null
+++ b/test/mmtty.c
@@ -0,0 +1,347 @@
+/* -*- 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) 2015 Aleksander Morgado <aleksander@aleksander.es>
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <mm-log.h>
+#include <mm-port-serial.h>
+#include <mm-port-serial-at.h>
+#include <mm-serial-parsers.h>
+
+#define PROGRAM_NAME "mmtty"
+#define PROGRAM_VERSION PACKAGE_VERSION
+
+/* Globals */
+static MMPortSerialAt *port;
+static GMainLoop *loop;
+static GIOChannel *input;
+static guint input_watch_id;
+
+/* Context */
+static gchar *device_str;
+static gboolean no_flash_flag;
+static gboolean no_echo_removal_flag;
+static gboolean spew_control_flag;
+static gint64 send_delay = -1;
+static gboolean send_lf_flag;
+static gboolean verbose_flag;
+static gboolean version_flag;
+
+static GOptionEntry main_entries[] = {
+ { "device", 'd', 0, G_OPTION_ARG_STRING, &device_str,
+ "Specify device path",
+ "[PATH]"
+ },
+ { "no-flash", 0, 0, G_OPTION_ARG_NONE, &no_flash_flag,
+ "Avoid flashing the port while opening",
+ NULL
+ },
+ { "no-echo-removal", 0, 0, G_OPTION_ARG_NONE, &no_echo_removal_flag,
+ "Avoid logic to remove echo",
+ NULL
+ },
+ { "spew-control", 0, 0, G_OPTION_ARG_NONE, &spew_control_flag,
+ "Enable spew control logic",
+ NULL
+ },
+ { "send-delay", 0, 0, G_OPTION_ARG_INT64, &send_delay,
+ "Send delay for each byte in microseconds (default=1000)",
+ "[DELAY]"
+ },
+ { "send-lf", 0, 0, G_OPTION_ARG_NONE, &send_lf_flag,
+ "Send <CR><LF> (default <CR> only)",
+ NULL
+ },
+ { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose_flag,
+ "Run action with verbose logs",
+ NULL
+ },
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &version_flag,
+ "Print version",
+ NULL
+ },
+ { NULL }
+};
+
+static void
+signals_handler (int signum)
+{
+ if (loop && g_main_loop_is_running (loop)) {
+ g_printerr ("%s\n",
+ "cancelling the main loop...\n");
+ g_main_loop_quit (loop);
+ }
+}
+
+static void
+print_version_and_exit (void)
+{
+ g_print ("\n"
+ PROGRAM_NAME " " PROGRAM_VERSION "\n"
+ "Copyright (2015) Aleksander Morgado\n"
+ "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl-2.0.html>\n"
+ "This is free software: you are free to change and redistribute it.\n"
+ "There is NO WARRANTY, to the extent permitted by law.\n"
+ "\n");
+ exit (EXIT_SUCCESS);
+}
+
+static void
+at_command_ready (MMPortSerialAt *serial_at,
+ GAsyncResult *res)
+{
+ const gchar *response;
+ GError *error = NULL;
+
+ response = mm_port_serial_at_command_finish (serial_at, res, &error);
+ if (response)
+ g_print ("%s\n", response);
+ if (error) {
+ g_printerr ("%s\n", error->message);
+ g_error_free (error);
+ }
+
+ g_print ("> ");
+}
+
+static gboolean
+input_callback (GIOChannel *channel,
+ GIOCondition condition)
+{
+ GError *error = NULL;
+ GIOStatus status;
+ gchar *line = NULL;
+
+ status = g_io_channel_read_line (channel, &line, NULL, NULL, &error);
+
+ switch (status) {
+ case G_IO_STATUS_NORMAL: {
+ gsize line_len = 0;
+
+ /* remove \r\n before running as AT command */
+ line_len = strlen (line);
+ while (line_len > 0 && (line[line_len - 1] == '\r' || line[line_len - 1] == '\n')) {
+ line[line_len - 1] = '\0';
+ line_len--;
+ }
+
+ mm_port_serial_at_command (port, line, 60, FALSE, FALSE, NULL,
+ (GAsyncReadyCallback) at_command_ready, NULL);
+ g_free (line);
+ return TRUE;
+ }
+ case G_IO_STATUS_ERROR:
+ g_printerr ("error: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+
+ case G_IO_STATUS_EOF:
+ g_warning ("error: No input data available");
+ return TRUE;
+
+ case G_IO_STATUS_AGAIN:
+ return TRUE;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ return FALSE;
+}
+
+static void
+flash_ready (MMPortSerial *serial,
+ GAsyncResult *res)
+{
+ GError *error = NULL;
+
+ if (!mm_port_serial_flash_finish (serial, res, &error)) {
+ g_printerr ("error: cannot flash serial port: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("ready\n");
+ g_print ("> ");
+
+ /* Setup input reading */
+ input = g_io_channel_unix_new (STDIN_FILENO);
+ input_watch_id = g_io_add_watch (input, G_IO_IN, (GIOFunc) input_callback, NULL);
+}
+
+static void
+serial_buffer_full (void)
+{
+ g_printerr ("error: serial buffer full\n");
+}
+
+static gboolean
+start_cb (void)
+{
+ GError *error = NULL;
+ const gchar *device_name;
+
+ device_name = device_str;
+ if (g_str_has_prefix (device_name, "/dev/"))
+ device_name += strlen ("/dev/");
+
+ g_print ("creating AT capable serial port for device '%s'...\n", device_name);
+ port = mm_port_serial_at_new (device_name, MM_PORT_SUBSYS_TTY);
+
+ /* Setup send delay */
+ if (send_delay >= 0) {
+ if (send_delay > 0)
+ g_print ("updating send delay to %" G_GINT64_FORMAT "us...\n", send_delay);
+ else
+ g_print ("disabling send delay...\n");
+ g_object_set (port, MM_PORT_SERIAL_SEND_DELAY, send_delay, NULL);
+ }
+
+ /* Setup echo removal */
+ if (no_echo_removal_flag) {
+ g_print ("disabling echo removal...\n");
+ g_object_set (port, MM_PORT_SERIAL_AT_REMOVE_ECHO, FALSE, NULL);
+ }
+
+ /* Setup spew control */
+ if (spew_control_flag) {
+ g_print ("enabling spew control...\n");
+ g_object_set (port, MM_PORT_SERIAL_SPEW_CONTROL, TRUE, NULL);
+ }
+
+ /* Setup LF */
+ if (send_lf_flag) {
+ g_print ("enabling LF...\n");
+ g_object_set (port, MM_PORT_SERIAL_AT_SEND_LF, TRUE, NULL);
+ }
+
+ /* Set common response parser */
+ mm_port_serial_at_set_response_parser (MM_PORT_SERIAL_AT (port),
+ mm_serial_parser_v1_parse,
+ mm_serial_parser_v1_new (),
+ mm_serial_parser_v1_destroy);
+
+ /* Try to open the port... */
+ g_print ("opening serial port...\n");
+ if (!mm_port_serial_open (MM_PORT_SERIAL (port), &error)) {
+ g_printerr ("error: cannot open serial port: %s\n", error->message);
+ exit (EXIT_FAILURE);
+ }
+
+ /* Warn on full buffer by default */
+ g_signal_connect (port, "buffer-full", G_CALLBACK (serial_buffer_full), NULL);
+
+ /* If we set FLASH_OK to FALSE, the flashing operation does nothing */
+ if (no_flash_flag) {
+ g_print ("disabling serial port flash...\n");
+ g_object_set (port, MM_PORT_SERIAL_FLASH_OK, FALSE, NULL);
+ } else
+ g_print ("flashing serial port...\n");
+ mm_port_serial_flash (MM_PORT_SERIAL (port),
+ 100,
+ FALSE,
+ (GAsyncReadyCallback) flash_ready,
+ NULL);
+ return G_SOURCE_REMOVE;
+}
+
+void
+_mm_log (gpointer obj,
+ const gchar *module,
+ const gchar *loc,
+ const gchar *func,
+ guint32 level,
+ const gchar *fmt,
+ ...)
+{
+ va_list args;
+ g_autofree gchar *msg = NULL;
+ const gchar *level_str = NULL;
+
+ if (!verbose_flag)
+ return;
+
+ switch (level) {
+ case MM_LOG_LEVEL_DEBUG:
+ level_str = "debug";
+ break;
+ case MM_LOG_LEVEL_WARN:
+ level_str = "warning";
+ break;
+ case MM_LOG_LEVEL_INFO:
+ level_str = "info";
+ break;
+ case MM_LOG_LEVEL_ERR:
+ level_str = "error";
+ break;
+ default:
+ break;
+ }
+
+ va_start (args, fmt);
+ msg = g_strdup_vprintf (fmt, args);
+ va_end (args);
+ g_print ("[%s] %s\n", level_str ? level_str : "unknown", msg);
+}
+
+int main (int argc, char **argv)
+{
+ GOptionContext *context;
+
+ setlocale (LC_ALL, "");
+
+ /* Setup option context, process it and destroy it */
+ context = g_option_context_new ("- ModemManager TTY testing");
+ g_option_context_add_main_entries (context, main_entries, NULL);
+ g_option_context_parse (context, &argc, &argv, NULL);
+ g_option_context_free (context);
+
+ if (version_flag)
+ print_version_and_exit ();
+
+ /* No device path given? */
+ if (!device_str) {
+ g_printerr ("error: no device path specified\n");
+ exit (EXIT_FAILURE);
+ }
+
+ /* Setup signals */
+ signal (SIGINT, signals_handler);
+ signal (SIGHUP, signals_handler);
+ signal (SIGTERM, signals_handler);
+
+ /* Setup main loop and shedule start in idle */
+ loop = g_main_loop_new (NULL, FALSE);
+ g_idle_add ((GSourceFunc)start_cb, NULL);
+ g_main_loop_run (loop);
+
+ /* Cleanup */
+ g_main_loop_unref (loop);
+ if (port) {
+ if (mm_port_serial_is_open (MM_PORT_SERIAL (port)))
+ mm_port_serial_close (MM_PORT_SERIAL (port));
+ g_object_unref (port);
+ }
+ if (input)
+ g_io_channel_unref (input);
+ return 0;
+}
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 00000000..af87c73b
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,3 @@
+SUBDIRS = . tests
+
+EXTRA_DIST = test-modemmanager-service.py
diff --git a/tools/test-modemmanager-service.py b/tools/test-modemmanager-service.py
new file mode 100755
index 00000000..2ca18b9f
--- /dev/null
+++ b/tools/test-modemmanager-service.py
@@ -0,0 +1,490 @@
+#!/usr/bin/env python3
+# -*- Mode: python; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+from __future__ import print_function
+
+import gi
+gi.require_version('ModemManager', '1.0')
+from gi.repository import GLib, ModemManager
+import argparse
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+import random
+import collections
+
+mainloop = GLib.MainLoop()
+
+#########################################################
+IFACE_DBUS = 'org.freedesktop.DBus'
+
+class UnknownInterfaceException(dbus.DBusException):
+ def __init__(self, *args, **kwargs):
+ self._dbus_error_name = '{}.UnknownInterface'.format(IFACE_DBUS)
+ super().__init__(*args, **kwargs)
+
+class UnknownPropertyException(dbus.DBusException):
+ def __init__(self, *args, **kwargs):
+ self._dbus_error_name = '{}.UnknownProperty'.format(IFACE_DBUS)
+ super().__init__(*args, **kwargs)
+
+class MobileEquipmentException(dbus.DBusException):
+ _dbus_error_name = '{}.Error.MobileEquipment'.format(IFACE_DBUS)
+ def __init__(self, *args, **kwargs):
+ equipment_error_num = kwargs.pop('equipment_error', None)
+ if equipment_error_num is not None:
+ equipment_error_except = ModemManager.MobileEquipmentError(equipment_error_num)
+ self._dbus_error_name = '{}.Error.MobileEquipment.{}'.format(IFACE_DBUS, equipment_error_except.value_nick)
+ super().__init__(*args, **kwargs)
+
+def log(msg):
+ if log_file:
+ try:
+ log_file.write(msg + "\n")
+ log_file.flush()
+ except Exception:
+ pass
+ else:
+ print(msg)
+
+def to_path_array(src):
+ array = dbus.Array([], signature=dbus.Signature('o'))
+ for o in src:
+ array.append(to_path(o))
+ return array
+
+def to_path(src):
+ if src:
+ return dbus.ObjectPath(src.path)
+ return dbus.ObjectPath("/")
+
+class ExportedObj(dbus.service.Object):
+
+ DBusInterface = collections.namedtuple('DBusInterface', ['dbus_iface', 'get_props_func', 'set_props_func', 'prop_changed_func'])
+
+ def __init__(self, bus, object_path):
+ super(ExportedObj, self).__init__(bus, object_path)
+ self._bus = bus
+ self.path = object_path
+ self.__ensure_dbus_ifaces()
+ log("Will add object with path '%s' to object manager" % object_path)
+ object_manager.add_object(self)
+
+ def __ensure_dbus_ifaces(self):
+ if not hasattr(self, '_ExportedObj__dbus_ifaces'):
+ self.__dbus_ifaces = {}
+
+ def add_dbus_interface(self, dbus_iface, get_props_func, set_props_func, prop_changed_func):
+ self.__ensure_dbus_ifaces()
+ self.__dbus_ifaces[dbus_iface] = ExportedObj.DBusInterface(dbus_iface, get_props_func, set_props_func, prop_changed_func)
+
+ def __dbus_interface_get(self, dbus_iface):
+ if dbus_iface not in self.__dbus_ifaces:
+ raise UnknownInterfaceException()
+ return self.__dbus_ifaces[dbus_iface]
+
+ def _dbus_property_get(self, dbus_iface, propname=None):
+ props = self.__dbus_interface_get(dbus_iface).get_props_func()
+ if propname is None:
+ return props
+ if propname not in props:
+ raise UnknownPropertyException()
+ return props[propname]
+
+ def _dbus_property_set(self, dbus_iface, propname, value):
+ props = self.__dbus_interface_get(dbus_iface).get_props_func()
+
+ try:
+ if props[propname] == value:
+ return
+ except KeyError:
+ raise UnknownPropertyException()
+
+ if self.__dbus_interface_get(dbus_iface).set_props_func is not None:
+ self.__dbus_interface_get(dbus_iface).set_props_func(propname, value)
+ self._dbus_property_notify(dbus_iface, propname)
+
+ def _dbus_property_notify(self, dbus_iface, propname):
+ prop = self._dbus_property_get(dbus_iface, propname)
+ self.__dbus_interface_get(dbus_iface).prop_changed_func(self, {propname: prop})
+ ExportedObj.PropertiesChanged(self, dbus_iface, {propname: prop}, [])
+
+ @dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as')
+ def PropertiesChanged(self, iface, changed, invalidated):
+ pass
+
+ @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='s', out_signature='a{sv}')
+ def GetAll(self, dbus_iface):
+ return self._dbus_property_get(dbus_iface)
+
+ @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ss', out_signature='v')
+ def Get(self, dbus_iface, name):
+ return self._dbus_property_get(dbus_iface, name)
+
+ @dbus.service.method(dbus_interface=dbus.PROPERTIES_IFACE, in_signature='ssv', out_signature='')
+ def Set(self, dbus_iface, name, value):
+ return self._dbus_property_set(dbus_iface, name, value)
+
+ def get_managed_ifaces(self):
+ my_ifaces = {}
+ for iface in self.__dbus_ifaces:
+ my_ifaces[iface] = self.__dbus_ifaces[iface].get_props_func()
+ return self.path, my_ifaces
+
+
+###################################################################
+IFACE_SIM = 'org.freedesktop.ModemManager1.Sim'
+
+PS_IMSI = "Imsi"
+PS_OPERATOR_IDENTIFIER = "OperatorIdentifier"
+PS_OPERATOR_NAME = "OperatorName"
+PS_SIM_IDENTIFIER = "SimIdentifier"
+
+class Sim(ExportedObj):
+ def __init__(self, bus, counter, iccid, modem):
+ object_path = "/org/freedesktop/ModemManager1/SIM/%d" % counter
+ self.iccid = iccid
+ self.modem = modem
+
+ self.add_dbus_interface(IFACE_SIM, self.__get_props, None, Sim.PropertiesChanged)
+ super(Sim, self).__init__(bus, object_path)
+
+ # Properties interface
+ def __get_props(self):
+ props = {}
+ props[PS_IMSI] = "Imsi_1"
+ props[PS_OPERATOR_IDENTIFIER] = "OperatorIdentifier_1"
+ props[PS_OPERATOR_NAME] = "OperatorName_1"
+ props[PS_SIM_IDENTIFIER] = self.iccid
+ return props
+
+ # methods
+ @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='ss', out_signature='')
+ def ChangePin(self, old_pin, new_pin):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='sb', out_signature='ao')
+ def EnablePin(self, pin, enabled):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='s', out_signature='')
+ def SendPin(self, pin):
+ if self.modem.equipmentError is not None:
+ raise MobileEquipmentException(equipment_error=self.modem.equipmentError)
+ self.modem.unlock()
+
+ @dbus.service.method(dbus_interface=IFACE_SIM, in_signature='ss', out_signature='')
+ def SendPuk(self, puk, pin):
+ self.modem.unlock()
+
+ # signals
+ @dbus.service.signal(IFACE_SIM, signature='a{sv}')
+ def PropertiesChanged(self, changed):
+ pass
+
+
+###################################################################
+IFACE_MODEM = 'org.freedesktop.ModemManager1.Modem'
+
+PM_SIM = "Sim"
+PM_BEARERS = "Bearers"
+PM_SUPPORTED_CAPABILITIES = "SupportedCapabilities"
+PM_CURRENT_CAPABILITIES = "CurrentCapabilities"
+PM_MAX_BEARERS = "MaxBearers"
+PM_MAX_ACTIVE_BEARERS = "MaxActiveBearers"
+PM_MANUFACTURER = "Manufacturer"
+PM_MODEL = "Model"
+PM_REVISION = "Revision"
+PM_DEVICE_IDENTIFIER = "DeviceIdentifier"
+PM_DEVICE = "Device"
+PM_DRIVERS = "Drivers"
+PM_PLUGIN = "Plugin"
+PM_PRIMARY_PORT = "PrimaryPort"
+PM_PORTS = "Ports"
+PM_EQUIPMENT_IDENTIFIER = "EquipmentIdentifier"
+PM_UNLOCK_REQUIRED = "UnlockRequired"
+PM_UNLOCK_RETRIES = "UnlockRetries"
+PM_STATE = "State"
+PM_STATE_FAILED_REASON = "StateFailedReason"
+PM_ACCESS_TECHNOLOGIES = "AccessTechnologies"
+PM_SIGNAL_QUALITY = "SignalQuality"
+PM_OWN_NUMBERS = "OwnNumbers"
+PM_POWER_STATE = "PowerState"
+PM_SUPPORTED_MODES = "SupportedModes"
+PM_CURRENT_MODES = "CurrentModes"
+PM_SUPPORTED_BANDS = "SupportedBands"
+PM_CURRENT_BANDS = "CurrentBands"
+PM_SUPPORTED_IP_FAMILIES = "SupportedIpFamilies"
+
+class Modem(ExportedObj):
+ counter = 0
+
+ def __init__(self, bus, add_sim, iccid):
+ object_path = "/org/freedesktop/ModemManager1/Modem/%d" % Modem.counter
+ self.sim_object = None
+ if add_sim:
+ self.sim_object = Sim(bus, Modem.counter, iccid, self)
+ self.sim_path = to_path(self.sim_object)
+ self.equipmentError = None
+ self.reset_status = True
+ self.reset_status_clear = False
+
+ self.__props = self.__init_default_props()
+
+ Modem.counter = Modem.counter + 1
+
+ self.add_dbus_interface(IFACE_MODEM, self.__get_props, self.__set_prop, Modem.PropertiesChanged)
+ super(Modem, self).__init__(bus, object_path)
+
+ # Properties interface
+ def __init_default_props(self):
+ props = {}
+ props[PM_SIM] = dbus.ObjectPath(self.sim_path)
+ props[PM_DEVICE] = dbus.String("/fake/path")
+ props[PM_UNLOCK_REQUIRED] = dbus.UInt32(ModemManager.ModemLock.NONE)
+ props[PM_STATE] = dbus.Int32(ModemManager.ModemState.UNKNOWN)
+ props[PM_STATE_FAILED_REASON] = dbus.UInt32(ModemManager.ModemStateFailedReason.UNKNOWN)
+ # Not already used properties
+ #props[PM_BEARERS] = None
+ #props[PM_SUPPORTED_CAPABILITIES] = None
+ #props[PM_CURRENT_CAPABILITIES] = None
+ #props[PM_MAX_BEARERS] = None
+ #props[PM_MAX_ACTIVE_BEARERS] = None
+ #props[PM_MANUFACTURER] = None
+ #props[PM_MODEL] = None
+ #props[PM_REVISION] = None
+ #props[PM_DEVICE_IDENTIFIER] = None
+ #props[PM_DRIVERS] = None
+ #props[PM_PLUGIN] = None
+ #props[PM_PRIMARY_PORT] = None
+ #props[PM_PORTS] = None
+ #props[PM_EQUIPMENT_IDENTIFIER] = None
+ #props[PM_UNLOCK_RETRIES] = dbus.UInt32(0)
+ #props[PM_ACCESS_TECHNOLOGIES] = None
+ #props[PM_SIGNAL_QUALITY] = None
+ #props[PM_OWN_NUMBERS] = None
+ #props[PM_POWER_STATE] = None
+ #props[PM_SUPPORTED_MODES] = None
+ #props[PM_CURRENT_MODES] = None
+ #props[PM_SUPPORTED_BANDS] = None
+ #props[PM_CURRENT_BANDS] = None
+ #props[PM_SUPPORTED_IP_FAMILIES] = None
+ return props
+
+ def __get_props(self):
+ return self.__props
+
+ def __set_prop(self, name, value):
+ try:
+ self.__props[name] = value
+ except KeyError:
+ pass
+
+ def unlock(self):
+ self._dbus_property_set(IFACE_MODEM, PM_UNLOCK_REQUIRED , dbus.UInt32(ModemManager.ModemLock.NONE))
+
+ # methods
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='b', out_signature='')
+ def Enable(self, enable):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='', out_signature='ao')
+ def ListBearers(self):
+ return None
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='a{sv}', out_signature='o')
+ def CreateBearer(self, properties):
+ return None
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='o', out_signature='')
+ def DeleteBearer(self, bearer):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='', out_signature='')
+ def Reset(self):
+ if not self.reset_status:
+ if self.reset_status_clear:
+ self.reset_status = True
+ self.reset_status_clear = False
+
+ raise Exception("Fake reset exception")
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='s', out_signature='')
+ def FactoryReset(self, code):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='u', out_signature='')
+ def SetPowerState(self, state):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='u', out_signature='')
+ def SetCurrentCapabilities(self, capabilites):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='(uu)', out_signature='')
+ def SetCurrentModes(self, modes):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='au', out_signature='')
+ def SetCurrentBands(self, bands):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MODEM, in_signature='su', out_signature='s')
+ def Command(self, cmd, timeout):
+ return None
+
+ # signals
+ @dbus.service.signal(IFACE_MODEM, signature='a{sv}')
+ def PropertiesChanged(self, changed):
+ pass
+
+ @dbus.service.signal(IFACE_MODEM, signature='iiu')
+ def StateChanged(self, old_state, new_state, reason):
+ pass
+
+
+###################################################################
+IFACE_OBJECT_MANAGER = 'org.freedesktop.DBus.ObjectManager'
+
+PATH_OBJECT_MANAGER = '/org/freedesktop/ModemManager1'
+
+IFACE_TEST = 'org.freedesktop.ModemManager1.LibmmGlibTest'
+IFACE_MM = 'org.freedesktop.ModemManager1'
+
+class ObjectManager(dbus.service.Object):
+ def __init__(self, bus, object_path):
+ super(ObjectManager, self).__init__(bus, object_path)
+ self.objs = []
+ self.bus = bus
+ self.modem = None
+
+ @dbus.service.method(dbus_interface=IFACE_OBJECT_MANAGER,
+ in_signature='', out_signature='a{oa{sa{sv}}}',
+ sender_keyword='sender')
+ def GetManagedObjects(self, sender=None):
+ managed_objects = {}
+ for obj in self.objs:
+ name, ifaces = obj.get_managed_ifaces()
+ managed_objects[name] = ifaces
+ return managed_objects
+
+ def add_object(self, obj):
+ self.objs.append(obj)
+ name, ifaces = obj.get_managed_ifaces()
+ self.InterfacesAdded(name, ifaces)
+
+ def remove_object(self, obj):
+ self.objs.remove(obj)
+ name, ifaces = obj.get_managed_ifaces()
+ self.InterfacesRemoved(name, ifaces.keys())
+
+ @dbus.service.signal(IFACE_OBJECT_MANAGER, signature='oa{sa{sv}}')
+ def InterfacesAdded(self, name, ifaces):
+ pass
+
+ @dbus.service.signal(IFACE_OBJECT_MANAGER, signature='oas')
+ def InterfacesRemoved(self, name, ifaces):
+ pass
+
+ # ModemManager methods
+ @dbus.service.method(dbus_interface=IFACE_MM, in_signature='', out_signature='')
+ def ScanDevices(self):
+ pass
+
+ @dbus.service.method(dbus_interface=IFACE_MM, in_signature='s', out_signature='')
+ def SetLogging(self, logging):
+ pass
+
+ # Testing methods
+ @dbus.service.method(IFACE_TEST, in_signature='', out_signature='')
+ def Quit(self):
+ mainloop.quit()
+
+ @dbus.service.method(IFACE_TEST, in_signature='bs', out_signature='o')
+ def AddModem(self, add_sim, iccid):
+ self.modem = Modem(self.bus, add_sim, iccid)
+ return dbus.ObjectPath(self.modem.path)
+
+ @dbus.service.method(IFACE_TEST, in_signature='iiu', out_signature='')
+ def EmitStateChanged(self, old_state, new_state, reason):
+ if self.modem is not None:
+ self.modem.StateChanged(old_state, new_state, reason)
+
+ @dbus.service.method(IFACE_TEST, in_signature='ub', out_signature='')
+ def SetMobileEquipmentError(self, error, clear):
+ if self.modem is not None:
+ if clear:
+ self.modem.equipmentError = None
+ else:
+ self.modem.equipmentError = error
+
+ @dbus.service.method(IFACE_TEST, in_signature='bb', out_signature='')
+ def SetResetStatus(self, status, clear):
+ if self.modem is not None:
+ self.modem.reset_status = status
+ self.modem.reset_status_clear = clear
+
+ @dbus.service.method(dbus_interface=IFACE_TEST, in_signature='', out_signature='')
+ def Restart(self):
+ bus.release_name("org.freedesktop.ModemManager1")
+ bus.request_name("org.freedesktop.ModemManager1")
+
+###################################################################
+def stdin_cb(io, condition):
+ mainloop.quit()
+
+def quit_cb(user_data):
+ mainloop.quit()
+
+def main():
+ parser = argparse.ArgumentParser(description="ModemManager dbus interface stub utility")
+ parser.add_argument("-f", "--log-file", help="Path of a file to log things into")
+
+ cfg = parser.parse_args()
+
+ global log_file
+
+ if cfg.log_file:
+ try:
+ log_file = open(cfg.log_file, "w")
+ except Exception:
+ log_file = None
+ else:
+ log_file = None
+
+ log("Starting mainloop")
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ random.seed()
+
+ global object_manager, bus
+
+ bus = dbus.SessionBus()
+ log("Creating object manager for /org/freedesktop/ModemManager1")
+ object_manager = ObjectManager(bus, "/org/freedesktop/ModemManager1")
+
+ log("Requesting name org.freedesktop.ModemManager1")
+ if not bus.request_name("org.freedesktop.ModemManager1"):
+ log("Unable to acquire the DBus name")
+ sys.exit(1)
+
+ # Watch stdin; if it closes, assume our parent has crashed, and exit
+ id1 = GLib.io_add_watch(0, GLib.IOCondition.HUP, stdin_cb)
+
+ log("Starting the main loop")
+ try:
+ mainloop.run()
+ except (Exception, KeyboardInterrupt):
+ pass
+
+ GLib.source_remove(id1)
+
+ log("Ending the stub")
+ if log_file:
+ log_file.close()
+ sys.exit(0)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/tests/Makefile.am b/tools/tests/Makefile.am
new file mode 100644
index 00000000..f84a8fec
--- /dev/null
+++ b/tools/tests/Makefile.am
@@ -0,0 +1,49 @@
+include $(top_srcdir)/gtester.make
+
+################################################################################
+# common
+################################################################################
+
+AM_CFLAGS = \
+ $(WARN_CFLAGS) \
+ $(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 \
+ $(NULL)
+
+LDADD = \
+ $(top_builddir)/libmm-glib/libmm-glib.la \
+ $(NULL)
+
+AM_LDFLAGS = \
+ $(WARN_LDFLAGS) \
+ $(MM_LIBS) \
+ $(MM_LDFLAGS) \
+ $(NULL)
+
+noinst_PROGRAMS = test-stub
+test_stub_CPPFLAGS = \
+ -DTEST_SERVICES=\""$(abs_top_builddir)/tools/tests/services"\" \
+ $(NULL)
+
+# only run the test if introspection was enabled
+if HAVE_INTROSPECTION
+TEST_PROGS += $(noinst_PROGRAMS)
+endif
+
+test-wrapper.sh: test-wrapper.sh.in
+ @sed \
+ -e 's|@abs_top_builddir[@]|$(abs_top_builddir)|g' \
+ -e 's|@abs_top_srcdir[@]|$(abs_top_srcdir)|g' \
+ $< >$@
+ @chmod +x $@
+
+BUILT_SOURCES = test-wrapper.sh
+CLEANFILES = test-wrapper.sh
+
+EXTRA_DIST += test-wrapper.sh.in services/org.freedesktop.ModemManager1.service.in
diff --git a/tools/tests/meson.build b/tools/tests/meson.build
new file mode 100644
index 00000000..deed6c3e
--- /dev/null
+++ b/tools/tests/meson.build
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+test_conf = {
+ 'abs_top_builddir': build_root,
+ 'abs_top_srcdir': source_root,
+}
+
+subdir('services')
+
+test_wrapper = configure_file(
+ input: 'test-wrapper.sh.in',
+ output: '@BASENAME@',
+ configuration: test_conf,
+)
+
+# common
+if enable_gir
+ test_unit = 'test-stub'
+
+ exe = executable(
+ test_unit,
+ test_unit + '.c',
+ include_directories: [top_inc, src_inc],
+ dependencies: libmm_glib_dep,
+ c_args: '-DTEST_SERVICES="@0@"'.format(meson.current_build_dir() / 'services'),
+ )
+
+ test(test_unit, exe)
+endif
diff --git a/tools/tests/services/meson.build b/tools/tests/services/meson.build
new file mode 100644
index 00000000..a17a471d
--- /dev/null
+++ b/tools/tests/services/meson.build
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2021 Iñigo Martinez <inigomartinez@gmail.com>
+
+configure_file(
+ input: 'org.freedesktop.ModemManager1.service.in',
+ output: '@BASENAME@',
+ configuration: test_conf,
+)
diff --git a/tools/tests/services/org.freedesktop.ModemManager1.service.in b/tools/tests/services/org.freedesktop.ModemManager1.service.in
new file mode 100644
index 00000000..f6113d1f
--- /dev/null
+++ b/tools/tests/services/org.freedesktop.ModemManager1.service.in
@@ -0,0 +1,3 @@
+[D-BUS Service]
+Name=org.freedesktop.ModemManager1
+Exec=@abs_top_builddir@/tools/tests/test-wrapper.sh
diff --git a/tools/tests/test-stub.c b/tools/tests/test-stub.c
new file mode 100644
index 00000000..b88b2a34
--- /dev/null
+++ b/tools/tests/test-stub.c
@@ -0,0 +1,459 @@
+/* -*- 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) 2020 Frederic Martinsons <frederic.martinsons@sigfox.com>
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <libmm-glib.h>
+#include <ModemManager-names.h>
+#define MM_LOG_NO_OBJECT
+#include <mm-log-test.h>
+
+#define MM_TEST_IFACE_NAME "org.freedesktop.ModemManager1.LibmmGlibTest"
+
+typedef struct {
+ GMainLoop *loop;
+ MMManager *mm_manager;
+ GDBusConnection *gdbus_connection;
+ GDBusProxy *mm_proxy;
+ GDBusProxy *mm_modem_prop_proxy;
+ GTestDBus *test_bus;
+ gchar *modem_object_path;
+ guint timeout_id;
+ MMSim *sim;
+ gboolean pin_error;
+} TestData;
+
+static void
+setup (TestData **ptdata,
+ gconstpointer data)
+{
+ GError *error = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData *)g_malloc0 (sizeof(TestData));
+ *ptdata = tdata;
+
+ tdata->loop = g_main_loop_new (NULL, FALSE);
+ g_assert_nonnull (tdata->loop);
+
+ tdata->test_bus = g_test_dbus_new (G_TEST_DBUS_NONE);
+ g_assert_nonnull (tdata->test_bus);
+
+ g_test_dbus_add_service_dir (tdata->test_bus, TEST_SERVICES);
+ g_test_dbus_up (tdata->test_bus);
+
+ /* Grab a proxy to the fake NM service to trigger tests */
+ tdata->mm_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ MM_DBUS_SERVICE,
+ MM_DBUS_PATH,
+ MM_TEST_IFACE_NAME,
+ NULL, &error);
+ g_assert_no_error (error);
+
+ tdata->gdbus_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ g_assert_no_error (error);
+
+ tdata->mm_manager = mm_manager_new_sync (tdata->gdbus_connection,
+ G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (tdata->mm_manager);
+}
+
+static void
+teardown (TestData **ptdata,
+ gconstpointer data)
+{
+ TestData *tdata = NULL;
+
+ tdata = *ptdata;
+ g_clear_object (&tdata->mm_modem_prop_proxy);
+ g_clear_object (&tdata->mm_proxy);
+ g_clear_object (&tdata->mm_manager);
+ g_clear_object (&tdata->gdbus_connection);
+ g_test_dbus_down (tdata->test_bus);
+ g_clear_object (&tdata->test_bus);
+ g_main_loop_unref (tdata->loop);
+ g_free (tdata);
+}
+
+static gboolean
+loop_timeout_cb (gpointer user_data)
+{
+ mm_err ("Timeout has elapsed");
+ g_assert_not_reached ();
+ return G_SOURCE_REMOVE;
+}
+
+static void
+run_loop_for_ms (TestData *tdata,
+ guint32 timeout)
+{
+ mm_info ("Run loop for %u ms", timeout);
+ tdata->timeout_id = g_timeout_add (timeout, loop_timeout_cb, tdata);
+ g_main_loop_run (tdata->loop);
+}
+
+static void
+stop_loop (TestData *tdata)
+{
+ if (tdata->timeout_id) {
+ g_source_remove (tdata->timeout_id);
+ tdata->timeout_id = 0;
+ }
+ mm_info ("Stop the loop");
+ g_main_loop_quit (tdata->loop);
+}
+
+static void
+add_modem_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GVariant *dbus_return = NULL;
+ GVariant *obj_path_variant = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info ("AddModem DBus call completed");
+ dbus_return = g_dbus_proxy_call_finish (tdata->mm_proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (dbus_return);
+ g_assert_cmpstr (g_variant_get_type_string (dbus_return), == , "(o)");
+
+ obj_path_variant = g_variant_get_child_value (dbus_return, 0);
+ tdata->modem_object_path = g_variant_dup_string (obj_path_variant, NULL);
+
+ g_assert_null (tdata->mm_modem_prop_proxy);
+ tdata->mm_modem_prop_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ NULL,
+ MM_DBUS_SERVICE,
+ tdata->modem_object_path,
+ "org.freedesktop.DBus.Properties",
+ NULL, &error);
+
+ g_assert_no_error (error);
+ g_assert_nonnull (tdata->mm_modem_prop_proxy);
+
+ g_variant_unref (dbus_return);
+ g_variant_unref (obj_path_variant);
+
+ stop_loop (tdata);
+}
+
+static gchar*
+add_modem (TestData *tdata,
+ gboolean add_sim,
+ const gchar *iccid)
+{
+ g_dbus_proxy_call (tdata->mm_proxy,
+ "AddModem",
+ g_variant_new ("(bs)", add_sim, iccid),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 1000,
+ NULL,
+ add_modem_completion_cb,
+ tdata);
+
+ run_loop_for_ms (tdata, 1000);
+ return tdata->modem_object_path;
+}
+
+static void
+emit_state_changed_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GVariant *dbus_return = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info ("EmitStateChanged DBus call completed");
+ dbus_return = g_dbus_proxy_call_finish (tdata->mm_proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (dbus_return);
+ g_variant_unref (dbus_return);
+
+ stop_loop (tdata);
+}
+
+static void
+set_modem_state (TestData *tdata,
+ MMModemState state,
+ MMModemStateFailedReason reason)
+{
+ GError *error = NULL;
+ GVariant *ret = NULL;
+ GVariant *old_state_variant = NULL;
+ gint old_state = 0;
+
+ g_assert_nonnull (tdata->mm_modem_prop_proxy);
+
+ /* Get current state */
+ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Get",
+ g_variant_new ("(ss)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_STATE),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_get (ret, "(v)", &old_state_variant);
+ old_state = g_variant_get_int32 (old_state_variant);
+ g_variant_unref (ret);
+ g_variant_unref (old_state_variant);
+
+ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new ("(ssv)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_STATE,
+ g_variant_new_int32 (state)),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_unref (ret);
+
+ ret = g_dbus_proxy_call_sync (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new ("(ssv)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_STATEFAILEDREASON,
+ g_variant_new_uint32 (reason)),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ -1,
+ NULL,
+ &error);
+
+ g_assert_no_error (error);
+ g_variant_unref (ret);
+
+ /* Emit state change signal */
+ g_dbus_proxy_call (tdata->mm_proxy,
+ "EmitStateChanged",
+ g_variant_new ("(iiu)", old_state, state, reason),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 1000,
+ NULL,
+ emit_state_changed_completion_cb,
+ tdata);
+
+ run_loop_for_ms (tdata, 1000);
+}
+
+static void
+set_modem_unlock_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ GVariant *dbus_return = NULL;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info ("org.freedesktop.DBus.Properties.Set DBus call completed");
+ dbus_return = g_dbus_proxy_call_finish (tdata->mm_modem_prop_proxy, res, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (dbus_return);
+ g_variant_unref (dbus_return);
+
+ stop_loop (tdata);
+}
+
+static void
+set_modem_unlock (TestData *tdata,
+ MMModemLock lock_state)
+{
+ g_assert_nonnull (tdata->mm_modem_prop_proxy);
+
+ g_dbus_proxy_call (tdata->mm_modem_prop_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new ("(ssv)",
+ MM_DBUS_INTERFACE_MODEM,
+ MM_MODEM_PROPERTY_UNLOCKREQUIRED,
+ g_variant_new_uint32 (lock_state)),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 1000,
+ NULL,
+ set_modem_unlock_completion_cb,
+ tdata);
+
+ run_loop_for_ms (tdata, 1000);
+}
+
+static void
+set_modem_equipment_error (TestData *tdata,
+ MMMobileEquipmentError equipmentError,
+ gboolean clear)
+{
+ GError *error = NULL;
+ GVariant *ret = NULL;
+
+ ret = g_dbus_proxy_call_sync (tdata->mm_proxy,
+ "SetMobileEquipmentError",
+ g_variant_new ("(ub)", equipmentError, clear),
+ G_DBUS_CALL_FLAGS_NO_AUTO_START,
+ 3000,
+ NULL,
+ &error);
+ g_assert_no_error (error);
+ g_variant_unref (ret);
+}
+
+static void
+test_modem_interface (TestData **ptdata,
+ gconstpointer data)
+{
+ TestData *tdata = NULL;
+ GDBusObject *modem_object = NULL;
+ MMModem *mm_modem = NULL;
+ g_autofree gchar *modem_path = NULL;
+
+ tdata = *ptdata;
+ /* Add a modem object (with no sim attached) */
+ modem_path = add_modem (tdata, FALSE, "");
+ modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (tdata->mm_manager), modem_path);
+ g_assert_nonnull (modem_object);
+
+ mm_modem = mm_object_get_modem (MM_OBJECT (modem_object));
+ g_clear_object (&modem_object);
+
+ /* Check the modem states */
+ g_assert_cmpuint (mm_modem_get_state (mm_modem), ==, MM_MODEM_STATE_UNKNOWN);
+ g_assert_cmpuint (mm_modem_get_state_failed_reason (mm_modem), ==, MM_MODEM_STATE_FAILED_REASON_UNKNOWN);
+
+ /* Set new state and check that it is propagated */
+ set_modem_state (tdata, MM_MODEM_STATE_REGISTERED, MM_MODEM_STATE_FAILED_REASON_NONE);
+
+ g_assert_cmpuint (mm_modem_get_state (mm_modem), ==, MM_MODEM_STATE_REGISTERED);
+ g_assert_cmpuint (mm_modem_get_state_failed_reason (mm_modem), ==, MM_MODEM_STATE_FAILED_REASON_NONE);
+
+ g_clear_object (&mm_modem);
+}
+
+static void
+mm_sim_send_pin_completion_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gboolean ret = FALSE;
+ TestData *tdata = NULL;
+
+ tdata = (TestData*)user_data;
+
+ mm_info("SendPin DBus method call completed");
+ ret = mm_sim_send_pin_finish (tdata->sim, res, &error);
+ if (tdata->pin_error) {
+ g_assert_nonnull (error);
+ g_assert_false (ret);
+ g_clear_error (&error);
+ } else {
+ g_assert_no_error (error);
+ g_assert_true (ret);
+ }
+ stop_loop (tdata);
+}
+
+static void
+test_sim_interface (TestData **ptdata,
+ gconstpointer data)
+{
+ TestData *tdata = NULL;
+ GDBusObject *modem_object = NULL;
+ MMModem *mm_modem = NULL;
+ GError *error = NULL;
+ g_autofree gchar *modem_path = NULL;
+
+ tdata = *ptdata;
+ /* Add a modem with a sim object */
+ modem_path = add_modem (tdata, TRUE, "89330122503000800750");
+ modem_object = g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (tdata->mm_manager), modem_path);
+ g_assert_nonnull (modem_object);
+ mm_modem = mm_object_get_modem (MM_OBJECT (modem_object));
+ g_clear_object (&modem_object);
+
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE);
+ /* Lock the modem */
+ set_modem_unlock (tdata, MM_MODEM_LOCK_SIM_PIN);
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN);
+
+ tdata->sim = mm_modem_get_sim_sync (mm_modem, NULL, &error);
+ g_assert_no_error (error);
+ g_assert_nonnull (tdata->sim);
+ g_assert_cmpstr (mm_sim_get_identifier(tdata->sim), ==, "89330122503000800750");
+
+ /* Send a pin code */
+ tdata->pin_error = FALSE;
+ mm_sim_send_pin (tdata->sim, "1234", NULL, mm_sim_send_pin_completion_cb, tdata);
+ run_loop_for_ms (tdata, 1000);
+
+ /* Check that the modem has been unlocked */
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE);
+
+ /* Re lock it */
+ set_modem_unlock (tdata, MM_MODEM_LOCK_SIM_PIN);
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN);
+
+ /* Set an error that will simulate wrong pin code */
+ set_modem_equipment_error (tdata, MM_MOBILE_EQUIPMENT_ERROR_INCORRECT_PASSWORD, FALSE);
+
+ tdata->pin_error = TRUE;
+ mm_sim_send_pin (tdata->sim, "0000", NULL, mm_sim_send_pin_completion_cb, tdata);
+ run_loop_for_ms (tdata, 1000);
+
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_SIM_PIN);
+
+ /* Clear the error and retry the pin code */
+ set_modem_equipment_error (tdata, 0, TRUE);
+ tdata->pin_error = FALSE;
+ mm_sim_send_pin (tdata->sim, "1234", NULL, mm_sim_send_pin_completion_cb, tdata);
+ run_loop_for_ms (tdata, 1000);
+
+ g_assert_cmpuint (mm_modem_get_unlock_required (mm_modem), ==, MM_MODEM_LOCK_NONE);
+
+ g_clear_object (&tdata->sim);
+ g_clear_object (&mm_modem);
+}
+
+int main (int argc,
+ char **argv)
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/MM/stub/modem/interface",
+ TestData *, NULL, setup,
+ test_modem_interface,
+ teardown);
+ g_test_add ("/MM/stub/sim/interface",
+ TestData *, NULL, setup,
+ test_sim_interface,
+ teardown);
+ return g_test_run ();
+}
diff --git a/tools/tests/test-wrapper.sh.in b/tools/tests/test-wrapper.sh.in
new file mode 100644
index 00000000..d64ea4cb
--- /dev/null
+++ b/tools/tests/test-wrapper.sh.in
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+# For debugging behavior of test-modemmanager-service.py, you can modify
+# this line to add --log-file option
+GI_TYPELIB_PATH=@abs_top_builddir@/libmm-glib @abs_top_srcdir@/tools/test-modemmanager-service.py
diff --git a/uml290/Makefile.am b/uml290/Makefile.am
deleted file mode 100644
index b4fc8b57..00000000
--- a/uml290/Makefile.am
+++ /dev/null
@@ -1,10 +0,0 @@
-noinst_PROGRAMS = uml290mode
-
-uml290mode_CPPFLAGS = -I$(top_srcdir)
-
-uml290mode_LDADD = \
- $(top_builddir)/libqcdm/src/libqcdm.la \
- $(top_builddir)/libwmc/src/libwmc.la
-
-uml290mode_SOURCES = uml290mode.c
-
diff --git a/uml290/uml290mode.c b/uml290/uml290mode.c
deleted file mode 100644
index b727b41b..00000000
--- a/uml290/uml290mode.c
+++ /dev/null
@@ -1,722 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2012 Red Hat, Inc.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <fcntl.h>
-#include <termios.h>
-#include <assert.h>
-#include <unistd.h>
-
-#include "libwmc/src/utils.h"
-#include "libwmc/src/errors.h"
-#include "libwmc/src/commands.h"
-#include "libwmc/src/com.h"
-
-#include "libqcdm/src/utils.h"
-#include "libqcdm/src/errors.h"
-#include "libqcdm/src/commands.h"
-#include "libqcdm/src/com.h"
-
-static int debug = 0;
-
-static void
-print_buf (const char *detail, const char *buf, size_t len)
-{
- int i = 0, z;
- wmcbool newline = FALSE;
- char tmp[500];
- u_int32_t flen;
-
- flen = snprintf (tmp, sizeof (tmp) - 1, "%s (%zu) ", detail, len);
- fprintf (stdout, "%s", tmp);
- for (i = 0; i < len; i++) {
- fprintf (stdout, "%02x ", buf[i] & 0xFF);
- if (((i + 1) % 16) == 0) {
- fprintf (stdout, "\n");
- z = flen;
- while (z--)
- fprintf (stdout, " ");
- newline = TRUE;
- } else
- newline = FALSE;
- }
-
- if (!newline)
- fprintf (stdout, "\n");
-}
-
-static int
-com_setup (const char *port)
-{
- int ret, fd;
-
- errno = 0;
- fd = open (port, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY);
- if (fd < 0) {
- fprintf (stderr, "E: failed to open port %s\n", port);
- return -1;
- }
-
- ret = ioctl (fd, TIOCEXCL);
- if (ret) {
- fprintf (stderr, "E: failed to lock port %s\n", port);
- close (fd);
- return -1;
- }
-
- return fd;
-}
-
-/******************************************************************/
-
-static wmcbool
-wmc_send (int fd, char *inbuf, size_t inbuf_len, size_t cmd_len)
-{
- int status;
- int eagain_count = 1000;
- size_t i = 0, sendlen;
- char sendbuf[600];
-
- if (debug)
- print_buf ("\nWMC:RAW>>>", inbuf, cmd_len);
-
- /* Encapsulate the data for the device */
- sendlen = wmc_encapsulate (inbuf, cmd_len, inbuf_len, sendbuf, sizeof (sendbuf), TRUE);
- if (sendlen <= 0) {
- fprintf (stderr, "E: failed to encapsulate WMC command\n");
- return FALSE;
- }
-
- if (debug)
- print_buf ("WMC:ENC>>>", sendbuf, sendlen);
-
- while (i < sendlen) {
- errno = 0;
- status = write (fd, &sendbuf[i], 1);
- if (status < 0) {
- if (errno == EAGAIN) {
- eagain_count--;
- if (eagain_count <= 0)
- return FALSE;
- } else
- assert (errno == 0);
- } else
- i++;
-
- usleep (1000);
- }
-
- return TRUE;
-}
-
-static size_t
-wmc_wait_reply (int fd, char *buf, size_t len)
-{
- fd_set in;
- int result;
- struct timeval timeout = { 1, 0 };
- char readbuf[2048];
- ssize_t bytes_read;
- int total = 0, retries = 0;
- size_t decap_len = 0;
-
- FD_ZERO (&in);
- FD_SET (fd, &in);
- result = select (fd + 1, &in, NULL, NULL, &timeout);
- if (result != 1 || !FD_ISSET (fd, &in))
- return 0;
-
- do {
- errno = 0;
- bytes_read = read (fd, &readbuf[total], 1);
- if ((bytes_read == 0) || (errno == EAGAIN)) {
- /* Haven't gotten the async control char yet */
- if (retries > 20)
- return 0; /* 2 seconds, give up */
-
- /* Otherwise wait a bit and try again */
- usleep (100000);
- retries++;
- continue;
- } else if (bytes_read == 1) {
- wmcbool more = FALSE, success;
- size_t used = 0;
-
- total++;
- decap_len = 0;
- success = wmc_decapsulate (readbuf, total, buf, len, &decap_len, &used, &more, TRUE);
-
- if (success && !more && debug)
- print_buf ("WMC:RAW<<<", readbuf, total);
-
- /* Discard used data */
- if (used > 0) {
- total -= used;
- memmove (readbuf, &readbuf[used], total);
- }
-
- if (success && !more) {
- /* Success; we have a packet */
- break;
- }
- } else {
- /* Some error occurred */
- return 0;
- }
- } while (total < sizeof (readbuf));
-
- if (debug)
- print_buf ("WMC:DEC<<<", buf, decap_len);
-
- return decap_len;
-}
-
-static int
-wmc_set_global_mode (const char *port, u_int8_t mode)
-{
- int fd, err;
- char buf[1024];
- size_t len;
- WmcResult *result;
- size_t reply_len;
-
- fd = com_setup (port);
- if (fd < 0)
- return -1;
-
- err = wmc_port_setup (fd);
- if (err) {
- fprintf (stderr, "E: failed to set up WMC port %s: %d\n", port, err);
- goto error;
- }
-
- len = wmc_cmd_set_global_mode_new (buf, sizeof (buf), mode);
- assert (len);
-
- /* Send the command */
- if (!wmc_send (fd, buf, sizeof (buf), len)) {
- fprintf (stderr, "E: failed to send WMC global mode command\n");
- goto error;
- }
-
- reply_len = wmc_wait_reply (fd, buf, sizeof (buf));
- if (!reply_len) {
- fprintf (stderr, "E: failed to receive global mode command reply\n");
- goto error;
- }
-
- /* Parse the response into a result structure */
- result = wmc_cmd_set_global_mode_result (buf, reply_len);
- if (!result) {
- fprintf (stderr, "E: failed to parse global mode command reply\n");
- goto error;
- }
- wmc_result_unref (result);
-
- close (fd);
- return 0;
-
-error:
- close (fd);
- return -1;
-}
-
-static const char *
-wmc_get_global_mode (const char *port)
-{
- int fd, err;
- char buf[1024];
- size_t len;
- WmcResult *result;
- size_t reply_len;
- u_int8_t mode = 0;
- const char *smode = NULL;
-
- fd = com_setup (port);
- if (fd < 0)
- return NULL;
-
- err = wmc_port_setup (fd);
- if (err) {
- fprintf (stderr, "E: failed to set up WMC port %s: %d\n", port, err);
- goto error;
- }
-
- len = wmc_cmd_get_global_mode_new (buf, sizeof (buf));
- assert (len);
-
- /* Send the command */
- if (!wmc_send (fd, buf, sizeof (buf), len)) {
- fprintf (stderr, "E: failed to send WMC global mode command\n");
- goto error;
- }
-
- reply_len = wmc_wait_reply (fd, buf, sizeof (buf));
- if (!reply_len) {
- fprintf (stderr, "E: failed to receive global mode command reply\n");
- goto error;
- }
-
- /* Parse the response into a result structure */
- result = wmc_cmd_get_global_mode_result (buf, reply_len);
- if (!result) {
- fprintf (stderr, "E: failed to parse global mode command reply\n");
- goto error;
- }
- wmc_result_unref (result);
-
- wmc_result_get_u8 (result, WMC_CMD_GET_GLOBAL_MODE_ITEM_MODE, &mode);
- switch (mode) {
- case WMC_NETWORK_MODE_AUTO_CDMA:
- smode = "CDMA/EVDO";
- break;
- case WMC_NETWORK_MODE_CDMA_ONLY:
- smode = "CDMA only";
- break;
- case WMC_NETWORK_MODE_EVDO_ONLY:
- smode = "EVDO only";
- break;
- case WMC_NETWORK_MODE_AUTO_GSM:
- smode = "GSM/UMTS";
- break;
- case WMC_NETWORK_MODE_GPRS_ONLY:
- smode = "GSM/GPRS/EDGE only";
- break;
- case WMC_NETWORK_MODE_UMTS_ONLY:
- smode = "UMTS/HSPA only";
- break;
- case WMC_NETWORK_MODE_AUTO:
- smode = "Auto";
- break;
- case WMC_NETWORK_MODE_LTE_ONLY:
- smode = "LTE only";
- break;
- default:
- break;
- }
-
- close (fd);
- return smode;
-
-error:
- close (fd);
- return NULL;
-}
-
-/******************************************************************/
-
-static qcdmbool
-qcdm_send (int fd, char *buf, size_t len)
-{
- int status;
- int eagain_count = 1000;
- size_t i = 0;
-
- if (debug)
- print_buf ("DM:ENC>>>", buf, len);
-
- while (i < len) {
- errno = 0;
- status = write (fd, &buf[i], 1);
- if (status < 0) {
- if (errno == EAGAIN) {
- eagain_count--;
- if (eagain_count <= 0)
- return FALSE;
- } else
- assert (errno == 0);
- } else
- i++;
-
- usleep (1000);
- }
-
- return TRUE;
-}
-
-static size_t
-qcdm_wait_reply (int fd, char *buf, size_t len)
-{
- fd_set in;
- int result;
- struct timeval timeout = { 1, 0 };
- char readbuf[1024];
- ssize_t bytes_read;
- int total = 0, retries = 0;
- size_t decap_len = 0;
-
- FD_ZERO (&in);
- FD_SET (fd, &in);
- result = select (fd + 1, &in, NULL, NULL, &timeout);
- if (result != 1 || !FD_ISSET (fd, &in))
- return 0;
-
- do {
- errno = 0;
- bytes_read = read (fd, &readbuf[total], 1);
- if ((bytes_read == 0) || (errno == EAGAIN)) {
- /* Haven't gotten the async control char yet */
- if (retries > 20)
- return 0; /* 2 seconds, give up */
-
- /* Otherwise wait a bit and try again */
- usleep (100000);
- retries++;
- continue;
- } else if (bytes_read == 1) {
- qcdmbool more = FALSE;
- qcdmbool success;
- size_t used = 0;
-
- total++;
- decap_len = 0;
- success = dm_decapsulate_buffer (readbuf, total, buf, len, &decap_len, &used, &more);
-
- /* Discard used data */
- if (used > 0) {
- total -= used;
- memmove (readbuf, &readbuf[used], total);
- }
-
- if (success && !more) {
- /* Success; we have a packet */
- break;
- }
- } else {
- /* Some error occurred */
- return 0;
- }
- } while (total < sizeof (readbuf));
-
- if (debug)
- print_buf ("QCDM:DEC<<", buf, decap_len);
-
- return decap_len;
-}
-
-static int
-qcdm_set_hdr_pref (const char *port, u_int8_t hdrpref)
-{
- int fd, err;
- char buf[512];
- size_t len;
- QcdmResult *result;
- size_t reply_len;
-
- fd = com_setup (port);
- if (fd < 0)
- return -1;
-
- err = qcdm_port_setup (fd);
- if (err != QCDM_SUCCESS) {
- fprintf (stderr, "E: failed to set up DM port %s: %d\n", port, err);
- goto error;
- }
-
- len = qcdm_cmd_nv_set_hdr_rev_pref_new (buf, sizeof (buf), hdrpref);
- assert (len);
-
- /* Send the command */
- if (!qcdm_send (fd, buf, len)) {
- fprintf (stderr, "E: failed to send QCDM HDR pref command\n");
- goto error;
- }
-
- reply_len = qcdm_wait_reply (fd, buf, sizeof (buf));
- if (!reply_len) {
- fprintf (stderr, "E: failed to receive HDR pref command reply\n");
- goto error;
- }
-
- /* Parse the response into a result structure */
- err = QCDM_SUCCESS;
- result = qcdm_cmd_nv_set_hdr_rev_pref_result (buf, reply_len, &err);
- if (!result) {
- fprintf (stderr, "E: failed to parse HDR pref command reply: %d\n", err);
- goto error;
- }
-
- qcdm_result_unref (result);
- close (fd);
- return 0;
-
-error:
- close (fd);
- return -1;
-}
-
-static const char *
-qcdm_get_hdr_pref (const char *port)
-{
- int fd, err;
- char buf[512];
- size_t len;
- QcdmResult *result = NULL;
- size_t reply_len;
- u_int8_t pref;
- const char *spref = NULL;
-
- fd = com_setup (port);
- if (fd < 0)
- return NULL;
-
- err = qcdm_port_setup (fd);
- if (err != QCDM_SUCCESS) {
- fprintf (stderr, "E: failed to set up DM port %s: %d\n", port, err);
- goto error;
- }
-
- len = qcdm_cmd_nv_get_hdr_rev_pref_new (buf, sizeof (buf));
- assert (len > 0);
-
- /* Send the command */
- if (!qcdm_send (fd, buf, len)) {
- fprintf (stderr, "E: failed to send QCDM HDR pref command\n");
- goto error;
- }
-
- reply_len = qcdm_wait_reply (fd, buf, sizeof (buf));
- if (!reply_len) {
- fprintf (stderr, "E: failed to receive HDR pref command reply\n");
- goto error;
- }
-
- /* Parse the response into a result structure */
- err = QCDM_SUCCESS;
- result = qcdm_cmd_nv_get_hdr_rev_pref_result (buf, reply_len, &err);
- if (!result) {
- fprintf (stderr, "E: failed to parse HDR pref command reply: %d\n", err);
- goto error;
- }
-
- err = qcdm_result_get_u8 (result, QCDM_CMD_NV_GET_HDR_REV_PREF_ITEM_REV_PREF, &pref);
- if (err != QCDM_SUCCESS)
- goto error;
-
- switch (pref) {
- case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_0:
- spref = "rev0";
- break;
- case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A:
- spref = "revA";
- break;
- case QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD:
- spref = "eHRPD";
- break;
- default:
- break;
- }
-
- qcdm_result_unref (result);
- close (fd);
- return spref;
-
-error:
- if (result)
- qcdm_result_unref (result);
- close (fd);
- return NULL;
-}
-
-static int
-qcdm_set_mode (const char *port, u_int8_t mode)
-{
- int fd, err;
- char buf[512];
- size_t len;
- QcdmResult *result;
- size_t reply_len;
-
- fd = com_setup (port);
- if (fd < 0)
- return -1;
-
- err = qcdm_port_setup (fd);
- if (err != QCDM_SUCCESS) {
- fprintf (stderr, "E: failed to set up DM port %s: %d\n", port, err);
- goto error;
- }
-
- len = qcdm_cmd_control_new (buf, sizeof (buf), mode);
- assert (len);
-
- /* Send the command */
- if (!qcdm_send (fd, buf, len)) {
- fprintf (stderr, "E: failed to send QCDM Control command\n");
- goto error;
- }
-
- reply_len = qcdm_wait_reply (fd, buf, sizeof (buf));
- if (!reply_len) {
- fprintf (stderr, "E: failed to receive Control command reply\n");
- goto error;
- }
-
- /* Parse the response into a result structure */
- err = QCDM_SUCCESS;
- result = qcdm_cmd_control_result (buf, reply_len, &err);
- if (!result) {
- fprintf (stderr, "E: failed to parse Control command reply: %d\n", err);
- goto error;
- }
-
- qcdm_result_unref (result);
- close (fd);
- return 0;
-
-error:
- close (fd);
- return -1;
-}
-
-/******************************************************************/
-
-static void
-usage (const char *prog)
-{
- fprintf (stderr, "Usage: %s <WMC port> <DM port> [<mode>] [--debug]\n", prog);
- fprintf (stderr, " <mode> = lte, auto-cdma, auto, cdma, evdo, auto-gsm, gprs, umts\n");
- fprintf (stderr, " If <mode> is missing, current mode will be printed.\n\n");
-}
-
-static wmcbool
-parse_mode (const char *s,
- u_int8_t *out_mode,
- u_int8_t *out_hdrpref,
- wmcbool *out_set_evdo)
-{
- if (strcasecmp (s, "lte") == 0) {
- *out_mode = WMC_NETWORK_MODE_LTE_ONLY;
- *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD;
- *out_set_evdo = TRUE;
- return TRUE;
- }
-
- if (strcasecmp (s, "auto-cdma") == 0) {
- *out_mode = WMC_NETWORK_MODE_AUTO_CDMA;
- *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A;
- *out_set_evdo = TRUE;
- return TRUE;
- }
-
- if (strcasecmp (s, "auto") == 0) {
- *out_mode = WMC_NETWORK_MODE_AUTO;
- *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD;
- *out_set_evdo = TRUE;
- return TRUE;
- }
-
- if (strcasecmp (s, "cdma") == 0) {
- *out_mode = WMC_NETWORK_MODE_CDMA_ONLY;
- *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A;
- *out_set_evdo = TRUE;
- return TRUE;
- }
-
- if (strcasecmp (s, "evdo") == 0) {
- *out_mode = WMC_NETWORK_MODE_EVDO_ONLY;
- *out_hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_A;
- *out_set_evdo = TRUE;
- return TRUE;
- }
-
- if (strcasecmp (s, "auto-gsm") == 0) {
- *out_mode = WMC_NETWORK_MODE_AUTO_GSM;
- *out_set_evdo = FALSE;
- return TRUE;
- }
-
- if (strcasecmp (s, "gprs") == 0) {
- *out_mode = WMC_NETWORK_MODE_GPRS_ONLY;
- *out_set_evdo = FALSE;
- return TRUE;
- }
-
- if (strcasecmp (s, "umts") == 0) {
- *out_mode = WMC_NETWORK_MODE_UMTS_ONLY;
- *out_set_evdo = FALSE;
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-int
-main (int argc, char *argv[])
-{
- u_int8_t mode = WMC_NETWORK_MODE_AUTO;
- u_int8_t hdrpref = QCDM_CMD_NV_HDR_REV_PREF_ITEM_REV_PREF_EHRPD;
- const char *wmcport = argv[1];
- const char *dmport = argv[2];
- const char *msg = NULL;
- wmcbool set_evdo = FALSE;
- wmcbool set_mode = FALSE;
-
- if (argc < 3 || argc > 5) {
- usage (argv[0]);
- return 1;
- }
-
- if (argc >= 4) {
- if (strcasecmp (argv[3], "--debug") == 0)
- debug = 1;
- else {
- set_mode = parse_mode (argv[3], &mode, &hdrpref, &set_evdo);
- if (!set_mode) {
- usage (argv[0]);
- return 1;
- }
- }
-
- if (argc >= 5 && strcasecmp (argv[4], "--debug") == 0)
- debug = 1;
- }
-
- if (debug) {
- putenv ("WMC_DEBUG=1");
- putenv ("QCDM_DEBUG=1");
- }
-
- if (set_mode) {
- if (wmc_set_global_mode (wmcport, mode))
- return 1;
- if (set_evdo && qcdm_set_hdr_pref (dmport, hdrpref))
- return 1;
-
- /* Send DM reset command */
- qcdm_set_mode (dmport, QCDM_CMD_CONTROL_MODE_OFFLINE);
- sleep (2);
- qcdm_set_mode (dmport, QCDM_CMD_CONTROL_MODE_RESET);
- sleep (2);
-
- fprintf (stdout, "Success setting mode to '%s': replug your device.\n", argv[3]);
- } else {
- msg = wmc_get_global_mode (wmcport);
- fprintf (stdout, "WMC Global Mode: %s\n", msg ? msg : "(unknown)");
- msg = qcdm_get_hdr_pref (dmport);
- fprintf (stdout, "QCDM HDR Revision: %s\n", msg ? msg : "(unknown)");
- }
-
- return 0;
-}
-