diff options
author | Paul Bartell <p.bartell@temperednetworks.com> | 2013-04-09 10:48:17 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2017-06-14 10:59:25 +0200 |
commit | 5a023d153c95d2f104dbdfcaa919ebf2f5a85221 (patch) | |
tree | 42d0612f74e1015cb6e953ec79da0b83b61a8ffc | |
parent | 75e8527987c1e225c1861a659206630305b2f31b (diff) | |
download | ModemManager-5a023d153c95d2f104dbdfcaa919ebf2f5a85221.tar.gz |
infineon: new pluginpaul/infineon
Signed-off-by: Paul Bartell <p.bartell@temperednetworks.com>
-rw-r--r-- | plugins/Makefile.am | 46 | ||||
-rw-r--r-- | plugins/infineon/77-mm-infineon-port-types.rules | 18 | ||||
-rw-r--r-- | plugins/infineon/mm-broadband-bearer-infineon.c | 874 | ||||
-rw-r--r-- | plugins/infineon/mm-broadband-bearer-infineon.h | 59 | ||||
-rw-r--r-- | plugins/infineon/mm-broadband-modem-infineon.c | 216 | ||||
-rw-r--r-- | plugins/infineon/mm-broadband-modem-infineon.h | 47 | ||||
-rw-r--r-- | plugins/infineon/mm-modem-helpers-infineon.c | 204 | ||||
-rw-r--r-- | plugins/infineon/mm-modem-helpers-infineon.h | 33 | ||||
-rw-r--r-- | plugins/infineon/mm-plugin-infineon.c | 88 | ||||
-rw-r--r-- | plugins/infineon/mm-plugin-infineon.h | 46 | ||||
-rw-r--r-- | plugins/infineon/tests/test-modem-helpers-infineon.c | 215 |
11 files changed, 1846 insertions, 0 deletions
diff --git a/plugins/Makefile.am b/plugins/Makefile.am index d3575522c..620f0d522 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -759,6 +759,52 @@ libmm_plugin_via_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) libmm_plugin_via_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) ################################################################################ +# plugin: Infineon +################################################################################ + +PLUGIN_INFINEON_COMPILER_FLAGS = \ + -I$(top_srcdir)/plugins/infineon \ + -I$(top_builddir)/plugins/infineon \ + $(NULL) + +noinst_LTLIBRARIES += libhelpers-infineon.la + +libhelpers_infineon_la_SOURCES = \ + infineon/mm-modem-helpers-infineon.c \ + infineon/mm-modem-helpers-infineon.h \ + $(NULL) + +libhelpers_infineon_la_CPPFLAGS = $(PLUGIN_INFINEON_COMPILER_FLAGS) + +dist_udevrules_DATA += infineon/77-mm-infineon-port-types.rules + +noinst_PROGRAMS += test-modem-helpers-infineon +test_modem_helpers_infineon_CPPFLAGS = $(PLUGIN_INFINEON_COMPILER_FLAGS) +test_modem_helpers_infineon_SOURCES = \ + infineon/tests/test-modem-helpers-infineon.c \ + $(NULL) + +test_modem_helpers_infineon_LDADD = \ + $(builddir)/libhelpers-infineon.la \ + $(top_builddir)/src/libhelpers.la \ + $(top_builddir)/libmm-glib/libmm-glib.la \ + $(NULL) + +pkglib_LTLIBRARIES += libmm-plugin-infineon.la +libmm_plugin_infineon_la_SOURCES = \ + infineon/mm-plugin-infineon.c \ + infineon/mm-plugin-infineon.h \ + infineon/mm-broadband-modem-infineon.c \ + infineon/mm-broadband-modem-infineon.h \ + infineon/mm-broadband-bearer-infineon.c \ + infineon/mm-broadband-bearer-infineon.h \ + $(NULL) + +libmm_plugin_infineon_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) $(PLUGIN_INFINEON_COMPILER_FLAGS) +libmm_plugin_infineon_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) +libmm_plugin_infineon_la_LIBADD = $(builddir)/libhelpers-infineon.la + +################################################################################ # plugin: telit ################################################################################ diff --git a/plugins/infineon/77-mm-infineon-port-types.rules b/plugins/infineon/77-mm-infineon-port-types.rules new file mode 100644 index 000000000..29ad08247 --- /dev/null +++ b/plugins/infineon/77-mm-infineon-port-types.rules @@ -0,0 +1,18 @@ +# do not edit this file, it will be overwritten on update + +ACTION!="add|change|move", GOTO="mm_infineon_port_types_end" +SUBSYSTEMS=="usb", ATTRS{idVendor}=="1519", GOTO="mm_infineon_port_types" +GOTO="mm_infineon_port_types_end" + +LABEL="mm_infineon_port_types" +SUBSYSTEMS=="usb", ATTRS{bInterfaceNumber}=="?*", ENV{.MM_USBIFNUM}="$attr{bInterfaceNumber}" + +# Sierra HL758x +ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{.MM_USBIFNUM}=="06", ENV{ID_MM_INFINEON_NCM_ID}="0" +ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{.MM_USBIFNUM}=="08", ENV{ID_MM_INFINEON_NCM_ID}="1" +ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{.MM_USBIFNUM}=="0a", ENV{ID_MM_INFINEON_NCM_ID}="2" +ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{.MM_USBIFNUM}=="0c", ENV{ID_MM_INFINEON_NCM_ID}="3" +ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{.MM_USBIFNUM}=="00", ENV{ID_MM_INFINEON_ACM_ID}="0" +ATTRS{idVendor}=="1519", ATTRS{idProduct}=="0443", ENV{.MM_USBIFNUM}=="04", ENV{ID_MM_INFINEON_ACM_ID}="2" + +LABEL="mm_infineon_port_types_end" diff --git a/plugins/infineon/mm-broadband-bearer-infineon.c b/plugins/infineon/mm-broadband-bearer-infineon.c new file mode 100644 index 000000000..fed7a101b --- /dev/null +++ b/plugins/infineon/mm-broadband-bearer-infineon.c @@ -0,0 +1,874 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2017 Tempered Networks Inc. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <arpa/inet.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-base-modem-at.h" +#include "mm-broadband-bearer-infineon.h" +#include "mm-log.h" +#include "mm-modem-helpers.h" +#include "mm-modem-helpers-infineon.h" + +G_DEFINE_TYPE (MMBroadbandBearerInfineon, mm_broadband_bearer_infineon, MM_TYPE_BROADBAND_BEARER) + +/*****************************************************************************/ +/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */ + +typedef enum { + DIAL_3GPP_CONTEXT_STEP_FIRST = 0, + DIAL_3GPP_CONTEXT_STEP_CHECK_CGACT, + DIAL_3GPP_CONTEXT_STEP_DEACT, + DIAL_3GPP_CONTEXT_STEP_SET_XGAUTH, + DIAL_3GPP_CONTEXT_STEP_SET_XDNS, + DIAL_3GPP_CONTEXT_STEP_SET_CGACT, + DIAL_3GPP_CONTEXT_STEP_SET_DATA_CHAN, + DIAL_3GPP_CONTEXT_STEP_START_DATA_CHAN, + DIAL_3GPP_CONTEXT_STEP_LAST +} Dial3gppContextStep; + +typedef struct { + MMBaseModem *modem; + MMPortSerialAt *primary; + MMPortSerialAt *secondary; + guint cid; + MMBearerIpFamily ip_family; + MMPort *data; + gint usb_interface_config_index; + gint usb_acm_config_index; + Dial3gppContextStep step; +} Dial3gppContext; + +static void +dial_3gpp_context_free (Dial3gppContext *ctx) +{ + g_clear_object (&ctx->modem); + g_clear_object (&ctx->primary); + g_clear_object (&ctx->secondary); + g_clear_object (&ctx->data); + g_slice_free (Dial3gppContext, ctx); +} + +/* Forward Function declarations */ +static void dial_3gpp_context_step (GTask *task); + +static MMPort * +dial_3gpp_finish (MMBroadbandBearer *self, + GAsyncResult *res, + GError **error) +{ + return MM_PORT (g_task_propagate_pointer (G_TASK (res), error)); +} + +static void +dial_3gpp_cgact_check_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + const gchar *response; + Dial3gppContext *ctx; + GError *error = NULL; + GList *cgact_list = NULL; + GList *l = NULL; + MM3gppPdpContextActive *pdp_ctx_active = NULL; + + ctx = (Dial3gppContext *) g_task_get_task_data (task); + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (!response) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Parse response */ + cgact_list = mm_3gpp_parse_cgact_read_response (response, &error); + g_assert(cgact_list != NULL); + + if (!cgact_list) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Iterate over list and check for active context with same CID */ + for (l = cgact_list; l; l = g_list_next(l)) { + pdp_ctx_active = l->data; + if (pdp_ctx_active->cid == ctx->cid) { + break; + } + else + pdp_ctx_active = NULL; + } + + /* If context is already active, deactivate it before attempting to configure and connect */ + if (pdp_ctx_active != NULL && pdp_ctx_active->active){ + mm_dbg("Selected CID is currently active."); + ctx->step++; + } + /* Otherwise, skip to auth step */ + else + ctx->step = DIAL_3GPP_CONTEXT_STEP_SET_XGAUTH; + + mm_3gpp_pdp_context_active_list_free (cgact_list); + dial_3gpp_context_step (task); +} + +typedef enum { + BEARER_INFINEON_AUTH_NONE = 0, + BEARER_INFINEON_AUTH_PAP = 1, + BEARER_INFINEON_AUTH_CHAP = 2 +} BearerInfineonAuthType; + +/* Generate XGAUTH string */ +static gchar * +build_auth_string (GTask *task) +{ + Dial3gppContext *ctx; + gchar *command; + const gchar *user; + const gchar *password; + MMBearerAllowedAuth auth_type; + MMBroadbandBearerInfineon *self; + + ctx = (Dial3gppContext *) g_task_get_task_data (task); + self = (MMBroadbandBearerInfineon *) g_task_get_source_object(task); + user = mm_bearer_properties_get_user (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); + password = mm_bearer_properties_get_password (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); + auth_type = mm_bearer_properties_get_allowed_auth (mm_base_bearer_peek_config (MM_BASE_BEARER (self))); + + /* Both user and password are required; otherwise firmware returns an error */ + if (user || password) { + gchar *encoded_user; + gchar *encoded_password; + BearerInfineonAuthType auth_type_inf = BEARER_INFINEON_AUTH_NONE; + + /* Prefer CHAP over PAP */ + if (auth_type & MM_BEARER_ALLOWED_AUTH_CHAP) + auth_type_inf = BEARER_INFINEON_AUTH_CHAP; + else if (auth_type & MM_BEARER_ALLOWED_AUTH_PAP) + auth_type_inf = BEARER_INFINEON_AUTH_PAP; + else if (auth_type == MM_BEARER_ALLOWED_AUTH_UNKNOWN) + auth_type_inf = BEARER_INFINEON_AUTH_CHAP; + else if (auth_type == MM_BEARER_ALLOWED_AUTH_NONE) + auth_type_inf = BEARER_INFINEON_AUTH_NONE; + + 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 ("+XGAUTH=%u,%u,\"%s\",\"%s\"", + ctx->cid, + auth_type_inf, + encoded_user ? encoded_user : "", + encoded_password ? encoded_password : ""); + g_free (encoded_user); + g_free (encoded_password); + } else { + command = g_strdup_printf ("+XGAUTH=%u,0,\"\",\"\"", ctx->cid); + } + return command; +} + +/* Generic AT command result ready callback */ +static void +dial_3gpp_res_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + Dial3gppContext *ctx; + GError *error = NULL; + + ctx = (Dial3gppContext *) g_task_get_task_data (task); + + if (!mm_base_modem_at_command_full_finish (modem, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + /* Go to next step */ + ctx->step++; + dial_3gpp_context_step (task); +} + +static void +dial_3gpp_context_step (GTask *task) +{ + Dial3gppContext *ctx; + MMBroadbandBearerInfineon *self; + + ctx = (Dial3gppContext *) g_task_get_task_data (task); + self = (MMBroadbandBearerInfineon *) g_task_get_source_object (task); + + mm_dbg ("running infineon dialing step %u/%u...", ctx->step, DIAL_3GPP_CONTEXT_STEP_LAST); + + /* Check for cancellation */ + if (g_task_return_error_if_cancelled (task)) { + g_object_unref (task); + return; + } + + switch (ctx->step) { + case DIAL_3GPP_CONTEXT_STEP_FIRST: + ctx->step++; + /* fall down to next step */ + case DIAL_3GPP_CONTEXT_STEP_CHECK_CGACT: + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + "+CGACT?", + 3, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dial_3gpp_cgact_check_ready, + task); + return; + + case DIAL_3GPP_CONTEXT_STEP_DEACT: { + /* Disconnect so that we can reconfigure a context that is already activated. */ + gchar *command; + + if (ctx->cid != 1) + { + command = g_strdup_printf ("+CGACT=0,%u", ctx->cid); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 30, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback) dial_3gpp_res_ready, + task); + return; + } else { + /* Skip this step if we are connecting via the default bearer with cid == 1 */ + ctx->step++; + } + } + + case DIAL_3GPP_CONTEXT_STEP_SET_XGAUTH: { + gchar *command; + + command = build_auth_string (task); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 3, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dial_3gpp_res_ready, + task); + g_free (command); + return; + } + + case DIAL_3GPP_CONTEXT_STEP_SET_XDNS: { + gchar *command; + + /* Select IP family with XDNS command */ + switch (mm_bearer_properties_get_ip_type (mm_base_bearer_peek_config (MM_BASE_BEARER (self)))) { + case MM_BEARER_IP_FAMILY_IPV4: + command = g_strdup_printf ("+XDNS=%u,1", ctx->cid); + break; + case MM_BEARER_IP_FAMILY_IPV6: + command = g_strdup_printf ("+XDNS=%u,2", ctx->cid); + break; + case MM_BEARER_IP_FAMILY_IPV4V6: /* Default to IPV4V6 dynamic DNS request */ + default: + command = g_strdup_printf ("+XDNS=%u,3", ctx->cid); + break; + } + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 3, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dial_3gpp_res_ready, + task); + g_free (command); + return; + } + + case DIAL_3GPP_CONTEXT_STEP_SET_CGACT: { + gchar *command; + + command = g_strdup_printf ("+CGACT=1,%u", ctx->cid); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 30, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dial_3gpp_res_ready, + task); + g_free (command); + return; + } + + case DIAL_3GPP_CONTEXT_STEP_SET_DATA_CHAN: { + gchar *command; + + /* Setup data channel mapping */ + /* See Intel XMM7160 AT Functional Spec for more info on this command */ + command = g_strdup_printf ("+XDATACHANNEL=1,1,\"/USBCDC/%u\",\"/USBHS/NCM/%u\",2,%u", + ctx->usb_acm_config_index, ctx->usb_interface_config_index, ctx->cid); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 3, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dial_3gpp_res_ready, + task); + g_free (command); + return; + } + + case DIAL_3GPP_CONTEXT_STEP_START_DATA_CHAN: { + gchar *command; + + command = g_strdup_printf ("+CGDATA=\"M-RAW_IP\",%u", ctx->cid); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 10, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dial_3gpp_res_ready, + task); + g_free (command); + return; + } + + case DIAL_3GPP_CONTEXT_STEP_LAST: + g_task_return_pointer (task, g_object_ref (ctx->data), g_object_unref); + g_object_unref (task); + return; + } +} + +static void +dial_3gpp (MMBroadbandBearer *self, + MMBaseModem *modem, + MMPortSerialAt *primary, + guint cid, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + Dial3gppContext *ctx; + GError *error = NULL; + + g_assert (primary != NULL); + + /* Setup task */ + task = g_task_new (self, cancellable, callback, user_data); + ctx = g_slice_new0 (Dial3gppContext); + g_task_set_task_data (task, ctx, (GDestroyNotify) dial_3gpp_context_free); + + /* Setup Context */ + ctx->modem = g_object_ref (modem); + ctx->primary = g_object_ref (primary); + ctx->cid = cid; + ctx->step = DIAL_3GPP_CONTEXT_STEP_FIRST; + ctx->usb_interface_config_index = -1; + ctx->usb_acm_config_index = -1; + + /* Get a net port for the connection */ + ctx->data = mm_base_modem_peek_best_data_port (MM_BASE_MODEM (modem), MM_PORT_TYPE_NET); + if (!ctx->data) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_NOT_FOUND, + "No valid data port found to launch connection"); + g_object_unref (task); + return; + } + g_object_ref (ctx->data); + + ctx->secondary = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (modem)); + if (ctx->secondary) + g_object_ref (ctx->secondary); + + /* Validate the USB configuration */ + ctx->usb_interface_config_index = mm_kernel_device_get_property_as_int(mm_port_peek_kernel_device(ctx->data), "ID_MM_INFINEON_NCM_ID"); + ctx->usb_acm_config_index = mm_kernel_device_get_property_as_int(mm_port_peek_kernel_device(ctx->data), "ID_MM_INFINEON_ACM_ID"); + + if (ctx->usb_interface_config_index < 0 || ctx->usb_acm_config_index < 0) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Run! */ + dial_3gpp_context_step (task); +} + +/*****************************************************************************/ +/* 3GPP IP config retrieval (sub-step of the 3GPP Connection sequence) */ + +typedef enum { + GET_IP_CONFIG_3GPP_CONTEXT_STEP_FIRST = 0, + GET_IP_CONFIG_3GPP_CONTEXT_STEP_CGPADDR, + GET_IP_CONFIG_3GPP_CONTEXT_STEP_XDNS, + GET_IP_CONFIG_3GPP_CONTEXT_STEP_LAST +} GetIPConfig3gppContextStep; + +typedef struct { + MMBaseModem *modem; + MMPortSerialAt *primary; + guint cid; + MMBearerIpFamily ip_family; + MMPort *data; + GetIPConfig3gppContextStep step; + MMBearerIpConfig *ipv4_config; + MMBearerIpConfig *ipv6_config; +} GetIPConfig3gppContext; + +static void +get_ip_config_3gpp_context_free (GetIPConfig3gppContext *ctx) +{ + g_clear_object (&ctx->modem); + g_clear_object (&ctx->primary); + g_clear_object (&ctx->data); + g_clear_object (&ctx->ipv4_config); + g_clear_object (&ctx->ipv6_config); +} + +/* forward declaration */ +static void get_ip_config_3gpp_context_step (GTask *task); + +static gboolean +get_ip_config_3gpp_finish (MMBroadbandBearer *self, + GAsyncResult *res, + MMBearerIpConfig **ipv4_config, + MMBearerIpConfig **ipv6_config, + GError **error) +{ + MMBearerConnectResult *configs; + MMBearerIpConfig *ipv4; + MMBearerIpConfig *ipv6; + + configs = g_task_propagate_pointer (G_TASK (res), error); + if (!configs) + return FALSE; + + /* IPv4 config */ + ipv4 = mm_bearer_connect_result_peek_ipv4_config (configs); + if (ipv4) + *ipv4_config = g_object_ref (ipv4); + + /* IPv6 config */ + ipv6 = mm_bearer_connect_result_peek_ipv6_config (configs); + + if (ipv6) + *ipv6_config = g_object_ref (ipv6); + + mm_bearer_connect_result_unref (configs); + return TRUE; +} + +static void +ip_info_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + const gchar *response; + GError *error = NULL; + guint cid; + gchar *ipv4addr = NULL; + gchar *ipv6addr = NULL; + GetIPConfig3gppContext *ctx; + + ctx = (GetIPConfig3gppContext *) g_task_get_task_data (task); + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (!response) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Parse response */ + if (!mm_infineon_parse_cgpaddr_response (response, &cid, &ipv4addr, &ipv6addr, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_warn_if_fail (cid == ctx->cid); + /* Create IPv4 config */ + if (ipv4addr) { + ctx->ipv4_config = mm_bearer_ip_config_new (); + mm_bearer_ip_config_set_method (ctx->ipv4_config, MM_BEARER_IP_METHOD_STATIC); + mm_bearer_ip_config_set_address (ctx->ipv4_config, ipv4addr); + mm_bearer_ip_config_set_prefix (ctx->ipv4_config, 0); + g_free (ipv4addr); + } + /* IPv6 config */ + if (ipv6addr) { + ctx->ipv6_config = mm_bearer_ip_config_new (); + mm_bearer_ip_config_set_method (ctx->ipv6_config, MM_BEARER_IP_METHOD_STATIC); + mm_bearer_ip_config_set_address (ctx->ipv6_config, ipv6addr); + mm_bearer_ip_config_set_prefix (ctx->ipv6_config, 0); + g_free (ipv6addr); + } + + ctx->step++; + get_ip_config_3gpp_context_step (task); +} + +static void +dns_info_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + const gchar *response; + GError *error = NULL; + GStrv dns_v4 = NULL; + GStrv dns_v6 = NULL; + GetIPConfig3gppContext *ctx; + + ctx = (GetIPConfig3gppContext *) g_task_get_task_data (task); + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (!response) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Parse XDNS results and return two vectors containing IPV4 and IPV6 addresses */ + if (!mm_infineon_parse_xdns_query_response (response, ctx->cid, &dns_v4, &dns_v6, &error)) { + g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Error parsing +XDNS response: '%s'", + response); + g_object_unref (task); + return; + } + if (ctx->ipv4_config) + mm_bearer_ip_config_set_dns (ctx->ipv4_config, (const gchar **)dns_v4); + + if (ctx->ipv6_config) + mm_bearer_ip_config_set_dns (ctx->ipv6_config, (const gchar **)dns_v6); + + ctx->step++; + get_ip_config_3gpp_context_step (task); +} + +static void +get_ip_config_3gpp_context_step (GTask *task) +{ + GetIPConfig3gppContext *ctx; + + ctx = (GetIPConfig3gppContext *) g_task_get_task_data (task); + + mm_dbg ("running infineon get ip config step %u/%u...", ctx->step, GET_IP_CONFIG_3GPP_CONTEXT_STEP_LAST); + + switch (ctx->step) { + case GET_IP_CONFIG_3GPP_CONTEXT_STEP_FIRST: + ctx->step++; + /* fall down to next step */ + case GET_IP_CONFIG_3GPP_CONTEXT_STEP_CGPADDR: { + gchar *command; + command = g_strdup_printf ("+CGPADDR=%u", ctx->cid); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 3, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)ip_info_ready, + task); + g_free (command); + return; + } + + case GET_IP_CONFIG_3GPP_CONTEXT_STEP_XDNS: + /* query DNS addresses */ + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + "+XDNS?", + 3, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback)dns_info_ready, + task); + return; + + case GET_IP_CONFIG_3GPP_CONTEXT_STEP_LAST: + g_task_return_pointer (task, + mm_bearer_connect_result_new (ctx->data, ctx->ipv4_config, ctx->ipv6_config), + (GDestroyNotify) mm_bearer_connect_result_unref); + + g_object_unref (task); + return; + } +} + +static void +get_ip_config_3gpp (MMBroadbandBearer *self, + MMBroadbandModem *modem, + MMPortSerialAt *primary, + MMPortSerialAt *secondary, + MMPort *data, + guint cid, + MMBearerIpFamily ip_family, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + GetIPConfig3gppContext *ctx; + + g_assert (primary != NULL); + g_assert (data != NULL); + g_assert (modem != NULL); + + /* Setup task and create disconnect context */ + task = g_task_new (self, NULL, callback, user_data); + ctx = g_slice_new0 (GetIPConfig3gppContext); + g_task_set_task_data (task, ctx, (GDestroyNotify) get_ip_config_3gpp_context_free); + + /* Setup context */ + ctx->modem = g_object_ref (modem); + ctx->primary = g_object_ref (primary); + ctx->cid = cid; + ctx->ip_family = ip_family; + ctx->data = g_object_ref (data); + ctx->step = GET_IP_CONFIG_3GPP_CONTEXT_STEP_FIRST; + ctx->ipv4_config = NULL; + ctx->ipv6_config = NULL; + + /* Start */ + get_ip_config_3gpp_context_step (task); +} + +/*****************************************************************************/ +/* Disconnect 3GPP */ + +typedef enum { + DISCONNECT_3GPP_CONTEXT_STEP_FIRST = 0, + DISCONNECT_3GPP_CONTEXT_STEP_CGACT, + DISCONNECT_3GPP_CONTEXT_STEP_LAST +} Disconnect3gppContextStep; + +typedef struct { + MMBaseModem *modem; + MMPortSerialAt *primary; + MMPort *data; + guint cid; + Disconnect3gppContextStep step; +} Disconnect3gppContext; + +static void +disconnect_3gpp_context_free (Disconnect3gppContext *ctx) +{ + g_clear_object (&ctx->modem); + g_clear_object (&ctx->primary); + g_clear_object (&ctx->data); + g_slice_free (Disconnect3gppContext, ctx); +} + +static gboolean +disconnect_3gpp_finish (MMBroadbandBearer *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +/* forward declaration */ +static void +disconnect_3gpp_context_step (GTask *task); + +static void +cgact_disconnect_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + Disconnect3gppContext *ctx; + GError *error = NULL; + + mm_base_modem_at_command_full_finish (modem, res, &error); + + if (error) { + if(!g_error_matches (error, + MM_CONNECTION_ERROR, + MM_CONNECTION_ERROR_NO_CARRIER)) + { + mm_dbg ("PDP context deactivation failed (not fatal): %s", error->message); + } + g_error_free (error); + } + + ctx = (Disconnect3gppContext *) g_task_get_task_data (task); + + /* Go on to next step */ + ctx->step++; + disconnect_3gpp_context_step (task); +} + +static void +disconnect_3gpp_context_step (GTask *task) +{ + Disconnect3gppContext *ctx; + + ctx = (Disconnect3gppContext *) g_task_get_task_data (task); + + mm_dbg ("running infineon disconnect step %u/%u...", ctx->step, DISCONNECT_3GPP_CONTEXT_STEP_LAST); + + switch (ctx->step) { + case DISCONNECT_3GPP_CONTEXT_STEP_FIRST: + ctx->step++; + /* fall down to next step */ + case DISCONNECT_3GPP_CONTEXT_STEP_CGACT: { + gchar *command; + + command = g_strdup_printf ("+CGACT=0,%u", ctx->cid ); + mm_base_modem_at_command_full (ctx->modem, + ctx->primary, + command, + 30, + FALSE, + FALSE, + FALSE, + (GAsyncReadyCallback) cgact_disconnect_ready, + task); + g_free (command); + return; + } + + case DISCONNECT_3GPP_CONTEXT_STEP_LAST:{ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + } +} + +static void +disconnect_3gpp (MMBroadbandBearer *self, + MMBroadbandModem *modem, + MMPortSerialAt *primary, + MMPortSerialAt *secondary, + MMPort *data, + guint cid, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + Disconnect3gppContext *ctx; + + g_assert (primary != NULL); + g_assert (data != NULL); + g_assert (modem != NULL); + + /* Setup task and create disconnect context */ + task = g_task_new (self, NULL, callback, user_data); + ctx = g_slice_new0 (Disconnect3gppContext); + g_task_set_task_data (task, ctx, (GDestroyNotify) disconnect_3gpp_context_free); + + /* Setup context */ + ctx->modem = g_object_ref (modem); + ctx->primary = g_object_ref (primary); + ctx->data = g_object_ref (data); + ctx->cid = cid; + ctx->step = DISCONNECT_3GPP_CONTEXT_STEP_FIRST; + + /* Start */ + disconnect_3gpp_context_step (task); +} + +/*****************************************************************************/ + +MMBaseBearer * +mm_broadband_bearer_infineon_new_finish (GAsyncResult *res, + GError **error) +{ + GObject *bearer; + GObject *source; + + source = g_async_result_get_source_object (res); + bearer = g_async_initable_new_finish (G_ASYNC_INITABLE (source), res, error); + g_object_unref (source); + + if (!bearer) + return NULL; + + /* Only export valid bearers */ + mm_base_bearer_export (MM_BASE_BEARER (bearer)); + + return MM_BASE_BEARER (bearer); +} + +void +mm_broadband_bearer_infineon_new (MMBroadbandModemInfineon *modem, + MMBearerProperties *config, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async ( + MM_TYPE_BROADBAND_BEARER_INFINEON, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + MM_BASE_BEARER_MODEM, modem, + MM_BASE_BEARER_CONFIG, config, + NULL); +} + +static void +mm_broadband_bearer_infineon_init (MMBroadbandBearerInfineon *self) +{ + + return; +} + +static void +mm_broadband_bearer_infineon_class_init (MMBroadbandBearerInfineonClass *klass) +{ + MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass); + + 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; + + broadband_bearer_class->disconnect_3gpp = disconnect_3gpp; + broadband_bearer_class->disconnect_3gpp_finish = disconnect_3gpp_finish; +} diff --git a/plugins/infineon/mm-broadband-bearer-infineon.h b/plugins/infineon/mm-broadband-bearer-infineon.h new file mode 100644 index 000000000..cfc6910ab --- /dev/null +++ b/plugins/infineon/mm-broadband-bearer-infineon.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + */ + +#ifndef MM_BROADBAND_BEARER_INFINEON_H +#define MM_BROADBAND_BEARER_INFINEON_H + +#include <glib.h> +#include <glib-object.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-broadband-bearer.h" +#include "mm-broadband-modem-infineon.h" + +#define MM_TYPE_BROADBAND_BEARER_INFINEON (mm_broadband_bearer_infineon_get_type ()) +#define MM_BROADBAND_BEARER_INFINEON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_BEARER_INFINEON, MMBroadbandBearerInfineon)) +#define MM_BROADBAND_BEARER_INFINEON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_BEARER_INFINEON, MMBroadbandBearerInfineonClass)) +#define MM_IS_BROADBAND_BEARER_INFINEON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_BEARER_INFINEON)) +#define MM_IS_BROADBAND_BEARER_INFINEON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_BEARER_INFINEON)) +#define MM_BROADBAND_BEARER_INFINEON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_BEARER_INFINEON, MMBroadbandBearerInfineonClass)) + +typedef struct _MMBroadbandBearerInfineon MMBroadbandBearerInfineon; +typedef struct _MMBroadbandBearerInfineonClass MMBroadbandBearerInfineonClass; +typedef struct _MMBroadbandBearerInfineonPrivate MMBroadbandBearerInfineonPrivate; + +struct _MMBroadbandBearerInfineon { + MMBroadbandBearer parent; + MMBroadbandBearerInfineonPrivate *priv; +}; + +struct _MMBroadbandBearerInfineonClass { + MMBroadbandBearerClass parent; +}; + +GType mm_broadband_bearer_infineon_get_type (void); + +/* Default 3GPP bearer creation implementation */ +void mm_broadband_bearer_infineon_new (MMBroadbandModemInfineon *modem, + MMBearerProperties *config, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +MMBaseBearer *mm_broadband_bearer_infineon_new_finish (GAsyncResult *res, + GError **error); + +#endif /* MM_BROADBAND_BEARER_INFINEON_H */ diff --git a/plugins/infineon/mm-broadband-modem-infineon.c b/plugins/infineon/mm-broadband-modem-infineon.c new file mode 100644 index 000000000..f20804f35 --- /dev/null +++ b/plugins/infineon/mm-broadband-modem-infineon.c @@ -0,0 +1,216 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2017 Tempered Networks Inc. + */ + +#include <config.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#include "ModemManager.h" +#include "mm-base-modem-at.h" +#include "mm-serial-parsers.h" +#include "mm-log.h" +#include "mm-errors-types.h" +#include "mm-iface-modem.h" +#include "mm-iface-modem-3gpp.h" +#include "mm-broadband-bearer-infineon.h" +#include "mm-broadband-modem-infineon.h" + +static void iface_modem_init (MMIfaceModem *iface); + + +G_DEFINE_TYPE_EXTENDED (MMBroadbandModemInfineon, mm_broadband_modem_infineon, MM_TYPE_BROADBAND_MODEM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init)) + +/*****************************************************************************/ +/* Create Bearer (Modem interface) */ + +static MMBaseBearer * +modem_create_bearer_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + MMBaseBearer *bearer; + + bearer = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)); + mm_dbg ("New Infineon bearer created at DBus path '%s'", mm_base_bearer_get_path (bearer)); + + return g_object_ref (bearer); +} + +static void +broadband_bearer_infineon_new_ready (GObject *source, + GAsyncResult *res, + GSimpleAsyncResult *simple) +{ + MMBaseBearer *bearer = NULL; + GError *error = NULL; + + bearer = mm_broadband_bearer_infineon_new_finish (res, &error); + if (!bearer) + g_simple_async_result_take_error (simple, error); + else + g_simple_async_result_set_op_res_gpointer (simple, + bearer, + (GDestroyNotify)g_object_unref); + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +static void +modem_create_bearer (MMIfaceModem *self, + MMBearerProperties *properties, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_create_bearer); + + mm_broadband_bearer_infineon_new (MM_BROADBAND_MODEM_INFINEON (self), + properties, + NULL, /* cancellable */ + (GAsyncReadyCallback)broadband_bearer_infineon_new_ready, + result); +} + +/*****************************************************************************/ +/* Reset (Modem interface) */ + +static gboolean +modem_reset_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); +} + +static void +modem_reset (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "AT+CFUN=1,1", + 3, + FALSE, + callback, + user_data); +} + +/*****************************************************************************/ +/* MODEM POWER DOWN */ + +static gboolean +modem_power_down_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); +} + +static void +modem_power_down (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + /* Use AT+CFUN=4 for power down. It will stop the RF (IMSI detach), and + * keeps access to the SIM */ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CFUN=4", + /* The modem usually completes +CFUN=4 within 1-2 seconds, + * but sometimes takes a ridiculously long time (~30-35 seconds). + * It's better to have a long timeout here than to have the + * modem not responding to subsequent AT commands until +CFUN=4 + * completes. */ + 40, + FALSE, + callback, + user_data); +} + + +/*****************************************************************************/ +/* Modem power up (Modem interface) */ + +static gboolean +modem_power_up_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +modem_power_up (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CFUN=1", + 10, + FALSE, + callback, + user_data); +} + +/*****************************************************************************/ + +MMBroadbandModemInfineon * +mm_broadband_modem_infineon_new (const gchar *device, + const gchar **drivers, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id) +{ + return g_object_new (MM_TYPE_BROADBAND_MODEM_INFINEON, + MM_BASE_MODEM_DEVICE, device, + MM_BASE_MODEM_DRIVERS, drivers, + MM_BASE_MODEM_PLUGIN, plugin, + MM_BASE_MODEM_VENDOR_ID, vendor_id, + MM_BASE_MODEM_PRODUCT_ID, product_id, + NULL); +} + +static void +mm_broadband_modem_infineon_init (MMBroadbandModemInfineon *self) +{ +} + +static void +iface_modem_init (MMIfaceModem *iface) +{ + iface->create_bearer = modem_create_bearer; + iface->create_bearer_finish = modem_create_bearer_finish; + iface->reset = modem_reset; + iface->reset_finish = modem_reset_finish; + iface->modem_power_down = modem_power_down; + iface->modem_power_down_finish = modem_power_down_finish; + iface->modem_power_up = modem_power_up; + iface->modem_power_up_finish = modem_power_up_finish; + +} + +static void +mm_broadband_modem_infineon_class_init (MMBroadbandModemInfineonClass *klass) +{ +} diff --git a/plugins/infineon/mm-broadband-modem-infineon.h b/plugins/infineon/mm-broadband-modem-infineon.h new file mode 100644 index 000000000..1b4ee5a28 --- /dev/null +++ b/plugins/infineon/mm-broadband-modem-infineon.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + */ + +#ifndef MM_BROADBAND_MODEM_INFINEON_H +#define MM_BROADBAND_MODEM_INFINEON_H + +#include "mm-broadband-modem.h" + +#define MM_TYPE_BROADBAND_MODEM_INFINEON (mm_broadband_modem_infineon_get_type ()) +#define MM_BROADBAND_MODEM_INFINEON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_INFINEON, MMBroadbandModemInfineon)) +#define MM_BROADBAND_MODEM_INFINEON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_INFINEON, MMBroadbandModemInfineonClass)) +#define MM_IS_BROADBAND_MODEM_INFINEON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_INFINEON)) +#define MM_IS_BROADBAND_MODEM_INFINEON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_INFINEON)) +#define MM_BROADBAND_MODEM_INFINEON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_INFINEON, MMBroadbandModemInfineonClass)) + +typedef struct _MMBroadbandModemInfineon MMBroadbandModemInfineon; +typedef struct _MMBroadbandModemInfineonClass MMBroadbandModemInfineonClass; + +struct _MMBroadbandModemInfineon { + MMBroadbandModem parent; +}; + +struct _MMBroadbandModemInfineonClass{ + MMBroadbandModemClass parent; +}; + +GType mm_broadband_modem_infineon_get_type (void); + +MMBroadbandModemInfineon *mm_broadband_modem_infineon_new (const gchar *device, + const gchar **drivers, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id); + +#endif /* MM_BROADBAND_MODEM_INFINEON_H */ diff --git a/plugins/infineon/mm-modem-helpers-infineon.c b/plugins/infineon/mm-modem-helpers-infineon.c new file mode 100644 index 000000000..ad1594429 --- /dev/null +++ b/plugins/infineon/mm-modem-helpers-infineon.c @@ -0,0 +1,204 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2017 Tempered Networks Inc. + * + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MMCLI +#include <libmm-glib.h> + +#include "mm-log.h" +#include "mm-modem-helpers.h" +#include "mm-modem-helpers-infineon.h" + +/*************************************************************************/ +/* Parse +CGPADDR response */ +gboolean +mm_infineon_parse_cgpaddr_response (const gchar *reply, + guint *cid, + gchar **ipv4addr, + gchar **ipv6addr, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info; + GError *inner_error = NULL; + gchar *ip1, *ip2; + + g_assert (reply); + g_assert (cid); + g_assert (ipv4addr); + g_assert (ipv6addr); + + *ipv4addr = NULL; + *ipv6addr = NULL; + + r = g_regex_new ("\\+CGPADDR:\\s*(\\d+)\\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)) { + inner_error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse CID from +CGPADDR response"); + goto out; + } + + ip1 = mm_get_string_unquoted_from_match_info (match_info, 3); + ip2 = mm_get_string_unquoted_from_match_info (match_info, 5); + + /* ip1 could contain an IPv4 or IPv6 address */ + if (ip1) { + /* Check if IPv6 address */ + if (g_strrstr (ip1, ":")) { + if (0 == g_strcmp0("0:0:0:0:0:0:0:0", ip1)) { + g_free (ip1); + } else { + *ipv6addr = ip1; + } + /* IPv4 address */ + } else { + if (0 == g_strcmp0("0.0.0.0", ip1)) { + g_free (ip1); + } else { + *ipv4addr = ip1; + } + } + } + + /* ip2 will always be an IPv6 address */ + if (ip2) { + if (g_strrstr (ip2, ":") && 0 != g_strcmp0("0:0:0:0:0:0:0:0", ip2)) { + *ipv6addr = ip2; + } else { + g_free (ip2); + } + } + } + + if (!*ipv6addr && !*ipv4addr) { + inner_error = g_error_new (MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse a valid IP address from +CGPADDR response."); + } + +out: + g_match_info_free (match_info); + g_regex_unref (r); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + return TRUE; +} + +static gboolean +g_ptr_array_contains_string (GPtrArray *arr, + gchar *str) +{ + gboolean found = FALSE; + guint i; + + /* Check inputs */ + g_assert (arr); + g_assert (str); + + for (i = 0; i < arr->len; i++) { + if (0 == g_strcmp0 ((gchar *) g_ptr_array_index (arr, i), str)) { + found = TRUE; + break; + } + } + return found; +} + +/*************************************************************************/ +/* Parse +XDNS response */ +gboolean +mm_infineon_parse_xdns_query_response (const gchar *reply, + guint cid, + GStrv *dns_v4, + GStrv *dns_v6, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info; + guint this_cid = 0; + GPtrArray *ipv4arr, *ipv6arr; + gchar *dns1, *dns2; + + ipv4arr = g_ptr_array_sized_new (3); + ipv6arr = g_ptr_array_sized_new (3); + + r = g_regex_new ("\\+XDNS:\\s*(\\d+)\\s*,\\s*\"(.*)\"\\s*,\\s*\"(.*)\"", + G_REGEX_OPTIMIZE | G_REGEX_RAW | G_REGEX_MULTILINE | G_REGEX_MATCH_NEWLINE_ANY, + 0, NULL); + g_assert (r != NULL); + + g_regex_match (r, reply, 0, &match_info); + while (g_match_info_matches (match_info)) { + if (mm_get_uint_from_match_info (match_info, 1, &this_cid) && + (this_cid == 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) { + /* Check if dns1 is an IPv6 address */ + if (g_strrstr (dns1, ":")) { + if(!g_ptr_array_contains_string(ipv6arr, dns1)) + g_ptr_array_add(ipv6arr, dns1); + else + g_free (dns1); + /* Otherwise dns1 is an ipv4 address. Check if valid and unique */ + } else if (0 != g_strcmp0 ("0.0.0.0", dns1) && !g_ptr_array_contains_string (ipv4arr, dns1)) { + g_ptr_array_add (ipv4arr, dns1); + } else { + g_free (dns1); + } + + /* Check if dns2 is an IPv6 address */ + if (g_strrstr (dns2, ":")) { + if(!g_ptr_array_contains_string (ipv6arr, dns2)) + g_ptr_array_add (ipv6arr, dns2); + else + g_free (dns2); + /* Otherwise dns2 is an ipv4 address. Check if valid and unique */ + } else if (0 != g_strcmp0 ("0.0.0.0", dns2) && !g_ptr_array_contains_string (ipv4arr, dns2)) { + g_ptr_array_add (ipv4arr, dns2); + } else { + g_free (dns2); + } + } + g_match_info_next (match_info, NULL); + } + g_match_info_free (match_info); + g_regex_unref (r); + + /* Add Null terminators to convert from GPtrArray to GStrv. */ + g_ptr_array_add (ipv4arr, NULL); + g_ptr_array_add (ipv6arr, NULL); + + *dns_v4 = (GStrv) g_ptr_array_free (ipv4arr, FALSE); + *dns_v6 = (GStrv) g_ptr_array_free (ipv6arr, FALSE); + + return TRUE; +}
\ No newline at end of file diff --git a/plugins/infineon/mm-modem-helpers-infineon.h b/plugins/infineon/mm-modem-helpers-infineon.h new file mode 100644 index 000000000..6cbf0e18b --- /dev/null +++ b/plugins/infineon/mm-modem-helpers-infineon.h @@ -0,0 +1,33 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2017 Tempered Networks Inc. + * + */ +#ifndef MM_MODEM_HELPERS_INFINEON_H +#define MM_MODEM_HELPERS_INFINEON_H + +#include <glib.h> + +gboolean +mm_infineon_parse_cgpaddr_response (const gchar *reply, + guint *cid, + gchar **ipv4addr, gchar **ipv6addr, + GError **error); + +gboolean +mm_infineon_parse_xdns_query_response (const gchar *reply, + guint cid, + GStrv *dns_v4, GStrv *dns_v6, + GError **error); +#endif /* MM_MODEM_HELPERS_INFINEON_H */ diff --git a/plugins/infineon/mm-plugin-infineon.c b/plugins/infineon/mm-plugin-infineon.c new file mode 100644 index 000000000..11ab59695 --- /dev/null +++ b/plugins/infineon/mm-plugin-infineon.c @@ -0,0 +1,88 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + * Copyright (C) 2017 Tempered Networks Inc. + */ + +#include <string.h> +#include <gmodule.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-broadband-modem-infineon.h" +#include "mm-plugin-infineon.h" +#include "mm-log.h" + +G_DEFINE_TYPE (MMPluginInfineon, mm_plugin_infineon, MM_TYPE_PLUGIN) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +static MMBaseModem * +create_modem (MMPlugin *self, + const gchar *sysfs_path, + const gchar **drivers, + guint16 vendor, + guint16 product, + GList *probes, + GError **error) +{ + return MM_BASE_MODEM (mm_broadband_modem_infineon_new (sysfs_path, + drivers, + mm_plugin_get_name (self), + vendor, + product)); +} + +/*****************************************************************************/ + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + static const gchar *subsystems[] = { "tty", "net", "usb", NULL }; + static const guint16 vendor_ids[] = { 0x1519, 0 }; + // We only want to support the NCM based USB composition. + static const guint16 product_ids_allow[] = { 0x0443, 0}; + // Otherwise fall back on the Generic plugin. + static const guint16 product_ids_forbid[] = { 0x0020, 0}; + + return MM_PLUGIN ( + g_object_new (MM_TYPE_PLUGIN_INFINEON, + MM_PLUGIN_NAME, "Infineon", + MM_PLUGIN_ALLOWED_SUBSYSTEMS, subsystems, + MM_PLUGIN_ALLOWED_VENDOR_IDS, vendor_ids, + MM_PLUGIN_ALLOWED_PRODUCT_IDS, product_ids_allow, + MM_PLUGIN_FORBIDDEN_PRODUCT_IDS, product_ids_forbid, + MM_PLUGIN_ALLOWED_AT, TRUE, + NULL)); +} + +static void +mm_plugin_infineon_init (MMPluginInfineon *self) +{ +} + +static void +mm_plugin_infineon_class_init (MMPluginInfineonClass *klass) +{ + MMPluginClass *plugin_class = MM_PLUGIN_CLASS (klass); + + plugin_class->create_modem = create_modem; +} diff --git a/plugins/infineon/mm-plugin-infineon.h b/plugins/infineon/mm-plugin-infineon.h new file mode 100644 index 000000000..d0f8e470e --- /dev/null +++ b/plugins/infineon/mm-plugin-infineon.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Copyright (C) 2013 Aleksander Morgado <aleksander@gnu.org> + */ + +#ifndef MM_PLUGIN_INFINEON_H +#define MM_PLUGIN_INFINEON_H + +#include "mm-plugin.h" + +#define MM_TYPE_PLUGIN_INFINEON (mm_plugin_infineon_get_type ()) +#define MM_PLUGIN_INFINEON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_INFINEON, MMPluginInfineon)) +#define MM_PLUGIN_INFINEON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_INFINEON, MMPluginInfineonClass)) +#define MM_IS_PLUGIN_INFINEON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_INFINEON)) +#define MM_IS_PLUGIN_INFINEON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_INFINEON)) +#define MM_PLUGIN_INFINEON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_INFINEON, MMPluginInfineonClass)) + +typedef struct { + MMPlugin parent; +} MMPluginInfineon; + +typedef struct { + MMPluginClass parent; +} MMPluginInfineonClass; + +GType mm_plugin_infineon_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_INFINEON_H */ diff --git a/plugins/infineon/tests/test-modem-helpers-infineon.c b/plugins/infineon/tests/test-modem-helpers-infineon.c new file mode 100644 index 000000000..6afe5dc58 --- /dev/null +++ b/plugins/infineon/tests/test-modem-helpers-infineon.c @@ -0,0 +1,215 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2017 Tempered Networks Inc + * + */ +#include <stdio.h> +#include <glib.h> +#include <glib-object.h> +#include <locale.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-modem-helpers.h" +#include "mm-modem-helpers-infineon.h" + +/*****************************************************************************/ +/* Test +CGPADDR responses */ + +typedef struct { + const gchar *str; + const guint cid; + const gboolean success; + const gchar *ipv4addr; + const gchar *ipv6addr; +} CgpAddrResponseText; + +static const CgpAddrResponseText cgpaddr_response_tests[] = { + { .str = "+CGPADDR: 1,\"10.11.12.133\"\r\n", + .cid = 1, + .success = TRUE, + .ipv4addr = "10.11.12.133", + .ipv6addr = NULL + }, + { .str = "+CGPADDR: 1,\"FE80:0:0:0:0:6B:B43D:6A01\"\r\n", + .cid = 1, + .success = TRUE, + .ipv4addr = NULL, + .ipv6addr = "FE80:0:0:0:0:6B:B43D:6A01" + }, + { .str = "+CGPADDR: 1,\"0.0.0.0\"\r\n", + .cid = 1, + .success = FALSE, + .ipv4addr = NULL, + .ipv6addr = NULL + }, + { .str = "+CGPADDR: 1,\"0:0:0:0:0:0:0:0\"\r\n", + .cid = 1, + .success = FALSE, + .ipv4addr = NULL, + .ipv6addr = NULL + }, + { .str = "+CGPADDR: 2,\"0.0.0.0\",\"0:0:0:0:0:0:0:0\"\r\n", + .cid = 2, + .success = FALSE, + .ipv4addr = NULL, + .ipv6addr = NULL + }, + { .str = "+CGPADDR: 3,\"100.120.138.254\",\"FE80:0:0:0:0:6B:B43E:1E01\"\r\n", + .cid = 3, + .success = TRUE, + .ipv4addr = "100.120.138.254", + .ipv6addr = "FE80:0:0:0:0:6B:B43E:1E01" + }, + { .str = "+CGPADDR: 3,\"0.0.0.0\",\"FE80:0:0:0:0:6B:B43E:1E01\"\r\n", + .cid = 3, + .success = TRUE, + .ipv4addr = NULL, + .ipv6addr = "FE80:0:0:0:0:6B:B43E:1E01" + }, + { .str = "+CGPADDR: 3,\"100.120.138.254\",\"0:0:0:0:0:0:0:0\"\r\n", + .cid = 3, + .success = TRUE, + .ipv4addr = "100.120.138.254", + .ipv6addr = NULL + } +}; + +static void +test_mm_infineon_parse_cgpaddr_response (void) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (cgpaddr_response_tests); i++) { + GError *error = NULL; + gboolean success; + guint cid = G_MAXUINT; + gchar *ipv4addr; + gchar *ipv6addr; + + success = mm_infineon_parse_cgpaddr_response (cgpaddr_response_tests[i].str, + &cid, &ipv4addr, &ipv6addr, + &error); + + if (!cgpaddr_response_tests[i].success) { + g_assert_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED); + g_assert (!success); + } else { + g_assert (success); + g_assert_no_error (error); + } + + g_assert_cmpuint (cgpaddr_response_tests[i].cid, ==, cid); + g_assert_cmpstr (cgpaddr_response_tests[i].ipv4addr, ==, ipv4addr); + g_assert_cmpstr (cgpaddr_response_tests[i].ipv6addr, ==, ipv6addr); + } +} + +/*****************************************************************************/ +/* Test +XDNS parser */ + +typedef struct { + const gchar *str; + const guint cid; + const guint dns_v4len; + const gchar *dns_v4[3]; + const guint dns_v6len; + const gchar *dns_v6[3]; +} XdnsResponseText; + +static const XdnsResponseText xdns_response_tests[] = { + { .str = "+XDNS: 1, \"172.26.38.1\", \"0.0.0.0\"\r\n\r\n+XDNS: 2, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 3, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 4, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 5, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n", + .cid = 1, + .dns_v4len = 1, + .dns_v4 = { "172.26.38.1", NULL }, + .dns_v6len = 0, + .dns_v6 = { NULL } + }, + { .str = "+XDNS: 1, \"172.26.38.1\", \"172.26.38.2\"\r\n\r\n+XDNS: 2, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 3, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 4, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 5, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n", + .cid = 1, + .dns_v4len = 2, + .dns_v4 = { "172.26.38.1", "172.26.38.2", NULL }, + .dns_v6len = 0, + .dns_v6 = { NULL } + }, + { .str = "+XDNS: 1, \"172.26.38.1\", \"0.0.0.0\"\r\n\r\n+XDNS: 2, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 3, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 4, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 5, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n", + .cid = 2, + .dns_v4len = 0, + .dns_v4 = { NULL }, + .dns_v6len = 0, + .dns_v6 = { NULL } + }, + { .str = "+XDNS: 1, \"172.26.38.1\", \"0.0.0.0\"\r\n\r\n+XDNS: 2, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 3, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 4, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 5, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n", + .cid = 0, + .dns_v4len = 0, + .dns_v4 = { NULL }, + .dns_v6len = 0, + .dns_v6 = { NULL } + }, + { .str = "+XDNS: 1, \"2001:4888:53:FF00:524:D:0:0\", \"2001:4888:52:FF00:528:D:0:0\"\r\n\r\n+XDNS: 2, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 3, \"198.224.166.135\", \"198.224.167.135\"\r\n\r\n+XDNS: 3, \"198.224.166.135\", \"2001:4888:53:FF00:524:D:0:0\"\r\n\r\n+XDNS: 3, \"198.224.166.135\", \"2001:4888:52:FF00:528:D:0:0\"\r\n\r\n+XDNS: 4, \"0.0.0.0\", \"0.0.0.0\"\r\n", + .cid = 1, + .dns_v4len = 0, + .dns_v4 = { NULL }, + .dns_v6len = 2, + .dns_v6 = { "2001:4888:53:FF00:524:D:0:0", "2001:4888:52:FF00:528:D:0:0", NULL } + }, + { .str = "+XDNS: 1, \"2001:4888:53:FF00:524:D:0:0\", \"2001:4888:52:FF00:528:D:0:0\"\r\n\r\n+XDNS: 2, \"0.0.0.0\", \"0.0.0.0\"\r\n\r\n+XDNS: 3, \"198.224.166.135\", \"198.224.167.135\"\r\n\r\n+XDNS: 3, \"198.224.166.135\", \"2001:4888:53:FF00:524:D:0:0\"\r\n\r\n+XDNS: 3, \"198.224.166.135\", \"2001:4888:52:FF00:528:D:0:0\"\r\n\r\n+XDNS: 4, \"0.0.0.0\", \"0.0.0.0\"\r\n", + .cid = 3, + .dns_v4len = 2, + .dns_v4 = { "198.224.166.135", "198.224.167.135", NULL }, + .dns_v6len = 2, + .dns_v6 = { "2001:4888:53:FF00:524:D:0:0", "2001:4888:52:FF00:528:D:0:0", NULL } + }, +}; + +static void +test_mm_infineon_parse_xdns_query_response (void) +{ + guint i, j; + + for (i = 0; i < G_N_ELEMENTS (xdns_response_tests); i++) { + GError *error = NULL; + gboolean success; + GStrv dns_v4; + GStrv dns_v6; + + success = mm_infineon_parse_xdns_query_response (xdns_response_tests[i].str, + xdns_response_tests[i].cid, + &dns_v4, &dns_v6, + &error); + g_assert_no_error (error); + g_assert (success); + + g_assert_cmpuint (xdns_response_tests[i].dns_v4len, ==, g_strv_length(dns_v4)); + for (j = 0; j < xdns_response_tests[i].dns_v4len; j++) { + g_assert_cmpstr (xdns_response_tests[i].dns_v4[j], ==, dns_v4[j]); + } + + g_assert_cmpuint (xdns_response_tests[i].dns_v6len, ==, g_strv_length(dns_v6)); + for (j = 0; j < xdns_response_tests[i].dns_v6len; j++) { + g_assert_cmpstr (xdns_response_tests[i].dns_v6[j], ==, dns_v6[j]); + } + } +} + +int main (int argc, char **argv) +{ + setlocale (LC_ALL, ""); + + g_test_init (&argc, &argv, NULL); + g_test_add_func ("/MM/infineon/cgpaddr", test_mm_infineon_parse_cgpaddr_response); + g_test_add_func ("/MM/infineon/xdns", test_mm_infineon_parse_xdns_query_response); + return g_test_run (); +} |