| /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details: |
| * |
| * Copyright (C) 2011 - 2012 Google, Inc. |
| * Copyright (c) 2022 Qualcomm Innovation Center, Inc. |
| */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <ModemManager.h> |
| #define _LIBMM_INSIDE_MM |
| #include <libmm-glib.h> |
| |
| #include "mm-iface-modem.h" |
| #include "mm-iface-modem-location.h" |
| #include "mm-iface-modem-3gpp.h" |
| #include "mm-base-modem.h" |
| #include "mm-modem-helpers.h" |
| #include "mm-error-helpers.h" |
| #include "mm-log.h" |
| #include "mm-log-helpers.h" |
| |
| #define SUBSYSTEM_3GPP "3gpp" |
| |
| /* When comparing EPS bearer settings take into account that: |
| * - 'password' may not always be readable. |
| * - 'apn-type' may not always be supported. |
| * - 'access-type-preference' may not always be reported. |
| * - 'profile-id' will not be known in the requested settings |
| * - 'profile-name' might not be known in the requested settings |
| * - we ignore settings not applicable to profiles, like 'allow-roaming' or |
| * 'rm-protocol'. |
| * - we apply very loose matching for all fields. |
| */ |
| #define MM_BEARER_PROPERTIES_CMP_FLAGS_EPS \ |
| (MM_BEARER_PROPERTIES_CMP_FLAGS_LOOSE | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_ID | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PROFILE_NAME | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_PASSWORD | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ALLOW_ROAMING | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_APN_TYPE | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_RM_PROTOCOL | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ACCESS_TYPE_PREFERENCE | \ |
| MM_BEARER_PROPERTIES_CMP_FLAGS_NO_ROAMING_ALLOWANCE) |
| |
| |
| /*****************************************************************************/ |
| /* Private data context */ |
| |
| #define PRIVATE_TAG "iface-modem-3gpp-private-tag" |
| static GQuark private_quark; |
| |
| typedef struct { |
| /* Interface enabled or disabled */ |
| gboolean iface_enabled; |
| /* Registration state */ |
| MMModem3gppRegistrationState state_cs; |
| MMModem3gppRegistrationState state_ps; |
| MMModem3gppRegistrationState state_eps; |
| MMModem3gppRegistrationState state_5gs; |
| gboolean manual_registration; |
| gchar *manual_registration_operator_id; |
| GCancellable *pending_registration_cancellable; |
| gboolean reloading_registration_info; |
| /* Registration checks */ |
| guint check_timeout_source; |
| gboolean check_running; |
| /* Packet service state */ |
| gboolean packet_service_state_update_supported; |
| } Private; |
| |
| static void |
| private_free (Private *priv) |
| { |
| g_free (priv->manual_registration_operator_id); |
| if (priv->pending_registration_cancellable) { |
| g_cancellable_cancel (priv->pending_registration_cancellable); |
| g_object_unref (priv->pending_registration_cancellable); |
| } |
| if (priv->check_timeout_source) |
| g_source_remove (priv->check_timeout_source); |
| g_slice_free (Private, priv); |
| } |
| |
| static Private * |
| get_private (MMIfaceModem3gpp *self) |
| { |
| Private *priv; |
| |
| if (G_UNLIKELY (!private_quark)) |
| private_quark = g_quark_from_static_string (PRIVATE_TAG); |
| |
| priv = g_object_get_qdata (G_OBJECT (self), private_quark); |
| if (!priv) { |
| priv = g_slice_new0 (Private); |
| priv->state_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; |
| priv->state_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; |
| priv->state_eps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; |
| priv->state_5gs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; |
| g_object_set_qdata_full (G_OBJECT (self), private_quark, priv, (GDestroyNotify)private_free); |
| } |
| |
| return priv; |
| } |
| |
| /*****************************************************************************/ |
| |
| #define GET_NETWORK_SUPPORTED(domain,DOMAIN) \ |
| static gboolean \ |
| get_##domain##_network_supported (MMIfaceModem3gpp *self) \ |
| { \ |
| gboolean supported = FALSE; \ |
| \ |
| g_object_get (self, \ |
| MM_IFACE_MODEM_3GPP_##DOMAIN##_NETWORK_SUPPORTED, &supported, \ |
| NULL); \ |
| return supported; \ |
| } |
| |
| GET_NETWORK_SUPPORTED (cs, CS) |
| GET_NETWORK_SUPPORTED (ps, PS) |
| GET_NETWORK_SUPPORTED (eps, EPS) |
| GET_NETWORK_SUPPORTED (5gs, 5GS) |
| |
| /*****************************************************************************/ |
| /* Helper method to wait for a final packet service state */ |
| |
| typedef struct { |
| MMModem3gppPacketServiceState final_state; |
| gulong state_changed_id; |
| guint timeout_id; |
| gulong cancellable_id; |
| } WaitForPacketServiceStateContext; |
| |
| MMModem3gppPacketServiceState |
| mm_iface_modem_3gpp_wait_for_packet_service_state_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| GError *inner_error = NULL; |
| gssize value; |
| |
| value = g_task_propagate_int (G_TASK (res), &inner_error); |
| if (inner_error) { |
| g_propagate_error (error, inner_error); |
| return MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; |
| } |
| return (MMModem3gppPacketServiceState)value; |
| } |
| |
| static void |
| wait_for_packet_service_state_context_complete (GTask *task, |
| MMModem3gppPacketServiceState state, |
| GError *error) |
| { |
| MMIfaceModem3gpp *self; |
| WaitForPacketServiceStateContext *ctx; |
| |
| self = g_task_get_source_object (task); |
| ctx = g_task_get_task_data (task); |
| |
| g_assert (ctx->state_changed_id); |
| if (g_signal_handler_is_connected (self, ctx->state_changed_id)) |
| g_signal_handler_disconnect (self, ctx->state_changed_id); |
| ctx->state_changed_id = 0; |
| |
| g_assert (ctx->timeout_id); |
| g_source_remove (ctx->timeout_id); |
| ctx->timeout_id = 0; |
| |
| if (!g_task_return_error_if_cancelled (task)) { |
| if (ctx->cancellable_id) { |
| g_cancellable_disconnect (g_task_get_cancellable (task), ctx->cancellable_id); |
| ctx->cancellable_id = 0; |
| } |
| if (error) |
| g_task_return_error (task, error); |
| else |
| g_task_return_int (task, state); |
| } |
| g_object_unref (task); |
| } |
| |
| static void |
| packet_service_wait_cancelled (GCancellable *cancellable, |
| GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| WaitForPacketServiceStateContext *ctx; |
| |
| self = g_task_get_source_object (task); |
| ctx = g_task_get_task_data (task); |
| |
| mm_obj_dbg (self, "wait for packet service state '%s': cancelled", |
| (ctx->final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? |
| "any" : mm_modem_3gpp_packet_service_state_get_string (ctx->final_state)); |
| |
| /* Given that the cancellable is the same one as in the task, we can complete the operation here |
| * without specifying an exact error. The task will itself be completed with a cancelled error. */ |
| g_assert (g_task_get_cancellable (task) == cancellable); |
| wait_for_packet_service_state_context_complete (task, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, NULL); |
| } |
| |
| static gboolean |
| packet_service_wait_timeout (GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| WaitForPacketServiceStateContext *ctx; |
| GError *error; |
| |
| self = g_task_get_source_object (task); |
| ctx = g_task_get_task_data (task); |
| |
| mm_obj_dbg (self, "wait for packet service state '%s': timed out", |
| (ctx->final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? |
| "any" : mm_modem_3gpp_packet_service_state_get_string (ctx->final_state)); |
| |
| error = g_error_new (MM_CORE_ERROR, |
| MM_CORE_ERROR_RETRY, |
| "Too much time waiting to get to a final packet service state"); |
| wait_for_packet_service_state_context_complete (task, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, error); |
| return G_SOURCE_REMOVE; |
| } |
| |
| static void |
| packet_service_state_changed (MMIfaceModem3gpp *self, |
| GParamSpec *spec, |
| GTask *task) |
| { |
| WaitForPacketServiceStateContext *ctx; |
| MMModem3gppPacketServiceState state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &state, |
| NULL); |
| |
| ctx = g_task_get_task_data (task); |
| |
| /* If we want a specific final state and this is not the one we were |
| * looking for, then skip */ |
| if ((ctx->final_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) && |
| (state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) && |
| (state != ctx->final_state)) |
| return; |
| |
| mm_obj_dbg (self, "wait for packet service state '%s': finished", |
| (ctx->final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? |
| "any" : mm_modem_3gpp_packet_service_state_get_string (ctx->final_state)); |
| |
| /* Done! */ |
| wait_for_packet_service_state_context_complete (task, state, NULL); |
| } |
| |
| void |
| mm_iface_modem_3gpp_wait_for_packet_service_state (MMIfaceModem3gpp *self, |
| MMModem3gppPacketServiceState final_state, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| MMModem3gppPacketServiceState state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; |
| WaitForPacketServiceStateContext *ctx; |
| GTask *task; |
| |
| task = g_task_new (self, cancellable, callback, user_data); |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &state, |
| NULL); |
| |
| /* Is this the state we actually wanted? */ |
| if (final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN || |
| (state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN && state == final_state)) { |
| g_task_return_int (task, state); |
| g_object_unref (task); |
| return; |
| } |
| |
| /* Otherwise, we'll need to wait for the exact one we want */ |
| ctx = g_new0 (WaitForPacketServiceStateContext, 1); |
| ctx->final_state = final_state; |
| |
| g_task_set_task_data (task, ctx, g_free); |
| |
| /* Ownership of the task will be shared among the signal handler, the timeout, |
| * and the cancellable. As soon as one of them is triggered, it should cancel the |
| * other two. */ |
| |
| /* Want to get notified when packet service state changes */ |
| ctx->state_changed_id = g_signal_connect (self, |
| "notify::" MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, |
| G_CALLBACK (packet_service_state_changed), |
| task); |
| /* But we don't want to wait forever */ |
| ctx->timeout_id = g_timeout_add_seconds (10, |
| (GSourceFunc)packet_service_wait_timeout, |
| task); |
| |
| /* And we want it to be cancellable */ |
| if (cancellable) { |
| ctx->cancellable_id = g_cancellable_connect (cancellable, |
| (GCallback) packet_service_wait_cancelled, |
| task, |
| NULL); |
| /* Do nothing if already cancelled, packet_service_wait_cancelled() will already be called */ |
| if (!ctx->cancellable_id) |
| return; |
| } |
| |
| mm_obj_dbg (self, "wait for packet service state '%s': started", |
| (final_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN) ? |
| "any" : mm_modem_3gpp_packet_service_state_get_string (final_state)); |
| } |
| |
| /*****************************************************************************/ |
| |
| void |
| mm_iface_modem_3gpp_bind_simple_status (MMIfaceModem3gpp *self, |
| MMSimpleStatus *status) |
| { |
| MmGdbusModem3gpp *skeleton; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, |
| NULL); |
| if (!skeleton) |
| return; |
| |
| g_object_bind_property (skeleton, "registration-state", |
| status, MM_SIMPLE_PROPERTY_3GPP_REGISTRATION_STATE, |
| G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); |
| |
| g_object_bind_property (skeleton, "operator-code", |
| status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_CODE, |
| G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); |
| |
| g_object_bind_property (skeleton, "operator-name", |
| status, MM_SIMPLE_PROPERTY_3GPP_OPERATOR_NAME, |
| G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); |
| |
| g_object_unref (skeleton); |
| } |
| |
| /*****************************************************************************/ |
| |
| static MMModem3gppPacketServiceState |
| get_consolidated_packet_service_state (MMIfaceModem3gpp *self) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| g_assert (!priv->packet_service_state_update_supported); |
| |
| /* If registered in any of PS, EPS or 5GS, then packet service domain is |
| * implicitly attached. */ |
| if (mm_modem_3gpp_registration_state_is_registered (priv->state_ps) || |
| mm_modem_3gpp_registration_state_is_registered (priv->state_eps) || |
| mm_modem_3gpp_registration_state_is_registered (priv->state_5gs)) |
| return MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED; |
| |
| if (mm_modem_3gpp_registration_state_is_registered (priv->state_cs)) |
| return MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED; |
| |
| /* If not registered in any of CS, PS, EPS or 5GS, then packet service |
| * domain is detached. */ |
| return MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED; |
| } |
| |
| static MMModem3gppRegistrationState |
| get_consolidated_reg_state (MMIfaceModem3gpp *self) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| |
| /* Some devices (Blackberries for example) will respond to +CGREG, but |
| * return ERROR for +CREG, probably because their firmware is just stupid. |
| * So here we prefer the +CREG response, but if we never got a successful |
| * +CREG response, we'll take +CGREG instead. |
| */ |
| if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) |
| return priv->state_cs; |
| if (priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) |
| return priv->state_ps; |
| if (priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) |
| return priv->state_eps; |
| if (priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) |
| return priv->state_5gs; |
| |
| /* Searching? */ |
| if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || |
| priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || |
| priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING || |
| priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING) |
| return MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; |
| |
| #define REG_STATE_IS_UNKNOWN_IDLE_DENIED(state) \ |
| (state == MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN || \ |
| state == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || \ |
| state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) |
| |
| /* If at least one state is DENIED and the others are UNKNOWN or IDLE, use DENIED */ |
| if ((priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || |
| priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || |
| priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED || |
| priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) && |
| REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_cs) && |
| REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_ps) && |
| REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_eps) && |
| REG_STATE_IS_UNKNOWN_IDLE_DENIED (priv->state_5gs)) |
| return MM_MODEM_3GPP_REGISTRATION_STATE_DENIED; |
| |
| #undef REG_STATE_IS_UNKNOWN_IDLE_DENIED |
| |
| /* Emergency services? */ |
| if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY || |
| priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY || |
| priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY || |
| priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY) |
| return MM_MODEM_3GPP_REGISTRATION_STATE_EMERGENCY_ONLY; |
| |
| /* Support for additional registration states reported when on LTE/5GNR. |
| * |
| * For example, we may see the modem registered in LTE (EPS==HOME), and we |
| * may get "SMS only" reported for CS. |
| * |
| * We give these states a very low priority w.r.t. the other ones as they |
| * are really likely never used (i.e. we would get as consolidated the LTE |
| * registration state, not the CS fall back state). |
| * |
| * We also warn in that case, because ideally we should always report the |
| * LTE registration state first, not this one. |
| */ |
| if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_SMS_ONLY || |
| priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_SMS_ONLY || |
| priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME_CSFB_NOT_PREFERRED || |
| priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING_CSFB_NOT_PREFERRED) { |
| mm_obj_warn (self, "3GPP CSFB registration state is consolidated: %s", |
| mm_modem_3gpp_registration_state_get_string (priv->state_cs)); |
| return priv->state_cs; |
| } |
| |
| /* Idle? */ |
| if (priv->state_cs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || |
| priv->state_ps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || |
| priv->state_eps == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE || |
| priv->state_5gs == MM_MODEM_3GPP_REGISTRATION_STATE_IDLE) |
| return MM_MODEM_3GPP_REGISTRATION_STATE_IDLE; |
| |
| return MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MMIfaceModem3gpp *self; |
| MmGdbusModem3gpp *skeleton; |
| GCancellable *cancellable; |
| gboolean force_registration; |
| gchar *operator_id; |
| GTimer *timer; |
| guint max_registration_time; |
| } RegisterInNetworkContext; |
| |
| static void |
| register_in_network_context_free (RegisterInNetworkContext *ctx) |
| { |
| if (ctx->timer) |
| g_timer_destroy (ctx->timer); |
| |
| if (ctx->cancellable) { |
| Private *priv; |
| |
| /* Clear our cancellable if still around */ |
| priv = get_private (ctx->self); |
| if (priv->pending_registration_cancellable == ctx->cancellable) |
| g_clear_object (&priv->pending_registration_cancellable); |
| g_object_unref (ctx->cancellable); |
| } |
| |
| g_free (ctx->operator_id); |
| if (ctx->skeleton) |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->self); |
| g_slice_free (RegisterInNetworkContext, ctx); |
| } |
| |
| static void |
| register_in_network_context_complete_failed (GTask *task, |
| GError *error) |
| { |
| RegisterInNetworkContext *ctx; |
| |
| ctx = g_task_get_task_data (task); |
| |
| mm_iface_modem_3gpp_update_cs_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); |
| mm_iface_modem_3gpp_update_ps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); |
| mm_iface_modem_3gpp_update_eps_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); |
| mm_iface_modem_3gpp_update_5gs_registration_state (ctx->self, MM_MODEM_3GPP_REGISTRATION_STATE_IDLE, TRUE); |
| mm_iface_modem_3gpp_apply_deferred_registration_state (ctx->self); |
| |
| mm_iface_modem_3gpp_update_access_technologies (ctx->self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); |
| mm_iface_modem_3gpp_update_location (ctx->self, 0, 0, 0); |
| |
| g_task_return_error (task, error); |
| g_object_unref (task); |
| } |
| |
| gboolean |
| mm_iface_modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| static gboolean run_registration_checks (GTask *task); |
| |
| static void |
| run_registration_checks_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| RegisterInNetworkContext *ctx; |
| GError *error = NULL; |
| MMModem3gppRegistrationState current_registration_state; |
| |
| ctx = g_task_get_task_data (task); |
| |
| mm_iface_modem_3gpp_run_registration_checks_finish (MM_IFACE_MODEM_3GPP (self), res, &error); |
| if (error) { |
| mm_obj_info (self, "3GPP registration check failed: %s", error->message); |
| register_in_network_context_complete_failed (task, error); |
| return; |
| } |
| |
| current_registration_state = get_consolidated_reg_state (ctx->self); |
| |
| /* If we got a final state and it's denied, we can assume the registration is |
| * finished */ |
| if (current_registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_DENIED) { |
| mm_obj_info (self, "registration denied"); |
| register_in_network_context_complete_failed ( |
| task, |
| mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_NOT_ALLOWED, self)); |
| return; |
| } |
| |
| /* If we got registered, end registration checks */ |
| if (mm_modem_3gpp_registration_state_is_registered (current_registration_state)) { |
| /* Request immediate access tech and signal update: we may have changed |
| * from home to roaming or viceversa, both registered states, so there |
| * wouldn't be an explicit refresh triggered from the modem interface as |
| * the modem never got un-registered during the sequence. */ |
| mm_iface_modem_refresh_signal (MM_IFACE_MODEM (ctx->self)); |
| mm_obj_info (self, "currently registered in a 3GPP network"); |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| return; |
| } |
| |
| /* Don't spend too much time waiting to get registered */ |
| if (g_timer_elapsed (ctx->timer, NULL) > ctx->max_registration_time) { |
| mm_obj_info (self, "3GPP registration check timed out"); |
| register_in_network_context_complete_failed ( |
| task, |
| mm_mobile_equipment_error_for_code (MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT, self)); |
| return; |
| } |
| |
| /* If we're still waiting for automatic registration to complete or |
| * fail, check again in a few seconds. |
| * |
| * This 3s timeout will catch results from automatic registrations as |
| * well. |
| */ |
| mm_obj_dbg (self, "not yet registered in a 3GPP network... will recheck soon"); |
| g_timeout_add_seconds (3, (GSourceFunc)run_registration_checks, task); |
| } |
| |
| static gboolean |
| run_registration_checks (GTask *task) |
| { |
| RegisterInNetworkContext *ctx; |
| |
| ctx = g_task_get_task_data (task); |
| |
| /* Get fresh registration state */ |
| mm_iface_modem_3gpp_run_registration_checks ( |
| ctx->self, |
| (GAsyncReadyCallback)run_registration_checks_ready, |
| task); |
| return G_SOURCE_REMOVE; |
| } |
| |
| static void |
| register_in_network_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish (self, res, &error)) { |
| /* Propagate error when trying to lock to network */ |
| register_in_network_context_complete_failed (task, error); |
| return; |
| } |
| |
| /* Now try to gather current registration status until we're registered or |
| * the time goes off */ |
| run_registration_checks (task); |
| } |
| |
| static void |
| initial_registration_checks_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| Private *priv; |
| RegisterInNetworkContext *ctx; |
| GError *error = NULL; |
| const gchar *current_operator_code; |
| MMModem3gppRegistrationState reg_state; |
| |
| priv = get_private (self); |
| ctx = g_task_get_task_data (task); |
| |
| if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error)) { |
| mm_obj_info (self, "Initial 3GPP registration check failed: %s", error->message); |
| g_error_free (error); |
| /* Just continue as if nothing happened */ |
| } |
| |
| current_operator_code = mm_gdbus_modem3gpp_get_operator_code (ctx->skeleton); |
| reg_state = mm_gdbus_modem3gpp_get_registration_state (ctx->skeleton); |
| |
| /* Manual registration requested? */ |
| if (ctx->operator_id) { |
| /* If already registered manually with the requested operator, we're done */ |
| if (!ctx->force_registration && |
| (g_strcmp0 (current_operator_code, ctx->operator_id) == 0) && |
| mm_modem_3gpp_registration_state_is_registered (reg_state) && |
| priv->manual_registration) { |
| mm_obj_info (self, "already registered manually in selected network '%s', manual registration not launched...", |
| current_operator_code); |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| return; |
| } |
| |
| /* Manual registration to a new operator required */ |
| mm_obj_info (self, "launching manual network registration in '%s'...", ctx->operator_id); |
| g_free (priv->manual_registration_operator_id); |
| priv->manual_registration_operator_id = g_strdup (ctx->operator_id); |
| priv->manual_registration = TRUE; |
| } |
| /* Automatic registration requested? */ |
| else { |
| /* If the modem is already registered and the last time it was asked |
| * automatic registration, we're done */ |
| if (!ctx->force_registration && |
| mm_modem_3gpp_registration_state_is_registered (reg_state) && |
| !priv->manual_registration) { |
| mm_obj_info (self, "already registered automatically in network '%s'," |
| " automatic registration not launched...", |
| current_operator_code ? current_operator_code : "unknown"); |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| return; |
| } |
| |
| /* Automatic registration to a new operator requested */ |
| mm_obj_info (self, "launching automatic network registration..."); |
| g_clear_pointer (&priv->manual_registration_operator_id, g_free); |
| priv->manual_registration = FALSE; |
| } |
| |
| ctx->cancellable = g_cancellable_new (); |
| |
| /* Keep an accessible reference to the cancellable, so that we can cancel |
| * previous request when needed */ |
| priv->pending_registration_cancellable = g_object_ref (ctx->cancellable); |
| |
| ctx->timer = g_timer_new (); |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network ( |
| self, |
| ctx->operator_id, |
| ctx->cancellable, |
| (GAsyncReadyCallback)register_in_network_ready, |
| task); |
| } |
| |
| void |
| mm_iface_modem_3gpp_register_in_network (MMIfaceModem3gpp *self, |
| const gchar *operator_id, |
| gboolean force_registration, |
| guint max_registration_time, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| Private *priv; |
| RegisterInNetworkContext *ctx; |
| GTask *task; |
| GError *error = NULL; |
| |
| priv = get_private (self); |
| |
| ctx = g_slice_new0 (RegisterInNetworkContext); |
| ctx->self = g_object_ref (self); |
| ctx->force_registration = force_registration; |
| ctx->operator_id = (operator_id && operator_id[0]) ? g_strdup (operator_id) : NULL; |
| ctx->max_registration_time = max_registration_time; |
| |
| task = g_task_new (self, NULL, callback, user_data); |
| g_task_set_task_data (task, ctx, (GDestroyNotify)register_in_network_context_free); |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| if (!ctx->skeleton) { |
| g_task_return_new_error (task, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| "Couldn't get interface skeleton"); |
| g_object_unref (task); |
| return; |
| } |
| |
| /* Validate input MCC/MNC */ |
| if (ctx->operator_id && !mm_3gpp_parse_operator_id (ctx->operator_id, NULL, NULL, NULL, &error)) { |
| g_assert (error != NULL); |
| g_task_return_error (task, error); |
| g_object_unref (task); |
| return; |
| } |
| |
| /* (Try to) cancel previous registration request */ |
| if (priv->pending_registration_cancellable) { |
| g_cancellable_cancel (priv->pending_registration_cancellable); |
| g_clear_object (&priv->pending_registration_cancellable); |
| } |
| |
| /* Query initial registration state here in order to avoid re-registering. */ |
| mm_iface_modem_3gpp_run_registration_checks ( |
| self, |
| (GAsyncReadyCallback)initial_registration_checks_ready, |
| task); |
| } |
| |
| /*****************************************************************************/ |
| /* Request to reregister using the last settings */ |
| |
| #define REREGISTER_IN_NETWORK_TIMEOUT 120 |
| |
| gboolean |
| mm_iface_modem_3gpp_reregister_in_network_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return mm_iface_modem_3gpp_register_in_network_finish (self, res, error); |
| } |
| |
| void |
| mm_iface_modem_3gpp_reregister_in_network (MMIfaceModem3gpp *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| Private *priv; |
| |
| /* Relaunch registration using the last used settings */ |
| priv = get_private (self); |
| mm_iface_modem_3gpp_register_in_network (self, |
| priv->manual_registration_operator_id, |
| TRUE, /* if already registered with same settings, force re-registration */ |
| REREGISTER_IN_NETWORK_TIMEOUT, |
| callback, |
| user_data); |
| } |
| |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModem3gpp *self; |
| gchar *operator_id; |
| } HandleRegisterContext; |
| |
| static void |
| handle_register_context_free (HandleRegisterContext *ctx) |
| { |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_free (ctx->operator_id); |
| g_slice_free (HandleRegisterContext, ctx); |
| } |
| |
| static void |
| handle_register_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleRegisterContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_iface_modem_3gpp_register_in_network_finish (self, res, &error)) { |
| if (ctx->operator_id && ctx->operator_id[0]) |
| mm_obj_warn (self, "failed registering modem in '%s': %s", ctx->operator_id, error->message); |
| else |
| mm_obj_warn (self, "failed registering modem: %s", error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| } else { |
| if (ctx->operator_id && ctx->operator_id[0]) |
| mm_obj_info (self, "modem registered in '%s'", ctx->operator_id); |
| else |
| mm_obj_info (self, "modem registered"); |
| mm_gdbus_modem3gpp_complete_register (ctx->skeleton, ctx->invocation); |
| } |
| |
| handle_register_context_free (ctx); |
| } |
| |
| static void |
| handle_register_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleRegisterContext *ctx) |
| { |
| MMModemState modem_state = MM_MODEM_STATE_UNKNOWN; |
| GError *error = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_register_context_free (ctx); |
| return; |
| } |
| |
| g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network != NULL); |
| g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->register_in_network_finish != NULL); |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_STATE, &modem_state, |
| NULL); |
| |
| switch (modem_state) { |
| case MM_MODEM_STATE_FAILED: |
| case MM_MODEM_STATE_UNKNOWN: |
| case MM_MODEM_STATE_INITIALIZING: |
| case MM_MODEM_STATE_LOCKED: |
| case MM_MODEM_STATE_DISABLED: |
| case MM_MODEM_STATE_DISABLING: |
| case MM_MODEM_STATE_ENABLING: |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, |
| "Device not yet enabled"); |
| handle_register_context_free (ctx); |
| return; |
| |
| case MM_MODEM_STATE_ENABLED: |
| case MM_MODEM_STATE_SEARCHING: |
| case MM_MODEM_STATE_REGISTERED: |
| if (ctx->operator_id && ctx->operator_id[0]) |
| mm_obj_info (self, "processing user request to register modem in '%s'...", ctx->operator_id); |
| else |
| mm_obj_info (self, "processing user request to register modem..."); |
| mm_iface_modem_3gpp_register_in_network (MM_IFACE_MODEM_3GPP (self), |
| ctx->operator_id, |
| FALSE, /* if already registered with same settings, do nothing */ |
| 60, |
| (GAsyncReadyCallback)handle_register_ready, |
| ctx); |
| return; |
| |
| case MM_MODEM_STATE_DISCONNECTING: |
| case MM_MODEM_STATE_CONNECTING: |
| case MM_MODEM_STATE_CONNECTED: |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE, |
| "Operation not allowed while modem is connected"); |
| handle_register_context_free (ctx); |
| return; |
| |
| default: |
| g_assert_not_reached (); |
| } |
| } |
| |
| static gboolean |
| handle_register (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| const gchar *operator_id, |
| MMIfaceModem3gpp *self) |
| { |
| HandleRegisterContext *ctx; |
| |
| ctx = g_slice_new0 (HandleRegisterContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->operator_id = g_strdup (operator_id); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)handle_register_auth_ready, |
| ctx); |
| |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModem3gpp *self; |
| } HandleScanContext; |
| |
| static void |
| handle_scan_context_free (HandleScanContext *ctx) |
| { |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_slice_free (HandleScanContext, ctx); |
| } |
| |
| static GVariant * |
| build_scan_networks_result (MMIfaceModem3gpp *self, |
| GList *info_list) |
| { |
| GList *l; |
| GVariantBuilder builder; |
| |
| g_variant_builder_init (&builder, G_VARIANT_TYPE ("aa{sv}")); |
| |
| for (l = info_list; l; l = g_list_next (l)) { |
| MM3gppNetworkInfo *info = l->data; |
| g_autofree gchar *access_tech_str = NULL; |
| |
| if (!info->operator_code) { |
| g_warn_if_reached (); |
| continue; |
| } |
| |
| /* log results as INFO */ |
| access_tech_str = mm_modem_access_technology_build_string_from_mask (info->access_tech); |
| mm_obj_info (self, " mccmnc: %s, status: %s, access tech: %s, long name: %s, short name: %s", |
| info->operator_code, |
| mm_modem_3gpp_network_availability_get_string (info->status), |
| access_tech_str, |
| info->operator_long ? info->operator_long : "n/a", |
| info->operator_short ? info->operator_short : "n/a"); |
| |
| g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}")); |
| g_variant_builder_add (&builder, "{sv}", "operator-code", g_variant_new_string (info->operator_code)); |
| g_variant_builder_add (&builder, "{sv}", "status", g_variant_new_uint32 (info->status)); |
| g_variant_builder_add (&builder, "{sv}", "access-technology", g_variant_new_uint32 (info->access_tech)); |
| if (info->operator_long) |
| g_variant_builder_add (&builder, "{sv}", "operator-long", g_variant_new_string (info->operator_long)); |
| if (info->operator_short) |
| g_variant_builder_add (&builder, "{sv}", "operator-short", g_variant_new_string (info->operator_short)); |
| g_variant_builder_close (&builder); |
| } |
| |
| return g_variant_ref_sink (g_variant_builder_end (&builder)); |
| } |
| |
| static void |
| handle_scan_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleScanContext *ctx) |
| { |
| GError *error = NULL; |
| GList *info_list; |
| g_autoptr(GVariant) dict_array = NULL; |
| |
| info_list = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks_finish (self, res, &error); |
| if (error) { |
| mm_obj_warn (self, "failed scanning networks: %s", error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_scan_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "network scan performed: %u found", g_list_length (info_list)); |
| dict_array = build_scan_networks_result (self, info_list); |
| mm_gdbus_modem3gpp_complete_scan (ctx->skeleton, ctx->invocation, dict_array); |
| mm_3gpp_network_info_list_free (info_list); |
| handle_scan_context_free (ctx); |
| } |
| |
| static void |
| handle_scan_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleScanContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_scan_context_free (ctx); |
| return; |
| } |
| |
| /* If scanning is not implemented, report an error */ |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks || |
| !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks_finish) { |
| g_dbus_method_invocation_return_error (ctx->invocation, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Cannot scan networks: operation not supported"); |
| handle_scan_context_free (ctx); |
| return; |
| } |
| |
| if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), |
| ctx->invocation, |
| MM_MODEM_STATE_ENABLED)) { |
| handle_scan_context_free (ctx); |
| return; |
| } |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->scan_networks ( |
| MM_IFACE_MODEM_3GPP (self), |
| (GAsyncReadyCallback)handle_scan_ready, |
| ctx); |
| } |
| |
| static gboolean |
| handle_scan (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| MMIfaceModem3gpp *self) |
| { |
| HandleScanContext *ctx; |
| |
| ctx = g_slice_new0 (HandleScanContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)handle_scan_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModem3gpp *self; |
| MMModem3gppEpsUeModeOperation mode; |
| } HandleSetEpsUeModeOperationContext; |
| |
| static void |
| handle_set_eps_ue_mode_operation_context_free (HandleSetEpsUeModeOperationContext *ctx) |
| { |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_slice_free (HandleSetEpsUeModeOperationContext, ctx); |
| } |
| |
| static void |
| after_set_load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleSetEpsUeModeOperationContext *ctx) |
| { |
| MMModem3gppEpsUeModeOperation uemode; |
| GError *error = NULL; |
| |
| uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error); |
| if (error) { |
| mm_obj_warn (self, "failed reloading EPS UE mode of operation after update to '%s': %s", |
| mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode), |
| error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| return; |
| } |
| |
| if (uemode != ctx->mode) { |
| mm_obj_info (self, "requested (%s) and reloaded (%s) EPS UE mode of operation don't match", |
| mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode), |
| mm_modem_3gpp_eps_ue_mode_operation_get_string (uemode)); |
| g_dbus_method_invocation_return_error_literal (ctx->invocation, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| |
| "EPS UE mode of operation wasn't updated"); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| return; |
| } |
| |
| mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode); |
| mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| } |
| |
| static void |
| handle_set_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleSetEpsUeModeOperationContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish (self, res, &error)) { |
| mm_obj_warn (self, "failed setting EPS UE mode of operation to '%s': %s", |
| mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode), |
| error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| return; |
| } |
| |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation ( |
| self, |
| (GAsyncReadyCallback)after_set_load_eps_ue_mode_operation_ready, |
| ctx); |
| return; |
| } |
| |
| /* Assume we're ok */ |
| mm_obj_info (self, "EPS UE mode of operation set to '%s'", |
| mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode)); |
| mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| } |
| |
| static void |
| handle_set_eps_ue_mode_operation_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleSetEpsUeModeOperationContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| return; |
| } |
| |
| /* Check if we already are in the requested mode */ |
| if (mm_gdbus_modem3gpp_get_eps_ue_mode_operation (ctx->skeleton) == ctx->mode) { |
| /* Nothing to do */ |
| mm_gdbus_modem3gpp_complete_set_eps_ue_mode_operation (ctx->skeleton, ctx->invocation); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| return; |
| } |
| |
| /* If UE mode update is not implemented, report an error */ |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation || |
| !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation_finish) { |
| g_dbus_method_invocation_return_error (ctx->invocation, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_UNSUPPORTED, |
| "Cannot set UE mode of operation for EPS: operation not supported"); |
| handle_set_eps_ue_mode_operation_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "processing user request to set EPS UE mode of operation to '%s'...", |
| mm_modem_3gpp_eps_ue_mode_operation_get_string (ctx->mode)); |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_eps_ue_mode_operation ( |
| MM_IFACE_MODEM_3GPP (self), |
| ctx->mode, |
| (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_ready, |
| ctx); |
| } |
| |
| static gboolean |
| handle_set_eps_ue_mode_operation (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| guint mode, |
| MMIfaceModem3gpp *self) |
| { |
| HandleSetEpsUeModeOperationContext *ctx; |
| |
| ctx = g_slice_new0 (HandleSetEpsUeModeOperationContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->mode = mode; |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)handle_set_eps_ue_mode_operation_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModem3gpp *self; |
| GVariant *dictionary; |
| MMBearerProperties *config; |
| } HandleSetInitialEpsBearerSettingsContext; |
| |
| static void |
| handle_set_initial_eps_bearer_settings_context_free (HandleSetInitialEpsBearerSettingsContext *ctx) |
| { |
| g_clear_object (&ctx->config); |
| g_variant_unref (ctx->dictionary); |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_slice_free (HandleSetInitialEpsBearerSettingsContext, ctx); |
| } |
| |
| static void |
| after_set_load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleSetInitialEpsBearerSettingsContext *ctx) |
| { |
| GError *error = NULL; |
| g_autoptr(MMBearerProperties) new_config = NULL; |
| g_autoptr(GVariant) dictionary = NULL; |
| |
| new_config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error); |
| if (error) { |
| mm_obj_warn (self, "failed reloading initial EPS bearer settings after update: %s", error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| /* When we request to set an empty/NULL APN, we instead select the default initial attach APN used by the modem, |
| which will probably not be an empty APN. If that's the case, we won't check for equality between the requested |
| APN and the APN which is now set. */ |
| if (mm_bearer_properties_get_apn (ctx->config) && g_strcmp0 (mm_bearer_properties_get_apn (ctx->config), "") != 0 && |
| !mm_bearer_properties_cmp (new_config, ctx->config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) { |
| mm_obj_warn (self, "requested and reloaded initial EPS bearer settings don't match"); |
| mm_obj_info (self, "reloaded initial EPS bearer settings:"); |
| mm_log_bearer_properties (self, MM_LOG_LEVEL_INFO, " ", new_config); |
| g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, |
| "Initial EPS bearer settings were not updated"); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| dictionary = mm_bearer_properties_get_dictionary (new_config); |
| mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary); |
| mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| } |
| |
| static void |
| set_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleSetInitialEpsBearerSettingsContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish (self, res, &error)) { |
| mm_obj_warn (self, "failed setting initial EPS bearer settings: %s", error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "initial EPS bearer settings updated"); |
| |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings ( |
| self, |
| (GAsyncReadyCallback)after_set_load_initial_eps_bearer_settings_ready, |
| ctx); |
| return; |
| } |
| |
| /* Assume we're ok */ |
| mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| } |
| |
| static void |
| set_initial_eps_bearer_settings_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleSetInitialEpsBearerSettingsContext *ctx) |
| { |
| GError *error = NULL; |
| GVariant *old_dictionary; |
| g_autoptr(MMBearerProperties) old_config = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| /* If UE mode update is not implemented, report an error */ |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings || |
| !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings_finish) { |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, |
| "Operation not supported"); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| ctx->config = mm_bearer_properties_new_from_dictionary (ctx->dictionary, &error); |
| if (!ctx->config) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "processing user request to set initial EPS bearer settings..."); |
| mm_log_bearer_properties (self, MM_LOG_LEVEL_INFO, " ", ctx->config); |
| |
| old_dictionary = mm_gdbus_modem3gpp_get_initial_eps_bearer_settings (ctx->skeleton); |
| if (old_dictionary) |
| old_config = mm_bearer_properties_new_from_dictionary (old_dictionary, NULL); |
| |
| if (old_config && mm_bearer_properties_cmp (ctx->config, old_config, MM_BEARER_PROPERTIES_CMP_FLAGS_EPS)) { |
| mm_obj_info (self, "skipped setting initial EPS bearer settings: same configuration provided"); |
| mm_gdbus_modem3gpp_complete_set_initial_eps_bearer_settings (ctx->skeleton, ctx->invocation); |
| handle_set_initial_eps_bearer_settings_context_free (ctx); |
| return; |
| } |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_initial_eps_bearer_settings ( |
| MM_IFACE_MODEM_3GPP (self), |
| ctx->config, |
| (GAsyncReadyCallback)set_initial_eps_bearer_settings_ready, |
| ctx); |
| } |
| |
| static gboolean |
| handle_set_initial_eps_bearer_settings (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| GVariant *dictionary, |
| MMIfaceModem3gpp *self) |
| { |
| HandleSetInitialEpsBearerSettingsContext *ctx; |
| |
| ctx = g_slice_new0 (HandleSetInitialEpsBearerSettingsContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->dictionary = g_variant_ref (dictionary); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)set_initial_eps_bearer_settings_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModem3gpp *self; |
| GVariant *dictionary; |
| MMModem3gppFacility facility; |
| gchar *facility_str; |
| guint8 slot; |
| gchar *control_key; |
| } HandleDisableFacilityLockContext; |
| |
| static void |
| handle_disable_facility_lock_context_free (HandleDisableFacilityLockContext *ctx) |
| { |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_variant_unref (ctx->dictionary); |
| g_free (ctx->control_key); |
| g_free (ctx->facility_str); |
| g_slice_free (HandleDisableFacilityLockContext, ctx); |
| } |
| |
| static void |
| update_lock_info_ready (MMIfaceModem *modem, |
| GAsyncResult *res, |
| HandleDisableFacilityLockContext *ctx) |
| { |
| GError *error = NULL; |
| |
| mm_iface_modem_update_lock_info_finish (modem, res, &error); |
| if (error) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_disable_facility_lock_context_free (ctx); |
| return; |
| } |
| |
| mm_gdbus_modem3gpp_complete_disable_facility_lock (ctx->skeleton, ctx->invocation); |
| handle_disable_facility_lock_context_free (ctx); |
| } |
| |
| static void |
| handle_disable_facility_lock_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleDisableFacilityLockContext *ctx) |
| { |
| MMModem3gppFacility facilities; |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish (self, res, &error)) { |
| mm_obj_warn (self, "failed disabling facility lock '%s': %s", |
| ctx->facility_str, error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_disable_facility_lock_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "facility lock '%s' disabled", ctx->facility_str); |
| |
| /* Update the Enabled Facility Locks property in the DBus interface */ |
| facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (ctx->skeleton); |
| facilities &= ~ctx->facility; |
| mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities); |
| |
| /* Recheck lock status after unlock code has been sent */ |
| mm_iface_modem_update_lock_info (MM_IFACE_MODEM (self), |
| MM_MODEM_LOCK_UNKNOWN, /* ask */ |
| (GAsyncReadyCallback)update_lock_info_ready, |
| ctx); |
| } |
| |
| static void |
| disable_facility_lock_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleDisableFacilityLockContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_disable_facility_lock_context_free (ctx); |
| return; |
| } |
| |
| /* If disable facility locks is not implemented, report an error */ |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock || |
| !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock_finish) { |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, |
| "Operation not supported"); |
| handle_disable_facility_lock_context_free (ctx); |
| return; |
| } |
| |
| /* Parse properties dictionary */ |
| if (!g_variant_is_of_type (ctx->dictionary, G_VARIANT_TYPE ("(us)"))) { |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "Invalid parameters"); |
| handle_disable_facility_lock_context_free (ctx); |
| return; |
| } |
| |
| /* Only modems with single slot or single configuration for all slots are supported */ |
| ctx->slot = 1; |
| |
| g_variant_get (ctx->dictionary, "(us)", &ctx->facility, &ctx->control_key); |
| |
| /* Only four facility locks can be disabled: |
| * - MM_MODEM_3GPP_FACILITY_NET_PERS (network personalization) |
| * - MM_MODEM_3GPP_FACILITY_NET_SUB_PERS (network subset personalization) |
| * - MM_MODEM_3GPP_FACILITY_PROVIDER_PERS (service provider personalization) |
| * - MM_MODEM_3GPP_FACILITY_CORP_PERS (corporate personalization) |
| */ |
| if (ctx->facility != (ctx->facility & (MM_MODEM_3GPP_FACILITY_NET_PERS | |
| MM_MODEM_3GPP_FACILITY_NET_SUB_PERS | |
| MM_MODEM_3GPP_FACILITY_PROVIDER_PERS | |
| MM_MODEM_3GPP_FACILITY_CORP_PERS))) { |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "Invalid type of facility lock to disable or empty key"); |
| handle_disable_facility_lock_context_free (ctx); |
| return; |
| } |
| |
| ctx->facility_str = mm_modem_3gpp_facility_build_string_from_mask (ctx->facility); |
| mm_obj_info (self, "processing user request to disable facility lock '%s'...", ctx->facility_str); |
| mm_obj_info (self, " control key: %s", mm_log_str_personal_info (ctx->control_key)); |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_facility_lock ( |
| MM_IFACE_MODEM_3GPP (self), |
| ctx->facility, |
| ctx->slot, |
| ctx->control_key, |
| (GAsyncReadyCallback)handle_disable_facility_lock_ready, |
| ctx); |
| } |
| |
| static gboolean |
| handle_disable_facility_lock (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| GVariant *dictionary, |
| MMIfaceModem3gpp *self) |
| { |
| HandleDisableFacilityLockContext *ctx; |
| |
| ctx = g_slice_new0 (HandleDisableFacilityLockContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->dictionary = g_variant_ref (dictionary); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)disable_facility_lock_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| /* Set Packet Service State (internal) */ |
| |
| gboolean |
| mm_iface_modem_3gpp_set_packet_service_state_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| static void |
| set_packet_service_state_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state_finish (self, res, &error)) |
| g_task_return_error (task, error); |
| else |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| } |
| |
| void |
| mm_iface_modem_3gpp_set_packet_service_state (MMIfaceModem3gpp *self, |
| MMModem3gppPacketServiceState packet_service_state, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| GTask *task; |
| |
| g_assert (packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED || |
| packet_service_state == MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED); |
| |
| task = g_task_new (self, NULL, callback, user_data); |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state || |
| !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state_finish) { |
| g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, |
| "Explicit packet service attach/detach operation not supported"); |
| g_object_unref (task); |
| return; |
| } |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_packet_service_state ( |
| self, |
| packet_service_state, |
| (GAsyncReadyCallback)set_packet_service_state_ready, |
| task); |
| } |
| |
| /*****************************************************************************/ |
| /* Set Packet Service State */ |
| |
| typedef struct { |
| MMIfaceModem3gpp *self; |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMModem3gppPacketServiceState packet_service_state; |
| } HandlePacketServiceStateContext; |
| |
| static void |
| handle_set_packet_service_state_context_free (HandlePacketServiceStateContext *ctx) |
| { |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->self); |
| g_slice_free (HandlePacketServiceStateContext,ctx); |
| } |
| |
| static void |
| internal_set_packet_service_state_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandlePacketServiceStateContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_iface_modem_3gpp_set_packet_service_state_finish (self, res, &error)) { |
| mm_obj_warn (self, "failed setting packet service state to '%s': %s", |
| mm_modem_3gpp_packet_service_state_get_string (ctx->packet_service_state), |
| error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| } else { |
| mm_obj_info (self, "packet service state set to '%s'", |
| mm_modem_3gpp_packet_service_state_get_string (ctx->packet_service_state)); |
| mm_gdbus_modem3gpp_complete_set_packet_service_state (ctx->skeleton, ctx->invocation); |
| } |
| handle_set_packet_service_state_context_free (ctx); |
| } |
| |
| static void |
| set_packet_service_state_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandlePacketServiceStateContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_packet_service_state_context_free (ctx); |
| return; |
| } |
| |
| if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self), |
| ctx->invocation, |
| MM_MODEM_STATE_ENABLED)) { |
| handle_set_packet_service_state_context_free (ctx); |
| return; |
| } |
| |
| if ((ctx->packet_service_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_ATTACHED) && |
| (ctx->packet_service_state != MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED)) { |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, |
| "Invalid packet service state requested"); |
| handle_set_packet_service_state_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "processing user request to set packet service state to '%s'...", |
| mm_modem_3gpp_packet_service_state_get_string (ctx->packet_service_state)); |
| mm_iface_modem_3gpp_set_packet_service_state (ctx->self, |
| ctx->packet_service_state, |
| (GAsyncReadyCallback)internal_set_packet_service_state_ready, |
| ctx); |
| } |
| |
| static gboolean |
| handle_set_packet_service_state (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| MMModem3gppPacketServiceState packet_service_state, |
| MMIfaceModem3gpp *self) |
| { |
| HandlePacketServiceStateContext *ctx; |
| |
| ctx = g_slice_new0 (HandlePacketServiceStateContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->packet_service_state = packet_service_state; |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)set_packet_service_state_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| GDBusMethodInvocation *invocation; |
| MMIfaceModem3gpp *self; |
| GVariant *dictionary; |
| MMNr5gRegistrationSettings *settings; |
| } HandleSetNr5gRegistrationSettingsContext; |
| |
| static void |
| handle_set_nr5g_registration_settings_context_free (HandleSetNr5gRegistrationSettingsContext *ctx) |
| { |
| g_clear_object (&ctx->settings); |
| g_variant_unref (ctx->dictionary); |
| g_object_unref (ctx->skeleton); |
| g_object_unref (ctx->invocation); |
| g_object_unref (ctx->self); |
| g_slice_free (HandleSetNr5gRegistrationSettingsContext, ctx); |
| } |
| |
| static void |
| after_set_load_nr5g_registration_settings_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleSetNr5gRegistrationSettingsContext *ctx) |
| { |
| GError *error = NULL; |
| g_autoptr(MMNr5gRegistrationSettings) new_settings = NULL; |
| g_autoptr(GVariant) dictionary = NULL; |
| |
| new_settings = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish (self, res, &error); |
| if (error) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "5GNR registration settings updated"); |
| |
| if (!mm_nr5g_registration_settings_cmp (new_settings, ctx->settings)) { |
| mm_obj_info (self, "requested and reloaded 5GNR registration settings don't match"); |
| g_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, |
| "5GNR registration settings were not updated"); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| dictionary = mm_nr5g_registration_settings_get_dictionary (new_settings); |
| mm_gdbus_modem3gpp_set_nr5g_registration_settings (ctx->skeleton, dictionary); |
| mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings (ctx->skeleton, ctx->invocation); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| } |
| |
| static void |
| set_nr5g_registration_settings_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| HandleSetNr5gRegistrationSettingsContext *ctx) |
| { |
| GError *error = NULL; |
| |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings_finish (self, res, &error)) { |
| mm_obj_warn (self, "failed setting 5GNR registration settings: %s", error->message); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings ( |
| self, |
| (GAsyncReadyCallback)after_set_load_nr5g_registration_settings_ready, |
| ctx); |
| return; |
| } |
| |
| /* Assume we're ok */ |
| mm_obj_info (self, "5GNR registration settings updated"); |
| mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings (ctx->skeleton, ctx->invocation); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| } |
| |
| static void |
| set_nr5g_registration_settings_auth_ready (MMBaseModem *self, |
| GAsyncResult *res, |
| HandleSetNr5gRegistrationSettingsContext *ctx) |
| { |
| GError *error = NULL; |
| GVariant *old_dictionary; |
| g_autoptr(MMNr5gRegistrationSettings) old_settings = NULL; |
| MMModem3gppDrxCycle new_drx_cycle; |
| MMModem3gppMicoMode new_mico_mode; |
| |
| if (!mm_base_modem_authorize_finish (self, res, &error)) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| /* If 5GNR registration settings update is not implemented, report an error */ |
| if (!MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings || |
| !MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings_finish) { |
| g_dbus_method_invocation_return_error (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, |
| "Operation not supported"); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| ctx->settings = mm_nr5g_registration_settings_new_from_dictionary (ctx->dictionary, &error); |
| if (!ctx->settings) { |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| new_drx_cycle = mm_nr5g_registration_settings_get_drx_cycle (ctx->settings); |
| if (new_drx_cycle == MM_MODEM_3GPP_DRX_CYCLE_UNSUPPORTED) { |
| g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid value for DRX cycle: %s", |
| mm_modem_3gpp_drx_cycle_get_string (new_drx_cycle)); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| new_mico_mode = mm_nr5g_registration_settings_get_mico_mode (ctx->settings); |
| if (new_mico_mode == MM_MODEM_3GPP_MICO_MODE_UNSUPPORTED) { |
| g_set_error (&error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "Invalid value for MICO mode: %s", |
| mm_modem_3gpp_mico_mode_get_string (new_mico_mode)); |
| g_dbus_method_invocation_take_error (ctx->invocation, error); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| mm_obj_info (self, "processing user request to set 5GNR registration settings..."); |
| |
| old_dictionary = mm_gdbus_modem3gpp_get_nr5g_registration_settings (ctx->skeleton); |
| if (old_dictionary) |
| old_settings = mm_nr5g_registration_settings_new_from_dictionary (old_dictionary, NULL); |
| |
| if (old_settings && mm_nr5g_registration_settings_cmp (ctx->settings, old_settings)) { |
| mm_obj_info (self, "skipped setting 5GNR registration settings: same configuration provided"); |
| mm_gdbus_modem3gpp_complete_set_nr5g_registration_settings (ctx->skeleton, ctx->invocation); |
| handle_set_nr5g_registration_settings_context_free (ctx); |
| return; |
| } |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->set_nr5g_registration_settings ( |
| MM_IFACE_MODEM_3GPP (self), |
| ctx->settings, |
| (GAsyncReadyCallback)set_nr5g_registration_settings_ready, |
| ctx); |
| } |
| |
| static gboolean |
| handle_set_nr5g_registration_settings (MmGdbusModem3gpp *skeleton, |
| GDBusMethodInvocation *invocation, |
| GVariant *dictionary, |
| MMIfaceModem3gpp *self) |
| { |
| HandleSetNr5gRegistrationSettingsContext *ctx; |
| |
| ctx = g_slice_new0 (HandleSetNr5gRegistrationSettingsContext); |
| ctx->skeleton = g_object_ref (skeleton); |
| ctx->invocation = g_object_ref (invocation); |
| ctx->self = g_object_ref (self); |
| ctx->dictionary = g_variant_ref (dictionary); |
| |
| mm_base_modem_authorize (MM_BASE_MODEM (self), |
| invocation, |
| MM_AUTHORIZATION_DEVICE_CONTROL, |
| (GAsyncReadyCallback)set_nr5g_registration_settings_auth_ready, |
| ctx); |
| return TRUE; |
| } |
| |
| /*****************************************************************************/ |
| |
| gboolean |
| mm_iface_modem_3gpp_run_registration_checks_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks_finish != NULL); |
| return MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks_finish (self, res, error); |
| } |
| |
| void |
| mm_iface_modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| gboolean is_cs_supported; |
| gboolean is_ps_supported; |
| gboolean is_eps_supported; |
| gboolean is_5gs_supported; |
| |
| g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks != NULL); |
| |
| is_cs_supported = get_cs_network_supported (self); |
| is_ps_supported = get_ps_network_supported (self); |
| is_eps_supported = get_eps_network_supported (self); |
| is_5gs_supported = get_5gs_network_supported (self); |
| |
| mm_obj_dbg (self, "running registration checks (CS: '%s', PS: '%s', EPS: '%s', 5GS: '%s')", |
| is_cs_supported ? "yes" : "no", |
| is_ps_supported ? "yes" : "no", |
| is_eps_supported ? "yes" : "no", |
| is_5gs_supported ? "yes" : "no"); |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->run_registration_checks (self, |
| is_cs_supported, |
| is_ps_supported, |
| is_eps_supported, |
| is_5gs_supported, |
| callback, |
| user_data); |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct { |
| MmGdbusModem3gpp *skeleton; |
| gboolean operator_code_loaded; |
| gboolean operator_name_loaded; |
| } ReloadCurrentRegistrationInfoContext; |
| |
| static void |
| reload_current_registration_info_context_free (ReloadCurrentRegistrationInfoContext *ctx) |
| { |
| if (ctx->skeleton) |
| g_object_unref (ctx->skeleton); |
| g_slice_free (ReloadCurrentRegistrationInfoContext, ctx); |
| } |
| |
| gboolean |
| mm_iface_modem_3gpp_reload_current_registration_info_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| static void reload_current_registration_info_context_step (GTask *task); |
| |
| static void |
| load_operator_name_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| ReloadCurrentRegistrationInfoContext *ctx; |
| GError *error = NULL; |
| gchar *str; |
| |
| ctx = g_task_get_task_data (task); |
| |
| str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish (self, res, &error); |
| if (error) { |
| mm_obj_warn (self, "couldn't load operator name: %s", error->message); |
| g_error_free (error); |
| } |
| |
| if (ctx->skeleton) |
| mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, str); |
| g_free (str); |
| |
| ctx->operator_name_loaded = TRUE; |
| reload_current_registration_info_context_step (task); |
| } |
| |
| static void |
| load_operator_code_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| ReloadCurrentRegistrationInfoContext *ctx; |
| GError *error = NULL; |
| gchar *str; |
| |
| ctx = g_task_get_task_data (task); |
| |
| str = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish (self, res, &error); |
| if (error) { |
| mm_obj_warn (self, "couldn't load operator code: %s", error->message); |
| } else if (!mm_3gpp_parse_operator_id (str, NULL, NULL, NULL, &error)) { |
| mm_obj_warn (self, "unexpected operator code string '%s': %s", str, error->message); |
| g_clear_pointer (&str, g_free); |
| } |
| g_clear_error (&error); |
| |
| if (ctx->skeleton) |
| mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, str); |
| |
| /* If we also implement the location interface, update the 3GPP location */ |
| if (str && MM_IS_IFACE_MODEM_LOCATION (self)) |
| mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), str); |
| |
| g_free (str); |
| |
| ctx->operator_code_loaded = TRUE; |
| reload_current_registration_info_context_step (task); |
| } |
| |
| static void |
| reload_current_registration_info_context_step (GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| ReloadCurrentRegistrationInfoContext *ctx; |
| |
| self = g_task_get_source_object (task); |
| ctx = g_task_get_task_data (task); |
| |
| if (!ctx->operator_code_loaded) { |
| /* Launch operator code update */ |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code ( |
| self, |
| (GAsyncReadyCallback)load_operator_code_ready, |
| task); |
| return; |
| } |
| |
| if (!ctx->operator_name_loaded) { |
| /* Launch operator name update */ |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name ( |
| self, |
| (GAsyncReadyCallback)load_operator_name_ready, |
| task); |
| return; |
| } |
| |
| /* If all are loaded, all done */ |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| } |
| |
| void |
| mm_iface_modem_3gpp_reload_current_registration_info (MMIfaceModem3gpp *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| ReloadCurrentRegistrationInfoContext *ctx; |
| GTask *task; |
| |
| ctx = g_slice_new0 (ReloadCurrentRegistrationInfoContext); |
| |
| task = g_task_new (self, NULL, callback, user_data); |
| g_task_set_task_data (task, ctx, (GDestroyNotify)reload_current_registration_info_context_free); |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| if (!ctx->skeleton) { |
| g_task_return_new_error (task, |
| MM_CORE_ERROR, |
| MM_CORE_ERROR_FAILED, |
| "Couldn't get interface skeleton"); |
| g_object_unref (task); |
| return; |
| } |
| |
| ctx->operator_code_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_code_finish); |
| if (ctx->operator_code_loaded) { |
| mm_gdbus_modem3gpp_set_operator_code (ctx->skeleton, NULL); |
| if (MM_IS_IFACE_MODEM_LOCATION (self)) |
| mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL); |
| } |
| |
| ctx->operator_name_loaded = !(MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_operator_name_finish); |
| if (ctx->operator_name_loaded) |
| mm_gdbus_modem3gpp_set_operator_name (ctx->skeleton, NULL); |
| |
| reload_current_registration_info_context_step (task); |
| } |
| |
| void |
| mm_iface_modem_3gpp_clear_current_operator (MMIfaceModem3gpp *self) |
| { |
| MmGdbusModem3gpp *skeleton = NULL; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, |
| NULL); |
| if (!skeleton) |
| return; |
| |
| mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL); |
| mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL); |
| if (MM_IS_IFACE_MODEM_LOCATION (self)) |
| mm_iface_modem_location_3gpp_update_operator_code (MM_IFACE_MODEM_LOCATION (self), NULL); |
| } |
| |
| /*****************************************************************************/ |
| |
| void |
| mm_iface_modem_3gpp_update_access_technologies (MMIfaceModem3gpp *self, |
| MMModemAccessTechnology access_tech) |
| { |
| Private *priv; |
| MMModem3gppRegistrationState state; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state, |
| NULL); |
| |
| /* Even if registration state didn't change, report access technology, |
| * but only if something valid to report */ |
| if (mm_modem_3gpp_registration_state_is_registered (state) || priv->reloading_registration_info) { |
| if (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) |
| mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), |
| access_tech, |
| MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); |
| } else |
| mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self), |
| MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN, |
| MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK); |
| } |
| |
| void |
| mm_iface_modem_3gpp_update_location (MMIfaceModem3gpp *self, |
| gulong location_area_code, |
| gulong tracking_area_code, |
| gulong cell_id) |
| { |
| Private *priv; |
| MMModem3gppRegistrationState state; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| if (!MM_IS_IFACE_MODEM_LOCATION (self)) |
| return; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &state, |
| NULL); |
| |
| /* Even if registration state didn't change, report access technology or |
| * location updates, but only if something valid to report. For the case |
| * where we're registering (loading current registration info after a state |
| * change to registered), we also allow LAC/CID updates. */ |
| if (mm_modem_3gpp_registration_state_is_registered (state) || priv->reloading_registration_info) { |
| if (location_area_code || tracking_area_code || cell_id) |
| mm_iface_modem_location_3gpp_update_lac_tac_ci (MM_IFACE_MODEM_LOCATION (self), |
| location_area_code, |
| tracking_area_code, |
| cell_id); |
| } else |
| mm_iface_modem_location_3gpp_clear (MM_IFACE_MODEM_LOCATION (self)); |
| } |
| |
| /*****************************************************************************/ |
| |
| static void update_packet_service_state (MMIfaceModem3gpp *self, |
| MMModem3gppPacketServiceState new_state); |
| |
| static void |
| update_registration_reload_current_registration_info_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| gpointer user_data) |
| { |
| Private *priv; |
| MMModem3gppRegistrationState new_state; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| new_state = GPOINTER_TO_UINT (user_data); |
| |
| /* Update packet service state if we don't support external updates */ |
| if (!priv->packet_service_state_update_supported) |
| update_packet_service_state (self, get_consolidated_packet_service_state (self)); |
| |
| mm_obj_msg (self, "3GPP registration state changed (registering -> %s)", |
| mm_modem_3gpp_registration_state_get_string (new_state)); |
| mm_obj_info (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'", |
| mm_modem_3gpp_registration_state_get_string (priv->state_cs), |
| mm_modem_3gpp_registration_state_get_string (priv->state_ps), |
| mm_modem_3gpp_registration_state_get_string (priv->state_eps), |
| mm_modem_3gpp_registration_state_get_string (priv->state_5gs), |
| mm_modem_3gpp_registration_state_get_string (new_state)); |
| |
| /* The properties in the interface are bound to the properties |
| * in the skeleton, so just updating here is enough */ |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, new_state, |
| NULL); |
| |
| mm_iface_modem_update_subsystem_state (MM_IFACE_MODEM (self), |
| SUBSYSTEM_3GPP, |
| MM_MODEM_STATE_REGISTERED, |
| MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); |
| |
| priv->reloading_registration_info = FALSE; |
| } |
| |
| static void |
| update_non_registered_state (MMIfaceModem3gpp *self, |
| MMModem3gppRegistrationState old_state, |
| MMModem3gppRegistrationState new_state) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| /* Not registered neither in home nor roaming network */ |
| mm_iface_modem_3gpp_clear_current_operator (self); |
| |
| /* No packet service if we're not registered. This change is done |
| * also when the device itself supports reporting the packet service |
| * state updates. */ |
| update_packet_service_state (self, MM_MODEM_3GPP_PACKET_SERVICE_STATE_DETACHED); |
| |
| /* The property in the interface is bound to the property |
| * in the skeleton, so just updating here is enough */ |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, new_state, |
| NULL); |
| |
| mm_iface_modem_update_subsystem_state ( |
| MM_IFACE_MODEM (self), |
| SUBSYSTEM_3GPP, |
| (new_state == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING ? |
| MM_MODEM_STATE_SEARCHING : |
| MM_MODEM_STATE_ENABLED), |
| MM_MODEM_STATE_CHANGE_REASON_UNKNOWN); |
| } |
| |
| static void |
| update_registration_state (MMIfaceModem3gpp *self, |
| MMModem3gppRegistrationState new_state) |
| { |
| Private *priv; |
| MMModem3gppRegistrationState old_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, &old_state, |
| NULL); |
| |
| /* Only set new state if different */ |
| if (new_state == old_state) { |
| MMModem3gppPacketServiceState old_packet_service_state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; |
| |
| /* If packet service updates are expected, we can ignore the packet service state as that |
| * info won't be used to build a consolidated packet service state */ |
| if (priv->packet_service_state_update_supported) |
| return; |
| |
| /* If packet service updates are not expected, also check whether there are changes |
| * in the consolidate packet service state */ |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &old_packet_service_state, |
| NULL); |
| if (old_packet_service_state == get_consolidated_packet_service_state (self)) |
| return; |
| } |
| |
| if (mm_modem_3gpp_registration_state_is_registered (new_state)) { |
| MMModemState modem_state; |
| |
| /* If already reloading registration info, skip it */ |
| if (priv->reloading_registration_info) |
| return; |
| |
| /* If the modem isn't already enabled, this registration state update |
| * could be due to a previously scheduled initial registration check |
| * when the modem was being enabled. We need to ignore it as otherwise |
| * it may cause an incorrect transition of the registration state and |
| * modem state when the modem is being disabled or still going through |
| * enable steps */ |
| modem_state = MM_MODEM_STATE_UNKNOWN; |
| g_object_get (self, |
| MM_IFACE_MODEM_STATE, &modem_state, |
| NULL); |
| if (modem_state < MM_MODEM_STATE_ENABLED) { |
| mm_obj_dbg (self, "3GPP registration state change ignored as modem isn't enabled"); |
| return; |
| } |
| |
| mm_obj_msg (self, "3GPP registration state changed (%s -> registering)", |
| mm_modem_3gpp_registration_state_get_string (old_state)); |
| |
| /* Reload current registration info. ONLY update the state to REGISTERED |
| * after having loaded operator code/name/subscription state */ |
| priv->reloading_registration_info = TRUE; |
| mm_iface_modem_3gpp_reload_current_registration_info ( |
| self, |
| (GAsyncReadyCallback)update_registration_reload_current_registration_info_ready, |
| GUINT_TO_POINTER (new_state)); |
| return; |
| } |
| |
| mm_obj_msg (self, "3GPP registration state changed (%s -> %s)", |
| mm_modem_3gpp_registration_state_get_string (old_state), |
| mm_modem_3gpp_registration_state_get_string (new_state)); |
| mm_obj_info (self, "consolidated registration state: cs '%s', ps '%s', eps '%s', 5gs '%s' --> '%s'", |
| mm_modem_3gpp_registration_state_get_string (priv->state_cs), |
| mm_modem_3gpp_registration_state_get_string (priv->state_ps), |
| mm_modem_3gpp_registration_state_get_string (priv->state_eps), |
| mm_modem_3gpp_registration_state_get_string (priv->state_5gs), |
| mm_modem_3gpp_registration_state_get_string (new_state)); |
| |
| update_non_registered_state (self, old_state, new_state); |
| } |
| |
| #define UPDATE_REGISTRATION_STATE(domain) \ |
| void \ |
| mm_iface_modem_3gpp_update_##domain##_registration_state (MMIfaceModem3gpp *self, \ |
| MMModem3gppRegistrationState state, \ |
| gboolean deferred) \ |
| { \ |
| Private *priv; \ |
| \ |
| if (!get_##domain##_network_supported (self)) \ |
| return; \ |
| \ |
| priv = get_private (self); \ |
| if (!priv->iface_enabled) \ |
| return; \ |
| priv->state_##domain = state; \ |
| \ |
| if (!deferred) \ |
| mm_iface_modem_3gpp_apply_deferred_registration_state (self); \ |
| } |
| |
| UPDATE_REGISTRATION_STATE (cs) |
| UPDATE_REGISTRATION_STATE (ps) |
| UPDATE_REGISTRATION_STATE (eps) |
| UPDATE_REGISTRATION_STATE (5gs) |
| |
| void |
| mm_iface_modem_3gpp_apply_deferred_registration_state (MMIfaceModem3gpp *self) |
| { |
| update_registration_state (self, get_consolidated_reg_state (self)); |
| } |
| |
| /*****************************************************************************/ |
| /* Packet service state as reported by the device */ |
| |
| static void |
| update_packet_service_state (MMIfaceModem3gpp *self, |
| MMModem3gppPacketServiceState new_state) |
| { |
| MMModem3gppPacketServiceState old_state = MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN; |
| Private *priv; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, &old_state, |
| NULL); |
| |
| /* Only set new state if different */ |
| if (old_state == new_state) |
| return; |
| |
| mm_obj_msg (self, "3GPP packet service state changed (%s -> %s)", |
| mm_modem_3gpp_packet_service_state_get_string (old_state), |
| mm_modem_3gpp_packet_service_state_get_string (new_state)); |
| |
| /* The properties in the interface are bound to the properties |
| * in the skeleton, so just updating here is enough */ |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, new_state, |
| NULL); |
| } |
| |
| void |
| mm_iface_modem_3gpp_update_packet_service_state (MMIfaceModem3gpp *self, |
| MMModem3gppPacketServiceState new_state) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| priv->packet_service_state_update_supported = TRUE; |
| update_packet_service_state (self, new_state); |
| } |
| |
| /*****************************************************************************/ |
| /* Periodic registration checks */ |
| |
| #define REGISTRATION_CHECK_TIMEOUT_SEC 30 |
| |
| static void |
| periodic_registration_checks_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res) |
| { |
| Private *priv; |
| GError *error = NULL; |
| |
| priv = get_private (self); |
| |
| mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error); |
| if (error) { |
| mm_obj_dbg (self, "couldn't refresh 3GPP registration status: %s", error->message); |
| g_error_free (error); |
| } |
| |
| priv->check_running = FALSE; |
| } |
| |
| static gboolean |
| periodic_registration_check (MMIfaceModem3gpp *self) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| |
| /* Only launch a new one if not one running already */ |
| if (!priv->check_running) { |
| priv->check_running = TRUE; |
| mm_iface_modem_3gpp_run_registration_checks ( |
| self, |
| (GAsyncReadyCallback)periodic_registration_checks_ready, |
| NULL); |
| } |
| return G_SOURCE_CONTINUE; |
| } |
| |
| static void |
| periodic_registration_check_disable (MMIfaceModem3gpp *self) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| |
| /* Do nothing if already disabled */ |
| if (!priv->check_timeout_source) |
| return; |
| |
| g_source_remove (priv->check_timeout_source); |
| priv->check_timeout_source = 0; |
| |
| mm_obj_dbg (self, "periodic 3GPP registration checks disabled"); |
| } |
| |
| static void |
| periodic_registration_check_enable (MMIfaceModem3gpp *self) |
| { |
| Private *priv; |
| |
| priv = get_private (self); |
| |
| /* Do nothing if already enabled */ |
| if (priv->check_timeout_source) |
| return; |
| |
| /* Create context and keep it as object data */ |
| mm_obj_dbg (self, "periodic 3GPP registration checks enabled"); |
| priv->check_timeout_source = g_timeout_add_seconds (REGISTRATION_CHECK_TIMEOUT_SEC, |
| (GSourceFunc)periodic_registration_check, |
| self); |
| } |
| |
| /*****************************************************************************/ |
| |
| void |
| mm_iface_modem_3gpp_update_pco_list (MMIfaceModem3gpp *self, |
| const GList *pco_list) |
| { |
| MmGdbusModem3gpp *skeleton = NULL; |
| GVariantBuilder builder; |
| GVariant *variant; |
| const GList *iter; |
| Private *priv; |
| |
| priv = get_private (self); |
| if (!priv->iface_enabled) |
| return; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, |
| NULL); |
| if (!skeleton) |
| return; |
| |
| g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ubay)")); |
| for (iter = pco_list; iter; iter = g_list_next (iter)) { |
| g_autoptr(GVariant) pco_variant = NULL; |
| |
| pco_variant = mm_pco_to_variant (MM_PCO (iter->data)); |
| g_variant_builder_add_value (&builder, pco_variant); |
| } |
| variant = g_variant_ref_sink (g_variant_builder_end (&builder)); |
| mm_gdbus_modem3gpp_set_pco (skeleton, variant); |
| g_variant_unref (variant); |
| g_object_unref (skeleton); |
| } |
| |
| /*****************************************************************************/ |
| |
| void |
| mm_iface_modem_3gpp_update_initial_eps_bearer (MMIfaceModem3gpp *self, |
| MMBearerProperties *properties) |
| { |
| g_autoptr(MmGdbusModem3gppSkeleton) skeleton = NULL; |
| g_autoptr(MMBaseBearer) old_bearer = NULL; |
| g_autoptr(MMBaseBearer) new_bearer = NULL; |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, |
| MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, &old_bearer, |
| NULL); |
| g_assert (skeleton); |
| |
| /* skip update? */ |
| if ((!old_bearer && !properties) || |
| (old_bearer && properties && |
| mm_bearer_properties_cmp (properties, |
| mm_base_bearer_peek_config (MM_BASE_BEARER (old_bearer)), |
| MM_BEARER_PROPERTIES_CMP_FLAGS_EPS))) |
| return; |
| |
| if (!properties) { |
| mm_obj_dbg (self, "clearing initial EPS bearer..."); |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, NULL, |
| NULL); |
| mm_gdbus_modem3gpp_set_initial_eps_bearer (MM_GDBUS_MODEM3GPP (skeleton), NULL); |
| return; |
| } |
| |
| mm_obj_dbg (self, "updating initial EPS bearer..."); |
| g_assert (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer); |
| new_bearer = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->create_initial_eps_bearer (self, properties); |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, new_bearer, |
| NULL); |
| mm_gdbus_modem3gpp_set_initial_eps_bearer (MM_GDBUS_MODEM3GPP (skeleton), |
| mm_base_bearer_get_path (new_bearer)); |
| } |
| |
| static void |
| reload_initial_eps_bearer_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res) |
| { |
| g_autoptr(MMBearerProperties) properties = NULL; |
| g_autoptr(GError) error = NULL; |
| |
| properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error); |
| if (!properties) { |
| mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message); |
| return; |
| } |
| |
| mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties); |
| } |
| |
| void |
| mm_iface_modem_3gpp_reload_initial_eps_bearer (MMIfaceModem3gpp *self) |
| { |
| if (get_eps_network_supported (self) && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer ( |
| self, |
| (GAsyncReadyCallback)reload_initial_eps_bearer_ready, |
| NULL); |
| } |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct _DisablingContext DisablingContext; |
| static void interface_disabling_step (GTask *task); |
| |
| typedef enum { |
| DISABLING_STEP_FIRST, |
| DISABLING_STEP_INITIAL_EPS_BEARER, |
| DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS, |
| DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS, |
| DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS, |
| DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS, |
| DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS, |
| DISABLING_STEP_REGISTRATION_STATE, |
| DISABLING_STEP_LAST |
| } DisablingStep; |
| |
| struct _DisablingContext { |
| DisablingStep step; |
| MmGdbusModem *skeleton; |
| }; |
| |
| static void |
| disabling_context_free (DisablingContext *ctx) |
| { |
| g_clear_object (&ctx->skeleton); |
| g_slice_free (DisablingContext, ctx); |
| } |
| |
| gboolean |
| mm_iface_modem_3gpp_disable_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| #undef VOID_REPLY_READY_FN |
| #define VOID_REPLY_READY_FN(NAME,DISPLAY) \ |
| static void \ |
| NAME##_ready (MMIfaceModem3gpp *self, \ |
| GAsyncResult *res, \ |
| GTask *task) \ |
| { \ |
| DisablingContext *ctx; \ |
| g_autoptr(GError) error = NULL; \ |
| \ |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (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_disabling_step (task); \ |
| } |
| |
| VOID_REPLY_READY_FN (cleanup_unsolicited_events, |
| "cleanup unsolicited events") |
| VOID_REPLY_READY_FN (disable_unsolicited_events, |
| "disable unsolicited events") |
| VOID_REPLY_READY_FN (cleanup_unsolicited_registration_events, |
| "cleanup unsolicited registration events") |
| VOID_REPLY_READY_FN (disable_unsolicited_registration_events, |
| "disable unsolicited registration events") |
| |
| static void |
| interface_disabling_step (GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| Private *priv; |
| DisablingContext *ctx; |
| |
| self = g_task_get_source_object (task); |
| priv = get_private (self); |
| ctx = g_task_get_task_data (task); |
| |
| switch (ctx->step) { |
| case DISABLING_STEP_FIRST: |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_INITIAL_EPS_BEARER: |
| mm_iface_modem_3gpp_update_initial_eps_bearer (self, NULL); |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_PERIODIC_REGISTRATION_CHECKS: |
| /* Disable periodic registration checks, if they were set */ |
| periodic_registration_check_disable (self); |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_DISABLE_UNSOLICITED_REGISTRATION_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_registration_events ( |
| self, |
| get_cs_network_supported (self), |
| get_ps_network_supported (self), |
| get_eps_network_supported (self), |
| (GAsyncReadyCallback)disable_unsolicited_registration_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_CLEANUP_UNSOLICITED_REGISTRATION_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_registration_events ( |
| self, |
| (GAsyncReadyCallback)cleanup_unsolicited_registration_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_CLEANUP_UNSOLICITED_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->cleanup_unsolicited_events ( |
| self, |
| (GAsyncReadyCallback)cleanup_unsolicited_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_DISABLE_UNSOLICITED_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->disable_unsolicited_events ( |
| self, |
| (GAsyncReadyCallback)disable_unsolicited_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_REGISTRATION_STATE: |
| update_packet_service_state (self, MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN); |
| update_registration_state (self, MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN); |
| mm_iface_modem_3gpp_update_access_technologies (self, MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN); |
| mm_iface_modem_3gpp_update_location (self, 0, 0, 0); |
| ctx->step++; |
| /* fall through */ |
| |
| case DISABLING_STEP_LAST: |
| /* Interface state is assumed enabled until the very end of the disabling sequence, |
| * so that updates are taken into account and not ignored. */ |
| priv->iface_enabled = FALSE; |
| /* 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_disable (MMIfaceModem3gpp *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| DisablingContext *ctx; |
| GTask *task; |
| |
| ctx = g_slice_new0 (DisablingContext); |
| ctx->step = DISABLING_STEP_FIRST; |
| |
| task = g_task_new (self, NULL, callback, user_data); |
| g_task_set_task_data (task, ctx, (GDestroyNotify)disabling_context_free); |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| if (!ctx->skeleton) { |
| g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, |
| "Couldn't get interface skeleton"); |
| g_object_unref (task); |
| return; |
| } |
| |
| interface_disabling_step (task); |
| } |
| |
| /*****************************************************************************/ |
| |
| typedef struct _EnablingContext EnablingContext; |
| static void interface_enabling_step (GTask *task); |
| |
| typedef enum { |
| ENABLING_STEP_FIRST, |
| ENABLING_STEP_SETUP_UNSOLICITED_EVENTS, |
| ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS, |
| ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS, |
| ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS, |
| ENABLING_STEP_INITIAL_EPS_BEARER, |
| ENABLING_STEP_LAST |
| } EnablingStep; |
| |
| struct _EnablingContext { |
| EnablingStep step; |
| MmGdbusModem3gpp *skeleton; |
| }; |
| |
| static void |
| enabling_context_free (EnablingContext *ctx) |
| { |
| g_clear_object (&ctx->skeleton); |
| g_slice_free (EnablingContext, ctx); |
| } |
| |
| gboolean |
| mm_iface_modem_3gpp_enable_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| static void |
| setup_unsolicited_events_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| EnablingContext *ctx; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish (self, res, &error); |
| if (error) { |
| mm_obj_dbg (self, "setting up unsolicited events failed: %s", error->message); |
| |
| /* If we get an error setting up unsolicited events, don't even bother trying to |
| * enable them. */ |
| ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS + 1; |
| interface_enabling_step (task); |
| return; |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_enabling_step (task); |
| } |
| |
| static void |
| enable_unsolicited_events_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| EnablingContext *ctx; |
| g_autoptr(GError) error = NULL; |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish (self, res, &error); |
| if (error) |
| mm_obj_dbg (self, "enabling unsolicited events failed: %s", error->message); |
| |
| /* Go on to next step */ |
| ctx = g_task_get_task_data (task); |
| ctx->step++; |
| interface_enabling_step (task); |
| } |
| |
| static void |
| setup_unsolicited_registration_events_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| EnablingContext *ctx; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish (self, res, &error); |
| if (error) { |
| mm_obj_dbg (self, "setting up unsolicited registration events failed: %s", error->message); |
| /* If error, setup periodic registration checks */ |
| periodic_registration_check_enable (self); |
| |
| /* If we get an error setting up unsolicited events, don't even bother trying to |
| * enable them. */ |
| ctx->step = ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS + 1; |
| interface_enabling_step (task); |
| return; |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_enabling_step (task); |
| } |
| |
| static void |
| enable_unsolicited_registration_events_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| EnablingContext *ctx; |
| g_autoptr(GError) error = NULL; |
| |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish (self, res, &error); |
| if (error) { |
| mm_obj_dbg (self, "enabling unsolicited registration events failed: %s", error->message); |
| /* If error, setup periodic registration checks */ |
| periodic_registration_check_enable (self); |
| } |
| |
| /* Go on to next step */ |
| ctx = g_task_get_task_data (task); |
| ctx->step++; |
| interface_enabling_step (task); |
| } |
| |
| static void |
| load_initial_eps_bearer_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| EnablingContext *ctx; |
| g_autoptr(MMBearerProperties) properties = NULL; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error); |
| if (!properties) |
| mm_obj_dbg (self, "couldn't load initial default bearer properties: %s", error->message); |
| else |
| mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties); |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_enabling_step (task); |
| } |
| |
| static void |
| interface_enabling_step (GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| Private *priv; |
| EnablingContext *ctx; |
| |
| self = g_task_get_source_object (task); |
| priv = get_private (self); |
| ctx = g_task_get_task_data (task); |
| |
| /* Don't run new steps if we're cancelled */ |
| if (g_task_return_error_if_cancelled (task)) { |
| priv->iface_enabled = FALSE; |
| g_object_unref (task); |
| return; |
| } |
| |
| switch (ctx->step) { |
| case ENABLING_STEP_FIRST: |
| /* Interface state is assumed enabled from the very beginning of the enabling sequence, |
| * so that updates are taken into account and not ignored. */ |
| priv->iface_enabled = TRUE; |
| ctx->step++; |
| /* fall through */ |
| |
| case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_events ( |
| self, |
| (GAsyncReadyCallback)setup_unsolicited_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_events ( |
| self, |
| (GAsyncReadyCallback)enable_unsolicited_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case ENABLING_STEP_SETUP_UNSOLICITED_REGISTRATION_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->setup_unsolicited_registration_events ( |
| self, |
| (GAsyncReadyCallback)setup_unsolicited_registration_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case ENABLING_STEP_ENABLE_UNSOLICITED_REGISTRATION_EVENTS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->enable_unsolicited_registration_events ( |
| self, |
| get_cs_network_supported (self), |
| get_ps_network_supported (self), |
| get_eps_network_supported (self), |
| (GAsyncReadyCallback)enable_unsolicited_registration_events_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case ENABLING_STEP_INITIAL_EPS_BEARER: |
| if (get_eps_network_supported (self) && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer ( |
| self, |
| (GAsyncReadyCallback)load_initial_eps_bearer_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case ENABLING_STEP_LAST: |
| /* We are done without errors! */ |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| return; |
| |
| default: |
| break; |
| } |
| |
| g_assert_not_reached (); |
| } |
| |
| void |
| mm_iface_modem_3gpp_enable (MMIfaceModem3gpp *self, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| EnablingContext *ctx; |
| GTask *task; |
| |
| ctx = g_slice_new0 (EnablingContext); |
| ctx->step = ENABLING_STEP_FIRST; |
| |
| task = g_task_new (self, cancellable, callback, user_data); |
| g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free); |
| |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &ctx->skeleton, |
| NULL); |
| if (!ctx->skeleton) { |
| g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, |
| "Couldn't get interface skeleton"); |
| g_object_unref (task); |
| return; |
| } |
| |
| interface_enabling_step (task); |
| } |
| |
| /*****************************************************************************/ |
| |
| #if defined WITH_SUSPEND_RESUME |
| |
| typedef struct _SyncingContext SyncingContext; |
| static void interface_syncing_step (GTask *task); |
| |
| typedef enum { |
| SYNCING_STEP_FIRST, |
| SYNCING_STEP_REFRESH_3GPP_REGISTRATION, |
| SYNCING_STEP_REFRESH_EPS_BEARER, |
| SYNCING_STEP_LAST |
| } SyncingStep; |
| |
| struct _SyncingContext { |
| SyncingStep step; |
| }; |
| |
| gboolean |
| mm_iface_modem_3gpp_sync_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| static void |
| sync_eps_bearer_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| SyncingContext *ctx; |
| g_autoptr(MMBearerProperties) properties = NULL; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| properties = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish (self, res, &error); |
| if (!properties) |
| mm_obj_dbg (self, "couldn't refresh initial EPS bearer status: %s", error->message); |
| else |
| mm_iface_modem_3gpp_update_initial_eps_bearer (self, properties); |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_syncing_step (task); |
| } |
| |
| static void |
| sync_eps_bearer (MMIfaceModem3gpp *self, |
| GAsyncReadyCallback callback, |
| GTask *task) |
| { |
| SyncingContext *ctx; |
| |
| /* Refresh EPS bearer if supported */ |
| if (get_eps_network_supported (self) && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer ( |
| self, |
| callback, |
| task); |
| return; |
| } |
| |
| /* If EPS is unsupported, just go to the next step */ |
| ctx = g_task_get_task_data (task); |
| ctx->step++; |
| interface_syncing_step (task); |
| } |
| |
| static void |
| sync_registration_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| SyncingContext *ctx; |
| g_autoptr (GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| if (!mm_iface_modem_3gpp_run_registration_checks_finish (self, res, &error)) |
| mm_obj_dbg (self, "couldn't synchronize 3GPP registration: %s", error->message); |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_syncing_step(task); |
| } |
| |
| static void |
| interface_syncing_step (GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| SyncingContext *ctx; |
| |
| self = g_task_get_source_object (task); |
| ctx = g_task_get_task_data (task); |
| |
| switch (ctx->step) { |
| case SYNCING_STEP_FIRST: |
| ctx->step++; |
| /* fall through */ |
| |
| case SYNCING_STEP_REFRESH_3GPP_REGISTRATION: |
| /* |
| * Refresh registration info to verify that the modem is still registered. |
| * Wait until registration checks are complete before going to the next step. |
| */ |
| mm_iface_modem_3gpp_run_registration_checks ( |
| self, |
| (GAsyncReadyCallback)sync_registration_ready, |
| task); |
| return; |
| |
| case SYNCING_STEP_REFRESH_EPS_BEARER: |
| /* |
| * Refresh EPS bearer and wait until complete. |
| * We want to make sure that the modem is fully enabled again |
| * when we refresh the mobile data connection bearers. |
| */ |
| sync_eps_bearer ( |
| self, |
| (GAsyncReadyCallback)sync_eps_bearer_ready, |
| task); |
| return; |
| |
| case SYNCING_STEP_LAST: |
| /* We are done without errors! */ |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| return; |
| |
| default: |
| break; |
| } |
| |
| g_assert_not_reached (); |
| } |
| |
| void |
| mm_iface_modem_3gpp_sync (MMIfaceModem3gpp *self, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| SyncingContext *ctx; |
| GTask *task; |
| |
| /* Create SyncingContext */ |
| ctx = g_new0 (SyncingContext, 1); |
| ctx->step = SYNCING_STEP_FIRST; |
| |
| /* Create sync steps task and execute it */ |
| task = g_task_new (self, NULL, callback, user_data); |
| g_task_set_task_data (task, ctx, (GDestroyNotify)g_free); |
| interface_syncing_step (task); |
| } |
| |
| #endif |
| |
| /*****************************************************************************/ |
| |
| typedef struct _InitializationContext InitializationContext; |
| static void interface_initialization_step (GTask *task); |
| |
| typedef enum { |
| INITIALIZATION_STEP_FIRST, |
| INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS, |
| INITIALIZATION_STEP_IMEI, |
| INITIALIZATION_STEP_TEST_LOCKED_OR_FAILED, |
| INITIALIZATION_STEP_EPS_UE_MODE_OPERATION, |
| INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS, |
| INITIALIZATION_STEP_NR5G_REGISTRATION_SETTINGS, |
| INITIALIZATION_STEP_CONNECT_SIGNALS, |
| INITIALIZATION_STEP_LAST |
| } InitializationStep; |
| |
| struct _InitializationContext { |
| MmGdbusModem3gpp *skeleton; |
| InitializationStep step; |
| }; |
| |
| static void |
| initialization_context_free (InitializationContext *ctx) |
| { |
| g_clear_object (&ctx->skeleton); |
| g_slice_free (InitializationContext, ctx); |
| } |
| |
| static void |
| sim_pin_lock_enabled_cb (MMBaseSim *self, |
| gboolean enabled, |
| MmGdbusModem3gpp *skeleton) |
| { |
| MMModem3gppFacility facilities; |
| |
| facilities = mm_gdbus_modem3gpp_get_enabled_facility_locks (skeleton); |
| if (enabled) |
| facilities |= MM_MODEM_3GPP_FACILITY_SIM; |
| else |
| facilities &= ~MM_MODEM_3GPP_FACILITY_SIM; |
| |
| mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, facilities); |
| } |
| |
| static void |
| load_nr5g_registration_settings_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| InitializationContext *ctx; |
| g_autoptr(GError) error = NULL; |
| g_autoptr(MMNr5gRegistrationSettings) settings = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| settings = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish (self, res, &error); |
| if (!settings) { |
| mm_obj_dbg (self, "couldn't load 5GNR registration settings: %s", error->message); |
| } else { |
| g_autoptr(GVariant) dictionary = NULL; |
| |
| dictionary = mm_nr5g_registration_settings_get_dictionary (settings); |
| mm_gdbus_modem3gpp_set_nr5g_registration_settings (ctx->skeleton, dictionary); |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (task); |
| } |
| |
| static void |
| load_initial_eps_bearer_settings_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| InitializationContext *ctx; |
| g_autoptr(MMBearerProperties) config = NULL; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| config = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish (self, res, &error); |
| if (!config) |
| mm_obj_dbg (self, "couldn't load initial EPS bearer settings: %s", error->message); |
| else { |
| g_autoptr(GVariant) dictionary = NULL; |
| |
| dictionary = mm_bearer_properties_get_dictionary (config); |
| mm_gdbus_modem3gpp_set_initial_eps_bearer_settings (ctx->skeleton, dictionary); |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (task); |
| } |
| |
| static void |
| load_eps_ue_mode_operation_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| InitializationContext *ctx; |
| MMModem3gppEpsUeModeOperation uemode; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| uemode = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish (self, res, &error); |
| mm_gdbus_modem3gpp_set_eps_ue_mode_operation (ctx->skeleton, uemode); |
| |
| if (error) |
| mm_obj_dbg (self, "couldn't load UE mode of operation for EPS: %s", error->message); |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (task); |
| } |
| |
| static void |
| load_enabled_facility_locks_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| InitializationContext *ctx; |
| MMModem3gppFacility facilities; |
| g_autoptr(GError) error = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| facilities = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish (self, res, &error); |
| mm_gdbus_modem3gpp_set_enabled_facility_locks (ctx->skeleton, facilities); |
| |
| if (error) |
| mm_obj_dbg (self, "couldn't load facility locks: %s", error->message); |
| else { |
| g_autoptr(MMBaseSim) sim = NULL; |
| |
| /* We loaded the initial list of facility locks; but we do need to update |
| * the SIM PIN lock status when that changes. We'll connect to the signal |
| * which notifies about such update. There is no need to ref self as the |
| * SIM itself is an object which exists as long as self exists. */ |
| g_object_get (self, MM_IFACE_MODEM_SIM, &sim, NULL); |
| |
| if (sim) |
| g_signal_connect (sim, |
| MM_BASE_SIM_PIN_LOCK_ENABLED, |
| G_CALLBACK (sim_pin_lock_enabled_cb), |
| ctx->skeleton); |
| } |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (task); |
| } |
| |
| static void |
| load_imei_ready (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GTask *task) |
| { |
| InitializationContext *ctx; |
| g_autoptr(GError) error = NULL; |
| g_autofree gchar *imei = NULL; |
| |
| ctx = g_task_get_task_data (task); |
| |
| imei = MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish (self, res, &error); |
| mm_gdbus_modem3gpp_set_imei (ctx->skeleton, imei); |
| |
| if (error) |
| mm_obj_dbg (self, "couldn't load IMEI: %s", error->message); |
| |
| /* Go on to next step */ |
| ctx->step++; |
| interface_initialization_step (task); |
| } |
| |
| static void |
| interface_initialization_step (GTask *task) |
| { |
| MMIfaceModem3gpp *self; |
| InitializationContext *ctx; |
| MMModemState modem_state; |
| |
| /* Don't run new steps if we're cancelled */ |
| if (g_task_return_error_if_cancelled (task)) { |
| g_object_unref (task); |
| return; |
| } |
| |
| self = g_task_get_source_object (task); |
| ctx = g_task_get_task_data (task); |
| |
| switch (ctx->step) { |
| case INITIALIZATION_STEP_FIRST: |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_ENABLED_FACILITY_LOCKS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_enabled_facility_locks ( |
| self, |
| (GAsyncReadyCallback)load_enabled_facility_locks_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_IMEI: |
| /* IMEI value is meant to be loaded only once during the whole |
| * lifetime of the modem. Therefore, if we already have it loaded, |
| * don't try to load it again. */ |
| if (!mm_gdbus_modem3gpp_get_imei (ctx->skeleton) && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_imei ( |
| self, |
| (GAsyncReadyCallback)load_imei_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_TEST_LOCKED_OR_FAILED: |
| modem_state = MM_MODEM_STATE_UNKNOWN; |
| g_object_get (self, |
| MM_IFACE_MODEM_STATE, &modem_state, |
| NULL); |
| if (modem_state == MM_MODEM_STATE_LOCKED || |
| modem_state == MM_MODEM_STATE_FAILED) { |
| /* Skip some steps and export the interface if modem is locked or failed */ |
| ctx->step = INITIALIZATION_STEP_LAST; |
| interface_initialization_step (task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_EPS_UE_MODE_OPERATION: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_eps_ue_mode_operation ( |
| self, |
| (GAsyncReadyCallback)load_eps_ue_mode_operation_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_EPS_INITIAL_BEARER_SETTINGS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_initial_eps_bearer_settings ( |
| self, |
| (GAsyncReadyCallback)load_initial_eps_bearer_settings_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_NR5G_REGISTRATION_SETTINGS: |
| if (MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings && |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings_finish) { |
| MM_IFACE_MODEM_3GPP_GET_INTERFACE (self)->load_nr5g_registration_settings ( |
| self, |
| (GAsyncReadyCallback)load_nr5g_registration_settings_ready, |
| task); |
| return; |
| } |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_CONNECT_SIGNALS: |
| /* We are done without errors! */ |
| |
| /* Handle method invocations */ |
| g_signal_connect (ctx->skeleton, |
| "handle-register", |
| G_CALLBACK (handle_register), |
| self); |
| g_signal_connect (ctx->skeleton, |
| "handle-scan", |
| G_CALLBACK (handle_scan), |
| self); |
| g_signal_connect (ctx->skeleton, |
| "handle-set-eps-ue-mode-operation", |
| G_CALLBACK (handle_set_eps_ue_mode_operation), |
| self); |
| g_signal_connect (ctx->skeleton, |
| "handle-set-initial-eps-bearer-settings", |
| G_CALLBACK (handle_set_initial_eps_bearer_settings), |
| self); |
| g_signal_connect (ctx->skeleton, |
| "handle-set-packet-service-state", |
| G_CALLBACK (handle_set_packet_service_state), |
| self); |
| g_signal_connect (ctx->skeleton, |
| "handle-set-nr5g-registration-settings", |
| G_CALLBACK (handle_set_nr5g_registration_settings), |
| self); |
| |
| ctx->step++; |
| /* fall through */ |
| |
| case INITIALIZATION_STEP_LAST: |
| /* Always connect the signal to unlock modem */ |
| g_signal_connect (ctx->skeleton, |
| "handle-disable-facility-lock", |
| G_CALLBACK (handle_disable_facility_lock), |
| self); |
| |
| /* Finally, export the new interface */ |
| mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self), |
| MM_GDBUS_MODEM3GPP (ctx->skeleton)); |
| |
| g_task_return_boolean (task, TRUE); |
| g_object_unref (task); |
| return; |
| |
| default: |
| break; |
| } |
| |
| g_assert_not_reached (); |
| } |
| |
| gboolean |
| mm_iface_modem_3gpp_initialize_finish (MMIfaceModem3gpp *self, |
| GAsyncResult *res, |
| GError **error) |
| { |
| return g_task_propagate_boolean (G_TASK (res), error); |
| } |
| |
| void |
| mm_iface_modem_3gpp_initialize (MMIfaceModem3gpp *self, |
| GCancellable *cancellable, |
| GAsyncReadyCallback callback, |
| gpointer user_data) |
| { |
| MmGdbusModem3gpp *skeleton = NULL; |
| InitializationContext *ctx; |
| GTask *task; |
| |
| /* Did we already create it? */ |
| g_object_get (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, |
| NULL); |
| if (!skeleton) { |
| skeleton = mm_gdbus_modem3gpp_skeleton_new (); |
| |
| /* Set all initial property defaults */ |
| mm_gdbus_modem3gpp_set_imei (skeleton, NULL); |
| mm_gdbus_modem3gpp_set_operator_code (skeleton, NULL); |
| mm_gdbus_modem3gpp_set_operator_name (skeleton, NULL); |
| mm_gdbus_modem3gpp_set_enabled_facility_locks (skeleton, MM_MODEM_3GPP_FACILITY_NONE); |
| mm_gdbus_modem3gpp_set_subscription_state (skeleton, MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN); |
| mm_gdbus_modem3gpp_set_pco (skeleton, NULL); |
| mm_gdbus_modem3gpp_set_initial_eps_bearer (skeleton, NULL); |
| |
| /* Bind our RegistrationState property */ |
| g_object_bind_property (self, MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, |
| skeleton, "registration-state", |
| G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); |
| |
| /* Bind our packet service state property */ |
| g_object_bind_property (self, MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, |
| skeleton, "packet-service-state", |
| G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); |
| |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, skeleton, |
| NULL); |
| } |
| |
| ctx = g_slice_new0 (InitializationContext); |
| ctx->step = INITIALIZATION_STEP_FIRST; |
| ctx->skeleton = skeleton; |
| |
| task = g_task_new (self, cancellable, callback, user_data); |
| g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free); |
| |
| /* Perform async initialization here */ |
| interface_initialization_step (task); |
| } |
| |
| void |
| mm_iface_modem_3gpp_shutdown (MMIfaceModem3gpp *self) |
| { |
| /* Unexport DBus interface and remove the skeleton */ |
| mm_gdbus_object_skeleton_set_modem3gpp (MM_GDBUS_OBJECT_SKELETON (self), NULL); |
| g_object_set (self, |
| MM_IFACE_MODEM_3GPP_DBUS_SKELETON, NULL, |
| NULL); |
| } |
| |
| /*****************************************************************************/ |
| |
| static void |
| iface_modem_3gpp_init (gpointer g_iface) |
| { |
| static gboolean initialized = FALSE; |
| |
| if (initialized) |
| return; |
| |
| /* Properties */ |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_object (MM_IFACE_MODEM_3GPP_DBUS_SKELETON, |
| "3GPP DBus skeleton", |
| "DBus skeleton for the 3GPP interface", |
| MM_GDBUS_TYPE_MODEM3GPP_SKELETON, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_enum (MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, |
| "RegistrationState", |
| "Registration state of the modem", |
| MM_TYPE_MODEM_3GPP_REGISTRATION_STATE, |
| MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_boolean (MM_IFACE_MODEM_3GPP_CS_NETWORK_SUPPORTED, |
| "CS network supported", |
| "Whether the modem works in the CS network", |
| TRUE, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_boolean (MM_IFACE_MODEM_3GPP_PS_NETWORK_SUPPORTED, |
| "PS network supported", |
| "Whether the modem works in the PS network", |
| TRUE, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_boolean (MM_IFACE_MODEM_3GPP_EPS_NETWORK_SUPPORTED, |
| "EPS network supported", |
| "Whether the modem works in the EPS network", |
| FALSE, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_boolean (MM_IFACE_MODEM_3GPP_5GS_NETWORK_SUPPORTED, |
| "5GS network supported", |
| "Whether the modem works in the 5GS network", |
| FALSE, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_flags (MM_IFACE_MODEM_3GPP_IGNORED_FACILITY_LOCKS, |
| "Ignored locks", |
| "Ignored facility locks", |
| MM_TYPE_MODEM_3GPP_FACILITY, |
| MM_MODEM_3GPP_FACILITY_NONE, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_object (MM_IFACE_MODEM_3GPP_INITIAL_EPS_BEARER, |
| "Initial EPS bearer", |
| "Initial EPS bearer setup during registration", |
| MM_TYPE_BASE_BEARER, |
| G_PARAM_READWRITE)); |
| |
| g_object_interface_install_property |
| (g_iface, |
| g_param_spec_enum (MM_IFACE_MODEM_3GPP_PACKET_SERVICE_STATE, |
| "PacketServiceState", |
| "Packet service state of the modem", |
| MM_TYPE_MODEM_3GPP_PACKET_SERVICE_STATE, |
| MM_MODEM_3GPP_PACKET_SERVICE_STATE_UNKNOWN, |
| G_PARAM_READWRITE)); |
| |
| initialized = TRUE; |
| } |
| |
| GType |
| mm_iface_modem_3gpp_get_type (void) |
| { |
| static GType iface_modem_3gpp_type = 0; |
| |
| if (!G_UNLIKELY (iface_modem_3gpp_type)) { |
| static const GTypeInfo info = { |
| sizeof (MMIfaceModem3gpp), /* class_size */ |
| iface_modem_3gpp_init, /* base_init */ |
| NULL, /* base_finalize */ |
| }; |
| |
| iface_modem_3gpp_type = g_type_register_static (G_TYPE_INTERFACE, |
| "MMIfaceModem3gpp", |
| &info, |
| 0); |
| |
| g_type_interface_add_prerequisite (iface_modem_3gpp_type, MM_TYPE_IFACE_MODEM); |
| } |
| |
| return iface_modem_3gpp_type; |
| } |