diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-08-07 12:30:16 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-08-29 17:26:44 +0200 |
commit | a6a34c3b9c12247a3973970adf0f93aaf22a1dfa (patch) | |
tree | 269550d733bf3eea23b587be3ce8fdf34a9fdce1 | |
parent | f015bf54db8de0e45f3843020684f38e3c339567 (diff) |
broadband-modem-qmi: implement 3GPP registration checks for NAS >= 1.8
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 449 |
1 files changed, 443 insertions, 6 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index a74b0a43..12a9708c 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -2940,6 +2940,434 @@ get_serving_system_ready (QmiClientNas *client, run_registration_checks_context_complete_and_free (ctx); } +static gboolean +process_common_info (QmiNasServiceStatus service_status, + gboolean domain_valid, + QmiNasNetworkServiceDomain domain, + gboolean roaming_status_valid, + QmiNasRoamingStatus roaming_status, + gboolean forbidden_valid, + gboolean forbidden, + gboolean lac_valid, + guint16 lac, + gboolean cid_valid, + guint32 cid, + gboolean network_id_valid, + const gchar *mcc, + const gchar *mnc, + MMModem3gppRegistrationState *mm_cs_registration_state, + MMModem3gppRegistrationState *mm_ps_registration_state, + guint16 *mm_lac, + guint32 *mm_cid, + gchar **mm_operator_id) +{ + MMModem3gppRegistrationState tmp_registration_state; + gboolean apply_cs; + gboolean apply_ps; + + if (service_status != QMI_NAS_SERVICE_STATUS_LIMITED && + service_status != QMI_NAS_SERVICE_STATUS_AVAILABLE && + service_status != QMI_NAS_SERVICE_STATUS_LIMITED_REGIONAL) + return FALSE; + + /* If we don't have domain, unknown */ + if (!domain_valid) + tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_NONE) + tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING; + else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_UNKNOWN) + tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + else { + /* If we have CS or PS service domain, assume registered for now */ + if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_CS) + apply_ps = FALSE; + else if (domain == QMI_NAS_NETWORK_SERVICE_DOMAIN_PS) + apply_cs = FALSE; + + /* Check if we really are roaming or forbidden */ + if (forbidden_valid && forbidden) + tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_DENIED; + else { + if (roaming_status_valid && roaming_status == QMI_NAS_ROAMING_STATUS_ON) + tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING; + else + tmp_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_HOME; + + /* If we're registered either at home or roaming, try to get LAC/CID */ + if (lac_valid) + *mm_lac = lac; + if (cid_valid) + *mm_cid = cid; + } + } + + if (apply_cs) + *mm_cs_registration_state = tmp_registration_state; + if (apply_ps) + *mm_cs_registration_state = tmp_registration_state; + + if (network_id_valid) { + *mm_operator_id = g_malloc (7); + memcpy (*mm_operator_id, mcc, 3); + if (mnc[2] == 0xFF) { + memcpy (*mm_operator_id, mnc, 2); + (*mm_operator_id)[5] = '\0'; + } else { + memcpy (*mm_operator_id, mnc, 3); + (*mm_operator_id)[6] = '\0'; + } + } + + return TRUE; +} + +static gboolean +process_gsm_info (QmiMessageNasGetSystemInfoOutput *output, + MMModemAccessTechnology *mm_access_technologies, + MMModem3gppRegistrationState *mm_cs_registration_state, + MMModem3gppRegistrationState *mm_ps_registration_state, + guint16 *mm_lac, + guint32 *mm_cid, + gchar **mm_operator_id) +{ + QmiNasServiceStatus service_status; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean egprs_support_valid; + gboolean egprs_support; + + *mm_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + *mm_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + *mm_lac = 0; + *mm_cid = 0; + g_free (*mm_operator_id); + *mm_operator_id = NULL; + + if (!qmi_message_nas_get_system_info_output_get_gsm_service_status ( + output, + &service_status, + NULL, /* true_service_status */ + NULL, /* preferred_data_path */ + NULL) || + !qmi_message_nas_get_system_info_output_get_gsm_system_info ( + output, + &domain_valid, &domain, + NULL, NULL, /* service_capability */ + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + NULL, NULL, NULL, /* registration_reject_info */ + &network_id_valid, &mcc, &mnc, + &egprs_support_valid, &egprs_support, + NULL, NULL, /* dtm_support */ + NULL)) { + mm_dbg ("No GSM service reported"); + /* No GSM service */ + return FALSE; + } + + if (!process_common_info (service_status, + domain_valid, domain, + roaming_status_valid, roaming_status, + forbidden_valid, forbidden, + lac_valid, lac, + cid_valid, cid, + network_id_valid, mcc, mnc, + mm_cs_registration_state, + mm_ps_registration_state, + mm_lac, + mm_cid, + mm_operator_id)) { + mm_dbg ("No GSM service registered"); + return FALSE; + } + + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_GSM; + if (egprs_support_valid && egprs_support) + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_EDGE; + + return TRUE; +} + +static gboolean +process_wcdma_info (QmiMessageNasGetSystemInfoOutput *output, + MMModemAccessTechnology *mm_access_technologies, + MMModem3gppRegistrationState *mm_cs_registration_state, + MMModem3gppRegistrationState *mm_ps_registration_state, + guint16 *mm_lac, + guint32 *mm_cid, + gchar **mm_operator_id) +{ + QmiNasServiceStatus service_status; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + gboolean hs_service_valid; + QmiNasWcdmaHsService hs_service; + + *mm_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + *mm_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + *mm_lac = 0; + *mm_cid = 0; + g_free (*mm_operator_id); + *mm_operator_id = NULL; + + if (!qmi_message_nas_get_system_info_output_get_wcdma_service_status ( + output, + &service_status, + NULL, /* true_service_status */ + NULL, /* preferred_data_path */ + NULL) || + !qmi_message_nas_get_system_info_output_get_wcdma_system_info ( + output, + &domain_valid, &domain, + NULL, NULL, /* service_capability */ + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + NULL, NULL, NULL, /* registration_reject_info */ + &network_id_valid, &mcc, &mnc, + NULL, NULL, /* hs_call_status */ + &hs_service_valid, &hs_service, + NULL, NULL, /* primary_scrambling_code */ + NULL)) { + mm_dbg ("No WCDMA service reported"); + /* No GSM service */ + return FALSE; + } + + if (!process_common_info (service_status, + domain_valid, domain, + roaming_status_valid, roaming_status, + forbidden_valid, forbidden, + lac_valid, lac, + cid_valid, cid, + network_id_valid, mcc, mnc, + mm_cs_registration_state, + mm_ps_registration_state, + mm_lac, + mm_cid, + mm_operator_id)) { + mm_dbg ("No WCDMA service registered"); + return FALSE; + } + + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_UMTS; + if (hs_service_valid) { + switch (hs_service) { + case QMI_NAS_WCDMA_HS_SERVICE_HSDPA_HSUPA_UNSUPPORTED: + break; + case QMI_NAS_WCDMA_HS_SERVICE_HSDPA_SUPPORTED: + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; + break; + case QMI_NAS_WCDMA_HS_SERVICE_HSUPA_SUPPORTED: + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_HSUPA; + break; + case QMI_NAS_WCDMA_HS_SERVICE_HSDPA_HSUPA_SUPPORTED: + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA; + break; + case QMI_NAS_WCDMA_HS_SERVICE_HSDPA_PLUS_SUPPORTED: + case QMI_NAS_WCDMA_HS_SERVICE_HSDPA_PLUS_HSUPA_SUPPORTED: + case QMI_NAS_WCDMA_HS_SERVICE_DC_HSDPA_PLUS_SUPPORTED: + case QMI_NAS_WCDMA_HS_SERVICE_DC_HSDPA_PLUS_HSUPA_SUPPORTED: + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_HSPA_PLUS; + break; + } + } + + return TRUE; +} + +static gboolean +process_lte_info (QmiMessageNasGetSystemInfoOutput *output, + MMModemAccessTechnology *mm_access_technologies, + MMModem3gppRegistrationState *mm_cs_registration_state, + MMModem3gppRegistrationState *mm_ps_registration_state, + guint16 *mm_lac, + guint32 *mm_cid, + gchar **mm_operator_id) +{ + QmiNasServiceStatus service_status; + gboolean domain_valid; + QmiNasNetworkServiceDomain domain; + gboolean roaming_status_valid; + QmiNasRoamingStatus roaming_status; + gboolean forbidden_valid; + gboolean forbidden; + gboolean lac_valid; + guint16 lac; + gboolean cid_valid; + guint32 cid; + gboolean network_id_valid; + const gchar *mcc; + const gchar *mnc; + + *mm_ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + *mm_cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + *mm_lac = 0; + *mm_cid = 0; + g_free (*mm_operator_id); + *mm_operator_id = NULL; + + if (!qmi_message_nas_get_system_info_output_get_lte_service_status ( + output, + &service_status, + NULL, /* true_service_status */ + NULL, /* preferred_data_path */ + NULL) || + !qmi_message_nas_get_system_info_output_get_lte_system_info ( + output, + &domain_valid, &domain, + NULL, NULL, /* service_capability */ + &roaming_status_valid, &roaming_status, + &forbidden_valid, &forbidden, + &lac_valid, &lac, + &cid_valid, &cid, + NULL, NULL, NULL, /* registration_reject_info */ + &network_id_valid, &mcc, &mnc, + NULL, NULL, /* tac */ + NULL)) { + mm_dbg ("No LTE service reported"); + /* No GSM service */ + return FALSE; + } + + if (!process_common_info (service_status, + domain_valid, domain, + roaming_status_valid, roaming_status, + forbidden_valid, forbidden, + lac_valid, lac, + cid_valid, cid, + network_id_valid, mcc, mnc, + mm_cs_registration_state, + mm_ps_registration_state, + mm_lac, + mm_cid, + mm_operator_id)) { + mm_dbg ("No LTE service registered"); + return FALSE; + } + + *mm_access_technologies |= MM_MODEM_ACCESS_TECHNOLOGY_LTE; + return TRUE; +} + +static void +get_system_info_ready (QmiClientNas *client, + GAsyncResult *res, + RunRegistrationChecksContext *ctx) +{ + QmiMessageNasGetSystemInfoOutput *output; + GError *error = NULL; + MMModemAccessTechnology access_technologies; + MMModem3gppRegistrationState cs_registration_state; + MMModem3gppRegistrationState ps_registration_state; + guint16 lac; + guint32 cid; + gchar *operator_id; + + output = qmi_client_nas_get_system_info_finish (client, res, &error); + if (!output) { + g_prefix_error (&error, "QMI operation failed: "); + g_simple_async_result_take_error (ctx->result, error); + run_registration_checks_context_complete_and_free (ctx); + return; + } + + if (!qmi_message_nas_get_system_info_output_get_result (output, &error)) { + g_prefix_error (&error, "Couldn't get system info: "); + g_simple_async_result_take_error (ctx->result, error); + qmi_message_nas_get_system_info_output_unref (output); + run_registration_checks_context_complete_and_free (ctx); + return; + } + + access_technologies = 0; + ps_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + cs_registration_state = MM_MODEM_3GPP_REGISTRATION_STATE_UNKNOWN; + lac = 0; + cid = 0; + operator_id = NULL; + + /* Process infos, with the following priority: + * LTE > WCDMA > GSM + * The first one giving results will be the one reported. + */ + if (!process_lte_info (output, + &access_technologies, + &cs_registration_state, + &ps_registration_state, + &lac, + &cid, + &operator_id) && + !process_wcdma_info (output, + &access_technologies, + &cs_registration_state, + &ps_registration_state, + &lac, + &cid, + &operator_id) && + !process_gsm_info (output, + &access_technologies, + &cs_registration_state, + &ps_registration_state, + &lac, + &cid, + &operator_id)) { + mm_dbg ("No service (GSM, WCDMA or LTE) reported"); + } + + /* Cache current operator ID */ + if (operator_id) { + g_free (ctx->self->priv->current_operator_id); + ctx->self->priv->current_operator_id = operator_id; + } + + /* Report new registration states */ + if (ctx->cs_supported) + mm_iface_modem_3gpp_update_cs_registration_state ( + MM_IFACE_MODEM_3GPP (ctx->self), + cs_registration_state, + access_technologies, + lac, + cid); + + if (ctx->ps_supported) + mm_iface_modem_3gpp_update_ps_registration_state ( + MM_IFACE_MODEM_3GPP (ctx->self), + ps_registration_state, + access_technologies, + lac, + cid); + + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + qmi_message_nas_get_system_info_output_unref (output); + run_registration_checks_context_complete_and_free (ctx); +} + static void modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, gboolean cs_supported, @@ -2965,12 +3393,21 @@ modem_3gpp_run_registration_checks (MMIfaceModem3gpp *self, ctx->cs_supported = cs_supported; ctx->ps_supported = ps_supported; - qmi_client_nas_get_serving_system (ctx->client, - NULL, - 10, - NULL, - (GAsyncReadyCallback)get_serving_system_ready, - ctx); + /* System Info was added in NAS 1.8 */ + if (qmi_client_check_version (client, 1, 8)) + qmi_client_nas_get_system_info (ctx->client, + NULL, + 10, + NULL, + (GAsyncReadyCallback)get_system_info_ready, + ctx); + else + qmi_client_nas_get_serving_system (ctx->client, + NULL, + 10, + NULL, + (GAsyncReadyCallback)get_serving_system_ready, + ctx); } /*****************************************************************************/ |