diff options
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 310 | ||||
-rw-r--r-- | src/mm-modem-helpers-qmi.c | 57 | ||||
-rw-r--r-- | src/mm-modem-helpers-qmi.h | 4 |
3 files changed, 364 insertions, 7 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index 3a718572..8e4c1168 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -146,7 +146,18 @@ modem_create_bearer (MMIfaceModem *self, } /*****************************************************************************/ -/* Capabilities loading (Modem interface) */ +/* Current Capabilities loading (Modem interface) */ + + +typedef struct { + MMBroadbandModemQmi *self; + QmiClientNas *nas_client; + QmiClientDms *dms_client; + GSimpleAsyncResult *result; + gboolean run_get_system_selection_preference; + gboolean run_get_technology_preference; + gboolean run_get_capabilities; +} LoadCurrentCapabilitiesContext; static MMModemCapability modem_load_current_capabilities_finish (MMIfaceModem *self, @@ -169,6 +180,289 @@ modem_load_current_capabilities_finish (MMIfaceModem *self, } static void +load_current_capabilities_context_complete_and_free (LoadCurrentCapabilitiesContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->result); + g_object_unref (ctx->nas_client); + g_object_unref (ctx->dms_client); + g_object_unref (ctx->self); + g_free (ctx); +} + +static void load_current_capabilities_context_step (LoadCurrentCapabilitiesContext *ctx); + +static void +load_current_capabilities_get_capabilities_ready (QmiClientDms *client, + GAsyncResult *res, + LoadCurrentCapabilitiesContext *ctx) +{ + MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; + QmiMessageDmsGetCapabilitiesOutput *output = NULL; + GError *error = NULL; + + output = qmi_client_dms_get_capabilities_finish (client, res, &error); + if (!output) { + g_prefix_error (&error, "QMI operation failed: "); + g_simple_async_result_take_error (ctx->result, error); + } else if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) { + g_prefix_error (&error, "Couldn't get Capabilities: "); + g_simple_async_result_take_error (ctx->result, error); + } else { + guint i; + guint mask = MM_MODEM_CAPABILITY_NONE; + GArray *radio_interface_list; + + qmi_message_dms_get_capabilities_output_get_info ( + output, + NULL, /* info_max_tx_channel_rate */ + NULL, /* info_max_rx_channel_rate */ + NULL, /* info_data_service_capability */ + NULL, /* info_sim_capability */ + &radio_interface_list, + NULL); + + for (i = 0; i < radio_interface_list->len; i++) { + mask |= mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list, + QmiDmsRadioInterface, + i)); + } + + /* Final capabilities are the intersection between the Technology + * Preference (ie, allowed modes) and the device's capabilities. If + * the Technology Preference was "auto" or unknown we just fall back to + * the Get Capabilities response. + */ + caps = ((MMModemCapability) GPOINTER_TO_UINT ( + g_simple_async_result_get_op_res_gpointer (ctx->result))); + if (caps == MM_MODEM_CAPABILITY_NONE) + caps = mask; + else + caps &= mask; + } + + if (output) + qmi_message_dms_get_capabilities_output_unref (output); + + g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (caps), NULL); + load_current_capabilities_context_complete_and_free (ctx); +} + +static void +load_current_capabilities_get_technology_preference_ready (QmiClientNas *client, + GAsyncResult *res, + LoadCurrentCapabilitiesContext *ctx) +{ + MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; + QmiMessageNasGetTechnologyPreferenceOutput *output = NULL; + GError *error = NULL; + + output = qmi_client_nas_get_technology_preference_finish (client, res, &error); + if (!output) { + mm_dbg ("QMI operation failed: %s", error->message); + g_error_free (error); + } else if (!qmi_message_nas_get_technology_preference_output_get_result (output, &error)) { + mm_dbg ("Couldn't get technology preference: %s", error->message); + g_error_free (error); + } else { + QmiNasRadioTechnologyPreference preference_mask; + + qmi_message_nas_get_technology_preference_output_get_active ( + output, + &preference_mask, + NULL, /* duration */ + NULL); + if (preference_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO) { + caps = mm_modem_capability_from_qmi_radio_technology_preference (preference_mask); + if (caps == MM_MODEM_CAPABILITY_NONE) { + gchar *str; + + str = qmi_nas_radio_technology_preference_build_string_from_mask (preference_mask); + mm_dbg ("Unsupported modes reported: '%s'", str); + g_free (str); + } + } + } + + if (output) + qmi_message_nas_get_technology_preference_output_unref (output); + + ctx->run_get_technology_preference = FALSE; + + /* Get DMS Capabilities too */ + g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (caps), NULL); + load_current_capabilities_context_step (ctx); +} + +static void +load_current_capabilities_get_system_selection_preference_ready (QmiClientNas *client, + GAsyncResult *res, + LoadCurrentCapabilitiesContext *ctx) +{ + MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; + QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL; + GError *error = NULL; + QmiNasRatModePreference mode_preference_mask = 0; + + output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error); + if (!output) { + mm_dbg ("QMI operation failed: %s", error->message); + g_error_free (error); + } else if (!qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) { + mm_dbg ("Couldn't get system selection preference: %s", error->message); + g_error_free (error); + } else if (!qmi_message_nas_get_system_selection_preference_output_get_mode_preference ( + output, + &mode_preference_mask, + NULL)) { + mm_dbg ("Mode preference not reported in system selection preference"); + } else { + caps = mm_modem_capability_from_qmi_rat_mode_preference (mode_preference_mask); + if (caps == MM_MODEM_CAPABILITY_NONE) { + gchar *str; + + str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference_mask); + mm_dbg ("Unsupported capabilities reported: '%s'", str); + g_free (str); + } + } + + if (output) + qmi_message_nas_get_system_selection_preference_output_unref (output); + + if (caps == MM_MODEM_CAPABILITY_NONE) { + /* Fall back to Technology Preference */ + ctx->run_get_system_selection_preference = FALSE; + load_current_capabilities_context_step (ctx); + return; + } + + /* Success */ + g_simple_async_result_set_op_res_gpointer (ctx->result, GUINT_TO_POINTER (caps), NULL); + load_current_capabilities_context_complete_and_free (ctx); +} + +static void +load_current_capabilities_context_step (LoadCurrentCapabilitiesContext *ctx) +{ + if (ctx->run_get_system_selection_preference) { + qmi_client_nas_get_system_selection_preference ( + ctx->nas_client, + NULL, /* no input */ + 5, + NULL, /* cancellable */ + (GAsyncReadyCallback)load_current_capabilities_get_system_selection_preference_ready, + ctx); + return; + } + + if (ctx->run_get_technology_preference) { + qmi_client_nas_get_technology_preference ( + ctx->nas_client, + NULL, /* no input */ + 5, + NULL, /* cancellable */ + (GAsyncReadyCallback)load_current_capabilities_get_technology_preference_ready, + ctx); + return; + } + + if (ctx->run_get_capabilities) { + qmi_client_dms_get_capabilities ( + ctx->dms_client, + NULL, /* no input */ + 5, + NULL, /* cancellable */ + (GAsyncReadyCallback)load_current_capabilities_get_capabilities_ready, + ctx); + return; + } + + g_simple_async_result_set_error ( + ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Loading current capabilities is not supported by this device"); + load_current_capabilities_context_complete_and_free (ctx); +} + +static void +modem_load_current_capabilities (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + LoadCurrentCapabilitiesContext *ctx; + QmiClient *nas_client = NULL; + QmiClient *dms_client = NULL; + + /* Best way to get current capabilities (ie, enabled radios) is + * Get System Selection Preference's "mode preference" TLV, but that's + * only supported by NAS >= 1.1, meaning older Gobi devices don't + * implement it. + * + * On these devices, the DMS Get Capabilities call appears to report + * currently enabled radios, but this does not take the user's + * technology preference into account. + * + * So in the absence of System Selection Preference, we check the + * Technology Preference first, and if that is "AUTO" we fall back to + * Get Capabilities. + */ + + mm_dbg ("loading current capabilities..."); + + if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self), + QMI_SERVICE_NAS, &nas_client, + callback, user_data)) + return; + + if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self), + QMI_SERVICE_DMS, &dms_client, + callback, user_data)) + return; + + ctx = g_new0 (LoadCurrentCapabilitiesContext, 1); + ctx->self = g_object_ref (self); + ctx->nas_client = g_object_ref (nas_client); + ctx->dms_client = g_object_ref (dms_client); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_load_current_capabilities); + + /* System selection preference introduced in NAS 1.1 */ + ctx->run_get_system_selection_preference = qmi_client_check_version (nas_client, 1, 1); + + ctx->run_get_technology_preference = TRUE; + ctx->run_get_capabilities = TRUE; + + load_current_capabilities_context_step (ctx); +} + +/*****************************************************************************/ +/* Modem Capabilities loading (Modem interface) */ + +static MMModemCapability +modem_load_modem_capabilities_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + MMModemCapability caps; + gchar *caps_str; + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return MM_MODEM_CAPABILITY_NONE; + + caps = ((MMModemCapability) GPOINTER_TO_UINT ( + g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res)))); + caps_str = mm_modem_capability_build_string_from_mask (caps); + mm_dbg ("loaded modem capabilities: %s", caps_str); + g_free (caps_str); + return caps; +} + +static void dms_get_capabilities_ready (QmiClientDms *client, GAsyncResult *res, GSimpleAsyncResult *simple) @@ -181,7 +475,7 @@ dms_get_capabilities_ready (QmiClientDms *client, g_prefix_error (&error, "QMI operation failed: "); g_simple_async_result_take_error (simple, error); } else if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) { - g_prefix_error (&error, "Couldn't get Capabilities: "); + g_prefix_error (&error, "Couldn't get modem capabilities: "); g_simple_async_result_take_error (simple, error); } else { guint i; @@ -216,9 +510,9 @@ dms_get_capabilities_ready (QmiClientDms *client, } static void -modem_load_current_capabilities (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) +modem_load_modem_capabilities (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) { GSimpleAsyncResult *result; QmiClient *client = NULL; @@ -231,9 +525,9 @@ modem_load_current_capabilities (MMIfaceModem *self, result = g_simple_async_result_new (G_OBJECT (self), callback, user_data, - modem_load_current_capabilities); + modem_load_modem_capabilities); - mm_dbg ("loading current capabilities..."); + mm_dbg ("loading modem capabilities..."); qmi_client_dms_get_capabilities (QMI_CLIENT_DMS (client), NULL, 5, @@ -4730,6 +5024,8 @@ iface_modem_init (MMIfaceModem *iface) /* Initialization steps */ iface->load_current_capabilities = modem_load_current_capabilities; iface->load_current_capabilities_finish = modem_load_current_capabilities_finish; + iface->load_modem_capabilities = modem_load_modem_capabilities; + iface->load_modem_capabilities_finish = modem_load_modem_capabilities_finish; iface->load_manufacturer = modem_load_manufacturer; iface->load_manufacturer_finish = modem_load_manufacturer_finish; iface->load_model = modem_load_model; diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c index c170dba6..0b5c4647 100644 --- a/src/mm-modem-helpers-qmi.c +++ b/src/mm-modem-helpers-qmi.c @@ -630,6 +630,63 @@ mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mode, /*****************************************************************************/ +MMModemCapability +mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi) +{ + MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; + + if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1X) + caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; + + if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_CDMA_1XEVDO) + caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; + + if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_GSM) + caps |= MM_MODEM_CAPABILITY_GSM_UMTS; + + if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_UMTS) + caps |= MM_MODEM_CAPABILITY_GSM_UMTS; + + if (qmi & QMI_NAS_RAT_MODE_PREFERENCE_LTE) + caps |= MM_MODEM_CAPABILITY_LTE; + + /* FIXME: LTE Advanced? */ + + return caps; +} + +/*****************************************************************************/ + +MMModemCapability +mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi) +{ + MMModemCapability caps = MM_MODEM_CAPABILITY_NONE; + + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP2) { + /* Skip AMPS */ + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA) + caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; /* CDMA */ + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_HDR) + caps |= MM_MODEM_CAPABILITY_CDMA_EVDO; /* EV-DO */ + } + + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_3GPP) { + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AMPS_OR_GSM) + caps |= MM_MODEM_CAPABILITY_GSM_UMTS; /* GSM */ + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_CDMA_OR_WCDMA) + caps |= MM_MODEM_CAPABILITY_GSM_UMTS; /* WCDMA */ + } + + if (qmi & QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_LTE) + caps |= MM_MODEM_CAPABILITY_LTE; + + /* FIXME: LTE Advanced? */ + + return caps; +} + +/*****************************************************************************/ + MMModemMode mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi) { diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h index 4a69653b..5c5df9dd 100644 --- a/src/mm-modem-helpers-qmi.h +++ b/src/mm-modem-helpers-qmi.h @@ -52,6 +52,10 @@ QmiNasRatModePreference mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mo gboolean is_cdma, gboolean is_3gpp); +MMModemCapability mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi); + +MMModemCapability mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi); + MMModemMode mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (QmiNasGsmWcdmaAcquisitionOrderPreference qmi); QmiNasGsmWcdmaAcquisitionOrderPreference mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (MMModemMode mode); |