diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2011-04-06 11:21:45 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2011-04-11 10:04:27 +0200 |
commit | c7119f0341670cc258292b5d39a9082485018fbf (patch) | |
tree | 962fc503aa47dd0ceb9d5865fcd8306473b57e64 /plugins/mm-modem-wavecom-gsm.c | |
parent | acf65de3b844cd13c2128275daca67ac630146bb (diff) |
wavecom: ensure the modem uses the highest possible mobile class
These modems can be configured to use different mobile classes. For each kind of
modem, the best mobile class is the highest one in the following order:
- Class A (3G only mode)
- Class B (PS or CS, GPRS/EDGE or GSM)
- Class CG (PS only, GPRS/EDGE)
- Class CC (CS only, GSM)
Diffstat (limited to 'plugins/mm-modem-wavecom-gsm.c')
-rw-r--r-- | plugins/mm-modem-wavecom-gsm.c | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/plugins/mm-modem-wavecom-gsm.c b/plugins/mm-modem-wavecom-gsm.c index e68af89d..480afbe3 100644 --- a/plugins/mm-modem-wavecom-gsm.c +++ b/plugins/mm-modem-wavecom-gsm.c @@ -24,6 +24,7 @@ #include "mm-modem-helpers.h" #include "mm-modem-wavecom-gsm.h" #include "mm-serial-parsers.h" +#include "mm-log.h" static void modem_init (MMModem *modem_class); @@ -33,6 +34,35 @@ G_DEFINE_TYPE_EXTENDED (MMModemWavecomGsm, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) +/* Bit flags for mobile station classes supported by the modem */ +typedef enum { + WAVECOM_MS_CLASS_UNKNOWN = 0, + /* Class C in circuit switched only mode, CS */ + WAVECOM_MS_CLASS_CC = 1 << 0, + /* Class C in GPRS only mode, PS */ + WAVECOM_MS_CLASS_CG = 1 << 1, + /* Class B (either CS or PS, not both at the same time) + * This should be the default for GSM/GPRS modems */ + WAVECOM_MS_CLASS_B = 1 << 2, + /* Class A in 3G only mode */ + WAVECOM_MS_CLASS_A = 1 << 3 +} WavecomMSClass; + +#define WAVECOM_MS_CLASS_CC_IDSTR "\"CC\"" +#define WAVECOM_MS_CLASS_CG_IDSTR "\"CG\"" +#define WAVECOM_MS_CLASS_B_IDSTR "\"B\"" +#define WAVECOM_MS_CLASS_A_IDSTR "\"A\"" + +#define MM_MODEM_WAVECOM_GSM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_WAVECOM_GSM, MMModemWavecomGsmPrivate)) + +typedef struct { + /* Bitmask for supported MS classes */ + guint8 supported_ms_classes; + /* Current MS class */ + WavecomMSClass current_ms_class; +} MMModemWavecomGsmPrivate; + MMModem * mm_modem_wavecom_gsm_new (const char *device, const char *driver, @@ -53,6 +83,23 @@ mm_modem_wavecom_gsm_new (const char *device, NULL)); } +static const gchar * +wavecom_ms_class_to_str (WavecomMSClass class) +{ + switch (class) { + case WAVECOM_MS_CLASS_CC: + return WAVECOM_MS_CLASS_CC_IDSTR; + case WAVECOM_MS_CLASS_CG: + return WAVECOM_MS_CLASS_CG_IDSTR; + case WAVECOM_MS_CLASS_B: + return WAVECOM_MS_CLASS_B_IDSTR; + case WAVECOM_MS_CLASS_A: + return WAVECOM_MS_CLASS_A_IDSTR; + default: + g_warn_if_reached (); + return NULL; + } +} static gboolean grab_port (MMModem *modem, @@ -198,6 +245,224 @@ get_access_technology (MMGenericGsm *gsm, mm_at_serial_port_queue_command (port, "+WGPRS=9,2", 3, get_access_technology_cb, 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 +set_highest_ms_class_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + guint new_class; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + new_class = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "new-class")); + if (new_class) + priv->current_ms_class = new_class; + + /* All done! */ + mm_dbg ("[4/4] All done..."); + enable_complete (MM_GENERIC_GSM (info->modem), NULL, info); +} + +static void +set_highest_ms_class (MMAtSerialPort *port, + MMCallbackInfo *info) +{ + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + guint new_class = 0; + + if (priv->supported_ms_classes & WAVECOM_MS_CLASS_A) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_A) { + /* A is supported but is not currently selected, switch to A */ + new_class = WAVECOM_MS_CLASS_A; + } + } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_B) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_B) { + /* B is supported but is not currently selected, switch to B */ + new_class = WAVECOM_MS_CLASS_B; + } + } else if (priv->supported_ms_classes & WAVECOM_MS_CLASS_CG) { + if (priv->current_ms_class != WAVECOM_MS_CLASS_CG) { + /* CG is supported but is not currently selected, switch to CG */ + new_class = WAVECOM_MS_CLASS_CG; + } + } + + if (new_class) { + const gchar *new_class_str; + gchar *cmd; + + new_class_str = wavecom_ms_class_to_str (new_class); + mm_dbg (" Changing mobile station class to: %s", new_class_str); + mm_callback_info_set_data (info, + "new-class", + GUINT_TO_POINTER (new_class), + NULL); + cmd = g_strdup_printf ("+CGCLASS=%s", new_class_str); + mm_at_serial_port_queue_command (port, cmd, 3, set_highest_ms_class_cb, info); + g_free (cmd); + return; + } + + /* if no need to change station class, then just go on */ + mm_dbg (" No need to change mobile station class"); + set_highest_ms_class_cb (port, NULL, NULL, info); +} + +static void +get_current_ms_class_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + const gchar *p; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + p = mm_strip_tag (response->str, "+CGCLASS:"); + + if (strncmp (p, + WAVECOM_MS_CLASS_A_IDSTR, + strlen (WAVECOM_MS_CLASS_A_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class A mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_A; + } else if (strncmp (p, + WAVECOM_MS_CLASS_B_IDSTR, + strlen (WAVECOM_MS_CLASS_B_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class B mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_B; + } else if (strncmp (p, + WAVECOM_MS_CLASS_CG_IDSTR, + strlen (WAVECOM_MS_CLASS_CG_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class CG mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_CG; + } else if (strncmp (p, + WAVECOM_MS_CLASS_CC_IDSTR, + strlen (WAVECOM_MS_CLASS_CC_IDSTR)) == 0) { + mm_dbg ("Modem configured as a Class CC mobile station"); + priv->current_ms_class = WAVECOM_MS_CLASS_CC; + } else { + GError *inner_error; + + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Unknown mobile station class: '%s'", + p); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + return; + } + + /* 3rd, set highest mobile station class possible */ + mm_dbg ("[3/4] Ensuring highest MS class..."); + set_highest_ms_class (port, info); +} + +static void +get_supported_ms_classes_cb (MMAtSerialPort *port, + GString *response, + GError *error, + gpointer user_data) +{ + MMCallbackInfo *info = user_data; + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (info->modem); + const gchar *p; + + if (error) { + enable_complete (MM_GENERIC_GSM (info->modem), error, info); + return; + } + + /* Reset currently supported MS classes */ + priv->supported_ms_classes = 0; + + p = mm_strip_tag (response->str, "+CGCLASS:"); + + if (strstr (p, WAVECOM_MS_CLASS_A_IDSTR)) { + mm_dbg ("Modem supports Class A mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_A; + } + + if (strstr (p, WAVECOM_MS_CLASS_B_IDSTR)) { + mm_dbg ("Modem supports Class B mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_B; + } + + if (strstr (p, WAVECOM_MS_CLASS_CG_IDSTR)) { + mm_dbg ("Modem supports Class CG mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_CG; + } + + if (strstr (p, WAVECOM_MS_CLASS_CC_IDSTR)) { + mm_dbg ("Modem supports Class CC mobile station"); + priv->supported_ms_classes |= WAVECOM_MS_CLASS_CC; + } + + /* If none received, error */ + if (!priv->supported_ms_classes) { + GError *inner_error; + + inner_error = g_error_new (MM_MODEM_ERROR, + MM_MODEM_ERROR_GENERAL, + "Couldn't get supported mobile station classes"); + enable_complete (MM_GENERIC_GSM (info->modem), inner_error, info); + g_error_free (inner_error); + return; + } + + /* 2nd, query for current MS class */ + mm_dbg ("[2/4] Getting current MS class..."); + mm_at_serial_port_queue_command (port, "+CGCLASS?", 3, get_current_ms_class_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; + } + + /* 1st, query for supported MS classes */ + mm_dbg ("[1/4] Getting supported MS classes..."); + mm_at_serial_port_queue_command (port, "+CGCLASS=?", 3, get_supported_ms_classes_cb, info); +} + /*****************************************************************************/ static void @@ -209,6 +474,11 @@ modem_init (MMModem *modem_class) static void mm_modem_wavecom_gsm_init (MMModemWavecomGsm *self) { + MMModemWavecomGsmPrivate *priv = MM_MODEM_WAVECOM_GSM_GET_PRIVATE (self); + + /* Set defaults */ + priv->supported_ms_classes = 0; /* This is a bitmask, so empty */ + priv->current_ms_class = WAVECOM_MS_CLASS_UNKNOWN; } static void @@ -217,6 +487,8 @@ mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); MMGenericGsmClass *gsm_class = MM_GENERIC_GSM_CLASS (klass); + g_type_class_add_private (object_class, sizeof (MMModemWavecomGsmPrivate)); + object_class->get_property = get_property; object_class->set_property = set_property; @@ -228,6 +500,7 @@ mm_modem_wavecom_gsm_class_init (MMModemWavecomGsmClass *klass) MM_GENERIC_GSM_PROP_FLOW_CONTROL_CMD, MM_GENERIC_GSM_FLOW_CONTROL_CMD); + gsm_class->do_enable_power_up_done = do_enable_power_up_done; gsm_class->get_access_technology = get_access_technology; } |