aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2010-03-08 20:01:22 -0800
committerDan Williams <dcbw@redhat.com>2010-03-08 20:01:22 -0800
commitf3721a5674ef7899d9a6ef0f4c5b356720bb9833 (patch)
tree51c469c5928b02414389e05302e8f89d9e1ef0e0 /src
parentf6c514897e40e768b180963f2782ed60527ffaa6 (diff)
parent9e231c3d4b463c32e17c7d2b1c50cea4e19d03ac (diff)
Merge remote branch 'origin/master' into qcdm
Diffstat (limited to 'src')
-rw-r--r--src/mm-auth-provider.h7
-rw-r--r--src/mm-generic-gsm.c880
-rw-r--r--src/mm-generic-gsm.h61
-rw-r--r--src/mm-modem-base.c3
-rw-r--r--src/mm-modem-cdma.c2
-rw-r--r--src/mm-modem-gsm-card.c16
-rw-r--r--src/mm-modem-gsm-network.c143
-rw-r--r--src/mm-modem-gsm-network.h45
-rw-r--r--src/mm-modem-gsm.h43
-rw-r--r--src/mm-modem-helpers.c228
-rw-r--r--src/mm-modem-helpers.h12
-rw-r--r--src/tests/test-modem-helpers.c323
12 files changed, 1444 insertions, 319 deletions
diff --git a/src/mm-auth-provider.h b/src/mm-auth-provider.h
index 94edc44d..c5fc961b 100644
--- a/src/mm-auth-provider.h
+++ b/src/mm-auth-provider.h
@@ -22,9 +22,10 @@
#include "mm-auth-request.h"
/* Authorizations */
-#define MM_AUTHORIZATION_DEVICE "org.freedesktop.ModemManager.Device"
-#define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager.Contacts"
-#define MM_AUTHORIZATION_SMS "org.freedesktop.ModemManager.SMS"
+#define MM_AUTHORIZATION_DEVICE_INFO "org.freedesktop.ModemManager.Device.Info"
+#define MM_AUTHORIZATION_DEVICE_CONTROL "org.freedesktop.ModemManager.Device.Control"
+#define MM_AUTHORIZATION_CONTACTS "org.freedesktop.ModemManager.Contacts"
+#define MM_AUTHORIZATION_SMS "org.freedesktop.ModemManager.SMS"
/******************/
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 425a92f7..fd4d8380 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -29,6 +29,8 @@
#include "mm-at-serial-port.h"
#include "mm-serial-parsers.h"
#include "mm-modem-helpers.h"
+#include "mm-options.h"
+#include "mm-properties-changed-signal.h"
static void modem_init (MMModem *modem_class);
static void modem_gsm_card_init (MMModemGsmCard *gsm_card_class);
@@ -55,10 +57,23 @@ typedef struct {
guint32 pin_check_tries;
guint pin_check_timeout;
+ MMModemGsmAllowedMode allowed_mode;
+
char *oper_code;
char *oper_name;
guint32 ip_method;
- gboolean unsolicited_registration;
+
+ GPtrArray *reg_regex;
+
+ guint poll_id;
+
+ /* CREG and CGREG info */
+ gboolean creg_poll;
+ gboolean cgreg_poll;
+ /* Index 0 for CREG, index 1 for CGREG */
+ gulong lac[2];
+ gulong cell_id[2];
+ MMModemGsmAccessTech act;
MMModemGsmNetworkRegStatus reg_status;
guint pending_reg_id;
@@ -87,6 +102,17 @@ static void reg_state_changed (MMAtSerialPort *port,
GMatchInfo *match_info,
gpointer user_data);
+static void get_reg_status_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data);
+
+static gboolean handle_reg_status_response (MMGenericGsm *self,
+ GString *response,
+ GError **error);
+
+static MMModemGsmAccessTech etsi_act_to_mm_act (gint act);
+
MMModem *
mm_generic_gsm_new (const char *device,
const char *driver,
@@ -104,15 +130,6 @@ mm_generic_gsm_new (const char *device,
}
void
-mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
- gboolean enabled)
-{
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
-
- MM_GENERIC_GSM_GET_PRIVATE (modem)->unsolicited_registration = enabled;
-}
-
-void
mm_generic_gsm_set_cid (MMGenericGsm *modem, guint32 cid)
{
g_return_if_fail (MM_IS_GENERIC_GSM (modem));
@@ -142,6 +159,45 @@ typedef struct {
guint code;
} CPinResult;
+static CPinResult unlock_results[] = {
+ { "SIM PIN", "sim-pin", MM_MOBILE_ERROR_SIM_PIN },
+ { "SIM PUK", "sim-puk", MM_MOBILE_ERROR_SIM_PUK },
+ { "PH-SIM PIN", "ph-sim-pin", MM_MOBILE_ERROR_PH_SIM_PIN },
+ { "PH-FSIM PIN", "ph-fsim-pin", MM_MOBILE_ERROR_PH_FSIM_PIN },
+ { "PH-FSIM PUK", "ph-fsim-puk", MM_MOBILE_ERROR_PH_FSIM_PUK },
+ { "SIM PIN2", "sim-pin2", MM_MOBILE_ERROR_SIM_PIN2 },
+ { "SIM PUK2", "sim-puk2", MM_MOBILE_ERROR_SIM_PUK2 },
+ { "PH-NET PIN", "ph-net-pin", MM_MOBILE_ERROR_NETWORK_PIN },
+ { "PH-NET PUK", "ph-net-puk", MM_MOBILE_ERROR_NETWORK_PUK },
+ { "PH-NETSUB PIN", "ph-netsub-pin", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
+ { "PH-NETSUB PUK", "ph-netsub-puk", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
+ { "PH-SP PIN", "ph-sp-pin", MM_MOBILE_ERROR_SERVICE_PIN },
+ { "PH-SP PUK", "ph-sp-puk", MM_MOBILE_ERROR_SERVICE_PUK },
+ { "PH-CORP PIN", "ph-corp-pin", MM_MOBILE_ERROR_CORP_PIN },
+ { "PH-CORP PUK", "ph-corp-puk", MM_MOBILE_ERROR_CORP_PUK },
+ { NULL, NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
+};
+
+static GError *
+error_for_unlock_required (const char *unlock)
+{
+ CPinResult *iter = &unlock_results[0];
+
+ if (!unlock || !strlen (unlock))
+ return NULL;
+
+ /* Translate the error */
+ while (iter->result) {
+ if (!strcmp (iter->normalized, unlock))
+ return mm_mobile_error_for_code (iter->code);
+ iter++;
+ }
+
+ return g_error_new (MM_MOBILE_ERROR,
+ MM_MOBILE_ERROR_UNKNOWN,
+ "Unknown unlock request '%s'", unlock);
+}
+
static void
pin_check_done (MMAtSerialPort *port,
GString *response,
@@ -150,24 +206,6 @@ pin_check_done (MMAtSerialPort *port,
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean parsed = FALSE;
- static CPinResult results[] = {
- { "SIM PIN", "sim-pin", MM_MOBILE_ERROR_SIM_PIN },
- { "SIM PUK", "sim-puk", MM_MOBILE_ERROR_SIM_PUK },
- { "PH-SIM PIN", "ph-sim-pin", MM_MOBILE_ERROR_PH_SIM_PIN },
- { "PH-FSIM PIN", "ph-fsim-pin", MM_MOBILE_ERROR_PH_FSIM_PIN },
- { "PH-FSIM PUK", "ph-fsim-puk", MM_MOBILE_ERROR_PH_FSIM_PUK },
- { "SIM PIN2", "sim-pin2", MM_MOBILE_ERROR_SIM_PIN2 },
- { "SIM PUK2", "sim-puk2", MM_MOBILE_ERROR_SIM_PUK2 },
- { "PH-NET PIN", "ph-net-pin", MM_MOBILE_ERROR_NETWORK_PIN },
- { "PH-NET PUK", "ph-net-puk", MM_MOBILE_ERROR_NETWORK_PUK },
- { "PH-NETSUB PIN", "ph-netsub-pin", MM_MOBILE_ERROR_NETWORK_SUBSET_PIN },
- { "PH-NETSUB PUK", "ph-netsub-puk", MM_MOBILE_ERROR_NETWORK_SUBSET_PUK },
- { "PH-SP PIN", "ph-sp-pin", MM_MOBILE_ERROR_SERVICE_PIN },
- { "PH-SP PUK", "ph-sp-puk", MM_MOBILE_ERROR_SERVICE_PUK },
- { "PH-CORP PIN", "ph-corp-pin", MM_MOBILE_ERROR_CORP_PIN },
- { "PH-CORP PUK", "ph-corp-puk", MM_MOBILE_ERROR_CORP_PUK },
- { NULL, NULL, MM_MOBILE_ERROR_PHONE_FAILURE },
- };
if (error)
info->error = g_error_copy (error);
@@ -178,7 +216,7 @@ pin_check_done (MMAtSerialPort *port,
mm_modem_base_set_unlock_required (MM_MODEM_BASE (info->modem), NULL);
parsed = TRUE;
} else {
- CPinResult *iter = &results[0];
+ CPinResult *iter = &unlock_results[0];
/* Translate the error */
while (iter->result) {
@@ -359,16 +397,22 @@ mm_generic_gsm_grab_port (MMGenericGsm *self,
port = mm_modem_base_add_port (MM_MODEM_BASE (self), subsys, name, ptype);
if (port && MM_IS_AT_SERIAL_PORT (port)) {
+ GPtrArray *array;
+ int i;
+
mm_at_serial_port_set_response_parser (MM_AT_SERIAL_PORT (port),
mm_serial_parser_v1_parse,
mm_serial_parser_v1_new (),
mm_serial_parser_v1_destroy);
- regex = g_regex_new ("\\r\\n\\+CREG: (\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
- mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port),
- regex, reg_state_changed,
- self, NULL);
- g_regex_unref (regex);
+ /* Set up CREG unsolicited message handlers */
+ array = mm_gsm_creg_regex_get (FALSE);
+ for (i = 0; i < array->len; i++) {
+ regex = g_ptr_array_index (array, i);
+
+ mm_at_serial_port_add_unsolicited_msg_handler (MM_AT_SERIAL_PORT (port), regex, reg_state_changed, self, NULL);
+ }
+ mm_gsm_creg_regex_destroy (array);
if (ptype == MM_PORT_TYPE_PRIMARY) {
priv->primary = MM_AT_SERIAL_PORT (port);
@@ -454,27 +498,223 @@ release_port (MMModem *modem, const char *subsys, const char *name)
check_valid (MM_GENERIC_GSM (modem));
}
+static void
+reg_poll_response (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+
+ if (!error)
+ handle_reg_status_response (self, response, NULL);
+}
+
+static gboolean
+periodic_poll_cb (gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (user_data);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ MMAtSerialPort *port = priv->primary;
+
+ if (mm_port_get_connected (MM_PORT (priv->primary))) {
+ if (!priv->secondary)
+ return TRUE; /* oh well, try later */
+
+ /* Use secondary port if primary is connected */
+ port = priv->secondary;
+ }
+
+ if (priv->creg_poll)
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, self);
+ if (priv->cgreg_poll)
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, self);
+
+ return TRUE; /* continue running */
+}
+
+static void
+cgreg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ g_clear_error (&info->error);
+
+ /* The modem doesn't like unsolicited CGREG, so we'll need to poll */
+ priv->cgreg_poll = TRUE;
+ if (!priv->poll_id)
+ priv->poll_id = g_timeout_add_seconds (10, periodic_poll_cb, info->modem);
+ }
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+ }
+ mm_callback_info_schedule (info);
+}
+
+static void
+cgreg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Ignore errors except modem removal errors */
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ g_clear_error (&info->error);
+ /* Try CGREG=1 instead */
+ mm_at_serial_port_queue_command (port, "+CGREG=1", 3, cgreg1_done, info);
+ } else {
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CGREG?", 10, reg_poll_response, info->modem);
+
+ /* All done */
+ mm_callback_info_schedule (info);
+ }
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+creg1_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ if (info->error) {
+ g_clear_error (&info->error);
+
+ /* The modem doesn't like unsolicited CREG, so we'll need to poll */
+ priv->creg_poll = TRUE;
+ if (!priv->poll_id)
+ priv->poll_id = g_timeout_add_seconds (10, periodic_poll_cb, info->modem);
+ }
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+creg2_done (MMAtSerialPort *port,
+ GString *response,
+ GError *error,
+ gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ /* Ignore errors except modem removal errors */
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (info->modem) {
+ if (info->error) {
+ g_clear_error (&info->error);
+ mm_at_serial_port_queue_command (port, "+CREG=1", 3, creg1_done, info);
+ } else {
+ /* Success; get initial state */
+ mm_at_serial_port_queue_command (port, "+CREG?", 10, reg_poll_response, info->modem);
+
+ /* Now try to set up CGREG messages */
+ mm_at_serial_port_queue_command (port, "+CGREG=2", 3, cgreg2_done, info);
+ }
+ } else {
+ /* Modem got removed */
+ mm_callback_info_schedule (info);
+ }
+}
+
+static void
+get_allowed_mode_done (MMModem *modem,
+ MMModemGsmAllowedMode mode,
+ GError *error,
+ gpointer user_data)
+{
+ if (modem) {
+ mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (modem),
+ error ? MM_MODEM_GSM_ALLOWED_MODE_ANY : mode);
+ }
+}
+
void
-mm_generic_gsm_enable_complete (MMGenericGsm *modem,
+mm_generic_gsm_enable_complete (MMGenericGsm *self,
GError *error,
MMCallbackInfo *info)
{
- g_return_if_fail (modem != NULL);
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
g_return_if_fail (info != NULL);
if (error) {
- mm_modem_set_state (MM_MODEM (modem),
+ mm_modem_set_state (MM_MODEM (self),
MM_MODEM_STATE_DISABLED,
MM_MODEM_STATE_REASON_NONE);
info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ return;
} else {
/* Modem is enabled; update the state */
- mm_generic_gsm_update_enabled_state (modem, FALSE, MM_MODEM_STATE_REASON_NONE);
+ mm_generic_gsm_update_enabled_state (self, FALSE, MM_MODEM_STATE_REASON_NONE);
}
- mm_callback_info_schedule (info);
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ /* Open the second port here if the modem has one. We'll use it for
+ * signal strength and registration updates when the device is connected,
+ * but also many devices will send unsolicited registration or other
+ * messages to the secondary port but not the primary.
+ */
+ if (priv->secondary) {
+ if (!mm_serial_port_open (MM_SERIAL_PORT (priv->secondary), &error)) {
+ if (mm_options_debug ()) {
+ g_warning ("%s: error opening secondary port: (%d) %s",
+ __func__,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ }
+ }
+ }
+
+ /* Get allowed mode */
+ if (MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode)
+ MM_GENERIC_GSM_GET_CLASS (self)->get_allowed_mode (self, get_allowed_mode_done, NULL);
+
+ /* Set up unsolicited registration notifications */
+ mm_at_serial_port_queue_command (priv->primary, "+CREG=2", 3, creg2_done, info);
+}
+
+static void
+real_do_enable_power_up_done (MMGenericGsm *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info)
+{
+ /* Ignore power-up errors as not all devices actually support CFUN=1 */
+ mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
}
static void
@@ -485,16 +725,15 @@ enable_done (MMAtSerialPort *port,
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- /* Ignore power-up command errors, not all devices actually support
- * CFUN=1.
- */
- /* FIXME: instead of just ignoring errors, since we allow subclasses
- * to override the power-on command, make a class function for powering
- * on the phone and let the subclass decided whether it wants to handle
- * errors or ignore them.
+ /* Let subclasses handle the power up command response/error; many devices
+ * don't support +CFUN, but for those that do let them handle the error
+ * correctly.
*/
-
- mm_generic_gsm_enable_complete (MM_GENERIC_GSM (info->modem), NULL, info);
+ g_assert (MM_GENERIC_GSM_GET_CLASS (info->modem)->do_enable_power_up_done);
+ MM_GENERIC_GSM_GET_CLASS (info->modem)->do_enable_power_up_done (MM_GENERIC_GSM (info->modem),
+ response,
+ error,
+ info);
}
static void
@@ -520,11 +759,6 @@ init_done (MMAtSerialPort *port,
mm_at_serial_port_queue_command (port, cmd, 2, NULL, NULL);
g_free (cmd);
- if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->unsolicited_registration)
- mm_at_serial_port_queue_command (port, "+CREG=1", 5, NULL, NULL);
- else
- mm_at_serial_port_queue_command (port, "+CREG=0", 5, NULL, NULL);
-
g_object_get (G_OBJECT (info->modem), MM_GENERIC_GSM_POWER_UP_CMD, &cmd, NULL);
if (cmd && strlen (cmd))
mm_at_serial_port_queue_command (port, cmd, 5, enable_done, user_data);
@@ -566,6 +800,18 @@ enable (MMModem *modem,
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
GError *error = NULL;
+ const char *unlock;
+
+ /* If the device needs a PIN, deal with that now */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock) {
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (modem, callback, user_data);
+ info->error = error_for_unlock_required (unlock);
+ mm_callback_info_schedule (info);
+ return;
+ }
/* First, reset the previously used CID */
mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
@@ -643,7 +889,8 @@ disable (MMModem *modem,
MMModemFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+ MMGenericGsm *self = MM_GENERIC_GSM (modem);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
MMModemState state;
@@ -651,6 +898,21 @@ disable (MMModem *modem,
mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0);
mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ priv->lac[0] = 0;
+ priv->lac[1] = 0;
+ priv->cell_id[0] = 0;
+ priv->cell_id[1] = 0;
+ mm_generic_gsm_update_access_technology (self, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+
+ /* Close the secondary port if its open */
+ if (priv->secondary && mm_serial_port_is_open (MM_SERIAL_PORT (priv->secondary)))
+ mm_serial_port_close (MM_SERIAL_PORT (priv->secondary));
+
info = mm_callback_info_new (modem, callback, user_data);
/* Cache the previous state so we can reset it if the operation fails */
@@ -802,26 +1064,28 @@ get_card_info (MMModem *modem,
mm_at_serial_port_queue_command_cached (priv->primary, "+CGMR", 3, get_version_done, info);
}
+#define PIN_CLOSE_PORT_TAG "close-port"
+
static void
pin_puk_recheck_done (MMModem *modem, GError *error, gpointer user_data)
{
- gboolean close_port = !!user_data;
+ MMCallbackInfo *info = (MMCallbackInfo *) user_data;
+ gboolean close_port = !!mm_callback_info_get_data (info, PIN_CLOSE_PORT_TAG);
/* modem could have been removed before we get here, in which case
* 'modem' will be NULL.
*/
- if (modem) {
- MMGenericGsmPrivate *priv;
+ info->error = mm_modem_check_removed (modem, error);
- g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+ if (modem && close_port) {
+ MMSerialPort *port;
- priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
- if (close_port)
- mm_serial_port_close (MM_SERIAL_PORT (priv->primary));
+ port = MM_SERIAL_PORT (MM_GENERIC_GSM_GET_PRIVATE (modem)->primary);
+ mm_serial_port_close (port);
}
-}
-#define PIN_CLOSE_PORT_TAG "close-port"
+ mm_callback_info_schedule (info);
+}
static void
send_puk_done (MMAtSerialPort *port,
@@ -832,15 +1096,16 @@ send_puk_done (MMAtSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean close_port = !!mm_callback_info_get_data (info, PIN_CLOSE_PORT_TAG);
- if (error)
+ if (error) {
info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ if (close_port)
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ }
- /* Get latest PUK status */
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem),
- pin_puk_recheck_done,
- GUINT_TO_POINTER (close_port));
-
- mm_callback_info_schedule (info);
+ /* Get latest PIN status */
+ mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
}
static void
@@ -892,15 +1157,16 @@ send_pin_done (MMAtSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
gboolean close_port = !!mm_callback_info_get_data (info, PIN_CLOSE_PORT_TAG);
- if (error)
+ if (error) {
info->error = g_error_copy (error);
+ mm_callback_info_schedule (info);
+ if (close_port)
+ mm_serial_port_close (MM_SERIAL_PORT (port));
+ return;
+ }
/* Get latest PIN status */
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem),
- pin_puk_recheck_done,
- GUINT_TO_POINTER (close_port));
-
- mm_callback_info_schedule (info);
+ mm_generic_gsm_check_pin (MM_GENERIC_GSM (info->modem), pin_puk_recheck_done, info);
}
static void
@@ -1199,14 +1465,22 @@ reg_state_changed (MMAtSerialPort *port,
{
MMGenericGsm *self = MM_GENERIC_GSM (user_data);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- char *str;
- gboolean done;
+ guint32 state = 0, idx;
+ gulong lac = 0, cell_id = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ GError *error = NULL;
- str = g_match_info_fetch (match_info, 1);
- done = reg_status_updated (self, atoi (str), NULL);
- g_free (str);
+ if (!mm_gsm_parse_creg_response (match_info, &state, &lac, &cell_id, &act, &cgreg, &error)) {
+ if (mm_options_debug ()) {
+ g_warning ("%s: error parsing unsolicited registration: %s",
+ __func__,
+ error && error->message ? error->message : "(unknown)");
+ }
+ return;
+ }
- if (done) {
+ if (reg_status_updated (self, state, NULL)) {
/* If registration is finished (either registered or failed) but the
* registration query hasn't completed yet, just remove the timeout and
* let the registration query complete.
@@ -1216,6 +1490,14 @@ reg_state_changed (MMAtSerialPort *port,
priv->pending_reg_id = 0;
}
}
+
+ idx = cgreg ? 1 : 0;
+ priv->lac[idx] = lac;
+ priv->cell_id[idx] = cell_id;
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act));
}
static gboolean
@@ -1242,6 +1524,58 @@ reg_status_again_remove (gpointer data)
g_source_remove (id);
}
+static gboolean
+handle_reg_status_response (MMGenericGsm *self,
+ GString *response,
+ GError **error)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+ GMatchInfo *match_info;
+ guint32 status = 0, idx;
+ gulong lac = 0, ci = 0;
+ gint act = -1;
+ gboolean cgreg = FALSE;
+ guint i;
+
+ /* Try to match the response */
+ for (i = 0; i < priv->reg_regex->len; i++) {
+ GRegex *r = g_ptr_array_index (priv->reg_regex, i);
+
+ if (g_regex_match (r, response->str, 0, &match_info))
+ break;
+ g_match_info_free (match_info);
+ match_info = NULL;
+ }
+
+ if (!match_info) {
+ g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse the registration status response");
+ return FALSE;
+ }
+
+ /* And parse it */
+ if (!mm_gsm_parse_creg_response (match_info, &status, &lac, &ci, &act, &cgreg, error)) {
+ g_match_info_free (match_info);
+ return FALSE;
+ }
+
+ /* Success; update cached location information */
+ idx = cgreg ? 1 : 0;
+ priv->lac[idx] = lac;
+ priv->cell_id[idx] = ci;
+
+ /* Only update access technology if it appeared in the CREG/CGREG response */
+ if (act != -1)
+ mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act));
+
+ if ((cgreg == FALSE) && status >= 0) {
+ /* Update cached registration status */
+ reg_status_updated (self, status, NULL);
+ }
+
+ return TRUE;
+}
+
static void
get_reg_status_done (MMAtSerialPort *port,
GString *response,
@@ -1251,72 +1585,46 @@ get_reg_status_done (MMAtSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- int reg_status = -1;
- GRegex *r;
- GMatchInfo *match_info;
- char *tmp;
guint id;
- gboolean status_done;
+
+ /* This function should only get called during the connect sequence when
+ * polling for registration state, since explicit registration requests
+ * from D-Bus clients are filled from the cached registration state.
+ */
+ g_return_if_fail (info == priv->pending_reg_info);
if (error) {
info->error = g_error_copy (error);
goto reg_done;
}
- r = g_regex_new ("\\+CREG:\\s*(\\d+),\\s*(\\d+)",
- G_REGEX_RAW | G_REGEX_OPTIMIZE,
- 0, &info->error);
- if (r) {
- g_regex_match_full (r, response->str, response->len, 0, 0, &match_info, &info->error);
- if (g_match_info_matches (match_info)) {
- /* Get reg status */
- tmp = g_match_info_fetch (match_info, 2);
- if (isdigit (tmp[0])) {
- tmp[1] = '\0';
- reg_status = atoi (tmp);
- } else {
- info->error = g_error_new (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Unknown registration status '%s'",
- tmp);
- }
- g_free (tmp);
- } else {
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Could not parse the registration status response");
- }
- g_match_info_free (match_info);
- g_regex_unref (r);
- }
+ /* The unsolicited registration state handlers will intercept the CREG
+ * response and update the cached registration state for us, so we usually
+ * just need to check the cached state here.
+ */
- if (reg_status >= 0) {
- /* Update cached registration status */
- status_done = reg_status_updated (self, reg_status, &info->error);
+ if (strlen (response->str)) {
+ /* But just in case the unsolicited handlers doesn't do it... */
+ if (!handle_reg_status_response (self, response, &info->error))
+ goto reg_done;
+ }
- /* If we're waiting for automatic registration to complete and it's
- * not done yet, check again in a few seconds.
+ if ( priv->reg_status != MM_MODEM_GSM_NETWORK_REG_STATUS_HOME
+ && priv->reg_status != MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING
+ && priv->reg_status != MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED) {
+ /* If we're still waiting for automatic registration to complete or
+ * fail, check again in a few seconds.
*/
- if ((info == priv->pending_reg_info) && !status_done) {
- g_clear_error (&info->error);
-
- /* Not registered yet; poll registration status again */
- id = g_timeout_add_seconds (1, reg_status_again, info);
- mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG,
- GUINT_TO_POINTER (id),
- reg_status_again_remove);
- return;
- }
+ id = g_timeout_add_seconds (1, reg_status_again, info);
+ mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG,
+ GUINT_TO_POINTER (id),
+ reg_status_again_remove);
+ return;
}
reg_done:
- if (info == priv->pending_reg_info) {
- /* For pending registration, this will schedule the callback for us */
- mm_generic_gsm_pending_registration_stop (self);
- } else {
- /* Otherwise for a direct registration request, schedule the callback now */
- mm_callback_info_schedule (info);
- }
+ /* This will schedule the pending registration's the callback for us */
+ mm_generic_gsm_pending_registration_stop (self);
}
static void
@@ -1345,7 +1653,9 @@ register_done (MMAtSerialPort *port,
if (priv->pending_reg_info) {
g_warn_if_fail (info == priv->pending_reg_info);
- /* Ignore errors here, get the actual registration status */
+ /* Don't use cached registration state here since it could be up to
+ * 30 seconds old. Get fresh registration state.
+ */
get_registration_status (port, info);
}
}
@@ -1430,28 +1740,16 @@ get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data)
{
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
MMCallbackInfo *info;
- MMSerialPort *port = priv->primary;
info = mm_callback_info_new_full (MM_MODEM (self),
gsm_network_reg_info_invoke,
G_CALLBACK (callback),
user_data);
-
- if (mm_port_get_connected (MM_PORT (priv->primary))) {
- if (!priv->secondary) {
- info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_CONNECTED,
- "Cannot get registration info while connected");
- mm_callback_info_schedule (info);
- return;
- }
-
- /* Use secondary port if primary is connected */
- port = priv->secondary;
- }
-
- get_registration_status (port, info);
+ /* Registration info updates are handled internally either by unsolicited
+ * updates or by polling. Thus just return the cached registration state.
+ */
+ mm_callback_info_schedule (info);
}
void
@@ -1897,6 +2195,128 @@ get_signal_quality (MMModemGsmNetwork *modem,
}
/*****************************************************************************/
+
+typedef struct {
+ MMModemGsmAccessTech mm_act;
+ gint etsi_act;
+} ModeEtsi;
+
+static ModeEtsi modes_table[] = {
+ { MM_MODEM_GSM_ACCESS_TECH_GSM, 0 },
+ { MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT, 1 },
+ { MM_MODEM_GSM_ACCESS_TECH_UMTS, 2 },
+ { MM_MODEM_GSM_ACCESS_TECH_EDGE, 3 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSDPA, 4 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSUPA, 5 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSPA, 6 },
+ { MM_MODEM_GSM_ACCESS_TECH_HSPA, 7 }, /* E-UTRAN/LTE => HSPA for now */
+ { MM_MODEM_GSM_ACCESS_TECH_UNKNOWN, -1 },
+};
+
+static MMModemGsmAccessTech
+etsi_act_to_mm_act (gint act)
+{
+ ModeEtsi *iter = &modes_table[0];
+
+ while (iter->mm_act != MM_MODEM_GSM_ACCESS_TECH_UNKNOWN) {
+ if (iter->etsi_act == act)
+ return iter->mm_act;
+ }
+ return MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+}
+
+void
+mm_generic_gsm_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (modem != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (modem));
+
+ g_return_if_fail (act >= MM_MODEM_GSM_ACCESS_TECH_UNKNOWN && act <= MM_MODEM_GSM_ACCESS_TECH_LAST);
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
+
+ if (act != priv->act) {
+ MMModemDeprecatedMode old_mode;
+
+ priv->act = act;
+ g_object_notify (G_OBJECT (modem), MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+
+ /* Deprecated value */
+ old_mode = mm_modem_gsm_network_act_to_old_mode (act);
+ g_signal_emit_by_name (G_OBJECT (modem), "network-mode", old_mode);
+ }
+}
+
+void
+mm_generic_gsm_update_allowed_mode (MMGenericGsm *self,
+ MMModemGsmAllowedMode mode)
+{
+ MMGenericGsmPrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (MM_IS_GENERIC_GSM (self));
+
+ priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (mode != priv->allowed_mode) {
+ priv->allowed_mode = mode;
+ g_object_notify (G_OBJECT (self), MM_MODEM_GSM_NETWORK_ALLOWED_MODE);
+ }
+}
+
+static void
+set_allowed_mode_done (MMModem *modem, GError *error, gpointer user_data)
+{
+ MMCallbackInfo *info = user_data;
+
+ info->error = mm_modem_check_removed (info->modem, error);
+ if (!info->error) {
+ MMModemGsmAllowedMode mode = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "mode"));
+
+ mm_generic_gsm_update_allowed_mode (MM_GENERIC_GSM (info->modem), mode);
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+set_allowed_mode (MMModemGsmNetwork *net,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
+{
+ MMGenericGsm *self = MM_GENERIC_GSM (net);
+ MMCallbackInfo *info;
+
+ info = mm_callback_info_new (MM_MODEM (self), callback, user_data);
+
+ switch (mode) {
+ case MM_MODEM_GSM_ALLOWED_MODE_ANY:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED:
+ case MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY:
+ case MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY:
+ if (!MM_GENERIC_GSM_GET_CLASS (self)->set_allowed_mode) {
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Operation not supported");
+ } else {
+ mm_callback_info_set_data (info, "mode", GUINT_TO_POINTER (mode), NULL);
+ MM_GENERIC_GSM_GET_CLASS (self)->set_allowed_mode (self, mode, set_allowed_mode_done, info);
+ }
+ break;
+ default:
+ info->error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, "Invalid mode.");
+ break;
+ }
+
+ if (info->error)
+ mm_callback_info_schedule (info);
+}
+
+/*****************************************************************************/
/* MMModemGsmSms interface */
static void
@@ -1972,9 +2392,8 @@ mm_generic_gsm_get_at_port (MMGenericGsm *modem,
/* MMModemSimple interface */
typedef enum {
- SIMPLE_STATE_BEGIN = 0,
+ SIMPLE_STATE_CHECK_PIN = 0,
SIMPLE_STATE_ENABLE,
- SIMPLE_STATE_CHECK_PIN,
SIMPLE_STATE_REGISTER,
SIMPLE_STATE_SET_APN,
SIMPLE_STATE_CONNECT,
@@ -2005,68 +2424,73 @@ static void
simple_state_machine (MMModem *modem, GError *error, gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *str;
+ const char *str, *unlock = NULL;
SimpleState state = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "simple-connect-state"));
- gboolean need_pin = FALSE;
+ SimpleState next_state = state;
+ gboolean done = FALSE;
if (error) {
- if (g_error_matches (error, MM_MOBILE_ERROR, MM_MOBILE_ERROR_SIM_PIN)) {
- need_pin = TRUE;
- state = SIMPLE_STATE_CHECK_PIN;
- } else {
- info->error = g_error_copy (error);
- goto out;
- }
+ info->error = g_error_copy (error);
+ goto out;
}
switch (state) {
- case SIMPLE_STATE_BEGIN:
- state = SIMPLE_STATE_ENABLE;
- mm_modem_enable (modem, simple_state_machine, info);
- break;
- case SIMPLE_STATE_ENABLE:
- state = SIMPLE_STATE_CHECK_PIN;
- mm_generic_gsm_check_pin (MM_GENERIC_GSM (modem), simple_state_machine, info);
- break;
case SIMPLE_STATE_CHECK_PIN:
- if (need_pin) {
- str = simple_get_string_property (info, "pin", &info->error);
- if (str)
- mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info);
- else
- info->error = g_error_copy (error);
- } else {
- str = simple_get_string_property (info, "network_id", &info->error);
- state = SIMPLE_STATE_REGISTER;
- if (!info->error)
- mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ next_state = SIMPLE_STATE_ENABLE;
+
+ /* If we need a PIN, send it now */
+ unlock = mm_modem_base_get_unlock_required (MM_MODEM_BASE (modem));
+ if (unlock) {
+ gboolean success = FALSE;
+
+ if (!strcmp (unlock, "sim-pin")) {
+ str = simple_get_string_property (info, "pin", &info->error);
+ if (str) {
+ mm_modem_gsm_card_send_pin (MM_MODEM_GSM_CARD (modem), str, simple_state_machine, info);
+ success = TRUE;
+ }
+ }
+ if (!success && !info->error)
+ info->error = error_for_unlock_required (unlock);
+ break;
}
+ /* Fall through if no PIN required */
+ case SIMPLE_STATE_ENABLE:
+ next_state = SIMPLE_STATE_REGISTER;
+ mm_modem_enable (modem, simple_state_machine, info);
break;
case SIMPLE_STATE_REGISTER:
+ next_state = SIMPLE_STATE_SET_APN;
+ str = simple_get_string_property (info, "network_id", &info->error);
+ if (info->error)
+ str = NULL;
+ mm_modem_gsm_network_register (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ break;
+ case SIMPLE_STATE_SET_APN:
+ next_state = SIMPLE_STATE_CONNECT;
str = simple_get_string_property (info, "apn", &info->error);
- if (str) {
- state = SIMPLE_STATE_SET_APN;
- mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
+ if (str || info->error) {
+ if (str)
+ mm_modem_gsm_network_set_apn (MM_MODEM_GSM_NETWORK (modem), str, simple_state_machine, info);
break;
}
- /* Fall through */
- case SIMPLE_STATE_SET_APN:
- str = simple_get_string_property (info, "number", &info->error);
- state = SIMPLE_STATE_CONNECT;
- mm_modem_connect (modem, str, simple_state_machine, info);
- break;
+ /* Fall through if no APN or no 'apn' property error */
case SIMPLE_STATE_CONNECT:
- state = SIMPLE_STATE_DONE;
+ next_state = SIMPLE_STATE_DONE;
+ str = simple_get_string_property (info, "number", &info->error);
+ if (!info->error)
+ mm_modem_connect (modem, str, simple_state_machine, info);
break;
case SIMPLE_STATE_DONE:
+ done = TRUE;
break;
}
out:
- if (info->error || state == SIMPLE_STATE_DONE)
+ if (info->error || done)
mm_callback_info_schedule (info);
else
- mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (state), NULL);
+ mm_callback_info_set_data (info, "simple-connect-state", GUINT_TO_POINTER (next_state), NULL);
}
static void
@@ -2142,17 +2566,6 @@ simple_status_got_band (MMModem *modem,
}
static void
-simple_status_got_mode (MMModem *modem,
- guint32 result,
- GError *error,
- gpointer user_data)
-{
- /* Ignore network mode errors since there's no generic implementation for it */
- if (!error)
- g_hash_table_insert ((GHashTable *) user_data, "network_mode", simple_uint_value (result));
-}
-
-static void
simple_status_got_reg_info (MMModemGsmNetwork *modem,
MMModemGsmNetworkRegStatus status,
const char *oper_code,
@@ -2191,9 +2604,11 @@ simple_get_status (MMModemSimple *simple,
MMModemSimpleGetStatusFn callback,
gpointer user_data)
{
- MMModemGsmNetwork *gsm;
+ MMModemGsmNetwork *gsm = MM_MODEM_GSM_NETWORK (simple);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (simple);
GHashTable *properties;
MMCallbackInfo *info;
+ MMModemDeprecatedMode old_mode;
info = mm_callback_info_new_full (MM_MODEM (simple),
simple_get_status_invoke,
@@ -2203,11 +2618,18 @@ simple_get_status (MMModemSimple *simple,
properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, simple_free_gvalue);
mm_callback_info_set_data (info, "simple-get-status", properties, (GDestroyNotify) g_hash_table_unref);
- gsm = MM_MODEM_GSM_NETWORK (simple);
mm_modem_gsm_network_get_signal_quality (gsm, simple_status_got_signal_quality, properties);
mm_modem_gsm_network_get_band (gsm, simple_status_got_band, properties);
- mm_modem_gsm_network_get_mode (gsm, simple_status_got_mode, properties);
mm_modem_gsm_network_get_registration_info (gsm, simple_status_got_reg_info, properties);
+
+ if (priv->act > -1) {
+ /* Deprecated key */
+ old_mode = mm_modem_gsm_network_act_to_old_mode (priv->act);
+ g_hash_table_insert (properties, "network_mode", simple_uint_value (old_mode));
+
+ /* New key */
+ g_hash_table_insert (properties, "access_technology", simple_uint_value (priv->act));
+ }
}
/*****************************************************************************/
@@ -2241,6 +2663,7 @@ modem_gsm_network_init (MMModemGsmNetwork *class)
{
class->do_register = do_register;
class->get_registration_info = get_registration_info;
+ class->set_allowed_mode = set_allowed_mode;
class->set_apn = set_apn;
class->scan = scan;
class->get_signal_quality = get_signal_quality;
@@ -2262,6 +2685,18 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_gsm_init (MMGenericGsm *self)
{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ priv->act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+ priv->reg_regex = mm_gsm_creg_regex_get (TRUE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
+ MM_MODEM_GSM_NETWORK_DBUS_INTERFACE);
+
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY,
+ MM_MODEM_GSM_NETWORK_DBUS_INTERFACE);
}
static void
@@ -2276,6 +2711,8 @@ set_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL:
case MM_GENERIC_GSM_PROP_SUPPORTED_BANDS:
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
+ case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
+ case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -2324,6 +2761,15 @@ get_property (GObject *object, guint prop_id,
case MM_GENERIC_GSM_PROP_SUPPORTED_MODES:
g_value_set_uint (value, 0);
break;
+ case MM_GENERIC_GSM_PROP_ALLOWED_MODE:
+ g_value_set_uint (value, priv->allowed_mode);
+ break;
+ case MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY:
+ if (mm_modem_get_state (MM_MODEM (object)) >= MM_MODEM_STATE_ENABLED)
+ g_value_set_uint (value, priv->act);
+ else
+ g_value_set_uint (value, MM_MODEM_GSM_ACCESS_TECH_UNKNOWN);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -2340,6 +2786,13 @@ finalize (GObject *object)
if (priv->pin_check_timeout)
g_source_remove (priv->pin_check_timeout);
+ if (priv->poll_id) {
+ g_source_remove (priv->poll_id);
+ priv->poll_id = 0;
+ }
+
+ mm_gsm_creg_regex_destroy (priv->reg_regex);
+
g_free (priv->oper_code);
g_free (priv->oper_name);
@@ -2360,6 +2813,7 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
object_class->finalize = finalize;
klass->do_enable = real_do_enable;
+ klass->do_enable_power_up_done = real_do_enable_power_up_done;
/* Properties */
g_object_class_override_property (object_class,
@@ -2378,6 +2832,14 @@ mm_generic_gsm_class_init (MMGenericGsmClass *klass)
MM_GENERIC_GSM_PROP_SUPPORTED_MODES,
MM_MODEM_GSM_CARD_SUPPORTED_MODES);
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_ALLOWED_MODE,
+ MM_MODEM_GSM_NETWORK_ALLOWED_MODE);
+
+ g_object_class_override_property (object_class,
+ MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY,
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY);
+
g_object_class_install_property
(object_class, MM_GENERIC_GSM_PROP_POWER_UP_CMD,
g_param_spec_string (MM_GENERIC_GSM_POWER_UP_CMD,
diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h
index b587d3a1..bf2b4576 100644
--- a/src/mm-generic-gsm.h
+++ b/src/mm-generic-gsm.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 - 2009 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_GENERIC_GSM_H
@@ -43,7 +43,9 @@ typedef enum {
MM_GENERIC_GSM_PROP_INIT_CMD,
MM_GENERIC_GSM_PROP_SUPPORTED_BANDS,
MM_GENERIC_GSM_PROP_SUPPORTED_MODES,
- MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL
+ MM_GENERIC_GSM_PROP_INIT_CMD_OPTIONAL,
+ MM_GENERIC_GSM_PROP_ALLOWED_MODE,
+ MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY
} MMGenericGsmProp;
@@ -59,9 +61,41 @@ typedef struct {
* that need to perform custom initialization sequences or other setup should
* generally override this method instead of the MMModem interface's enable()
* method, unless the customization must happen *after* the generic init
- * sequence has completed.
+ * sequence has completed. When the subclass' enable attempt is complete
+ * the subclass should call mm_generic_gsm_enable_complete() with any error
+ * encountered during the process and the MMCallbackInfo created from the
+ * callback and user_data passed in here.
*/
void (*do_enable) (MMGenericGsm *self, MMModemFn callback, gpointer user_data);
+
+ /* Called after the generic class has attempted to power up the modem.
+ * Subclasses can handle errors here if they know the device supports their
+ * power up command. Will only be called if the device does *not* override
+ * the MMModem enable() command or allows the generic class' do_enable()
+ * handler to execute.
+ */
+ void (*do_enable_power_up_done) (MMGenericGsm *self,
+ GString *response,
+ GError *error,
+ MMCallbackInfo *info);
+
+ /* Called by the generic class to set the allowed operating mode of the device */
+ void (*set_allowed_mode) (MMGenericGsm *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to get the allowed operating mode of the device */
+ void (*get_allowed_mode) (MMGenericGsm *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
+
+ /* Called by the generic class to the current radio access technology the
+ * device is using while communicating with the base station.
+ */
+ void (*get_access_technology) (MMGenericGsm *self,
+ MMModemUIntFn callback,
+ gpointer user_data);
} MMGenericGsmClass;
GType mm_generic_gsm_get_type (void);
@@ -74,10 +108,7 @@ MMModem *mm_generic_gsm_new (const char *device,
#define MM_GENERIC_GSM_PREV_STATE_TAG "prev-state"
-void mm_generic_gsm_set_unsolicited_registration (MMGenericGsm *modem,
- gboolean enabled);
-
-void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
+void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem);
void mm_generic_gsm_set_cid (MMGenericGsm *modem,
guint32 cid);
@@ -86,6 +117,22 @@ guint32 mm_generic_gsm_get_cid (MMGenericGsm *modem);
void mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
MMModemGsmNetworkRegStatus status);
+/* Called to asynchronously update the current allowed operating mode that the
+ * device is allowed to use when connecting to a network. This isn't the
+ * specific access technology the device is currently using (see
+ * mm_generic_gsm_set_access_technology() for that) but the mode the device is
+ * allowed to choose from when connecting.
+ */
+void mm_generic_gsm_update_allowed_mode (MMGenericGsm *modem,
+ MMModemGsmAllowedMode mode);
+
+/* Called to asynchronously update the current access technology of the device;
+ * this is NOT the 2G/3G mode preference, but the current radio access
+ * technology being used to communicate with the base station.
+ */
+void mm_generic_gsm_update_access_technology (MMGenericGsm *modem,
+ MMModemGsmAccessTech act);
+
void mm_generic_gsm_check_pin (MMGenericGsm *modem,
MMModemFn callback,
gpointer user_data);
diff --git a/src/mm-modem-base.c b/src/mm-modem-base.c
index 20ba2a4c..8af9bd14 100644
--- a/src/mm-modem-base.c
+++ b/src/mm-modem-base.c
@@ -265,6 +265,9 @@ mm_modem_base_init (MMModemBase *self)
mm_properties_changed_signal_register_property (G_OBJECT (self),
MM_MODEM_ENABLED,
MM_MODEM_DBUS_INTERFACE);
+ mm_properties_changed_signal_register_property (G_OBJECT (self),
+ MM_MODEM_UNLOCK_REQUIRED,
+ MM_MODEM_DBUS_INTERFACE);
}
static void
diff --git a/src/mm-modem-cdma.c b/src/mm-modem-cdma.c
index 1a4fe6a2..e80dc4e2 100644
--- a/src/mm-modem-cdma.c
+++ b/src/mm-modem-cdma.c
@@ -212,7 +212,7 @@ impl_modem_cdma_get_esn (MMModemCdma *self, DBusGMethodInvocation *context)
/* Make sure the caller is authorized to get the ESN */
if (!mm_modem_auth_request (MM_MODEM (self),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_INFO,
context,
esn_auth_cb,
NULL,
diff --git a/src/mm-modem-gsm-card.c b/src/mm-modem-gsm-card.c
index 9881871c..432a4a3f 100644
--- a/src/mm-modem-gsm-card.c
+++ b/src/mm-modem-gsm-card.c
@@ -225,7 +225,7 @@ impl_gsm_modem_get_imei (MMModemGsmCard *modem, DBusGMethodInvocation *context)
/* Make sure the caller is authorized to get the IMEI */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_INFO,
context,
imei_auth_cb,
NULL,
@@ -262,7 +262,7 @@ impl_gsm_modem_get_imsi (MMModemGsmCard *modem, DBusGMethodInvocation *context)
/* Make sure the caller is authorized to get the IMSI */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_INFO,
context,
imsi_auth_cb,
NULL,
@@ -343,7 +343,7 @@ impl_gsm_modem_send_puk (MMModemGsmCard *modem,
/* Make sure the caller is authorized to send the PUK */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
context,
send_puk_auth_cb,
info,
@@ -386,7 +386,7 @@ impl_gsm_modem_send_pin (MMModemGsmCard *modem,
/* Make sure the caller is authorized to unlock the modem */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
context,
send_pin_auth_cb,
info,
@@ -430,7 +430,7 @@ impl_gsm_modem_enable_pin (MMModemGsmCard *modem,
/* Make sure the caller is authorized to enable a PIN */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
context,
enable_pin_auth_cb,
info,
@@ -474,7 +474,7 @@ impl_gsm_modem_change_pin (MMModemGsmCard *modem,
/* Make sure the caller is authorized to change the PIN */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
context,
change_pin_auth_cb,
info,
@@ -503,7 +503,7 @@ mm_modem_gsm_card_init (gpointer g_iface)
"Supported Modes",
"Supported frequency bands of the card",
MM_MODEM_GSM_BAND_UNKNOWN,
- MM_MODEM_GSM_BAND_LAST,
+ G_MAXUINT32,
MM_MODEM_GSM_BAND_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
@@ -513,7 +513,7 @@ mm_modem_gsm_card_init (gpointer g_iface)
"Supported Modes",
"Supported modes of the card (ex 2G preferred, 3G preferred, 2G only, etc",
MM_MODEM_GSM_MODE_UNKNOWN,
- MM_MODEM_GSM_MODE_LAST,
+ G_MAXUINT32,
MM_MODEM_GSM_MODE_UNKNOWN,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
}
diff --git a/src/mm-modem-gsm-network.c b/src/mm-modem-gsm-network.c
index bf52b942..7f7b655b 100644
--- a/src/mm-modem-gsm-network.c
+++ b/src/mm-modem-gsm-network.c
@@ -11,6 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 Novell, Inc.
+ * Copyright (C) 2010 Red Hat, Inc.
*/
#include <string.h>
@@ -42,8 +43,12 @@ static void impl_gsm_modem_set_band (MMModemGsmNetwork *modem,
static void impl_gsm_modem_get_band (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context);
+static void impl_gsm_modem_set_allowed_mode (MMModemGsmNetwork *modem,
+ MMModemGsmAllowedMode mode,
+ DBusGMethodInvocation *context);
+
static void impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+ MMModemDeprecatedMode old_mode,
DBusGMethodInvocation *context);
static void impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem,
@@ -68,6 +73,47 @@ static guint signals[LAST_SIGNAL] = { 0 };
/*****************************************************************************/
+MMModemGsmAllowedMode
+mm_modem_gsm_network_old_mode_to_allowed (MMModemDeprecatedMode old_mode)
+{
+ /* Translate deprecated mode into new mode */
+ switch (old_mode) {
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED:
+ return MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED:
+ return MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY:
+ return MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY:
+ return MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY;
+ case MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY:
+ default:
+ return MM_MODEM_GSM_ALLOWED_MODE_ANY;
+ }
+}
+
+MMModemDeprecatedMode
+mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act)
+{
+ /* Translate new mode into old deprecated mode */
+ if (act & MM_MODEM_GSM_ACCESS_TECH_GPRS)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_EDGE)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_UMTS)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSDPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSUPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA;
+ else if (act & MM_MODEM_GSM_ACCESS_TECH_HSPA)
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA;
+
+ return MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY;
+}
+
+/*****************************************************************************/
+
static void
async_call_done (MMModem *modem, GError *error, gpointer user_data)
{
@@ -305,35 +351,21 @@ mm_modem_gsm_network_get_band (MMModemGsmNetwork *self,
}
void
-mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data)
+mm_modem_gsm_network_set_allowed_mode (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
g_return_if_fail (callback != NULL);
- if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode)
- MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_network_mode (self, mode, callback, user_data);
+ if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_allowed_mode)
+ MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->set_allowed_mode (self, mode, callback, user_data);
else
async_call_not_supported (self, callback, user_data);
}
void
-mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data)
-{
- g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
- g_return_if_fail (callback != NULL);
-
- if (MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode)
- MM_MODEM_GSM_NETWORK_GET_INTERFACE (self)->get_network_mode (self, callback, user_data);
- else
- uint_call_not_supported (self, callback, user_data);
-}
-
-void
mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data)
@@ -369,15 +401,6 @@ mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self,
oper_name ? oper_name : "");
}
-void
-mm_modem_gsm_network_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode)
-{
- g_return_if_fail (MM_IS_MODEM_GSM_NETWORK (self));
-
- g_signal_emit (self, signals[NETWORK_MODE], 0, mode);
-}
-
/*****************************************************************************/
static void
@@ -422,7 +445,7 @@ impl_gsm_modem_scan (MMModemGsmNetwork *modem,
/* Make sure the caller is authorized to request a scan */
if (!mm_modem_auth_request (MM_MODEM (modem),
- MM_AUTHORIZATION_DEVICE,
+ MM_AUTHORIZATION_DEVICE_CONTROL,
context,
scan_auth_cb,
NULL,
@@ -493,7 +516,28 @@ impl_gsm_modem_get_band (MMModemGsmNetwork *modem,
static void
impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
- MMModemGsmMode mode,
+ MMModemDeprecatedMode old_mode,
+ DBusGMethodInvocation *context)
+{
+ if (!check_for_single_value (old_mode)) {
+ GError *error;
+
+ error = g_error_new_literal (MM_MODEM_ERROR, MM_MODEM_ERROR_OPERATION_NOT_SUPPORTED,
+ "Invalid arguments (more than one value given)");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ mm_modem_gsm_network_set_allowed_mode (modem,
+ mm_modem_gsm_network_old_mode_to_allowed (old_mode),
+ async_call_done,
+ context);
+}
+
+static void
+impl_gsm_modem_set_allowed_mode (MMModemGsmNetwork *modem,
+ MMModemGsmAllowedMode mode,
DBusGMethodInvocation *context)
{
if (!check_for_single_value (mode)) {
@@ -506,14 +550,20 @@ impl_gsm_modem_set_network_mode (MMModemGsmNetwork *modem,
return;
}
- mm_modem_gsm_network_set_mode (modem, mode, async_call_done, context);
+ mm_modem_gsm_network_set_allowed_mode (modem, mode, async_call_done, context);
}
static void
impl_gsm_modem_get_network_mode (MMModemGsmNetwork *modem,
DBusGMethodInvocation *context)
{
- mm_modem_gsm_network_get_mode (modem, uint_call_done, context);
+ MMModemGsmAccessTech act = MM_MODEM_GSM_ACCESS_TECH_UNKNOWN;
+
+ /* DEPRECATED; it's now a property so it's quite easy to handle */
+ g_object_get (G_OBJECT (modem),
+ MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY, &act,
+ NULL);
+ dbus_g_method_return (context, mm_modem_gsm_network_act_to_old_mode (act));
}
static void
@@ -534,6 +584,28 @@ mm_modem_gsm_network_init (gpointer g_iface)
if (initialized)
return;
+ /* Properties */
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_GSM_NETWORK_ALLOWED_MODE,
+ "Allowed Mode",
+ "Allowed network access mode",
+ MM_MODEM_GSM_ALLOWED_MODE_ANY,
+ MM_MODEM_GSM_ALLOWED_MODE_LAST,
+ MM_MODEM_GSM_ALLOWED_MODE_ANY,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_uint (MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY,
+ "Access Technology",
+ "Current access technology in use when connected to "
+ "a mobile network.",
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN,
+ MM_MODEM_GSM_ACCESS_TECH_LAST,
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
/* Signals */
signals[SIGNAL_QUALITY] =
g_signal_new ("signal-quality",
@@ -559,8 +631,7 @@ mm_modem_gsm_network_init (gpointer g_iface)
g_signal_new ("network-mode",
iface_type,
G_SIGNAL_RUN_FIRST,
- G_STRUCT_OFFSET (MMModemGsmNetwork, network_mode),
- NULL, NULL,
+ 0, NULL, NULL,
g_cclosure_marshal_VOID__UINT,
G_TYPE_NONE, 1,
G_TYPE_UINT);
diff --git a/src/mm-modem-gsm-network.h b/src/mm-modem-gsm-network.h
index 493baec7..a5157893 100644
--- a/src/mm-modem-gsm-network.h
+++ b/src/mm-modem-gsm-network.h
@@ -11,7 +11,7 @@
* GNU General Public License for more details:
*
* Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2009 Red Hat, Inc.
+ * Copyright (C) 2009 - 2010 Red Hat, Inc.
*/
#ifndef MM_MODEM_GSM_NETWORK_H
@@ -20,11 +20,23 @@
#include <mm-modem.h>
#include <mm-modem-gsm.h>
+#define MM_MODEM_GSM_NETWORK_DBUS_INTERFACE "org.freedesktop.ModemManager.Modem.Gsm.Network"
+
#define MM_TYPE_MODEM_GSM_NETWORK (mm_modem_gsm_network_get_type ())
#define MM_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork))
#define MM_IS_MODEM_GSM_NETWORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_MODEM_GSM_NETWORK))
#define MM_MODEM_GSM_NETWORK_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), MM_TYPE_MODEM_GSM_NETWORK, MMModemGsmNetwork))
+#define MM_MODEM_GSM_NETWORK_ALLOWED_MODE "allowed-mode"
+#define MM_MODEM_GSM_NETWORK_ACCESS_TECHNOLOGY "access-technology"
+
+typedef enum {
+ MM_MODEM_GSM_NETWORK_PROP_FIRST = 0x1200,
+
+ MM_MODEM_GSM_NETWORK_PROP_ALLOWED_MODE = MM_MODEM_GSM_NETWORK_PROP_FIRST,
+ MM_MODEM_GSM_NETWORK_PROP_ACCESS_TECHNOLOGY,
+} MMModemGsmNetworkProp;
+
typedef enum {
MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE = 0,
MM_MODEM_GSM_NETWORK_REG_STATUS_HOME = 1,
@@ -80,15 +92,11 @@ struct _MMModemGsmNetwork {
MMModemUIntFn callback,
gpointer user_data);
- void (*set_network_mode) (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
+ void (*set_allowed_mode) (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
MMModemFn callback,
gpointer user_data);
- void (*get_network_mode) (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data);
-
void (*get_registration_info) (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data);
@@ -101,9 +109,6 @@ struct _MMModemGsmNetwork {
MMModemGsmNetworkRegStatus status,
const char *open_code,
const char *oper_name);
-
- void (*network_mode) (MMModemGsmNetwork *self,
- MMModemGsmMode mode);
};
GType mm_modem_gsm_network_get_type (void);
@@ -135,15 +140,6 @@ void mm_modem_gsm_network_get_band (MMModemGsmNetwork *self,
MMModemUIntFn callback,
gpointer user_data);
-void mm_modem_gsm_network_set_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode,
- MMModemFn callback,
- gpointer user_data);
-
-void mm_modem_gsm_network_get_mode (MMModemGsmNetwork *self,
- MMModemUIntFn callback,
- gpointer user_data);
-
void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegInfoFn callback,
gpointer user_data);
@@ -153,12 +149,19 @@ void mm_modem_gsm_network_get_registration_info (MMModemGsmNetwork *self,
void mm_modem_gsm_network_signal_quality (MMModemGsmNetwork *self,
guint32 quality);
+void mm_modem_gsm_network_set_allowed_mode (MMModemGsmNetwork *self,
+ MMModemGsmAllowedMode mode,
+ MMModemFn callback,
+ gpointer user_data);
+
void mm_modem_gsm_network_registration_info (MMModemGsmNetwork *self,
MMModemGsmNetworkRegStatus status,
const char *oper_code,
const char *oper_name);
-void mm_modem_gsm_network_mode (MMModemGsmNetwork *self,
- MMModemGsmMode mode);
+/* Private */
+MMModemDeprecatedMode mm_modem_gsm_network_act_to_old_mode (MMModemGsmAccessTech act);
+
+MMModemGsmAllowedMode mm_modem_gsm_network_old_mode_to_allowed (MMModemDeprecatedMode old_mode);
#endif /* MM_MODEM_GSM_NETWORK_H */
diff --git a/src/mm-modem-gsm.h b/src/mm-modem-gsm.h
index 852ff853..5f20b20d 100644
--- a/src/mm-modem-gsm.h
+++ b/src/mm-modem-gsm.h
@@ -30,11 +30,35 @@ typedef enum {
MM_MODEM_GSM_MODE_3G_ONLY = 0x00000100,
MM_MODEM_GSM_MODE_HSUPA = 0x00000200,
MM_MODEM_GSM_MODE_HSPA = 0x00000400,
-
- MM_MODEM_GSM_MODE_LAST = MM_MODEM_GSM_MODE_HSPA
+ MM_MODEM_GSM_MODE_GSM = 0x00000800,
+ MM_MODEM_GSM_MODE_GSM_COMPACT = 0x00001000,
} MMModemGsmMode;
typedef enum {
+ MM_MODEM_GSM_ALLOWED_MODE_ANY = 0,
+ MM_MODEM_GSM_ALLOWED_MODE_2G_PREFERRED = 1,
+ MM_MODEM_GSM_ALLOWED_MODE_3G_PREFERRED = 2,
+ MM_MODEM_GSM_ALLOWED_MODE_2G_ONLY = 3,
+ MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY = 4,
+
+ MM_MODEM_GSM_ALLOWED_MODE_LAST = MM_MODEM_GSM_ALLOWED_MODE_3G_ONLY
+} MMModemGsmAllowedMode;
+
+typedef enum {
+ MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0,
+ MM_MODEM_GSM_ACCESS_TECH_GSM = 1,
+ MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 2,
+ MM_MODEM_GSM_ACCESS_TECH_GPRS = 3,
+ MM_MODEM_GSM_ACCESS_TECH_EDGE = 4, /* GSM w/EGPRS */
+ MM_MODEM_GSM_ACCESS_TECH_UMTS = 5, /* UTRAN */
+ MM_MODEM_GSM_ACCESS_TECH_HSDPA = 6, /* UTRAN w/HSDPA */
+ MM_MODEM_GSM_ACCESS_TECH_HSUPA = 7, /* UTRAN w/HSUPA */
+ MM_MODEM_GSM_ACCESS_TECH_HSPA = 8, /* UTRAN w/HSDPA and HSUPA */
+
+ MM_MODEM_GSM_ACCESS_TECH_LAST = MM_MODEM_GSM_ACCESS_TECH_HSPA
+} MMModemGsmAccessTech;
+
+typedef enum {
MM_MODEM_GSM_BAND_UNKNOWN = 0x00000000,
MM_MODEM_GSM_BAND_ANY = 0x00000001,
MM_MODEM_GSM_BAND_EGSM = 0x00000002, /* 900 MHz */
@@ -53,5 +77,18 @@ typedef enum {
} MMModemGsmBand;
-#endif /* MM_MODEM_GSM_H */
+typedef enum {
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_ANY = 0,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_GPRS,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_EDGE,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_UMTS,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSDPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_PREFERRED,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_PREFERRED,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_2G_ONLY,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_3G_ONLY,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSUPA,
+ MM_MODEM_GSM_NETWORK_DEPRECATED_MODE_HSPA
+} MMModemDeprecatedMode;
+#endif /* MM_MODEM_GSM_H */
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
index 1741b5f0..56834d6d 100644
--- a/src/mm-modem-helpers.c
+++ b/src/mm-modem-helpers.c
@@ -17,6 +17,7 @@
#include <glib.h>
#include <string.h>
#include <ctype.h>
+#include <stdlib.h>
#include "mm-errors.h"
#include "mm-modem-helpers.h"
@@ -201,3 +202,230 @@ mm_gsm_destroy_scan_data (gpointer data)
g_ptr_array_free (results, TRUE);
}
+/*************************************************************************/
+
+/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
+#define CREG1 "\\+CG?REG:\\s*(\\d{1})"
+
+/* +CREG: <n>,<stat> (GSM 07.07 CREG=1 solicited) */
+#define CREG2 "\\+CG?REG:\\s*(\\d{1}),\\s*(\\d{1})"
+
+/* +CREG: <stat>,<lac>,<ci> (GSM 07.07 CREG=2 unsolicited) */
+#define CREG3 "\\+CG?REG:\\s*(\\d{1}),\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+
+/* +CREG: <n>,<stat>,<lac>,<ci> (GSM 07.07 solicited and some CREG=2 unsolicited) */
+#define CREG4 "\\+CG?REG:\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)"
+
+/* +CREG: <stat>,<lac>,<ci>,<AcT> (ETSI 27.007 CREG=2 unsolicited) */
+#define CREG5 "\\+CG?REG:\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
+
+/* +CREG: <n>,<stat>,<lac>,<ci>,<AcT> (ETSI 27.007 solicited and some CREG=2 unsolicited) */
+#define CREG6 "\\+CG?REG:\\s*(\\d{1}),\\s*(\\d{1})\\s*,\\s*([^,\\s]*)\\s*,\\s*([^,\\s]*)\\s*,\\s*(\\d{1,2})"
+
+GPtrArray *
+mm_gsm_creg_regex_get (gboolean solicited)
+{
+ GPtrArray *array = g_ptr_array_sized_new (6);
+ GRegex *regex;
+
+ /* #1 */
+ if (solicited)
+ regex = g_regex_new (CREG1 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG1 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #2 */
+ if (solicited)
+ regex = g_regex_new (CREG2 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG2 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #3 */
+ if (solicited)
+ regex = g_regex_new (CREG3 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG3 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #4 */
+ if (solicited)
+ regex = g_regex_new (CREG4 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG4 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #5 */
+ if (solicited)
+ regex = g_regex_new (CREG5 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG5 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ /* #6 */
+ if (solicited)
+ regex = g_regex_new (CREG6 "$", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ else
+ regex = g_regex_new ("\\r\\n" CREG6 "\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ g_assert (regex);
+ g_ptr_array_add (array, regex);
+
+ return array;
+}
+
+void
+mm_gsm_creg_regex_destroy (GPtrArray *array)
+{
+ g_ptr_array_foreach (array, (GFunc) g_regex_unref, NULL);
+ g_ptr_array_free (array, TRUE);
+}
+
+/*************************************************************************/
+
+static gulong
+parse_uint (char *str, int base, glong nmin, glong nmax, gboolean *valid)
+{
+ gulong ret = 0;
+ char *endquote;
+
+ *valid = FALSE;
+ if (!str)
+ return 0;
+
+ /* Strip quotes */
+ if (str[0] == '"')
+ str++;
+ endquote = strchr (str, '"');
+ if (endquote)
+ *endquote = '\0';
+
+ if (strlen (str)) {
+ ret = strtol (str, NULL, base);
+ if ((nmin == nmax) || (ret >= nmin && ret <= nmax))
+ *valid = TRUE;
+ }
+ return *valid ? (guint) ret : 0;
+}
+
+gboolean
+mm_gsm_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_greg,
+ GError **error)
+{
+ gboolean success = FALSE, foo;
+ gint n_matches, act = -1;
+ gulong stat = 0, lac = 0, ci = 0;
+ guint istat = 0, ilac = 0, ici = 0, iact = 0;
+ char *str;
+ const char *orig_str;
+
+ g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (out_reg_state != NULL, FALSE);
+ g_return_val_if_fail (out_lac != NULL, FALSE);
+ g_return_val_if_fail (out_ci != NULL, FALSE);
+ g_return_val_if_fail (out_act != NULL, FALSE);
+ g_return_val_if_fail (out_greg != NULL, FALSE);
+
+ /* Normally the number of matches could be used to determine what each
+ * item is, but we have overlap in one case.
+ */
+ n_matches = g_match_info_get_match_count (info);
+ if (n_matches == 2) {
+ /* CREG=1: +CREG: <stat> */
+ istat = 1;
+ } else if (n_matches == 3) {
+ /* Solicited response: +CREG: <n>,<stat> */
+ istat = 2;
+ } else if (n_matches == 4) {
+ /* CREG=2 (GSM 07.07): +CREG: <stat>,<lac>,<ci> */
+ istat = 1;
+ ilac = 2;
+ ici = 3;
+ } else if (n_matches == 5) {
+ /* CREG=2 (ETSI 27.007): +CREG: <stat>,<lac>,<ci>,<AcT>
+ * CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>
+ */
+
+ /* To distinguish, check length of the second match item. If it's
+ * more than one digit or has quotes in it, then we have the first format.
+ */
+ str = g_match_info_fetch (info, 2);
+ if (str && (strchr (str, '"') || strlen (str) > 1)) {
+ g_free (str);
+ istat = 1;
+ ilac = 2;
+ ici = 3;
+ iact = 4;
+ } else {
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ }
+ } else if (n_matches == 6) {
+ /* CREG=2 (non-standard): +CREG: <n>,<stat>,<lac>,<ci>,<AcT> */
+ istat = 2;
+ ilac = 3;
+ ici = 4;
+ iact = 5;
+ }
+
+ /* Status */
+ str = g_match_info_fetch (info, istat);
+ stat = parse_uint (str, 10, 0, 5, &success);
+ g_free (str);
+ if (!success) {
+ g_set_error_literal (error,
+ MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL,
+ "Could not parse the registration status response");
+ return FALSE;
+ }
+
+ /* Location Area Code */
+ if (ilac) {
+ /* FIXME: some phones apparently swap the LAC bytes (LG, SonyEricsson,
+ * Sagem). Need to handle that.
+ */
+ str = g_match_info_fetch (info, ilac);
+ lac = parse_uint (str, 16, 1, 0xFFFF, &foo);
+ g_free (str);
+ }
+
+ /* Cell ID */
+ if (ici) {
+ str = g_match_info_fetch (info, ici);
+ ci = parse_uint (str, 16, 1, 0x0FFFFFFE, &foo);
+ g_free (str);
+ }
+
+ /* Access Technology */
+ if (iact) {
+ str = g_match_info_fetch (info, iact);
+ act = (gint) parse_uint (str, 10, 0, 7, &foo);
+ g_free (str);
+ if (!foo)
+ act = -1;
+ }
+
+ orig_str = g_match_info_get_string (info);
+ *out_greg = !!strstr (orig_str, "+CGREG");
+
+ *out_reg_state = (guint32) stat;
+ if (stat != 4) {
+ /* Don't fill in lac/ci/act if the device's state is unknown */
+ *out_lac = lac;
+ *out_ci = ci;
+ *out_act = act;
+ }
+ return TRUE;
+}
+
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
index ddc9cbce..57956766 100644
--- a/src/mm-modem-helpers.h
+++ b/src/mm-modem-helpers.h
@@ -27,5 +27,17 @@ GPtrArray *mm_gsm_parse_scan_response (const char *reply, GError **error);
void mm_gsm_destroy_scan_data (gpointer data);
+GPtrArray *mm_gsm_creg_regex_get (gboolean solicited);
+
+void mm_gsm_creg_regex_destroy (GPtrArray *array);
+
+gboolean mm_gsm_parse_creg_response (GMatchInfo *info,
+ guint32 *out_reg_state,
+ gulong *out_lac,
+ gulong *out_ci,
+ gint *out_act,
+ gboolean *out_greg,
+ GError **error);
+
#endif /* MM_MODEM_HELPERS_H */
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
index f5b4b6a8..980c4ae7 100644
--- a/src/tests/test-modem-helpers.c
+++ b/src/tests/test-modem-helpers.c
@@ -18,6 +18,11 @@
#include "mm-modem-helpers.h"
+typedef struct {
+ GPtrArray *solicited_creg;
+ GPtrArray *unsolicited_creg;
+} TestData;
+
#define MM_SCAN_TAG_STATUS "status"
#define MM_SCAN_TAG_OPER_LONG "operator-long"
#define MM_SCAN_TAG_OPER_SHORT "operator-short"
@@ -35,10 +40,10 @@ typedef struct {
#define ARRAY_LEN(i) (sizeof (i) / sizeof (i[0]))
static void
-test_results (const char *desc,
- const char *reply,
- OperEntry *expected_results,
- guint32 expected_results_len)
+test_cops_results (const char *desc,
+ const char *reply,
+ OperEntry *expected_results,
+ guint32 expected_results_len)
{
guint i;
GError *error = NULL;
@@ -101,7 +106,7 @@ test_cops_response_tm506 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("TM-506", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -113,7 +118,7 @@ test_cops_response_gt3gplus (void *f, gpointer d)
{ "1", "Cingular", "Cingular", "310410", "0" },
};
- test_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("GlobeTrotter 3G+ (nozomi)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -126,7 +131,7 @@ test_cops_response_ac881 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AirCard 881", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -139,7 +144,7 @@ test_cops_response_gtmax36 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GlobeTrotter MAX 3.6", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -152,7 +157,7 @@ test_cops_response_ac860 (void *f, gpointer d)
{ "1", "Cingular", "Cinglr", "310410", "0" },
};
- test_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AirCard 860", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -165,7 +170,7 @@ test_cops_response_gtm378 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GTM378", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -177,7 +182,7 @@ test_cops_response_motoc (void *f, gpointer d)
{ "0", "Cingular Wireless", NULL, "310410", NULL },
};
- test_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("BUSlink SCWi275u (Motorola C-series)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -189,7 +194,7 @@ test_cops_response_mf627a (void *f, gpointer d)
{ "3", "Voicestream Wireless Corporation", "VSTREAM", "31026", "0" },
};
- test_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF627 (A)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -201,7 +206,7 @@ test_cops_response_mf627b (void *f, gpointer d)
{ "3", NULL, NULL, "31026", "0" },
};
- test_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF627 (B)", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -213,7 +218,7 @@ test_cops_response_e160g (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E160G", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -226,7 +231,7 @@ test_cops_response_mercury (void *f, gpointer d)
{ "1", "T-Mobile", "TMO", "31026", "0" },
};
- test_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra AT&T USBConnect Mercury", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -239,7 +244,7 @@ test_cops_response_quicksilver (void *f, gpointer d)
{ "1", "AT&T", NULL, "310260", "0" },
};
- test_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option AT&T Quicksilver", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -251,7 +256,7 @@ test_cops_response_icon225 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option iCON 225", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -265,7 +270,7 @@ test_cops_response_icon452 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option iCON 452", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -278,7 +283,7 @@ test_cops_response_f3507g (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "2" }
};
- test_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Ericsson F3507g", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -291,7 +296,7 @@ test_cops_response_f3607gw (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Ericsson F3607gw", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -304,7 +309,7 @@ test_cops_response_mc8775 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" }
};
- test_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sierra MC8775", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -317,7 +322,7 @@ test_cops_response_n80 (void *f, gpointer d)
{ "1", "Cingular", NULL, "31041", NULL },
};
- test_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Nokia N80", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -329,7 +334,7 @@ test_cops_response_e1550 (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E1550", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -341,7 +346,7 @@ test_cops_response_mf622 (void *f, gpointer d)
{ "1", NULL, NULL, "310410", "0" },
};
- test_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("ZTE MF622", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -354,7 +359,7 @@ test_cops_response_e226 (void *f, gpointer d)
{ "1", NULL, NULL, "310410", "0" },
};
- test_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Huawei E226", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -367,7 +372,7 @@ test_cops_response_xu870 (void *f, gpointer d)
{ "1", "T-Mobile", "TMO", "31026", "0" },
};
- test_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Novatel XU870", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -380,7 +385,7 @@ test_cops_response_gtultraexpress (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Option GlobeTrotter Ultra Express", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -392,7 +397,7 @@ test_cops_response_n2720 (void *f, gpointer d)
{ "1", "AT&T", NULL, "310410", "0" },
};
- test_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Nokia 2720", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -405,7 +410,7 @@ test_cops_response_gobi (void *f, gpointer d)
{ "1", "AT&T", "AT&T", "310410", "0" },
};
- test_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Qualcomm Gobi", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -426,7 +431,7 @@ test_cops_response_sek600i (void *f, gpointer d)
{ "3", NULL, NULL, "26207", NULL },
};
- test_results ("Sony-Ericsson K600i", reply, &expected[0], ARRAY_LEN (expected));
+ test_cops_results ("Sony-Ericsson K600i", reply, &expected[0], ARRAY_LEN (expected));
}
static void
@@ -453,6 +458,238 @@ test_cops_response_umts_invalid (void *f, gpointer d)
g_assert (error == NULL);
}
+typedef struct {
+ guint32 state;
+ gulong lac;
+ gulong ci;
+ gint act;
+
+ guint regex_num;
+ gboolean cgreg;
+} CregResult;
+
+static void
+test_creg_match (const char *test,
+ gboolean solicited,
+ const char *reply,
+ TestData *data,
+ const CregResult *result)
+{
+ int i;
+ GMatchInfo *info = NULL;
+ guint32 state = 0;
+ gulong lac = 0, ci = 0;
+ gint access_tech = -1;
+ GError *error = NULL;
+ gboolean success, cgreg = FALSE;
+ guint regex_num = 0;
+ GPtrArray *array;
+
+ g_assert (reply);
+ g_assert (test);
+ g_assert (data);
+ g_assert (result);
+
+ g_print ("\nTesting %s +CREG %s response...\n",
+ test,
+ solicited ? "solicited" : "unsolicited");
+
+ array = solicited ? data->solicited_creg : data->unsolicited_creg;
+ for (i = 0; i < array->len; i++) {
+ GRegex *r = g_ptr_array_index (array, i);
+
+ if (g_regex_match (r, reply, 0, &info)) {
+ regex_num = i + 1;
+ break;
+ }
+ g_match_info_free (info);
+ info = NULL;
+ }
+
+ g_assert (info != NULL);
+ g_assert (regex_num == result->regex_num);
+
+ success = mm_gsm_parse_creg_response (info, &state, &lac, &ci, &access_tech, &cgreg, &error);
+ g_assert (success);
+ g_assert (error == NULL);
+ g_assert (state == result->state);
+ g_assert (lac == result->lac);
+ g_assert (ci == result->ci);
+ g_assert (access_tech == result->act);
+ g_assert (cgreg == result->cgreg);
+}
+
+static void
+test_creg1_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 1,3";
+ const CregResult result = { 3, 0, 0, -1 , 2, FALSE};
+
+ test_creg_match ("CREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_creg1_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 3\r\n";
+ const CregResult result = { 3, 0, 0, -1 , 1, FALSE};
+
+ test_creg_match ("CREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_mercury_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 0,1,84CD,00D30173";
+ const CregResult result = { 1, 0x84cd, 0xd30173, -1 , 4, FALSE};
+
+ test_creg_match ("Sierra Mercury CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_mercury_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 1,84CD,00D30156\r\n";
+ const CregResult result = { 1, 0x84cd, 0xd30156, -1 , 3, FALSE};
+
+ test_creg_match ("Sierra Mercury CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_sek850i_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,\"CE00\",\"01CEAD8F\"";
+ const CregResult result = { 1, 0xce00, 0x01cead8f, -1 , 4, FALSE};
+
+ test_creg_match ("Sony Ericsson K850i CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_sek850i_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 1,\"CE00\",\"00005449\"\r\n";
+ const CregResult result = { 1, 0xce00, 0x5449, -1 , 3, FALSE};
+
+ test_creg_match ("Sony Ericsson K850i CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_solicited_unregistered (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,0,00,0";
+ const CregResult result = { 0, 0, 0, -1 , 4, FALSE};
+
+ test_creg_match ("Huawei E160G unregistered CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,8BE3,2BAF";
+ const CregResult result = { 1, 0x8be3, 0x2baf, -1 , 4, FALSE};
+
+ test_creg_match ("Huawei E160G CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_e160g_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,8BE3,2BAF\r\n";
+ const CregResult result = { 2, 0x8be3, 0x2baf, -1 , 3, FALSE};
+
+ test_creg_match ("Huawei E160G CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_creg2_tm506_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CREG: 2,1,\"8BE3\",\"00002BAF\"";
+ const CregResult result = { 1, 0x8BE3, 0x2BAF, -1 , 4, FALSE};
+
+ /* Test leading zeros in the CI */
+ test_creg_match ("Sony Ericsson TM-506 CREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_creg2_xu870_unsolicited_unregistered (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CREG: 2,,\r\n";
+ const CregResult result = { 2, 0, 0, -1 , 3, FALSE};
+
+ test_creg_match ("Novatel XU870 unregistered CREG=2", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg1_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CGREG: 1,3";
+ const CregResult result = { 3, 0, 0, -1 , 2, TRUE};
+
+ test_creg_match ("CGREG=1", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg1_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 3\r\n";
+ const CregResult result = { 3, 0, 0, -1 , 1, TRUE};
+
+ test_creg_match ("CGREG=1", FALSE, reply, data, &result);
+}
+
+static void
+test_cgreg2_f3607gw_solicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "+CGREG: 2,1,\"8BE3\",\"00002B5D\",3";
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, 3 , 6, TRUE};
+
+ test_creg_match ("Ericsson F3607gw CGREG=2", TRUE, reply, data, &result);
+}
+
+static void
+test_cgreg2_f3607gw_unsolicited (void *f, gpointer d)
+{
+ TestData *data = (TestData *) d;
+ const char *reply = "\r\n+CGREG: 1,\"8BE3\",\"00002B5D\",3\r\n";
+ const CregResult result = { 1, 0x8BE3, 0x2B5D, 3 , 5, TRUE};
+
+ test_creg_match ("Ericsson F3607gw CGREG=2", FALSE, reply, data, &result);
+}
+
+
+static TestData *
+test_data_new (void)
+{
+ TestData *data;
+
+ data = g_malloc0 (sizeof (TestData));
+ data->solicited_creg = mm_gsm_creg_regex_get (TRUE);
+ data->unsolicited_creg = mm_gsm_creg_regex_get (FALSE);
+ return data;
+}
+
+static void
+test_data_free (TestData *data)
+{
+ mm_gsm_creg_regex_destroy (data->solicited_creg);
+ mm_gsm_creg_regex_destroy (data->unsolicited_creg);
+ g_free (data);
+}
+
typedef void (*TCFunc)(void);
@@ -461,10 +698,13 @@ typedef void (*TCFunc)(void);
int main (int argc, char **argv)
{
GTestSuite *suite;
+ TestData *data;
+ gint result;
g_test_init (&argc, &argv, NULL);
suite = g_test_get_root ();
+ data = test_data_new ();
g_test_suite_add (suite, TESTCASE (test_cops_response_tm506, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_gt3gplus, NULL));
@@ -496,6 +736,27 @@ int main (int argc, char **argv)
g_test_suite_add (suite, TESTCASE (test_cops_response_gsm_invalid, NULL));
g_test_suite_add (suite, TESTCASE (test_cops_response_umts_invalid, NULL));
- return g_test_run ();
+ g_test_suite_add (suite, TESTCASE (test_creg1_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg1_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_mercury_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_mercury_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_sek850i_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited_unregistered, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_e160g_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_tm506_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_creg2_xu870_unsolicited_unregistered, data));
+
+ g_test_suite_add (suite, TESTCASE (test_cgreg1_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg1_unsolicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_solicited, data));
+ g_test_suite_add (suite, TESTCASE (test_cgreg2_f3607gw_unsolicited, data));
+
+ result = g_test_run ();
+
+ test_data_free (data);
+
+ return result;
}