iface-modem-simple: new connection logic when more than one bearer around

For mixed 3GPP+CDMA modems, we first try to connect the 3GPP bearers, and then
the CDMA ones.
This commit is contained in:
Aleksander Morgado
2012-01-12 14:07:58 +01:00
parent d19cf55f8f
commit f07101943c
3 changed files with 385 additions and 63 deletions

View File

@@ -26,6 +26,8 @@
#include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp.h"
#include "mm-iface-modem-cdma.h" #include "mm-iface-modem-cdma.h"
#include "mm-iface-modem-simple.h" #include "mm-iface-modem-simple.h"
#include "mm-bearer-3gpp.h"
#include "mm-bearer-cdma.h"
#include "mm-log.h" #include "mm-log.h"
/*****************************************************************************/ /*****************************************************************************/
@@ -168,6 +170,314 @@ register_in_3gpp_or_cdma_network (MMIfaceModemSimple *self,
/*****************************************************************************/ /*****************************************************************************/
typedef struct {
GSimpleAsyncResult *result;
MMIfaceModemSimple *self;
MMCommonBearerProperties *bearer_properties;
gboolean create_cdma_bearer;
gboolean create_3gpp_bearer;
GList *list;
} CreateBearersContext;
static void
create_bearers_context_complete_and_free (CreateBearersContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
g_list_free_full (ctx->list, (GDestroyNotify)g_object_unref);
g_object_unref (ctx->bearer_properties);
g_object_unref (ctx->result);
g_object_unref (ctx->self);
g_free (ctx);
}
static GList *
create_3gpp_and_cdma_bearers_finish (MMIfaceModemSimple *self,
GAsyncResult *res,
GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
/* We return the list itself. Note that there was no GDestroyNotify given when
* the result was set, as we know that this finish() is always executed */
return (GList *) g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
}
static void create_next_bearer (CreateBearersContext *ctx);
static void
create_bearer_ready (MMIfaceModem *self,
GAsyncResult *res,
CreateBearersContext *ctx)
{
GError *error = NULL;
MMBearer *bearer;
bearer = mm_iface_modem_create_bearer_finish (self, res, &error);
if (!bearer) {
g_simple_async_result_take_error (ctx->result, error);
create_bearers_context_complete_and_free (ctx);
return;
}
/* Keep the new bearer */
ctx->list = g_list_prepend (ctx->list, bearer);
/* And see if we need to create a new one */
create_next_bearer (ctx);
}
static void
create_next_bearer (CreateBearersContext *ctx)
{
/* Create 3GPP bearer if needed */
if (ctx->create_3gpp_bearer) {
ctx->create_3gpp_bearer = FALSE;
mm_iface_modem_create_bearer (MM_IFACE_MODEM (ctx->self),
ctx->bearer_properties,
(GAsyncReadyCallback)create_bearer_ready,
ctx);
return;
}
/* Create CDMA bearer if needed */
if (ctx->create_cdma_bearer) {
MMCommonBearerProperties *cdma_properties = NULL;
ctx->create_cdma_bearer = FALSE;
/* If the bearer properties has 'apn', we need to remove that before
* trying to create the bearer. */
if (mm_common_bearer_properties_get_apn (ctx->bearer_properties)) {
cdma_properties = mm_common_bearer_properties_dup (ctx->bearer_properties);
mm_common_bearer_properties_set_apn (cdma_properties, NULL);
}
mm_iface_modem_create_bearer (
MM_IFACE_MODEM (ctx->self),
(cdma_properties ? cdma_properties : ctx->bearer_properties),
(GAsyncReadyCallback)create_bearer_ready,
ctx);
if (cdma_properties)
g_object_unref (cdma_properties);
return;
}
/* If no more bearers to create, we're done.
* NOTE: we won't provide a GDestroyNotify to clear the gpointer passed as
* result, as we know that finish() will ALWAYS be executed. */
g_assert (ctx->list != NULL);
g_simple_async_result_set_op_res_gpointer (ctx->result, ctx->list, NULL);
ctx->list = NULL;
create_bearers_context_complete_and_free (ctx);
}
static void
create_3gpp_and_cdma_bearers (MMIfaceModemSimple *self,
MMCommonBearerProperties *bearer_properties,
GAsyncReadyCallback callback,
gpointer user_data)
{
guint n_bearers_needed;
MMBearerList *list = NULL;
CreateBearersContext *ctx;
/* The implementation of this async method requires a valid callback, so
* that we ensure that finish() will always be called. */
g_assert (callback != NULL);
ctx = g_new0 (CreateBearersContext, 1);
ctx->self = g_object_ref (self);
ctx->bearer_properties = g_object_ref (bearer_properties);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
create_3gpp_and_cdma_bearers);
/* 3GPP-only modems... */
if (mm_iface_modem_is_3gpp_only (MM_IFACE_MODEM (ctx->self)))
ctx->create_3gpp_bearer = TRUE;
/* CDMA-only modems... */
else if (mm_iface_modem_is_cdma_only (MM_IFACE_MODEM (ctx->self)))
ctx->create_cdma_bearer = TRUE;
/* Mixed CDMA+3GPP(LTE) modems */
else {
/* If we have APN, we'll create both 3GPP and CDMA bearers.
* Otherwise we'll only create a CDMA bearer. */
if (mm_common_bearer_properties_get_apn (ctx->bearer_properties)) {
ctx->create_3gpp_bearer = TRUE;
}
ctx->create_cdma_bearer = TRUE;
}
n_bearers_needed = ctx->create_3gpp_bearer + ctx->create_cdma_bearer;
if (n_bearers_needed == 0)
g_assert_not_reached ();
g_object_get (self,
MM_IFACE_MODEM_BEARER_LIST, &list,
NULL);
/* TODO: check if the bearers we want to create are already in the list */
/* If we don't have enough space to create all needed bearers, try to remove
* all existing ones first BUT only if that will give us enough space. */
if (mm_bearer_list_get_max (list) < (mm_bearer_list_get_count (list) +
n_bearers_needed)) {
if (mm_bearer_list_get_max (list) < n_bearers_needed) {
g_simple_async_result_set_error (
ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_TOO_MANY,
"Cannot create bearers: need %u but only %u allowed",
n_bearers_needed,
mm_bearer_list_get_max (list));
create_bearers_context_complete_and_free (ctx);
g_object_unref (list);
return;
}
/* We are told to force the creation of the new bearer.
* We'll remove all existing bearers, and then go on creating the new one */
mm_bearer_list_delete_all_bearers (list);
}
create_next_bearer (ctx);
g_object_unref (list);
}
/*****************************************************************************/
typedef struct {
MMIfaceModemSimple *self;
GSimpleAsyncResult *result;
GList *bearers;
MMBearer *current;
} ConnectBearerContext;
static void
connect_bearer_context_complete_and_free (ConnectBearerContext *ctx)
{
g_simple_async_result_complete_in_idle (ctx->result);
if (ctx->current)
g_object_unref (ctx->current);
g_list_free_full (ctx->bearers, (GDestroyNotify)g_object_unref);
g_object_unref (ctx->result);
g_object_unref (ctx->self);
g_free (ctx);
}
static MMBearer *
connect_3gpp_or_cdma_bearer_finish (MMIfaceModemSimple *self,
GAsyncResult *res,
GError **error)
{
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return NULL;
return MM_BEARER (g_object_ref (g_simple_async_result_get_op_res_gpointer (
G_SIMPLE_ASYNC_RESULT (res))));
}
static void connect_next_bearer (ConnectBearerContext *ctx);
static void
connect_bearer_ready (MMBearer *bearer,
GAsyncResult *res,
ConnectBearerContext *ctx)
{
GError *error = NULL;
if (!mm_bearer_connect_finish (bearer, res, &error)) {
mm_dbg ("Couldn't connect bearer: '%s'", error->message);
g_error_free (error);
/* We'll try with the next one */
connect_next_bearer (ctx);
return;
}
/* We got connected! */
g_simple_async_result_set_op_res_gpointer (ctx->result,
g_object_ref (ctx->current),
(GDestroyNotify)g_object_unref);
connect_bearer_context_complete_and_free (ctx);
}
static void
connect_next_bearer (ConnectBearerContext *ctx)
{
GList *l;
g_clear_object (&ctx->current);
/* First, look for 3GPP bearers */
for (l = ctx->bearers; l; l = g_list_next (l)) {
if (MM_IS_BEARER_3GPP (l->data)) {
/* Try to connect the current bearer. If the modem is not yet
* registered in the 3GPP network, connection won't succeed.
* Steal the reference from the list. */
ctx->current = MM_BEARER (l->data);
ctx->bearers = g_list_delete_link (ctx->bearers, l);
mm_bearer_connect (MM_BEARER (ctx->current),
NULL, /* no number given */
(GAsyncReadyCallback)connect_bearer_ready,
ctx);
return;
}
}
/* Then, we look for CDMA bearers */
for (l = ctx->bearers; l; l = g_list_next (l)) {
if (MM_IS_BEARER_CDMA (l->data)) {
/* Try to connect the current bearer. If the modem is not yet
* registered in the 3GPP network, connection won't succeed.
* Steal the reference from the list. */
ctx->current = MM_BEARER (l->data);
ctx->bearers = g_list_delete_link (ctx->bearers, l);
mm_bearer_connect (MM_BEARER (ctx->current),
NULL, /* no number given */
(GAsyncReadyCallback)connect_bearer_ready,
ctx);
return;
}
}
/* Here we shouldn't have any remaining bearer.
* POTS modem not yet supported */
/* If we got here, we didn't get connected :-/ */
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_UNAUTHORIZED,
"Cannot connect any bearer");
connect_bearer_context_complete_and_free (ctx);
}
static void
connect_3gpp_or_cdma_bearer (MMIfaceModemSimple *self,
GList *bearers,
GAsyncReadyCallback callback,
gpointer user_data)
{
ConnectBearerContext *ctx;
g_assert (bearers != NULL);
ctx = g_new0 (ConnectBearerContext, 1);
ctx->self = g_object_ref (self);
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
connect_3gpp_or_cdma_bearer);
ctx->bearers = g_list_copy (bearers);
g_list_foreach (ctx->bearers, (GFunc)g_object_ref, NULL);
connect_next_bearer (ctx);
}
/*****************************************************************************/
typedef enum { typedef enum {
CONNECTION_STEP_FIRST, CONNECTION_STEP_FIRST,
CONNECTION_STEP_UNLOCK_CHECK, CONNECTION_STEP_UNLOCK_CHECK,
@@ -190,31 +500,34 @@ typedef struct {
MMCommonConnectProperties *properties; MMCommonConnectProperties *properties;
/* Results to set */ /* Results to set */
MMBearer *bearer; GList *bearers;
MMBearer *connected_bearer;
} ConnectionContext; } ConnectionContext;
static void static void
connection_context_free (ConnectionContext *ctx) connection_context_free (ConnectionContext *ctx)
{ {
g_list_free_full (ctx->bearers, (GDestroyNotify)g_object_unref);
if (ctx->connected_bearer)
g_object_unref (ctx->connected_bearer);
g_object_unref (ctx->properties); g_object_unref (ctx->properties);
g_object_unref (ctx->skeleton); g_object_unref (ctx->skeleton);
g_object_unref (ctx->invocation); g_object_unref (ctx->invocation);
g_object_unref (ctx->self); g_object_unref (ctx->self);
if (ctx->bearer)
g_object_unref (ctx->bearer);
g_free (ctx); g_free (ctx);
} }
static void connection_step (ConnectionContext *ctx); static void connection_step (ConnectionContext *ctx);
static void static void
connect_bearer_ready (MMBearer *bearer, connect_3gpp_or_cdma_bearer_ready (MMIfaceModemSimple *self,
GAsyncResult *res, GAsyncResult *res,
ConnectionContext *ctx) ConnectionContext *ctx)
{ {
GError *error = NULL; GError *error = NULL;
if (!mm_bearer_connect_finish (bearer, res, &error)) { ctx->connected_bearer = connect_3gpp_or_cdma_bearer_finish (self, res, &error);
if (!ctx->connected_bearer) {
g_dbus_method_invocation_take_error (ctx->invocation, error); g_dbus_method_invocation_take_error (ctx->invocation, error);
connection_context_free (ctx); connection_context_free (ctx);
return; return;
@@ -226,20 +539,21 @@ connect_bearer_ready (MMBearer *bearer,
} }
static void static void
create_bearer_ready (MMIfaceModem *self, create_3gpp_and_cdma_bearers_ready (MMIfaceModemSimple *self,
GAsyncResult *res, GAsyncResult *res,
ConnectionContext *ctx) ConnectionContext *ctx)
{ {
GError *error = NULL; GError *error = NULL;
ctx->bearer = mm_iface_modem_create_bearer_finish (self, res, &error); /* List ownership for the caller */
if (!ctx->bearer) { ctx->bearers = create_3gpp_and_cdma_bearers_finish (self, res, &error);
if (!ctx->bearers) {
g_dbus_method_invocation_take_error (ctx->invocation, error); g_dbus_method_invocation_take_error (ctx->invocation, error);
connection_context_free (ctx); connection_context_free (ctx);
return; return;
} }
/* Bearer available! */ /* Bearers available! */
ctx->step++; ctx->step++;
connection_step (ctx); connection_step (ctx);
} }
@@ -497,40 +811,60 @@ connection_step (ConnectionContext *ctx)
* So, fall down to next step */ * So, fall down to next step */
ctx->step++; ctx->step++;
case CONNECTION_STEP_BEARER: { case CONNECTION_STEP_BEARER:
MMCommonBearerProperties *bearer_properties;
mm_info ("Simple connect state (%d/%d): Bearer", mm_info ("Simple connect state (%d/%d): Bearer",
ctx->step, CONNECTION_STEP_LAST); ctx->step, CONNECTION_STEP_LAST);
bearer_properties = (mm_common_connect_properties_get_bearer_properties ( if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) ||
ctx->properties)); mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
mm_iface_modem_create_bearer ( MMCommonBearerProperties *bearer_properties;
MM_IFACE_MODEM (ctx->self),
TRUE, /* Try to force bearer creation */ bearer_properties = (mm_common_connect_properties_get_bearer_properties (
bearer_properties, ctx->properties));
(GAsyncReadyCallback)create_bearer_ready, /* 3GPP and/or CDMA bearer creation */
ctx); create_3gpp_and_cdma_bearers (
g_object_unref (bearer_properties); ctx->self,
bearer_properties,
(GAsyncReadyCallback)create_3gpp_and_cdma_bearers_ready,
ctx);
g_object_unref (bearer_properties);
return;
}
/* If not 3GPP and not CDMA, this will possibly be a POTS modem,
* currently unsupported. So, just abort. */
g_assert_not_reached ();
return; return;
}
case CONNECTION_STEP_CONNECT: case CONNECTION_STEP_CONNECT:
mm_info ("Simple connect state (%d/%d): Connect", mm_info ("Simple connect state (%d/%d): Connect",
ctx->step, CONNECTION_STEP_LAST); ctx->step, CONNECTION_STEP_LAST);
mm_bearer_connect (ctx->bearer,
NULL, /* no number given */ if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) ||
(GAsyncReadyCallback)connect_bearer_ready, mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) {
ctx); /* 3GPP or CDMA bearer connection */
connect_3gpp_or_cdma_bearer (
ctx->self,
ctx->bearers,
(GAsyncReadyCallback)connect_3gpp_or_cdma_bearer_ready,
ctx);
return;
}
/* If not 3GPP and not CDMA, this will possibly be a POTS modem,
* currently unsupported. So, just abort. */
g_assert_not_reached ();
return; return;
case CONNECTION_STEP_LAST: case CONNECTION_STEP_LAST:
mm_info ("Simple connect state (%d/%d): All done", mm_info ("Simple connect state (%d/%d): All done",
ctx->step, CONNECTION_STEP_LAST); ctx->step, CONNECTION_STEP_LAST);
/* All done, yey! */ /* All done, yey! */
mm_gdbus_modem_simple_complete_connect (ctx->skeleton, mm_gdbus_modem_simple_complete_connect (
ctx->invocation, ctx->skeleton,
mm_bearer_get_path (ctx->bearer)); ctx->invocation,
mm_bearer_get_path (ctx->connected_bearer));
connection_context_free (ctx); connection_context_free (ctx);
return; return;
} }

