summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2018-07-15 23:07:23 +0200
committerDan Williams <dcbw@redhat.com>2018-09-12 17:25:19 +0000
commit7f18a9370a11e4913db72659aa1c92b12f3f7044 (patch)
treed5ae51998355f9051f4cabdd89de325957c0d3b8
parentac69466c9da7c553c7f4c4a4197614af560abc83 (diff)
downloadModemManager-7f18a9370a11e4913db72659aa1c92b12f3f7044.tar.gz
broadband-modem-mbim: use QMI-based mode/capability switching if supported
The implementation available in the shared QMI logic can be used as-is, with one important note: if the QMI-based mode/capability switching is used, we MUST also use QMI-based "3GPP network registration" logic. This is needed because the MBIM command used to select which 3GPP network to connect to allows specifying the mask of access technologies desired, and that would overwrite whatever we had previously set with QMI-based mode/capability switching commands.
-rw-r--r--src/mm-broadband-modem-mbim.c411
-rw-r--r--src/mm-modem-helpers-mbim.c22
-rw-r--r--src/mm-modem-helpers-mbim.h3
3 files changed, 387 insertions, 49 deletions
diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c
index 48406208f..84ad0cb12 100644
--- a/src/mm-broadband-modem-mbim.c
+++ b/src/mm-broadband-modem-mbim.c
@@ -123,6 +123,11 @@ struct _MMBroadbandModemMbimPrivate {
/* For notifying when the mbim-proxy connection is dead */
gulong mbim_device_removed_id;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* Flag when QMI-based capability/mode switching is in use */
+ gboolean qmi_capability_and_mode_switching;
+#endif
};
/*****************************************************************************/
@@ -195,7 +200,22 @@ shared_qmi_peek_client (MMSharedQmi *self,
#endif
/*****************************************************************************/
-/* Current Capabilities loading (Modem interface) */
+/* Current capabilities (Modem interface) */
+
+typedef struct {
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ MMModemCapability current_qmi;
+#endif
+ MbimDevice *device;
+ MMModemCapability current_mbim;
+} LoadCurrentCapabilitiesContext;
+
+static void
+load_current_capabilities_context_free (LoadCurrentCapabilitiesContext *ctx)
+{
+ g_object_unref (ctx->device);
+ g_free (ctx);
+}
static MMModemCapability
modem_load_current_capabilities_finish (MMIfaceModem *self,
@@ -214,21 +234,71 @@ modem_load_current_capabilities_finish (MMIfaceModem *self,
}
static void
+complete_current_capabilities (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ LoadCurrentCapabilitiesContext *ctx;
+ MMModemCapability result = 0;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* Warn if the MBIM loaded capabilities isn't a subset of the QMI loaded ones */
+ if (ctx->current_qmi && ctx->current_mbim) {
+ gchar *mbim_caps_str;
+ gchar *qmi_caps_str;
+
+ mbim_caps_str = mm_common_build_capabilities_string ((const MMModemCapability *)&(ctx->current_mbim), 1);
+ qmi_caps_str = mm_common_build_capabilities_string ((const MMModemCapability *)&(ctx->current_qmi), 1);
+
+ if ((ctx->current_mbim & ctx->current_qmi) != ctx->current_mbim)
+ mm_warn ("MBIM reported current capabilities (%s) not found in QMI-over-MBIM reported ones (%s)",
+ mbim_caps_str, qmi_caps_str);
+ else
+ mm_dbg ("MBIM reported current capabilities (%s) is a subset of the QMI-over-MBIM reported ones (%s)",
+ mbim_caps_str, qmi_caps_str);
+ g_free (mbim_caps_str);
+ g_free (qmi_caps_str);
+
+ result = ctx->current_qmi;
+ self->priv->qmi_capability_and_mode_switching = TRUE;
+ } else if (ctx->current_qmi) {
+ result = ctx->current_qmi;
+ self->priv->qmi_capability_and_mode_switching = TRUE;
+ } else
+ result = ctx->current_mbim;
+
+ /* If current capabilities loading is done via QMI, we can safely assume that all the other
+ * capability and mode related operations are going to be done via QMI as well, so that we
+ * don't mix both logics */
+ if (self->priv->qmi_capability_and_mode_switching)
+ mm_info ("QMI-based capability and mode switching support enabled");
+#else
+ result = ctx->current_mbim;
+#endif
+
+ g_task_return_int (task, (gint) result);
+ g_object_unref (task);
+}
+
+static void
device_caps_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
{
- MMBroadbandModemMbim *self;
- MMModemCapability mask;
- MbimMessage *response;
- GError *error = NULL;
+ MMBroadbandModemMbim *self;
+ MbimMessage *response;
+ GError *error = NULL;
+ LoadCurrentCapabilitiesContext *ctx;
self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
response = mbim_device_command_finish (device, res, &error);
- if (response &&
- mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) &&
- mbim_message_device_caps_response_parse (
+ if (!response ||
+ !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) ||
+ !mbim_message_device_caps_response_parse (
response,
NULL, /* device_type */
&self->priv->caps_cellular_class,
@@ -243,45 +313,31 @@ device_caps_query_ready (MbimDevice *device,
&self->priv->caps_firmware_info,
&self->priv->caps_hardware_info,
&error)) {
- /* Build mask of modem capabilities */
- mask = 0;
- if (self->priv->caps_cellular_class & MBIM_CELLULAR_CLASS_GSM)
- mask |= MM_MODEM_CAPABILITY_GSM_UMTS;
-
-#if 0 /* Disable until we add MBIM CDMA support */
- if (self->priv->caps_cellular_class & MBIM_CELLULAR_CLASS_CDMA)
- mask |= MM_MODEM_CAPABILITY_CDMA_EVDO;
-#endif
-
- if (self->priv->caps_data_class & MBIM_DATA_CLASS_LTE)
- mask |= MM_MODEM_CAPABILITY_LTE;
- g_task_return_int (task, mask);
- } else
g_task_return_error (task, error);
+ g_object_unref (task);
+ goto out;
+ }
- g_object_unref (task);
+ ctx->current_mbim = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class,
+ self->priv->caps_data_class);
+ complete_current_capabilities (task);
+out:
if (response)
mbim_message_unref (response);
}
static void
-modem_load_current_capabilities (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_current_capabilities_mbim (GTask *task)
{
- MbimDevice *device;
- MbimMessage *message;
- GTask *task;
-
- if (!peek_device (self, &device, callback, user_data))
- return;
+ MbimMessage *message;
+ LoadCurrentCapabilitiesContext *ctx;
- task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_task_get_task_data (task);
mm_dbg ("loading current capabilities...");
message = mbim_message_device_caps_query_new (NULL);
- mbim_device_command (device,
+ mbim_device_command (ctx->device,
message,
10,
NULL,
@@ -290,6 +346,147 @@ modem_load_current_capabilities (MMIfaceModem *self,
mbim_message_unref (message);
}
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+
+static void
+qmi_load_current_capabilities_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ LoadCurrentCapabilitiesContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->current_qmi = mm_shared_qmi_load_current_capabilities_finish (self, res, &error);
+ if (!ctx->current_qmi) {
+ mm_dbg ("Couldn't load currrent capabilities using QMI over MBIM: %s", error->message);
+ g_clear_error (&error);
+ }
+
+ load_current_capabilities_mbim (task);
+}
+
+#endif
+
+static void
+modem_load_current_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MbimDevice *device;
+ GTask *task;
+ LoadCurrentCapabilitiesContext *ctx;
+
+ if (!peek_device (self, &device, callback, user_data))
+ return;
+
+ task = g_task_new (self, NULL, callback, user_data);
+ ctx = g_new0 (LoadCurrentCapabilitiesContext, 1);
+ ctx->device = g_object_ref (device);
+ g_task_set_task_data (task, ctx, (GDestroyNotify) load_current_capabilities_context_free);
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ mm_shared_qmi_load_current_capabilities (self,
+ (GAsyncReadyCallback)qmi_load_current_capabilities_ready,
+ task);
+#else
+ load_current_capabilities_mbim (task);
+#endif
+}
+
+/*****************************************************************************/
+/* Supported Capabilities loading (Modem interface) */
+
+static GArray *
+modem_load_supported_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_load_supported_capabilities_finish (self, res, error);
+#endif
+ return g_task_propagate_pointer (G_TASK (res), error);
+}
+
+static void
+load_supported_capabilities_mbim (GTask *task)
+{
+ MMBroadbandModemMbim *self;
+ MMModemCapability current;
+ GArray *supported = NULL;
+
+ self = g_task_get_source_object (task);
+
+ /* Current capabilities should have been cached already, just assume them */
+ current = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class, self->priv->caps_data_class);
+ if (current != 0) {
+ supported = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1);
+ g_array_append_val (supported, current);
+ }
+
+ if (!supported)
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
+ "Couldn't load supported capabilities: no previously catched current capabilities");
+ else
+ g_task_return_pointer (task, supported, (GDestroyNotify) g_array_unref);
+ g_object_unref (task);
+}
+
+static void
+modem_load_supported_capabilities (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_load_supported_capabilities (self, callback, user_data);
+ return;
+ }
+#endif
+
+ task = g_task_new (self, NULL, callback, user_data);
+ load_supported_capabilities_mbim (task);
+}
+
+/*****************************************************************************/
+/* Capabilities switching (Modem interface) */
+
+static gboolean
+modem_set_current_capabilities_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_set_current_capabilities_finish (self, res, error);
+#endif
+ g_assert (error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_set_current_capabilities (MMIfaceModem *self,
+ MMModemCapability capabilities,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_set_current_capabilities (self, capabilities, callback, user_data);
+ return;
+ }
+#endif
+
+ g_task_report_new_error (self, callback, user_data,
+ modem_set_current_capabilities,
+ MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Capability switching is not supported");
+}
+
/*****************************************************************************/
/* Manufacturer loading (Modem interface) */
@@ -492,21 +689,22 @@ modem_load_supported_modes_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_load_supported_modes_finish (self, res, error);
+#endif
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
-modem_load_supported_modes (MMIfaceModem *_self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+load_supported_modes_mbim (GTask *task)
{
- MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (_self);
- GArray *combinations;
- MMModemModeCombination mode;
- MMModemMode all;
- GTask *task;
+ MMBroadbandModemMbim *self;
+ MMModemModeCombination mode;
+ MMModemMode all;
+ GArray *supported;
- task = g_task_new (self, NULL, callback, user_data);
+ self = g_task_get_source_object (task);
if (self->priv->caps_data_class == 0) {
g_task_return_new_error (task,
@@ -543,15 +741,105 @@ modem_load_supported_modes (MMIfaceModem *_self,
all |= MM_MODEM_MODE_4G;
/* Build a mask with all supported modes */
- combinations = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
+ supported = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1);
mode.allowed = all;
mode.preferred = MM_MODEM_MODE_NONE;
- g_array_append_val (combinations, mode);
+ g_array_append_val (supported, mode);
- g_task_return_pointer (task, combinations, (GDestroyNotify)g_array_unref);
+ g_task_return_pointer (task, supported, (GDestroyNotify) g_array_unref);
g_object_unref (task);
}
+static void
+modem_load_supported_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_load_supported_modes (self, callback, user_data);
+ return;
+ }
+#endif
+
+ task = g_task_new (self, NULL, callback, user_data);
+ load_supported_modes_mbim (task);
+}
+
+/*****************************************************************************/
+/* Current modes loading (Modem interface) */
+
+static gboolean
+modem_load_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemMode *allowed,
+ MMModemMode *preferred,
+ GError **error)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_load_current_modes_finish (self, res, allowed, preferred, error);
+#endif
+ g_assert (error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_load_current_modes (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_load_current_modes (self, callback, user_data);
+ return;
+ }
+#endif
+
+ g_task_report_new_error (self, callback, user_data,
+ modem_set_current_capabilities,
+ MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Current mode loading is not supported");
+}
+
+/*****************************************************************************/
+/* Current modes switching (Modem interface) */
+
+static gboolean
+modem_set_current_modes_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_set_current_modes_finish (self, res, error);
+#endif
+ g_assert (error);
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+modem_set_current_modes (MMIfaceModem *self,
+ MMModemMode allowed,
+ MMModemMode preferred,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_set_current_modes (self, allowed, preferred, callback, user_data);
+ return;
+ }
+#endif
+
+ g_task_report_new_error (self, callback, user_data,
+ modem_set_current_capabilities,
+ MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
+ "Capability switching is not supported");
+}
+
/*****************************************************************************/
/* Load supported IP families (Modem interface) */
@@ -1898,9 +2186,10 @@ initialization_started (MMBroadbandModem *self,
#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
/* Setup services to open */
ctx->qmi_services[0] = QMI_SERVICE_DMS;
- ctx->qmi_services[1] = QMI_SERVICE_PDS;
- ctx->qmi_services[2] = QMI_SERVICE_LOC;
- ctx->qmi_services[3] = QMI_SERVICE_UNKNOWN;
+ ctx->qmi_services[1] = QMI_SERVICE_NAS;
+ ctx->qmi_services[2] = QMI_SERVICE_PDS;
+ ctx->qmi_services[3] = QMI_SERVICE_LOC;
+ ctx->qmi_services[4] = QMI_SERVICE_UNKNOWN;
#endif
/* Now open our MBIM port */
@@ -3197,6 +3486,11 @@ modem_3gpp_register_in_network_finish (MMIfaceModem3gpp *self,
GAsyncResult *res,
GError **error)
{
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching)
+ return mm_shared_qmi_3gpp_register_in_network_finish (self, res, error);
+#endif
+
return g_task_propagate_boolean (G_TASK (res), error);
}
@@ -3249,6 +3543,17 @@ modem_3gpp_register_in_network (MMIfaceModem3gpp *self,
MbimMessage *message;
GTask *task;
+#if defined WITH_QMI && QMI_MBIM_QMUX_SUPPORTED
+ /* data_class set to 0 in the MBIM register state set message ends up
+ * selecting some "auto" mode that would overwrite whatever capabilities
+ * and modes we had set. So, if we're using QMI-based capability and
+ * mode switching, also use QMI-based network registration. */
+ if (MM_BROADBAND_MODEM_MBIM (self)->priv->qmi_capability_and_mode_switching) {
+ mm_shared_qmi_3gpp_register_in_network (self, operator_id, cancellable, callback, user_data);
+ return;
+ }
+#endif
+
if (!peek_device (self, &device, callback, user_data))
return;
@@ -4444,8 +4749,12 @@ static void
iface_modem_init (MMIfaceModem *iface)
{
/* Initialization steps */
+ iface->load_supported_capabilities = modem_load_supported_capabilities;
+ iface->load_supported_capabilities_finish = modem_load_supported_capabilities_finish;
iface->load_current_capabilities = modem_load_current_capabilities;
iface->load_current_capabilities_finish = modem_load_current_capabilities_finish;
+ iface->set_current_capabilities = modem_set_current_capabilities;
+ iface->set_current_capabilities_finish = modem_set_current_capabilities_finish;
iface->load_manufacturer = modem_load_manufacturer;
iface->load_manufacturer_finish = modem_load_manufacturer_finish;
iface->load_model = modem_load_model;
@@ -4460,6 +4769,10 @@ iface_modem_init (MMIfaceModem *iface)
iface->load_device_identifier_finish = modem_load_device_identifier_finish;
iface->load_supported_modes = modem_load_supported_modes;
iface->load_supported_modes_finish = modem_load_supported_modes_finish;
+ iface->load_current_modes = modem_load_current_modes;
+ iface->load_current_modes_finish = modem_load_current_modes_finish;
+ iface->set_current_modes = modem_set_current_modes;
+ iface->set_current_modes_finish = modem_set_current_modes_finish;
iface->load_unlock_required = modem_load_unlock_required;
iface->load_unlock_required_finish = modem_load_unlock_required_finish;
iface->load_unlock_retries = modem_load_unlock_retries;
diff --git a/src/mm-modem-helpers-mbim.c b/src/mm-modem-helpers-mbim.c
index 0da5588f7..680b774b8 100644
--- a/src/mm-modem-helpers-mbim.c
+++ b/src/mm-modem-helpers-mbim.c
@@ -21,6 +21,28 @@
/*****************************************************************************/
+MMModemCapability
+mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class,
+ MbimDataClass caps_data_class)
+{
+ MMModemCapability mask = 0;
+
+ if (caps_cellular_class & MBIM_CELLULAR_CLASS_GSM)
+ mask |= MM_MODEM_CAPABILITY_GSM_UMTS;
+
+#if 0 /* Disable until we add MBIM CDMA support */
+ if (caps_cellular_class & MBIM_CELLULAR_CLASS_CDMA)
+ mask |= MM_MODEM_CAPABILITY_CDMA_EVDO;
+#endif
+
+ if (caps_data_class & MBIM_DATA_CLASS_LTE)
+ mask |= MM_MODEM_CAPABILITY_LTE;
+
+ return mask;
+}
+
+/*****************************************************************************/
+
MMModemLock
mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type)
{
diff --git a/src/mm-modem-helpers-mbim.h b/src/mm-modem-helpers-mbim.h
index 67b6b57f0..6ef51f363 100644
--- a/src/mm-modem-helpers-mbim.h
+++ b/src/mm-modem-helpers-mbim.h
@@ -24,6 +24,9 @@
/*****************************************************************************/
/* MBIM/BasicConnect to MM translations */
+MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class,
+ MbimDataClass caps_data_class);
+
MMModemLock mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type);
MMModem3gppRegistrationState mm_modem_3gpp_registration_state_from_mbim_register_state (MbimRegisterState state);