diff options
author | Thieu Le <thieule@chromium.org> | 2014-01-15 13:06:39 -0800 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2014-01-18 14:08:31 +0100 |
commit | 87f0f808bf9282e3de3bfcf7486049ff9b6f6aa8 (patch) | |
tree | 913f89141e865faba4ade73228ad18bfc9446135 | |
parent | afabde0f2c710302024d4fd40c8d719d4196ded6 (diff) |
altair-lte: set subscription state using PCO
This patch sets the subscription state using Verizon's PCO values.
-rw-r--r-- | plugins/altair/mm-broadband-modem-altair-lte.c | 229 | ||||
-rw-r--r-- | plugins/altair/mm-modem-helpers-altair-lte.c | 155 | ||||
-rw-r--r-- | plugins/altair/mm-modem-helpers-altair-lte.h | 8 | ||||
-rw-r--r-- | plugins/altair/tests/test-modem-helpers-altair-lte.c | 38 | ||||
-rw-r--r-- | src/mm-iface-modem-3gpp.c | 1 |
5 files changed, 415 insertions, 16 deletions
diff --git a/plugins/altair/mm-broadband-modem-altair-lte.c b/plugins/altair/mm-broadband-modem-altair-lte.c index 9d3373cd..7c52002d 100644 --- a/plugins/altair/mm-broadband-modem-altair-lte.c +++ b/plugins/altair/mm-broadband-modem-altair-lte.c @@ -61,6 +61,8 @@ struct _MMBroadbandModemAltairLtePrivate { guint sim_refresh_timer_id; /* Regex for bearer related notifications */ GRegex *statcm_regex; + /* Regex for PCO notifications */ + GRegex *pcoinfo_regex; }; static MMIfaceModem3gpp *iface_modem_3gpp_parent; @@ -540,7 +542,6 @@ run_registration_checks_ready (MMIfaceModem3gpp *self, { GError *error = NULL; gboolean success; - MMModem3gppRegistrationState registration_state; g_assert (iface_modem_3gpp_parent->run_registration_checks_finish); success = iface_modem_3gpp_parent->run_registration_checks_finish (self, res, &error); @@ -552,21 +553,7 @@ run_registration_checks_ready (MMIfaceModem3gpp *self, return; } - g_object_get (self, - MM_IFACE_MODEM_3GPP_REGISTRATION_STATE, ®istration_state, - NULL); - - if (registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_HOME || - registration_state == MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) { - mm_dbg ("Registration succeeded: Marking the SIM as provisioned."); - mm_iface_modem_3gpp_update_subscription_state (self, MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED); - g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - return; - } - - mm_dbg ("Registration not successful yet. Checking if SIM is unprovisioned."); + mm_dbg ("Checking if SIM is unprovisioned (ignoring registration state)."); mm_base_modem_at_command (MM_BASE_MODEM (self), "+CEER", 6, @@ -789,6 +776,11 @@ altair_statcm_changed (MMAtSerialPort *port, /* Setup/Cleanup unsolicited events (3GPP interface) */ static void +altair_pco_info_changed (MMAtSerialPort *port, + GMatchInfo *match_info, + MMBroadbandModemAltairLte *self); + +static void set_3gpp_unsolicited_events_handlers (MMBroadbandModemAltairLte *self, gboolean enable) { @@ -818,6 +810,14 @@ set_3gpp_unsolicited_events_handlers (MMBroadbandModemAltairLte *self, enable ? (MMAtSerialUnsolicitedMsgFn)altair_statcm_changed : NULL, enable ? self : NULL, NULL); + + /* PCO info handler */ + mm_at_serial_port_add_unsolicited_msg_handler ( + ports[i], + self->priv->pcoinfo_regex, + enable ? (MMAtSerialUnsolicitedMsgFn)altair_pco_info_changed : NULL, + enable ? self : NULL, + NULL); } } @@ -929,6 +929,7 @@ response_processor_no_result_stop_on_error (MMBaseModem *self, static const MMBaseModemAtCommand unsolicited_events_enable_sequence[] = { { "%STATCM=1", 10, FALSE, response_processor_no_result_stop_on_error }, { "%NOTIFYEV=\"SIMREFRESH\",1", 10, FALSE, NULL }, + { "%PCOINFO=1", 10, FALSE, NULL }, { NULL } }; @@ -1005,6 +1006,7 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self, static const MMBaseModemAtCommand unsolicited_events_disable_sequence[] = { { "%STATCM=0", 10, FALSE, NULL }, { "%NOTIFYEV=\"SIMREFRESH\",0", 10, FALSE, NULL }, + { "%PCOINFO=0", 10, FALSE, NULL }, { NULL } }; @@ -1164,6 +1166,196 @@ modem_3gpp_load_operator_name (MMIfaceModem3gpp *self, } /*****************************************************************************/ +/* Subscription State loading (3GPP interface) */ + +typedef struct { + MMIfaceModem3gpp *self; + GSimpleAsyncResult *result; + gchar *pco_info; +} LoadSubscriptionStateContext; + +static void +load_subscription_state_context_complete_and_free (LoadSubscriptionStateContext *ctx) +{ + g_simple_async_result_complete (ctx->result); + g_free (ctx->pco_info); + g_object_unref (ctx->result); + g_object_unref (ctx->self); + g_slice_free (LoadSubscriptionStateContext, ctx); +} + +static MMModem3gppSubscriptionState +altair_vzw_pco_value_to_mm_modem_3gpp_subscription_state (guint pco_value) +{ + switch (pco_value) { + case 0: + return MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED; + case 3: + return MM_MODEM_3GPP_SUBSCRIPTION_STATE_OUT_OF_DATA; + case 5: + return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNPROVISIONED; + default: + return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN; + } +} + +static MMModem3gppSubscriptionState +modem_3gpp_load_subscription_state_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN; + + return GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +altair_load_internet_cid_ready (MMIfaceModem3gpp *self, + GAsyncResult *res, + LoadSubscriptionStateContext *ctx) +{ + const gchar *response; + GError *error = NULL; + guint cid; + guint pco_value = -1; + MMModem3gppSubscriptionState subscription_state; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (error) { + mm_dbg ("Failed to load internet CID."); + g_simple_async_result_take_error (ctx->result, error); + load_subscription_state_context_complete_and_free (ctx); + return; + } + + cid = altair_parse_cid (response, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + load_subscription_state_context_complete_and_free (ctx); + return; + } + + mm_dbg ("Parsing vendor PCO info: %s", ctx->pco_info); + pco_value = altair_parse_vendor_pco_info (ctx->pco_info, cid, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + load_subscription_state_context_complete_and_free (ctx); + return; + } + mm_dbg ("PCO value = %d", pco_value); + + subscription_state = altair_vzw_pco_value_to_mm_modem_3gpp_subscription_state (pco_value); + if (subscription_state == MM_MODEM_3GPP_SUBSCRIPTION_STATE_UNKNOWN) { + /* The PCO value is loaded after the modem has successfully registered + * with the network. So even if the PCO value is unknown here, + * the successful registration indicates a provisioned SIM. + */ + subscription_state = MM_MODEM_3GPP_SUBSCRIPTION_STATE_PROVISIONED; + } + + g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (subscription_state), NULL); + load_subscription_state_context_complete_and_free (ctx); +} + +static void +altair_get_subscription_state (MMIfaceModem3gpp *self, + LoadSubscriptionStateContext *ctx) +{ + /* Get the latest internet CID first */ + mm_dbg ("Loading internet CID..."); + mm_base_modem_at_command (MM_BASE_MODEM (self), + "%CGINFO=\"cid\",1", + 6, + FALSE, + (GAsyncReadyCallback)altair_load_internet_cid_ready, + ctx); +} + +static void +altair_load_vendor_pco_info_ready (MMIfaceModem3gpp *self, + GAsyncResult *res, + LoadSubscriptionStateContext *ctx) +{ + const gchar *response; + GError *error = NULL; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (error) { + mm_dbg ("Failed to load vendor PCO info."); + g_simple_async_result_take_error (ctx->result, error); + load_subscription_state_context_complete_and_free (ctx); + return; + } + g_assert (response); + ctx->pco_info = g_strdup (response); + altair_get_subscription_state (self, ctx); +} + +static void +modem_3gpp_load_subscription_state (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + LoadSubscriptionStateContext *ctx; + + ctx = g_slice_new0 (LoadSubscriptionStateContext); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_3gpp_load_subscription_state); + + mm_dbg ("Loading vendor PCO info..."); + mm_base_modem_at_command (MM_BASE_MODEM (self), + "%PCOINFO?", + 6, + FALSE, + (GAsyncReadyCallback)altair_load_vendor_pco_info_ready, + ctx); +} + +/*****************************************************************************/ +/* PCOINFO unsolicited event handler */ + +static void +altair_get_subscription_state_ready (MMBroadbandModemAltairLte *self, + GAsyncResult *res, + gpointer *user_data) +{ + GError *error = NULL; + MMModem3gppSubscriptionState subscription_state; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), &error)) { + mm_warn ("Couldn't load Subscription State: '%s'", error->message); + g_error_free (error); + return; + } + + subscription_state = GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))); + mm_iface_modem_3gpp_update_subscription_state (MM_IFACE_MODEM_3GPP (self), subscription_state); +} + +static void +altair_pco_info_changed (MMAtSerialPort *port, + GMatchInfo *match_info, + MMBroadbandModemAltairLte *self) +{ + LoadSubscriptionStateContext *ctx; + const gchar *response; + + ctx = g_slice_new0 (LoadSubscriptionStateContext); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + (GAsyncReadyCallback)altair_get_subscription_state_ready, + NULL, + altair_pco_info_changed); + response = g_match_info_fetch (match_info, 0); + ctx->pco_info = g_strdup (response); + altair_get_subscription_state (MM_IFACE_MODEM_3GPP (self), ctx); +} + +/*****************************************************************************/ /* Generic ports open/close context */ static const gchar *primary_init_sequence[] = { @@ -1230,6 +1422,8 @@ mm_broadband_modem_altair_lte_init (MMBroadbandModemAltairLte *self) self->priv->sim_refresh_timer_id = 0; self->priv->statcm_regex = g_regex_new ("\\r\\n\\%STATCM:\\s*(\\d*),?(\\d*)\\r+\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + self->priv->pcoinfo_regex = g_regex_new ("\\r\\n\\%PCOINFO:\\s*(\\d*),([^,\\s]*),([^,\\s]*)\\r+\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); } static void @@ -1241,6 +1435,7 @@ finalize (GObject *object) g_source_remove (self->priv->sim_refresh_timer_id); g_regex_unref (self->priv->sim_refresh_regex); g_regex_unref (self->priv->statcm_regex); + g_regex_unref (self->priv->pcoinfo_regex); G_OBJECT_CLASS (mm_broadband_modem_altair_lte_parent_class)->finalize (object); } @@ -1310,6 +1505,8 @@ iface_modem_3gpp_init (MMIfaceModem3gpp *iface) iface->load_operator_code_finish = modem_3gpp_load_operator_code_finish; iface->load_operator_name = modem_3gpp_load_operator_name; iface->load_operator_name_finish = modem_3gpp_load_operator_name_finish; + iface->load_subscription_state = modem_3gpp_load_subscription_state; + iface->load_subscription_state_finish = modem_3gpp_load_subscription_state_finish; } static void diff --git a/plugins/altair/mm-modem-helpers-altair-lte.c b/plugins/altair/mm-modem-helpers-altair-lte.c index 9e5b421e..5d3ac374 100644 --- a/plugins/altair/mm-modem-helpers-altair-lte.c +++ b/plugins/altair/mm-modem-helpers-altair-lte.c @@ -66,3 +66,158 @@ mm_altair_parse_ceer_response (const gchar *response, g_regex_unref (r); return ceer_response; } + +/*****************************************************************************/ +/* %CGINFO="cid",1 response parser */ + +guint +altair_parse_cid (const gchar *response, GError **error) +{ + GRegex *regex; + GMatchInfo *match_info; + guint cid = -1; + + regex = g_regex_new ("\\%CGINFO:\\s*(\\d+)", G_REGEX_RAW, 0, NULL); + g_assert (regex); + if (!g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, error)) { + g_regex_unref (regex); + return -1; + } + + if (!mm_get_uint_from_match_info (match_info, 1, &cid)) + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse %%CGINFO=\"cid\",1 response"); + + g_match_info_free (match_info); + g_regex_unref (regex); + return cid; +} + +/*****************************************************************************/ +/* %PCOINFO response parser */ + +static guint +altair_extract_vzw_pco_value (const gchar *pco_payload, GError **error) +{ + GRegex *regex; + GMatchInfo *match_info; + guint pco_value = -1; + + /* Extract PCO value from PCO payload. + * The PCO value in the VZW network is after the VZW PLMN (MCC+MNC 311-480). + */ + regex = g_regex_new ("130184(\\d+)", G_REGEX_RAW, 0, NULL); + g_assert (regex); + if (!g_regex_match_full (regex, + pco_payload, + strlen (pco_payload), + 0, + 0, + &match_info, + error)) + return -1; + + if (!g_match_info_matches (match_info) || + !mm_get_uint_from_match_info (match_info, 1, &pco_value)) + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse PCO value from PCO payload: '%s'", + pco_payload); + + g_match_info_free (match_info); + g_regex_unref (regex); + + return pco_value; +} + +guint +altair_parse_vendor_pco_info (const gchar *pco_info, + guint cid, + GError **error) +{ + GRegex *regex; + GMatchInfo *match_info; + guint pco_value = -1; + gint num_matches; + + if (!pco_info[0]) + /* No APNs configured, all done */ + return -1; + + /* Expected %PCOINFO response: + * + * Solicited response: %PCOINFO:<mode>,<cid>[,<pcoid>[,<payload>]] + * Unsolicited response: %PCOINFO:<cid>,<pcoid>[,<payload>] + */ + regex = g_regex_new ("\\%PCOINFO:(?:\\s*\\d+\\s*,)?(\\d+)\\s*(,([^,\\)]*),([0-9A-Fa-f]*))?", + G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, + 0, NULL); + g_assert (regex); + if (!g_regex_match_full (regex, pco_info, strlen (pco_info), 0, 0, &match_info, error)) + return -1; + + num_matches = g_match_info_get_match_count (match_info); + if (num_matches != 5) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse substrings, number of matches: %d", + num_matches); + return -1; + } + + while (g_match_info_matches (match_info)) { + guint pco_cid; + gchar *pco_id; + gchar *pco_payload; + + if (!mm_get_uint_from_match_info (match_info, 1, &pco_cid)) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse CID from PCO info: '%s'", + pco_info); + break; + } + + if (pco_cid != cid) { + g_match_info_next (match_info, error); + continue; + } + + pco_id = mm_get_string_unquoted_from_match_info (match_info, 3); + if (!pco_id) { + g_set_error (error, + MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Couldn't parse PCO ID from PCO info: '%s'", + pco_info); + break; + } + + if (g_strcmp0 (pco_id, "FF00")) { + g_match_info_next (match_info, error); + continue; + } + + pco_payload = mm_get_string_unquoted_from_match_info (match_info, 4); + if (!pco_payload) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse PCO payload from PCO info: '%s'", + pco_info); + break; + } + + pco_value = altair_extract_vzw_pco_value (pco_payload, error); + break; + } + + g_match_info_free (match_info); + g_regex_unref (regex); + + return pco_value; +} diff --git a/plugins/altair/mm-modem-helpers-altair-lte.h b/plugins/altair/mm-modem-helpers-altair-lte.h index dbd641cd..f7ae6301 100644 --- a/plugins/altair/mm-modem-helpers-altair-lte.h +++ b/plugins/altair/mm-modem-helpers-altair-lte.h @@ -23,4 +23,12 @@ gchar *mm_altair_parse_ceer_response (const gchar *response, GError **error); +/* %CGINFO="cid",1 response parser */ +guint altair_parse_cid (const gchar *response, GError **error); + +/* %PCOINFO response parser */ +guint altair_parse_vendor_pco_info (const gchar *pco_info, + guint cid, + GError **error); + #endif /* MM_MODEM_HELPERS_ALTAIR_H */ diff --git a/plugins/altair/tests/test-modem-helpers-altair-lte.c b/plugins/altair/tests/test-modem-helpers-altair-lte.c index 665b928f..50f2b205 100644 --- a/plugins/altair/tests/test-modem-helpers-altair-lte.c +++ b/plugins/altair/tests/test-modem-helpers-altair-lte.c @@ -62,6 +62,42 @@ test_ceer (void) } } +static void +test_parse_cid (void) +{ + g_assert (altair_parse_cid ("%CGINFO: 2", NULL) == 2); + g_assert (altair_parse_cid ("%CGINFO:blah", NULL) == -1); +} + +static void +test_parse_vendor_pco_info (void) +{ + guint pco_value; + + /* Valid PCO values */ + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018400", 3, NULL); + g_assert (pco_value == 0); + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018403", 3, NULL); + g_assert (pco_value == 3); + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018405", 3, NULL); + g_assert (pco_value == 5); + /* Different container */ + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,3,F000,13018401", 3, NULL); + g_assert (pco_value == -1); + /* Different CID */ + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018401", 1, NULL); + g_assert (pco_value == -1); + /* Different payload */ + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,3,FF00,13018501", 1, NULL); + g_assert (pco_value == -1); + /* Bad PCO info */ + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: blah,blah,FF00,13018401", 1, NULL); + g_assert (pco_value == -1); + /* Multiline PCO info */ + pco_value = altair_parse_vendor_pco_info ("%PCOINFO: 1,1,FF00,13018400\r\n%PCOINFO: 1,3,FF00,13018403", 3, NULL); + g_assert (pco_value == 3); +} + int main (int argc, char **argv) { setlocale (LC_ALL, ""); @@ -70,6 +106,8 @@ int main (int argc, char **argv) g_test_init (&argc, &argv, NULL); g_test_add_func ("/MM/altair/ceer", test_ceer); + g_test_add_func ("/MM/altair/parse_cid", test_parse_cid); + g_test_add_func ("/MM/altair/parse_vendor_pco_info", test_parse_vendor_pco_info); return g_test_run (); } diff --git a/src/mm-iface-modem-3gpp.c b/src/mm-iface-modem-3gpp.c index 051b750d..d6c149ad 100644 --- a/src/mm-iface-modem-3gpp.c +++ b/src/mm-iface-modem-3gpp.c @@ -1236,6 +1236,7 @@ mm_iface_modem_3gpp_update_subscription_state (MMIfaceModem3gpp *self, MM_IFACE_MODEM_3GPP_DBUS_SKELETON, &skeleton, NULL); if (skeleton) { + mm_dbg ("Setting subscription state to: %s", mm_modem_3gpp_subscription_state_get_string (state)); mm_gdbus_modem3gpp_set_subscription_state (skeleton, state); g_object_unref (skeleton); } |