diff options
Diffstat (limited to 'plugins/mm-modem-cinterion-gsm.c')
-rw-r--r-- | plugins/mm-modem-cinterion-gsm.c | 1121 |
1 files changed, 0 insertions, 1121 deletions
diff --git a/plugins/mm-modem-cinterion-gsm.c b/plugins/mm-modem-cinterion-gsm.c deleted file mode 100644 index 9220539f..00000000 --- a/plugins/mm-modem-cinterion-gsm.c +++ /dev/null @@ -1,1121 +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 - * Author: Aleksander Morgado <aleksander@lanedo.com> - */ - -#include <config.h> - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <ctype.h> - -#include "mm-errors.h" -#include "mm-modem-helpers.h" -#include "mm-modem-cinterion-gsm.h" -#include "mm-serial-parsers.h" -#include "mm-log.h" - -static void modem_gsm_network_init (MMModemGsmNetwork *gsm_network_class); - -G_DEFINE_TYPE_EXTENDED (MMModemCinterionGsm, mm_modem_cinterion_gsm, MM_TYPE_GENERIC_GSM, 0, - G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM_GSM_NETWORK, modem_gsm_network_init)) - -/* Mask of all bands supported in 2G devices */ -#define ALL_2G_BANDS \ - (MM_MODEM_GSM_BAND_EGSM | \ - MM_MODEM_GSM_BAND_DCS | \ - MM_MODEM_GSM_BAND_PCS | \ - MM_MODEM_GSM_BAND_G850) - -/* Mask of all bands supported in 3G devices (including some 2G bands) */ -#define ALL_3G_BANDS \ - (MM_MODEM_GSM_BAND_EGSM | \ - MM_MODEM_GSM_BAND_DCS | \ - MM_MODEM_GSM_BAND_PCS | \ - MM_MODEM_GSM_BAND_G850 | \ - MM_MODEM_GSM_BAND_U2100 | \ - MM_MODEM_GSM_BAND_U1900 | \ - MM_MODEM_GSM_BAND_U850) - -#define MM_MODEM_CINTERION_GSM_GET_PRIVATE(o) \ - (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_CINTERION_GSM, MMModemCinterionGsmPrivate)) - -typedef struct { - /* Flag to know if we should try AT^SIND or not to get psinfo */ - gboolean sind_psinfo; - - /* Supported networks */ - gboolean only_geran; - gboolean only_utran; - gboolean both_geran_utran; - - /* Current allowed mode */ - MMModemGsmAllowedMode allowed_mode; - - /* Bitmask for currently active bands */ - guint32 current_bands; - - /* Command to go into sleep mode */ - gchar *sleep_mode_cmd; -} MMModemCinterionGsmPrivate; - -/* Setup relationship between the band bitmask in the modem and the bitmask - * in ModemManager. */ -typedef struct { - gchar *cinterion_band; - guint32 mm_band_mask; -} 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", MM_MODEM_GSM_BAND_EGSM }, - { "2", MM_MODEM_GSM_BAND_DCS }, - { "4", MM_MODEM_GSM_BAND_PCS }, - { "8", MM_MODEM_GSM_BAND_G850 }, - { "3", (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_DCS) }, - { "5", (MM_MODEM_GSM_BAND_EGSM | MM_MODEM_GSM_BAND_PCS) }, - { "10", (MM_MODEM_GSM_BAND_G850 | MM_MODEM_GSM_BAND_DCS) }, - { "12", (MM_MODEM_GSM_BAND_G850 | MM_MODEM_GSM_BAND_PCS) }, - { "15", ALL_2G_BANDS } -}; - -/* Setup relationship between the 3G band bitmask in the modem and the bitmask - * in ModemManager. */ -typedef struct { - guint32 cinterion_band_flag; - guint32 mm_band_flag; -} 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_GSM_BAND_EGSM }, - { (1 << 1), MM_MODEM_GSM_BAND_DCS }, - { (1 << 2), MM_MODEM_GSM_BAND_PCS }, - { (1 << 3), MM_MODEM_GSM_BAND_G850 }, - { (1 << 4), MM_MODEM_GSM_BAND_U2100 }, - { (1 << 5), MM_MODEM_GSM_BAND_U1900 }, - { (1 << 6), MM_MODEM_GSM_BAND_U850 } -}; - -MMModem * -mm_modem_cinterion_gsm_new (const char *device, - const char *driver, - const char *plugin, - guint32 vendor, - guint32 product) -{ - g_return_val_if_fail (device != NULL, NULL); - g_return_val_if_fail (driver != NULL, NULL); - g_return_val_if_fail (plugin != NULL, NULL); - - return MM_MODEM (g_object_new (MM_TYPE_MODEM_CINTERION_GSM, - MM_MODEM_MASTER_DEVICE, device, - MM_MODEM_DRIVER, driver, - MM_MODEM_PLUGIN, plugin, - MM_MODEM_HW_VID, vendor, - MM_MODEM_HW_PID, product, - MM_MODEM_BASE_MAX_TIMEOUTS, 3, - NULL)); -} - -static void -port_grabbed (MMGenericGsm *gsm, - MMPort *port, - MMAtPortFlags pflags, - gpointer user_data) -{ - if (MM_IS_AT_SERIAL_PORT (port)) { - /* Set RTS/CTS flow control by default */ - g_object_set (G_OBJECT (port), MM_SERIAL_PORT_RTS_CTS, TRUE, NULL); - } -} - -static void -convert_str_from_ucs2 (gchar **str) -{ - const char *p; - char *converted; - size_t len; - - p = *str; - len = strlen (p); - - /* Len needs to be a multiple of 4 for UCS2 */ - if ((len < 4) || ((len % 4) != 0)) - return; - - while (*p) { - if (!isxdigit (*p++)) - return; - } - - converted = mm_modem_charset_hex_to_utf8 (*str, MM_MODEM_CHARSET_UCS2); - if (converted) { - g_free (*str); - *str = converted; - } -} - -static void -get_2g_band_done (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) - info->error = g_error_copy (error); - else { - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - guint32 mm_band = MM_MODEM_GSM_BAND_UNKNOWN; - GRegex *regex; - GMatchInfo *match_info = NULL; - - /* 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); - if (regex && - g_regex_match_full (regex, response->str, response->len, 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 */ - if (mm_generic_gsm_get_charset (MM_GENERIC_GSM (info->modem)) == MM_MODEM_CHARSET_UCS2) - convert_str_from_ucs2 (¤t); - - for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { - if (strcmp (bands_2g[i].cinterion_band, current) == 0) { - mm_band = bands_2g[i].mm_band_mask; - break; - } - } - - g_free (current); - } - } - - if (mm_band == MM_MODEM_GSM_BAND_UNKNOWN) { - g_set_error (&info->error, - MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Couldn't get bands configuration"); - } else { - priv->current_bands = mm_band; - mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); - } - - if (regex) - g_regex_unref (regex); - if (match_info) - g_match_info_free (match_info); - } - - mm_callback_info_schedule (info); -} - -static void -get_3g_band_done (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) - info->error = g_error_copy (error); - else { - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - guint32 mm_band = 0; - GRegex *regex; - GMatchInfo *match_info = NULL; - - /* 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 <rba> replied is a number, not a string. - */ - regex = g_regex_new ("\\^SCFG:\\s*\"Radio/Band\",\\s*(\\d*)", 0, 0, NULL); - if (regex && - g_regex_match_full (regex, response->str, response->len, 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) - mm_band |= bands_3g[i].mm_band_flag; - } - - g_free (current); - } - } - - if (mm_band == 0) { - g_set_error (&info->error, - MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Couldn't get bands configuration"); - } else { - priv->current_bands = mm_band; - mm_callback_info_set_result (info, GUINT_TO_POINTER (mm_band), NULL); - } - - if (regex) - g_regex_unref (regex); - if (match_info) - g_match_info_free (match_info); - } - - mm_callback_info_schedule (info); -} - -static void -get_band (MMModemGsmNetwork *self, - MMModemUIntFn callback, - gpointer user_data) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (self); - MMAtSerialPort *port; - MMCallbackInfo *info; - - info = mm_callback_info_uint_new (MM_MODEM (self), callback, user_data); - - /* If results are already cached, return them */ - if (priv->current_bands > 0) { - mm_callback_info_set_result (info, GUINT_TO_POINTER (priv->current_bands), NULL); - mm_callback_info_schedule (info); - return; - } - - /* Otherwise ask the modem */ - port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); - if (!port) { - mm_callback_info_schedule (info); - return; - } - - /* 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_at_serial_port_queue_command (port, - "AT^SCFG=\"Radio/Band\"", - 3, - ((!priv->only_utran && !priv->both_geran_utran) ? - get_2g_band_done : - get_3g_band_done), - info); -} - -static void -set_band_done (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) - info->error = g_error_copy (error); - else { - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - - priv->current_bands = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-band")); - } - - mm_callback_info_schedule (info); -} - -static void -set_2g_band (MMModemGsmNetwork *self, - MMModemGsmBand band, - MMAtSerialPort *port, - MMCallbackInfo *info) -{ - const gchar *cinterion_band = NULL; - gchar *cinterion_band_ucs2 = NULL; - gchar *cmd; - guint i; - - /* If we get ANY, reset to all-2G bands to get the proper value */ - if (band == MM_MODEM_GSM_BAND_ANY) - band = ALL_2G_BANDS; - - /* Loop looking for correct allowed masks */ - for (i = 0; i < G_N_ELEMENTS (bands_2g); i++) { - if (bands_2g[i].mm_band_mask == band) { - cinterion_band = bands_2g[i].cinterion_band; - break; - } - } - - /* If we didn't find a match, set an error */ - if (!cinterion_band) { - info->error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Wrong band mask: '%u'", band); - mm_callback_info_schedule (info); - return; - } - - if (mm_generic_gsm_get_charset (MM_GENERIC_GSM (info->modem)) == MM_MODEM_CHARSET_UCS2) - cinterion_band_ucs2 = mm_modem_charset_utf8_to_hex (cinterion_band, MM_MODEM_CHARSET_UCS2); - - mm_callback_info_set_data (info, - "new-band", - GUINT_TO_POINTER ((guint)band), - NULL); - /* Following the setup: - * AT^SCFG="Radion/Band",<rbp>,<rba> - * 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_ucs2 ? cinterion_band_ucs2 : cinterion_band, - cinterion_band_ucs2 ? cinterion_band_ucs2 : cinterion_band); - mm_at_serial_port_queue_command (port, cmd, 3, set_band_done, info); - g_free (cmd); - g_free (cinterion_band_ucs2); -} - -static void -set_3g_band (MMModemGsmNetwork *self, - MMModemGsmBand band, - MMAtSerialPort *port, - MMCallbackInfo *info) -{ - guint32 cinterion_band = 0; - gchar *cmd; - guint i; - - /* If we get ANY, reset to all-3G bands to get the proper value */ - if (band == MM_MODEM_GSM_BAND_ANY) - band = ALL_3G_BANDS; - - /* Loop looking for correct allowed masks */ - for (i = 0; i < G_N_ELEMENTS (bands_3g); i++) { - if (band & bands_3g[i].mm_band_flag) { - cinterion_band |= bands_3g[i].cinterion_band_flag; - } - } - - /* If we didn't find a match, set an error */ - if (!cinterion_band) { - info->error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Wrong band mask: '%u'", band); - mm_callback_info_schedule (info); - return; - } - - mm_callback_info_set_data (info, - "new-band", - GUINT_TO_POINTER ((guint)band), - NULL); - /* Following the setup: - * AT^SCFG="Radion/Band",<rba> - * 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_at_serial_port_queue_command (port, cmd, 3, set_band_done, info); - g_free (cmd); -} - -static void -set_band (MMModemGsmNetwork *self, - MMModemGsmBand band, - MMModemFn callback, - gpointer user_data) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (self); - MMAtSerialPort *port; - MMCallbackInfo *info; - - info = mm_callback_info_new (MM_MODEM (self), callback, user_data); - - /* Are we trying to change the band to the same bands currently - * being used? if so, we're done */ - if (priv->current_bands == band) { - mm_callback_info_schedule (info); - return; - } - - /* Otherwise ask the modem */ - port = mm_generic_gsm_get_best_at_port (MM_GENERIC_GSM (self), &info->error); - if (!port) { - mm_callback_info_schedule (info); - return; - } - - if (!priv->only_utran && - !priv->both_geran_utran) - set_2g_band (self, band, port, info); - else - set_3g_band (self, band, port, info); -} - -static MMModemGsmAccessTech -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_GSM_ACCESS_TECH_UNKNOWN; - case '1': - case '2': - return MM_MODEM_GSM_ACCESS_TECH_GPRS; - case '3': - case '4': - return MM_MODEM_GSM_ACCESS_TECH_EDGE; - default: - break; - } - } - - g_set_error (error, - MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Couldn't get network capabilities, " - "invalid GPRS status value: '%s'", - gprs_status); - return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; -} - -static MMModemGsmAccessTech -get_access_technology_from_psinfo (const gchar *psinfo, - GError **error) -{ - if (strlen (psinfo) == 1) { - switch (psinfo[0]) { - case '0': - return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; - case '1': - case '2': - return MM_MODEM_GSM_ACCESS_TECH_GPRS; - case '3': - case '4': - return MM_MODEM_GSM_ACCESS_TECH_EDGE; - case '5': - case '6': - return MM_MODEM_GSM_ACCESS_TECH_UMTS; - case '7': - case '8': - return MM_MODEM_GSM_ACCESS_TECH_HSDPA; - default: - break; - } - } - - g_set_error (error, - MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Couldn't get network capabilities, " - "invalid psinfo value: '%s'", - psinfo); - return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; -} - -static void -get_smong_cb (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = user_data; - MMModemCinterionGsmPrivate *priv; - MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; - GMatchInfo *match_info = NULL; - GRegex *regex; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) { - info->error = g_error_copy (error); - mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); - mm_callback_info_schedule (info); - return; - } - - priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - - /* 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->str, response->len, 0, 0, &match_info, NULL)) { - gchar *gprs_status; - - gprs_status = g_match_info_fetch (match_info, 2); - act = get_access_technology_from_smong_gprs_status (gprs_status, &info->error); - g_free (gprs_status); - - /* We'll default to use SMONG then */ - priv->sind_psinfo = FALSE; - } else { - g_set_error (&info->error, - MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Couldn't get network capabilities, invalid SMONG reply: '%s'", - response->str); - - /* We'll reset here the flag to try to use SIND/psinfo the next time */ - priv->sind_psinfo = TRUE; - } - - g_match_info_free (match_info); - g_regex_unref (regex); - - mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); - mm_callback_info_schedule (info); -} - -static void -get_sind_cb (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = user_data; - MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; - GMatchInfo *match_info = NULL; - GRegex *regex; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) { - info->error = g_error_copy (error); - mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); - mm_callback_info_schedule (info); - 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->str, response->len, 0, 0, &match_info, NULL)) { - gchar *ind_value; - - ind_value = g_match_info_fetch (match_info, 2); - act = get_access_technology_from_psinfo (ind_value, &info->error); - g_free (ind_value); - mm_callback_info_set_result (info, GUINT_TO_POINTER (act), NULL); - mm_callback_info_schedule (info); - } else { - /* If there was no 'psinfo' indicator, we'll try AT^SMONG and read the cell - * info table. */ - mm_at_serial_port_queue_command (port, "^SMONG", 3, get_smong_cb, info); - } - - g_match_info_free (match_info); - g_regex_unref (regex); -} - -static void -get_access_technology (MMGenericGsm *gsm, - MMModemUIntFn callback, - gpointer user_data) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (gsm); - MMAtSerialPort *port; - MMCallbackInfo *info; - - info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); - - port = mm_generic_gsm_get_best_at_port (gsm, &info->error); - if (!port) { - mm_callback_info_schedule (info); - return; - } - - if (priv->sind_psinfo) { - mm_at_serial_port_queue_command (port, "^SIND?", 3, get_sind_cb, info); - } else { - mm_at_serial_port_queue_command (port, "^SMONG", 3, get_smong_cb, info); - } -} - -static void -get_allowed_mode (MMGenericGsm *gsm, - MMModemUIntFn callback, - gpointer user_data) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (gsm); - MMCallbackInfo *info; - - info = mm_callback_info_uint_new (MM_MODEM (gsm), callback, user_data); - - /* We just return cached value. For 3G devices we could try to ask the - * modem for the Access technology being used, and base 3G-only or 2G-only - * replies on that, but we wouldn't be covering all possible allowed modes - * anyway. */ - mm_callback_info_set_result (info, - GUINT_TO_POINTER (priv->allowed_mode), - NULL); - mm_callback_info_schedule (info); -} - -static void -set_allowed_mode_cb (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = (MMCallbackInfo *) user_data; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) - info->error = g_error_copy (error); - else { - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - - priv->allowed_mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-mode")); - } - - mm_callback_info_schedule (info); -} - -static void -set_allowed_mode (MMGenericGsm *gsm, - MMModemGsmAllowedMode mode, - MMModemFn callback, - gpointer user_data) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (gsm); - MMCallbackInfo *info; - - info = mm_callback_info_new (MM_MODEM (gsm), callback, user_data); - - /* For dual 2G/3G devices... */ - if (priv->both_geran_utran) { - GString *cmd; - MMAtSerialPort *port; - - port = mm_generic_gsm_get_best_at_port (gsm, &info->error); - if (!port) { - mm_callback_info_schedule (info); - return; - } - - /* 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 (mode == MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY) { - g_string_append (cmd, "2"); - } else if (mode == MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY) { - g_string_append (cmd, "0"); - } /* else, no AcT given, defaults to Auto */ - - mm_callback_info_set_data (info, - "new-mode", - GUINT_TO_POINTER (mode), - NULL); - - mm_at_serial_port_queue_command (port, cmd->str, 3, set_allowed_mode_cb, info); - g_string_free (cmd, TRUE); - mm_callback_info_schedule (info); - return; - } - - /* For 3G-only devices, allow only 3G-related allowed modes */ - if (priv->only_utran) { - switch (mode) { - case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED: - case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY: - case MM_MODEM_GSM_ALLOWED_MODE_ANY: - priv->allowed_mode = mode; - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Cannot set desired allowed mode, " - "not a 2G device"); - break; - } - mm_callback_info_schedule (info); - return; - } - - /* For 2G devices, allow only 2G-related allowed modes */ - switch (mode) { - case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED: - case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY: - case MM_MODEM_GSM_ALLOWED_MODE_ANY: - priv->allowed_mode = mode; - break; - default: - info->error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Cannot set desired allowed mode, " - "not a 3G device"); - break; - } - mm_callback_info_schedule (info); -} - -static void -enable_complete (MMGenericGsm *gsm, - GError *error, - MMCallbackInfo *info) -{ - /* Do NOT chain up parent do_enable_power_up_done(), as it actually ignores - * all errors. */ - - mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), error, info); -} - -static void -get_supported_functionality_status_cb (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = user_data; - MMModemCinterionGsmPrivate *priv; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) { - enable_complete (MM_GENERIC_GSM (info->modem), error, info); - return; - } - - priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - - /* 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. - */ - - g_free (priv->sleep_mode_cmd); - if (strstr (response->str, "4") != NULL) { - mm_dbg ("Device supports CFUN=4 sleep mode"); - priv->sleep_mode_cmd = g_strdup ("+CFUN=4"); - } else if (strstr (response->str, "7") != NULL) { - mm_dbg ("Device supports CFUN=7 sleep mode"); - priv->sleep_mode_cmd = g_strdup ("+CFUN=7"); - } else { - mm_warn ("Unknown functionality mode to go into sleep mode"); - priv->sleep_mode_cmd = NULL; - } - - /* All done without errors! */ - mm_dbg ("[3/3] All done"); - enable_complete (MM_GENERIC_GSM (info->modem), NULL, info); -} - -static void -get_supported_networks_cb (MMAtSerialPort *port, - GString *response, - GError *error, - gpointer user_data) -{ - MMCallbackInfo *info = user_data; - MMModemCinterionGsmPrivate *priv; - - /* If the modem has already been removed, return without - * scheduling callback */ - if (mm_callback_info_check_modem_removed (info)) - return; - - if (error) { - enable_complete (MM_GENERIC_GSM (info->modem), error, info); - return; - } - - priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); - - /* 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. * */ - - if (strstr (response->str, "12") != NULL) { - mm_dbg ("Device allows 2G-only network mode"); - priv->only_geran = TRUE; - } - - if (strstr (response->str, "22") != NULL) { - mm_dbg ("Device allows 3G-only network mode"); - priv->only_utran = TRUE; - } - - if (strstr (response->str, "25") != NULL) { - mm_dbg ("Device allows 2G/3G network mode"); - priv->both_geran_utran = TRUE; - } - - /* If no expected ID found, error */ - if (!priv->only_geran && - !priv->only_utran && - !priv->both_geran_utran) { - GError *inner_error = NULL; - - mm_warn ("Invalid list of supported networks: '%s'", - response->str); - inner_error = g_error_new (MM_MODEM_ERROR, - MM_MODEM_ERROR_GENERAL, - "Invalid list of supported networks: '%s'", - response->str); - enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); - g_error_free (inner_error); - return; - } - - /* Next, check which supported functionality modes are available */ - mm_dbg ("[2/3] Getting list of supported functionality status..."); - mm_at_serial_port_queue_command (port, "+CFUN=?", 3, get_supported_functionality_status_cb, info); -} - -static void -do_enable_power_up_done (MMGenericGsm *gsm, - GString *response, - GError *error, - MMCallbackInfo *info) -{ - MMAtSerialPort *port; - GError *inner_error = NULL; - - if (error) { - enable_complete (gsm, error, info); - return; - } - - /* Get port */ - port = mm_generic_gsm_get_best_at_port (gsm, &inner_error); - if (!port) { - enable_complete (gsm, inner_error, info); - g_error_free (inner_error); - return; - } - - /* First, list supported networks */ - mm_dbg ("[1/3] Getting list of supported networks..."); - mm_at_serial_port_queue_command (port, "+WS46=?", 3, get_supported_networks_cb, info); -} - -static void -set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - /* Do nothing... see set_property() in parent, which also does nothing */ -} - -static void -get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (object); - - switch (prop_id) { - case MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD: - /* We need to enable RTS/CTS so that CYCLIC SLEEP mode works */ - g_value_set_string (value, "\\Q3"); - break; - case MM_GENERIC_GSM_PROP_SMS_INDICATION_ENABLE_CMD: - /* AT+CNMO=<mode>,[<mt>[,<bm>[,<ds>[,<bfr>]]]] - * but <bfr> should be either not set, or equal to 1; - * and <ds> can be only either 0 or 2 (EGS5) - */ - g_value_set_string (value, "+CNMI=2,1,2,2,1"); - break; - case MM_GENERIC_GSM_PROP_SMS_STORAGE_LOCATION_CMD: - /* AT=CPMS=<mem1>[,<mem2>[,<mem3>]] - * but <mem3> should be either not set, or equal to "SM" or "MT". - * This <mem3> parameter is only meaningful if <mt>=2 in AT+CNMI - * */ - g_value_set_string (value, "+CPMS=\"ME\",\"ME\""); - break; - case MM_GENERIC_GSM_PROP_CMER_ENABLE_CMD: - /* AT=CMER=[<mode>[,<keyp>[,<disp>[,<ind>[,<bfr>]]]]] - * but <ind> should be either not set, or equal to 0 or 2. - * Enabled with 2. - * */ - g_value_set_string (value, "+CMER=3,0,0,2"); - break; - case MM_GENERIC_GSM_PROP_POWER_DOWN_CMD: - /* Use the value computed before, if any. */ - g_value_set_string (value, priv->sleep_mode_cmd ? priv->sleep_mode_cmd : ""); - break; - default: - break; - } -} - -static void -finalize (GObject *object) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (object); - - g_free (priv->sleep_mode_cmd); - - G_OBJECT_CLASS (mm_modem_cinterion_gsm_parent_class)->finalize (object); -} - -/*****************************************************************************/ - -static void -modem_gsm_network_init (MMModemGsmNetwork *network_class) -{ - network_class->set_band = set_band; - network_class->get_band = get_band; -} - -static void -mm_modem_cinterion_gsm_init (MMModemCinterionGsm *self) -{ - MMModemCinterionGsmPrivate *priv = MM_MODEM_CINTERION_GSM_GET_PRIVATE (self); - - /* Set defaults */ - priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */ - priv->allowed_mode = MM_MODEM_GSM_ALLOWED_MODE_ANY; - priv->only_geran = FALSE; - priv->only_utran = FALSE; - priv->both_geran_utran = FALSE; - priv->current_bands = 0; /* This is a bitmask, so empty */ -} - -static void -mm_modem_cinterion_gsm_class_init (MMModemCinterionGsmClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); - - mm_modem_cinterion_gsm_parent_class = g_type_class_peek_parent (klass); - g_type_class_add_private (object_class, sizeof (MMModemCinterionGsmPrivate)); - - object_class->finalize = finalize; - object_class->get_property = get_property; - object_class->set_property = set_property; - gsm_class->port_grabbed = port_grabbed; - - g_object_class_override_property (object_class, - MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD, - MM_GENERIC_GSM_FLOW_CONTROL_CMD); - - g_object_class_override_property (object_class, - MM_GENERIC_GSM_PROP_SMS_INDICATION_ENABLE_CMD, - MM_GENERIC_GSM_SMS_INDICATION_ENABLE_CMD); - - g_object_class_override_property (object_class, - MM_GENERIC_GSM_PROP_SMS_STORAGE_LOCATION_CMD, - MM_GENERIC_GSM_SMS_STORAGE_LOCATION_CMD); - - g_object_class_override_property (object_class, - MM_GENERIC_GSM_PROP_CMER_ENABLE_CMD, - MM_GENERIC_GSM_CMER_ENABLE_CMD); - - g_object_class_override_property (object_class, - MM_GENERIC_GSM_PROP_POWER_DOWN_CMD, - MM_GENERIC_GSM_POWER_DOWN_CMD); - - gsm_class->do_enable_power_up_done = do_enable_power_up_done; - gsm_class->set_allowed_mode = set_allowed_mode; - gsm_class->get_allowed_mode = get_allowed_mode; - gsm_class->get_access_technology = get_access_technology; -} - |