diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2011-11-29 14:23:20 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-03-15 14:14:30 +0100 |
commit | 48a9da117b50f8586b3838f69d27012c69c59939 (patch) | |
tree | cb77773c6208478d1aa64586a8efdf049d8117b4 | |
parent | b3389ff2d437e7ebba541af53b32ef4f779872ac (diff) |
broadband-modem: implement CS/PS registration checks
Running a CS/PS registration check may not return any direct result (this is, no
MMModem3gppRegistrationStatus returned), as usually replies to +CREG? and
+CGREG? are parsed as unsolicited messages.
-rw-r--r-- | src/mm-broadband-modem.c | 225 |
1 files changed, 222 insertions, 3 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 3e07146d..e86afcaf 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -63,6 +63,11 @@ struct _MMBroadbandModemPrivate { MMModemState modem_state; MMModemCapability modem_current_capabilities; MMModem3gppRegistrationState modem_3gpp_registration_state; + + /* 3GPP registration helpers */ + GPtrArray *reg_regex; + MMModem3gppRegistrationState reg_cs; + MMModem3gppRegistrationState reg_ps; }; static gboolean @@ -1140,6 +1145,31 @@ load_imei (MMIfaceModem3gpp *self, /*****************************************************************************/ /* Unsolicited registration messages handling (3GPP) */ +static MMModem3gppRegistrationState +get_consolidated_reg_state (MMBroadbandModem *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 (self->priv->reg_cs == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || + self->priv->reg_cs == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) + return self->priv->reg_cs; + + if (self->priv->reg_ps == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || + self->priv->reg_ps == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) + return self->priv->reg_ps; + + if (self->priv->reg_cs == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING) + return self->priv->reg_cs; + + if (self->priv->reg_ps == MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING) + return self->priv->reg_ps; + + return self->priv->reg_cs; +} + static gboolean setup_unsolicited_registration_finish (MMIfaceModem3gpp *self, GAsyncResult *res, @@ -1155,7 +1185,7 @@ reg_state_changed (MMAtSerialPort *port, { MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; gulong lac = 0, cell_id = 0; - gint act = -1; + MMModemAccessTech act = MM_MODEM_ACCESS_TECH_UNKNOWN; gboolean cgreg = FALSE; GError *error = NULL; @@ -1172,8 +1202,14 @@ reg_state_changed (MMAtSerialPort *port, return; } + if (cgreg) + self->priv->reg_ps = state; + else + self->priv->reg_cs = state; + /* Report new registration state */ - mm_iface_modem_3gpp_update_registration_state (MM_IFACE_MODEM_3GPP (self), state); + mm_iface_modem_3gpp_update_registration_state (MM_IFACE_MODEM_3GPP (self), + get_consolidated_reg_state (self)); /* /\* If registration is finished (either registered or failed) but the */ /* * registration query hasn't completed yet, just remove the timeout and */ @@ -1237,6 +1273,170 @@ setup_unsolicited_registration (MMIfaceModem3gpp *self, } /*****************************************************************************/ +/* CS and PS Registration checks (3GPP) */ + +static gboolean +run_cs_registration_check_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static gboolean +run_ps_registration_check_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +registration_status_check_ready (MMBroadbandModem *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GVariant *response_variant; + const gchar *response = NULL; + GError *error = NULL; + + response_variant = mm_at_command_finish (G_OBJECT (self), res, &error); + if (response_variant) + response = g_variant_get_string (response_variant, NULL); + + if (!response) { + g_assert (error != NULL); + g_simple_async_result_take_error (operation_result, error); + } + /* Unsolicited registration status handlers will usually process the + * response for us, but just in case they don't, do that here. + */ + else if (!response[0]) + /* Done */ + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + else { + GMatchInfo *match_info; + guint i; + + /* Try to match the response */ + for (i = 0; i < self->priv->reg_regex->len; i++) { + if (g_regex_match ((GRegex *)g_ptr_array_index (self->priv->reg_regex, i), + response, + 0, + &match_info)) + break; + g_match_info_free (match_info); + match_info = NULL; + } + + if (!match_info) { + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Unknown registration status response: '%s'", + response); + } else { + GError *inner_error = NULL; + gboolean parsed; + gboolean cgreg = FALSE; + MMModem3gppRegistrationState state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + MMModemAccessTech act = MM_MODEM_ACCESS_TECH_UNKNOWN; + gulong lac = 0; + gulong cid = 0; + + parsed = mm_gsm_parse_creg_response (match_info, + &state, + &lac, + &cid, + &act, + &cgreg, + &inner_error); + g_match_info_free (match_info); + + if (!parsed) { + if (inner_error) + g_simple_async_result_take_error (operation_result, inner_error); + else + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Error parsing registration response: '%s'", + response); + } else { + if (cgreg) + self->priv->reg_ps = state; + else + self->priv->reg_cs = state; + + /* Report new registration state */ + mm_iface_modem_3gpp_update_registration_state (MM_IFACE_MODEM_3GPP (self), + get_consolidated_reg_state (self)); + + /* TODO: report LAC/CI location */ + /* TODO: report access technology, if available */ + + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + } + } + } + + if (response_variant) + g_variant_unref (response_variant); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +run_cs_registration_check (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + run_cs_registration_check); + + /* Check current CS-registration state. */ + mm_at_command (G_OBJECT (self), + mm_base_modem_get_port_primary (MM_BASE_MODEM (self)), + "+CREG?", + 10, + (MMAtResponseProcessor)common_parse_string_reply, + NULL, /* response processor context */ + "s", /* raw reply */ + NULL, /* cancellable */ + (GAsyncReadyCallback)registration_status_check_ready, + result); +} + +static void +run_ps_registration_check (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + run_ps_registration_check); + + /* Check current PS-registration state. */ + mm_at_command (G_OBJECT (self), + mm_base_modem_get_port_primary (MM_BASE_MODEM (self)), + "+CGREG?", + 10, + (MMAtResponseProcessor)common_parse_string_reply, + NULL, /* response processor context */ + "s", /* raw reply */ + NULL, /* cancellable */ + (GAsyncReadyCallback)registration_status_check_ready, + result); +} + +/*****************************************************************************/ /* CS and PS Registrations (3GPP) */ static gboolean @@ -1480,6 +1680,7 @@ enable_finish (MMBaseModem *self, &error)) { \ g_simple_async_result_take_error (G_SIMPLE_ASYNC_RESULT (ctx->result), error); \ enabling_context_complete_and_free (ctx); \ + return; \ } \ \ /* Go on to next step */ \ @@ -1515,7 +1716,6 @@ enabling_step (EnablingContext *ctx) ctx); return; } - /* Fall down to next step */ ctx->step++; @@ -1852,6 +2052,20 @@ mm_broadband_modem_init (MMBroadbandModem *self) self->priv->modem_state = MM_MODEM_STATE_UNKNOWN; self->priv->modem_current_capabilities = MM_MODEM_CAPABILITY_NONE; self->priv->modem_3gpp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + self->priv->reg_regex = mm_gsm_creg_regex_get (TRUE); + self->priv->reg_cs = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + self->priv->reg_ps = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; +} + +static void +finalize (GObject *object) +{ + MMBroadbandModem *self = MM_BROADBAND_MODEM (object); + + if (self->priv->reg_regex) + mm_gsm_creg_regex_destroy (self->priv->reg_regex); + + G_OBJECT_CLASS (mm_broadband_modem_parent_class)->finalize (object); } static void @@ -1911,8 +2125,12 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) iface->setup_unsolicited_registration_finish = setup_unsolicited_registration_finish; iface->setup_cs_registration = setup_cs_registration; iface->setup_cs_registration_finish = setup_cs_registration_finish; + iface->run_cs_registration_check = run_cs_registration_check; + iface->run_cs_registration_check_finish = run_cs_registration_check_finish; iface->setup_ps_registration = setup_ps_registration; iface->setup_ps_registration_finish = setup_ps_registration_finish; + iface->run_ps_registration_check = run_ps_registration_check; + iface->run_ps_registration_check_finish = run_ps_registration_check_finish; } static void @@ -1927,6 +2145,7 @@ mm_broadband_modem_class_init (MMBroadbandModemClass *klass) object_class->set_property = set_property; object_class->get_property = get_property; object_class->dispose = dispose; + object_class->finalize = finalize; base_modem_class->initialize = initialize; base_modem_class->initialize_finish = initialize_finish; |