diff options
author | Dan Williams <dcbw@redhat.com> | 2010-03-08 20:01:22 -0800 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-03-08 20:01:22 -0800 |
commit | f3721a5674ef7899d9a6ef0f4c5b356720bb9833 (patch) | |
tree | 51c469c5928b02414389e05302e8f89d9e1ef0e0 /src | |
parent | f6c514897e40e768b180963f2782ed60527ffaa6 (diff) | |
parent | 9e231c3d4b463c32e17c7d2b1c50cea4e19d03ac (diff) |
Merge remote branch 'origin/master' into qcdm
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-auth-provider.h | 7 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 880 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 61 | ||||
-rw-r--r-- | src/mm-modem-base.c | 3 | ||||
-rw-r--r-- | src/mm-modem-cdma.c | 2 | ||||
-rw-r--r-- | src/mm-modem-gsm-card.c | 16 | ||||
-rw-r--r-- | src/mm-modem-gsm-network.c | 143 | ||||
-rw-r--r-- | src/mm-modem-gsm-network.h | 45 | ||||
-rw-r--r-- | src/mm-modem-gsm.h | 43 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 228 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 12 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 323 |
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; } |