From a9c406e3c0d0c9c21e43e978ec3d531f3fbec93c Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Sun, 19 Feb 2012 10:04:01 +0100 Subject: cinterion: new Cinterion-specific source directory --- plugins/Makefile.am | 8 +- plugins/cinterion/mm-broadband-modem-cinterion.c | 1354 ++++++++++++++++++++++ plugins/cinterion/mm-broadband-modem-cinterion.h | 51 + plugins/cinterion/mm-plugin-cinterion.c | 118 ++ plugins/cinterion/mm-plugin-cinterion.h | 47 + plugins/mm-broadband-modem-cinterion.c | 1354 ---------------------- plugins/mm-broadband-modem-cinterion.h | 51 - plugins/mm-plugin-cinterion.c | 118 -- plugins/mm-plugin-cinterion.h | 47 - 9 files changed, 1574 insertions(+), 1574 deletions(-) create mode 100644 plugins/cinterion/mm-broadband-modem-cinterion.c create mode 100644 plugins/cinterion/mm-broadband-modem-cinterion.h create mode 100644 plugins/cinterion/mm-plugin-cinterion.c create mode 100644 plugins/cinterion/mm-plugin-cinterion.h delete mode 100644 plugins/mm-broadband-modem-cinterion.c delete mode 100644 plugins/mm-broadband-modem-cinterion.h delete mode 100644 plugins/mm-plugin-cinterion.c delete mode 100644 plugins/mm-plugin-cinterion.h (limited to 'plugins') diff --git a/plugins/Makefile.am b/plugins/Makefile.am index cc9b339e..d8a16abc 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -237,10 +237,10 @@ libmm_plugin_nokia_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) # Cinterion (previously Siemens) modem libmm_plugin_cinterion_la_SOURCES = \ - mm-plugin-cinterion.c \ - mm-plugin-cinterion.h \ - mm-broadband-modem-cinterion.c \ - mm-broadband-modem-cinterion.h + cinterion/mm-plugin-cinterion.c \ + cinterion/mm-plugin-cinterion.h \ + cinterion/mm-broadband-modem-cinterion.c \ + cinterion/mm-broadband-modem-cinterion.h libmm_plugin_cinterion_la_CPPFLAGS = $(PLUGIN_COMMON_COMPILER_FLAGS) libmm_plugin_cinterion_la_LDFLAGS = $(PLUGIN_COMMON_LINKER_FLAGS) diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c new file mode 100644 index 00000000..e816ac88 --- /dev/null +++ b/plugins/cinterion/mm-broadband-modem-cinterion.c @@ -0,0 +1,1354 @@ +/* -*- 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) 2011 Ammonit Measurement GmbH + * Copyright (C) 2011 Google Inc. + * Author: Aleksander Morgado + */ + +#include + +#include +#include +#include +#include +#include + +#include "ModemManager.h" +#include "mm-modem-helpers.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-iface-modem-messaging.h" +#include "mm-base-modem-at.h" +#include "mm-broadband-modem-cinterion.h" + +static void iface_modem_init (MMIfaceModem *iface); +static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); +static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); + +G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion, MM_TYPE_BROADBAND_MODEM, 0, + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)); + +struct _MMBroadbandModemCinterionPrivate { + /* Flag to know if we should try AT^SIND or not to get psinfo */ + gboolean sind_psinfo; + + /* Command to go into sleep mode */ + gchar *sleep_mode_cmd; + + /* Supported networks */ + gboolean only_geran; + gboolean only_utran; + gboolean both_geran_utran; +}; + +/* Setup relationship between the band bitmask in the modem and the bitmask + * in ModemManager. */ +typedef struct { + gchar *cinterion_band; + guint n_mm_bands; + MMModemBand mm_bands [4]; +} CinterionBand2G; + +/* Table checked in both MC75i (GPRS/EDGE) and EGS5 (GPRS) references. + * Note that the modem's configuration is also based on a bitmask, but as we + * will just support some of the combinations, we just use strings for them. + */ +static const CinterionBand2G bands_2g[] = { + { "1", 1, { MM_MODEM_BAND_EGSM, 0, 0, 0 }}, + { "2", 1, { MM_MODEM_BAND_DCS, 0, 0, 0 }}, + { "4", 1, { MM_MODEM_BAND_PCS, 0, 0, 0 }}, + { "8", 1, { MM_MODEM_BAND_G850, 0, 0, 0 }}, + { "3", 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, 0, 0 }}, + { "5", 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_PCS, 0, 0 }}, + { "10", 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_DCS, 0, 0 }}, + { "12", 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS, 0, 0 }}, + { "15", 4, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850 }} +}; + +/* Setup relationship between the 3G band bitmask in the modem and the bitmask + * in ModemManager. */ +typedef struct { + guint32 cinterion_band_flag; + MMModemBand mm_band; +} CinterionBand3G; + +/* Table checked in HC25 (3G) reference. This table includes both 2G and 3G + * frequencies. Depending on which one is configured, one access technology or + * the other will be used. This may conflict with the allowed mode configuration + * set, so you shouldn't for example set 3G frequency bands, and then use a + * 2G-only allowed mode. */ +static const CinterionBand3G bands_3g[] = { + { (1 << 0), MM_MODEM_BAND_EGSM }, + { (1 << 1), MM_MODEM_BAND_DCS }, + { (1 << 2), MM_MODEM_BAND_PCS }, + { (1 << 3), MM_MODEM_BAND_G850 }, + { (1 << 4), MM_MODEM_BAND_U2100 }, + { (1 << 5), MM_MODEM_BAND_U1900 }, + { (1 << 6), MM_MODEM_BAND_U850 } +}; + +/*****************************************************************************/ +/* Unsolicited events enabling */ + +static gboolean +enable_unsolicited_events_finish (MMIfaceModem3gpp *self, + GAsyncResult *res, + GError **error) +{ + return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); +} + +static void +enable_unsolicited_events (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + mm_base_modem_at_command_in_port ( + MM_BASE_MODEM (self), + /* Only primary port is expected in the Cinterion modems */ + mm_base_modem_get_port_primary (MM_BASE_MODEM (self)), + /* AT=CMER=[[,[,[,[,]]]]] + * but should be either not set, or equal to 0 or 2. + * Enabled with 2. + */ + "+CMER=3,0,0,2", + 3, + FALSE, + NULL, /* cancellable */ + callback, + user_data); +} + +/*****************************************************************************/ +/* Enable unsolicited events (SMS indications) (Messaging interface) */ + +static gboolean +messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self, + GAsyncResult *res, + GError **error) +{ + return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); +} + +static void +messaging_enable_unsolicited_events (MMIfaceModemMessaging *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + /* AT+CNMI=,[[,[,[,]]]] + * but should be either not set, or equal to 1; + * and can be only either 0 or 2 (EGS5) + */ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CNMI=2,1,2,2,1", + 3, + FALSE, + NULL, /* cancellable */ + callback, + user_data); +} + +/*****************************************************************************/ +/* MODEM POWER DOWN */ + +static gboolean +modem_power_down_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +sleep_ready (MMBaseModem *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GError *error = NULL; + + mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + + /* Ignore errors */ + if (error) { + mm_dbg ("Couldn't send power down command: '%s'", error->message); + g_error_free (error); + } + + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); +} + +static void +send_sleep_mode_command (MMBroadbandModemCinterion *self, + GSimpleAsyncResult *operation_result) +{ + if (self->priv->sleep_mode_cmd && + self->priv->sleep_mode_cmd[0]) { + mm_base_modem_at_command (MM_BASE_MODEM (self), + self->priv->sleep_mode_cmd, + 5, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)sleep_ready, + operation_result); + return; + } + + /* No default command; just finish without sending anything */ + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + g_simple_async_result_complete_in_idle (operation_result); + g_object_unref (operation_result); +} + +static void +supported_functionality_status_query_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + + g_assert (self->priv->sleep_mode_cmd == NULL); + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + mm_warn ("Couldn't query supported functionality status: '%s'", + error->message); + g_error_free (error); + self->priv->sleep_mode_cmd = g_strdup (""); + } else { + /* We need to get which power-off command to use to put the modem in low + * power mode (with serial port open for AT commands, but with RF switched + * off). According to the documentation of various Cinterion modems, some + * support AT+CFUN=4 (HC25) and those which don't support it can use + * AT+CFUN=7 (CYCLIC SLEEP mode with 2s timeout after last character + * received in the serial port). + * + * So, just look for '4' in the reply; if not found, look for '7', and if + * not found, report warning and don't use any. + */ + if (strstr (response, "4") != NULL) { + mm_dbg ("Device supports CFUN=4 sleep mode"); + self->priv->sleep_mode_cmd = g_strdup ("+CFUN=4"); + } else if (strstr (response, "7") != NULL) { + mm_dbg ("Device supports CFUN=7 sleep mode"); + self->priv->sleep_mode_cmd = g_strdup ("+CFUN=7"); + } else { + mm_warn ("Unknown functionality mode to go into sleep mode"); + self->priv->sleep_mode_cmd = g_strdup (""); + } + } + + send_sleep_mode_command (self, operation_result); +} + +static void +modem_power_down (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemCinterion *cinterion = MM_BROADBAND_MODEM_CINTERION (self); + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + modem_power_down); + + /* If sleep command already decided, use it. */ + if (cinterion->priv->sleep_mode_cmd) + send_sleep_mode_command (MM_BROADBAND_MODEM_CINTERION (self), + result); + else + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "+CFUN=?", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)supported_functionality_status_query_ready, + result); +} + +/*****************************************************************************/ +/* ACCESS TECHNOLOGIES */ + +static gboolean +load_access_technologies_finish (MMIfaceModem *self, + GAsyncResult *res, + MMModemAccessTechnology *access_technologies, + guint *mask, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return FALSE; + + *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT ( + g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); + *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; + return TRUE; +} + +static MMModemAccessTechnology +get_access_technology_from_smong_gprs_status (const gchar *gprs_status, + GError **error) +{ + if (strlen (gprs_status) == 1) { + switch (gprs_status[0]) { + case '0': + return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; + case '1': + case '2': + return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; + case '3': + case '4': + return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; + default: + break; + } + } + + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Couldn't get network capabilities, " + "invalid GPRS status value: '%s'", + gprs_status); + return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; +} + +static void +smong_query_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + GMatchInfo *match_info = NULL; + GRegex *regex; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + /* The AT^SMONG command returns a cell info table, where the second + * column identifies the "GPRS status", which is exactly what we want. + * So we'll try to read that second number in the values row. + * + * AT^SMONG + * GPRS Monitor + * BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell # + * 0776 1 - - 214 03 2 00 01 + * OK + */ + regex = g_regex_new (".*GPRS Monitor\\r\\n" + "BCCH\\s*G.*\\r\\n" + "(\\d*)\\s*(\\d*)\\s*", 0, 0, NULL); + if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { + gchar *gprs_status; + MMModemAccessTechnology act; + + gprs_status = g_match_info_fetch (match_info, 2); + act = get_access_technology_from_smong_gprs_status (gprs_status, &error); + g_free (gprs_status); + + if (error) + g_simple_async_result_take_error (operation_result, error); + else { + /* We'll default to use SMONG then */ + self->priv->sind_psinfo = FALSE; + g_simple_async_result_set_op_res_gpointer (operation_result, + GUINT_TO_POINTER (act), + NULL); + } + } else { + /* We'll reset here the flag to try to use SIND/psinfo the next time */ + self->priv->sind_psinfo = TRUE; + + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Couldn't get network capabilities, " + "invalid SMONG reply: '%s'", + response); + } + + g_match_info_free (match_info); + g_regex_unref (regex); + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static MMModemAccessTechnology +get_access_technology_from_psinfo (const gchar *psinfo, + GError **error) +{ + if (strlen (psinfo) == 1) { + switch (psinfo[0]) { + case '0': + return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; + case '1': + case '2': + return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; + case '3': + case '4': + return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; + case '5': + case '6': + return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; + case '7': + case '8': + return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; + default: + break; + } + } + + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Couldn't get network capabilities, " + "invalid psinfo value: '%s'", + psinfo); + return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; +} + +static void +sind_query_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + GMatchInfo *match_info = NULL; + GRegex *regex; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + /* The AT^SIND? command replies a list of several different indicators. + * We will only look for 'psinfo' which is the one which may tell us + * the available network access technology. Note that only 3G-enabled + * devices seem to have this indicator. + * + * AT+SIND? + * ^SIND: battchg,1,1 + * ^SIND: signal,1,99 + * ... + */ + regex = g_regex_new ("\\r\\n\\^SIND:\\s*psinfo,\\s*(\\d*),\\s*(\\d*)", 0, 0, NULL); + if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { + MMModemAccessTechnology act; + gchar *ind_value; + + ind_value = g_match_info_fetch (match_info, 2); + act = get_access_technology_from_psinfo (ind_value, &error); + g_free (ind_value); + g_simple_async_result_set_op_res_gpointer (operation_result, GUINT_TO_POINTER (act), NULL); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + } else { + /* If there was no 'psinfo' indicator, we'll try AT^SMONG and read the cell + * info table. */ + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "^SMONG", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)smong_query_ready, + operation_result); + } + + g_match_info_free (match_info); + g_regex_unref (regex); +} + +static void +load_access_technologies (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + load_access_technologies); + + if (broadband->priv->sind_psinfo) { + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "^SIND?", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)sind_query_ready, + result); + return; + } + + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "^SMONG", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)smong_query_ready, + result); +} + +/*****************************************************************************/ +/* SUPPORTED MODES */ + +static MMModemMode +load_supported_modes_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return MM_MODEM_MODE_NONE; + + return (MMModemMode) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +supported_networks_query_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + MMModemMode mode; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + /* Note: Documentation says that AT+WS46=? is replied with '+WS46:' followed + * by a list of supported network modes between parenthesis, but the EGS5 + * used to test this didn't use the 'WS46:' prefix. Also, more than one + * numeric ID may appear in the list, that's why they are checked + * separately. */ + + mode = MM_MODEM_MODE_NONE; + + if (strstr (response, "12") != NULL) { + mm_dbg ("Device allows 2G-only network mode"); + self->priv->only_geran = TRUE; + mode |= MM_MODEM_MODE_2G; + } + + if (strstr (response, "22") != NULL) { + mm_dbg ("Device allows 3G-only network mode"); + self->priv->only_utran = TRUE; + mode |= MM_MODEM_MODE_3G; + } + + if (strstr (response, "25") != NULL) { + mm_dbg ("Device allows 2G/3G network mode"); + self->priv->both_geran_utran = TRUE; + mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); + } + + /* If no expected ID found, error */ + if (mode == MM_MODEM_MODE_NONE) + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Invalid list of supported networks: '%s'", + response); + else + g_simple_async_result_set_op_res_gpointer (operation_result, + GUINT_TO_POINTER (mode), + NULL); + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +load_supported_modes (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + load_supported_modes); + + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + "+WS46=?", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)supported_networks_query_ready, + result); +} + +/*****************************************************************************/ +/* ALLOWED MODES */ + +static gboolean +set_allowed_modes_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +allowed_access_technology_update_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GError *error = NULL; + + mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (error) + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + else + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +set_allowed_modes (MMIfaceModem *self, + MMModemMode allowed, + MMModemMode preferred, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + set_allowed_modes); + + /* For dual 2G/3G devices... */ + if (broadband->priv->both_geran_utran) { + GString *cmd; + + /* We will try to simulate the possible allowed modes here. The + * Cinterion devices do not seem to allow setting preferred access + * technology in 3G devices, but they allow restricting to a given + * one: + * - 2G-only is forced by forcing GERAN RAT (AcT=0) + * - 3G-only is forced by forcing UTRAN RAT (AcT=2) + * - for the remaining ones, we default to automatic selection of RAT, + * which is based on the quality of the connection. + */ + cmd = g_string_new ("+COPS=,,,"); + if (allowed == MM_MODEM_MODE_3G && + preferred == MM_MODEM_MODE_NONE) { + g_string_append (cmd, "2"); + } else if (allowed == MM_MODEM_MODE_2G && + preferred == MM_MODEM_MODE_NONE) { + g_string_append (cmd, "0"); + } else { + gchar *allowed_str; + gchar *preferred_str; + + /* no AcT given, defaults to Auto */ + allowed_str = mm_modem_mode_build_string_from_mask (allowed); + preferred_str = mm_modem_mode_build_string_from_mask (preferred); + mm_warn ("Requested mode (allowed: '%s', preferred: '%s') not " + "supported by the modem. Defaulting to automatic mode.", + allowed_str, + preferred_str); + g_free (allowed_str); + g_free (preferred_str); + } + + mm_base_modem_at_command ( + MM_BASE_MODEM (self), + cmd->str, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)allowed_access_technology_update_ready, + result); + g_string_free (cmd, TRUE); + return; + } + + /* For 3G-only devices, allow only 3G-related allowed modes. + * For 2G-only devices, allow only 2G-related allowed modes. + * + * Note that the common logic of the interface already limits the + * allowed/preferred modes that can be tried in these cases. */ + if (broadband->priv->only_utran || + broadband->priv->only_geran) { + gchar *allowed_str; + gchar *preferred_str; + + allowed_str = mm_modem_mode_build_string_from_mask (allowed); + preferred_str = mm_modem_mode_build_string_from_mask (preferred); + mm_dbg ("Not doing anything. Assuming requested mode " + "(allowed: '%s', preferred: '%s') is supported by " + "%s-only modem.", + allowed_str, + preferred_str, + broadband->priv->only_utran ? "3G" : "2G"); + g_free (allowed_str); + g_free (preferred_str); + g_simple_async_result_set_op_res_gboolean (result, TRUE); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + return; + } + + g_assert_not_reached (); +} + +/*****************************************************************************/ +/* SUPPORTED BANDS */ + +static GArray * +load_supported_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + /* Never fails */ + return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +load_supported_bands (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); + GArray *bands; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + load_supported_bands); + + /* We do assume that we already know if the modem is 2G-only, 3G-only or + * 2G+3G. This is checked quite before trying to load supported bands. */ + + bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); + g_array_index (bands, MMModemBand, 0) = MM_MODEM_BAND_EGSM; + g_array_index (bands, MMModemBand, 1) = MM_MODEM_BAND_DCS; + g_array_index (bands, MMModemBand, 2) = MM_MODEM_BAND_PCS; + g_array_index (bands, MMModemBand, 3) = MM_MODEM_BAND_G850; + + /* Add 3G-specific bands */ + if (broadband->priv->only_utran || + broadband->priv->both_geran_utran) { + g_array_set_size (bands, 7); + g_array_index (bands, MMModemBand, 4) = MM_MODEM_BAND_U2100; + g_array_index (bands, MMModemBand, 5) = MM_MODEM_BAND_U1900; + g_array_index (bands, MMModemBand, 6) = MM_MODEM_BAND_U850; + } + + g_simple_async_result_set_op_res_gpointer (result, + bands, + (GDestroyNotify)g_array_unref); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); +} + +/*****************************************************************************/ +/* CURRENT BANDS */ + +static GArray * +load_current_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) + return NULL; + + return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( + G_SIMPLE_ASYNC_RESULT (res))); +} + +static void +get_2g_band_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + GArray *bands_array = NULL; + GRegex *regex; + GMatchInfo *match_info = NULL; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + /* The AT^SCFG? command replies a list of several different config + * values. We will only look for 'Radio/Band". + * + * AT+SCFG="Radio/Band" + * ^SCFG: "Radio/Band","0031","0031" + * + * Note that "0031" is a UCS2-encoded string, as we configured UCS2 as + * character set to use. + */ + regex = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"(.*)\",\\s*\"(.*)\"", 0, 0, NULL); + g_assert (regex != NULL); + + if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { + gchar *current; + + /* The first number given is the current band configuration, the + * second number given is the allowed band configuration, which we + * don't really need to get here. */ + current = g_match_info_fetch (match_info, 1); + if (current) { + guint i; + + /* If in UCS2, convert to UTF-8 */ + current = mm_broadband_modem_take_and_convert_to_utf8 (MM_BROADBAND_MODEM (self), + current); + + for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { + if (strcmp (bands_2g[i].cinterion_band, current) == 0) { + guint j; + + if (G_UNLIKELY (!bands_array)) + bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); + + for (j = 0; j < 4 && bands_2g[i].mm_bands[j] != 0; j++) + g_array_append_val (bands_array, bands_2g[i].mm_bands[j]); + + break; + } + } + + g_free (current); + } + } + + if (match_info) + g_match_info_free (match_info); + g_regex_unref (regex); + + if (!bands_array) + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse current bands reply"); + else + g_simple_async_result_set_op_res_gpointer (operation_result, + bands_array, + (GDestroyNotify)g_array_unref); + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +get_3g_band_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + const gchar *response; + GError *error = NULL; + GArray *bands_array = NULL; + GRegex *regex; + GMatchInfo *match_info = NULL; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!response) { + /* Let the error be critical. */ + g_simple_async_result_take_error (operation_result, error); + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); + return; + } + + /* The AT^SCFG? command replies a list of several different config + * values. We will only look for 'Radio/Band". + * + * AT+SCFG="Radio/Band" + * ^SCFG: "Radio/Band",127 + * + * Note that in this case, the replied is a number, not a string. + */ + regex = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*(\\d*)", 0, 0, NULL); + g_assert (regex != NULL); + + if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { + gchar *current; + + current = g_match_info_fetch (match_info, 1); + if (current) { + guint32 current_int; + guint i; + + current_int = (guint32) atoi (current); + + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { + if (current_int & bands_3g[i].cinterion_band_flag) { + if (G_UNLIKELY (!bands_array)) + bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); + g_array_append_val (bands_array, bands_3g[i].mm_band); + } + } + + g_free (current); + } + } + + if (match_info) + g_match_info_free (match_info); + g_regex_unref (regex); + + if (!bands_array) + g_simple_async_result_set_error (operation_result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't parse current bands reply"); + else + g_simple_async_result_set_op_res_gpointer (operation_result, + bands_array, + (GDestroyNotify)g_array_unref); + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +load_current_bands (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + load_current_bands); + + /* Query the currently used Radio/Band. The query command is the same for + * both 2G and 3G devices, but the reply reader is different. */ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "AT^SCFG=\"Radio/Band\"", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)((!broadband->priv->only_utran && + !broadband->priv->both_geran_utran) ? + get_2g_band_ready : + get_3g_band_ready), + result); +} + +/*****************************************************************************/ +/* SET BANDS */ + +static gboolean +set_bands_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +scfg_set_ready (MMBaseModem *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GError *error = NULL; + + if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) + /* Let the error be critical */ + g_simple_async_result_take_error (operation_result, error); + else + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +set_bands_3g (MMIfaceModem *self, + GArray *bands_array, + GSimpleAsyncResult *result) +{ + GArray *bands_array_final; + guint cinterion_band = 0; + guint i; + gchar *bands_string; + gchar *cmd; + + /* The special case of ANY should be treated separately. */ + if (bands_array->len == 1 && + g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { + /* We build an array with all bands to set; so that we use the same + * logic to build the cinterion_band, and so that we can log the list of + * bands being set properly */ + bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), G_N_ELEMENTS (bands_3g)); + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) + g_array_append_val (bands_array_final, bands_3g[i].mm_band); + } else + bands_array_final = g_array_ref (bands_array); + + for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { + guint j; + + for (j = 0; j < bands_array_final->len; j++) { + if (g_array_index (bands_array_final, MMModemBand, j) == bands_3g[i].mm_band) { + cinterion_band |= bands_3g[i].cinterion_band_flag; + break; + } + } + } + + bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data, + bands_array_final->len); + g_array_unref (bands_array_final); + + if (!cinterion_band) { + g_simple_async_result_set_error (result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "The given band combination is not supported: '%s'", + bands_string); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + g_free (bands_string); + return; + } + + mm_dbg ("Setting new bands to use: '%s'", bands_string); + + /* Following the setup: + * AT^SCFG="Radion/Band", + * We will set the preferred band equal to the allowed band, so that we force + * the modem to connect at that specific frequency only. Note that we will be + * passing a number here! + */ + cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u", cinterion_band); + mm_base_modem_at_command (MM_BASE_MODEM (self), + cmd, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)scfg_set_ready, + result); + g_free (cmd); + g_free (bands_string); +} + +static void +set_bands_2g (MMIfaceModem *self, + GArray *bands_array, + GSimpleAsyncResult *result) +{ + GArray *bands_array_final; + gchar *cinterion_band = NULL; + guint i; + gchar *bands_string; + gchar *cmd; + + /* If the iface properly checked the given list against the supported bands, + * it's not possible to get an array longer than 4 here. */ + g_assert (bands_array->len <= 4); + + /* The special case of ANY should be treated separately. */ + if (bands_array->len == 1 && + g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { + const CinterionBand2G *all; + + /* All bands is the last element in our 2G bands array */ + all = &bands_2g[G_N_ELEMENTS (bands_2g) - 1]; + + /* We build an array with all bands to set; so that we use the same + * logic to build the cinterion_band, and so that we can log the list of + * bands being set properly */ + bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); + g_array_append_vals (bands_array_final, all->mm_bands, all->n_mm_bands); + } else + bands_array_final = g_array_ref (bands_array); + + for (i = 0; !cinterion_band && i < G_N_ELEMENTS (bands_2g); i++) { + GArray *supported_combination; + + supported_combination = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), bands_2g[i].n_mm_bands); + g_array_append_vals (supported_combination, bands_2g[i].mm_bands, bands_2g[i].n_mm_bands); + + /* Check if the given array is exactly one of the supported combinations */ + if (mm_common_bands_garray_cmp (bands_array_final, supported_combination)) + cinterion_band = g_strdup (bands_2g[i].cinterion_band); + + g_array_unref (supported_combination); + } + + bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data, + bands_array_final->len); + g_array_unref (bands_array_final); + + if (!cinterion_band) { + g_simple_async_result_set_error (result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "The given band combination is not supported: '%s'", + bands_string); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + g_free (bands_string); + return; + } + + + mm_dbg ("Setting new bands to use: '%s'", bands_string); + cinterion_band = (mm_broadband_modem_take_and_convert_to_current_charset ( + MM_BROADBAND_MODEM (self), + cinterion_band)); + if (!cinterion_band) { + g_simple_async_result_set_error (result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Couldn't convert band set to current charset"); + g_simple_async_result_complete_in_idle (result); + g_object_unref (result); + g_free (bands_string); + return; + } + + /* Following the setup: + * AT^SCFG="Radion/Band",, + * We will set the preferred band equal to the allowed band, so that we force + * the modem to connect at that specific frequency only. Note that we will be + * passing double-quote enclosed strings here! + */ + cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", + cinterion_band, + cinterion_band); + + mm_base_modem_at_command (MM_BASE_MODEM (self), + cmd, + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)scfg_set_ready, + result); + + g_free (cmd); + g_free (cinterion_band); + g_free (bands_string); +} + +static void +set_bands (MMIfaceModem *self, + GArray *bands_array, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemCinterion *cinterion = MM_BROADBAND_MODEM_CINTERION (self); + GSimpleAsyncResult *result; + + /* The bands that we get here are previously validated by the interface, and + * that means that ALL the bands given here were also given in the list of + * supported bands. BUT BUT, that doesn't mean that the exact list of bands + * will end up being valid, as not all combinations are possible. E.g, + * Cinterion modems supporting only 2G have specific combinations allowed. + */ + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + set_bands); + + if (!cinterion->priv->only_utran && + !cinterion->priv->both_geran_utran) + set_bands_2g (self, bands_array, result); + else + set_bands_3g (self, bands_array, result); +} + +/*****************************************************************************/ +/* FLOW CONTROL */ + +static gboolean +setup_flow_control_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +setup_flow_control_ready (MMBroadbandModemCinterion *self, + GAsyncResult *res, + GSimpleAsyncResult *operation_result) +{ + GError *error = NULL; + + if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) + /* Let the error be critical. We DO need RTS/CTS in order to have + * proper modem disabling. */ + g_simple_async_result_take_error (operation_result, error); + else + g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); + + g_simple_async_result_complete (operation_result); + g_object_unref (operation_result); +} + +static void +setup_flow_control (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + + result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + setup_flow_control); + + /* We need to enable RTS/CTS so that CYCLIC SLEEP mode works */ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "\\Q3", + 3, + FALSE, + NULL, /* cancellable */ + (GAsyncReadyCallback)setup_flow_control_ready, + result); +} + +/*****************************************************************************/ + +MMBroadbandModemCinterion * +mm_broadband_modem_cinterion_new (const gchar *device, + const gchar *driver, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id) +{ + return g_object_new (MM_TYPE_BROADBAND_MODEM_CINTERION, + MM_BASE_MODEM_DEVICE, device, + MM_BASE_MODEM_DRIVER, driver, + MM_BASE_MODEM_PLUGIN, plugin, + MM_BASE_MODEM_VENDOR_ID, vendor_id, + MM_BASE_MODEM_PRODUCT_ID, product_id, + MM_IFACE_MODEM_MESSAGING_SMS_MEM3_STORAGE, MM_SMS_STORAGE_MT, + NULL); +} + +static void +mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self) +{ + /* Initialize private data */ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), + MM_TYPE_BROADBAND_MODEM_CINTERION, + MMBroadbandModemCinterionPrivate); + + /* Set defaults */ + self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */ +} + +static void +finalize (GObject *object) +{ + MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (object); + + g_free (self->priv->sleep_mode_cmd); + + G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object); +} + +static void +iface_modem_init (MMIfaceModem *iface) +{ + iface->load_supported_modes = load_supported_modes; + iface->load_supported_modes_finish = load_supported_modes_finish; + iface->set_allowed_modes = set_allowed_modes; + iface->set_allowed_modes_finish = set_allowed_modes_finish; + iface->load_supported_bands = load_supported_bands; + iface->load_supported_bands_finish = load_supported_bands_finish; + iface->load_current_bands = load_current_bands; + iface->load_current_bands_finish = load_current_bands_finish; + iface->set_bands = set_bands; + iface->set_bands_finish = set_bands_finish; + iface->load_access_technologies = load_access_technologies; + iface->load_access_technologies_finish = load_access_technologies_finish; + iface->setup_flow_control = setup_flow_control; + iface->setup_flow_control_finish = setup_flow_control_finish; + iface->modem_power_down = modem_power_down; + iface->modem_power_down_finish = modem_power_down_finish; +} + +static void +iface_modem_3gpp_init (MMIfaceModem3gpp *iface) +{ + iface->enable_unsolicited_events = enable_unsolicited_events; + iface->enable_unsolicited_events_finish = enable_unsolicited_events_finish; +} + +static void +iface_modem_messaging_init (MMIfaceModemMessaging *iface) +{ + iface->enable_unsolicited_events = messaging_enable_unsolicited_events; + iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; +} + +static void +mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMBroadbandModemCinterionPrivate)); + + /* Virtual methods */ + object_class->finalize = finalize; +} diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.h b/plugins/cinterion/mm-broadband-modem-cinterion.h new file mode 100644 index 00000000..53e95b4f --- /dev/null +++ b/plugins/cinterion/mm-broadband-modem-cinterion.h @@ -0,0 +1,51 @@ +/* -*- 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) 2011 Ammonit Measurement GmbH + * Copyright (C) 2011 Google Inc. + * Author: Aleksander Morgado + */ + +#ifndef MM_BROADBAND_MODEM_CINTERION_H +#define MM_BROADBAND_MODEM_CINTERION_H + +#include "mm-broadband-modem.h" + +#define MM_TYPE_BROADBAND_MODEM_CINTERION (mm_broadband_modem_cinterion_get_type ()) +#define MM_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterion)) +#define MM_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass)) +#define MM_IS_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION)) +#define MM_IS_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION)) +#define MM_BROADBAND_MODEM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass)) + +typedef struct _MMBroadbandModemCinterion MMBroadbandModemCinterion; +typedef struct _MMBroadbandModemCinterionClass MMBroadbandModemCinterionClass; +typedef struct _MMBroadbandModemCinterionPrivate MMBroadbandModemCinterionPrivate; + +struct _MMBroadbandModemCinterion { + MMBroadbandModem parent; + MMBroadbandModemCinterionPrivate *priv; +}; + +struct _MMBroadbandModemCinterionClass{ + MMBroadbandModemClass parent; +}; + +GType mm_broadband_modem_cinterion_get_type (void); + +MMBroadbandModemCinterion *mm_broadband_modem_cinterion_new (const gchar *device, + const gchar *driver, + const gchar *plugin, + guint16 vendor_id, + guint16 product_id); + +#endif /* MM_BROADBAND_MODEM_CINTERION_H */ diff --git a/plugins/cinterion/mm-plugin-cinterion.c b/plugins/cinterion/mm-plugin-cinterion.c new file mode 100644 index 00000000..28de08f1 --- /dev/null +++ b/plugins/cinterion/mm-plugin-cinterion.c @@ -0,0 +1,118 @@ +/* -*- 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) 2011 Ammonit Measurement GmbH + * Copyright (C) 2011 Google Inc. + * Author: Aleksander Morgado + */ + +#include +#include + +#include "mm-plugin-cinterion.h" +#include "mm-broadband-modem-cinterion.h" +#include "mm-errors-types.h" +#include "mm-log.h" + +G_DEFINE_TYPE (MMPluginCinterion, mm_plugin_cinterion, MM_TYPE_PLUGIN_BASE) + +int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; +int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; + +static MMBaseModem * +grab_port (MMPluginBase *base, + MMBaseModem *existing, + MMPortProbe *probe, + GError **error) +{ + MMBaseModem *modem = NULL; + const gchar *name, *subsys, *driver; + guint16 vendor = 0, product = 0; + + /* The Cinterion plugin cannot do anything with non-AT ports */ + if (!mm_port_probe_is_at (probe)) { + g_set_error_literal (error, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Ignoring non-AT port"); + return NULL; + } + + subsys = mm_port_probe_get_port_subsys (probe); + name = mm_port_probe_get_port_name (probe); + driver = mm_port_probe_get_port_driver (probe); + + /* Try to get Product IDs from udev. Note that it is not an error + * if we can't get them in our case, as we also support serial + * modems. */ + mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product); + + /* If this is the first port being grabbed, create a new modem object */ + if (!existing) + modem = MM_BASE_MODEM (mm_broadband_modem_cinterion_new (mm_port_probe_get_port_physdev (probe), + driver, + mm_plugin_get_name (MM_PLUGIN (base)), + vendor, + product)); + + if (!mm_base_modem_grab_port (existing ? existing : modem, + subsys, + name, + MM_PORT_TYPE_AT, /* we only allow AT ports here */ + MM_AT_PORT_FLAG_NONE, + error)) { + if (modem) + g_object_unref (modem); + return NULL; + } + + return existing ? existing : modem; +} + +/*****************************************************************************/ + +G_MODULE_EXPORT MMPlugin * +mm_plugin_create (void) +{ + static const gchar *subsystems[] = { "tty", NULL }; + static const gchar *vendor_strings[] = { "cinterion", "siemens", NULL }; + static const guint16 vendor_ids[] = { 0x1e2d, 0x0681, 0 }; + + return MM_PLUGIN ( + g_object_new (MM_TYPE_PLUGIN_CINTERION, + MM_PLUGIN_BASE_NAME, "Cinterion", + MM_PLUGIN_BASE_ALLOWED_SUBSYSTEMS, subsystems, + MM_PLUGIN_BASE_ALLOWED_VENDOR_STRINGS, vendor_strings, + MM_PLUGIN_BASE_ALLOWED_VENDOR_IDS, vendor_ids, + MM_PLUGIN_BASE_ALLOWED_AT, TRUE, + MM_PLUGIN_BASE_SORT_LAST, TRUE, + NULL)); +} + +static void +mm_plugin_cinterion_init (MMPluginCinterion *self) +{ +} + +static void +mm_plugin_cinterion_class_init (MMPluginCinterionClass *klass) +{ + MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); + + pb_class->grab_port = grab_port; +} diff --git a/plugins/cinterion/mm-plugin-cinterion.h b/plugins/cinterion/mm-plugin-cinterion.h new file mode 100644 index 00000000..13497653 --- /dev/null +++ b/plugins/cinterion/mm-plugin-cinterion.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. + * + * 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) 2011 Ammonit Measurement GmbH + * Author: Aleksander Morgado + */ + +#ifndef MM_PLUGIN_CINTERION_H +#define MM_PLUGIN_CINTERION_H + +#include "mm-plugin-base.h" + +#define MM_TYPE_PLUGIN_CINTERION (mm_plugin_cinterion_get_type ()) +#define MM_PLUGIN_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterion)) +#define MM_PLUGIN_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterionClass)) +#define MM_IS_PLUGIN_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_CINTERION)) +#define MM_IS_PLUGIN_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_CINTERION)) +#define MM_PLUGIN_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterionClass)) + +typedef struct { + MMPluginBase parent; +} MMPluginCinterion; + +typedef struct { + MMPluginBaseClass parent; +} MMPluginCinterionClass; + +GType mm_plugin_cinterion_get_type (void); + +G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); + +#endif /* MM_PLUGIN_CINTERION_H */ diff --git a/plugins/mm-broadband-modem-cinterion.c b/plugins/mm-broadband-modem-cinterion.c deleted file mode 100644 index e816ac88..00000000 --- a/plugins/mm-broadband-modem-cinterion.c +++ /dev/null @@ -1,1354 +0,0 @@ -/* -*- 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) 2011 Ammonit Measurement GmbH - * Copyright (C) 2011 Google Inc. - * Author: Aleksander Morgado - */ - -#include - -#include -#include -#include -#include -#include - -#include "ModemManager.h" -#include "mm-modem-helpers.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-iface-modem-messaging.h" -#include "mm-base-modem-at.h" -#include "mm-broadband-modem-cinterion.h" - -static void iface_modem_init (MMIfaceModem *iface); -static void iface_modem_3gpp_init (MMIfaceModem3gpp *iface); -static void iface_modem_messaging_init (MMIfaceModemMessaging *iface); - -G_DEFINE_TYPE_EXTENDED (MMBroadbandModemCinterion, mm_broadband_modem_cinterion, MM_TYPE_BROADBAND_MODEM, 0, - G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) - G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) - G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init)); - -struct _MMBroadbandModemCinterionPrivate { - /* Flag to know if we should try AT^SIND or not to get psinfo */ - gboolean sind_psinfo; - - /* Command to go into sleep mode */ - gchar *sleep_mode_cmd; - - /* Supported networks */ - gboolean only_geran; - gboolean only_utran; - gboolean both_geran_utran; -}; - -/* Setup relationship between the band bitmask in the modem and the bitmask - * in ModemManager. */ -typedef struct { - gchar *cinterion_band; - guint n_mm_bands; - MMModemBand mm_bands [4]; -} CinterionBand2G; - -/* Table checked in both MC75i (GPRS/EDGE) and EGS5 (GPRS) references. - * Note that the modem's configuration is also based on a bitmask, but as we - * will just support some of the combinations, we just use strings for them. - */ -static const CinterionBand2G bands_2g[] = { - { "1", 1, { MM_MODEM_BAND_EGSM, 0, 0, 0 }}, - { "2", 1, { MM_MODEM_BAND_DCS, 0, 0, 0 }}, - { "4", 1, { MM_MODEM_BAND_PCS, 0, 0, 0 }}, - { "8", 1, { MM_MODEM_BAND_G850, 0, 0, 0 }}, - { "3", 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, 0, 0 }}, - { "5", 2, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_PCS, 0, 0 }}, - { "10", 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_DCS, 0, 0 }}, - { "12", 2, { MM_MODEM_BAND_G850, MM_MODEM_BAND_PCS, 0, 0 }}, - { "15", 4, { MM_MODEM_BAND_EGSM, MM_MODEM_BAND_DCS, MM_MODEM_BAND_PCS, MM_MODEM_BAND_G850 }} -}; - -/* Setup relationship between the 3G band bitmask in the modem and the bitmask - * in ModemManager. */ -typedef struct { - guint32 cinterion_band_flag; - MMModemBand mm_band; -} CinterionBand3G; - -/* Table checked in HC25 (3G) reference. This table includes both 2G and 3G - * frequencies. Depending on which one is configured, one access technology or - * the other will be used. This may conflict with the allowed mode configuration - * set, so you shouldn't for example set 3G frequency bands, and then use a - * 2G-only allowed mode. */ -static const CinterionBand3G bands_3g[] = { - { (1 << 0), MM_MODEM_BAND_EGSM }, - { (1 << 1), MM_MODEM_BAND_DCS }, - { (1 << 2), MM_MODEM_BAND_PCS }, - { (1 << 3), MM_MODEM_BAND_G850 }, - { (1 << 4), MM_MODEM_BAND_U2100 }, - { (1 << 5), MM_MODEM_BAND_U1900 }, - { (1 << 6), MM_MODEM_BAND_U850 } -}; - -/*****************************************************************************/ -/* Unsolicited events enabling */ - -static gboolean -enable_unsolicited_events_finish (MMIfaceModem3gpp *self, - GAsyncResult *res, - GError **error) -{ - return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); -} - -static void -enable_unsolicited_events (MMIfaceModem3gpp *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - mm_base_modem_at_command_in_port ( - MM_BASE_MODEM (self), - /* Only primary port is expected in the Cinterion modems */ - mm_base_modem_get_port_primary (MM_BASE_MODEM (self)), - /* AT=CMER=[[,[,[,[,]]]]] - * but should be either not set, or equal to 0 or 2. - * Enabled with 2. - */ - "+CMER=3,0,0,2", - 3, - FALSE, - NULL, /* cancellable */ - callback, - user_data); -} - -/*****************************************************************************/ -/* Enable unsolicited events (SMS indications) (Messaging interface) */ - -static gboolean -messaging_enable_unsolicited_events_finish (MMIfaceModemMessaging *self, - GAsyncResult *res, - GError **error) -{ - return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); -} - -static void -messaging_enable_unsolicited_events (MMIfaceModemMessaging *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - /* AT+CNMI=,[[,[,[,]]]] - * but should be either not set, or equal to 1; - * and can be only either 0 or 2 (EGS5) - */ - mm_base_modem_at_command (MM_BASE_MODEM (self), - "+CNMI=2,1,2,2,1", - 3, - FALSE, - NULL, /* cancellable */ - callback, - user_data); -} - -/*****************************************************************************/ -/* MODEM POWER DOWN */ - -static gboolean -modem_power_down_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void -sleep_ready (MMBaseModem *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - GError *error = NULL; - - mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - - /* Ignore errors */ - if (error) { - mm_dbg ("Couldn't send power down command: '%s'", error->message); - g_error_free (error); - } - - g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); - g_simple_async_result_complete_in_idle (operation_result); - g_object_unref (operation_result); -} - -static void -send_sleep_mode_command (MMBroadbandModemCinterion *self, - GSimpleAsyncResult *operation_result) -{ - if (self->priv->sleep_mode_cmd && - self->priv->sleep_mode_cmd[0]) { - mm_base_modem_at_command (MM_BASE_MODEM (self), - self->priv->sleep_mode_cmd, - 5, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)sleep_ready, - operation_result); - return; - } - - /* No default command; just finish without sending anything */ - g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); - g_simple_async_result_complete_in_idle (operation_result); - g_object_unref (operation_result); -} - -static void -supported_functionality_status_query_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - const gchar *response; - GError *error = NULL; - - g_assert (self->priv->sleep_mode_cmd == NULL); - - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!response) { - mm_warn ("Couldn't query supported functionality status: '%s'", - error->message); - g_error_free (error); - self->priv->sleep_mode_cmd = g_strdup (""); - } else { - /* We need to get which power-off command to use to put the modem in low - * power mode (with serial port open for AT commands, but with RF switched - * off). According to the documentation of various Cinterion modems, some - * support AT+CFUN=4 (HC25) and those which don't support it can use - * AT+CFUN=7 (CYCLIC SLEEP mode with 2s timeout after last character - * received in the serial port). - * - * So, just look for '4' in the reply; if not found, look for '7', and if - * not found, report warning and don't use any. - */ - if (strstr (response, "4") != NULL) { - mm_dbg ("Device supports CFUN=4 sleep mode"); - self->priv->sleep_mode_cmd = g_strdup ("+CFUN=4"); - } else if (strstr (response, "7") != NULL) { - mm_dbg ("Device supports CFUN=7 sleep mode"); - self->priv->sleep_mode_cmd = g_strdup ("+CFUN=7"); - } else { - mm_warn ("Unknown functionality mode to go into sleep mode"); - self->priv->sleep_mode_cmd = g_strdup (""); - } - } - - send_sleep_mode_command (self, operation_result); -} - -static void -modem_power_down (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - MMBroadbandModemCinterion *cinterion = MM_BROADBAND_MODEM_CINTERION (self); - GSimpleAsyncResult *result; - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - modem_power_down); - - /* If sleep command already decided, use it. */ - if (cinterion->priv->sleep_mode_cmd) - send_sleep_mode_command (MM_BROADBAND_MODEM_CINTERION (self), - result); - else - mm_base_modem_at_command ( - MM_BASE_MODEM (self), - "+CFUN=?", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)supported_functionality_status_query_ready, - result); -} - -/*****************************************************************************/ -/* ACCESS TECHNOLOGIES */ - -static gboolean -load_access_technologies_finish (MMIfaceModem *self, - GAsyncResult *res, - MMModemAccessTechnology *access_technologies, - guint *mask, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return FALSE; - - *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT ( - g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (res))); - *mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY; - return TRUE; -} - -static MMModemAccessTechnology -get_access_technology_from_smong_gprs_status (const gchar *gprs_status, - GError **error) -{ - if (strlen (gprs_status) == 1) { - switch (gprs_status[0]) { - case '0': - return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; - case '1': - case '2': - return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; - case '3': - case '4': - return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; - default: - break; - } - } - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Couldn't get network capabilities, " - "invalid GPRS status value: '%s'", - gprs_status); - return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; -} - -static void -smong_query_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - const gchar *response; - GError *error = NULL; - GMatchInfo *match_info = NULL; - GRegex *regex; - - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!response) { - /* Let the error be critical. */ - g_simple_async_result_take_error (operation_result, error); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - return; - } - - /* The AT^SMONG command returns a cell info table, where the second - * column identifies the "GPRS status", which is exactly what we want. - * So we'll try to read that second number in the values row. - * - * AT^SMONG - * GPRS Monitor - * BCCH G PBCCH PAT MCC MNC NOM TA RAC # Cell # - * 0776 1 - - 214 03 2 00 01 - * OK - */ - regex = g_regex_new (".*GPRS Monitor\\r\\n" - "BCCH\\s*G.*\\r\\n" - "(\\d*)\\s*(\\d*)\\s*", 0, 0, NULL); - if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { - gchar *gprs_status; - MMModemAccessTechnology act; - - gprs_status = g_match_info_fetch (match_info, 2); - act = get_access_technology_from_smong_gprs_status (gprs_status, &error); - g_free (gprs_status); - - if (error) - g_simple_async_result_take_error (operation_result, error); - else { - /* We'll default to use SMONG then */ - self->priv->sind_psinfo = FALSE; - g_simple_async_result_set_op_res_gpointer (operation_result, - GUINT_TO_POINTER (act), - NULL); - } - } else { - /* We'll reset here the flag to try to use SIND/psinfo the next time */ - self->priv->sind_psinfo = TRUE; - - g_simple_async_result_set_error (operation_result, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Couldn't get network capabilities, " - "invalid SMONG reply: '%s'", - response); - } - - g_match_info_free (match_info); - g_regex_unref (regex); - - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static MMModemAccessTechnology -get_access_technology_from_psinfo (const gchar *psinfo, - GError **error) -{ - if (strlen (psinfo) == 1) { - switch (psinfo[0]) { - case '0': - return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; - case '1': - case '2': - return MM_MODEM_ACCESS_TECHNOLOGY_GPRS; - case '3': - case '4': - return MM_MODEM_ACCESS_TECHNOLOGY_EDGE; - case '5': - case '6': - return MM_MODEM_ACCESS_TECHNOLOGY_UMTS; - case '7': - case '8': - return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA; - default: - break; - } - } - - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_INVALID_ARGS, - "Couldn't get network capabilities, " - "invalid psinfo value: '%s'", - psinfo); - return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN; -} - -static void -sind_query_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - const gchar *response; - GError *error = NULL; - GMatchInfo *match_info = NULL; - GRegex *regex; - - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!response) { - /* Let the error be critical. */ - g_simple_async_result_take_error (operation_result, error); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - return; - } - - /* The AT^SIND? command replies a list of several different indicators. - * We will only look for 'psinfo' which is the one which may tell us - * the available network access technology. Note that only 3G-enabled - * devices seem to have this indicator. - * - * AT+SIND? - * ^SIND: battchg,1,1 - * ^SIND: signal,1,99 - * ... - */ - regex = g_regex_new ("\\r\\n\\^SIND:\\s*psinfo,\\s*(\\d*),\\s*(\\d*)", 0, 0, NULL); - if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { - MMModemAccessTechnology act; - gchar *ind_value; - - ind_value = g_match_info_fetch (match_info, 2); - act = get_access_technology_from_psinfo (ind_value, &error); - g_free (ind_value); - g_simple_async_result_set_op_res_gpointer (operation_result, GUINT_TO_POINTER (act), NULL); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - } else { - /* If there was no 'psinfo' indicator, we'll try AT^SMONG and read the cell - * info table. */ - mm_base_modem_at_command ( - MM_BASE_MODEM (self), - "^SMONG", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)smong_query_ready, - operation_result); - } - - g_match_info_free (match_info); - g_regex_unref (regex); -} - -static void -load_access_technologies (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); - GSimpleAsyncResult *result; - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - load_access_technologies); - - if (broadband->priv->sind_psinfo) { - mm_base_modem_at_command ( - MM_BASE_MODEM (self), - "^SIND?", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)sind_query_ready, - result); - return; - } - - mm_base_modem_at_command ( - MM_BASE_MODEM (self), - "^SMONG", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)smong_query_ready, - result); -} - -/*****************************************************************************/ -/* SUPPORTED MODES */ - -static MMModemMode -load_supported_modes_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return MM_MODEM_MODE_NONE; - - return (MMModemMode) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (res))); -} - -static void -supported_networks_query_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - const gchar *response; - GError *error = NULL; - MMModemMode mode; - - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!response) { - /* Let the error be critical. */ - g_simple_async_result_take_error (operation_result, error); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - return; - } - - /* Note: Documentation says that AT+WS46=? is replied with '+WS46:' followed - * by a list of supported network modes between parenthesis, but the EGS5 - * used to test this didn't use the 'WS46:' prefix. Also, more than one - * numeric ID may appear in the list, that's why they are checked - * separately. */ - - mode = MM_MODEM_MODE_NONE; - - if (strstr (response, "12") != NULL) { - mm_dbg ("Device allows 2G-only network mode"); - self->priv->only_geran = TRUE; - mode |= MM_MODEM_MODE_2G; - } - - if (strstr (response, "22") != NULL) { - mm_dbg ("Device allows 3G-only network mode"); - self->priv->only_utran = TRUE; - mode |= MM_MODEM_MODE_3G; - } - - if (strstr (response, "25") != NULL) { - mm_dbg ("Device allows 2G/3G network mode"); - self->priv->both_geran_utran = TRUE; - mode |= (MM_MODEM_MODE_2G | MM_MODEM_MODE_3G); - } - - /* If no expected ID found, error */ - if (mode == MM_MODEM_MODE_NONE) - g_simple_async_result_set_error (operation_result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Invalid list of supported networks: '%s'", - response); - else - g_simple_async_result_set_op_res_gpointer (operation_result, - GUINT_TO_POINTER (mode), - NULL); - - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static void -load_supported_modes (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - load_supported_modes); - - mm_base_modem_at_command ( - MM_BASE_MODEM (self), - "+WS46=?", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)supported_networks_query_ready, - result); -} - -/*****************************************************************************/ -/* ALLOWED MODES */ - -static gboolean -set_allowed_modes_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void -allowed_access_technology_update_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - GError *error = NULL; - - mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (error) - /* Let the error be critical. */ - g_simple_async_result_take_error (operation_result, error); - else - g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static void -set_allowed_modes (MMIfaceModem *self, - MMModemMode allowed, - MMModemMode preferred, - GAsyncReadyCallback callback, - gpointer user_data) -{ - MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); - GSimpleAsyncResult *result; - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - set_allowed_modes); - - /* For dual 2G/3G devices... */ - if (broadband->priv->both_geran_utran) { - GString *cmd; - - /* We will try to simulate the possible allowed modes here. The - * Cinterion devices do not seem to allow setting preferred access - * technology in 3G devices, but they allow restricting to a given - * one: - * - 2G-only is forced by forcing GERAN RAT (AcT=0) - * - 3G-only is forced by forcing UTRAN RAT (AcT=2) - * - for the remaining ones, we default to automatic selection of RAT, - * which is based on the quality of the connection. - */ - cmd = g_string_new ("+COPS=,,,"); - if (allowed == MM_MODEM_MODE_3G && - preferred == MM_MODEM_MODE_NONE) { - g_string_append (cmd, "2"); - } else if (allowed == MM_MODEM_MODE_2G && - preferred == MM_MODEM_MODE_NONE) { - g_string_append (cmd, "0"); - } else { - gchar *allowed_str; - gchar *preferred_str; - - /* no AcT given, defaults to Auto */ - allowed_str = mm_modem_mode_build_string_from_mask (allowed); - preferred_str = mm_modem_mode_build_string_from_mask (preferred); - mm_warn ("Requested mode (allowed: '%s', preferred: '%s') not " - "supported by the modem. Defaulting to automatic mode.", - allowed_str, - preferred_str); - g_free (allowed_str); - g_free (preferred_str); - } - - mm_base_modem_at_command ( - MM_BASE_MODEM (self), - cmd->str, - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)allowed_access_technology_update_ready, - result); - g_string_free (cmd, TRUE); - return; - } - - /* For 3G-only devices, allow only 3G-related allowed modes. - * For 2G-only devices, allow only 2G-related allowed modes. - * - * Note that the common logic of the interface already limits the - * allowed/preferred modes that can be tried in these cases. */ - if (broadband->priv->only_utran || - broadband->priv->only_geran) { - gchar *allowed_str; - gchar *preferred_str; - - allowed_str = mm_modem_mode_build_string_from_mask (allowed); - preferred_str = mm_modem_mode_build_string_from_mask (preferred); - mm_dbg ("Not doing anything. Assuming requested mode " - "(allowed: '%s', preferred: '%s') is supported by " - "%s-only modem.", - allowed_str, - preferred_str, - broadband->priv->only_utran ? "3G" : "2G"); - g_free (allowed_str); - g_free (preferred_str); - g_simple_async_result_set_op_res_gboolean (result, TRUE); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - return; - } - - g_assert_not_reached (); -} - -/*****************************************************************************/ -/* SUPPORTED BANDS */ - -static GArray * -load_supported_bands_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - /* Never fails */ - return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (res))); -} - -static void -load_supported_bands (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); - GArray *bands; - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - load_supported_bands); - - /* We do assume that we already know if the modem is 2G-only, 3G-only or - * 2G+3G. This is checked quite before trying to load supported bands. */ - - bands = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); - g_array_index (bands, MMModemBand, 0) = MM_MODEM_BAND_EGSM; - g_array_index (bands, MMModemBand, 1) = MM_MODEM_BAND_DCS; - g_array_index (bands, MMModemBand, 2) = MM_MODEM_BAND_PCS; - g_array_index (bands, MMModemBand, 3) = MM_MODEM_BAND_G850; - - /* Add 3G-specific bands */ - if (broadband->priv->only_utran || - broadband->priv->both_geran_utran) { - g_array_set_size (bands, 7); - g_array_index (bands, MMModemBand, 4) = MM_MODEM_BAND_U2100; - g_array_index (bands, MMModemBand, 5) = MM_MODEM_BAND_U1900; - g_array_index (bands, MMModemBand, 6) = MM_MODEM_BAND_U850; - } - - g_simple_async_result_set_op_res_gpointer (result, - bands, - (GDestroyNotify)g_array_unref); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); -} - -/*****************************************************************************/ -/* CURRENT BANDS */ - -static GArray * -load_current_bands_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error)) - return NULL; - - return (GArray *) g_array_ref (g_simple_async_result_get_op_res_gpointer ( - G_SIMPLE_ASYNC_RESULT (res))); -} - -static void -get_2g_band_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - const gchar *response; - GError *error = NULL; - GArray *bands_array = NULL; - GRegex *regex; - GMatchInfo *match_info = NULL; - - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!response) { - /* Let the error be critical. */ - g_simple_async_result_take_error (operation_result, error); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - return; - } - - /* The AT^SCFG? command replies a list of several different config - * values. We will only look for 'Radio/Band". - * - * AT+SCFG="Radio/Band" - * ^SCFG: "Radio/Band","0031","0031" - * - * Note that "0031" is a UCS2-encoded string, as we configured UCS2 as - * character set to use. - */ - regex = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*\"(.*)\",\\s*\"(.*)\"", 0, 0, NULL); - g_assert (regex != NULL); - - if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { - gchar *current; - - /* The first number given is the current band configuration, the - * second number given is the allowed band configuration, which we - * don't really need to get here. */ - current = g_match_info_fetch (match_info, 1); - if (current) { - guint i; - - /* If in UCS2, convert to UTF-8 */ - current = mm_broadband_modem_take_and_convert_to_utf8 (MM_BROADBAND_MODEM (self), - current); - - for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { - if (strcmp (bands_2g[i].cinterion_band, current) == 0) { - guint j; - - if (G_UNLIKELY (!bands_array)) - bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); - - for (j = 0; j < 4 && bands_2g[i].mm_bands[j] != 0; j++) - g_array_append_val (bands_array, bands_2g[i].mm_bands[j]); - - break; - } - } - - g_free (current); - } - } - - if (match_info) - g_match_info_free (match_info); - g_regex_unref (regex); - - if (!bands_array) - g_simple_async_result_set_error (operation_result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't parse current bands reply"); - else - g_simple_async_result_set_op_res_gpointer (operation_result, - bands_array, - (GDestroyNotify)g_array_unref); - - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static void -get_3g_band_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - const gchar *response; - GError *error = NULL; - GArray *bands_array = NULL; - GRegex *regex; - GMatchInfo *match_info = NULL; - - response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!response) { - /* Let the error be critical. */ - g_simple_async_result_take_error (operation_result, error); - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); - return; - } - - /* The AT^SCFG? command replies a list of several different config - * values. We will only look for 'Radio/Band". - * - * AT+SCFG="Radio/Band" - * ^SCFG: "Radio/Band",127 - * - * Note that in this case, the replied is a number, not a string. - */ - regex = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*(\\d*)", 0, 0, NULL); - g_assert (regex != NULL); - - if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) { - gchar *current; - - current = g_match_info_fetch (match_info, 1); - if (current) { - guint32 current_int; - guint i; - - current_int = (guint32) atoi (current); - - for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { - if (current_int & bands_3g[i].cinterion_band_flag) { - if (G_UNLIKELY (!bands_array)) - bands_array = g_array_new (FALSE, FALSE, sizeof (MMModemBand)); - g_array_append_val (bands_array, bands_3g[i].mm_band); - } - } - - g_free (current); - } - } - - if (match_info) - g_match_info_free (match_info); - g_regex_unref (regex); - - if (!bands_array) - g_simple_async_result_set_error (operation_result, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't parse current bands reply"); - else - g_simple_async_result_set_op_res_gpointer (operation_result, - bands_array, - (GDestroyNotify)g_array_unref); - - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static void -load_current_bands (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self); - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - load_current_bands); - - /* Query the currently used Radio/Band. The query command is the same for - * both 2G and 3G devices, but the reply reader is different. */ - mm_base_modem_at_command (MM_BASE_MODEM (self), - "AT^SCFG=\"Radio/Band\"", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)((!broadband->priv->only_utran && - !broadband->priv->both_geran_utran) ? - get_2g_band_ready : - get_3g_band_ready), - result); -} - -/*****************************************************************************/ -/* SET BANDS */ - -static gboolean -set_bands_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void -scfg_set_ready (MMBaseModem *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - GError *error = NULL; - - if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) - /* Let the error be critical */ - g_simple_async_result_take_error (operation_result, error); - else - g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); - - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static void -set_bands_3g (MMIfaceModem *self, - GArray *bands_array, - GSimpleAsyncResult *result) -{ - GArray *bands_array_final; - guint cinterion_band = 0; - guint i; - gchar *bands_string; - gchar *cmd; - - /* The special case of ANY should be treated separately. */ - if (bands_array->len == 1 && - g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { - /* We build an array with all bands to set; so that we use the same - * logic to build the cinterion_band, and so that we can log the list of - * bands being set properly */ - bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), G_N_ELEMENTS (bands_3g)); - for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) - g_array_append_val (bands_array_final, bands_3g[i].mm_band); - } else - bands_array_final = g_array_ref (bands_array); - - for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { - guint j; - - for (j = 0; j < bands_array_final->len; j++) { - if (g_array_index (bands_array_final, MMModemBand, j) == bands_3g[i].mm_band) { - cinterion_band |= bands_3g[i].cinterion_band_flag; - break; - } - } - } - - bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data, - bands_array_final->len); - g_array_unref (bands_array_final); - - if (!cinterion_band) { - g_simple_async_result_set_error (result, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "The given band combination is not supported: '%s'", - bands_string); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - g_free (bands_string); - return; - } - - mm_dbg ("Setting new bands to use: '%s'", bands_string); - - /* Following the setup: - * AT^SCFG="Radion/Band", - * We will set the preferred band equal to the allowed band, so that we force - * the modem to connect at that specific frequency only. Note that we will be - * passing a number here! - */ - cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",%u", cinterion_band); - mm_base_modem_at_command (MM_BASE_MODEM (self), - cmd, - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)scfg_set_ready, - result); - g_free (cmd); - g_free (bands_string); -} - -static void -set_bands_2g (MMIfaceModem *self, - GArray *bands_array, - GSimpleAsyncResult *result) -{ - GArray *bands_array_final; - gchar *cinterion_band = NULL; - guint i; - gchar *bands_string; - gchar *cmd; - - /* If the iface properly checked the given list against the supported bands, - * it's not possible to get an array longer than 4 here. */ - g_assert (bands_array->len <= 4); - - /* The special case of ANY should be treated separately. */ - if (bands_array->len == 1 && - g_array_index (bands_array, MMModemBand, 0) == MM_MODEM_BAND_ANY) { - const CinterionBand2G *all; - - /* All bands is the last element in our 2G bands array */ - all = &bands_2g[G_N_ELEMENTS (bands_2g) - 1]; - - /* We build an array with all bands to set; so that we use the same - * logic to build the cinterion_band, and so that we can log the list of - * bands being set properly */ - bands_array_final = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), 4); - g_array_append_vals (bands_array_final, all->mm_bands, all->n_mm_bands); - } else - bands_array_final = g_array_ref (bands_array); - - for (i = 0; !cinterion_band && i < G_N_ELEMENTS (bands_2g); i++) { - GArray *supported_combination; - - supported_combination = g_array_sized_new (FALSE, FALSE, sizeof (MMModemBand), bands_2g[i].n_mm_bands); - g_array_append_vals (supported_combination, bands_2g[i].mm_bands, bands_2g[i].n_mm_bands); - - /* Check if the given array is exactly one of the supported combinations */ - if (mm_common_bands_garray_cmp (bands_array_final, supported_combination)) - cinterion_band = g_strdup (bands_2g[i].cinterion_band); - - g_array_unref (supported_combination); - } - - bands_string = mm_common_build_bands_string ((MMModemBand *)bands_array_final->data, - bands_array_final->len); - g_array_unref (bands_array_final); - - if (!cinterion_band) { - g_simple_async_result_set_error (result, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "The given band combination is not supported: '%s'", - bands_string); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - g_free (bands_string); - return; - } - - - mm_dbg ("Setting new bands to use: '%s'", bands_string); - cinterion_band = (mm_broadband_modem_take_and_convert_to_current_charset ( - MM_BROADBAND_MODEM (self), - cinterion_band)); - if (!cinterion_band) { - g_simple_async_result_set_error (result, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "Couldn't convert band set to current charset"); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - g_free (bands_string); - return; - } - - /* Following the setup: - * AT^SCFG="Radion/Band",, - * We will set the preferred band equal to the allowed band, so that we force - * the modem to connect at that specific frequency only. Note that we will be - * passing double-quote enclosed strings here! - */ - cmd = g_strdup_printf ("^SCFG=\"Radio/Band\",\"%s\",\"%s\"", - cinterion_band, - cinterion_band); - - mm_base_modem_at_command (MM_BASE_MODEM (self), - cmd, - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)scfg_set_ready, - result); - - g_free (cmd); - g_free (cinterion_band); - g_free (bands_string); -} - -static void -set_bands (MMIfaceModem *self, - GArray *bands_array, - GAsyncReadyCallback callback, - gpointer user_data) -{ - MMBroadbandModemCinterion *cinterion = MM_BROADBAND_MODEM_CINTERION (self); - GSimpleAsyncResult *result; - - /* The bands that we get here are previously validated by the interface, and - * that means that ALL the bands given here were also given in the list of - * supported bands. BUT BUT, that doesn't mean that the exact list of bands - * will end up being valid, as not all combinations are possible. E.g, - * Cinterion modems supporting only 2G have specific combinations allowed. - */ - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - set_bands); - - if (!cinterion->priv->only_utran && - !cinterion->priv->both_geran_utran) - set_bands_2g (self, bands_array, result); - else - set_bands_3g (self, bands_array, result); -} - -/*****************************************************************************/ -/* FLOW CONTROL */ - -static gboolean -setup_flow_control_finish (MMIfaceModem *self, - GAsyncResult *res, - GError **error) -{ - return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); -} - -static void -setup_flow_control_ready (MMBroadbandModemCinterion *self, - GAsyncResult *res, - GSimpleAsyncResult *operation_result) -{ - GError *error = NULL; - - if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) - /* Let the error be critical. We DO need RTS/CTS in order to have - * proper modem disabling. */ - g_simple_async_result_take_error (operation_result, error); - else - g_simple_async_result_set_op_res_gboolean (operation_result, TRUE); - - g_simple_async_result_complete (operation_result); - g_object_unref (operation_result); -} - -static void -setup_flow_control (MMIfaceModem *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - - result = g_simple_async_result_new (G_OBJECT (self), - callback, - user_data, - setup_flow_control); - - /* We need to enable RTS/CTS so that CYCLIC SLEEP mode works */ - mm_base_modem_at_command (MM_BASE_MODEM (self), - "\\Q3", - 3, - FALSE, - NULL, /* cancellable */ - (GAsyncReadyCallback)setup_flow_control_ready, - result); -} - -/*****************************************************************************/ - -MMBroadbandModemCinterion * -mm_broadband_modem_cinterion_new (const gchar *device, - const gchar *driver, - const gchar *plugin, - guint16 vendor_id, - guint16 product_id) -{ - return g_object_new (MM_TYPE_BROADBAND_MODEM_CINTERION, - MM_BASE_MODEM_DEVICE, device, - MM_BASE_MODEM_DRIVER, driver, - MM_BASE_MODEM_PLUGIN, plugin, - MM_BASE_MODEM_VENDOR_ID, vendor_id, - MM_BASE_MODEM_PRODUCT_ID, product_id, - MM_IFACE_MODEM_MESSAGING_SMS_MEM3_STORAGE, MM_SMS_STORAGE_MT, - NULL); -} - -static void -mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self) -{ - /* Initialize private data */ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), - MM_TYPE_BROADBAND_MODEM_CINTERION, - MMBroadbandModemCinterionPrivate); - - /* Set defaults */ - self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */ -} - -static void -finalize (GObject *object) -{ - MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (object); - - g_free (self->priv->sleep_mode_cmd); - - G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object); -} - -static void -iface_modem_init (MMIfaceModem *iface) -{ - iface->load_supported_modes = load_supported_modes; - iface->load_supported_modes_finish = load_supported_modes_finish; - iface->set_allowed_modes = set_allowed_modes; - iface->set_allowed_modes_finish = set_allowed_modes_finish; - iface->load_supported_bands = load_supported_bands; - iface->load_supported_bands_finish = load_supported_bands_finish; - iface->load_current_bands = load_current_bands; - iface->load_current_bands_finish = load_current_bands_finish; - iface->set_bands = set_bands; - iface->set_bands_finish = set_bands_finish; - iface->load_access_technologies = load_access_technologies; - iface->load_access_technologies_finish = load_access_technologies_finish; - iface->setup_flow_control = setup_flow_control; - iface->setup_flow_control_finish = setup_flow_control_finish; - iface->modem_power_down = modem_power_down; - iface->modem_power_down_finish = modem_power_down_finish; -} - -static void -iface_modem_3gpp_init (MMIfaceModem3gpp *iface) -{ - iface->enable_unsolicited_events = enable_unsolicited_events; - iface->enable_unsolicited_events_finish = enable_unsolicited_events_finish; -} - -static void -iface_modem_messaging_init (MMIfaceModemMessaging *iface) -{ - iface->enable_unsolicited_events = messaging_enable_unsolicited_events; - iface->enable_unsolicited_events_finish = messaging_enable_unsolicited_events_finish; -} - -static void -mm_broadband_modem_cinterion_class_init (MMBroadbandModemCinterionClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (object_class, sizeof (MMBroadbandModemCinterionPrivate)); - - /* Virtual methods */ - object_class->finalize = finalize; -} diff --git a/plugins/mm-broadband-modem-cinterion.h b/plugins/mm-broadband-modem-cinterion.h deleted file mode 100644 index 53e95b4f..00000000 --- a/plugins/mm-broadband-modem-cinterion.h +++ /dev/null @@ -1,51 +0,0 @@ -/* -*- 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) 2011 Ammonit Measurement GmbH - * Copyright (C) 2011 Google Inc. - * Author: Aleksander Morgado - */ - -#ifndef MM_BROADBAND_MODEM_CINTERION_H -#define MM_BROADBAND_MODEM_CINTERION_H - -#include "mm-broadband-modem.h" - -#define MM_TYPE_BROADBAND_MODEM_CINTERION (mm_broadband_modem_cinterion_get_type ()) -#define MM_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterion)) -#define MM_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass)) -#define MM_IS_BROADBAND_MODEM_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION)) -#define MM_IS_BROADBAND_MODEM_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BROADBAND_MODEM_CINTERION)) -#define MM_BROADBAND_MODEM_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BROADBAND_MODEM_CINTERION, MMBroadbandModemCinterionClass)) - -typedef struct _MMBroadbandModemCinterion MMBroadbandModemCinterion; -typedef struct _MMBroadbandModemCinterionClass MMBroadbandModemCinterionClass; -typedef struct _MMBroadbandModemCinterionPrivate MMBroadbandModemCinterionPrivate; - -struct _MMBroadbandModemCinterion { - MMBroadbandModem parent; - MMBroadbandModemCinterionPrivate *priv; -}; - -struct _MMBroadbandModemCinterionClass{ - MMBroadbandModemClass parent; -}; - -GType mm_broadband_modem_cinterion_get_type (void); - -MMBroadbandModemCinterion *mm_broadband_modem_cinterion_new (const gchar *device, - const gchar *driver, - const gchar *plugin, - guint16 vendor_id, - guint16 product_id); - -#endif /* MM_BROADBAND_MODEM_CINTERION_H */ diff --git a/plugins/mm-plugin-cinterion.c b/plugins/mm-plugin-cinterion.c deleted file mode 100644 index 28de08f1..00000000 --- a/plugins/mm-plugin-cinterion.c +++ /dev/null @@ -1,118 +0,0 @@ -/* -*- 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) 2011 Ammonit Measurement GmbH - * Copyright (C) 2011 Google Inc. - * Author: Aleksander Morgado - */ - -#include -#include - -#include "mm-plugin-cinterion.h" -#include "mm-broadband-modem-cinterion.h" -#include "mm-errors-types.h" -#include "mm-log.h" - -G_DEFINE_TYPE (MMPluginCinterion, mm_plugin_cinterion, MM_TYPE_PLUGIN_BASE) - -int mm_plugin_major_version = MM_PLUGIN_MAJOR_VERSION; -int mm_plugin_minor_version = MM_PLUGIN_MINOR_VERSION; - -static MMBaseModem * -grab_port (MMPluginBase *base, - MMBaseModem *existing, - MMPortProbe *probe, - GError **error) -{ - MMBaseModem *modem = NULL; - const gchar *name, *subsys, *driver; - guint16 vendor = 0, product = 0; - - /* The Cinterion plugin cannot do anything with non-AT ports */ - if (!mm_port_probe_is_at (probe)) { - g_set_error_literal (error, - MM_CORE_ERROR, - MM_CORE_ERROR_UNSUPPORTED, - "Ignoring non-AT port"); - return NULL; - } - - subsys = mm_port_probe_get_port_subsys (probe); - name = mm_port_probe_get_port_name (probe); - driver = mm_port_probe_get_port_driver (probe); - - /* Try to get Product IDs from udev. Note that it is not an error - * if we can't get them in our case, as we also support serial - * modems. */ - mm_plugin_base_get_device_ids (base, subsys, name, &vendor, &product); - - /* If this is the first port being grabbed, create a new modem object */ - if (!existing) - modem = MM_BASE_MODEM (mm_broadband_modem_cinterion_new (mm_port_probe_get_port_physdev (probe), - driver, - mm_plugin_get_name (MM_PLUGIN (base)), - vendor, - product)); - - if (!mm_base_modem_grab_port (existing ? existing : modem, - subsys, - name, - MM_PORT_TYPE_AT, /* we only allow AT ports here */ - MM_AT_PORT_FLAG_NONE, - error)) { - if (modem) - g_object_unref (modem); - return NULL; - } - - return existing ? existing : modem; -} - -/*****************************************************************************/ - -G_MODULE_EXPORT MMPlugin * -mm_plugin_create (void) -{ - static const gchar *subsystems[] = { "tty", NULL }; - static const gchar *vendor_strings[] = { "cinterion", "siemens", NULL }; - static const guint16 vendor_ids[] = { 0x1e2d, 0x0681, 0 }; - - return MM_PLUGIN ( - g_object_new (MM_TYPE_PLUGIN_CINTERION, - MM_PLUGIN_BASE_NAME, "Cinterion", - MM_PLUGIN_BASE_ALLOWED_SUBSYSTEMS, subsystems, - MM_PLUGIN_BASE_ALLOWED_VENDOR_STRINGS, vendor_strings, - MM_PLUGIN_BASE_ALLOWED_VENDOR_IDS, vendor_ids, - MM_PLUGIN_BASE_ALLOWED_AT, TRUE, - MM_PLUGIN_BASE_SORT_LAST, TRUE, - NULL)); -} - -static void -mm_plugin_cinterion_init (MMPluginCinterion *self) -{ -} - -static void -mm_plugin_cinterion_class_init (MMPluginCinterionClass *klass) -{ - MMPluginBaseClass *pb_class = MM_PLUGIN_BASE_CLASS (klass); - - pb_class->grab_port = grab_port; -} diff --git a/plugins/mm-plugin-cinterion.h b/plugins/mm-plugin-cinterion.h deleted file mode 100644 index 13497653..00000000 --- a/plugins/mm-plugin-cinterion.h +++ /dev/null @@ -1,47 +0,0 @@ -/* -*- 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) 2011 Ammonit Measurement GmbH - * Author: Aleksander Morgado - */ - -#ifndef MM_PLUGIN_CINTERION_H -#define MM_PLUGIN_CINTERION_H - -#include "mm-plugin-base.h" - -#define MM_TYPE_PLUGIN_CINTERION (mm_plugin_cinterion_get_type ()) -#define MM_PLUGIN_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterion)) -#define MM_PLUGIN_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterionClass)) -#define MM_IS_PLUGIN_CINTERION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_PLUGIN_CINTERION)) -#define MM_IS_PLUGIN_CINTERION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_PLUGIN_CINTERION)) -#define MM_PLUGIN_CINTERION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_PLUGIN_CINTERION, MMPluginCinterionClass)) - -typedef struct { - MMPluginBase parent; -} MMPluginCinterion; - -typedef struct { - MMPluginBaseClass parent; -} MMPluginCinterionClass; - -GType mm_plugin_cinterion_get_type (void); - -G_MODULE_EXPORT MMPlugin *mm_plugin_create (void); - -#endif /* MM_PLUGIN_CINTERION_H */ -- cgit v1.2.3-70-g09d2