View File

@@ -220,7 +220,6 @@ create_bearer_ready (MMIfaceModem *self,
void void
mm_iface_modem_create_bearer (MMIfaceModem *self, mm_iface_modem_create_bearer (MMIfaceModem *self,
gboolean force,
MMCommonBearerProperties *properties, MMCommonBearerProperties *properties,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data) gpointer user_data)
@@ -231,33 +230,24 @@ mm_iface_modem_create_bearer (MMIfaceModem *self,
MM_IFACE_MODEM_BEARER_LIST, &list, MM_IFACE_MODEM_BEARER_LIST, &list,
NULL); NULL);
if (mm_bearer_list_get_count (list) == mm_bearer_list_get_max (list)) { if (mm_bearer_list_get_count (list) == mm_bearer_list_get_max (list))
if (!force) { g_simple_async_report_error_in_idle (
g_simple_async_report_error_in_idle ( G_OBJECT (self),
G_OBJECT (self), callback,
callback, user_data,
user_data, MM_CORE_ERROR,
MM_CORE_ERROR, MM_CORE_ERROR_TOO_MANY,
MM_CORE_ERROR_TOO_MANY, "Cannot add new bearer: already reached maximum (%u)",
"Cannot add new bearer: already reached maximum (%u)", mm_bearer_list_get_count (list));
mm_bearer_list_get_count (list)); else
g_object_unref (list); MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer (
return; self,
} properties,
(GAsyncReadyCallback)create_bearer_ready,
/* We are told to force the creation of the new bearer. g_simple_async_result_new (G_OBJECT (self),
* We'll remove all existing bearers, and then go on creating the new one */ callback,
mm_bearer_list_delete_all_bearers (list); user_data,
} mm_iface_modem_create_bearer));
MM_IFACE_MODEM_GET_INTERFACE (self)->create_bearer (
self,
properties,
(GAsyncReadyCallback)create_bearer_ready,
g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
mm_iface_modem_create_bearer));
g_object_unref (list); g_object_unref (list);
} }
@@ -296,7 +286,6 @@ handle_create_bearer (MmGdbusModem *skeleton,
} else { } else {
mm_iface_modem_create_bearer ( mm_iface_modem_create_bearer (
self, self,
FALSE, /* don't force when request comes from DBus */
properties, properties,
(GAsyncReadyCallback)handle_create_bearer_ready, (GAsyncReadyCallback)handle_create_bearer_ready,
dbus_call_context_new (skeleton, dbus_call_context_new (skeleton,

View File

@@ -348,7 +348,6 @@ gboolean mm_iface_modem_set_allowed_bands_finish (MMIfaceModem *self,
/* Allow creating bearers */ /* Allow creating bearers */
void mm_iface_modem_create_bearer (MMIfaceModem *self, void mm_iface_modem_create_bearer (MMIfaceModem *self,
gboolean force,
MMCommonBearerProperties *properties, MMCommonBearerProperties *properties,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
gpointer user_data); gpointer user_data);