Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d00f3ccde2
|
||
|
|
befee319b4
|
||
|
|
f2b9ab1ad7 | ||
|
|
5160a4f409 | ||
|
|
8b51fb4385 | ||
|
|
79d495c892 | ||
|
|
e867a49d92 | ||
|
|
b59666f484 | ||
|
|
d22bc4d402 | ||
|
|
3a1184785b | ||
|
|
4a3aba8928 | ||
|
|
19f6d15edf | ||
|
|
958bc69299 | ||
|
|
3827a63d33 | ||
|
|
46cc38570d | ||
|
|
dc3a7f0d3b | ||
|
|
9f0db52ed0 | ||
|
|
240a9145ad | ||
|
|
018e440848 | ||
|
|
0c7d573969 | ||
|
|
1ec2b86239 | ||
|
|
4fa4084600 | ||
|
|
dae00a9f2f | ||
|
|
3157a90c5b | ||
|
|
34148f8e8b | ||
|
|
c146dd6094 | ||
|
|
d80f98da01 | ||
|
|
b97f864acd | ||
|
|
5e29ece5ec | ||
|
|
e12b3adb35 | ||
|
|
649350dfff | ||
|
|
18521b6882 | ||
|
|
7fc6788737 |
@@ -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
|
||||
-------------------------------------------
|
||||
This is a new stable release of ModemManager.
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
This object also controls any process-wide operation, such as the log
|
||||
level being used by the daemon.
|
||||
</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 id="ref-dbus-object-modem">
|
||||
@@ -129,22 +129,22 @@
|
||||
or the
|
||||
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.ModemCdma">CDMA interface</link>.
|
||||
</para>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.CellBroadcast.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml"/>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml"/>
|
||||
<!--xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Contacts.xml"/-->
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Simple.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.ModemCdma.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Messaging.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Location.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Time.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Voice.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.CellBroadcast.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Firmware.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Sar.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Signal.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Oma.xml"/>
|
||||
<!--xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Modem.Contacts.xml"/-->
|
||||
</chapter>
|
||||
|
||||
<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
|
||||
modem into connected state.
|
||||
</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 id="ref-dbus-object-sim">
|
||||
@@ -166,7 +166,7 @@
|
||||
expose up to one SIM object, which allows SIM-specific actions such as PIN
|
||||
unlocking.
|
||||
</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 id="ref-dbus-object-sms">
|
||||
@@ -176,7 +176,7 @@
|
||||
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Messaging">Messaging interface</link>
|
||||
will export one SMS object for each SMS stored in the device.
|
||||
</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 id="ref-dbus-object-call">
|
||||
@@ -186,17 +186,17 @@
|
||||
<link linkend="gdbus-org.freedesktop.ModemManager1.Modem.Voice">Voice interface</link>
|
||||
will export one Call object for each Call managed in the device.
|
||||
</para>
|
||||
<xi:include href="../../../../libmm-glib/generated/mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml"/>
|
||||
<xi:include href="mm-gdbus-doc-org.freedesktop.ModemManager1.Call.xml"/>
|
||||
</chapter>
|
||||
|
||||
<chapter id="ref-dbus-object-call">
|
||||
<chapter id="ref-dbus-object-cbm">
|
||||
<title>The <literal>/org/freedesktop/ModemManager/CBM</literal> objects</title>
|
||||
<para>
|
||||
Modems implementing the
|
||||
<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.
|
||||
</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>
|
||||
|
||||
</part>
|
||||
|
||||
@@ -22,6 +22,9 @@ expand_content_files = [
|
||||
'ModemManager-overview.xml',
|
||||
# FIXME: workaround because only strings can be included and not custom targets (gen_docs)
|
||||
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Bearer.xml',
|
||||
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.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.Location.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.ModemCdma.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.Simple.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.Sim.xml',
|
||||
generated_build_dir / 'mm-gdbus-doc-org.freedesktop.ModemManager1.Sms.xml',
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
|
||||
project(
|
||||
'ModemManager', 'c',
|
||||
version: '1.24.0',
|
||||
version: '1.24.2',
|
||||
license: 'GPL2',
|
||||
default_options: [
|
||||
'buildtype=debugoptimized',
|
||||
|
||||
@@ -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 *
|
||||
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 NULL;
|
||||
return get_lower_device_name_by_iflink (sysfs_path);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@@ -338,10 +338,21 @@ kernel_device_get_name (MMKernelDevice *_self)
|
||||
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 *
|
||||
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 *
|
||||
|
||||
+46
-15
@@ -59,14 +59,19 @@ quit_cb (gpointer user_data)
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
|
||||
static void
|
||||
sleeping_cb (MMSleepMonitor *sleep_monitor)
|
||||
sleeping_cb (MMSleepMonitor *sleep_monitor,
|
||||
MMSleepContext *ctx)
|
||||
{
|
||||
if (mm_context_get_test_low_power_suspend_resume ()) {
|
||||
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 {
|
||||
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
|
||||
sleeping_quick_cb (MMSleepMonitor *sleep_monitor)
|
||||
sleeping_quick_cb (MMSleepMonitor *sleep_monitor,
|
||||
MMSleepContext *ctx)
|
||||
{
|
||||
if (mm_context_get_test_low_power_suspend_resume ()) {
|
||||
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");
|
||||
}
|
||||
|
||||
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
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -241,28 +267,33 @@ main (int argc, char *argv[])
|
||||
loop = NULL;
|
||||
|
||||
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
|
||||
* forever: if disabling the modems takes longer than 20s, just
|
||||
* shutdown anyway. */
|
||||
timer = g_timer_new ();
|
||||
while (mm_base_manager_num_modems (manager) &&
|
||||
g_timer_elapsed (timer, NULL) < (gdouble)MAX_SHUTDOWN_TIME_SECS) {
|
||||
GMainContext *ctx = g_main_loop_get_context (inner);
|
||||
while (mm_base_manager_num_modems (manager))
|
||||
g_main_loop_run (inner);
|
||||
|
||||
g_main_context_iteration (ctx, FALSE);
|
||||
g_usleep (50);
|
||||
}
|
||||
g_signal_handler_disconnect (ctx, sleep_done_id);
|
||||
g_object_unref (ctx);
|
||||
|
||||
if (mm_base_manager_num_modems (manager))
|
||||
mm_warn ("disabling modems took too long, shutting down with %u modems around",
|
||||
mm_base_manager_num_modems (manager));
|
||||
|
||||
g_object_unref (manager);
|
||||
g_timer_destroy (timer);
|
||||
}
|
||||
|
||||
g_main_loop_unref (inner);
|
||||
|
||||
@@ -299,6 +299,7 @@ sources = files(
|
||||
'mm-port-probe-at.c',
|
||||
'mm-private-boxed-types.c',
|
||||
'mm-sms-list.c',
|
||||
'mm-sleep-context.c',
|
||||
)
|
||||
|
||||
sources += daemon_enums_sources
|
||||
|
||||
+332
-116
@@ -17,6 +17,8 @@
|
||||
* Copyright (C) 2011 - 2016 Aleksander Morgado <aleksander@aleksander.es>
|
||||
*/
|
||||
|
||||
#include "gio/gio.h"
|
||||
#include "glib.h"
|
||||
#include <config.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 *
|
||||
find_device_by_port (MMBaseManager *manager,
|
||||
MMKernelDevice *port)
|
||||
@@ -367,7 +352,7 @@ device_removed (MMBaseManager *self,
|
||||
|
||||
/* The device may have already been removed from the tracking HT, we
|
||||
* just try to remove it and if it fails, we ignore it */
|
||||
mm_device_remove_modem (device);
|
||||
mm_device_remove_modem_quick (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);
|
||||
}
|
||||
|
||||
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
|
||||
additional_port (MMBaseManager *self,
|
||||
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);
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (device);
|
||||
device_support_check_add_all_ports (self, device);
|
||||
mm_device_remove_modem (device,
|
||||
(GAsyncReadyCallback)additional_port_modem_remove_ready,
|
||||
g_object_ref (self));
|
||||
}
|
||||
|
||||
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 {
|
||||
MMBaseManager *self;
|
||||
gboolean low_power;
|
||||
gboolean remove;
|
||||
} DisableContext;
|
||||
MMBaseModem *modem;
|
||||
MMBaseManagerCleanupFlags flags;
|
||||
DeviceCleanupStep step;
|
||||
} DeviceCleanupContext;
|
||||
|
||||
static void
|
||||
disable_context_free (DisableContext *ctx)
|
||||
static void device_cleanup_step (GTask *task);
|
||||
|
||||
static DeviceCleanupContext *
|
||||
device_cleanup_context_new (MMBaseModem *modem,
|
||||
MMBaseManagerCleanupFlags flags)
|
||||
{
|
||||
g_object_unref (ctx->self);
|
||||
g_slice_free (DisableContext, ctx);
|
||||
DeviceCleanupContext *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
|
||||
remove_device_after_disable (MMBaseModem *modem,
|
||||
DisableContext *ctx)
|
||||
device_cleanup_context_free (DeviceCleanupContext *ctx)
|
||||
{
|
||||
MMDevice *device;
|
||||
|
||||
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);
|
||||
g_clear_object (&ctx->modem);
|
||||
g_slice_free (DeviceCleanupContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_low_power_ready (MMIfaceModem *modem,
|
||||
GAsyncResult *res,
|
||||
DisableContext *ctx)
|
||||
modem_remove_ready (MMDevice *device,
|
||||
GAsyncResult *res,
|
||||
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))
|
||||
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)
|
||||
remove_device_after_disable (MM_BASE_MODEM (modem), ctx);
|
||||
else
|
||||
disable_context_free (ctx);
|
||||
ctx->step++;
|
||||
device_cleanup_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
shutdown_disable_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
DisableContext *ctx)
|
||||
cleanup_disable_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
g_autoptr(GError) error = NULL;
|
||||
DeviceCleanupContext *ctx;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
/* We don't care about errors disabling at this point */
|
||||
if (!mm_base_modem_disable_finish (modem, res, &error)) {
|
||||
mm_obj_info (ctx->self, "disabling modem failed: %s", error->message);
|
||||
}
|
||||
/* Bring the modem to low power mode if requested */
|
||||
else if (ctx->low_power) {
|
||||
mm_iface_modem_set_power_state (MM_IFACE_MODEM (modem),
|
||||
MM_MODEM_POWER_STATE_LOW,
|
||||
(GAsyncReadyCallback)shutdown_low_power_ready,
|
||||
ctx);
|
||||
return;
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
if (mm_base_modem_disable_finish (modem, res, &error)) {
|
||||
ctx->step++;
|
||||
} else {
|
||||
mm_obj_info (modem, "disabling modem failed: %s", error->message);
|
||||
|
||||
/* If disabling failed there's no point in trying to low-power the
|
||||
* modem; just remove it.
|
||||
*/
|
||||
ctx->step = DEVICE_CLEANUP_STEP_REMOVE;
|
||||
}
|
||||
|
||||
if (ctx->remove)
|
||||
remove_device_after_disable (modem, ctx);
|
||||
else
|
||||
disable_context_free (ctx);
|
||||
device_cleanup_step (task);
|
||||
}
|
||||
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
static void
|
||||
foreach_disable (gpointer key,
|
||||
MMDevice *device,
|
||||
DisableContext *foreach_ctx)
|
||||
cleanup_terse_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
MMBaseModem *modem;
|
||||
DisableContext *ctx;
|
||||
DeviceCleanupContext *ctx;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
modem = mm_device_peek_modem (device);
|
||||
if (!modem)
|
||||
return;
|
||||
ctx = g_task_get_task_data (task);
|
||||
if (!mm_base_modem_terse_finish (modem, res, &error))
|
||||
mm_obj_info (modem, "setting terse failed: %s", error->message);
|
||||
|
||||
ctx = g_slice_new0 (DisableContext);
|
||||
ctx->self = g_object_ref (foreach_ctx->self);
|
||||
ctx->low_power = foreach_ctx->low_power;
|
||||
ctx->remove = foreach_ctx->remove;
|
||||
ctx->step++;
|
||||
|
||||
mm_base_modem_disable (modem,
|
||||
MM_BASE_MODEM_OPERATION_LOCK_REQUIRED,
|
||||
MM_BASE_MODEM_OPERATION_PRIORITY_OVERRIDE,
|
||||
(GAsyncReadyCallback)shutdown_disable_ready,
|
||||
ctx);
|
||||
device_cleanup_step (task);
|
||||
}
|
||||
#endif
|
||||
|
||||
static gboolean
|
||||
foreach_remove (gpointer key,
|
||||
MMDevice *device,
|
||||
MMBaseManager *self)
|
||||
has_cleanup_flag (MMBaseManagerCleanupFlags flags,
|
||||
MMBaseManagerCleanupFlags expected)
|
||||
{
|
||||
MMBaseModem *modem;
|
||||
return flags & expected;
|
||||
}
|
||||
|
||||
modem = mm_device_peek_modem (device);
|
||||
if (modem)
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (device);
|
||||
return TRUE;
|
||||
static void
|
||||
device_cleanup_step (GTask *task)
|
||||
{
|
||||
DeviceCleanupContext *ctx;
|
||||
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
|
||||
mm_base_manager_shutdown (MMBaseManager *self,
|
||||
gboolean disable,
|
||||
gboolean low_power,
|
||||
gboolean remove)
|
||||
mm_base_manager_cleanup (MMBaseManager *self,
|
||||
MMBaseManagerCleanupFlags flags,
|
||||
MMSleepContext *sleep_ctx)
|
||||
{
|
||||
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 (MM_IS_BASE_MANAGER (self));
|
||||
|
||||
/* Cancel all ongoing auth requests */
|
||||
g_cancellable_cancel (self->priv->authp_cancellable);
|
||||
|
||||
if (disable) {
|
||||
DisableContext foreach_ctx = {
|
||||
.self = self,
|
||||
.low_power = low_power,
|
||||
.remove = remove,
|
||||
};
|
||||
g_hash_table_foreach (self->priv->devices, (GHFunc)foreach_disable, &foreach_ctx);
|
||||
disable = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_DISABLE);
|
||||
low_power = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_LOW_POWER);
|
||||
remove = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_REMOVE);
|
||||
terse = has_cleanup_flag (flags, MM_BASE_MANAGER_CLEANUP_TERSE);
|
||||
mm_obj_dbg (self,
|
||||
"manager shutting down... (%s%s%s%s%s%s%s)",
|
||||
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
|
||||
* has to iterate the mainloop until all devices have been disabled and
|
||||
* removed.
|
||||
*/
|
||||
return;
|
||||
ctx = manager_cleanup_context_new (self, sleep_ctx);
|
||||
|
||||
g_hash_table_iter_init (&iter, self->priv->devices);
|
||||
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &device)) {
|
||||
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) {
|
||||
/* Otherwise, just remove directly */
|
||||
g_hash_table_foreach_remove (self->priv->devices, (GHRFunc)foreach_remove, self);
|
||||
}
|
||||
/* Increase cleanup timeout for each connected bearer; disconnecting a
|
||||
* bearer can take many seconds.
|
||||
*/
|
||||
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
|
||||
@@ -1713,7 +1929,7 @@ handle_set_profile (MmGdbusTest *skeleton,
|
||||
out:
|
||||
|
||||
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));
|
||||
mm_dbus_method_invocation_return_gerror (invocation, error);
|
||||
g_error_free (error);
|
||||
|
||||
+12
-4
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "mm-filter.h"
|
||||
#include "mm-gdbus-manager.h"
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
#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))
|
||||
@@ -71,10 +72,17 @@ MMBaseManager *mm_base_manager_new (GDBusConnection *bus,
|
||||
void mm_base_manager_start (MMBaseManager *manager,
|
||||
gboolean manual_scan);
|
||||
|
||||
void mm_base_manager_shutdown (MMBaseManager *manager,
|
||||
gboolean disable,
|
||||
gboolean power_low,
|
||||
gboolean remove);
|
||||
typedef enum {
|
||||
MM_BASE_MANAGER_CLEANUP_NONE = 0x0,
|
||||
MM_BASE_MANAGER_CLEANUP_DISABLE = 0x1,
|
||||
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
|
||||
void mm_base_manager_sync (MMBaseManager *manager);
|
||||
|
||||
+206
-16
@@ -87,6 +87,7 @@ struct _MMBaseModemPrivate {
|
||||
* should be done by the modem. */
|
||||
GCancellable *cancellable;
|
||||
gulong invalid_if_cancelled;
|
||||
guint invalid_from_idle;
|
||||
|
||||
gchar *device;
|
||||
gchar *physdev;
|
||||
@@ -101,6 +102,7 @@ struct _MMBaseModemPrivate {
|
||||
gboolean hotplugged;
|
||||
gboolean valid;
|
||||
gboolean reprobe;
|
||||
gboolean torn_down;
|
||||
|
||||
guint max_timeouts;
|
||||
|
||||
@@ -728,6 +730,7 @@ typedef enum {
|
||||
STATE_OPERATION_TYPE_DISABLE,
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
STATE_OPERATION_TYPE_SYNC,
|
||||
STATE_OPERATION_TYPE_TERSE,
|
||||
#endif
|
||||
} StateOperationType;
|
||||
|
||||
@@ -853,6 +856,12 @@ state_operation (MMBaseModem *self,
|
||||
ctx->operation = MM_BASE_MODEM_GET_CLASS (self)->sync;
|
||||
ctx->operation_finish = MM_BASE_MODEM_GET_CLASS (self)->sync_finish;
|
||||
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
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
@@ -907,6 +916,28 @@ mm_base_modem_sync (MMBaseModem *self,
|
||||
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 */
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -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
|
||||
base_modem_invalid_idle (MMBaseModem *self)
|
||||
{
|
||||
clear_invalid_from_idle (self);
|
||||
|
||||
/* Ensure the modem is set invalid if we get the modem-wide cancellable
|
||||
* cancelled */
|
||||
mm_base_modem_set_valid (self, FALSE);
|
||||
g_object_unref (self);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
@@ -2146,9 +2186,14 @@ static void
|
||||
base_modem_cancelled (GCancellable *cancellable,
|
||||
MMBaseModem *self)
|
||||
{
|
||||
clear_invalid_from_idle (self);
|
||||
|
||||
/* 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. */
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
cleanup_modem_port (MMBaseModem *self,
|
||||
MMPort *port)
|
||||
teardown_context_ref (TeardownContext *ctx)
|
||||
{
|
||||
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_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_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
|
||||
/* We need to close the MBIM port cleanly when disposing the modem object */
|
||||
if (MM_IS_PORT_MBIM (port)) {
|
||||
mm_port_mbim_close (MM_PORT_MBIM (port), NULL, NULL);
|
||||
mm_port_mbim_close (MM_PORT_MBIM (port),
|
||||
ctx ? (GAsyncReadyCallback)mbim_port_close_ready : NULL,
|
||||
ctx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -2186,27 +2339,61 @@ cleanup_modem_port (MMBaseModem *self,
|
||||
* allocating too many newer allocations will fail with client-ids-exhausted
|
||||
* errors. */
|
||||
if (MM_IS_PORT_QMI (port)) {
|
||||
mm_port_qmi_close (MM_PORT_QMI (port), NULL, NULL);
|
||||
mm_port_qmi_close (MM_PORT_QMI (port),
|
||||
ctx ? (GAsyncReadyCallback)qmi_port_close_ready : NULL,
|
||||
ctx);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ctx)
|
||||
teardown_context_unref (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
teardown_ports_table (MMBaseModem *self,
|
||||
GHashTable **ht)
|
||||
teardown_ports_tables (MMBaseModem *self,
|
||||
TeardownContext *ctx)
|
||||
{
|
||||
GHashTable **tables[2] = {
|
||||
&self->priv->link_ports,
|
||||
&self->priv->ports,
|
||||
};
|
||||
GHashTableIter iter;
|
||||
gpointer value;
|
||||
gpointer key;
|
||||
guint i;
|
||||
|
||||
if (!*ht)
|
||||
return;
|
||||
for (i = 0; i < G_N_ELEMENTS (tables); i++) {
|
||||
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);
|
||||
while (g_hash_table_iter_next (&iter, &key, &value))
|
||||
cleanup_modem_port (self, MM_PORT (value));
|
||||
g_hash_table_destroy (g_steal_pointer (ht));
|
||||
gboolean
|
||||
mm_base_modem_teardown_ports_finish (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
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_clear_object (&self->priv->cancellable);
|
||||
|
||||
clear_invalid_from_idle (self);
|
||||
|
||||
g_clear_object (&self->priv->primary);
|
||||
g_clear_object (&self->priv->secondary);
|
||||
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);
|
||||
#endif
|
||||
|
||||
teardown_ports_table (self, &self->priv->link_ports);
|
||||
teardown_ports_table (self, &self->priv->ports);
|
||||
if (!self->priv->torn_down)
|
||||
mm_obj_warn (self, "teardown not called before dispose");
|
||||
teardown_ports_tables (self, NULL);
|
||||
|
||||
g_clear_object (&self->priv->connection);
|
||||
|
||||
|
||||
+30
-7
@@ -109,6 +109,15 @@ struct _MMBaseModemClass {
|
||||
* this method triggers a synchronization of all modem interfaces */
|
||||
StateOperation sync;
|
||||
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
|
||||
|
||||
/* Allow plugins to subclass port object creation as needed */
|
||||
@@ -298,13 +307,27 @@ gboolean mm_base_modem_disable_finish (MMBaseModem *self,
|
||||
GError **error);
|
||||
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
void mm_base_modem_sync (MMBaseModem *self,
|
||||
MMBaseModemOperationLock operation_lock,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_base_modem_sync_finish (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_base_modem_sync (MMBaseModem *self,
|
||||
MMBaseModemOperationLock operation_lock,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_base_modem_sync_finish (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
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
|
||||
|
||||
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 */
|
||||
|
||||
@@ -1458,10 +1458,11 @@ load_settings_from_bearer (MMBearerMbim *self,
|
||||
guint max_multiplexed_bearers;
|
||||
const gchar *data_port_driver;
|
||||
|
||||
if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem),
|
||||
¤t_multiplexed_bearers,
|
||||
&max_multiplexed_bearers,
|
||||
error))
|
||||
if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem),
|
||||
MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED,
|
||||
¤t_multiplexed_bearers,
|
||||
&max_multiplexed_bearers,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* Check multiplex support in the kernel and the device */
|
||||
|
||||
+5
-4
@@ -2424,10 +2424,11 @@ load_settings_from_bearer (MMBearerQmi *self,
|
||||
guint max_multiplexed_bearers;
|
||||
gboolean multiplex_supported = TRUE;
|
||||
|
||||
if (!mm_broadband_modem_get_active_multiplexed_bearers (MM_BROADBAND_MODEM (ctx->modem),
|
||||
¤t_multiplexed_bearers,
|
||||
&max_multiplexed_bearers,
|
||||
error))
|
||||
if (!mm_iface_modem_count_bearers (MM_IFACE_MODEM (ctx->modem),
|
||||
MM_IFACE_MODEM_COUNT_BEARERS_FLAG_MULTIPLEXED,
|
||||
¤t_multiplexed_bearers,
|
||||
&max_multiplexed_bearers,
|
||||
error))
|
||||
return FALSE;
|
||||
|
||||
/* Check multiplex support in the kernel and the device */
|
||||
|
||||
@@ -5840,6 +5840,7 @@ static gboolean process_pdu_messages (MMBroadbandModemMbim *self,
|
||||
guint32 messages_count,
|
||||
MbimSmsPduReadRecordArray *pdu_messages,
|
||||
guint expected_index,
|
||||
gboolean unstored,
|
||||
GError **error);
|
||||
|
||||
static void
|
||||
@@ -5864,6 +5865,7 @@ sms_notification_read_flash_sms (MMBroadbandModemMbim *self,
|
||||
messages_count,
|
||||
pdu_messages,
|
||||
SMS_PART_INVALID_INDEX,
|
||||
TRUE,
|
||||
&error))
|
||||
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,
|
||||
pdu_messages,
|
||||
ctx->expected_index,
|
||||
FALSE,
|
||||
&error))
|
||||
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
|
||||
add_sms_part (MMBroadbandModemMbim *self,
|
||||
const MbimSmsPduReadRecord *pdu,
|
||||
guint part_index,
|
||||
guint expected_index)
|
||||
{
|
||||
MMSmsPart *part;
|
||||
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_size,
|
||||
self,
|
||||
@@ -9062,6 +9066,7 @@ process_pdu_messages (MMBroadbandModemMbim *self,
|
||||
guint32 messages_count,
|
||||
MbimSmsPduReadRecordArray *pdu_messages,
|
||||
guint expected_index,
|
||||
gboolean unstored,
|
||||
GError **error)
|
||||
{
|
||||
guint i;
|
||||
@@ -9087,11 +9092,26 @@ process_pdu_messages (MMBroadbandModemMbim *self,
|
||||
|
||||
mm_obj_dbg (self, "%u SMS PDUs reported", messages_count);
|
||||
for (i = 0; i < messages_count; i++) {
|
||||
if (pdu_messages[i]) {
|
||||
mm_obj_dbg (self, "processing SMS PDU %u/%u...", i+1, messages_count);
|
||||
add_sms_part (self, pdu_messages[i], expected_index);
|
||||
} else
|
||||
guint message_index;
|
||||
|
||||
if (!pdu_messages[i]) {
|
||||
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;
|
||||
@@ -9126,6 +9146,7 @@ sms_read_query_ready (MbimDevice *device,
|
||||
messages_count,
|
||||
pdu_messages,
|
||||
SMS_PART_INVALID_INDEX,
|
||||
FALSE,
|
||||
&error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
@@ -10416,10 +10437,6 @@ dispose (GObject *object)
|
||||
/* Explicitly remove notification handler */
|
||||
self->priv->setup_flags = PROCESS_NOTIFICATION_FLAG_NONE;
|
||||
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);
|
||||
|
||||
@@ -8380,7 +8380,6 @@ messaging_event_report_indication_cb (QmiClientNas *client,
|
||||
QmiWmsAckIndicator ack_ind;
|
||||
guint32 transaction_id;
|
||||
QmiWmsMessageFormat msg_format;
|
||||
QmiWmsMessageTagType tag;
|
||||
GArray *raw_data = NULL;
|
||||
|
||||
/* 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 */
|
||||
storage = QMI_WMS_STORAGE_TYPE_NONE;
|
||||
memory_index = 0;
|
||||
tag = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ;
|
||||
add_new_read_sms_part (MM_IFACE_MODEM_MESSAGING (self),
|
||||
storage,
|
||||
memory_index,
|
||||
tag,
|
||||
QMI_WMS_STORAGE_TYPE_NONE,
|
||||
SMS_PART_INVALID_INDEX,
|
||||
QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ,
|
||||
msg_format,
|
||||
TRUE,
|
||||
raw_data);
|
||||
@@ -13994,17 +13990,6 @@ static void
|
||||
dispose (GObject *object)
|
||||
{
|
||||
MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (object);
|
||||
MMPortQmi *qmi;
|
||||
|
||||
/* If any port cleanup is needed, it must be done during dispose(), as
|
||||
* the modem object will be affected by an explicit g_object_run_dispose()
|
||||
* that will remove all port references right away */
|
||||
qmi = mm_broadband_modem_qmi_peek_port_qmi (self);
|
||||
if (qmi) {
|
||||
/* 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);
|
||||
self->priv->firmware_list = NULL;
|
||||
|
||||
+157
-68
@@ -323,25 +323,32 @@ ports_context_ref (PortsContext *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
|
||||
ports_context_unref (PortsContext *ctx)
|
||||
{
|
||||
if (g_atomic_int_dec_and_test (&ctx->ref_count)) {
|
||||
if (ctx->primary) {
|
||||
if (ctx->primary_open)
|
||||
mm_port_serial_close (MM_PORT_SERIAL (ctx->primary));
|
||||
g_object_unref (ctx->primary);
|
||||
}
|
||||
if (ctx->secondary) {
|
||||
if (ctx->secondary_open)
|
||||
mm_port_serial_close (MM_PORT_SERIAL (ctx->secondary));
|
||||
g_object_unref (ctx->secondary);
|
||||
}
|
||||
if (ctx->qcdm) {
|
||||
if (ctx->qcdm_open)
|
||||
mm_port_serial_close (MM_PORT_SERIAL (ctx->qcdm));
|
||||
g_object_unref (ctx->qcdm);
|
||||
}
|
||||
ports_context_dispose (ctx);
|
||||
g_clear_object (&ctx->primary);
|
||||
g_clear_object (&ctx->secondary);
|
||||
g_clear_object (&ctx->qcdm);
|
||||
|
||||
g_free (ctx);
|
||||
}
|
||||
}
|
||||
@@ -761,11 +768,14 @@ parse_caps_cgmm (MMBaseModem *self,
|
||||
if (!response)
|
||||
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") ||
|
||||
strstr (response, "GSM1800") ||
|
||||
strstr (response, "GSM1900") ||
|
||||
strstr (response, "GSM850")) {
|
||||
strstr (response, "GSM850") ||
|
||||
strstr (response, "MTK2")) {
|
||||
/* At least, it's a GSM modem */
|
||||
*result = g_variant_new_uint32 (MM_MODEM_CAPABILITY_GSM_UMTS);
|
||||
return MM_BASE_MODEM_AT_RESPONSE_PROCESSOR_RESULT_SUCCESS;
|
||||
@@ -5584,6 +5594,21 @@ modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self,
|
||||
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
|
||||
@@ -5825,13 +5850,17 @@ run_registration_checks_context_step (GTask *task)
|
||||
error = g_steal_pointer (&ctx->error_cs);
|
||||
else
|
||||
g_assert_not_reached ();
|
||||
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (error)
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
/* When everything is done, recheck operator info as it may have changed */
|
||||
mm_iface_modem_3gpp_reload_current_registration_info (
|
||||
MM_IFACE_MODEM_3GPP (self),
|
||||
(GAsyncReadyCallback)run_registration_checks_reload_current_registration_info_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -13129,6 +13158,103 @@ synchronize (MMBaseModem *self,
|
||||
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
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -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 *
|
||||
mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
|
||||
const gchar *ati,
|
||||
@@ -14258,6 +14338,13 @@ dispose (GObject *object)
|
||||
g_clear_object (&self->priv->modem_simple_status);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -14609,6 +14696,8 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass)
|
||||
#if defined WITH_SUSPEND_RESUME
|
||||
base_modem_class->sync = synchronize;
|
||||
base_modem_class->sync_finish = synchronize_finish;
|
||||
base_modem_class->terse = terse;
|
||||
base_modem_class->terse_finish = terse_finish;
|
||||
#endif
|
||||
|
||||
klass->setup_ports = setup_ports;
|
||||
|
||||
@@ -143,12 +143,6 @@ gboolean mm_broadband_modem_sim_hot_swap_ports_context_init (MMBroadbandModem
|
||||
GError **error);
|
||||
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 */
|
||||
gint mm_broadband_modem_get_initial_eps_bearer_cid (MMBroadbandModem *self);
|
||||
|
||||
|
||||
+99
-14
@@ -459,14 +459,66 @@ clear_modem (MMDevice *self)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mm_device_remove_modem (MMDevice *self)
|
||||
gboolean
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
modem_valid (MMBaseModem *modem,
|
||||
GParamSpec *pspec,
|
||||
@@ -498,9 +569,9 @@ modem_valid (MMBaseModem *modem,
|
||||
{
|
||||
if (!mm_base_modem_get_valid (modem)) {
|
||||
/* Modem no longer valid */
|
||||
mm_device_remove_modem (self);
|
||||
if (mm_base_modem_get_reprobe (modem))
|
||||
self->priv->reprobe_id = g_timeout_add_seconds (REPROBE_SECS, (GSourceFunc)reprobe, self);
|
||||
mm_device_remove_modem (self,
|
||||
(GAsyncReadyCallback)modem_valid_remove_ready,
|
||||
g_object_ref (modem));
|
||||
} else {
|
||||
/* Modem now valid, export it, but only if we really have it around.
|
||||
* 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);
|
||||
}
|
||||
|
||||
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
|
||||
inhibit_disable_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
@@ -742,14 +826,15 @@ inhibit_disable_ready (MMBaseModem *modem,
|
||||
|
||||
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);
|
||||
else {
|
||||
g_cancellable_cancel (mm_base_modem_peek_cancellable (modem));
|
||||
mm_device_remove_modem (self);
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
g_object_unref (task);
|
||||
|
||||
mm_device_remove_modem (self,
|
||||
(GAsyncReadyCallback)inhibit_disable_modem_remove_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -899,7 +984,7 @@ set_property (GObject *object,
|
||||
self->priv->plugin = g_value_dup_object (value);
|
||||
break;
|
||||
case PROP_MODEM:
|
||||
clear_modem (self);
|
||||
g_assert (self->priv->modem == NULL);
|
||||
self->priv->modem = g_value_dup_object (value);
|
||||
break;
|
||||
case PROP_HOTPLUGGED:
|
||||
|
||||
+10
-4
@@ -80,10 +80,16 @@ void mm_device_release_port_name (MMDevice *self,
|
||||
const gchar *subsystem,
|
||||
const gchar *name);
|
||||
|
||||
gboolean mm_device_create_modem (MMDevice *self,
|
||||
GError **error);
|
||||
void mm_device_remove_modem (MMDevice *self);
|
||||
void mm_device_initialize_modem (MMDevice *self);
|
||||
gboolean mm_device_create_modem (MMDevice *self,
|
||||
GError **error);
|
||||
void mm_device_remove_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,
|
||||
GAsyncReadyCallback callback,
|
||||
|
||||
@@ -3232,6 +3232,8 @@ static void interface_syncing_step (GTask *task);
|
||||
|
||||
typedef enum {
|
||||
SYNCING_STEP_FIRST,
|
||||
SYNCING_STEP_ENABLE_UNSOLICITED_EVENTS,
|
||||
SYNCING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS,
|
||||
SYNCING_STEP_REFRESH_3GPP_REGISTRATION,
|
||||
SYNCING_STEP_REFRESH_EPS_BEARER,
|
||||
SYNCING_STEP_LAST
|
||||
@@ -3249,6 +3251,31 @@ mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
|
||||
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
|
||||
sync_eps_bearer_ready (MMIfaceModem3gpp *self,
|
||||
GAsyncResult *res,
|
||||
@@ -3327,6 +3354,33 @@ interface_syncing_step (GTask *task)
|
||||
ctx->step++;
|
||||
/* 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:
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -318,6 +318,13 @@ gboolean mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self,
|
||||
GAsyncResult *res,
|
||||
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
|
||||
|
||||
/* Shutdown Modem 3GPP interface */
|
||||
|
||||
@@ -32,6 +32,10 @@ static GQuark in_call_event_context_quark;
|
||||
|
||||
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
|
||||
@@ -1781,6 +1785,7 @@ typedef enum {
|
||||
IN_CALL_SETUP_STEP_FIRST,
|
||||
IN_CALL_SETUP_STEP_UNSOLICITED_EVENTS,
|
||||
IN_CALL_SETUP_STEP_AUDIO_CHANNEL,
|
||||
IN_CALL_SETUP_STEP_CHECK_POLLING,
|
||||
IN_CALL_SETUP_STEP_LAST,
|
||||
} InCallSetupStep;
|
||||
|
||||
@@ -1908,6 +1913,10 @@ in_call_setup_context_step (GTask *task)
|
||||
}
|
||||
ctx->step++;
|
||||
/* 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:
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
|
||||
@@ -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
|
||||
mm_iface_modem_default_init (MMIfaceModemInterface *iface)
|
||||
{
|
||||
|
||||
@@ -620,4 +620,19 @@ void mm_iface_modem_modify_sim (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 */
|
||||
|
||||
@@ -1218,8 +1218,9 @@ get_mm_access_tech_from_etsi_access_tech (guint val,
|
||||
return MM_MODEM_ACCESS_TECHNOLOGY_HSUPA;
|
||||
case 6:
|
||||
return MM_MODEM_ACCESS_TECHNOLOGY_HSPA;
|
||||
case 7: /* E-UTRAN */
|
||||
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 */
|
||||
return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
||||
return act;
|
||||
|
||||
@@ -1482,8 +1482,6 @@ port_serial_close_force (MMPortSerial *self)
|
||||
if (self->priv->forced_close)
|
||||
return;
|
||||
|
||||
mm_obj_dbg (self, "forced to close port");
|
||||
|
||||
/* Mark as having forced the close, so that we don't warn about incorrect
|
||||
* open counts */
|
||||
self->priv->forced_close = TRUE;
|
||||
@@ -1493,6 +1491,8 @@ port_serial_close_force (MMPortSerial *self)
|
||||
|
||||
/* If already closed, done */
|
||||
if (self->priv->open_count > 0) {
|
||||
mm_obj_dbg (self, "forced to close port");
|
||||
|
||||
_close_internal (self, TRUE);
|
||||
|
||||
/* Notify about the forced close status */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "mm-log-object.h"
|
||||
#include "mm-utils.h"
|
||||
#include "mm-sleep-monitor.h"
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
#define PD_NAME "org.chromium.PowerManager"
|
||||
#define PD_PATH "/org/chromium/PowerManager"
|
||||
@@ -37,13 +38,15 @@
|
||||
struct _MMSleepMonitor {
|
||||
GObject parent_instance;
|
||||
|
||||
GDBusProxy *pd_proxy;
|
||||
GDBusProxy *pd_proxy;
|
||||
MMSleepContext *sleep_ctx;
|
||||
guint sleep_done_id;
|
||||
};
|
||||
|
||||
struct _MMSleepMonitorClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*sleeping) (MMSleepMonitor *monitor);
|
||||
void (*sleeping) (MMSleepMonitor *monitor, MMSleepContext *ctx);
|
||||
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
|
||||
signal_cb (GDBusProxy *proxy,
|
||||
const gchar *sendername,
|
||||
@@ -79,14 +102,24 @@ signal_cb (GDBusProxy *proxy,
|
||||
{
|
||||
MMSleepMonitor *self = data;
|
||||
|
||||
if (proxy == self->pd_proxy) {
|
||||
if (strcmp (signalname, "SuspendImminent") == 0) {
|
||||
mm_obj_msg (self, "system suspend signal from powerd");
|
||||
g_signal_emit (self, signals[SLEEPING], 0);
|
||||
} else if (strcmp (signalname, "SuspendDone") == 0) {
|
||||
mm_obj_msg (self, "system resume signal from powerd");
|
||||
g_signal_emit (self, signals[RESUMING], 0);
|
||||
if (proxy != self->pd_proxy)
|
||||
return;
|
||||
|
||||
if (strcmp (signalname, "SuspendImminent") == 0) {
|
||||
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 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
|
||||
finalize (GObject *object)
|
||||
dispose (GObject *object)
|
||||
{
|
||||
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
|
||||
|
||||
if (self->pd_proxy)
|
||||
g_object_unref (self->pd_proxy);
|
||||
cleanup_sleep_context (self);
|
||||
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)->finalize (object);
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -144,7 +176,7 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->finalize = finalize;
|
||||
gobject_class->dispose = dispose;
|
||||
|
||||
signals[SLEEPING] = g_signal_new (MM_SLEEP_MONITOR_SLEEPING,
|
||||
MM_TYPE_SLEEP_MONITOR,
|
||||
@@ -152,8 +184,8 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
G_STRUCT_OFFSET (MMSleepMonitorClass, sleeping),
|
||||
NULL, /* accumulator */
|
||||
NULL, /* accumulator data */
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, G_TYPE_CANCELLABLE);
|
||||
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
|
||||
MM_TYPE_SLEEP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "mm-log-object.h"
|
||||
#include "mm-utils.h"
|
||||
#include "mm-sleep-monitor.h"
|
||||
#include "mm-sleep-context.h"
|
||||
|
||||
#define SD_NAME "org.freedesktop.login1"
|
||||
#define SD_PATH "/org/freedesktop/login1"
|
||||
@@ -37,14 +38,16 @@
|
||||
struct _MMSleepMonitor {
|
||||
GObject parent_instance;
|
||||
|
||||
GDBusProxy *sd_proxy;
|
||||
gint inhibit_fd;
|
||||
GDBusProxy *sd_proxy;
|
||||
gint inhibit_fd;
|
||||
MMSleepContext *sleep_ctx;
|
||||
guint sleep_done_id;
|
||||
};
|
||||
|
||||
struct _MMSleepMonitorClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*sleeping) (MMSleepMonitor *monitor);
|
||||
void (*sleeping) (MMSleepMonitor *monitor, MMSleepContext *ctx);
|
||||
void (*resuming) (MMSleepMonitor *monitor);
|
||||
};
|
||||
|
||||
@@ -137,6 +140,27 @@ take_inhibitor (MMSleepMonitor *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
|
||||
signal_cb (GDBusProxy *proxy,
|
||||
const gchar *sendername,
|
||||
@@ -154,8 +178,17 @@ signal_cb (GDBusProxy *proxy,
|
||||
|
||||
if (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 {
|
||||
mm_obj_msg (self, "system is resuming");
|
||||
take_inhibitor (self);
|
||||
@@ -219,17 +252,25 @@ mm_sleep_monitor_init (MMSleepMonitor *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
|
||||
finalize (GObject *object)
|
||||
{
|
||||
MMSleepMonitor *self = MM_SLEEP_MONITOR (object);
|
||||
|
||||
drop_inhibitor (self);
|
||||
if (self->sd_proxy)
|
||||
g_object_unref (self->sd_proxy);
|
||||
|
||||
if (G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize != NULL)
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
|
||||
G_OBJECT_CLASS (mm_sleep_monitor_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -245,6 +286,7 @@ mm_sleep_monitor_class_init (MMSleepMonitorClass *klass)
|
||||
|
||||
gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = dispose;
|
||||
gobject_class->finalize = finalize;
|
||||
|
||||
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),
|
||||
NULL, /* accumulator */
|
||||
NULL, /* accumulator data */
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
g_cclosure_marshal_VOID__OBJECT,
|
||||
G_TYPE_NONE, 1, MM_TYPE_SLEEP_CONTEXT);
|
||||
signals[RESUMING] = g_signal_new (MM_SLEEP_MONITOR_RESUMING,
|
||||
MM_TYPE_SLEEP_MONITOR,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
|
||||
@@ -794,7 +794,7 @@ mm_cinterion_parse_sxrat_test (const gchar *response,
|
||||
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,
|
||||
0, NULL);
|
||||
|
||||
|
||||
@@ -1826,6 +1826,38 @@ test_sxrat_response_els61 (void)
|
||||
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
|
||||
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/sxrat", test_sxrat);
|
||||
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/cops/only-mode-2g", test_cops_only_mode_2g);
|
||||
g_test_add_func ("/MM/cinterion/cops/only-mode-3g", test_cops_only_mode_3g);
|
||||
|
||||
@@ -75,8 +75,8 @@ get_private (MMSharedFibocom *self)
|
||||
priv->class_parent = MM_SHARED_FIBOCOM_GET_IFACE (self)->peek_parent_class (self);
|
||||
|
||||
/* Setup firmware interface of parent class */
|
||||
g_assert (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);
|
||||
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);
|
||||
|
||||
g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
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_mnc_length, NULL));
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_string_to_access_tech, NULL));
|
||||
|
||||
result = g_test_run ();
|
||||
|
||||
reg_test_data_free (reg_data);
|
||||
|
||||
Reference in New Issue
Block a user