Compare commits

33 Commits
Author SHA1 Message Date
SymbX d00f3ccde2 workaround to get lower device name 2026-10-15 01:33:08 +03:00
SymbX befee319b4 add ability to supply kernel driver name from udev 2026-10-15 00:10:44 +03:00
Dan Williams f2b9ab1ad7 Merge request !1384 from 'mm-1-24-2'
release: bump version to 1.24.2

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1384
2025-07-28 15:15:43 -05:00
Dan Williams 5160a4f409 release: bump version to 1.24.2
Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-07-28 15:07:18 -05:00
Dan Williams 8b51fb4385 Merge request !1383 from '1-24-suspend-bearer-timeout'
[1.24] base-manager: increase suspend timeout for each active bearer/disable unsolicited 3GPP events during quick suspend/resume

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1383
2025-07-28 14:25:36 -05:00
Dan Williams 79d495c892 base-manager: increase suspend timeout for each active bearer
Some modems can take a very long time (~20s) to reply to bearer
disconnect requests especially in marginal signal situations where
the modem must resend messages to the network after internal
timeouts. Give the modem a chance to suspend successfully if
there are active bearers.

Related: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1311#note_2938638

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-07-28 09:46:13 -05:00
Dan Williams e867a49d92 sleep-context: add ability to increase timeout
Track when the timeout was started, so that if it needs to be increased
we can calculate how long the new timeout should be. Then cancel the
original timeout and start a new one with.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-07-28 09:46:13 -05:00
Dan Williams b59666f484 broadband-modem,iface-modem: move bearer counting to interface and generalize
Allow counting more kinds of bearers than just multiplexed ones.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-07-28 09:46:13 -05:00
biemster d22bc4d402 iface-modem: disable unsolicited 3GPP events during quick suspend/resume
This introduces the "terse" mode during quick suspend/resume, right now
it is used by 3GPP interface to disable unsolicited events, but in
future it can be used by other interfaces as well.

This prevents device to wake-up from suspend for each unsolicited while
still allowing to wake up on calls or SMS messages.

Signed-off-by: biemster <biemster@gmail.com>
Signed-off-by: Bhushan Shah <bshah@kde.org>
2025-07-28 09:45:10 -05:00
Dan Williams 3a1184785b base-manager: rename manager/device shutdown to manager/device cleanup
In preparation for a "terse" mode, but also because "shutdown" was
kind of weird since we're not always shutting the modem down; sometimes
we're just putting it in low-power mode.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-07-28 09:39:46 -05:00
Dan Williams 4a3aba8928 Merge request !1349 from 'mm-1-24-mbim-multipart-fix'
[1.24] broadband-modem-qmi,broadband-modem-mbim: always use SMS_PART_INVALID_INDEX unstored parts

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1349
2025-05-15 08:42:40 -05:00
Dan Williams 19f6d15edf broadband-modem-qmi,broadband-modem-mbim: always use SMS_PART_INVALID_INDEX unstored parts
MMSmsList has logic to allow multiple unstored messages as long as
they use SMS_PART_INVALID_INDEX. These messages aren't stored so
they don't have an index.

But the MBIM and QMI modems used index 0 for unstored messages,
meaning there could only ever be one and also that multipart
messages would fail to be combined.

Let's fix that by just using SMS_PART_INVALID_INDEX for every
scenario where an unstored message comes in.

Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/979

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 1d5cc0addb)
2025-05-08 20:44:02 -05:00
Dan Williams 958bc69299 Merge request !1348 from '1-24-cherry-picks-05-08-25'
[1.24] stable cherry-picks 2025-05-08

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1348
2025-05-08 18:12:47 -05:00
Dan Williams 3827a63d33 modem-helpers: report C*REG=2 AcT 9 as NB-IOT access technology
Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 2e27b02004)
2025-05-08 16:08:28 -05:00
Dan Williams 46cc38570d broadband-modem: reload registration info after manual registration
This refreshes operator information after manual registration. QMI and
MBIM implementations return operator info along with registration info
so they handle this easily, while with AT-based devices we must run
additional commands to retrieve that after the manual registration has
finished.

Suggested by: Kirill Buksha <kirill.buksha@axians.rs>
Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/971

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit febab17f07)
2025-05-08 16:08:21 -05:00
Dan Williams dc3a7f0d3b broadband-modem: detect old Mediatek 62xx chipsets as GSM-capable
They don't necessarily reply to +WS46 but like old Motorola ESX
devices they reply to +CGMM with a known string.

Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/980

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 8cf25a97a1)
2025-05-08 16:07:58 -05:00
Dan Williams 9f0db52ed0 iface-modem-voice: recheck call state polling when call is started
When an outgoing call is created and added to the call list its state
is UNKNOWN. The call list emits the ADDED signal which MMIfaceModemVoice
listens for to check whether to begin polling call state.

But since UNKNOWN is not an in-progress call state polling isn't started.
And nothing was rechecking whether to start polling when the call state
changed to something other than UNKNOWN.

Do that so that we can track call progress as it dials out and is
answered.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 92e666e1c9)
2025-05-08 16:07:39 -05:00
Dan Williams 240a9145ad modem-helpers-cinterion: allow spaces in ^SXRAT test response
^SXRAT: (0-6), (0,2,3), (0,2,3)

Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/974

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 6b6997362b)
2025-05-08 16:07:33 -05:00
Dan Williams 018e440848 shared-fibocom: don't assume parent implements the firmware interface
MMBroadbandModemMbimMtkFibocom doesn't, so don't assert it.

Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/975

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 046b79a676)
2025-05-08 16:07:22 -05:00
Dan Williams 0c7d573969 modem-helpers: fix checking of CDMA/EVDO access technology
Missing ! for strncmp() meant the test was backwards and would
erroneously report 1xRTT and EVDOr0 when those access technologies
were not in use.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
(cherry picked from commit 9e205f4784)
2025-05-08 16:07:11 -05:00
Jan Alexander Steffens (heftig) 1ec2b86239 docs: Fix gtk-doc build
Fix a duplicate `id` and the inclusions of gdbus docs.

Signed-off-by: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
(cherry picked from commit 078a92cd5c)
2025-05-08 16:07:02 -05:00
Dan Williams 4fa4084600 Merge request !1347 from 'sleep-wait-1.24'
[1.24] Wait until device operations complete before dropping suspend inhibitor

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1347
2025-05-08 16:04:02 -05:00
Dan Williams dae00a9f2f broadband-modem: close serial ports in dispose
We want ports to be closed as early as possible; so make sure they
are closed in dispose() which is run explicitly by MMDevice when
cleaning up and removing the modem. Otherwise they could stay
open longer than we want, causing problems for suspend/resume.

This caused problems with suspend because cancelling the
MMBaseModem's cancellable triggered an idle handler that held
a reference to the modem, preventing finalize() from running
until after the suspend code was finished, thus preventing
ports from being closed and cleaned up. Closing them in
dispose() handles this.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams 3157a90c5b broadband-modem: remove invalid idle handler during dispose
base_modem_invalid_idle() needs to keep a reference to the
MMBaseModem until its done. But if the modem is cancelled during
removal right before we're trying to forget about it, keeping
that reference can prevent everything getting cleaned up by
MMBaseModem's dispose() until after the mainloop has run again
and all our removal operations are supposedly complete.

MMDevice uses an explicit dispose() trigger that mitigates
most of this (and requires it for other reasons, like breaking
reference cycles between the modem and it's various child
classes) but it's somewhat error-prone to keep the modem
around if we don't really need to.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams 34148f8e8b broadband-modem-qmi,mbim: remove redundant port closes
This will just be done immediately after by MMBaseModem's
dispose() handler.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams c146dd6094 base-manager: convert shutdown to steps
Make the call flow clearer.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams d80f98da01 base-manager,device: consolidate modem cancelling into mm_device_remove_modem()
Every use of mm_device_remove_modem() was already trying to cancel the
modem's canellable, so let's just put it there and simplify the callers.

Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams b97f864acd base-manager,sleep-monitor: wait for modem operations before sleeping
Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams 5e29ece5ec base-modem: clarify port cleanup/close behavior for serial ports
Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams e12b3adb35 base-modem: allow port teardown operation to use callbacks if needed
Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams 649350dfff port-serial: don't log force-closes if not really being forced close
Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-05-08 15:44:36 -05:00
Dan Williams 18521b6882 Merge request !1332 from 'bump-1-24-1'
build: unstable release version bump to 1.24.1

