aboutsummaryrefslogtreecommitdiff
path: root/src/mm-bearer-mbim.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-bearer-mbim.c')
-rw-r--r--src/mm-bearer-mbim.c239
1 files changed, 200 insertions, 39 deletions
diff --git a/src/mm-bearer-mbim.c b/src/mm-bearer-mbim.c
index 323483ce..49fd618d 100644
--- a/src/mm-bearer-mbim.c
+++ b/src/mm-bearer-mbim.c
@@ -35,9 +35,10 @@
G_DEFINE_TYPE (MMBearerMbim, mm_bearer_mbim, MM_TYPE_BASE_BEARER)
struct _MMBearerMbimPrivate {
- /* The session ID for this bearer */
- guint32 session_id;
- MMPort *data;
+ MMPortMbim *mbim;
+ MMPort *data;
+ MMPort *link;
+ guint32 session_id;
};
/*****************************************************************************/
@@ -191,10 +192,14 @@ reload_stats (MMBaseBearer *self,
/*****************************************************************************/
/* Connect */
+#define WAIT_LINK_PORT_TIMEOUT_MS 2500
+
typedef enum {
CONNECT_STEP_FIRST,
CONNECT_STEP_PACKET_SERVICE,
CONNECT_STEP_PROVISIONED_CONTEXTS,
+ CONNECT_STEP_SETUP_LINK,
+ CONNECT_STEP_SETUP_LINK_MASTER_UP,
CONNECT_STEP_CHECK_DISCONNECTED,
CONNECT_STEP_ENSURE_DISCONNECTED,
CONNECT_STEP_CONNECT,
@@ -210,14 +215,26 @@ typedef struct {
MbimContextIpType requested_ip_type;
MbimContextIpType activated_ip_type;
MMBearerConnectResult *connect_result;
+ /* multiplex support */
+ guint session_id;
+ gchar *link_prefix_hint;
+ gchar *link_name;
+ MMPort *link;
} ConnectContext;
static void
connect_context_free (ConnectContext *ctx)
{
- if (ctx->connect_result)
- mm_bearer_connect_result_unref (ctx->connect_result);
- g_object_unref (ctx->data);
+ if (ctx->link_name) {
+ mm_port_mbim_cleanup_link (ctx->mbim, ctx->link_name, NULL, NULL);
+ g_free (ctx->link_name);
+ }
+ g_clear_object (&ctx->link);
+ g_free (ctx->link_prefix_hint);
+
+ g_clear_pointer (&ctx->connect_result, (GDestroyNotify)mm_bearer_connect_result_unref);
+
+ g_clear_object (&ctx->data);
g_object_unref (ctx->properties);
g_object_unref (ctx->mbim);
g_slice_free (ConnectContext, ctx);
@@ -543,7 +560,10 @@ ip_configuration_query_ready (MbimDevice *device,
}
/* Store result */
- ctx->connect_result = mm_bearer_connect_result_new (ctx->data, ipv4_config, ipv6_config);
+ ctx->connect_result = mm_bearer_connect_result_new (ctx->link ? ctx->link : ctx->data,
+ ipv4_config,
+ ipv6_config);
+ mm_bearer_connect_result_set_multiplexed (ctx->connect_result, !!ctx->link);
}
if (error) {
@@ -692,6 +712,90 @@ check_disconnected_ready (MbimDevice *device,
}
static void
+master_interface_up_ready (MMPortNet *link,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_port_net_link_setup_finish (link, res, &error)) {
+ g_prefix_error (&error, "Couldn't bring master interface up: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+wait_link_port_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ GTask *task)
+{
+ ConnectContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ ctx->link = mm_base_modem_wait_link_port_finish (modem, res, &error);
+ if (!ctx->link) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* Keep on */
+ ctx->step++;
+ connect_context_step (task);
+}
+
+static void
+setup_link_ready (MMPortMbim *mbim,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBearerMbim *self;
+ ConnectContext *ctx;
+ GError *error = NULL;
+ g_autoptr(MMBaseModem) modem = NULL;
+
+ self = g_task_get_source_object (task);
+ ctx = g_task_get_task_data (task);
+
+ ctx->link_name = mm_port_mbim_setup_link_finish (mbim, res, &ctx->session_id, &error);
+ if (!ctx->link_name) {
+ g_prefix_error (&error, "failed to create net link for device: ");
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* From now on link_name will be set, and we'll use that to know
+ * whether we should cleanup the link upon a connection failure */
+ mm_obj_info (self, "net link %s created (session id %u)", ctx->link_name, ctx->session_id);
+
+ /* Wait for the data port with the given interface name, which will be
+ * added asynchronously */
+ g_object_get (self,
+ MM_BASE_BEARER_MODEM, &modem,
+ NULL);
+ g_assert (modem);
+
+ mm_base_modem_wait_link_port (modem,
+ "net",
+ ctx->link_name,
+ WAIT_LINK_PORT_TIMEOUT_MS,
+ (GAsyncReadyCallback) wait_link_port_ready,
+ task);
+}
+
+static void
provisioned_contexts_query_ready (MbimDevice *device,
GAsyncResult *res,
GTask *task)
@@ -852,10 +956,40 @@ connect_context_step (GTask *task)
task);
return;
+ case CONNECT_STEP_SETUP_LINK:
+ /* if a link prefix hint is available, it's because we should be doing
+ * multiplexing */
+ if (ctx->link_prefix_hint) {
+ mm_obj_dbg (self, "setting up new multiplexed link...");
+ mm_port_mbim_setup_link (ctx->mbim,
+ ctx->data,
+ ctx->link_prefix_hint,
+ (GAsyncReadyCallback) setup_link_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
+ case CONNECT_STEP_SETUP_LINK_MASTER_UP:
+ /* if the connection is done through a new link, we need to ifup the master interface */
+ if (ctx->link) {
+ mm_obj_dbg (self, "bringing master interface %s up...", mm_port_get_device (ctx->data));
+ mm_port_net_link_setup (MM_PORT_NET (ctx->data),
+ TRUE,
+ 0, /* ignore */
+ g_task_get_cancellable (task),
+ (GAsyncReadyCallback) master_interface_up_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall through */
+
case CONNECT_STEP_CHECK_DISCONNECTED:
- mm_obj_dbg (self, "checking if session is disconnected...");
+ mm_obj_dbg (self, "checking if session %u is disconnected...", ctx->session_id);
message = mbim_message_connect_query_new (
- self->priv->session_id,
+ ctx->session_id,
MBIM_ACTIVATION_STATE_UNKNOWN,
MBIM_VOICE_CALL_STATE_NONE,
MBIM_CONTEXT_IP_TYPE_DEFAULT,
@@ -871,9 +1005,9 @@ connect_context_step (GTask *task)
return;
case CONNECT_STEP_ENSURE_DISCONNECTED:
- mm_obj_dbg (self, "ensuring session is disconnected...");
+ mm_obj_dbg (self, "ensuring session %u is disconnected...", ctx->session_id);
message = mbim_message_connect_set_new (
- self->priv->session_id,
+ ctx->session_id,
MBIM_ACTIVATION_COMMAND_DEACTIVATE,
"",
"",
@@ -937,10 +1071,10 @@ connect_context_step (GTask *task)
return;
}
- mm_obj_dbg (self, "launching %s connection with APN '%s'...",
- mbim_context_ip_type_get_string (ctx->requested_ip_type), apn);
+ mm_obj_dbg (self, "launching %s connection with APN '%s' in session %u...",
+ mbim_context_ip_type_get_string (ctx->requested_ip_type), apn, ctx->session_id);
message = mbim_message_connect_set_new (
- self->priv->session_id,
+ ctx->session_id,
MBIM_ACTIVATION_COMMAND_ACTIVATE,
apn ? apn : "",
user ? user : "",
@@ -962,7 +1096,7 @@ connect_context_step (GTask *task)
case CONNECT_STEP_IP_CONFIGURATION:
mm_obj_dbg (self, "querying IP configuration...");
message = mbim_message_ip_configuration_query_new (
- self->priv->session_id,
+ ctx->session_id,
MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv4configurationavailable */
MBIM_IP_CONFIGURATION_AVAILABLE_FLAG_NONE, /* ipv6configurationavailable */
0, /* ipv4addresscount */
@@ -988,11 +1122,20 @@ connect_context_step (GTask *task)
case CONNECT_STEP_LAST:
/* Port is connected; update the state */
- mm_port_set_connected (MM_PORT (ctx->data), TRUE);
+ mm_port_set_connected (ctx->link ? ctx->link : ctx->data, TRUE);
- /* Keep the data port */
+ /* Keep connection related data */
+ g_assert (self->priv->mbim == NULL);
+ self->priv->mbim = g_object_ref (ctx->mbim);
g_assert (self->priv->data == NULL);
- self->priv->data = g_object_ref (ctx->data);
+ self->priv->data = ctx->data ? g_object_ref (ctx->data) : NULL;
+ g_assert (self->priv->link == NULL);
+ self->priv->link = ctx->link ? g_object_ref (ctx->link) : NULL;
+ g_assert (!self->priv->session_id);
+ self->priv->session_id = ctx->session_id;
+
+ /* reset the link name to avoid cleaning up the link on context free */
+ g_clear_pointer (&ctx->link_name, g_free);
/* Set operation result */
g_task_return_pointer (
@@ -1015,12 +1158,13 @@ _connect (MMBaseBearer *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
- ConnectContext *ctx;
- MMPort *data;
- MMPortMbim *mbim;
- const gchar *apn;
- GTask *task;
- g_autoptr(MMBaseModem) modem = NULL;
+ ConnectContext *ctx;
+ MMPort *data;
+ MMPortMbim *mbim;
+ const gchar *apn;
+ GTask *task;
+ MMBearerMultiplexSupport multiplex;
+ g_autoptr(MMBaseModem) modem = NULL;
if (!peek_ports (self, &mbim, &data, callback, user_data))
return;
@@ -1046,10 +1190,6 @@ _connect (MMBaseBearer *self,
return;
}
- mm_obj_dbg (self, "launching connection with data port (%s/%s)",
- mm_port_subsys_get_string (mm_port_get_subsys (data)),
- mm_port_get_device (data));
-
ctx = g_slice_new0 (ConnectContext);
ctx->mbim = g_object_ref (mbim);
ctx->data = g_object_ref (data);
@@ -1061,6 +1201,18 @@ _connect (MMBaseBearer *self,
MM_BASE_BEARER_CONFIG, &ctx->properties,
NULL);
+ multiplex = mm_bearer_properties_get_multiplex (ctx->properties);
+ if (multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUESTED ||
+ multiplex == MM_BEARER_MULTIPLEX_SUPPORT_REQUIRED) {
+ /* the link prefix hint given must be modem-specific */
+ ctx->link_prefix_hint = g_strdup_printf ("mbimmux%u.", mm_base_modem_get_dbus_id (MM_BASE_MODEM (modem)));
+ }
+
+ mm_obj_dbg (self, "launching %sconnection with data port (%s/%s)",
+ ctx->link_prefix_hint ? "multiplexed " : "",
+ mm_port_subsys_get_string (mm_port_get_subsys (data)),
+ mm_port_get_device (data));
+
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_task_data (task, ctx, (GDestroyNotify)connect_context_free);
@@ -1079,7 +1231,7 @@ typedef enum {
typedef struct {
MMPortMbim *mbim;
- MMPort *data;
+ guint session_id;
DisconnectStep step;
} DisconnectContext;
@@ -1087,7 +1239,6 @@ static void
disconnect_context_free (DisconnectContext *ctx)
{
g_object_unref (ctx->mbim);
- g_object_unref (ctx->data);
g_slice_free (DisconnectContext, ctx);
}
@@ -1106,6 +1257,19 @@ reset_bearer_connection (MMBearerMbim *self)
mm_port_set_connected (self->priv->data, FALSE);
g_clear_object (&self->priv->data);
}
+
+ if (self->priv->link) {
+ g_assert (self->priv->mbim);
+ /* Link is disconnected; update the state */
+ mm_port_set_connected (self->priv->link, FALSE);
+ mm_port_mbim_cleanup_link (self->priv->mbim,
+ mm_port_get_device (self->priv->link),
+ NULL,
+ NULL);
+ g_clear_object (&self->priv->link);
+ }
+ self->priv->session_id = 0;
+ g_clear_object (&self->priv->mbim);
}
static void disconnect_context_step (GTask *task);
@@ -1214,7 +1378,7 @@ disconnect_context_step (GTask *task)
g_autoptr(MbimMessage) message = NULL;
message = mbim_message_connect_set_new (
- self->priv->session_id,
+ ctx->session_id,
MBIM_ACTIVATION_COMMAND_DEACTIVATE,
"",
"",
@@ -1252,16 +1416,13 @@ disconnect (MMBaseBearer *_self,
gpointer user_data)
{
MMBearerMbim *self = MM_BEARER_MBIM (_self);
- MMPortMbim *mbim;
DisconnectContext *ctx;
GTask *task;
- if (!peek_ports (self, &mbim, NULL, callback, user_data))
- return;
-
task = g_task_new (self, NULL, callback, user_data);
- if (!self->priv->data) {
+ if ((!self->priv->data && !self->priv->link) ||
+ !self->priv->mbim) {
mm_obj_dbg (self, "no need to disconnect: MBIM bearer is already disconnected");
g_task_return_boolean (task, TRUE);
g_object_unref (task);
@@ -1273,8 +1434,8 @@ disconnect (MMBaseBearer *_self,
mm_port_get_device (self->priv->data));
ctx = g_slice_new0 (DisconnectContext);
- ctx->mbim = g_object_ref (mbim);
- ctx->data = g_object_ref (self->priv->data);
+ ctx->mbim = g_object_ref (self->priv->mbim);
+ ctx->session_id = self->priv->session_id;
ctx->step = DISCONNECT_STEP_FIRST;
g_task_set_task_data (task, ctx, (GDestroyNotify)disconnect_context_free);
@@ -1341,7 +1502,7 @@ dispose (GObject *object)
{
MMBearerMbim *self = MM_BEARER_MBIM (object);
- g_clear_object (&self->priv->data);
+ reset_bearer_connection (self);
G_OBJECT_CLASS (mm_bearer_mbim_parent_class)->dispose (object);
}