aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mm-broadband-modem-qmi.c1232
-rw-r--r--src/mm-modem-helpers-qmi.c131
-rw-r--r--src/mm-modem-helpers-qmi.h8
-rw-r--r--src/mm-shared-qmi.c1267
-rw-r--r--src/mm-shared-qmi.h67
5 files changed, 1466 insertions, 1239 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index c729d228..54082ae0 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -79,9 +79,6 @@ struct _MMBroadbandModemQmiPrivate {
gchar *meid;
gchar *esn;
- /* Cached supported radio interfaces; in order to load supported modes */
- GArray *supported_radio_interfaces;
-
/* Cached supported frequency bands; in order to handle ANY */
GArray *supported_bands;
@@ -193,597 +190,6 @@ modem_create_bearer (MMIfaceModem *self,
}
/*****************************************************************************/
-/* Current Capabilities loading (Modem interface) */
-
-typedef struct {
- QmiClientNas *nas_client;
- QmiClientDms *dms_client;
- gboolean run_get_system_selection_preference;
- gboolean run_get_technology_preference;
- MMQmiCapabilitiesContext capabilities_context;
-} LoadCurrentCapabilitiesContext;
-
-static MMModemCapability
-modem_load_current_capabilities_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- GError *inner_error = NULL;
- gssize value;
-
- value = g_task_propagate_int (G_TASK (res), &inner_error);
- if (inner_error) {
- g_propagate_error (error, inner_error);
- return MM_MODEM_CAPABILITY_NONE;
- }
- return (MMModemCapability)value;
-}
-
-static void
-load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx)
-{
- g_object_unref (ctx->nas_client);
- g_object_unref (ctx->dms_client);
- g_slice_free (LoadCurrentCapabilitiesContext, ctx);
-}
-
-static void load_current_capabilities_context_step (GTask *task);
-
-static void
-load_current_capabilities_get_capabilities_ready (QmiClientDms *client,
- GAsyncResult *res,
- GTask *task)
-{
- LoadCurrentCapabilitiesContext *ctx;
- QmiMessageDmsGetCapabilitiesOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
- output = qmi_client_dms_get_capabilities_finish (client, res, &error);
- if (!output) {
- g_prefix_error (&error, "QMI operation failed: ");
- g_task_return_error (task, error);
- } else if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get Capabilities: ");
- g_task_return_error (task, error);
- } else {
- guint i;
- 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++) {
- ctx->capabilities_context.dms_capabilities |=
- mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list,
- QmiDmsRadioInterface,
- i));
- }
-
- g_task_return_int (task,
- mm_modem_capability_from_qmi_capabilities_context (&ctx->capabilities_context));
- }
-
- if (output)
- qmi_message_dms_get_capabilities_output_unref (output);
- g_object_unref (task);
-}
-
-static void
-load_current_capabilities_get_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- LoadCurrentCapabilitiesContext *ctx;
- QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
- 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 {
- qmi_message_nas_get_technology_preference_output_get_active (
- output,
- &ctx->capabilities_context.nas_tp_mask,
- NULL, /* duration */
- NULL);
- }
-
- if (output)
- qmi_message_nas_get_technology_preference_output_unref (output);
-
- /* Mark as TP already run */
- ctx->run_get_technology_preference = FALSE;
- load_current_capabilities_context_step (task);
-}
-
-static void
-load_current_capabilities_get_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- LoadCurrentCapabilitiesContext *ctx;
- QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
- 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 {
- qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
- output,
- &ctx->capabilities_context.nas_ssp_mode_preference_mask,
- NULL);
- }
-
- if (output)
- qmi_message_nas_get_system_selection_preference_output_unref (output);
-
- /* Mark as SSP already run */
- ctx->run_get_system_selection_preference = FALSE;
- load_current_capabilities_context_step (task);
-}
-
-static void
-load_current_capabilities_context_step (GTask *task)
-{
- LoadCurrentCapabilitiesContext *ctx;
-
- ctx = g_task_get_task_data (task);
- 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,
- task);
- 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,
- task);
- return;
- }
-
- qmi_client_dms_get_capabilities (
- ctx->dms_client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)load_current_capabilities_get_capabilities_ready,
- task);
-}
-
-static void
-modem_load_current_capabilities (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- LoadCurrentCapabilitiesContext *ctx;
- GTask *task;
- 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 (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_NAS, &nas_client,
- callback, user_data))
- return;
-
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_DMS, &dms_client,
- callback, user_data))
- return;
-
- ctx = g_slice_new0 (LoadCurrentCapabilitiesContext);
- ctx->nas_client = g_object_ref (nas_client);
- ctx->dms_client = g_object_ref (dms_client);
-
- /* 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;
-
- task = g_task_new (self, NULL, callback, user_data);
- g_task_set_task_data (task,
- ctx,
- (GDestroyNotify)load_current_capabilities_context_free);
-
- load_current_capabilities_context_step (task);
-}
-
-/*****************************************************************************/
-/* Supported capabilities loading (Modem interface) */
-
-static GArray *
-modem_load_supported_capabilities_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return g_task_propagate_pointer (G_TASK (res), error);
-}
-
-static void
-dms_get_capabilities_ready (QmiClientDms *client,
- GAsyncResult *res,
- GTask *task)
-{
- 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_task_return_error (task, error);
- } else if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't get supported capabilities: ");
- g_task_return_error (task, error);
- } else {
- MMBroadbandModemQmi *self;
- guint i;
- MMModemCapability mask = MM_MODEM_CAPABILITY_NONE;
- MMModemCapability single;
- GArray *radio_interface_list;
- GArray *supported_combinations;
- GArray *filtered_combinations;
-
- self = g_task_get_source_object (task);
-
- 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));
- }
-
- /* Cache supported radio interfaces */
- if (self->priv->supported_radio_interfaces)
- g_array_unref (self->priv->supported_radio_interfaces);
- self->priv->supported_radio_interfaces = g_array_ref (radio_interface_list);
-
- supported_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 7);
-
- /* Add all possible supported capability combinations, we will filter
- * them out afterwards */
-
- /* GSM/UMTS */
- single = MM_MODEM_CAPABILITY_GSM_UMTS;
- g_array_append_val (supported_combinations, single);
- /* CDMA/EVDO */
- single = MM_MODEM_CAPABILITY_CDMA_EVDO;
- g_array_append_val (supported_combinations, single);
- /* LTE only */
- single = MM_MODEM_CAPABILITY_LTE;
- g_array_append_val (supported_combinations, single);
- /* GSM/UMTS + CDMA/EVDO */
- single = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_GSM_UMTS);
- g_array_append_val (supported_combinations, single);
- /* GSM/UMTS + LTE */
- single = (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (supported_combinations, single);
- /* CDMA/EVDO + LTE */
- single = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (supported_combinations, single);
- /* GSM/UMTS + CDMA/EVDO + LTE */
- single = (MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE);
- g_array_append_val (supported_combinations, single);
-
- /* Now filter out based on the real capabilities of the modem */
- filtered_combinations = mm_filter_supported_capabilities (mask,
- supported_combinations);
- g_array_unref (supported_combinations);
-
- g_task_return_pointer (task,
- filtered_combinations,
- (GDestroyNotify) g_array_unref);
- }
-
- if (output)
- qmi_message_dms_get_capabilities_output_unref (output);
-
- g_object_unref (task);
-}
-
-static void
-modem_load_supported_capabilities (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- QmiClient *client = NULL;
-
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_DMS, &client,
- callback, user_data))
- return;
-
- mm_dbg ("loading supported capabilities...");
- qmi_client_dms_get_capabilities (QMI_CLIENT_DMS (client),
- NULL,
- 5,
- NULL,
- (GAsyncReadyCallback)dms_get_capabilities_ready,
- g_task_new (self, NULL, callback, user_data));
-}
-
-/*****************************************************************************/
-/* Current capabilities setting (Modem interface) */
-
-typedef struct {
- QmiClientNas *client;
- MMModemCapability capabilities;
- gboolean run_set_system_selection_preference;
- gboolean run_set_technology_preference;
-} SetCurrentCapabilitiesContext;
-
-static void
-set_current_capabilities_context_free (SetCurrentCapabilitiesContext *ctx)
-{
- g_object_unref (ctx->client);
- g_slice_free (SetCurrentCapabilitiesContext, ctx);
-}
-
-static gboolean
-set_current_capabilities_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return g_task_propagate_boolean (G_TASK (res), error);
-}
-
-static void
-capabilities_reset_ready (MMIfaceModem *self,
- GAsyncResult *res,
- GTask *task)
-{
- GError *error = NULL;
-
- if (!mm_shared_qmi_reset_finish (self, res, &error))
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
-
- g_object_unref (task);
-}
-
-static void
-capabilities_reset (GTask *task)
-{
- MMBroadbandModemQmi *self;
-
- self = g_task_get_source_object (task);
-
- /* Power cycle the modem */
- mm_shared_qmi_reset (MM_IFACE_MODEM (self),
- (GAsyncReadyCallback)capabilities_reset_ready,
- task);
-}
-
-static void set_current_capabilities_context_step (GTask *task);
-
-static void
-capabilities_set_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- SetCurrentCapabilitiesContext *ctx;
- QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
- output = qmi_client_nas_set_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_set_technology_preference_output_get_result (output, &error) &&
- !g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- mm_dbg ("Couldn't set technology preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_technology_preference_output_unref (output);
- } else {
- if (error)
- g_error_free (error);
-
- /* Good! now reboot the modem */
- capabilities_reset (task);
- qmi_message_nas_set_technology_preference_output_unref (output);
- return;
- }
-
- ctx->run_set_technology_preference = FALSE;
- set_current_capabilities_context_step (task);
-}
-
-static void
-capabilities_set_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- SetCurrentCapabilitiesContext *ctx;
- QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
- output = qmi_client_nas_set_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_set_system_selection_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't set system selection preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- } else {
- /* Good! now reboot the modem */
- capabilities_reset (task);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- return;
- }
-
- /* Try with the deprecated command */
- ctx->run_set_system_selection_preference = FALSE;
- set_current_capabilities_context_step (task);
-}
-
-static void
-set_current_capabilities_context_step (GTask *task)
-{
- SetCurrentCapabilitiesContext *ctx;
-
- ctx = g_task_get_task_data (task);
- if (ctx->run_set_system_selection_preference) {
- QmiMessageNasSetSystemSelectionPreferenceInput *input;
- QmiNasRatModePreference pref;
-
- pref = mm_modem_capability_to_qmi_rat_mode_preference (ctx->capabilities);
- if (!pref) {
- gchar *str;
-
- str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled capabilities setting: '%s'",
- str);
- g_object_unref (task);
- g_free (str);
- return;
- }
-
- input = qmi_message_nas_set_system_selection_preference_input_new ();
- qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
- qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_system_selection_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)capabilities_set_system_selection_preference_ready,
- task);
- qmi_message_nas_set_system_selection_preference_input_unref (input);
- return;
- }
-
- if (ctx->run_set_technology_preference) {
- QmiMessageNasSetTechnologyPreferenceInput *input;
- QmiNasRadioTechnologyPreference pref;
-
- pref = mm_modem_capability_to_qmi_radio_technology_preference (ctx->capabilities);
- if (!pref) {
- gchar *str;
-
- str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled capabilities setting: '%s'",
- str);
- g_object_unref (task);
- g_free (str);
- return;
- }
-
- input = qmi_message_nas_set_technology_preference_input_new ();
- qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_technology_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)capabilities_set_technology_preference_ready,
- task);
- qmi_message_nas_set_technology_preference_input_unref (input);
- return;
- }
-
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting capabilities is not supported by this device");
- g_object_unref (task);
-}
-
-static void
-set_current_capabilities (MMIfaceModem *self,
- MMModemCapability capabilities,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SetCurrentCapabilitiesContext *ctx;
- GTask *task;
- QmiClient *client = NULL;
-
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- ctx = g_slice_new0 (SetCurrentCapabilitiesContext);
- ctx->client = g_object_ref (client);
- ctx->capabilities = capabilities;
-
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_set_system_selection_preference = qmi_client_check_version (client, 1, 1);
-
- /* Technology preference introduced in NAS 1.0, so always available */
- ctx->run_set_technology_preference = TRUE;
-
- task = g_task_new (self, NULL, callback, user_data);
- g_task_set_task_data (task,
- ctx,
- (GDestroyNotify)set_current_capabilities_context_free);
-
- set_current_capabilities_context_step (task);
-}
-
-/*****************************************************************************/
/* Manufacturer loading (Modem interface) */
static gchar *
@@ -2235,139 +1641,6 @@ set_current_bands (MMIfaceModem *_self,
}
/*****************************************************************************/
-/* Load supported modes (Modem interface) */
-
-static GArray *
-modem_load_supported_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return g_task_propagate_pointer (G_TASK (res), error);
-}
-
-static void
-modem_load_supported_modes (MMIfaceModem *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
- GTask *task;
- GArray *combinations;
- MMModemModeCombination mode;
-
- task = g_task_new (self, NULL, callback, user_data);
-
- if (!self->priv->supported_radio_interfaces) {
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot load supported modes, no radio interface list");
- g_object_unref (task);
- return;
- }
-
- /* Build combinations
- *
- * (1) If current capabilities [GSM/UMTS]:
- * [2G only]
- * [3G only]
- * [2G + 3G]
- * [2G + 3G] 2G preferred
- * [2G + 3G] 3G preferred
- *
- * (2) If current capabilities [CDMA/EVDO]:
- * [2G only]
- * [3G only]
- *
- * (3) If current capabilities [LTE]:
- * [4G only]
- *
- * (4) If current capabilities [GSM/UMTS + CDMA/EVDO]:
- * [2G only]
- * [3G only]
- * [2G + 3G]
- * [2G + 3G] 2G preferred
- * [2G + 3G] 3G preferred
- *
- * (5) If current capabilities [GSM/UMTS + LTE]:
- * [2G + 3G + 4G]
- *
- * (6) If current capabilities [CDMA/EVDO + LTE]:
- * [2G + 3G + 4G]
- *
- * (7) If current capabilities [GSM/UMTS + CDMA/EVDO + LTE]:
- * [2G + 3G + 4G]
- */
-
- combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5);
-
- /* LTE only, don't allow further mode switching */
- if (mm_iface_modem_is_3gpp_lte_only (_self)) {
- /* 4G only */
- mode.allowed = MM_MODEM_MODE_4G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- }
- /* LTE and others, only allow to have all, no further preference */
- else if (mm_iface_modem_is_3gpp_lte (_self)) {
- /* 2G, 3G and 4G */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- }
- /* Non-LTE modem, include allowed and preferred combinations */
- else {
- MMModemMode mask_all;
- guint i;
- GArray *all;
- GArray *filtered;
-
-
- /* Build all, based on the supported radio interfaces */
- mask_all = MM_MODEM_MODE_NONE;
- for (i = 0; i < self->priv->supported_radio_interfaces->len; i++)
- mask_all |= mm_modem_mode_from_qmi_radio_interface (g_array_index (self->priv->supported_radio_interfaces,
- QmiDmsRadioInterface,
- i));
-
-
- /* 2G only */
- mode.allowed = MM_MODEM_MODE_2G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- /* 3G only */
- mode.allowed = MM_MODEM_MODE_3G;
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- /* 2G and 3G */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
- /* 2G and 3G, 2G preferred */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_2G;
- g_array_append_val (combinations, mode);
- /* 2G and 3G, 3G preferred */
- mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
- mode.preferred = MM_MODEM_MODE_3G;
- g_array_append_val (combinations, mode);
-
- /* Filter out those unsupported modes */
- mode.allowed = mask_all;
- mode.preferred = MM_MODEM_MODE_NONE;
- all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
- g_array_append_val (all, mode);
- filtered = mm_filter_supported_modes (all, combinations);
- g_array_unref (all);
- g_array_unref (combinations);
- combinations = filtered;
- }
-
- g_task_return_pointer (task, combinations, (GDestroyNotify) g_array_unref);
- g_object_unref (task);
-}
-
-/*****************************************************************************/
/* Load supported IP families (Modem interface) */
static MMBearerIpFamily
@@ -3088,485 +2361,6 @@ create_sim (MMIfaceModem *self,
}
/*****************************************************************************/
-/* Load current modes (Modem interface) */
-
-typedef struct {
- QmiClientNas *client;
- gboolean run_get_system_selection_preference;
- gboolean run_get_technology_preference;
-} LoadCurrentModesContext;
-
-typedef struct {
- MMModemMode allowed;
- MMModemMode preferred;
-} LoadCurrentModesResult;
-
-static void
-load_current_modes_context_free (LoadCurrentModesContext *ctx)
-{
- g_object_unref (ctx->client);
- g_free (ctx);
-}
-
-static gboolean
-load_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemMode *allowed,
- MMModemMode *preferred,
- GError **error)
-{
- LoadCurrentModesResult *result;
-
- result = g_task_propagate_pointer (G_TASK (res), error);
- if (!result)
- return FALSE;
-
- *allowed = result->allowed;
- *preferred = result->preferred;
- g_free (result);
- return TRUE;
-}
-
-static void load_current_modes_context_step (GTask *task);
-
-static void
-get_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- LoadCurrentModesContext *ctx;
- LoadCurrentModesResult *result = NULL;
- QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
-
- 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 {
- MMModemMode allowed;
- QmiNasRadioTechnologyPreference preference_mask;
-
- qmi_message_nas_get_technology_preference_output_get_active (
- output,
- &preference_mask,
- NULL, /* duration */
- NULL);
- allowed = mm_modem_mode_from_qmi_radio_technology_preference (preference_mask);
- if (allowed == MM_MODEM_MODE_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);
- } else {
- /* We got a valid value from here */
- result = g_new (LoadCurrentModesResult, 1);
- result->allowed = allowed;
- result->preferred = MM_MODEM_MODE_NONE;
- }
- }
-
- if (output)
- qmi_message_nas_get_technology_preference_output_unref (output);
-
- if (!result) {
- ctx->run_get_technology_preference = FALSE;
- load_current_modes_context_step (task);
- return;
- }
-
- g_task_return_pointer (task, result, g_free);
- g_object_unref (task);
-}
-
-static void
-current_modes_get_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- LoadCurrentModesContext *ctx;
- LoadCurrentModesResult *result = NULL;
- QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
- QmiNasRatModePreference mode_preference_mask = 0;
-
- ctx = g_task_get_task_data (task);
-
- 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 {
- MMModemMode allowed;
-
- allowed = mm_modem_mode_from_qmi_rat_mode_preference (mode_preference_mask);
- if (allowed == MM_MODEM_MODE_NONE) {
- gchar *str;
-
- str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference_mask);
- mm_dbg ("Unsupported modes reported: '%s'", str);
- g_free (str);
- } else {
- QmiNasGsmWcdmaAcquisitionOrderPreference gsm_or_wcdma;
-
- /* We got a valid value from here */
- result = g_new (LoadCurrentModesResult, 1);
- result->allowed = allowed;
- result->preferred = MM_MODEM_MODE_NONE;
-
- if ((mode_preference_mask & QMI_NAS_RAT_MODE_PREFERENCE_GSM) &&
- (mode_preference_mask & QMI_NAS_RAT_MODE_PREFERENCE_UMTS) &&
- qmi_message_nas_get_system_selection_preference_output_get_gsm_wcdma_acquisition_order_preference (
- output,
- &gsm_or_wcdma,
- NULL)) {
- result->preferred = mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (gsm_or_wcdma);
- }
- }
- }
-
- if (output)
- qmi_message_nas_get_system_selection_preference_output_unref (output);
-
- if (!result) {
- /* Try with the deprecated command */
- ctx->run_get_system_selection_preference = FALSE;
- load_current_modes_context_step (task);
- return;
- }
-
- g_task_return_pointer (task, result, g_free);
- g_object_unref (task);
-}
-
-static void
-load_current_modes_context_step (GTask *task)
-{
- LoadCurrentModesContext *ctx;
-
- ctx = g_task_get_task_data (task);
-
- if (ctx->run_get_system_selection_preference) {
- qmi_client_nas_get_system_selection_preference (
- ctx->client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)current_modes_get_system_selection_preference_ready,
- task);
- return;
- }
-
- if (ctx->run_get_technology_preference) {
- qmi_client_nas_get_technology_preference (
- ctx->client,
- NULL, /* no input */
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)get_technology_preference_ready,
- task);
- return;
- }
-
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Loading current modes is not supported by this device");
- g_object_unref (task);
-}
-
-static void
-load_current_modes (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- LoadCurrentModesContext *ctx;
- GTask *task;
- QmiClient *client = NULL;
-
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- ctx = g_new0 (LoadCurrentModesContext, 1);
- ctx->client = g_object_ref (client);
-
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_get_system_selection_preference = qmi_client_check_version (client, 1, 1);
-
- /* Technology preference introduced in NAS 1.0, so always available */
- ctx->run_get_technology_preference = TRUE;
-
- task = g_task_new (self, NULL, callback, user_data);
- g_task_set_task_data (task,
- ctx,
- (GDestroyNotify)load_current_modes_context_free);
-
- load_current_modes_context_step (task);
-}
-
-/*****************************************************************************/
-/* Set allowed modes (Modem interface) */
-
-typedef struct {
- QmiClientNas *client;
- MMModemMode allowed;
- MMModemMode preferred;
- gboolean run_set_system_selection_preference;
- gboolean run_set_technology_preference;
-} SetCurrentModesContext;
-
-static void
-set_current_modes_context_free (SetCurrentModesContext *ctx)
-{
- g_object_unref (ctx->client);
- g_slice_free (SetCurrentModesContext, ctx);
-}
-
-static gboolean
-set_current_modes_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error)
-{
- return g_task_propagate_boolean (G_TASK (res), error);
-}
-
-static void set_current_modes_context_step (GTask *task);
-
-static void
-set_technology_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- SetCurrentModesContext *ctx;
- QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
-
- output = qmi_client_nas_set_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_set_technology_preference_output_get_result (output, &error) &&
- !g_error_matches (error,
- QMI_PROTOCOL_ERROR,
- QMI_PROTOCOL_ERROR_NO_EFFECT)) {
- mm_dbg ("Couldn't set technology preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_technology_preference_output_unref (output);
- } else {
- if (error)
- g_error_free (error);
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
- qmi_message_nas_set_technology_preference_output_unref (output);
- return;
- }
-
- ctx->run_set_technology_preference = FALSE;
- set_current_modes_context_step (task);
-}
-
-static void
-allowed_modes_set_system_selection_preference_ready (QmiClientNas *client,
- GAsyncResult *res,
- GTask *task)
-{
- SetCurrentModesContext *ctx;
- QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
- GError *error = NULL;
-
- ctx = g_task_get_task_data (task);
-
- output = qmi_client_nas_set_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_set_system_selection_preference_output_get_result (output, &error)) {
- mm_dbg ("Couldn't set system selection preference: %s", error->message);
- g_error_free (error);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- } else {
- /* Good! TODO: do we really need to wait for the indication? */
- g_task_return_boolean (task, TRUE);
- g_object_unref (task);
- qmi_message_nas_set_system_selection_preference_output_unref (output);
- return;
- }
-
- /* Try with the deprecated command */
- ctx->run_set_system_selection_preference = FALSE;
- set_current_modes_context_step (task);
-}
-
-static void
-set_current_modes_context_step (GTask *task)
-{
- MMIfaceModem *self;
- SetCurrentModesContext *ctx;
-
- self = g_task_get_source_object (task);
- ctx = g_task_get_task_data (task);
-
- if (ctx->run_set_system_selection_preference) {
- QmiMessageNasSetSystemSelectionPreferenceInput *input;
- QmiNasRatModePreference pref;
-
- pref = mm_modem_mode_to_qmi_rat_mode_preference (ctx->allowed,
- mm_iface_modem_is_cdma (self),
- mm_iface_modem_is_3gpp (self));
- if (!pref) {
- gchar *str;
-
- str = mm_modem_mode_build_string_from_mask (ctx->allowed);
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled allowed mode setting: '%s'",
- str);
- g_object_unref (task);
- g_free (str);
- return;
- }
-
- input = qmi_message_nas_set_system_selection_preference_input_new ();
- qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
-
- /* Only set acquisition order preference if both 2G and 3G given as allowed */
- if (mm_iface_modem_is_3gpp (self) &&
- ctx->allowed & MM_MODEM_MODE_2G &&
- ctx->allowed & MM_MODEM_MODE_3G) {
- QmiNasGsmWcdmaAcquisitionOrderPreference order;
-
- order = mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (ctx->preferred);
- qmi_message_nas_set_system_selection_preference_input_set_gsm_wcdma_acquisition_order_preference (input, order, NULL);
- }
-
- qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_system_selection_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)allowed_modes_set_system_selection_preference_ready,
- task);
- qmi_message_nas_set_system_selection_preference_input_unref (input);
- return;
- }
-
- if (ctx->run_set_technology_preference) {
- QmiMessageNasSetTechnologyPreferenceInput *input;
- QmiNasRadioTechnologyPreference pref;
-
- if (ctx->preferred != MM_MODEM_MODE_NONE) {
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Cannot set specific preferred mode");
- g_object_unref (task);
- return;
- }
-
- pref = mm_modem_mode_to_qmi_radio_technology_preference (ctx->allowed,
- mm_iface_modem_is_cdma (self));
- if (!pref) {
- gchar *str;
-
- str = mm_modem_mode_build_string_from_mask (ctx->allowed);
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_FAILED,
- "Unhandled allowed mode setting: '%s'",
- str);
- g_object_unref (task);
- g_free (str);
- return;
- }
-
- input = qmi_message_nas_set_technology_preference_input_new ();
- qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
-
- qmi_client_nas_set_technology_preference (
- ctx->client,
- input,
- 5,
- NULL, /* cancellable */
- (GAsyncReadyCallback)set_technology_preference_ready,
- task);
- qmi_message_nas_set_technology_preference_input_unref (input);
- return;
- }
-
- g_task_return_new_error (task,
- MM_CORE_ERROR,
- MM_CORE_ERROR_UNSUPPORTED,
- "Setting allowed modes is not supported by this device");
- g_object_unref (task);
-}
-
-static void
-set_current_modes (MMIfaceModem *self,
- MMModemMode allowed,
- MMModemMode preferred,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- SetCurrentModesContext *ctx;
- GTask *task;
- QmiClient *client = NULL;
-
- if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
- QMI_SERVICE_NAS, &client,
- callback, user_data))
- return;
-
- ctx = g_slice_new0 (SetCurrentModesContext);
- ctx->client = g_object_ref (client);
-
- if (allowed == MM_MODEM_MODE_ANY && ctx->preferred == MM_MODEM_MODE_NONE) {
- ctx->allowed = MM_MODEM_MODE_NONE;
- if (mm_iface_modem_is_2g (self))
- ctx->allowed |= MM_MODEM_MODE_2G;
- if (mm_iface_modem_is_3g (self))
- ctx->allowed |= MM_MODEM_MODE_3G;
- if (mm_iface_modem_is_4g (self))
- ctx->allowed |= MM_MODEM_MODE_4G;
- ctx->preferred = MM_MODEM_MODE_NONE;
- } else {
- ctx->allowed = allowed;
- ctx->preferred = preferred;
- }
-
- /* System selection preference introduced in NAS 1.1 */
- ctx->run_set_system_selection_preference = qmi_client_check_version (client, 1, 1);
-
- /* Technology preference introduced in NAS 1.0, so always available */
- ctx->run_set_technology_preference = TRUE;
-
- task = g_task_new (self, NULL, callback, user_data);
- g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free);
-
- set_current_modes_context_step (task);
-}
-
-/*****************************************************************************/
/* IMEI loading (3GPP interface) */
static gchar *
@@ -10148,8 +8942,6 @@ finalize (GObject *object)
g_free (self->priv->current_operator_description);
if (self->priv->supported_bands)
g_array_unref (self->priv->supported_bands);
- if (self->priv->supported_radio_interfaces)
- g_array_unref (self->priv->supported_radio_interfaces);
G_OBJECT_CLASS (mm_broadband_modem_qmi_parent_class)->finalize (object);
}
@@ -10171,12 +8963,12 @@ static void
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_supported_capabilities = modem_load_supported_capabilities;
- iface->load_supported_capabilities_finish = modem_load_supported_capabilities_finish;
- iface->set_current_capabilities = set_current_capabilities;
- iface->set_current_capabilities_finish = set_current_capabilities_finish;
+ iface->load_current_capabilities = mm_shared_qmi_load_current_capabilities;
+ iface->load_current_capabilities_finish = mm_shared_qmi_load_current_capabilities_finish;
+ iface->load_supported_capabilities = mm_shared_qmi_load_supported_capabilities;
+ iface->load_supported_capabilities_finish = mm_shared_qmi_load_supported_capabilities_finish;
+ iface->set_current_capabilities = mm_shared_qmi_set_current_capabilities;
+ iface->set_current_capabilities_finish = mm_shared_qmi_set_current_capabilities_finish;
iface->load_manufacturer = modem_load_manufacturer;
iface->load_manufacturer_finish = modem_load_manufacturer_finish;
iface->load_model = modem_load_model;
@@ -10197,8 +8989,8 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_unlock_retries_finish = modem_load_unlock_retries_finish;
iface->load_supported_bands = modem_load_supported_bands;
iface->load_supported_bands_finish = modem_load_supported_bands_finish;
- iface->load_supported_modes = modem_load_supported_modes;
- iface->load_supported_modes_finish = modem_load_supported_modes_finish;
+ iface->load_supported_modes = mm_shared_qmi_load_supported_modes;
+ iface->load_supported_modes_finish = mm_shared_qmi_load_supported_modes_finish;
iface->load_power_state = load_power_state;
iface->load_power_state_finish = load_power_state_finish;
iface->load_supported_ip_families = modem_load_supported_ip_families;
@@ -10219,10 +9011,10 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_supported_charsets_finish = NULL;
iface->setup_charset = NULL;
iface->setup_charset_finish = NULL;
- iface->load_current_modes = load_current_modes;
- iface->load_current_modes_finish = load_current_modes_finish;
- iface->set_current_modes = set_current_modes;
- iface->set_current_modes_finish = set_current_modes_finish;
+ iface->load_current_modes = mm_shared_qmi_load_current_modes;
+ iface->load_current_modes_finish = mm_shared_qmi_load_current_modes_finish;
+ iface->set_current_modes = mm_shared_qmi_set_current_modes;
+ iface->set_current_modes_finish = mm_shared_qmi_set_current_modes_finish;
iface->load_signal_quality = load_signal_quality;
iface->load_signal_quality_finish = load_signal_quality_finish;
iface->load_current_bands = modem_load_current_bands;
diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c
index ccc2a912..5df34478 100644
--- a/src/mm-modem-helpers-qmi.c
+++ b/src/mm-modem-helpers-qmi.c
@@ -754,6 +754,25 @@ mm_modem_access_technologies_from_qmi_data_capability_array (GArray *data_capabi
/*****************************************************************************/
MMModemMode
+mm_modem_mode_from_qmi_nas_radio_interface (QmiNasRadioInterface iface)
+{
+ switch (iface) {
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1X:
+ case QMI_NAS_RADIO_INTERFACE_GSM:
+ return MM_MODEM_MODE_2G;
+ case QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO:
+ case QMI_NAS_RADIO_INTERFACE_UMTS:
+ return MM_MODEM_MODE_3G;
+ case QMI_NAS_RADIO_INTERFACE_LTE:
+ return MM_MODEM_MODE_4G;
+ default:
+ return MM_MODEM_MODE_NONE;
+ }
+}
+
+/*****************************************************************************/
+
+MMModemMode
mm_modem_mode_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi)
{
MMModemMode mode = MM_MODEM_MODE_NONE;
@@ -909,6 +928,94 @@ mm_modem_capability_to_qmi_rat_mode_preference (MMModemCapability caps)
/*****************************************************************************/
+GArray *
+mm_modem_capability_to_qmi_acquisition_order_preference (MMModemCapability caps)
+{
+ GArray *array;
+ QmiNasRadioInterface value;
+
+ array = g_array_new (FALSE, FALSE, sizeof (QmiNasRadioInterface));
+
+ if (caps & MM_MODEM_CAPABILITY_LTE) {
+ value = QMI_NAS_RADIO_INTERFACE_LTE;
+ g_array_append_val (array, value);
+ }
+
+ if (caps & MM_MODEM_CAPABILITY_GSM_UMTS) {
+ value = QMI_NAS_RADIO_INTERFACE_UMTS;
+ g_array_append_val (array, value);
+ value = QMI_NAS_RADIO_INTERFACE_GSM;
+ g_array_append_val (array, value);
+ }
+
+ if (caps & MM_MODEM_CAPABILITY_CDMA_EVDO) {
+ value = QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO;
+ g_array_append_val (array, value);
+ value = QMI_NAS_RADIO_INTERFACE_CDMA_1X;
+ g_array_append_val (array, value);
+ }
+
+ return array;
+}
+
+GArray *
+mm_modem_mode_to_qmi_acquisition_order_preference (MMModemMode allowed,
+ MMModemMode preferred,
+ gboolean is_cdma,
+ gboolean is_3gpp)
+{
+ GArray *array;
+ QmiNasRadioInterface value;
+
+ array = g_array_new (FALSE, FALSE, sizeof (QmiNasRadioInterface));
+
+ if (allowed & MM_MODEM_MODE_4G) {
+ value = QMI_NAS_RADIO_INTERFACE_LTE;
+ if (preferred == MM_MODEM_MODE_4G)
+ g_array_prepend_val (array, value);
+ else
+ g_array_append_val (array, value);
+ }
+
+ if (allowed & MM_MODEM_MODE_3G) {
+ if (is_cdma) {
+ value = QMI_NAS_RADIO_INTERFACE_CDMA_1XEVDO;
+ if (preferred == MM_MODEM_MODE_3G)
+ g_array_prepend_val (array, value);
+ else
+ g_array_append_val (array, value);
+ }
+ if (is_3gpp) {
+ value = QMI_NAS_RADIO_INTERFACE_UMTS;
+ if (preferred == MM_MODEM_MODE_3G)
+ g_array_prepend_val (array, value);
+ else
+ g_array_append_val (array, value);
+ }
+ }
+
+ if (allowed & MM_MODEM_MODE_2G) {
+ if (is_cdma) {
+ value = QMI_NAS_RADIO_INTERFACE_CDMA_1X;
+ if (preferred == MM_MODEM_MODE_2G)
+ g_array_prepend_val (array, value);
+ else
+ g_array_append_val (array, value);
+ }
+ if (is_3gpp) {
+ value = QMI_NAS_RADIO_INTERFACE_GSM;
+ if (preferred == MM_MODEM_MODE_2G)
+ g_array_prepend_val (array, value);
+ else
+ g_array_append_val (array, value);
+ }
+ }
+
+ return array;
+}
+
+/*****************************************************************************/
+
MMModemCapability
mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi)
{
@@ -1200,6 +1307,15 @@ mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth)
/*****************************************************************************/
+/**
+ * The only case where we need to apply some logic to decide what the current
+ * capabilities are is when we have a multimode CDMA/EVDO+GSM/UMTS device, in
+ * which case we'll check the SSP and TP current values to decide which
+ * capabilities are present and which have been disabled.
+ *
+ * For all the other cases, the DMS capabilities are exactly the current ones,
+ * as there would be no capability switching support.
+ */
MMModemCapability
mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx)
{
@@ -1209,16 +1325,19 @@ mm_modem_capability_from_qmi_capabilities_context (MMQmiCapabilitiesContext *ctx
gchar *dms_capabilities_str;
gchar *tmp_str;
- /* SSP logic to gather capabilities uses the Mode Preference TLV if available,
- * and if not available it falls back to using Band Preference TLVs */
+ /* If not a multimode device, we're done */
+#define MULTIMODE (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO)
+ if ((ctx->dms_capabilities & MULTIMODE) != MULTIMODE)
+ return ctx->dms_capabilities;
+
+ /* We have a multimode CDMA/EVDO+GSM/UMTS device, check SSP and TP */
+
+ /* SSP logic to gather capabilities uses the Mode Preference TLV if available */
if (ctx->nas_ssp_mode_preference_mask)
tmp = mm_modem_capability_from_qmi_rat_mode_preference (ctx->nas_ssp_mode_preference_mask);
-
/* If no value retrieved from SSP, check TP. We only process TP
* values if not 'auto'. */
- if ( tmp == MM_MODEM_CAPABILITY_NONE
- && ctx->nas_tp_mask
- && ctx->nas_tp_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO)
+ else if (ctx->nas_tp_mask && (ctx->nas_tp_mask != QMI_NAS_RADIO_TECHNOLOGY_PREFERENCE_AUTO))
tmp = mm_modem_capability_from_qmi_radio_technology_preference (ctx->nas_tp_mask);
/* Final capabilities are the intersection between the Technology
diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h
index 8f02b95b..1deea7c3 100644
--- a/src/mm-modem-helpers-qmi.h
+++ b/src/mm-modem-helpers-qmi.h
@@ -46,6 +46,8 @@ MMModemAccessTechnology mm_modem_access_technologies_from_qmi_radio_interface_ar
MMModemAccessTechnology mm_modem_access_technology_from_qmi_data_capability (QmiNasDataCapability cap);
MMModemAccessTechnology mm_modem_access_technologies_from_qmi_data_capability_array (GArray *data_capabilities);
+MMModemMode mm_modem_mode_from_qmi_nas_radio_interface (QmiNasRadioInterface iface);
+
MMModemMode mm_modem_mode_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi);
QmiNasRadioTechnologyPreference mm_modem_mode_to_qmi_radio_technology_preference (MMModemMode mode,
gboolean is_cdma);
@@ -58,6 +60,12 @@ QmiNasRatModePreference mm_modem_mode_to_qmi_rat_mode_preference (MMModemMode mo
MMModemCapability mm_modem_capability_from_qmi_rat_mode_preference (QmiNasRatModePreference qmi);
QmiNasRatModePreference mm_modem_capability_to_qmi_rat_mode_preference (MMModemCapability caps);
+GArray *mm_modem_capability_to_qmi_acquisition_order_preference (MMModemCapability caps);
+GArray *mm_modem_mode_to_qmi_acquisition_order_preference (MMModemMode allowed,
+ MMModemMode preferred,
+ gboolean is_cdma,
+ gboolean is_3gpp);
+
MMModemCapability mm_modem_capability_from_qmi_radio_technology_preference (QmiNasRadioTechnologyPreference qmi);
QmiNasRadioTechnologyPreference mm_modem_capability_to_qmi_radio_technology_preference (MMModemCapability caps);
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
index dba063cf..58645320 100644
--- a/src/mm-shared-qmi.c
+++ b/src/mm-shared-qmi.c
@@ -40,7 +40,20 @@
#define PRIVATE_TAG "shared-qmi-private-tag"
static GQuark private_quark;
+typedef enum {
+ FEATURE_UNKNOWN,
+ FEATURE_UNSUPPORTED,
+ FEATURE_SUPPORTED,
+} Feature;
+
typedef struct {
+ /* Capabilities & modes helpers */
+ MMModemCapability current_capabilities;
+ GArray *supported_radio_interfaces;
+ Feature feature_nas_technology_preference;
+ Feature feature_nas_system_selection_preference;
+ gboolean disable_4g_only_mode;
+
/* Location helpers */
MMIfaceModemLocation *iface_modem_location_parent;
MMModemLocationSource enabled_sources;
@@ -56,6 +69,8 @@ typedef struct {
static void
private_free (Private *priv)
{
+ if (priv->supported_radio_interfaces)
+ g_array_unref (priv->supported_radio_interfaces);
if (priv->pds_location_event_report_indication_id)
g_signal_handler_disconnect (priv->pds_client, priv->pds_location_event_report_indication_id);
if (priv->pds_client)
@@ -80,6 +95,9 @@ get_private (MMSharedQmi *self)
if (!priv) {
priv = g_slice_new0 (Private);
+ priv->feature_nas_technology_preference = FEATURE_UNKNOWN;
+ priv->feature_nas_system_selection_preference = FEATURE_UNKNOWN;
+
/* Setup parent class' MMIfaceModemLocation */
g_assert (MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface);
priv->iface_modem_location_parent = MM_SHARED_QMI_GET_INTERFACE (self)->peek_parent_location_interface (self);
@@ -91,6 +109,1255 @@ get_private (MMSharedQmi *self)
}
/*****************************************************************************/
+/* Current capabilities setting (Modem interface) */
+
+typedef enum {
+ SET_CURRENT_CAPABILITIES_STEP_FIRST,
+ SET_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE,
+ SET_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE,
+ SET_CURRENT_CAPABILITIES_STEP_RESET,
+ SET_CURRENT_CAPABILITIES_STEP_LAST,
+} SetCurrentCapabilitiesStep;
+
+typedef struct {
+ QmiClientNas *client;
+ MMModemCapability capabilities;
+ gboolean capabilities_updated;
+ SetCurrentCapabilitiesStep step;
+} SetCurrentCapabilitiesContext;
+
+static void
+set_current_capabilities_context_free (SetCurrentCapabilitiesContext *ctx)
+{
+ g_object_unref (ctx->client);
+ g_slice_free (SetCurrentCapabilitiesContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_set_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void set_current_capabilities_step (GTask *task);
+
+static void
+set_current_capabilities_reset_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_shared_qmi_reset_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ set_current_capabilities_step (task);
+}
+
+static void
+set_current_capabilities_set_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_set_technology_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_technology_preference_output_get_result (output, &error)) {
+ /* A no-effect error here is not a real error */
+ if (!g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+ /* no effect, just end operation without reset */
+ g_clear_error (&error);
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_LAST;
+ set_current_capabilities_step (task);
+ goto out;
+ }
+
+ /* success! */
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_RESET;
+ set_current_capabilities_step (task);
+
+out:
+ if (output)
+ qmi_message_nas_set_technology_preference_output_unref (output);
+}
+
+static void
+set_current_capabilities_technology_preference (GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceInput *input;
+ QmiNasRadioTechnologyPreference pref;
+
+ ctx = g_task_get_task_data (task);
+
+ pref = mm_modem_capability_to_qmi_radio_technology_preference (ctx->capabilities);
+ if (!pref) {
+ gchar *str;
+
+ str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled capabilities setting: '%s'",
+ str);
+ g_object_unref (task);
+ g_free (str);
+ return;
+ }
+
+ input = qmi_message_nas_set_technology_preference_input_new ();
+ qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_technology_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)set_current_capabilities_set_technology_preference_ready,
+ task);
+ qmi_message_nas_set_technology_preference_input_unref (input);
+}
+
+static void
+set_current_capabilities_set_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
+
+ /* success! */
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_RESET;
+ set_current_capabilities_step (task);
+
+out:
+ if (output)
+ qmi_message_nas_set_system_selection_preference_output_unref (output);
+}
+
+static void
+set_current_capabilities_system_selection_preference (GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ SetCurrentCapabilitiesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceInput *input;
+ QmiNasRatModePreference pref;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ pref = mm_modem_capability_to_qmi_rat_mode_preference (ctx->capabilities);
+ if (!pref) {
+ gchar *str;
+
+ str = mm_modem_capability_build_string_from_mask (ctx->capabilities);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled capabilities setting: '%s'",
+ str);
+ g_object_unref (task);
+ g_free (str);
+ return;
+ }
+
+ input = qmi_message_nas_set_system_selection_preference_input_new ();
+ qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
+ qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_system_selection_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)set_current_capabilities_set_system_selection_preference_ready,
+ task);
+ qmi_message_nas_set_system_selection_preference_input_unref (input);
+}
+
+static void
+set_current_capabilities_step (GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ SetCurrentCapabilitiesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case SET_CURRENT_CAPABILITIES_STEP_FIRST:
+ /* Error out early if both unsupported */
+ if ((priv->feature_nas_system_selection_preference != FEATURE_SUPPORTED) &&
+ (priv->feature_nas_technology_preference != FEATURE_SUPPORTED)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Setting capabilities is not supported by this device");
+ g_object_unref (task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case SET_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE:
+ if (priv->feature_nas_system_selection_preference == FEATURE_SUPPORTED) {
+ set_current_capabilities_system_selection_preference (task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case SET_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE:
+ if (priv->feature_nas_technology_preference == FEATURE_SUPPORTED) {
+ set_current_capabilities_technology_preference (task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case SET_CURRENT_CAPABILITIES_STEP_RESET:
+ mm_shared_qmi_reset (MM_IFACE_MODEM (self),
+ (GAsyncReadyCallback)set_current_capabilities_reset_ready,
+ task);
+ return;
+
+ case SET_CURRENT_CAPABILITIES_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+ }
+}
+
+void
+mm_shared_qmi_set_current_capabilities (MMIfaceModem *self,
+ MMModemCapability capabilities,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ SetCurrentCapabilitiesContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->feature_nas_technology_preference != FEATURE_UNKNOWN);
+ g_assert (priv->feature_nas_system_selection_preference != FEATURE_UNKNOWN);
+
+ ctx = g_slice_new0 (SetCurrentCapabilitiesContext);
+ ctx->client = g_object_ref (client);
+ ctx->capabilities = capabilities;
+ ctx->step = SET_CURRENT_CAPABILITIES_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_capabilities_context_free);
+
+ set_current_capabilities_step (task);
+}
+
+/*****************************************************************************/
+/* Current capabilities (Modem interface) */
+
+typedef enum {
+ LOAD_CURRENT_CAPABILITIES_STEP_FIRST,
+ LOAD_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE,
+ LOAD_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE,
+ LOAD_CURRENT_CAPABILITIES_STEP_DMS_GET_CAPABILITIES,
+ LOAD_CURRENT_CAPABILITIES_STEP_LAST,
+} LoadCurrentCapabilitiesStep;
+
+typedef struct {
+ QmiClientNas *nas_client;
+ QmiClientDms *dms_client;
+ LoadCurrentCapabilitiesStep step;
+ MMQmiCapabilitiesContext capabilities_context;
+} LoadCurrentCapabilitiesContext;
+
+MMModemCapability
+mm_shared_qmi_load_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ GError *inner_error = NULL;
+ gssize value;
+
+ value = g_task_propagate_int (G_TASK (res), &inner_error);
+ if (inner_error) {
+ g_propagate_error (error, inner_error);
+ return MM_MODEM_CAPABILITY_NONE;
+ }
+ return (MMModemCapability)value;
+}
+
+static void
+load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx)
+{
+ g_object_unref (ctx->nas_client);
+ g_object_unref (ctx->dms_client);
+ g_slice_free (LoadCurrentCapabilitiesContext, ctx);
+}
+
+static void load_current_capabilities_step (GTask *task);
+
+static void
+load_current_capabilities_get_capabilities_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+ QmiMessageDmsGetCapabilitiesOutput *output = NULL;
+ GError *error = NULL;
+ guint i;
+ GArray *radio_interface_list;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (self);
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_dms_get_capabilities_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ goto out;
+ }
+
+ if (!qmi_message_dms_get_capabilities_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't get Capabilities: ");
+ goto out;
+ }
+
+ 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);
+
+ /* Cache supported radio interfaces */
+ g_assert (!priv->supported_radio_interfaces);
+ priv->supported_radio_interfaces = g_array_ref (radio_interface_list);
+
+ for (i = 0; i < radio_interface_list->len; i++)
+ ctx->capabilities_context.dms_capabilities |=
+ mm_modem_capability_from_qmi_radio_interface (g_array_index (radio_interface_list, QmiDmsRadioInterface, i));
+
+out:
+ if (output)
+ qmi_message_dms_get_capabilities_output_unref (output);
+
+ /* Failure in DMS Get Capabilities is fatal */
+ if (error) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ ctx->step++;
+ load_current_capabilities_step (task);
+}
+
+static void
+load_current_capabilities_get_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+ QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ 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);
+ priv->feature_nas_technology_preference = FEATURE_UNSUPPORTED;
+ } 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);
+ priv->feature_nas_technology_preference = FEATURE_SUPPORTED;
+ } else {
+ qmi_message_nas_get_technology_preference_output_get_active (
+ output,
+ &ctx->capabilities_context.nas_tp_mask,
+ NULL, /* duration */
+ NULL);
+ priv->feature_nas_technology_preference = FEATURE_SUPPORTED;
+ }
+
+ if (output)
+ qmi_message_nas_get_technology_preference_output_unref (output);
+
+ ctx->step++;
+ load_current_capabilities_step (task);
+}
+
+static void
+load_current_capabilities_get_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+ QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+ priv = get_private (MM_SHARED_QMI (self));
+
+ 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);
+ priv->feature_nas_system_selection_preference = FEATURE_UNSUPPORTED;
+ } 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);
+ priv->feature_nas_system_selection_preference = FEATURE_SUPPORTED;
+ } else {
+ qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
+ output,
+ &ctx->capabilities_context.nas_ssp_mode_preference_mask,
+ NULL);
+ priv->feature_nas_system_selection_preference = FEATURE_SUPPORTED;
+ }
+
+ if (output)
+ qmi_message_nas_get_system_selection_preference_output_unref (output);
+
+ ctx->step++;
+ load_current_capabilities_step (task);
+}
+
+static void
+load_current_capabilities_step (GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ LoadCurrentCapabilitiesContext *ctx;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case LOAD_CURRENT_CAPABILITIES_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_NAS_SYSTEM_SELECTION_PREFERENCE:
+ qmi_client_nas_get_system_selection_preference (
+ ctx->nas_client, NULL, 5, NULL,
+ (GAsyncReadyCallback)load_current_capabilities_get_system_selection_preference_ready,
+ task);
+ return;
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_NAS_TECHNOLOGY_PREFERENCE:
+ qmi_client_nas_get_technology_preference (
+ ctx->nas_client, NULL, 5, NULL,
+ (GAsyncReadyCallback)load_current_capabilities_get_technology_preference_ready,
+ task);
+ return;
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_DMS_GET_CAPABILITIES:
+ qmi_client_dms_get_capabilities (
+ ctx->dms_client, NULL, 5, NULL,
+ (GAsyncReadyCallback)load_current_capabilities_get_capabilities_ready,
+ task);
+ return;
+
+ case LOAD_CURRENT_CAPABILITIES_STEP_LAST:
+ g_assert (priv->feature_nas_technology_preference != FEATURE_UNKNOWN);
+ g_assert (priv->feature_nas_system_selection_preference != FEATURE_UNKNOWN);
+ priv->current_capabilities = mm_modem_capability_from_qmi_capabilities_context (&ctx->capabilities_context);
+ g_task_return_int (task, priv->current_capabilities);
+ g_object_unref (task);
+ return;
+ }
+}
+
+void
+mm_shared_qmi_load_current_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ LoadCurrentCapabilitiesContext *ctx;
+ GTask *task;
+ QmiClient *nas_client = NULL;
+ QmiClient *dms_client = NULL;
+ Private *priv;
+
+ /*
+ * We assume that DMS Get Capabilities reports always the same result,
+ * that will include all capabilities supported by the device regardless
+ * of which ones are configured at the moment. E.g. for the Load Supported
+ * Capabilities we base the logic exclusively on this method's output.
+ *
+ * We then consider 3 different cases:
+ * a) If the device supports NAS System Selection Preference, we use the
+ * "mode preference" TLV to select currently enabled capabilities.
+ * b) If the device supports NAS Technology Preference (older devices),
+ * we use this method to select currently enabled capabilities.
+ * c) If none of those messages is supported we don't allow swiching
+ * capabilities.
+ */
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &nas_client,
+ callback, user_data))
+ return;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_DMS, &dms_client,
+ callback, user_data))
+ return;
+
+ /* Current capabilities is the first thing run, and will only be run once per modem,
+ * so we should here check support for the optional features. */
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->feature_nas_technology_preference == FEATURE_UNKNOWN);
+ g_assert (priv->feature_nas_system_selection_preference == FEATURE_UNKNOWN);
+
+ ctx = g_slice_new0 (LoadCurrentCapabilitiesContext);
+ ctx->nas_client = g_object_ref (nas_client);
+ ctx->dms_client = g_object_ref (dms_client);
+ ctx->step = LOAD_CURRENT_CAPABILITIES_STEP_FIRST;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_current_capabilities_context_free);
+
+ load_current_capabilities_step (task);
+}
+
+/*****************************************************************************/
+/* Supported capabilities (Modem interface) */
+
+GArray *
+mm_shared_qmi_load_supported_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+mm_shared_qmi_load_supported_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ Private *priv;
+ MMModemCapability mask;
+ MMModemCapability single;
+ GArray *supported_combinations;
+ guint i;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* List of radio interfaces preloaded in current capabilities */
+ priv = get_private (MM_SHARED_QMI (self));
+ if (!priv->supported_radio_interfaces) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "cannot load current capabilities without radio interface information");
+ g_object_unref (task);
+ return;
+ }
+
+ /* Build mask with all supported capabilities */
+ mask = MM_MODEM_CAPABILITY_NONE;
+ for (i = 0; i < priv->supported_radio_interfaces->len; i++)
+ mask |= mm_modem_capability_from_qmi_radio_interface (g_array_index (priv->supported_radio_interfaces, QmiDmsRadioInterface, i));
+
+ supported_combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 3);
+
+ /* Add all possible supported capability combinations.
+ * In order to avoid unnecessary modem reboots, we will only implement capabilities
+ * switching only when switching GSM/UMTS+CDMA/EVDO multimode devices, and only if
+ * we have support for the commands doing it.
+ */
+ if (priv->feature_nas_technology_preference == FEATURE_SUPPORTED || priv->feature_nas_system_selection_preference == FEATURE_UNKNOWN) {
+ if (mask == (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO)) {
+ /* Multimode GSM/UMTS+CDMA/EVDO device switched to GSM/UMTS only */
+ single = MM_MODEM_CAPABILITY_GSM_UMTS;
+ g_array_append_val (supported_combinations, single);
+ /* Multimode GSM/UMTS+CDMA/EVDO device switched to CDMA/EVDO only */
+ single = MM_MODEM_CAPABILITY_CDMA_EVDO;
+ g_array_append_val (supported_combinations, single);
+ } else if (mask == (MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE)) {
+ /* Multimode GSM/UMTS+CDMA/EVDO+LTE device switched to GSM/UMTS+LTE only */
+ single = MM_MODEM_CAPABILITY_GSM_UMTS | MM_MODEM_CAPABILITY_LTE;
+ g_array_append_val (supported_combinations, single);
+ /* Multimode GSM/UMTS+CDMA/EVDO+LTE device switched to CDMA/EVDO+LTE only */
+ single = MM_MODEM_CAPABILITY_CDMA_EVDO | MM_MODEM_CAPABILITY_LTE;
+ g_array_append_val (supported_combinations, single);
+ /*
+ * Multimode GSM/UMTS+CDMA/EVDO+LTE device switched to LTE only.
+ *
+ * This case is required because we use the same methods and operations to
+ * switch capabilities and modes. For the LTE capability there is a direct
+ * related 4G mode, and so we cannot select a '4G only' mode in this device
+ * because we wouldn't be able to know the full list of current capabilities
+ * if the device was rebooted, as we would only see LTE capability. So,
+ * handle this special case so that the LTE/4G-only mode can exclusively be
+ * selected as capability switching in this kind of devices.
+ */
+ priv->disable_4g_only_mode = TRUE;
+ single = MM_MODEM_CAPABILITY_LTE;
+ g_array_append_val (supported_combinations, single);
+ }
+ }
+
+ /* Add the full mask itself */
+ single = mask;
+ g_array_append_val (supported_combinations, single);
+
+ g_task_return_pointer (task, supported_combinations, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Allowed modes setting (Modem interface) */
+
+typedef struct {
+ QmiClientNas *client;
+ MMModemMode allowed;
+ MMModemMode preferred;
+} SetCurrentModesContext;
+
+static void
+set_current_modes_context_free (SetCurrentModesContext *ctx)
+{
+ g_object_unref (ctx->client);
+ g_slice_free (SetCurrentModesContext, ctx);
+}
+
+gboolean
+mm_shared_qmi_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+set_current_modes_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentModesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_set_technology_preference_finish (client, res, &error);
+ if (!output ||
+ (!qmi_message_nas_set_technology_preference_output_get_result (output, &error) &&
+ !g_error_matches (error, QMI_PROTOCOL_ERROR, QMI_PROTOCOL_ERROR_NO_EFFECT))) {
+ g_task_return_error (task, error);
+ } else {
+ g_clear_error (&error);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (task);
+
+ if (output)
+ qmi_message_nas_set_technology_preference_output_unref (output);
+}
+
+static void
+set_current_modes_technology_preference (GTask *task)
+{
+ MMIfaceModem *self;
+ SetCurrentModesContext *ctx;
+ QmiMessageNasSetTechnologyPreferenceInput *input;
+ QmiNasRadioTechnologyPreference pref;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ if (ctx->preferred != MM_MODEM_MODE_NONE) {
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Cannot set specific preferred mode");
+ g_object_unref (task);
+ return;
+ }
+
+ pref = mm_modem_mode_to_qmi_radio_technology_preference (ctx->allowed, mm_iface_modem_is_cdma (self));
+ if (!pref) {
+ gchar *str;
+
+ str = mm_modem_mode_build_string_from_mask (ctx->allowed);
+ g_task_return_new_error (task,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Unhandled allowed mode setting: '%s'",
+ str);
+ g_object_unref (task);
+ g_free (str);
+ return;
+ }
+
+ input = qmi_message_nas_set_technology_preference_input_new ();
+ qmi_message_nas_set_technology_preference_input_set_current (input, pref, QMI_NAS_PREFERENCE_DURATION_PERMANENT, NULL);
+
+ qmi_client_nas_set_technology_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)set_current_modes_technology_preference_ready,
+ task);
+ qmi_message_nas_set_technology_preference_input_unref (input);
+}
+
+static void
+set_current_modes_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ SetCurrentModesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_set_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_set_system_selection_preference_output_get_result (output, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+
+ if (output)
+ qmi_message_nas_set_system_selection_preference_output_unref (output);
+}
+
+static void
+set_current_modes_system_selection_preference (GTask *task)
+{
+ MMIfaceModem *self;
+ SetCurrentModesContext *ctx;
+ QmiMessageNasSetSystemSelectionPreferenceInput *input;
+ Private *priv;
+ QmiNasRatModePreference pref;
+
+ self = g_task_get_source_object (task);
+ priv = get_private (MM_SHARED_QMI (self));
+ ctx = g_task_get_task_data (task);
+
+ input = qmi_message_nas_set_system_selection_preference_input_new ();
+ qmi_message_nas_set_system_selection_preference_input_set_change_duration (input, QMI_NAS_CHANGE_DURATION_PERMANENT, NULL);
+
+ /* Preferred modes */
+
+ if (ctx->preferred != MM_MODEM_MODE_NONE) {
+ GArray *array;
+
+ /* Acquisition order array */
+ array = mm_modem_mode_to_qmi_acquisition_order_preference (ctx->allowed,
+ ctx->preferred,
+ mm_iface_modem_is_cdma (self),
+ mm_iface_modem_is_3gpp (self));
+ g_assert (array);
+ qmi_message_nas_set_system_selection_preference_input_set_acquisition_order_preference (input, array, NULL);
+ g_array_unref (array);
+
+ /* Only set GSM/WCDMA acquisition order preference if both 2G and 3G given as allowed */
+ if (mm_iface_modem_is_3gpp (self) && ((ctx->allowed & (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G)) == (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G))) {
+ QmiNasGsmWcdmaAcquisitionOrderPreference order;
+
+ order = mm_modem_mode_to_qmi_gsm_wcdma_acquisition_order_preference (ctx->preferred);
+ qmi_message_nas_set_system_selection_preference_input_set_gsm_wcdma_acquisition_order_preference (input, order, NULL);
+ }
+ }
+
+ /* Allowed modes */
+ pref = mm_modem_mode_to_qmi_rat_mode_preference (ctx->allowed,
+ mm_iface_modem_is_cdma (self),
+ mm_iface_modem_is_3gpp (self));
+ qmi_message_nas_set_system_selection_preference_input_set_mode_preference (input, pref, NULL);
+
+ qmi_client_nas_set_system_selection_preference (
+ ctx->client,
+ input,
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)set_current_modes_system_selection_preference_ready,
+ task);
+ qmi_message_nas_set_system_selection_preference_input_unref (input);
+}
+
+void
+mm_shared_qmi_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ SetCurrentModesContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+ Private *priv;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ ctx = g_slice_new0 (SetCurrentModesContext);
+ ctx->client = g_object_ref (client);
+
+ if (allowed == MM_MODEM_MODE_ANY && ctx->preferred == MM_MODEM_MODE_NONE) {
+ ctx->allowed = MM_MODEM_MODE_NONE;
+ if (mm_iface_modem_is_2g (self))
+ ctx->allowed |= MM_MODEM_MODE_2G;
+ if (mm_iface_modem_is_3g (self))
+ ctx->allowed |= MM_MODEM_MODE_3G;
+ if (mm_iface_modem_is_4g (self))
+ ctx->allowed |= MM_MODEM_MODE_4G;
+ ctx->preferred = MM_MODEM_MODE_NONE;
+ } else {
+ ctx->allowed = allowed;
+ ctx->preferred = preferred;
+ }
+
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)set_current_modes_context_free);
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ if (priv->feature_nas_system_selection_preference == FEATURE_SUPPORTED) {
+ set_current_modes_system_selection_preference (task);
+ return;
+ }
+
+ if (priv->feature_nas_technology_preference == FEATURE_SUPPORTED) {
+ set_current_modes_technology_preference (task);
+ return;
+ }
+
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Setting allowed modes is not supported by this device");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Load current modes (Modem interface) */
+
+typedef struct {
+ QmiClientNas *client;
+} LoadCurrentModesContext;
+
+typedef struct {
+ MMModemMode allowed;
+ MMModemMode preferred;
+} LoadCurrentModesResult;
+
+static void
+load_current_modes_context_free (LoadCurrentModesContext *ctx)
+{
+ g_object_unref (ctx->client);
+ g_free (ctx);
+}
+
+gboolean
+mm_shared_qmi_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+ LoadCurrentModesResult *result;
+
+ result = g_task_propagate_pointer (G_TASK (res), error);
+ if (!result)
+ return FALSE;
+
+ *allowed = result->allowed;
+ *preferred = result->preferred;
+ g_free (result);
+ return TRUE;
+}
+
+static void
+get_technology_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadCurrentModesContext *ctx;
+ LoadCurrentModesResult *result = NULL;
+ QmiMessageNasGetTechnologyPreferenceOutput *output = NULL;
+ GError *error = NULL;
+ MMModemMode allowed;
+ QmiNasRadioTechnologyPreference preference_mask;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_get_technology_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_get_technology_preference_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ qmi_message_nas_get_technology_preference_output_get_active (
+ output,
+ &preference_mask,
+ NULL, /* duration */
+ NULL);
+ allowed = mm_modem_mode_from_qmi_radio_technology_preference (preference_mask);
+ if (allowed == MM_MODEM_MODE_NONE) {
+ gchar *str;
+
+ str = qmi_nas_radio_technology_preference_build_string_from_mask (preference_mask);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported modes reported: '%s'", str);
+ g_free (str);
+ goto out;
+ }
+
+ /* We got a valid value from here */
+ result = g_new (LoadCurrentModesResult, 1);
+ result->allowed = allowed;
+ result->preferred = MM_MODEM_MODE_NONE;
+ g_task_return_pointer (task, result, g_free);
+
+out:
+ if (output)
+ qmi_message_nas_get_technology_preference_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+load_current_modes_technology_preference (GTask *task)
+{
+ LoadCurrentModesContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+
+ qmi_client_nas_get_technology_preference (
+ ctx->client,
+ NULL, /* no input */
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)get_technology_preference_ready,
+ task);
+}
+
+static void
+load_current_modes_system_selection_preference_ready (QmiClientNas *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadCurrentModesContext *ctx;
+ LoadCurrentModesResult *result = NULL;
+ QmiMessageNasGetSystemSelectionPreferenceOutput *output = NULL;
+ GError *error = NULL;
+ QmiNasRatModePreference mode_preference_mask = 0;
+ MMModemMode allowed;
+
+ ctx = g_task_get_task_data (task);
+
+ output = qmi_client_nas_get_system_selection_preference_finish (client, res, &error);
+ if (!output || !qmi_message_nas_get_system_selection_preference_output_get_result (output, &error)) {
+ g_task_return_error (task, error);
+ goto out;
+ }
+
+ if (!qmi_message_nas_get_system_selection_preference_output_get_mode_preference (
+ output,
+ &mode_preference_mask,
+ NULL)) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Mode preference not reported in system selection preference");
+ goto out;
+ }
+
+ allowed = mm_modem_mode_from_qmi_rat_mode_preference (mode_preference_mask);
+ if (allowed == MM_MODEM_MODE_NONE) {
+ gchar *str;
+
+ str = qmi_nas_rat_mode_preference_build_string_from_mask (mode_preference_mask);
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Unsupported modes reported: '%s'", str);
+ g_free (str);
+ goto out;
+ }
+
+ /* We got a valid value from here */
+ result = g_new (LoadCurrentModesResult, 1);
+ result->allowed = allowed;
+ result->preferred = MM_MODEM_MODE_NONE;
+
+ /* For 2G+3G only rely on the GSM/WCDMA acquisition order preference TLV */
+ if (mode_preference_mask == (QMI_NAS_RAT_MODE_PREFERENCE_GSM | QMI_NAS_RAT_MODE_PREFERENCE_UMTS)) {
+ QmiNasGsmWcdmaAcquisitionOrderPreference gsm_or_wcdma;
+
+ if (qmi_message_nas_get_system_selection_preference_output_get_gsm_wcdma_acquisition_order_preference (
+ output,
+ &gsm_or_wcdma,
+ NULL))
+ result->preferred = mm_modem_mode_from_qmi_gsm_wcdma_acquisition_order_preference (gsm_or_wcdma);
+ }
+ /* Otherwise, rely on the acquisition order array TLV */
+ else {
+ GArray *array;
+
+ if (qmi_message_nas_get_system_selection_preference_output_get_acquisition_order_preference (
+ output,
+ &array,
+ NULL) &&
+ array->len > 0) {
+ guint i;
+
+ /* The array of preference contains the preference of the full list of supported
+ * access technologies, regardless of whether they're enabled or not. So, look for
+ * the first one that is flagged as enabled, not just the first one in the array.
+ */
+ for (i = 0; i < array->len; i++) {
+ MMModemMode mode;
+
+ mode = mm_modem_mode_from_qmi_nas_radio_interface (g_array_index (array, QmiNasRadioInterface, i));
+ if (allowed == mode)
+ break;
+ if (allowed & mode) {
+ result->preferred = mode;
+ break;
+ }
+ }
+ }
+ }
+
+ g_task_return_pointer (task, result, g_free);
+
+out:
+ if (output)
+ qmi_message_nas_get_system_selection_preference_output_unref (output);
+ g_object_unref (task);
+}
+
+static void
+load_current_modes_system_selection_preference (GTask *task)
+{
+ LoadCurrentModesContext *ctx;
+
+ ctx = g_task_get_task_data (task);
+ qmi_client_nas_get_system_selection_preference (
+ ctx->client,
+ NULL, /* no input */
+ 5,
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)load_current_modes_system_selection_preference_ready,
+ task);
+}
+
+void
+mm_shared_qmi_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Private *priv;
+ LoadCurrentModesContext *ctx;
+ GTask *task;
+ QmiClient *client = NULL;
+
+ if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self),
+ QMI_SERVICE_NAS, &client,
+ callback, user_data))
+ return;
+
+ ctx = g_new0 (LoadCurrentModesContext, 1);
+ ctx->client = g_object_ref (client);
+ task = g_task_new (self, NULL, callback, user_data);
+ g_task_set_task_data (task, ctx, (GDestroyNotify)load_current_modes_context_free);
+
+ priv = get_private (MM_SHARED_QMI (self));
+
+ if (priv->feature_nas_system_selection_preference != FEATURE_UNSUPPORTED) {
+ load_current_modes_system_selection_preference (task);
+ return;
+ }
+
+ if (priv->feature_nas_technology_preference != FEATURE_UNSUPPORTED) {
+ load_current_modes_technology_preference (task);
+ return;
+ }
+
+ /* Default to supported */
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Loading current modes is not supported by this device");
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
+/* Supported modes (Modem interface) */
+
+GArray *
+mm_shared_qmi_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+void
+mm_shared_qmi_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+ GArray *combinations;
+ MMModemModeCombination mode;
+ Private *priv;
+ MMModemMode mask_all;
+ guint i;
+ GArray *all;
+ GArray *filtered;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ priv = get_private (MM_SHARED_QMI (self));
+ g_assert (priv->supported_radio_interfaces);
+
+ /* Build all, based on the supported radio interfaces */
+ mask_all = MM_MODEM_MODE_NONE;
+ for (i = 0; i < priv->supported_radio_interfaces->len; i++)
+ mask_all |= mm_modem_mode_from_qmi_radio_interface (g_array_index (priv->supported_radio_interfaces, QmiDmsRadioInterface, i));
+ mode.allowed = mask_all;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ g_array_append_val (all, mode);
+
+ /* If SSP and TP are not supported, ignore supported mode management */
+ if (priv->feature_nas_system_selection_preference == FEATURE_UNSUPPORTED && priv->feature_nas_technology_preference == FEATURE_UNSUPPORTED) {
+ g_task_return_pointer (task, all, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+ return;
+ }
+
+ combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 5);
+
+ /* 2G-only, 3G-only */
+ mode.allowed = MM_MODEM_MODE_2G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ mode.allowed = MM_MODEM_MODE_3G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+
+ /* 4G-only mode is not possible in multimode GSM/UMTS+CDMA/EVDO+LTE
+ * devices. This configuration may be selected as "LTE only" capability
+ * instead. */
+ if (!priv->disable_4g_only_mode) {
+ mode.allowed = MM_MODEM_MODE_4G;
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
+ /* 2G+3G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G);
+ if (priv->feature_nas_system_selection_preference != FEATURE_UNSUPPORTED) {
+ mode.preferred = MM_MODEM_MODE_3G;
+ g_array_append_val (combinations, mode);
+ mode.preferred = MM_MODEM_MODE_2G;
+ g_array_append_val (combinations, mode);
+ } else {
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
+ /* 2G+4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_4G);
+ if (priv->feature_nas_system_selection_preference != FEATURE_UNSUPPORTED) {
+ mode.preferred = MM_MODEM_MODE_4G;
+ g_array_append_val (combinations, mode);
+ mode.preferred = MM_MODEM_MODE_2G;
+ g_array_append_val (combinations, mode);
+ } else {
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
+ /* 3G+4G */
+ mode.allowed = (MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ if (priv->feature_nas_system_selection_preference != FEATURE_UNSUPPORTED) {
+ mode.preferred = MM_MODEM_MODE_3G;
+ g_array_append_val (combinations, mode);
+ mode.preferred = MM_MODEM_MODE_4G;
+ g_array_append_val (combinations, mode);
+ } else {
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
+ /* 2G+3G+4G */
+ mode.allowed = (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G | MM_MODEM_MODE_4G);
+ if (priv->feature_nas_system_selection_preference != FEATURE_UNSUPPORTED) {
+ mode.preferred = MM_MODEM_MODE_4G;
+ g_array_append_val (combinations, mode);
+ mode.preferred = MM_MODEM_MODE_3G;
+ g_array_append_val (combinations, mode);
+ mode.preferred = MM_MODEM_MODE_2G;
+ g_array_append_val (combinations, mode);
+ } else {
+ mode.preferred = MM_MODEM_MODE_NONE;
+ g_array_append_val (combinations, mode);
+ }
+
+ /* Filter out unsupported modes */
+ filtered = mm_filter_supported_modes (all, combinations);
+ g_array_unref (all);
+ g_array_unref (combinations);
+
+ g_task_return_pointer (task, filtered, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+/*****************************************************************************/
/* Reset (Modem interface) */
gboolean
diff --git a/src/mm-shared-qmi.h b/src/mm-shared-qmi.h
index af660caa..9b3e10c5 100644
--- a/src/mm-shared-qmi.h
+++ b/src/mm-shared-qmi.h
@@ -62,19 +62,60 @@ gboolean mm_shared_qmi_ensure_client (MMSharedQmi *self,
/* Shared QMI device management support */
-void mm_shared_qmi_reset (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_shared_qmi_reset_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error);
-void mm_shared_qmi_factory_reset (MMIfaceModem *self,
- const gchar *code,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean mm_shared_qmi_factory_reset_finish (MMIfaceModem *self,
- GAsyncResult *res,
- GError **error);
+void mm_shared_qmi_load_supported_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_qmi_load_supported_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_current_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+MMModemCapability mm_shared_qmi_load_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_set_current_capabilities (MMIfaceModem *self,
+ MMModemCapability capabilities,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_set_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GArray *mm_shared_qmi_load_supported_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error);
+void mm_shared_qmi_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_reset (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
+void mm_shared_qmi_factory_reset (MMIfaceModem *self,
+ const gchar *code,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean mm_shared_qmi_factory_reset_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
/* Shared QMI location support */