https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1332
2025-04-10 01:57:37 +00:00
Dan Williams 7fc6788737 build: unstable release version bump to 1.24.1
Signed-off-by: Dan Williams <dan@ioncontrol.co>
2025-04-09 20:43:22 -05:00
35 changed files with 1758 additions and 349 deletions
+28
View File
@@ -1,3 +1,31 @@
ModemManager 1.24.2
-------------------------------------------
This is a new stable release of ModemManager.
The most important features and changes in this release are the following:
* Core:
** Wait until bearers are disconnected before allowing system to suspend
** Fix checks for CDMA/EVDO access technology in some modems
** Detect old Mediatek 62xx-based devices as GSM-capable
** Refresh registration info after manual registration for AT-based devices
** Correctly report NB-IOT access technology
** Disable unsolicited 3GPP events during quick suspend/resume
* MBIM:
** Fix re-assembly of multi-part unstored SMS messages
* QMI:
** Fix re-assembly of multi-part unstored SMS messages
* Voice interface:
** Correctly start call state polling on some modems
* Plugins:
** fibocomm: don't assert when checking for firmware interface
** cinterion: allow spaces in ^SXRAT response
ModemManager 1.24.0 ModemManager 1.24.0
------------------------------------------- -------------------------------------------
This is a new stable release of ModemManager. This is a new stable release of ModemManager.
@@ -92,7 +92,7 @@
This object also controls any process-wide operation, such as the log This object also controls any process-wide operation, such as the log
level being used by the daemon. level being used by the daemon.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.xml"/>
</chapter> </chapter>
<chapter id="ref-dbus-object-modem"> <chapter id="ref-dbus-object-modem">
@@ -129,22 +129,22 @@
or the or the
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.ModemCdma">CDMA interface</link>. <link linkend="gdbus-org.freedesktop.ModemManager1.Modem.ModemCdma">CDMA interface</link>.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml"/> <xi:include href="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="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="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="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="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="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="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="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="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="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.CellBroadcast.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.CellBroadcast.xml"/>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml"/> <xi:include href="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="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="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="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml"/>
<!--xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Contacts.xml"/--> <!--xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Contacts.xml"/-->
</chapter> </chapter>
<chapter id="ref-dbus-object-bearer"> <chapter id="ref-dbus-object-bearer">
@@ -155,7 +155,7 @@
may expose one or more Bearer objects, which can then be used to get the may expose one or more Bearer objects, which can then be used to get the
modem into connected state. modem into connected state.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml"/>
</chapter> </chapter>
<chapter id="ref-dbus-object-sim"> <chapter id="ref-dbus-object-sim">
@@ -166,7 +166,7 @@
expose up to one SIM object, which allows SIM-specific actions such as PIN expose up to one SIM object, which allows SIM-specific actions such as PIN
unlocking. unlocking.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Sim.xml"/>
</chapter> </chapter>
<chapter id="ref-dbus-object-sms"> <chapter id="ref-dbus-object-sms">
@@ -176,7 +176,7 @@
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Messaging">Messaging interface</link> <link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Messaging">Messaging interface</link>
will export one SMS object for each SMS stored in the device. will export one SMS object for each SMS stored in the device.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml"/>
</chapter> </chapter>
<chapter id="ref-dbus-object-call"> <chapter id="ref-dbus-object-call">
@@ -186,17 +186,17 @@
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Voice">Voice interface</link> <link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Voice">Voice interface</link>
will export one Call object for each Call managed in the device. will export one Call object for each Call managed in the device.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml"/>
</chapter> </chapter>
<chapter id="ref-dbus-object-call"> <chapter id="ref-dbus-object-cbm">
<title>The <literal>/org/freedesktop/ModemManager/CBM</literal> objects</title> <title>The <literal>/org/freedesktop/ModemManager/CBM</literal> objects</title>
<para> <para>
Modems implementing the Modems implementing the
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.CellBroadcast">Cell Broadcast interface</link> <link linkend="gdbus-org.freedesktop.ModemManager1.Modem.CellBroadcast">Cell Broadcast interface</link>
will export one CBM object for each Cell Broadcast message received by the device. will export one CBM object for each Cell Broadcast message received by the device.
</para> </para>
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml"/> <xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Cbm.xml"/>
</chapter> </chapter>
</part> </part>
+5
View File
@@ -22,6 +22,9 @@ expand_content_files = [
'ModemManager-overview.xml', 'ModemManager-overview.xml',
# FIXME: workaround because only strings can be included and not custom targets (gen_docs) # 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.Bearer.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Cbm.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.CellBroadcast.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.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.Location.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml',
@@ -30,9 +33,11 @@ expand_content_files = [
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.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.ModemCdma.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.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.Simple.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.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.Sim.xml',
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml', generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml',
+1 -1
View File
@@ -3,7 +3,7 @@
project( project(
'ModemManager', 'c', 'ModemManager', 'c',
version: '1.24.0', version: '1.24.2',
license: 'GPL2', license: 'GPL2',
default_options: [ default_options: [
'buildtype=debugoptimized', 'buildtype=debugoptimized',
+53 -1
View File
@@ -24,6 +24,58 @@
/******************************************************************************/ /******************************************************************************/
static gboolean
read_sysfs_int (const gchar *path,
gint *value)
{
g_autofree gchar *contents = NULL;
gchar *end = NULL;
gint64 parsed;
if (!g_file_get_contents (path, &contents, NULL, NULL))
return FALSE;
parsed = g_ascii_strtoll (contents, &end, 10);
if (end == contents)
return FALSE;
*value = (gint) parsed;
return TRUE;
}
/* Net devices stacked by drivers that create no lower_* adjacency links (e.g. rmnet on Android vendor
* kernels) still report the parent's interface index in 'iflink'. */
static gchar *
get_lower_device_name_by_iflink (const gchar *sysfs_path)
{
g_autofree gchar *ifindex_path = NULL;
g_autofree gchar *iflink_path = NULL;
g_autoptr(GDir) dir = NULL;
const gchar *name;
gint ifindex;
gint iflink;
ifindex_path = g_build_filename (sysfs_path, "ifindex", NULL);
iflink_path = g_build_filename (sysfs_path, "iflink", NULL);
if (!read_sysfs_int (ifindex_path, &ifindex) ||
!read_sysfs_int (iflink_path, &iflink) ||
iflink == ifindex || iflink <= 0)
return NULL;
dir = g_dir_open ("/sys/class/net", 0, NULL);
if (!dir)
return NULL;
while ((name = g_dir_read_name (dir)) != NULL) {
g_autofree gchar *path = NULL;
gint index;
path = g_build_filename ("/sys/class/net", name, "ifindex", NULL);
if (read_sysfs_int (path, &index) && index == iflink)
return g_strdup (name);
}
return NULL;
}
gchar * gchar *
mm_kernel_device_get_lower_device_name (const gchar *sysfs_path) mm_kernel_device_get_lower_device_name (const gchar *sysfs_path)
{ {
@@ -60,7 +112,7 @@ mm_kernel_device_get_lower_device_name (const gchar *sysfs_path)
return g_path_get_basename (real_path); return g_path_get_basename (real_path);
} }
return NULL; return get_lower_device_name_by_iflink (sysfs_path);
} }
/******************************************************************************/ /******************************************************************************/
+13 -2
View File
@@ -338,10 +338,21 @@ kernel_device_get_name (MMKernelDevice *_self)
return mm_kernel_event_properties_get_name (self->priv->properties); return mm_kernel_event_properties_get_name (self->priv->properties);
} }
/* Driver name for devices without one in sysfs, e.g. the virtual rmnet_ipa0 of
* Qualcomm vendor kernels, which is handled like the mainline 'ipa' driver */
#define ID_MM_KERNEL_DRIVER "ID_MM_KERNEL_DRIVER"
static const gchar * static const gchar *
kernel_device_get_driver (MMKernelDevice *self) kernel_device_get_driver (MMKernelDevice *_self)
{ {
return MM_KERNEL_DEVICE_UDEV (self)->priv->driver; MMKernelDeviceUdev *self;
const gchar *driver;
self = MM_KERNEL_DEVICE_UDEV (_self);
if (self->priv->device &&
(driver = g_udev_device_get_property (self->priv->device, ID_MM_KERNEL_DRIVER)) != NULL)
return driver;
return self->priv->driver;
} }
static const gchar * static const gchar *
+46 -15
View File
@@ -59,14 +59,19 @@ quit_cb (gpointer user_data)
#if defined WITH_SUSPEND_RESUME #if defined WITH_SUSPEND_RESUME
static void static void
sleeping_cb (MMSleepMonitor *sleep_monitor) sleeping_cb (MMSleepMonitor *sleep_monitor,
MMSleepContext *ctx)
{ {
if (mm_context_get_test_low_power_suspend_resume ()) { if (mm_context_get_test_low_power_suspend_resume ()) {
mm_dbg ("removing devices and setting them in low power mode... (sleeping)"); mm_dbg ("removing devices and setting them in low power mode... (sleeping)");
mm_base_manager_shutdown (manager, TRUE, TRUE, TRUE); mm_base_manager_cleanup (manager,
MM_BASE_MANAGER_CLEANUP_DISABLE |
MM_BASE_MANAGER_CLEANUP_LOW_POWER |
MM_BASE_MANAGER_CLEANUP_REMOVE,
ctx);
} else { } else {
mm_dbg ("removing devices... (sleeping)"); mm_dbg ("removing devices... (sleeping)");
mm_base_manager_shutdown (manager, FALSE, FALSE, TRUE); mm_base_manager_cleanup (manager, MM_BASE_MANAGER_CLEANUP_REMOVE, ctx);
} }
} }
@@ -78,11 +83,20 @@ resuming_cb (MMSleepMonitor *sleep_monitor)
} }
static void static void
sleeping_quick_cb (MMSleepMonitor *sleep_monitor) sleeping_quick_cb (MMSleepMonitor *sleep_monitor,
MMSleepContext *ctx)
{ {
if (mm_context_get_test_low_power_suspend_resume ()) { if (mm_context_get_test_low_power_suspend_resume ()) {
mm_dbg ("setting modem in low power mode... (sleeping)"); mm_dbg ("setting modem in low power mode... (sleeping)");
mm_base_manager_shutdown (manager, TRUE, TRUE, FALSE); mm_base_manager_cleanup (manager,
MM_BASE_MANAGER_CLEANUP_DISABLE |
MM_BASE_MANAGER_CLEANUP_LOW_POWER,
ctx);
} else {
mm_dbg ("setting modem in terse mode... (only send important signals (call/text))");
mm_base_manager_cleanup (manager,
MM_BASE_MANAGER_CLEANUP_TERSE,
ctx);
} }
} }
@@ -168,6 +182,18 @@ register_dbus_errors (void)
g_dbus_error_register_error (G_IO_ERROR, G_IO_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");
} }
static void
shutdown_done (MMSleepContext *sleep_ctx,
GError *error,
GMainLoop *quit_loop)
{
if (error)
mm_warn ("shutdown failed: %s", error->message);
else
mm_msg ("shutdown complete");
g_main_loop_quit (quit_loop);
}
int int
main (int argc, char *argv[]) main (int argc, char *argv[])
{ {
@@ -241,28 +267,33 @@ main (int argc, char *argv[])
loop = NULL; loop = NULL;
if (manager) { if (manager) {
GTimer *timer; MMSleepContext *ctx;
guint sleep_done_id;
mm_base_manager_shutdown (manager, TRUE, FALSE, TRUE); ctx = mm_sleep_context_new (MAX_SHUTDOWN_TIME_SECS);
sleep_done_id = g_signal_connect (ctx,
MM_SLEEP_CONTEXT_DONE,
(GCallback)shutdown_done,
inner);
mm_base_manager_cleanup (manager,
MM_BASE_MANAGER_CLEANUP_DISABLE | MM_BASE_MANAGER_CLEANUP_REMOVE,
ctx);
/* Wait for all modems to be disabled and removed, but don't wait /* Wait for all modems to be disabled and removed, but don't wait
* forever: if disabling the modems takes longer than 20s, just * forever: if disabling the modems takes longer than 20s, just
* shutdown anyway. */ * shutdown anyway. */
timer = g_timer_new (); while (mm_base_manager_num_modems (manager))
while (mm_base_manager_num_modems (manager) && g_main_loop_run (inner);
g_timer_elapsed (timer, NULL) < (gdouble)MAX_SHUTDOWN_TIME_SECS) {
GMainContext *ctx = g_main_loop_get_context (inner);
g_main_context_iteration (ctx, FALSE); g_signal_handler_disconnect (ctx, sleep_done_id);
g_usleep (50); g_object_unref (ctx);
}
if (mm_base_manager_num_modems (manager)) 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)); mm_base_manager_num_modems (manager));
g_object_unref (manager); g_object_unref (manager);
g_timer_destroy (timer);
} }
g_main_loop_unref (inner); g_main_loop_unref (inner);
+1
View File
@@ -299,6 +299,7 @@ sources = files(
'mm-port-probe-at.c', 'mm-port-probe-at.c',
'mm-private-boxed-types.c', 'mm-private-boxed-types.c',
'mm-sms-list.c', 'mm-sms-list.c',
'mm-sleep-context.c',
) )
sources += daemon_enums_sources sources += daemon_enums_sources
+332 -116
View File
@@ -17,6 +17,8 @@
* Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es> * Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
*/ */
#include "gio/gio.h"
#include "glib.h"
#include <config.h> #include <config.h>
#include <string.h> #include <string.h>
@@ -129,23 +131,6 @@ struct _MMBaseManagerPrivate {
/*****************************************************************************/ /*****************************************************************************/
static MMDevice *
find_device_by_modem (MMBaseManager *manager,
MMBaseModem *modem)
{
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 (modem == mm_device_peek_modem (candidate))
return candidate;
}
return NULL;
}
static MMDevice * static MMDevice *
find_device_by_port (MMBaseManager *manager, find_device_by_port (MMBaseManager *manager,
MMKernelDevice *port) MMKernelDevice *port)
@@ -367,7 +352,7 @@ device_removed (MMBaseManager *self,
/* The device may have already been removed from the tracking HT, we /* The device may have already been removed from the tracking HT, we
* just try to remove it and if it fails, we ignore it */ * just try to remove it and if it fails, we ignore it */
mm_device_remove_modem (device); mm_device_remove_modem_quick (device);
g_hash_table_remove (self->priv->devices, mm_device_get_uid (device)); g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
} }
} }
@@ -455,6 +440,19 @@ existing_port (MMBaseManager *self,
device_support_check_add_single_port (self, device, port); device_support_check_add_single_port (self, device, port);
} }
static void
additional_port_modem_remove_ready (MMDevice *device,
GAsyncResult *res,
MMBaseManager *self)
{
g_autoptr(GError) error = NULL;
if (!mm_device_remove_modem_finish (device, res, &error))
mm_obj_warn (device, "removing modem failed: %s", error->message);
device_support_check_add_all_ports (self, device);
g_object_unref (self);
}
static void static void
additional_port (MMBaseManager *self, additional_port (MMBaseManager *self,
MMDevice *device, MMDevice *device,
@@ -500,9 +498,9 @@ additional_port (MMBaseManager *self,
} }
mm_obj_info (self, "last modem object creation in device '%s' succeeded, but we have a new port addition, will retry", uid); mm_obj_info (self, "last modem object creation in device '%s' succeeded, but we have a new port addition, will retry", uid);
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem)); mm_device_remove_modem (device,
mm_device_remove_modem (device); (GAsyncReadyCallback)additional_port_modem_remove_ready,
device_support_check_add_all_ports (self, device); g_object_ref (self));
} }
static void static void
@@ -873,146 +871,364 @@ mm_base_manager_start (MMBaseManager *self,
/*****************************************************************************/ /*****************************************************************************/
typedef enum {
DEVICE_CLEANUP_STEP_FIRST = 0,
DEVICE_CLEANUP_STEP_DISABLE,
DEVICE_CLEANUP_STEP_LOW_POWER,
DEVICE_CLEANUP_STEP_REMOVE,
DEVICE_CLEANUP_STEP_TERSE,
DEVICE_CLEANUP_STEP_LAST,
} DeviceCleanupStep;
typedef struct { typedef struct {
MMBaseManager *self; MMBaseModem *modem;
gboolean low_power; MMBaseManagerCleanupFlags flags;
gboolean remove; DeviceCleanupStep step;
} DisableContext; } DeviceCleanupContext;
static void static void device_cleanup_step (GTask *task);
disable_context_free (DisableContext *ctx)
static DeviceCleanupContext *
device_cleanup_context_new (MMBaseModem *modem,
MMBaseManagerCleanupFlags flags)
{ {
g_object_unref (ctx->self); DeviceCleanupContext *ctx;
g_slice_free (DisableContext, ctx);
ctx = g_slice_new0 (DeviceCleanupContext);
if (modem)
ctx->modem = g_object_ref (modem);
ctx->step = DEVICE_CLEANUP_STEP_FIRST;
ctx->flags = flags;
return ctx;
} }
static void static void
remove_device_after_disable (MMBaseModem *modem, device_cleanup_context_free (DeviceCleanupContext *ctx)
DisableContext *ctx)
{ {
MMDevice *device; g_clear_object (&ctx->modem);
g_slice_free (DeviceCleanupContext, ctx);
device = find_device_by_modem (ctx->self, modem);
if (device) {
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
mm_device_remove_modem (device);
g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (device));
}
disable_context_free (ctx);
} }
static void static void
shutdown_low_power_ready (MMIfaceModem *modem, modem_remove_ready (MMDevice *device,
GAsyncResult *res, GAsyncResult *res,
DisableContext *ctx) GTask *task)
{ {
g_autoptr(GError) error = NULL; DeviceCleanupContext *ctx;
g_autoptr(GError) error = NULL;
ctx = g_task_get_task_data (task);
if (!mm_device_remove_modem_finish (device, res, &error))
mm_obj_warn (device, "removing modem failed: %s", error->message);
ctx->step++;
device_cleanup_step (task);
}
static void
cleanup_low_power_ready (MMIfaceModem *modem,
GAsyncResult *res,
GTask *task)
{
DeviceCleanupContext *ctx;
g_autoptr(GError) error = NULL;
ctx = g_task_get_task_data (task);
if (!mm_iface_modem_set_power_state_finish (modem, res, NULL, &error)) if (!mm_iface_modem_set_power_state_finish (modem, res, NULL, &error))
mm_obj_info (ctx->self, "changing to low power state failed: %s", error->message); mm_obj_info (modem, "changing to low power state failed: %s", error->message);
if (ctx->remove) ctx->step++;
remove_device_after_disable (MM_BASE_MODEM (modem), ctx); device_cleanup_step (task);
else
disable_context_free (ctx);
} }
static void static void
shutdown_disable_ready (MMBaseModem *modem, cleanup_disable_ready (MMBaseModem *modem,
GAsyncResult *res, GAsyncResult *res,
DisableContext *ctx) GTask *task)
{ {
g_autoptr(GError) error = NULL; DeviceCleanupContext *ctx;
g_autoptr(GError) error = NULL;
/* We don't care about errors disabling at this point */ ctx = g_task_get_task_data (task);
if (!mm_base_modem_disable_finish (modem, res, &error)) {
mm_obj_info (ctx->self, "disabling modem failed: %s", error->message); if (mm_base_modem_disable_finish (modem, res, &error)) {
} ctx->step++;
/* Bring the modem to low power mode if requested */ } else {
else if (ctx->low_power) { mm_obj_info (modem, "disabling modem failed: %s", error->message);
mm_iface_modem_set_power_state (MM_IFACE_MODEM (modem),
MM_MODEM_POWER_STATE_LOW, /* If disabling failed there's no point in trying to low-power the
(GAsyncReadyCallback)shutdown_low_power_ready, * modem; just remove it.
ctx); */
return; ctx->step = DEVICE_CLEANUP_STEP_REMOVE;
} }
if (ctx->remove) device_cleanup_step (task);
remove_device_after_disable (modem, ctx);
else
disable_context_free (ctx);
} }
#if defined WITH_SUSPEND_RESUME
static void static void
foreach_disable (gpointer key, cleanup_terse_ready (MMBaseModem *modem,
MMDevice *device, GAsyncResult *res,
DisableContext *foreach_ctx) GTask *task)
{ {
MMBaseModem *modem; DeviceCleanupContext *ctx;
DisableContext *ctx; g_autoptr(GError) error = NULL;
modem = mm_device_peek_modem (device); ctx = g_task_get_task_data (task);
if (!modem) if (!mm_base_modem_terse_finish (modem, res, &error))
return; mm_obj_info (modem, "setting terse failed: %s", error->message);
ctx = g_slice_new0 (DisableContext); ctx->step++;
ctx->self = g_object_ref (foreach_ctx->self);
ctx->low_power = foreach_ctx->low_power;
ctx->remove = foreach_ctx->remove;
mm_base_modem_disable (modem, device_cleanup_step (task);
MM_BASE_MODEM_OPERATION_LOCK_REQUIRED,
MM_BASE_MODEM_OPERATION_PRIORITY_OVERRIDE,
(GAsyncReadyCallback)shutdown_disable_ready,
ctx);
} }
#endif
static gboolean static gboolean
foreach_remove (gpointer key, has_cleanup_flag (MMBaseManagerCleanupFlags flags,
MMDevice *device, MMBaseManagerCleanupFlags expected)
MMBaseManager *self)
{ {
MMBaseModem *modem; return flags & expected;
}
modem = mm_device_peek_modem (device); static void
if (modem) device_cleanup_step (GTask *task)
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem)); {
mm_device_remove_modem (device); DeviceCleanupContext *ctx;
return TRUE; MMDevice *device;
ctx = g_task_get_task_data (task);
device = g_task_get_source_object (task);
switch (ctx->step) {
case DEVICE_CLEANUP_STEP_FIRST:
ctx->step++;
/* fall through */
case DEVICE_CLEANUP_STEP_DISABLE:
if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_DISABLE)) {
mm_obj_dbg (device,
"disabling modem%d",
mm_base_modem_get_dbus_id (ctx->modem));
mm_base_modem_disable (ctx->modem,
MM_BASE_MODEM_OPERATION_LOCK_REQUIRED,
MM_BASE_MODEM_OPERATION_PRIORITY_OVERRIDE,
(GAsyncReadyCallback)cleanup_disable_ready,
task);
return;
}
ctx->step++;
/* fall through */
case DEVICE_CLEANUP_STEP_LOW_POWER:
if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_LOW_POWER)) {
mm_obj_dbg (device,
"setting modem%d to low-power state",
mm_base_modem_get_dbus_id (ctx->modem));
mm_iface_modem_set_power_state (MM_IFACE_MODEM (ctx->modem),
MM_MODEM_POWER_STATE_LOW,
(GAsyncReadyCallback)cleanup_low_power_ready,
task);
return;
}
ctx->step++;
/* fall through */
case DEVICE_CLEANUP_STEP_REMOVE:
if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_REMOVE)) {
mm_obj_dbg (device,
"removing modem%d",
mm_base_modem_get_dbus_id (ctx->modem));
mm_device_remove_modem (device,
(GAsyncReadyCallback)modem_remove_ready,
task);
return;
}
ctx->step++;
/* fall through */
case DEVICE_CLEANUP_STEP_TERSE:
#if defined WITH_SUSPEND_RESUME
if (ctx->modem && has_cleanup_flag (ctx->flags, MM_BASE_MANAGER_CLEANUP_TERSE)) {
mm_obj_dbg(device,
"settting terse on modem%d",
mm_base_modem_get_dbus_id (ctx->modem));
mm_base_modem_terse (ctx->modem,
MM_BASE_MODEM_OPERATION_LOCK_REQUIRED,
(GAsyncReadyCallback)cleanup_terse_ready,
task);
return;
}
#endif
ctx->step++;
/* fall through */
case DEVICE_CLEANUP_STEP_LAST:
mm_obj_dbg (device, "device cleanup complete");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
typedef struct {
MMBaseManager *self;
GSList *tasks;
GSList *devices_to_remove;
MMSleepContext *sleep_ctx;
} ManagerCleanupContext;
static ManagerCleanupContext *
manager_cleanup_context_new (MMBaseManager *self,
MMSleepContext *sleep_ctx)
{
ManagerCleanupContext *ctx;
ctx = g_slice_new0 (ManagerCleanupContext);
ctx->self = g_object_ref (self);
ctx->sleep_ctx = g_object_ref (sleep_ctx);
return ctx;
}
static void
manager_cleanup_context_maybe_complete (ManagerCleanupContext *ctx,
GTask *task,
MMDevice *device,
gboolean remove)
{
if (task)
ctx->tasks = g_slist_remove (ctx->tasks, task);
if (remove) {
g_assert (device);
ctx->devices_to_remove = g_slist_append (ctx->devices_to_remove, device);
}
if (ctx->tasks == NULL) {
GSList *iter;
/* Remove devices at the end to avoid modifying the hash table
* during iteration, in case a device has no modem and the steps
* all fall through.
*/
for (iter = ctx->devices_to_remove; iter; iter = iter->next)
g_hash_table_remove (ctx->self->priv->devices, mm_device_get_uid (device));
g_slist_free (ctx->devices_to_remove);
mm_obj_dbg (ctx->self, "cleanup complete");
mm_sleep_context_complete (ctx->sleep_ctx, NULL);
g_object_unref (ctx->sleep_ctx);
g_object_unref (ctx->self);
g_slice_free (ManagerCleanupContext, ctx);
}
}
static void
device_cleanup_ready (MMDevice *device,
GAsyncResult *res,
ManagerCleanupContext *ctx)
{
GTask *task;
DeviceCleanupContext *dctx;
g_autoptr(GError) error = NULL;
gboolean remove;
task = G_TASK (res);
if (!g_task_propagate_boolean (task, &error)) {
mm_obj_info (ctx->self,
"failed to cleanup device %s: %s",
mm_device_get_uid (device),
error->message);
}
dctx = g_task_get_task_data (task);
remove = has_cleanup_flag (dctx->flags, MM_BASE_MANAGER_CLEANUP_REMOVE);
manager_cleanup_context_maybe_complete (ctx, task, device, remove);
} }
void void
mm_base_manager_shutdown (MMBaseManager *self, mm_base_manager_cleanup (MMBaseManager *self,
gboolean disable, MMBaseManagerCleanupFlags flags,
gboolean low_power, MMSleepContext *sleep_ctx)
gboolean remove)
{ {
ManagerCleanupContext *ctx;
GHashTableIter iter;
MMDevice *device;
gboolean disable, remove, low_power, terse;
guint active_bearers = 0;
g_return_if_fail (self != NULL); g_return_if_fail (self != NULL);
g_return_if_fail (MM_IS_BASE_MANAGER (self)); g_return_if_fail (MM_IS_BASE_MANAGER (self));
/* Cancel all ongoing auth requests */ /* Cancel all ongoing auth requests */
g_cancellable_cancel (self->priv->authp_cancellable); g_cancellable_cancel (self->priv->authp_cancellable);
if (disable) { disable = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_DISABLE);
DisableContext foreach_ctx = { low_power = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_LOW_POWER);
.self = self, remove = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_REMOVE);
.low_power = low_power, terse = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_TERSE);
.remove = remove, mm_obj_dbg (self,
}; "manager shutting down... (%s%s%s%s%s%s%s)",
g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, &foreach_ctx); disable ? "disable" : "",
disable && (low_power || remove || terse) ? "," : "",
low_power ? "low-power" : "",
low_power && (remove || terse) ? "," : "",
remove ? "remove" : "",
(remove && terse) ? "," : "",
terse ? "terse" : "");
/* Disabling may take a few iterations of the mainloop, so the caller ctx = manager_cleanup_context_new (self, sleep_ctx);
* has to iterate the mainloop until all devices have been disabled and
* removed. g_hash_table_iter_init (&iter, self->priv->devices);
*/ while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
return; DeviceCleanupContext *dctx;
GTask *task;
MMBaseModem *modem;
modem = mm_device_peek_modem (device);
if (modem) {
guint count = 0;
mm_iface_modem_count_bearers (MM_IFACE_MODEM (modem),
MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE,
&count,
NULL,
NULL);
active_bearers += count;
}
task = g_task_new (device,
NULL,
(GAsyncReadyCallback)device_cleanup_ready,
ctx);
ctx->tasks = g_slist_append (ctx->tasks, task);
dctx = device_cleanup_context_new (modem, flags);
g_task_set_task_data (task, dctx, (GDestroyNotify)device_cleanup_context_free);
device_cleanup_step (task);
} }
if (remove) { /* Increase cleanup timeout for each connected bearer; disconnecting a
/* Otherwise, just remove directly */ * bearer can take many seconds.
g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self); */
} if (active_bearers)
mm_sleep_context_timeout_backoff (sleep_ctx, active_bearers * 20);
/* Complete cleanup if there were no devices */
manager_cleanup_context_maybe_complete (ctx, NULL, NULL, FALSE);
} }
guint32 guint32
@@ -1713,7 +1929,7 @@ handle_set_profile (MmGdbusTest *skeleton,
out: out:
if (error) { if (error) {
mm_device_remove_modem (device); mm_device_remove_modem_quick (device);
g_hash_table_remove (self->priv->devices, mm_device_get_uid (device)); g_hash_table_remove (self->priv->devices, mm_device_get_uid (device));
mm_dbus_method_invocation_return_gerror (invocation, error); mm_dbus_method_invocation_return_gerror (invocation, error);
g_error_free (error); g_error_free (error);
+12 -4
View File
@@ -25,6 +25,7 @@
#include "mm-filter.h" #include "mm-filter.h"
#include "mm-gdbus-manager.h" #include "mm-gdbus-manager.h"
#include "mm-sleep-context.h"
#define MM_TYPE_BASE_MANAGER (mm_base_manager_get_type ()) #define MM_TYPE_BASE_MANAGER (mm_base_manager_get_type ())
#define MM_BASE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_MANAGER, MMBaseManager)) #define MM_BASE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_MANAGER, MMBaseManager))
@@ -71,10 +72,17 @@ MMBaseManager *mm_base_manager_new (GDBusConnection *bus,
void mm_base_manager_start (MMBaseManager *manager, void mm_base_manager_start (MMBaseManager *manager,
gboolean manual_scan); gboolean manual_scan);
void mm_base_manager_shutdown (MMBaseManager *manager, typedef enum {
gboolean disable, MM_BASE_MANAGER_CLEANUP_NONE = 0x0,
gboolean power_low, MM_BASE_MANAGER_CLEANUP_DISABLE = 0x1,
gboolean remove); MM_BASE_MANAGER_CLEANUP_LOW_POWER = 0x2,
MM_BASE_MANAGER_CLEANUP_REMOVE = 0x4,
MM_BASE_MANAGER_CLEANUP_TERSE = 0x8,
} MMBaseManagerCleanupFlags;
void mm_base_manager_cleanup (MMBaseManager *manager,
MMBaseManagerCleanupFlags flags,
MMSleepContext *ctx);
#if defined WITH_SUSPEND_RESUME #if defined WITH_SUSPEND_RESUME
void mm_base_manager_sync (MMBaseManager *manager); void mm_base_manager_sync (MMBaseManager *manager);
+206 -16
View File
@@ -87,6 +87,7 @@ struct _MMBaseModemPrivate {
* should be done by the modem. */ * should be done by the modem. */
GCancellable *cancellable; GCancellable *cancellable;
gulong invalid_if_cancelled; gulong invalid_if_cancelled;
guint invalid_from_idle;
gchar *device; gchar *device;
gchar *physdev; gchar *physdev;
@@ -101,6 +102,7 @@ struct _MMBaseModemPrivate {
gboolean hotplugged; gboolean hotplugged;
gboolean valid; gboolean valid;
gboolean reprobe; gboolean reprobe;
gboolean torn_down;
guint max_timeouts; guint max_timeouts;
@@ -728,6 +730,7 @@ typedef enum {
STATE_OPERATION_TYPE_DISABLE, STATE_OPERATION_TYPE_DISABLE,
#if defined WITH_SUSPEND_RESUME #if defined WITH_SUSPEND_RESUME
STATE_OPERATION_TYPE_SYNC, STATE_OPERATION_TYPE_SYNC,
STATE_OPERATION_TYPE_TERSE,
#endif #endif
} StateOperationType; } StateOperationType;
@@ -853,6 +856,12 @@ state_operation (MMBaseModem *self,
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->sync; ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->sync;
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->sync_finish; ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->sync_finish;
break; break;
case STATE_OPERATION_TYPE_TERSE:
operation_description = "terse";
optional = TRUE;
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->terse;
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->terse_finish;
break;
#endif #endif
default: default:
g_assert_not_reached (); g_assert_not_reached ();
@@ -907,6 +916,28 @@ mm_base_modem_sync (MMBaseModem *self,
user_data); user_data);
} }
gboolean
mm_base_modem_terse_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return state_operation_finish (self, res, error);
}
void
mm_base_modem_terse (MMBaseModem *self,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data)
{
state_operation (self,
STATE_OPERATION_TYPE_TERSE,
operation_lock,
MM_BASE_MODEM_OPERATION_PRIORITY_DEFAULT,
callback,
user_data);
}
#endif /* WITH_SUSPEND_RESUME */ #endif /* WITH_SUSPEND_RESUME */
/******************************************************************************/ /******************************************************************************/
@@ -2132,13 +2163,22 @@ mm_base_modem_get_subsystem_device_id (MMBaseModem *self)
/*****************************************************************************/ /*****************************************************************************/
static void
clear_invalid_from_idle (MMBaseModem *self)
{
if (self->priv->invalid_from_idle)
g_source_remove (self->priv->invalid_from_idle);
self->priv->invalid_from_idle = 0;
}
static gboolean static gboolean
base_modem_invalid_idle (MMBaseModem *self) base_modem_invalid_idle (MMBaseModem *self)
{ {
clear_invalid_from_idle (self);
/* Ensure the modem is set invalid if we get the modem-wide cancellable /* Ensure the modem is set invalid if we get the modem-wide cancellable
* cancelled */ * cancelled */
mm_base_modem_set_valid (self, FALSE); mm_base_modem_set_valid (self, FALSE);
g_object_unref (self);
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
@@ -2146,9 +2186,14 @@ static void
base_modem_cancelled (GCancellable *cancellable, base_modem_cancelled (GCancellable *cancellable,
MMBaseModem *self) MMBaseModem *self)
{ {
clear_invalid_from_idle (self);
/* NOTE: Don't call set_valid() directly here, do it in an idle, and ensure /* NOTE: Don't call set_valid() directly here, do it in an idle, and ensure
* that we pass a valid reference of the modem object as context. */ * that we pass a valid reference of the modem object as context. */
g_idle_add ((GSourceFunc)base_modem_invalid_idle, g_object_ref (self)); self->priv->invalid_from_idle = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
(GSourceFunc)base_modem_invalid_idle,
g_object_ref (self),
(GDestroyNotify) g_object_unref);
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -2160,9 +2205,107 @@ setup_ports_table (GHashTable **ht)
*ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); *ht = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
} }
typedef struct {
volatile gint refcount;
GError *error;
/* The Context owns the GTask so that we can ensure the Task returns only
* when all ports are closed. The reference count tracks open ports and
* the context will return the Task when it reaches zero (indicating all
* outstanding operations are complete and ports are closed).
*/
GTask *task;
} TeardownContext;
static TeardownContext *
teardown_context_new (GObject *source_object,
GAsyncReadyCallback callback,
gpointer user_data)
{
TeardownContext *ctx;
ctx = g_slice_new0 (TeardownContext);
ctx->refcount = 1;
ctx->task = g_task_new (source_object, NULL, callback, user_data);
return ctx;
}
static void static void
cleanup_modem_port (MMBaseModem *self, teardown_context_ref (TeardownContext *ctx)
MMPort *port) {
g_atomic_int_inc (&ctx->refcount);
}
static void
teardown_context_unref (TeardownContext *ctx)
{
if (g_atomic_int_dec_and_test (&ctx->refcount)) {
if (ctx->error)
g_task_return_error (ctx->task, g_steal_pointer (&ctx->error));
else
g_task_return_boolean (ctx->task, TRUE);
g_clear_object (&ctx->task);
g_slice_free (TeardownContext, ctx);
}
}
#if defined (WITH_MBIM) || defined (WITH_QMI)
static void
teardown_context_add_error (TeardownContext *ctx,
MMPort *port,
GError *error)
{
if (!ctx->error) {
ctx->error = g_error_copy (error);
g_prefix_error (&ctx->error,
"[%s] teardown error: ",
mm_port_get_device (port));
} else {
g_prefix_error (&ctx->error,
"[%s] teardown error: %s; ",
mm_port_get_device (port),
error->message);
}
}
#endif
#if defined WITH_MBIM
static void
mbim_port_close_ready (MMPortMbim *port,
GAsyncResult *res,
TeardownContext *ctx)
{
g_autoptr(GError) error = NULL;
if (!mm_port_mbim_close_finish (port, res, &error))
teardown_context_add_error (ctx, MM_PORT (port), error);
teardown_context_unref (ctx);
}
#endif
#if defined WITH_QMI
static void
qmi_port_close_ready (MMPortQmi *port,
GAsyncResult *res,
TeardownContext *ctx)
{
g_autoptr(GError) error = NULL;
if (!mm_port_qmi_close_finish (port, res, &error))
teardown_context_add_error (ctx, MM_PORT (port), error);
teardown_context_unref (ctx);
}
#endif
static void
cleanup_modem_port (MMBaseModem *self,
MMPort *port,
TeardownContext *ctx)
{ {
mm_obj_dbg (self, "cleaning up port '%s/%s'...", mm_obj_dbg (self, "cleaning up port '%s/%s'...",
mm_port_subsys_get_string (mm_port_get_subsys (MM_PORT (port))), mm_port_subsys_get_string (mm_port_get_subsys (MM_PORT (port))),
@@ -2172,10 +2315,20 @@ cleanup_modem_port (MMBaseModem *self,
g_signal_handlers_disconnect_by_func (port, port_timed_out_cb, self); g_signal_handlers_disconnect_by_func (port, port_timed_out_cb, self);
g_signal_handlers_disconnect_by_func (port, port_removed_cb, self); g_signal_handlers_disconnect_by_func (port, port_removed_cb, self);
if (ctx)
teardown_context_ref (ctx);
/* No need to close serial ports here as they do not require a specific
* shutdown procedure with message exchanges and callbacks. They will be
* closed when the modem is invalidated or disposed.
*/
#if defined WITH_MBIM #if defined WITH_MBIM
/* We need to close the MBIM port cleanly when disposing the modem object */ /* We need to close the MBIM port cleanly when disposing the modem object */
if (MM_IS_PORT_MBIM (port)) { if (MM_IS_PORT_MBIM (port)) {
mm_port_mbim_close (MM_PORT_MBIM (port), NULL, NULL); mm_port_mbim_close (MM_PORT_MBIM (port),
ctx ? (GAsyncReadyCallback)mbim_port_close_ready : NULL,
ctx);
return; return;
} }
#endif #endif
@@ -2186,27 +2339,61 @@ cleanup_modem_port (MMBaseModem *self,
* allocating too many newer allocations will fail with client-ids-exhausted * allocating too many newer allocations will fail with client-ids-exhausted
* errors. */ * errors. */
if (MM_IS_PORT_QMI (port)) { if (MM_IS_PORT_QMI (port)) {
mm_port_qmi_close (MM_PORT_QMI (port), NULL, NULL); mm_port_qmi_close (MM_PORT_QMI (port),
ctx ? (GAsyncReadyCallback)qmi_port_close_ready : NULL,
ctx);
return; return;
} }
#endif #endif
if (ctx)
teardown_context_unref (ctx);
} }
static void static void
teardown_ports_table (MMBaseModem *self, teardown_ports_tables (MMBaseModem *self,
GHashTable **ht) TeardownContext *ctx)
{ {
GHashTable **tables[2] = {
&self->priv->link_ports,
&self->priv->ports,
};
GHashTableIter iter; GHashTableIter iter;
gpointer value; gpointer value;
gpointer key; gpointer key;
guint i;
if (!*ht) for (i = 0; i < G_N_ELEMENTS (tables); i++) {
return; if (*tables[i]) {
g_hash_table_iter_init (&iter, *tables[i]);
while (g_hash_table_iter_next (&iter, &key, &value))
cleanup_modem_port (self, MM_PORT (value), ctx);
g_hash_table_destroy (*tables[i]);
*tables[i] = NULL;
}
}
}
g_hash_table_iter_init (&iter, *ht); gboolean
while (g_hash_table_iter_next (&iter, &key, &value)) mm_base_modem_teardown_ports_finish (MMBaseModem *self,
cleanup_modem_port (self, MM_PORT (value)); GAsyncResult *res,
g_hash_table_destroy (g_steal_pointer (ht)); GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
void
mm_base_modem_teardown_ports (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
TeardownContext *ctx;
self->priv->torn_down = TRUE;
ctx = teardown_context_new (G_OBJECT (self), callback, user_data);
teardown_ports_tables (self, ctx);
teardown_context_unref (ctx);
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -2412,6 +2599,8 @@ dispose (GObject *object)
g_cancellable_cancel (self->priv->cancellable); g_cancellable_cancel (self->priv->cancellable);
g_clear_object (&self->priv->cancellable); g_clear_object (&self->priv->cancellable);
clear_invalid_from_idle (self);
g_clear_object (&self->priv->primary); g_clear_object (&self->priv->primary);
g_clear_object (&self->priv->secondary); g_clear_object (&self->priv->secondary);
g_list_free_full (g_steal_pointer (&self->priv->data), g_object_unref); g_list_free_full (g_steal_pointer (&self->priv->data), g_object_unref);
@@ -2426,8 +2615,9 @@ dispose (GObject *object)
g_list_free_full (g_steal_pointer (&self->priv->mbim), g_object_unref); g_list_free_full (g_steal_pointer (&self->priv->mbim), g_object_unref);
#endif #endif
teardown_ports_table (self, &self->priv->link_ports); if (!self->priv->torn_down)
teardown_ports_table (self, &self->priv->ports); mm_obj_warn (self, "teardown not called before dispose");
teardown_ports_tables (self, NULL);
g_clear_object (&self->priv->connection); g_clear_object (&self->priv->connection);
+30 -7
View File
@@ -109,6 +109,15 @@ struct _MMBaseModemClass {
* this method triggers a synchronization of all modem interfaces */ * this method triggers a synchronization of all modem interfaces */
StateOperation sync; StateOperation sync;
StateOperationFinish sync_finish; StateOperationFinish sync_finish;
/* Modem terse.
* When suspending in quick suspend/resume mode,
* this method disables unsolicited events on the 3GPP interface only.
* This is enough for phones to suspend properly, but it
* might be useful to extend it to all other interfaces
* that support it as well */
StateOperation terse;
StateOperationFinish terse_finish;
#endif #endif
/* Allow plugins to subclass port object creation as needed */ /* Allow plugins to subclass port object creation as needed */
@@ -298,13 +307,27 @@ gboolean mm_base_modem_disable_finish (MMBaseModem *self,
GError **error); GError **error);
#if defined WITH_SUSPEND_RESUME #if defined WITH_SUSPEND_RESUME
void mm_base_modem_sync (MMBaseModem *self, void mm_base_modem_sync (MMBaseModem *self,
MMBaseModemOperationLock operation_lock, MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);
gboolean mm_base_modem_sync_finish (MMBaseModem *self, gboolean mm_base_modem_sync_finish (MMBaseModem *self,
GAsyncResult *res, GAsyncResult *res,
GError **error); GError **error);
void mm_base_modem_terse (MMBaseModem *self,
MMBaseModemOperationLock operation_lock,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_base_modem_terse_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error);
#endif #endif
void mm_base_modem_teardown_ports (MMBaseModem *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_base_modem_teardown_ports_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error);
#endif /* MM_BASE_MODEM_H */ #endif /* MM_BASE_MODEM_H */
+5 -4
View File
@@ -1458,10 +1458,11 @@ load_settings_from_bearer (MMBearerMbim *self,
guint max_multiplexed_bearers; guint max_multiplexed_bearers;
const gchar *data_port_driver; const gchar *data_port_driver;
if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem),
&current_multiplexed_bearers, MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED,
&max_multiplexed_bearers, &current_multiplexed_bearers,
error)) &max_multiplexed_bearers,
error))
return FALSE; return FALSE;
/* Check multiplex support in the kernel and the device */ /* Check multiplex support in the kernel and the device */
+5 -4
View File
@@ -2424,10 +2424,11 @@ load_settings_from_bearer (MMBearerQmi *self,
guint max_multiplexed_bearers; guint max_multiplexed_bearers;
gboolean multiplex_supported = TRUE; gboolean multiplex_supported = TRUE;
if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem), if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem),
&current_multiplexed_bearers, MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED,
&max_multiplexed_bearers, &current_multiplexed_bearers,
error)) &max_multiplexed_bearers,
error))
return FALSE; return FALSE;
/* Check multiplex support in the kernel and the device */ /* Check multiplex support in the kernel and the device */
+26 -9
View File
@@ -5840,6 +5840,7 @@ static gboolean process_pdu_messages (MMBroadbandModemMbim *self,
guint32 messages_count, guint32 messages_count,
MbimSmsPduReadRecordArray *pdu_messages, MbimSmsPduReadRecordArray *pdu_messages,
guint expected_index, guint expected_index,
gboolean unstored,
GError **error); GError **error);
static void static void
@@ -5864,6 +5865,7 @@ sms_notification_read_flash_sms (MMBroadbandModemMbim *self,
messages_count, messages_count,
pdu_messages, pdu_messages,
SMS_PART_INVALID_INDEX, SMS_PART_INVALID_INDEX,
TRUE,
&error)) &error))
mm_obj_dbg (self, "flash SMS message reading failed: %s", error->message); mm_obj_dbg (self, "flash SMS message reading failed: %s", error->message);
} }
@@ -5943,6 +5945,7 @@ alert_sms_read_query_ready (MbimDevice *device,
messages_count, messages_count,
pdu_messages, pdu_messages,
ctx->expected_index, ctx->expected_index,
FALSE,
&error)) &error))
mm_obj_dbg (ctx->self, "SMS message reading failed: %s", error->message); mm_obj_dbg (ctx->self, "SMS message reading failed: %s", error->message);
@@ -9024,12 +9027,13 @@ load_initial_sms_parts_finish (MMIfaceModemMessaging *self,
static void static void
add_sms_part (MMBroadbandModemMbim *self, add_sms_part (MMBroadbandModemMbim *self,
const MbimSmsPduReadRecord *pdu, const MbimSmsPduReadRecord *pdu,
guint part_index,
guint expected_index) guint expected_index)
{ {
MMSmsPart *part; MMSmsPart *part;
g_autoptr(GError) error = NULL; g_autoptr(GError) error = NULL;
part = mm_sms_part_3gpp_new_from_binary_pdu (pdu->message_index, part = mm_sms_part_3gpp_new_from_binary_pdu (part_index,
pdu->pdu_data, pdu->pdu_data,
pdu->pdu_data_size, pdu->pdu_data_size,
self, self,
@@ -9062,6 +9066,7 @@ process_pdu_messages (MMBroadbandModemMbim *self,
guint32 messages_count, guint32 messages_count,
MbimSmsPduReadRecordArray *pdu_messages, MbimSmsPduReadRecordArray *pdu_messages,
guint expected_index, guint expected_index,
gboolean unstored,
GError **error) GError **error)
{ {
guint i; guint i;
@@ -9087,11 +9092,26 @@ process_pdu_messages (MMBroadbandModemMbim *self,
mm_obj_dbg (self, "%u SMS PDUs reported", messages_count); mm_obj_dbg (self, "%u SMS PDUs reported", messages_count);
for (i = 0; i < messages_count; i++) { for (i = 0; i < messages_count; i++) {
if (pdu_messages[i]) { guint message_index;
mm_obj_dbg (self, "processing SMS PDU %u/%u...", i+1, messages_count);
add_sms_part (self, pdu_messages[i], expected_index); if (!pdu_messages[i]) {
} else
mm_obj_dbg (self, "ignoring invalid SMS PDU %u/%u...", i+1, messages_count); mm_obj_dbg (self, "ignoring invalid SMS PDU %u/%u...", i+1, messages_count);
continue;
}
mm_obj_dbg (self, "processing SMS PDU %u/%u...", i+1, messages_count);
message_index = pdu_messages[i]->message_index;
if (unstored && message_index != 0) {
mm_obj_warn (self,
"processed unstored PDU with non-zero message index (%u)",
message_index);
}
add_sms_part (self,
pdu_messages[i],
unstored ? SMS_PART_INVALID_INDEX : message_index,
expected_index);
} }
return TRUE; return TRUE;
@@ -9126,6 +9146,7 @@ sms_read_query_ready (MbimDevice *device,
messages_count, messages_count,
pdu_messages, pdu_messages,
SMS_PART_INVALID_INDEX, SMS_PART_INVALID_INDEX,
FALSE,
&error)) &error))
g_task_return_error (task, error); g_task_return_error (task, error);
else else
@@ -10416,10 +10437,6 @@ dispose (GObject *object)
/* Explicitly remove notification handler */ /* Explicitly remove notification handler */
self->priv->setup_flags = PROCESS_NOTIFICATION_FLAG_NONE; self->priv->setup_flags = PROCESS_NOTIFICATION_FLAG_NONE;
common_setup_cleanup_unsolicited_events_sync (self, mbim, FALSE); common_setup_cleanup_unsolicited_events_sync (self, mbim, FALSE);
/* 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_clear_object (&self->priv->unlock_retries); g_clear_object (&self->priv->unlock_retries);
+3 -18
View File
@@ -8380,7 +8380,6 @@ messaging_event_report_indication_cb (QmiClientNas *client,
QmiWmsAckIndicator ack_ind; QmiWmsAckIndicator ack_ind;
guint32 transaction_id; guint32 transaction_id;
QmiWmsMessageFormat msg_format; QmiWmsMessageFormat msg_format;
QmiWmsMessageTagType tag;
GArray *raw_data = NULL; GArray *raw_data = NULL;
/* Handle transfer-route MT messages */ /* Handle transfer-route MT messages */
@@ -8425,13 +8424,10 @@ messaging_event_report_indication_cb (QmiClientNas *client,
} }
/* Defaults for transfer-route messages, which are not stored anywhere */ /* 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), add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self),
storage, QMI_WMS_STORAGE_TYPE_NONE,
memory_index, SMS_PART_INVALID_INDEX,
tag, QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ,
msg_format, msg_format,
TRUE, TRUE,
raw_data); raw_data);
@@ -13994,17 +13990,6 @@ static void
dispose (GObject *object) dispose (GObject *object)
{ {
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (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) {
/* 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, g_object_unref); g_list_free_full (self->priv->firmware_list, g_object_unref);
self->priv->firmware_list = NULL; self->priv->firmware_list = NULL;
+157 -68
View File
@@ -323,25 +323,32 @@ ports_context_ref (PortsContext *ctx)
return ctx; return ctx;
} }
static void
ports_context_dispose (PortsContext *ctx)
{
if (ctx->primary && ctx->primary_open) {
mm_port_serial_close (MM_PORT_SERIAL (ctx->primary));
ctx->primary_open = FALSE;
}
if (ctx->secondary && ctx->secondary_open) {
mm_port_serial_close (MM_PORT_SERIAL (ctx->secondary));
ctx->secondary_open = FALSE;
}
if (ctx->qcdm && ctx->qcdm_open) {
mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
ctx->qcdm_open = FALSE;
}
}
static void static void
ports_context_unref (PortsContext *ctx) ports_context_unref (PortsContext *ctx)
{ {
if (g_atomic_int_dec_and_test (&ctx->ref_count)) { if (g_atomic_int_dec_and_test (&ctx->ref_count)) {
if (ctx->primary) { ports_context_dispose (ctx);
if (ctx->primary_open) g_clear_object (&ctx->primary);
mm_port_serial_close (MM_PORT_SERIAL (ctx->primary)); g_clear_object (&ctx->secondary);
g_object_unref (ctx->primary); g_clear_object (&ctx->qcdm);
}
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); g_free (ctx);
} }
} }
@@ -761,11 +768,14 @@ parse_caps_cgmm (MMBaseModem *self,
if (!response) if (!response)
return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE; return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_CONTINUE;
/* This check detects some really old Motorola GPRS dongles and phones */ /* This check detects some really old Motorola and Mediatek GPRS dongles
* and phones.
*/
if (strstr (response, "GSM900") || if (strstr (response, "GSM900") ||
strstr (response, "GSM1800") || strstr (response, "GSM1800") ||
strstr (response, "GSM1900") || strstr (response, "GSM1900") ||
strstr (response, "GSM850")) { strstr (response, "GSM850") ||
strstr (response, "MTK2")) {
/* At least, it's a GSM modem */ /* At least, it's a GSM modem */
*result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS); *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_SUCCESS;
@@ -5584,6 +5594,21 @@ modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
return g_task_propagate_boolean (G_TASK (res), error); return g_task_propagate_boolean (G_TASK (res), error);
} }
static void
run_registration_checks_reload_current_registration_info_ready (MMIfaceModem3gpp *modem,
GAsyncResult *res,
GTask *task)
{
GError *error = NULL;
mm_iface_modem_3gpp_reload_current_registration_info_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 run_registration_checks_context_step (GTask *task); static void run_registration_checks_context_step (GTask *task);
static void static void
@@ -5825,13 +5850,17 @@ run_registration_checks_context_step (GTask *task)
error = g_steal_pointer (&ctx->error_cs); error = g_steal_pointer (&ctx->error_cs);
else else
g_assert_not_reached (); g_assert_not_reached ();
g_task_return_error (task, error);
g_object_unref (task);
return;
} }
if (error) /* When everything is done, recheck operator info as it may have changed */
g_task_return_error (task, error); mm_iface_modem_3gpp_reload_current_registration_info (
else MM_IFACE_MODEM_3GPP (self),
g_task_return_boolean (task, TRUE); (GAsyncReadyCallback)run_registration_checks_reload_current_registration_info_ready,
g_object_unref (task); task);
} }
static void static void
@@ -13129,6 +13158,103 @@ synchronize (MMBaseModem *self,
syncing_step (task); syncing_step (task);
} }
typedef enum {
TERSE_STEP_FIRST,
TERSE_STEP_DISABLE_UNSOLICITED_EVENTS_3GPP,
TERSE_STEP_LAST,
} TerseStep;
typedef struct {
TerseStep step;
} TerseContext;
static void terse_step (GTask *task);
static gboolean
terse_finish (MMBaseModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
static void
iface_modem_3gpp_terse_ready (MMIfaceModem3gpp *self,
GAsyncResult *res,
GTask *task)
{
TerseContext *ctx;
g_autoptr(GError) error = NULL;
ctx = g_task_get_task_data (task);
if (!mm_iface_modem_3gpp_terse_finish (self, res, &error))
mm_obj_warn (self, "3GPP interface disable unsolicited events failed: %s", error->message);
/* Go on to next step */
ctx->step++;
terse_step (task);
}
static void
terse_step (GTask *task)
{
MMBroadbandModem *self;
TerseContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case TERSE_STEP_FIRST:
ctx->step++;
/* fall through */
case TERSE_STEP_DISABLE_UNSOLICITED_EVENTS_3GPP:
if (self->priv->modem_3gpp_dbus_skeleton &&
(self->priv->modem_state >= MM_MODEM_STATE_ENABLED)) {
mm_obj_msg (self, "terse state (%d/%d): 3GPP interface disable unsolicited events",
ctx->step, TERSE_STEP_LAST);
mm_iface_modem_3gpp_terse (MM_IFACE_MODEM_3GPP (self), (GAsyncReadyCallback)iface_modem_3gpp_terse_ready, task);
return;
}
ctx->step++;
/* fall through */
case TERSE_STEP_LAST:
mm_obj_msg (self, "setting terse state (%d/%d): all done",
ctx->step, TERSE_STEP_LAST);
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
break;
}
g_assert_not_reached ();
}
static void
terse (MMBaseModem *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
TerseContext *ctx;
GTask *task;
task = g_task_new (self, cancellable, callback, user_data);
/* Create TerseContext */
ctx = g_new0 (TerseContext, 1);
ctx->step = TERSE_STEP_FIRST;
g_task_set_task_data (task, ctx, (GDestroyNotify)g_free);
terse_step (task);
}
#endif #endif
/*****************************************************************************/ /*****************************************************************************/
@@ -13673,52 +13799,6 @@ mm_broadband_modem_get_current_charset (MMBroadbandModem *self)
/*****************************************************************************/ /*****************************************************************************/
static void
bearer_count_multiplexed_connected (MMBaseBearer *bearer,
guint *count)
{
/* The Multiplexed property is only set if connected, so it's enough to check
* that one to see if we're connected and multiplexed */
if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer)))
*count += 1;
}
gboolean
mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self,
guint *out_current,
guint *out_max,
GError **error)
{
g_autoptr(MMBearerList) list = NULL;
guint max;
guint count = 0;
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (!list) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable");
return FALSE;
}
max = mm_bearer_list_get_max_active_multiplexed (list);
mm_bearer_list_foreach (list,
(MMBearerListForeachFunc)bearer_count_multiplexed_connected,
&count);
g_assert (!(!max && count));
if (out_max)
*out_max = max;
if (out_current)
*out_current = count;
return TRUE;
}
/*****************************************************************************/
gchar * gchar *
mm_broadband_modem_create_device_identifier (MMBroadbandModem *self, mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
const gchar *ati, const gchar *ati,
@@ -14258,6 +14338,13 @@ dispose (GObject *object)
g_clear_object (&self->priv->modem_simple_status); g_clear_object (&self->priv->modem_simple_status);
g_clear_object (&self->priv->modem_cell_broadcast_cbm_list); g_clear_object (&self->priv->modem_cell_broadcast_cbm_list);
if (self->priv->enabled_ports_ctx)
ports_context_dispose (self->priv->enabled_ports_ctx);
if (self->priv->sim_hot_swap_ports_ctx)
ports_context_dispose (self->priv->sim_hot_swap_ports_ctx);
if (self->priv->in_call_ports_ctx)
ports_context_dispose (self->priv->in_call_ports_ctx);
G_OBJECT_CLASS (mm_broadband_modem_parent_class)->dispose (object); G_OBJECT_CLASS (mm_broadband_modem_parent_class)->dispose (object);
} }
@@ -14609,6 +14696,8 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
#if defined WITH_SUSPEND_RESUME #if defined WITH_SUSPEND_RESUME
base_modem_class->sync = synchronize; base_modem_class->sync = synchronize;
base_modem_class->sync_finish = synchronize_finish; base_modem_class->sync_finish = synchronize_finish;
base_modem_class->terse = terse;
base_modem_class->terse_finish = terse_finish;
#endif #endif
klass->setup_ports = setup_ports; klass->setup_ports = setup_ports;
-6
View File
@@ -143,12 +143,6 @@ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem
GError **error); GError **error);
void mm_broadband_modem_sim_hot_swap_ports_context_reset (MMBroadbandModem *self); void mm_broadband_modem_sim_hot_swap_ports_context_reset (MMBroadbandModem *self);
/* Helper to manage multiplexed bearers */
gboolean mm_broadband_modem_get_active_multiplexed_bearers (MMBroadbandModem *self,
guint *out_current,
guint *out_max,
GError **error);
/* Helper to manage initial EPS bearer */ /* Helper to manage initial EPS bearer */
gint mm_broadband_modem_get_initial_eps_bearer_cid (MMBroadbandModem *self); gint mm_broadband_modem_get_initial_eps_bearer_cid (MMBroadbandModem *self);
+99 -14
View File
@@ -459,14 +459,66 @@ clear_modem (MMDevice *self)
} }
} }
void gboolean
mm_device_remove_modem (MMDevice *self) mm_device_remove_modem_finish (MMDevice *self,
GAsyncResult *res,
GError **error)
{ {
if (!self->priv->modem) return g_task_propagate_boolean (G_TASK (res), error);
}
static void
teardown_ports_ready (MMBaseModem *modem,
GAsyncResult *res,
GTask *task)
{
MMDevice *self;
GError *error = NULL;
gboolean success;
self = g_task_get_source_object (task);
success = mm_base_modem_teardown_ports_finish (modem, res, &error);
clear_modem (self);
if (!success)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
void
mm_device_remove_modem (MMDevice *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
GTask *task;
task = g_task_new (self, NULL, callback, user_data);
if (!self->priv->modem) {
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return; return;
}
unexport_modem (self); unexport_modem (self);
clear_modem (self); g_cancellable_cancel (mm_base_modem_peek_cancellable (self->priv->modem));
mm_base_modem_teardown_ports (self->priv->modem,
(GAsyncReadyCallback)teardown_ports_ready,
task);
}
/* Should only be used from testing code or when the hardware is no longer
* present and cannot be gracefully cleaned up.
*/
void
mm_device_remove_modem_quick (MMDevice *self)
{
if (self->priv->modem) {
unexport_modem (self);
clear_modem (self);
}
} }
/*****************************************************************************/ /*****************************************************************************/
@@ -491,6 +543,25 @@ reprobe (MMDevice *self)
return G_SOURCE_REMOVE; return G_SOURCE_REMOVE;
} }
static void
modem_valid_remove_ready (MMDevice *self,
GAsyncResult *res,
MMBaseModem *modem)
{
g_autoptr(GError) error = NULL;
if (!mm_device_remove_modem_finish (self, res, &error))
mm_obj_warn (self, "removing modem failed: %s", error->message);
if (mm_base_modem_get_reprobe (modem)) {
if (self->priv->reprobe_id)
g_source_remove (self->priv->reprobe_id);
self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self);
}
g_object_unref (modem);
}
static void static void
modem_valid (MMBaseModem *modem, modem_valid (MMBaseModem *modem,
GParamSpec *pspec, GParamSpec *pspec,
@@ -498,9 +569,9 @@ modem_valid (MMBaseModem *modem,
{ {
if (!mm_base_modem_get_valid (modem)) { if (!mm_base_modem_get_valid (modem)) {
/* Modem no longer valid */ /* Modem no longer valid */
mm_device_remove_modem (self); mm_device_remove_modem (self,
if (mm_base_modem_get_reprobe (modem)) (GAsyncReadyCallback)modem_valid_remove_ready,
self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self); g_object_ref (modem));
} else { } else {
/* Modem now valid, export it, but only if we really have it around. /* Modem now valid, export it, but only if we really have it around.
* It may happen that the initialization sequence fails because the * It may happen that the initialization sequence fails because the
@@ -732,6 +803,19 @@ mm_device_inhibit_finish (MMDevice *self,
return g_task_propagate_boolean (G_TASK (res), error); return g_task_propagate_boolean (G_TASK (res), error);
} }
static void
inhibit_disable_modem_remove_ready (MMDevice *self,
GAsyncResult *res,
GTask *task)
{
g_autoptr(GError) error = NULL;
if (!mm_device_remove_modem_finish (self, res, &error))
mm_obj_warn (self, "removing modem failed: %s", error->message);
g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
static void static void
inhibit_disable_ready (MMBaseModem *modem, inhibit_disable_ready (MMBaseModem *modem,
GAsyncResult *res, GAsyncResult *res,
@@ -742,14 +826,15 @@ inhibit_disable_ready (MMBaseModem *modem,
self = g_task_get_source_object (task); self = g_task_get_source_object (task);
if (!mm_base_modem_disable_finish (modem, res, &error)) if (!mm_base_modem_disable_finish (modem, res, &error)) {
g_task_return_error (task, error); g_task_return_error (task, error);
else { g_object_unref (task);
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem)); return;
mm_device_remove_modem (self);
g_task_return_boolean (task, TRUE);
} }
g_object_unref (task);
mm_device_remove_modem (self,
(GAsyncReadyCallback)inhibit_disable_modem_remove_ready,
task);
} }
void void
@@ -899,7 +984,7 @@ set_property (GObject *object,
self->priv->plugin = g_value_dup_object (value); self->priv->plugin = g_value_dup_object (value);
break; break;
case PROP_MODEM: case PROP_MODEM:
clear_modem (self); g_assert (self->priv->modem == NULL);
self->priv->modem = g_value_dup_object (value); self->priv->modem = g_value_dup_object (value);
break; break;
case PROP_HOTPLUGGED: case PROP_HOTPLUGGED:
+10 -4
View File
@@ -80,10 +80,16 @@ void mm_device_release_port_name (MMDevice *self,
const gchar *subsystem, const gchar *subsystem,
const gchar *name); const gchar *name);
gboolean mm_device_create_modem (MMDevice *self, gboolean mm_device_create_modem (MMDevice *self,
GError **error); GError **error);
void mm_device_remove_modem (MMDevice *self); void mm_device_remove_modem (MMDevice *self,
void mm_device_initialize_modem (MMDevice *self); GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_device_remove_modem_finish (MMDevice *self,
GAsyncResult *res,
GError **error);
void mm_device_remove_modem_quick (MMDevice *self);
void mm_device_initialize_modem (MMDevice *self);
void mm_device_inhibit (MMDevice *self, void mm_device_inhibit (MMDevice *self,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
+177
View File
@@ -3232,6 +3232,8 @@ static void interface_syncing_step (GTask *task);
typedef enum { typedef enum {
SYNCING_STEP_FIRST, SYNCING_STEP_FIRST,
SYNCING_STEP_ENABLE_UNSOLICITED_EVENTS,
SYNCING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS,
SYNCING_STEP_REFRESH_3GPP_REGISTRATION, SYNCING_STEP_REFRESH_3GPP_REGISTRATION,
SYNCING_STEP_REFRESH_EPS_BEARER, SYNCING_STEP_REFRESH_EPS_BEARER,
SYNCING_STEP_LAST SYNCING_STEP_LAST
@@ -3249,6 +3251,31 @@ mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
return g_task_propagate_boolean (G_TASK (res), error); return g_task_propagate_boolean (G_TASK (res), error);
} }
#undef VOID_SYNC_REPLY_READY_FN
#define VOID_SYNC_REPLY_READY_FN(NAME,DISPLAY) \
static void \
sync_##NAME##_ready (MMIfaceModem3gpp *self, \
GAsyncResult *res, \
GTask *task) \
{ \
DisablingContext *ctx; \
g_autoptr(GError) error = NULL; \
\
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->NAME##_finish (self, res, &error); \
if (error) \
mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \
\
/* Go on to next step */ \
ctx = g_task_get_task_data (task); \
ctx->step++; \
interface_syncing_step (task); \
}
VOID_SYNC_REPLY_READY_FN (enable_unsolicited_events,
"enable unsolicited events")
VOID_SYNC_REPLY_READY_FN (enable_unsolicited_registration_events,
"enable unsolicited registration events")
static void static void
sync_eps_bearer_ready (MMIfaceModem3gpp *self, sync_eps_bearer_ready (MMIfaceModem3gpp *self,
GAsyncResult *res, GAsyncResult *res,
@@ -3327,6 +3354,33 @@ interface_syncing_step (GTask *task)
ctx->step++; ctx->step++;
/* fall through */ /* fall through */
case SYNCING_STEP_ENABLE_UNSOLICITED_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_events &&
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_events_finish) {
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_events (
self,
(GAsyncReadyCallback)sync_enable_unsolicited_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case SYNCING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_registration_events &&
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_registration_events_finish) {
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->enable_unsolicited_registration_events (
self,
get_cs_network_supported (self),
get_ps_network_supported (self),
get_eps_network_supported (self),
(GAsyncReadyCallback)sync_enable_unsolicited_registration_events_ready,
task);
return;
}
ctx->step++;
/* fall through */
case SYNCING_STEP_REFRESH_3GPP_REGISTRATION: case SYNCING_STEP_REFRESH_3GPP_REGISTRATION:
/* /*
* Refresh registration info to verify that the modem is still registered. * Refresh registration info to verify that the modem is still registered.
@@ -3381,6 +3435,129 @@ mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self,
interface_syncing_step (task); interface_syncing_step (task);
} }
typedef struct _TerseContext TerseContext;
static void interface_terse_step (GTask *task);
typedef enum {
TERSE_STEP_FIRST,
TERSE_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS,
TERSE_STEP_DISABLE_UNSOLICITED_EVENTS,
TERSE_STEP_LAST
} TerseStep;
struct _TerseContext {
TerseStep step;
};
gboolean
mm_iface_modem_3gpp_terse_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_boolean (G_TASK (res), error);
}
#undef VOID_TERSE_REPLY_READY_FN
#define VOID_TERSE_REPLY_READY_FN(NAME,DISPLAY) \
static void \
terse_##NAME##_ready (MMIfaceModem3gpp *self, \
GAsyncResult *res, \
GTask *task) \
{ \
TerseContext *ctx; \
g_autoptr(GError) error = NULL; \
\
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->NAME##_finish (self, res, &error); \
if (error) \
mm_obj_dbg (self, "couldn't %s: %s", DISPLAY, error->message); \
\
/* Go on to next step */ \
ctx = g_task_get_task_data (task); \
ctx->step++; \
interface_terse_step (task); \
}
VOID_TERSE_REPLY_READY_FN (disable_unsolicited_events,
"disable unsolicited events")
VOID_TERSE_REPLY_READY_FN (disable_unsolicited_registration_events,
"disable unsolicited registration events")
static void
interface_terse_step (GTask *task)
{
MMIfaceModem3gpp *self;
TerseContext *ctx;
self = g_task_get_source_object (task);
ctx = g_task_get_task_data (task);
switch (ctx->step) {
case TERSE_STEP_FIRST:
ctx->step++;
/* fall through */
case TERSE_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_registration_events &&
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_registration_events_finish) {
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_registration_events (
self,
get_cs_network_supported (self),
get_ps_network_supported (self),
get_eps_network_supported (self),
(GAsyncReadyCallback)terse_disable_unsolicited_registration_events_ready,
task);
mm_obj_msg (self, "terse state 3GPP (%d/%d): disable unsolicited registration events done",
ctx->step, TERSE_STEP_LAST);
return;
}
ctx->step++;
/* fall through */
case TERSE_STEP_DISABLE_UNSOLICITED_EVENTS:
if (MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_events &&
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_events_finish) {
MM_IFACE_MODEM_3GPP_GET_IFACE (self)->disable_unsolicited_events (
self,
(GAsyncReadyCallback)terse_disable_unsolicited_events_ready,
task);
mm_obj_msg (self, "terse state 3GPP (%d/%d): disable unsolicited events done",
ctx->step, TERSE_STEP_LAST);
return;
}
ctx->step++;
/* fall through */
case TERSE_STEP_LAST:
/* We are done without errors! */
g_task_return_boolean (task, TRUE);
g_object_unref (task);
return;
default:
g_assert_not_reached ();
}
g_assert_not_reached ();
}
void
mm_iface_modem_3gpp_terse (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
TerseContext *ctx;
GTask *task;
/* Create SyncingContext */
ctx = g_new0 (TerseContext, 1);
ctx->step = TERSE_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_terse_step (task);
}
#endif #endif
/*****************************************************************************/ /*****************************************************************************/
+7
View File
@@ -318,6 +318,13 @@ gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
GAsyncResult *res, GAsyncResult *res,
GError **error); GError **error);
void mm_iface_modem_3gpp_terse (MMIfaceModem3gpp *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_iface_modem_3gpp_terse_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error);
#endif #endif
/* Shutdown Modem 3GPP interface */ /* Shutdown Modem 3GPP interface */
+9
View File
@@ -32,6 +32,10 @@ static GQuark in_call_event_context_quark;
G_DEFINE_INTERFACE (MMIfaceModemVoice, mm_iface_modem_voice, MM_TYPE_IFACE_MODEM) G_DEFINE_INTERFACE (MMIfaceModemVoice, mm_iface_modem_voice, MM_TYPE_IFACE_MODEM)
static void setup_call_list_polling (MMCallList *call_list,
const gchar *call_path_added,
MMIfaceModemVoice *self);
/*****************************************************************************/ /*****************************************************************************/
void void
@@ -1781,6 +1785,7 @@ typedef enum {
IN_CALL_SETUP_STEP_FIRST, IN_CALL_SETUP_STEP_FIRST,
IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS, IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS,
IN_CALL_SETUP_STEP_AUDIO_CHANNEL, IN_CALL_SETUP_STEP_AUDIO_CHANNEL,
IN_CALL_SETUP_STEP_CHECK_POLLING,
IN_CALL_SETUP_STEP_LAST, IN_CALL_SETUP_STEP_LAST,
} InCallSetupStep; } InCallSetupStep;
@@ -1908,6 +1913,10 @@ in_call_setup_context_step (GTask *task)
} }
ctx->step++; ctx->step++;
/* fall-through */ /* fall-through */
case IN_CALL_SETUP_STEP_CHECK_POLLING:
setup_call_list_polling (NULL, NULL, self);
ctx->step++;
/* fall-through */
case IN_CALL_SETUP_STEP_LAST: case IN_CALL_SETUP_STEP_LAST:
g_task_return_boolean (task, TRUE); g_task_return_boolean (task, TRUE);
g_object_unref (task); g_object_unref (task);
+68
View File
@@ -6720,6 +6720,74 @@ mm_iface_modem_get_current_modes (MMIfaceModem *self,
/*****************************************************************************/ /*****************************************************************************/
typedef struct {
const MMIfaceModemCountBearersFlags flags;
guint count;
} CountBearersContext;
static void
bearer_count (MMBaseBearer *bearer,
CountBearersContext *ctx)
{
MMBearerStatus status;
status = mm_base_bearer_get_status (MM_BASE_BEARER (bearer));
if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED) {
/* The Multiplexed property is only set if connected, so it's enough to check
* that one to see if we're connected and multiplexed */
if (mm_gdbus_bearer_get_multiplexed (MM_GDBUS_BEARER (bearer)))
ctx->count++;
} else if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_CONNECTED) {
if (status == MM_BEARER_STATUS_CONNECTED)
ctx->count++;
} else if (ctx->flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE) {
if (status >= MM_BEARER_STATUS_CONNECTING)
ctx->count++;
} else
ctx->count++;
}
gboolean
mm_iface_modem_count_bearers (MMIfaceModem *self,
MMIfaceModemCountBearersFlags flags,
guint *out_current,
guint *out_max,
GError **error)
{
g_autoptr(MMBearerList) list = NULL;
guint max = 0;
CountBearersContext ctx = {
.flags = flags,
.count = 0,
};
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
if (!list) {
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Bearer list unavailable");
return FALSE;
}
if (flags & MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED)
max = mm_bearer_list_get_max_active_multiplexed (list);
else
max = mm_bearer_list_get_max_active (list);
mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_count, &ctx);
g_assert (max >= ctx.count);
if (out_max)
*out_max = max;
if (out_current)
*out_current = ctx.count;
return TRUE;
}
/*****************************************************************************/
static void static void
mm_iface_modem_default_init (MMIfaceModemInterface *iface) mm_iface_modem_default_init (MMIfaceModemInterface *iface)
{ {
+15
View File
@@ -620,4 +620,19 @@ void mm_iface_modem_modify_sim (MMIfaceModem *self,
void mm_iface_modem_process_sim_event (MMIfaceModem *self); void mm_iface_modem_process_sim_event (MMIfaceModem *self);
typedef enum {
/* Connecting or connected bearers */
MM_IFACE_MODEM_COUNT_BEARERS_FLAG_ACTIVE = 0x1,
/* Fully connected bearers */
MM_IFACE_MODEM_COUNT_BEARERS_FLAG_CONNECTED = 0x2,
/* Multiplexed implies connected */
MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED = 0x4,
} MMIfaceModemCountBearersFlags;
gboolean mm_iface_modem_count_bearers (MMIfaceModem *self,
MMIfaceModemCountBearersFlags flags,
guint *out_current,
guint *out_max,
GError **error);
#endif /* MM_IFACE_MODEM_H */ #endif /* MM_IFACE_MODEM_H */
+5 -4
View File
@@ -1218,8 +1218,9 @@ get_mm_access_tech_from_etsi_access_tech (guint val,
return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
case 6: case 6:
return MM_MODEM_ACCESS_TECHNOLOGY_HSPA; return MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
case 7: /* E-UTRAN */
case 9: /* E-UTRAN (NB-S1) */ case 9: /* E-UTRAN (NB-S1) */
return MM_MODEM_ACCESS_TECHNOLOGY_LTE_NB_IOT;
case 7: /* E-UTRAN */
case 10: /* E-UTRA connected to a 5GCN */ case 10: /* E-UTRA connected to a 5GCN */
return MM_MODEM_ACCESS_TECHNOLOGY_LTE; return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
case 11: /* NR connected to a 5G CN */ case 11: /* NR connected to a 5G CN */
@@ -4160,11 +4161,11 @@ mm_string_to_access_tech (const gchar *string)
* are included in other strings too. * are included in other strings too.
*/ */
len = strlen (string); len = strlen (string);
if (strncmp (string, "EVDO", 4) && (len >= 4 && !isalnum (string[4]))) if (!strncmp (string, "EVDO", 4) && (len >= 4 && !isalnum (string[4])))
act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; act |= MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
if (strncmp (string, "CDMA", 4) && (len >= 4 && !isalnum (string[4]))) if (!strncmp (string, "CDMA", 4) && (len >= 4 && !isalnum (string[4])))
act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT; act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT;
if (strncmp (string, "CDMA-EVDO", 9) && (len >= 9 && !isalnum (string[9]))) if (!strncmp (string, "CDMA-EVDO", 9) && (len >= 9 && !isalnum (string[9])))
act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0; act |= MM_MODEM_ACCESS_TECHNOLOGY_1XRTT | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0;
return act; return act;
+2 -2
View File
@@ -1482,8 +1482,6 @@ port_serial_close_force (MMPortSerial *self)
if (self->priv->forced_close) if (self->priv->forced_close)
return; return;
mm_obj_dbg (self, "forced to close port");
/* Mark as having forced the close, so that we don't warn about incorrect /* Mark as having forced the close, so that we don't warn about incorrect
* open counts */ * open counts */
self->priv->forced_close = TRUE; self->priv->forced_close = TRUE;
@@ -1493,6 +1491,8 @@ port_serial_close_force (MMPortSerial *self)
/* If already closed, done */ /* If already closed, done */
if (self->priv->open_count > 0) { if (self->priv->open_count > 0) {
mm_obj_dbg (self, "forced to close port");
_close_internal (self, TRUE); _close_internal (self, TRUE);
/* Notify about the forced close status */ /* Notify about the forced close status */
+192
View File
@@ -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 2025 Dan Williams <dan@ioncontrol.co>
*/
#include <gio/gio.h>
#include "mm-utils.h"
#include "mm-sleep-context.h"
static void log_object_iface_init (MMLogObjectInterface *iface);
G_DEFINE_TYPE_EXTENDED (MMSleepContext, mm_sleep_context, G_TYPE_OBJECT, 0,
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
struct _MMSleepContextPrivate {
GTask *task;
guint timeout_id;
gint64 timeout_start;
};
enum {
DONE,
LAST_SIGNAL,
};
static guint signals[LAST_SIGNAL] = { 0 };
/*****************************************************************************/
static gchar *
log_object_build_id (MMLogObject *_self)
{
return g_strdup_printf ("sleep-context");
}
/*****************************************************************************/
static void
timeout_cleanup (MMSleepContext *self)
{
if (self->priv->timeout_id)
g_source_remove (self->priv->timeout_id);
self->priv->timeout_id = 0;
self->priv->timeout_start = 0;
}
void
mm_sleep_context_complete (MMSleepContext *self,
GError *error)
{
timeout_cleanup (self);
if (error) {
mm_obj_dbg (self, "completing with error '%s'", error->message);
g_task_return_error (self->priv->task, error);
} else {
mm_obj_dbg (self, "completing with success");
g_task_return_boolean (self->priv->task, TRUE);
}
g_clear_object (&self->priv->task);
}
static gboolean
operation_timeout (MMSleepContext *self)
{
mm_obj_dbg (self, "timeout");
mm_sleep_context_complete (self,
g_error_new_literal (G_IO_ERROR,
G_IO_ERROR_TIMED_OUT,
"timeout waiting for sleep preparation to complete"));
return G_SOURCE_REMOVE;
}
static void
operation_ready (MMSleepContext *self,
GAsyncResult *res)
{
g_autoptr(GError) error = NULL;
gboolean success;
mm_obj_dbg (self, "notifying listeners task is done");
success = g_task_propagate_boolean (G_TASK (res), &error);
g_assert (success || error);
g_signal_emit (self, signals[DONE], 0, error);
}
static void
timeout_add (MMSleepContext *self,
guint interval)
{
timeout_cleanup (self);
self->priv->timeout_start = g_get_monotonic_time ();
self->priv->timeout_id = g_timeout_add_seconds (interval,
(GSourceFunc)operation_timeout,
self);
}
void
mm_sleep_context_timeout_backoff (MMSleepContext *self,
guint more_seconds)
{
gint64 cur_time;
gint64 new_interval;
new_interval = more_seconds * G_USEC_PER_SEC;
if (self->priv->timeout_start) {
cur_time = g_get_monotonic_time ();
new_interval += (cur_time - self->priv->timeout_start);
}
timeout_add (self, new_interval / G_USEC_PER_SEC);
}
/*****************************************************************************/
MMSleepContext *
mm_sleep_context_new (guint timeout_seconds)
{
MMSleepContext *self;
self = MM_SLEEP_CONTEXT (g_object_new (MM_TYPE_SLEEP_CONTEXT, NULL));
self->priv->task = g_task_new (self,
NULL,
(GAsyncReadyCallback)operation_ready,
NULL);
timeout_add (self, timeout_seconds);
mm_obj_dbg (self, "new context with %d second timeout", timeout_seconds);
return self;
}
static void
log_object_iface_init (MMLogObjectInterface *iface)
{
iface->build_id = log_object_build_id;
}
static void
mm_sleep_context_init (MMSleepContext *self)
{
/* Initialize opaque pointer to private data */
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
MM_TYPE_SLEEP_CONTEXT,
MMSleepContextPrivate);
}
static void
dispose (GObject *object)
{
MMSleepContext *self = MM_SLEEP_CONTEXT (object);
timeout_cleanup (self);
/* Task must always be completed and cleared before disposal */
g_assert (self->priv->task == NULL);
G_OBJECT_CLASS (mm_sleep_context_parent_class)->dispose (object);
}
static void
mm_sleep_context_class_init (MMSleepContextClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
g_type_class_add_private (object_class, sizeof (MMSleepContextPrivate));
/* Virtual methods */
object_class->dispose = dispose;
signals[DONE] = g_signal_new (MM_SLEEP_CONTEXT_DONE,
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (MMSleepContextClass, done),
NULL, /* accumulator */
NULL, /* accumulator data */
g_cclosure_marshal_generic,
G_TYPE_NONE,
1,
G_TYPE_ERROR);
}
+65
View File
@@ -0,0 +1,65 @@
/* -*- 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 2025 Dan Williams <dan@ioncontrol.co>
*/
#ifndef MM_SLEEP_CONTEXT_H
#define MM_SLEEP_CONTEXT_H
#include <glib-object.h>
#include <glib.h>
G_BEGIN_DECLS
#define MM_TYPE_SLEEP_CONTEXT (mm_sleep_context_get_type ())
#define MM_SLEEP_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SLEEP_CONTEXT, MMSleepContext))
#define MM_SLEEP_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SLEEP_CONTEXT, MMSleepContextClass))
#define MM_IS_SLEEP_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SLEEP_CONTEXT))
#define MM_IS_SLEEP_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SLEEP_CONTEXT))
#define MM_SLEEP_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SLEEP_CONTEXT, MMSleepContextClass))
#define MM_SLEEP_CONTEXT_DONE "done"
typedef struct _MMSleepContext MMSleepContext;
typedef struct _MMSleepContextClass MMSleepContextClass;
typedef struct _MMSleepContextPrivate MMSleepContextPrivate;
struct _MMSleepContext {
GObject parent;
MMSleepContextPrivate *priv;
};
struct _MMSleepContextClass {
GObjectClass parent;
void (*done) (MMSleepContext *ctx, GError *error);
};
GType mm_sleep_context_get_type (void);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSleepContext, g_object_unref)
MMSleepContext *mm_sleep_context_new (guint timeout_seconds);
void mm_sleep_context_timeout_backoff (MMSleepContext *self,
guint more_seconds);
/* The MMSleepContext assumes ownership of @error */
void mm_sleep_context_complete (MMSleepContext *self,
GError *error);
void mm_sleep_context_dispose (MMSleepContext *self);
G_END_DECLS
#endif /* MM_SLEEP_CONTEXT_H */
+49 -17
View File
@@ -29,6 +29,7 @@
#include "mm-log-object.h" #include "mm-log-object.h"
#include "mm-utils.h" #include "mm-utils.h"
#include "mm-sleep-monitor.h" #include "mm-sleep-monitor.h"
#include "mm-sleep-context.h"
#define PD_NAME "org.chromium.PowerManager" #define PD_NAME "org.chromium.PowerManager"
#define PD_PATH "/org/chromium/PowerManager" #define PD_PATH "/org/chromium/PowerManager"
@@ -37,13 +38,15 @@
struct _MMSleepMonitor { struct _MMSleepMonitor {
GObject parent_instance; GObject parent_instance;
GDBusProxy *pd_proxy; GDBusProxy *pd_proxy;
MMSleepContext *sleep_ctx;
guint sleep_done_id;
}; };
struct _MMSleepMonitorClass { struct _MMSleepMonitorClass {
GObjectClass parent_class; GObjectClass parent_class;
void (*sleeping) (MMSleepMonitor *monitor); void (*sleeping) (MMSleepMonitor *monitor, MMSleepContext *ctx);
void (*resuming) (MMSleepMonitor *monitor); void (*resuming) (MMSleepMonitor *monitor);
}; };
@@ -70,6 +73,26 @@ log_object_build_id (MMLogObject *_self)
/********************************************************************/ /********************************************************************/
static void
cleanup_sleep_context (MMSleepMonitor *self)
{
if (self->sleep_ctx && self->sleep_done_id)
g_signal_handler_disconnect (self->sleep_ctx, self->sleep_done_id);
self->sleep_done_id = 0;
g_clear_object (&self->sleep_ctx);
}
static void
sleep_context_done (MMSleepContext *sleep_ctx,
GError *error,
MMSleepMonitor *self)
{
if (error)
mm_obj_warn (self, "sleep context failed: %s", error->message);
mm_obj_msg (self, "ready to sleep");
cleanup_sleep_context (self);
}
static void static void
signal_cb (GDBusProxy *proxy, signal_cb (GDBusProxy *proxy,
const gchar *sendername, const gchar *sendername,
@@ -79,14 +102,24 @@ signal_cb (GDBusProxy *proxy,
{ {
MMSleepMonitor *self = data; MMSleepMonitor *self = data;
if (proxy == self->pd_proxy) { if (proxy != self->pd_proxy)
if (strcmp (signalname, "SuspendImminent") == 0) { return;
mm_obj_msg (self, "system suspend signal from powerd");
g_signal_emit (self, signals[SLEEPING], 0); if (strcmp (signalname, "SuspendImminent") == 0) {
} else if (strcmp (signalname, "SuspendDone") == 0) { if (self->sleep_ctx || self->sleep_done_id) {
mm_obj_msg (self, "system resume signal from powerd"); mm_obj_warn (self, "clearing unfinished sleep context...");
g_signal_emit (self, signals[RESUMING], 0); cleanup_sleep_context (self);
} }
self->sleep_ctx = mm_sleep_context_new (5);
self->sleep_done_id = g_signal_connect (self->sleep_ctx,
MM_SLEEP_CONTEXT_DONE,
(GCallback)sleep_context_done,
self);
g_signal_emit (self, signals[SLEEPING], 0, self->sleep_ctx);
} else if (strcmp (signalname, "SuspendDone") == 0) {
mm_obj_msg (self, "system resume signal from powerd");
g_signal_emit (self, signals[RESUMING], 0);
} }
} }
@@ -120,15 +153,14 @@ mm_sleep_monitor_init (MMSleepMonitor *self)
} }
static void static void
finalize (GObject *object) dispose (GObject *object)
{ {
MMSleepMonitor *self = MM_SLEEP_MONITOR (object); MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
if (self->pd_proxy) cleanup_sleep_context (self);
g_object_unref (self->pd_proxy); g_clear_object (&self->pd_proxy);
if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL) G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->dispose (object);
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
} }
static void static void
@@ -144,7 +176,7 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
gobject_class = G_OBJECT_CLASS (klass); gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = finalize; gobject_class->dispose = dispose;
signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING, signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
MM_TYPE_SLEEP_MONITOR, MM_TYPE_SLEEP_MONITOR,
@@ -152,8 +184,8 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping), G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
NULL, /* accumulator */ NULL, /* accumulator */
NULL, /* accumulator data */ NULL, /* accumulator data */
g_cclosure_marshal_VOID__VOID, g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 0); G_TYPE_NONE, 1, G_TYPE_CANCELLABLE);
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING, signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
MM_TYPE_SLEEP_MONITOR, MM_TYPE_SLEEP_MONITOR,
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
+53 -11
View File
@@ -29,6 +29,7 @@
#include "mm-log-object.h" #include "mm-log-object.h"
#include "mm-utils.h" #include "mm-utils.h"
#include "mm-sleep-monitor.h" #include "mm-sleep-monitor.h"
#include "mm-sleep-context.h"
#define SD_NAME "org.freedesktop.login1" #define SD_NAME "org.freedesktop.login1"
#define SD_PATH "/org/freedesktop/login1" #define SD_PATH "/org/freedesktop/login1"
@@ -37,14 +38,16 @@
struct _MMSleepMonitor { struct _MMSleepMonitor {
GObject parent_instance; GObject parent_instance;
GDBusProxy *sd_proxy; GDBusProxy *sd_proxy;
gint inhibit_fd; gint inhibit_fd;
MMSleepContext *sleep_ctx;
guint sleep_done_id;
}; };
struct _MMSleepMonitorClass { struct _MMSleepMonitorClass {
GObjectClass parent_class; GObjectClass parent_class;
void (*sleeping) (MMSleepMonitor *monitor); void (*sleeping) (MMSleepMonitor *monitor, MMSleepContext *ctx);
void (*resuming) (MMSleepMonitor *monitor); void (*resuming) (MMSleepMonitor *monitor);
}; };
@@ -137,6 +140,27 @@ take_inhibitor (MMSleepMonitor *self)
self); self);
} }
static void
cleanup_sleep_context (MMSleepMonitor *self)
{
if (self->sleep_ctx && self->sleep_done_id)
g_signal_handler_disconnect (self->sleep_ctx, self->sleep_done_id);
self->sleep_done_id = 0;
g_clear_object (&self->sleep_ctx);
}
static void
sleep_context_done (MMSleepContext *sleep_ctx,
GError *error,
MMSleepMonitor *self)
{
if (error)
mm_obj_warn (self, "sleep context failed: %s", error->message);
mm_obj_msg (self, "ready to sleep; dropping inhibitor");
drop_inhibitor (self);
cleanup_sleep_context (self);
}
static void static void
signal_cb (GDBusProxy *proxy, signal_cb (GDBusProxy *proxy,
const gchar *sendername, const gchar *sendername,
@@ -154,8 +178,17 @@ signal_cb (GDBusProxy *proxy,
if (is_about_to_suspend) { if (is_about_to_suspend) {
mm_obj_msg (self, "system is about to suspend"); mm_obj_msg (self, "system is about to suspend");
g_signal_emit (self, signals[SLEEPING], 0);
drop_inhibitor (self); if (self->sleep_ctx || self->sleep_done_id) {
mm_obj_warn (self, "clearing unfinished sleep context...");
cleanup_sleep_context (self);
}
self->sleep_ctx = mm_sleep_context_new (5);
self->sleep_done_id = g_signal_connect (self->sleep_ctx,
MM_SLEEP_CONTEXT_DONE,
(GCallback)sleep_context_done,
self);
g_signal_emit (self, signals[SLEEPING], 0, self->sleep_ctx);
} else { } else {
mm_obj_msg (self, "system is resuming"); mm_obj_msg (self, "system is resuming");
take_inhibitor (self); take_inhibitor (self);
@@ -219,17 +252,25 @@ mm_sleep_monitor_init (MMSleepMonitor *self)
(GAsyncReadyCallback) on_proxy_acquired, self); (GAsyncReadyCallback) on_proxy_acquired, self);
} }
static void
dispose (GObject *object)
{
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
cleanup_sleep_context (self);
g_clear_object (&self->sd_proxy);
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->dispose (object);
}
static void static void
finalize (GObject *object) finalize (GObject *object)
{ {
MMSleepMonitor *self = MM_SLEEP_MONITOR (object); MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
drop_inhibitor (self); 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);
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
} }
static void static void
@@ -245,6 +286,7 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
gobject_class = G_OBJECT_CLASS (klass); gobject_class = G_OBJECT_CLASS (klass);
gobject_class->dispose = dispose;
gobject_class->finalize = finalize; gobject_class->finalize = finalize;
signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING, signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
@@ -253,8 +295,8 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping), G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
NULL, /* accumulator */ NULL, /* accumulator */
NULL, /* accumulator data */ NULL, /* accumulator data */
g_cclosure_marshal_VOID__VOID, g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 0); G_TYPE_NONE, 1, MM_TYPE_SLEEP_CONTEXT);
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING, signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
MM_TYPE_SLEEP_MONITOR, MM_TYPE_SLEEP_MONITOR,
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,
@@ -794,7 +794,7 @@ mm_cinterion_parse_sxrat_test (const gchar *response,
return FALSE; return FALSE;
} }
r = g_regex_new ("\\^SXRAT:\\s*\\(([^\\)]*)\\),\\(([^\\)]*)\\)(,\\(([^\\)]*)\\))?(?:\\r\\n)?", r = g_regex_new ("\\^SXRAT:\\s*\\(([^\\)]*)\\),\\s*\\(([^\\)]*)\\)(,\\s*\\(([^\\)]*)\\))?(?:\\r\\n)?",
G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW,
0, NULL); 0, NULL);
@@ -1826,6 +1826,38 @@ test_sxrat_response_els61 (void)
g_array_unref (expected_pref2); g_array_unref (expected_pref2);
} }
static void
test_sxrat_response_pls63_w (void)
{
GArray *expected_rat;
GArray *expected_pref1;
GArray *expected_pref2;
const guint vals[] = { 0, 2, 3 };
guint val;
const gchar *response =
"^SXRAT: (0-6), (0,2,3), (0,2,3)\r\n"
"\r\n";
expected_rat = g_array_sized_new (FALSE, FALSE, sizeof (guint), 7);
for (val = 0; val < 7; val++)
g_array_append_val (expected_rat, val);
expected_pref1 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
g_array_append_vals (expected_pref1, &vals, 3);
expected_pref2 = g_array_sized_new (FALSE, FALSE, sizeof (guint), 3);
g_array_append_vals (expected_pref2, &vals, 3);
common_test_sxrat (response,
expected_rat,
expected_pref1,
expected_pref2);
g_array_unref (expected_rat);
g_array_unref (expected_pref1);
g_array_unref (expected_pref2);
}
static void static void
test_sxrat_response_other (void) test_sxrat_response_other (void)
{ {
@@ -2177,6 +2209,7 @@ int main (int argc, char **argv)
g_test_add_func ("/MM/cinterion/sgauth", test_sgauth_response); g_test_add_func ("/MM/cinterion/sgauth", test_sgauth_response);
g_test_add_func ("/MM/cinterion/sxrat", test_sxrat); g_test_add_func ("/MM/cinterion/sxrat", test_sxrat);
g_test_add_func ("/MM/cinterion/sxrat/response/els61", test_sxrat_response_els61); g_test_add_func ("/MM/cinterion/sxrat/response/els61", test_sxrat_response_els61);
g_test_add_func ("/MM/cinterion/sxrat/response/pls63w", test_sxrat_response_pls63_w);
g_test_add_func ("/MM/cinterion/sxrat/response/other", test_sxrat_response_other); g_test_add_func ("/MM/cinterion/sxrat/response/other", test_sxrat_response_other);
g_test_add_func ("/MM/cinterion/cops/only-mode-2g", test_cops_only_mode_2g); g_test_add_func ("/MM/cinterion/cops/only-mode-2g", test_cops_only_mode_2g);
g_test_add_func ("/MM/cinterion/cops/only-mode-3g", test_cops_only_mode_3g); g_test_add_func ("/MM/cinterion/cops/only-mode-3g", test_cops_only_mode_3g);
+2 -2
View File
@@ -75,8 +75,8 @@ get_private (MMSharedFibocom *self)
priv->class_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_class (self); priv->class_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_class (self);
/* Setup firmware interface of parent class */ /* Setup firmware interface of parent class */
g_assert (MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_firmware_interface); if (MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_firmware_interface)
priv->iface_modem_firmware_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_firmware_interface (self); priv->iface_modem_firmware_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_firmware_interface (self);
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
} }
+25
View File
@@ -4866,6 +4866,29 @@ test_mnc_length (void)
/*****************************************************************************/ /*****************************************************************************/
typedef struct {
const gchar *str;
const MMModemAccessTechnology act;
} TestActData;
static const TestActData test_act_data[] = {
{ "EDGE", MM_MODEM_ACCESS_TECHNOLOGY_EDGE },
/* Ensure WCDMA does not get counted as 3GPP2 CDMA */
{ "WCDMA", MM_MODEM_ACCESS_TECHNOLOGY_UMTS },
{ "CDMA-EVDO", MM_MODEM_ACCESS_TECHNOLOGY_1XRTT | MM_MODEM_ACCESS_TECHNOLOGY_EVDO0 },
};
static void
test_string_to_access_tech (void)
{
guint i;
for (i = 0; i < G_N_ELEMENTS (test_act_data); i++)
g_assert_cmpint (mm_string_to_access_tech (test_act_data[i].str), ==, test_act_data[i].act);
}
/*****************************************************************************/
#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL) #define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (GTestFixtureFunc) t, NULL)
int main (int argc, char **argv) int main (int argc, char **argv)
@@ -5111,6 +5134,8 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_spn_to_utf8, NULL)); g_test_suite_add (suite, TESTCASE (test_spn_to_utf8, NULL));
g_test_suite_add (suite, TESTCASE (test_mnc_length, NULL)); g_test_suite_add (suite, TESTCASE (test_mnc_length, NULL));
g_test_suite_add (suite, TESTCASE (test_string_to_access_tech, NULL));
result = g_test_run (); result = g_test_run ();
reg_test_data_free (reg_data); reg_test_data_free (reg_data);