aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2013-04-09 12:43:27 +0200
committerAleksander Morgado <aleksander@lanedo.com>2013-04-09 12:45:44 +0200
commit7f54d09c28e21a856166a85e822159c64300e13b (patch)
tree4edf845218645de1db76b5befe3ae388b381374f
parent0f63737460a99dd91f9e177568f83f61c779ef6e (diff)
infineon: custom 3GPP dialling logicaleksander/infineon
-rw-r--r--plugins/infineon/mm-broadband-bearer-infineon.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/plugins/infineon/mm-broadband-bearer-infineon.c b/plugins/infineon/mm-broadband-bearer-infineon.c
index 287846b7..a97d6ef0 100644
--- a/plugins/infineon/mm-broadband-bearer-infineon.c
+++ b/plugins/infineon/mm-broadband-bearer-infineon.c
@@ -33,6 +33,482 @@
G_DEFINE_TYPE (MMBroadbandBearerInfineon, mm_broadband_bearer_infineon, MM_TYPE_BROADBAND_BEARER)
+struct _MMBroadbandBearerInfineonPrivate {
+ MMBearerIpConfig *current_ip_config;
+};
+
+/*****************************************************************************/
+/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
+
+typedef struct {
+ GSimpleAsyncResult *result;
+ MMBroadbandBearerInfineon *self;
+ MMBaseModem *modem;
+ MMAtSerialPort *primary;
+ guint cid;
+ GCancellable *cancellable;
+ gboolean close_data_on_exit;
+ MMBearerIpConfig *ip_config;
+} Dial3gppContext;
+
+static void
+dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
+{
+ g_simple_async_result_complete_in_idle (ctx->result);
+ if (ctx->close_data_on_exit)
+ mm_serial_port_close (MM_SERIAL_PORT (ctx->primary));
+ if (ctx->ip_config)
+ g_object_unref (ctx->ip_config);
+ g_object_unref (ctx->cancellable);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->primary);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_slice_free (Dial3gppContext, ctx);
+}
+
+static gboolean
+dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx,
+ GError **error)
+{
+ if (!g_cancellable_is_cancelled (ctx->cancellable))
+ return FALSE;
+
+ g_set_error (error,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_CANCELLED,
+ "Dial operation has been cancelled");
+ return TRUE;
+}
+
+static gboolean
+dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error))
+ return FALSE;
+
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return TRUE;
+}
+
+static MMPort *
+dial_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return NULL;
+
+ return MM_PORT (g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res))));
+}
+
+static gboolean
+parse_xdns_query_response (const gchar *reply,
+ guint *cid,
+ gchar **dns1,
+ gchar **dns2)
+{
+ GRegex *r;
+ GMatchInfo *match_info;
+ gboolean success = FALSE;
+
+ *dns1 = NULL;
+ *dns2 = NULL;
+
+ r = g_regex_new ("\\+XDNS:\\s*(\\d+)\\s*,\\s*\"(.*)\"\\s*,\\s*\"(.*)\"",
+ G_REGEX_OPTIMIZE | G_REGEX_RAW,
+ 0, NULL);
+ g_assert (r != NULL);
+
+ if (g_regex_match (r, reply, 0, &match_info)) {
+ if (mm_get_uint_from_match_info (match_info, 1, cid) &&
+ (*dns1 = mm_get_string_unquoted_from_match_info (match_info, 2)) != NULL &&
+ (*dns2 = mm_get_string_unquoted_from_match_info (match_info, 3)) != NULL)
+ success = TRUE;
+ }
+ g_match_info_free (match_info);
+ g_regex_unref (r);
+
+ if (!success) {
+ g_free (*dns1);
+ g_free (*dns2);
+ }
+
+ return success;
+}
+
+static void
+data_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ GError *error = NULL;
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Don't close on exit */
+ ctx->close_data_on_exit = FALSE;
+
+ /* Store the IP config */
+ g_warn_if_fail (ctx->self->priv->current_ip_config == NULL);
+ ctx->self->priv->current_ip_config = g_object_ref (ctx->ip_config);
+
+ /* TODO: setup TUN/TAP? */
+
+ g_simple_async_result_set_op_res_gpointer (ctx->result,
+ g_object_ref (ctx->primary),
+ g_object_unref);
+ dial_3gpp_context_complete_and_free (ctx);
+}
+
+static void
+dns_info_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+ gchar *command;
+ guint cid;
+ gchar *dns[3];
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (!response) {
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Parse response */
+ if (!parse_xdns_query_response (response, &cid, &dns[0], &dns[1])) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Error parsing +XDNS response: '%s'",
+ response);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_warn_if_fail (cid == ctx->cid);
+
+ dns[2] = NULL;
+ mm_bearer_ip_config_set_dns (ctx->ip_config, (const gchar **)dns);
+
+ /* Success, launch data */
+ command = g_strdup_printf ("AT+CGDATA=\"M-RAW_IP\",%u", ctx->cid);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)data_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+ip_info_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ const gchar *response;
+ GError *error = NULL;
+ guint cid;
+ gchar *ip;
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ response = mm_base_modem_at_command_full_finish (modem, res, &error);
+ if (!response) {
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Parse response */
+ if (!mm_3gpp_parse_cgpaddr_write_response (response, &cid, &ip)) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Error parsing +CGPADDR response: '%s'",
+ response);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ g_warn_if_fail (cid == ctx->cid);
+
+ /* Create IP config */
+ ctx->ip_config = mm_bearer_ip_config_new ();
+ mm_bearer_ip_config_set_method (ctx->ip_config, MM_BEARER_IP_METHOD_STATIC);
+ mm_bearer_ip_config_set_address (ctx->ip_config, ip);
+ mm_bearer_ip_config_set_prefix (ctx->ip_config, 0);
+ g_free (ip);
+
+ /* Success, query DNS addresses */
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ "AT+XDNS?",
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)dns_info_ready,
+ ctx);
+}
+
+static void
+activate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ GError *error = NULL;
+ gchar *command;
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Success, query address */
+ command = g_strdup_printf ("AT+CGPADDR=%u", ctx->cid);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)ip_info_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+ip_type_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ GError *error = NULL;
+ gchar *command;
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Success, activate context */
+ command = g_strdup_printf ("AT+CGACT=1,%u", ctx->cid);
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)activate_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+authenticate_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ Dial3gppContext *ctx)
+{
+ GError *error = NULL;
+ gchar *command;
+
+ /* If cancelled, complete */
+ if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
+ return;
+
+ if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ dial_3gpp_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Success, select IP type */
+ switch (mm_bearer_properties_get_ip_type (mm_bearer_peek_config (MM_BEARER (ctx->self)))) {
+ case MM_BEARER_IP_FAMILY_IPV4:
+ command = g_strdup_printf ("AT+XDNS=%u,1", ctx->cid);
+ break;
+ case MM_BEARER_IP_FAMILY_IPV6:
+ command = g_strdup_printf ("AT+XDNS=%u,2", ctx->cid);
+ break;
+ case MM_BEARER_IP_FAMILY_IPV4V6:
+ command = g_strdup_printf ("AT+XDNS=%u,3", ctx->cid);
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)ip_type_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+authenticate (Dial3gppContext *ctx)
+{
+ gchar *command;
+ const gchar *user;
+ const gchar *password;
+
+ user = mm_bearer_properties_get_user (mm_bearer_peek_config (MM_BEARER (ctx->self)));
+ password = mm_bearer_properties_get_password (mm_bearer_peek_config (MM_BEARER (ctx->self)));
+
+ /* Both user and password are required; otherwise firmware returns an error */
+ if (user || password) {
+ gchar *encoded_user;
+ gchar *encoded_password;
+
+ encoded_user = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (ctx->modem),
+ g_strdup (user));
+ encoded_password = mm_broadband_modem_take_and_convert_to_current_charset (MM_BROADBAND_MODEM (ctx->modem),
+ g_strdup (password));
+
+ command = g_strdup_printf ("AT+XGAUTH=%u,1,\"%s\",\"%s\"",
+ ctx->cid,
+ encoded_user ? encoded_user : "",
+ encoded_password ? encoded_password : "");
+ g_free (encoded_user);
+ g_free (encoded_password);
+ } else
+ command = g_strdup ("AT+XGAUTH=%u,0,\"\",\"\"");
+
+ mm_base_modem_at_command_full (ctx->modem,
+ ctx->primary,
+ command,
+ 3,
+ FALSE,
+ FALSE, /* raw */
+ NULL, /* cancellable */
+ (GAsyncReadyCallback)authenticate_ready,
+ ctx);
+ g_free (command);
+}
+
+static void
+dial_3gpp (MMBroadbandBearer *self,
+ MMBaseModem *modem,
+ MMAtSerialPort *primary,
+ guint cid,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Dial3gppContext *ctx;
+
+ g_assert (primary != NULL);
+
+ ctx = g_slice_new0 (Dial3gppContext);
+ ctx->self = g_object_ref (self);
+ ctx->modem = g_object_ref (modem);
+ ctx->primary = g_object_ref (primary);
+ ctx->cid = cid;
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ dial_3gpp);
+ ctx->cancellable = g_object_ref (cancellable);
+
+ /* Use primary */
+ mm_serial_port_open (MM_SERIAL_PORT (ctx->primary), NULL);
+ ctx->close_data_on_exit = TRUE;
+
+ authenticate (ctx);
+}
+
+/*****************************************************************************/
+/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */
+
+static gboolean
+get_ip_config_3gpp_finish (MMBroadbandBearer *self,
+ GAsyncResult *res,
+ MMBearerIpConfig **ipv4_config,
+ MMBearerIpConfig **ipv6_config,
+ GError **error)
+{
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ return FALSE;
+
+ *ipv4_config = g_object_ref (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ /* TODO: clearly not ok. How do we get IPv6? */
+ *ipv6_config = NULL;
+ return TRUE;
+}
+
+static void
+get_ip_config_3gpp (MMBroadbandBearer *_self,
+ MMBroadbandModem *modem,
+ MMAtSerialPort *primary,
+ MMAtSerialPort *secondary,
+ MMPort *data,
+ guint cid,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandBearerInfineon *self = MM_BROADBAND_BEARER_INFINEON (_self);
+ GSimpleAsyncResult *result;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ get_ip_config_3gpp);
+ if (!self->priv->current_ip_config)
+ g_simple_async_result_set_error (result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "No current IP config found");
+ else
+ g_simple_async_result_set_op_res_gpointer (result,
+ self->priv->current_ip_config,
+ g_object_unref);
+
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+}
+
/*****************************************************************************/
MMBearer *
@@ -76,9 +552,35 @@ mm_broadband_bearer_infineon_new (MMBroadbandModemInfineon *modem,
static void
mm_broadband_bearer_infineon_init (MMBroadbandBearerInfineon *self)
{
+ /* Initialize private data */
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
+ MM_TYPE_BROADBAND_BEARER_INFINEON,
+ MMBroadbandBearerInfineonPrivate);
+}
+
+static void
+dispose (GObject *object)
+{
+ MMBroadbandBearerInfineon *self = MM_BROADBAND_BEARER_INFINEON (object);
+
+ g_clear_object (&self->priv->current_ip_config);
+
+ G_OBJECT_CLASS (mm_broadband_bearer_infineon_parent_class)->dispose (object);
}
static void
mm_broadband_bearer_infineon_class_init (MMBroadbandBearerInfineonClass *klass)
{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (MMBroadbandBearerInfineonPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+
+ broadband_bearer_class->dial_3gpp = dial_3gpp;
+ broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
+ broadband_bearer_class->get_ip_config_3gpp = get_ip_config_3gpp;
+ broadband_bearer_class->get_ip_config_3gpp_finish = get_ip_config_3gpp_finish;
}