diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-01-12 14:07:58 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2012-03-15 14:14:49 +0100 |
commit | f07101943cdd3d74fe3f2600953d6085bc670e24 (patch) | |
tree | 925953df36ba22f20cd2dd8fe606cc7e1efb562d /src | |
parent | d19cf55f8f35bd82408ecfce012cdebe0ed2183f (diff) |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-iface-modem-simple.c | 400 | ||||
-rw-r--r-- | src/mm-iface-modem.c | 47 | ||||
-rw-r--r-- | src/mm-iface-modem.h | 1 |
3 files changed, 385 insertions, 63 deletions
diff --git a/src/mm-iface-modem-simple.c b/src/mm-iface-modem-simple.c index 179b51a1..dfcc176f 100644 --- a/src/mm-iface-modem-simple.c +++ b/src/mm-iface-modem-simple.c @@ -26,6 +26,8 @@ #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-simple.h" +#include "mm-bearer-3gpp.h" +#include "mm-bearer-cdma.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 { CONNECTION_STEP_FIRST, CONNECTION_STEP_UNLOCK_CHECK, @@ -190,31 +500,34 @@ typedef struct { MMCommonConnectProperties *properties; /* Results to set */ - MMBearer *bearer; + GList *bearers; + MMBearer *connected_bearer; } ConnectionContext; static void 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->skeleton); g_object_unref (ctx->invocation); g_object_unref (ctx->self); - if (ctx->bearer) - g_object_unref (ctx->bearer); g_free (ctx); } static void connection_step (ConnectionContext *ctx); static void -connect_bearer_ready (MMBearer *bearer, - GAsyncResult *res, - ConnectionContext *ctx) +connect_3gpp_or_cdma_bearer_ready (MMIfaceModemSimple *self, + GAsyncResult *res, + ConnectionContext *ctx) { 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); connection_context_free (ctx); return; @@ -226,20 +539,21 @@ connect_bearer_ready (MMBearer *bearer, } static void -create_bearer_ready (MMIfaceModem *self, - GAsyncResult *res, - ConnectionContext *ctx) +create_3gpp_and_cdma_bearers_ready (MMIfaceModemSimple *self, + GAsyncResult *res, + ConnectionContext *ctx) { GError *error = NULL; - ctx->bearer = mm_iface_modem_create_bearer_finish (self, res, &error); - if (!ctx->bearer) { + /* List ownership for the caller */ + ctx->bearers = create_3gpp_and_cdma_bearers_finish (self, res, &error); + if (!ctx->bearers) { g_dbus_method_invocation_take_error (ctx->invocation, error); connection_context_free (ctx); return; } - /* Bearer available! */ + /* Bearers available! */ ctx->step++; connection_step (ctx); } @@ -497,40 +811,60 @@ connection_step (ConnectionContext *ctx) * So, fall down to next step */ ctx->step++; - case CONNECTION_STEP_BEARER: { - MMCommonBearerProperties *bearer_properties; - + case CONNECTION_STEP_BEARER: mm_info ("Simple connect state (%d/%d): Bearer", ctx->step, CONNECTION_STEP_LAST); - bearer_properties = (mm_common_connect_properties_get_bearer_properties ( - ctx->properties)); - mm_iface_modem_create_bearer ( - MM_IFACE_MODEM (ctx->self), - TRUE, /* Try to force bearer creation */ - bearer_properties, - (GAsyncReadyCallback)create_bearer_ready, - ctx); - g_object_unref (bearer_properties); + if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) || + mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) { + MMCommonBearerProperties *bearer_properties; + + bearer_properties = (mm_common_connect_properties_get_bearer_properties ( + ctx->properties)); + /* 3GPP and/or CDMA bearer creation */ + create_3gpp_and_cdma_bearers ( + 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; - } case CONNECTION_STEP_CONNECT: mm_info ("Simple connect state (%d/%d): Connect", ctx->step, CONNECTION_STEP_LAST); - mm_bearer_connect (ctx->bearer, - NULL, /* no number given */ - (GAsyncReadyCallback)connect_bearer_ready, - ctx); + + if (mm_iface_modem_is_3gpp (MM_IFACE_MODEM (ctx->self)) || + mm_iface_modem_is_cdma (MM_IFACE_MODEM (ctx->self))) { + /* 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; case CONNECTION_STEP_LAST: mm_info ("Simple connect state (%d/%d): All done", ctx->step, CONNECTION_STEP_LAST); /* All done, yey! */ - mm_gdbus_modem_simple_complete_connect (ctx->skeleton, - ctx->invocation, - mm_bearer_get_path (ctx->bearer)); + mm_gdbus_modem_simple_complete_connect ( + ctx->skeleton, + ctx->invocation, + mm_bearer_get_path (ctx->connected_bearer)); connection_context_free (ctx); return; } diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index 855931a3..0c9912e9 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -220,7 +220,6 @@ create_bearer_ready (MMIfaceModem *self, void mm_iface_modem_create_bearer (MMIfaceModem *self, - gboolean force, MMCommonBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data) @@ -231,33 +230,24 @@ mm_iface_modem_create_bearer (MMIfaceModem *self, MM_IFACE_MODEM_BEARER_LIST, &list, NULL); - if (mm_bearer_list_get_count (list) == mm_bearer_list_get_max (list)) { - if (!force) { - g_simple_async_report_error_in_idle ( - G_OBJECT (self), - callback, - user_data, - MM_CORE_ERROR, - MM_CORE_ERROR_TOO_MANY, - "Cannot add new bearer: already reached maximum (%u)", - mm_bearer_list_get_count (list)); - 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); - } - - 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)); + if (mm_bearer_list_get_count (list) == mm_bearer_list_get_max (list)) + g_simple_async_report_error_in_idle ( + G_OBJECT (self), + callback, + user_data, + MM_CORE_ERROR, + MM_CORE_ERROR_TOO_MANY, + "Cannot add new bearer: already reached maximum (%u)", + mm_bearer_list_get_count (list)); + else + 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); } @@ -296,7 +286,6 @@ handle_create_bearer (MmGdbusModem *skeleton, } else { mm_iface_modem_create_bearer ( self, - FALSE, /* don't force when request comes from DBus */ properties, (GAsyncReadyCallback)handle_create_bearer_ready, dbus_call_context_new (skeleton, diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 141a975a..b7ab6102 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -348,7 +348,6 @@ gboolean mm_iface_modem_set_allowed_bands_finish (MMIfaceModem *self, /* Allow creating bearers */ void mm_iface_modem_create_bearer (MMIfaceModem *self, - gboolean force, MMCommonBearerProperties *properties, GAsyncReadyCallback callback, gpointer user_data); |