diff options
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 1232 | ||||
-rw-r--r-- | src/mm-modem-helpers-qmi.c | 131 | ||||
-rw-r--r-- | src/mm-modem-helpers-qmi.h | 8 | ||||
-rw-r--r-- | src/mm-shared-qmi.c | 1267 | ||||
-rw-r--r-- | src/mm-shared-qmi.h | 67 |
6 files changed, 1467 insertions, 1240 deletions
diff --git a/configure.ac b/configure.ac index dd06b3af..f6623eab 100644 --- a/configure.ac +++ b/configure.ac @@ -341,7 +341,7 @@ dnl----------------------------------------------------------------------------- dnl QMI support (enabled by default) dnl -LIBQMI_VERSION=1.21.3 +LIBQMI_VERSION=1.21.4 AC_ARG_WITH(qmi, AS_HELP_STRING([--without-qmi], [Build without QMI support]), [], [with_qmi=yes]) AM_CONDITIONAL(WITH_QMI, test "x$with_qmi" = "xyes") 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 */ |