diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2011-04-14 15:46:11 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2011-06-06 17:20:16 +0200 |
commit | 795de120503af8474999f6ad0233a407e50ecf8d (patch) | |
tree | 325770ce38fc2282185c87bc27746bdbd2e7259b | |
parent | c6060e8c0f4c384c49b1e0effb0e149ce9bf15c9 (diff) |
cinterion: query network technology capabilities
We try to look for 'psinfo' indication in AT^SIND? output (available in 3G
devices from Cinterion), and if that is not available, we try to use the
AT^SMONG GPRS monitor (available in 2G devices from Cinterion).
-rw-r--r-- | plugins/mm-modem-cinterion-gsm.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/plugins/mm-modem-cinterion-gsm.c b/plugins/mm-modem-cinterion-gsm.c index 07bc864e..cb9ec078 100644 --- a/plugins/mm-modem-cinterion-gsm.c +++ b/plugins/mm-modem-cinterion-gsm.c @@ -28,6 +28,14 @@ G_DEFINE_TYPE (MMModemCinterionGsm, mm_modem_cinterion_gsm, MM_TYPE_GENERIC_GSM); +#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; +} MMModemCinterionGsmPrivate; + MMModem * mm_modem_cinterion_gsm_new (const char *device, const char *driver, @@ -48,15 +56,212 @@ mm_modem_cinterion_gsm_new (const char *device, NULL)); } +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 = MM_MODEM_CINTERION_GSM_GET_PRIVATE (info->modem); + MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN; + GMatchInfo *match_info = NULL; + GRegex *regex; + + 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^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; + } + + 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 (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); + return; + } + + /* 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); +} + +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 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 */ } 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); + + g_type_class_add_private (object_class, sizeof (MMModemCinterionGsmPrivate)); + + gsm_class->get_access_technology = get_access_technology; } |