aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/mm-modem-cinterion-gsm.c205
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;
}