aboutsummaryrefslogtreecommitdiff
path: root/src/mm-iface-modem.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-iface-modem.c')
-rw-r--r--src/mm-iface-modem.c374
1 files changed, 266 insertions, 108 deletions
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index f0f10ce..5edbbae 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -80,12 +80,12 @@ mm_iface_modem_bind_simple_status (MMIfaceModem *self,
/*****************************************************************************/
/* Helper method to wait for a final state */
-#define MODEM_STATE_IS_INTERMEDIATE(state) \
- (state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING || \
- state == MM_MODEM_STATE_INITIALIZING)
+#define MODEM_STATE_IS_INTERMEDIATE(state) \
+ (state == MM_MODEM_STATE_INITIALIZING || \
+ state == MM_MODEM_STATE_DISABLING || \
+ state == MM_MODEM_STATE_ENABLING || \
+ state == MM_MODEM_STATE_DISCONNECTING || \
+ state == MM_MODEM_STATE_CONNECTING)
typedef struct {
MMIfaceModem *self;
@@ -98,9 +98,14 @@ typedef struct {
static void
wait_for_final_state_context_complete_and_free (WaitForFinalStateContext *ctx)
{
+ /* The callback associated with 'ctx->result' may update the modem state.
+ * Disconnect the signal handler for modem state changes before completing
+ * 'ctx->result' in order to prevent state_changed from being invoked, which
+ * invokes wait_for_final_state_context_complete_and_free (ctx) again. */
+ g_signal_handler_disconnect (ctx->self, ctx->state_changed_id);
+
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->result);
- g_signal_handler_disconnect (ctx->self, ctx->state_changed_id);
g_source_remove (ctx->state_changed_wait_id);
g_object_unref (ctx->self);
g_slice_free (WaitForFinalStateContext, ctx);
@@ -206,6 +211,169 @@ mm_iface_modem_wait_for_final_state (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Helper method to load unlock required, considering retries */
+
+#define MAX_RETRIES 6
+
+typedef struct {
+ MMIfaceModem *self;
+ GSimpleAsyncResult *result;
+ guint retries;
+ guint pin_check_timeout_id;
+} InternalLoadUnlockRequiredContext;
+
+static void
+internal_load_unlock_required_context_complete_and_free (InternalLoadUnlockRequiredContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->self);
+ g_slice_free (InternalLoadUnlockRequiredContext, ctx);
+}
+
+static MMModemLock
+internal_load_unlock_required_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return MM_MODEM_LOCK_UNKNOWN;
+
+ return (MMModemLock) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+}
+
+static void internal_load_unlock_required_context_step (InternalLoadUnlockRequiredContext *ctx);
+
+static gboolean
+load_unlock_required_again (InternalLoadUnlockRequiredContext *ctx)
+{
+ ctx->pin_check_timeout_id = 0;
+ /* Retry the step */
+ internal_load_unlock_required_context_step (ctx);
+ return FALSE;
+}
+
+static void
+load_unlock_required_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ InternalLoadUnlockRequiredContext *ctx)
+{
+ GError *error = NULL;
+ MMModemLock lock;
+
+ lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ if (error) {
+ mm_dbg ("Couldn't check if unlock required: '%s'", error->message);
+
+ /* For several kinds of errors, just return them directly */
+ if (error->domain == MM_SERIAL_ERROR ||
+ g_error_matches (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* For the remaining ones, retry if possible */
+ if (ctx->retries < MAX_RETRIES) {
+ ctx->retries++;
+ mm_dbg ("Retrying (%u) unlock required check", ctx->retries);
+
+ g_assert (ctx->pin_check_timeout_id == 0);
+ ctx->pin_check_timeout_id = g_timeout_add_seconds (2,
+ (GSourceFunc)load_unlock_required_again,
+ ctx);
+ g_error_free (error);
+ return;
+ }
+
+ /* If reached max retries and still reporting error... default to SIM error */
+ g_error_free (error);
+ g_simple_async_result_set_error (ctx->result,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE,
+ "Couldn't get SIM lock status after %u retries",
+ MAX_RETRIES);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Got the lock value, return it */
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ GUINT_TO_POINTER (lock),
+ NULL);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+}
+
+static void
+internal_load_unlock_required_context_step (InternalLoadUnlockRequiredContext *ctx)
+{
+ g_assert (ctx->pin_check_timeout_id == 0);
+ MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
+ ctx->self,
+ (GAsyncReadyCallback)load_unlock_required_ready,
+ ctx);
+}
+
+static void
+internal_load_unlock_required (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ InternalLoadUnlockRequiredContext *ctx;
+
+ ctx = g_slice_new0 (InternalLoadUnlockRequiredContext);
+ ctx->self = g_object_ref (self);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ internal_load_unlock_required);
+
+ if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required ||
+ !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
+ /* Just assume that no lock is required */
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ internal_load_unlock_required_context_complete_and_free (ctx);
+ return;
+ }
+
+ internal_load_unlock_required_context_step (ctx);
+}
+
+/*****************************************************************************/
+
+static void
+bearer_list_updated (MMBearerList *bearer_list,
+ GParamSpec *pspec,
+ MMIfaceModem *self)
+{
+ MmGdbusModem *skeleton;
+ gchar **paths;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (!skeleton)
+ return;
+
+ paths = mm_bearer_list_get_paths (bearer_list);
+ mm_gdbus_modem_set_bearers (skeleton, (const gchar *const *)paths);
+ g_strfreev (paths);
+
+ g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
+}
+
+/*****************************************************************************/
static MMModemState get_current_consolidated_state (MMIfaceModem *self, MMModemState modem_state);
@@ -1331,7 +1499,7 @@ get_updated_consolidated_state (MMIfaceModem *self,
/* Reported subsystem states will be REGISTRATION-related. This means
* that we would only expect a subset of the states being reported for
- * the subystem. Warn if we get others */
+ * the subsystem. Warn if we get others */
g_warn_if_fail (subsystem_state == MM_MODEM_STATE_ENABLED ||
subsystem_state == MM_MODEM_STATE_SEARCHING ||
subsystem_state == MM_MODEM_STATE_REGISTERED);
@@ -1626,7 +1794,6 @@ handle_reset_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleResetContext *ctx)
{
- MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
@@ -1646,20 +1813,6 @@ handle_reset_auth_ready (MMBaseModem *self,
return;
}
- modem_state = MM_MODEM_STATE_UNKNOWN;
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &modem_state,
- NULL);
-
- if (modem_state < MM_MODEM_STATE_DISABLED) {
- g_dbus_method_invocation_return_error (ctx->invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot reset modem: not initialized/unlocked yet");
- handle_reset_context_free (ctx);
- return;
- }
-
MM_IFACE_MODEM_GET_INTERFACE (self)->reset (MM_IFACE_MODEM (self),
(GAsyncReadyCallback)handle_reset_ready,
ctx);
@@ -1725,7 +1878,6 @@ handle_factory_reset_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleFactoryResetContext *ctx)
{
- MMModemState modem_state;
GError *error = NULL;
if (!mm_base_modem_authorize_finish (self, res, &error)) {
@@ -1746,21 +1898,6 @@ handle_factory_reset_auth_ready (MMBaseModem *self,
return;
}
- modem_state = MM_MODEM_STATE_UNKNOWN;
- g_object_get (self,
- MM_IFACE_MODEM_STATE, &modem_state,
- NULL);
-
- if (modem_state < MM_MODEM_STATE_DISABLED) {
- g_dbus_method_invocation_return_error (ctx->invocation,
- MM_CORE_ERROR,
- MM_CORE_ERROR_WRONG_STATE,
- "Cannot reset the modem to factory defaults: "
- "not initialized/unlocked yet");
- handle_factory_reset_context_free (ctx);
- return;
- }
-
MM_IFACE_MODEM_GET_INTERFACE (self)->factory_reset (MM_IFACE_MODEM (self),
ctx->code,
(GAsyncReadyCallback)handle_factory_reset_ready,
@@ -2398,7 +2535,7 @@ mm_iface_modem_set_current_modes (MMIfaceModem *self,
supported = mm_common_mode_combinations_variant_to_garray (
mm_gdbus_modem_get_supported_modes (ctx->skeleton));
- /* Don't allow mode switchin if only one item given in the supported list */
+ /* Don't allow mode switching if only one item given in the supported list */
if (supported->len == 1) {
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
@@ -2701,8 +2838,6 @@ typedef enum {
typedef struct {
MMIfaceModem *self;
UpdateLockInfoContextStep step;
- guint pin_check_tries;
- guint pin_check_timeout_id;
GSimpleAsyncResult *result;
MmGdbusModem *skeleton;
MMModemLock lock;
@@ -2775,23 +2910,14 @@ modem_after_sim_unlock_ready (MMIfaceModem *self,
update_lock_info_context_step (ctx);
}
-static gboolean
-load_unlock_required_again (UpdateLockInfoContext *ctx)
-{
- ctx->pin_check_timeout_id = 0;
- /* Retry the step */
- update_lock_info_context_step (ctx);
- return FALSE;
-}
-
static void
-load_unlock_required_ready (MMIfaceModem *self,
- GAsyncResult *res,
- UpdateLockInfoContext *ctx)
+internal_load_unlock_required_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ UpdateLockInfoContext *ctx)
{
GError *error = NULL;
- ctx->lock = MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ ctx->lock = internal_load_unlock_required_finish (self, res, &error);
if (error) {
/* Treat several SIM related, serial and other core errors as critical
* and abort the checks. These will end up moving the modem to a FAILED
@@ -2804,44 +2930,32 @@ load_unlock_required_ready (MMIfaceModem *self,
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
update_lock_info_context_step (ctx);
return;
- } else if (g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
- g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
- g_error_matches (error,
- MM_MOBILE_EQUIPMENT_ERROR,
- MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
- if (mm_iface_modem_is_cdma (self)) {
- /* For mixed 3GPP+3GPP2 devices, skip SIM errors */
- mm_dbg ("Skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
- g_error_free (error);
- ctx->lock = MM_MODEM_LOCK_NONE;
- } else {
- /* SIM errors are only critical in 3GPP-only devices */
+ }
+
+ if (g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_NOT_INSERTED) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_FAILURE) ||
+ g_error_matches (error,
+ MM_MOBILE_EQUIPMENT_ERROR,
+ MM_MOBILE_EQUIPMENT_ERROR_SIM_WRONG)) {
+ /* SIM errors are only critical in 3GPP-only devices */
+ if (!mm_iface_modem_is_cdma (self)) {
ctx->saved_error = error;
ctx->step = UPDATE_LOCK_INFO_CONTEXT_STEP_LAST;
update_lock_info_context_step (ctx);
return;
}
+
+ /* For mixed 3GPP+3GPP2 devices, skip SIM errors */
+ mm_dbg ("Skipping SIM error in 3GPP2-capable device, assuming no lock is needed");
+ g_error_free (error);
+ ctx->lock = MM_MODEM_LOCK_NONE;
} else {
mm_dbg ("Couldn't check if unlock required: '%s'", error->message);
g_error_free (error);
-
- /* Retry up to 6 times */
- if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE &&
- ++ctx->pin_check_tries < 6) {
- mm_dbg ("Retrying (%u) unlock required check", ctx->pin_check_tries);
- if (ctx->pin_check_timeout_id)
- g_source_remove (ctx->pin_check_timeout_id);
- ctx->pin_check_timeout_id = g_timeout_add_seconds (2,
- (GSourceFunc)load_unlock_required_again,
- ctx);
- return;
- }
-
- /* If reached max retries and still reporting error, set UNKNOWN */
ctx->lock = MM_MODEM_LOCK_UNKNOWN;
}
}
@@ -2873,12 +2987,10 @@ update_lock_info_context_step (UpdateLockInfoContext *ctx)
/* Don't re-ask if already known */
if (ctx->lock == MM_MODEM_LOCK_UNKNOWN) {
/* If we're already unlocked, we're done */
- if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
+ if (mm_gdbus_modem_get_unlock_required (ctx->skeleton) != MM_MODEM_LOCK_NONE) {
+ internal_load_unlock_required (
ctx->self,
- (GAsyncReadyCallback)load_unlock_required_ready,
+ (GAsyncReadyCallback)internal_load_unlock_required_ready,
ctx);
return;
}
@@ -3561,6 +3673,12 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct
if (!g_cancellable_is_cancelled (ctx->cancellable))
return FALSE;
+ /* Simply ignore any fatal error encountered as the initialization is cancelled anyway. */
+ if (ctx->fatal_error) {
+ g_error_free (ctx->fatal_error);
+ ctx->fatal_error = NULL;
+ }
+
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
@@ -3617,13 +3735,13 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct
}
static void
-current_capabilities_load_unlock_required_ready (MMIfaceModem *self,
- GAsyncResult *res,
- InitializationContext *ctx)
+current_capabilities_internal_load_unlock_required_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ InitializationContext *ctx)
{
GError *error = NULL;
- MM_IFACE_MODEM_GET_INTERFACE (self)->load_unlock_required_finish (self, res, &error);
+ internal_load_unlock_required_finish (self, res, &error);
if (error) {
/* These SIM errors indicate that there is NO valid SIM available. So,
* remove all 3GPP caps from the current capabilities */
@@ -3682,13 +3800,11 @@ load_current_capabilities_ready (MMIfaceModem *self,
/* If the device is a multimode device (3GPP+3GPP2) check whether we have a
* SIM or not. */
if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO &&
- (caps & MM_MODEM_CAPABILITY_GSM_UMTS || caps & MM_MODEM_CAPABILITY_LTE) &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required &&
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required_finish) {
+ (caps & MM_MODEM_CAPABILITY_GSM_UMTS || caps & MM_MODEM_CAPABILITY_LTE)) {
mm_dbg ("Checking if multimode device has a SIM...");
- MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_unlock_required (
+ internal_load_unlock_required (
ctx->self,
- (GAsyncReadyCallback)current_capabilities_load_unlock_required_ready,
+ (GAsyncReadyCallback)current_capabilities_internal_load_unlock_required_ready,
ctx);
return;
}
@@ -3877,6 +3993,21 @@ sim_reinit_ready (MMSim *sim,
interface_initialization_step (ctx);
}
+void
+mm_iface_modem_update_own_numbers (MMIfaceModem *self,
+ const GStrv own_numbers)
+{
+ MmGdbusModem *skeleton = NULL;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+ if (skeleton) {
+ mm_gdbus_modem_set_own_numbers (skeleton, (const gchar * const *)own_numbers);
+ g_object_unref (skeleton);
+ }
+}
+
static void
load_own_numbers_ready (MMIfaceModem *self,
GAsyncResult *res,
@@ -4106,6 +4237,10 @@ interface_initialization_step (InitializationContext *ctx)
/* Create new default list */
list = mm_bearer_list_new (n, n);
+ g_signal_connect (list,
+ "notify::" MM_BEARER_LIST_NUM_BEARERS,
+ G_CALLBACK (bearer_list_updated),
+ ctx->self);
g_object_set (ctx->self,
MM_IFACE_MODEM_BEARER_LIST, list,
NULL);
@@ -4335,7 +4470,7 @@ interface_initialization_step (InitializationContext *ctx)
* This will try to load any missing property value that couldn't be
* retrieved before due to having the SIM locked. */
mm_sim_initialize (sim,
- NULL, /* TODO: cancellable */
+ ctx->cancellable,
(GAsyncReadyCallback)sim_reinit_ready,
ctx);
g_object_unref (sim);
@@ -4441,6 +4576,17 @@ interface_initialization_step (InitializationContext *ctx)
"handle-set-current-capabilities",
G_CALLBACK (handle_set_current_capabilities),
ctx->self);
+ /* Allow the reset and factory reset operation in FAILED state to rescue the modem.
+ * Also, for a modem that doesn't support SIM hot swapping, a reset is needed to
+ * force the modem to detect the newly inserted SIM. */
+ g_signal_connect (ctx->skeleton,
+ "handle-reset",
+ G_CALLBACK (handle_reset),
+ ctx->self);
+ g_signal_connect (ctx->skeleton,
+ "handle-factory-reset",
+ G_CALLBACK (handle_factory_reset),
+ ctx->self);
if (ctx->fatal_error) {
g_simple_async_result_take_error (ctx->result, ctx->fatal_error);
@@ -4473,14 +4619,6 @@ interface_initialization_step (InitializationContext *ctx)
G_CALLBACK (handle_set_power_state),
ctx->self);
g_signal_connect (ctx->skeleton,
- "handle-reset",
- G_CALLBACK (handle_reset),
- ctx->self);
- g_signal_connect (ctx->skeleton,
- "handle-factory-reset",
- G_CALLBACK (handle_factory_reset),
- ctx->self);
- g_signal_connect (ctx->skeleton,
"handle-set-current-bands",
G_CALLBACK (handle_set_current_bands),
ctx->self);
@@ -4806,6 +4944,26 @@ mm_iface_modem_is_cdma_only (MMIfaceModem *self)
/*****************************************************************************/
+const gchar *
+mm_iface_modem_get_model (MMIfaceModem *self)
+{
+ const gchar *model = NULL;
+ MmGdbusModem *skeleton;
+
+ g_object_get (self,
+ MM_IFACE_MODEM_DBUS_SKELETON, &skeleton,
+ NULL);
+
+ if (skeleton) {
+ model = mm_gdbus_modem_get_model (skeleton);
+ g_object_unref (skeleton);
+ }
+
+ return model;
+}
+
+/*****************************************************************************/
+
static void
iface_modem_init (gpointer g_iface)
{