bearer-mbim: implement multiplex support

This commit is contained in:
Aleksander Morgado
2021-03-11 11:24:57 +01:00
parent 32c4b20066
commit 2d374b1c41

View File

@@ -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) {
@@ -691,6 +711,90 @@ check_disconnected_ready (MbimDevice *device,
connect_context_step (task);
}
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,
@@ -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);
}