diff options
author | Dan Williams <dcbw@redhat.com> | 2010-06-07 22:21:01 -0700 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2010-06-07 22:21:01 -0700 |
commit | 72a1a6caf67a15655ade8dc5eed4f94f86107171 (patch) | |
tree | 52ccca1952ba5107f713eaa76e093ba1f81f2791 | |
parent | f4bfd9410680f268bc739f46020a0adb5c41c8e3 (diff) |
gsm: use PS registration status if CS isn't available
Some devices (Blackberries) always respond to AT+CREG with ERROR,
but will respond to AT+CGREG normally. Ugh. Handle that by
using the PS registration status from AT+CGREG if we don't have
a valid CS registration status at all.
-rw-r--r-- | src/mm-generic-gsm.c | 160 | ||||
-rw-r--r-- | src/mm-generic-gsm.h | 6 |
2 files changed, 120 insertions, 46 deletions
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 8c155e2b..7cdc7f14 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -79,7 +79,8 @@ typedef struct { gulong cell_id[2]; MMModemGsmAccessTech act; - MMModemGsmNetworkRegStatus reg_status; + /* Index 0 for CREG, index 1 for CGREG */ + MMModemGsmNetworkRegStatus reg_status[2]; guint pending_reg_id; MMCallbackInfo *pending_reg_info; gboolean manual_reg; @@ -129,6 +130,7 @@ static void _internal_update_access_technology (MMGenericGsm *modem, static void reg_info_updated (MMGenericGsm *self, gboolean update_rs, + MMGenericGsmRegType rs_type, MMModemGsmNetworkRegStatus status, gboolean update_code, const char *oper_code, @@ -270,20 +272,49 @@ check_pin (MMGenericGsm *modem, /*****************************************************************************/ +static MMModemGsmNetworkRegStatus +gsm_reg_status (MMGenericGsm *self) +{ + MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + + /* Some devices (Blackberries for example) will respond to +CGREG, but + * return ERROR for +CREG, probably because their firmware is just stupid. + * So here we prefer the +CREG response, but if we never got a successful + * +CREG response, we'll take +CGREG instead. + */ + + if ( priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME + || priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) + return priv->reg_status[0]; + + if ( priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME + || priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) + return priv->reg_status[1]; + + if (priv->reg_status[0] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING) + return priv->reg_status[0]; + + if (priv->reg_status[1] == MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING) + return priv->reg_status[1]; + + if (priv->reg_status[0] != MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN) + return priv->reg_status[0]; + + return priv->reg_status[1]; +} + void mm_generic_gsm_update_enabled_state (MMGenericGsm *self, gboolean stay_connected, MMModemStateReason reason) { - MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); - /* While connected we don't want registration status changes to change * the modem's state away from CONNECTED. */ if (stay_connected && (mm_modem_get_state (MM_MODEM (self)) >= MM_MODEM_STATE_DISCONNECTING)) return; - switch (priv->reg_status) { + switch (gsm_reg_status (self)) { case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME: case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING: mm_modem_set_state (MM_MODEM (self), MM_MODEM_STATE_REGISTERED, reason); @@ -990,8 +1021,15 @@ disable_done (MMAtSerialPort *port, MM_MODEM_STATE_DISABLED, MM_MODEM_STATE_REASON_NONE); - /* Clear out registration info */ + /* Clear out circuit-switched registration info... */ reg_info_updated (self, + MM_GENERIC_GSM_REG_TYPE_CS, + TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN, + TRUE, NULL, + TRUE, NULL); + /* ...and packet-switched registration info */ + reg_info_updated (self, + MM_GENERIC_GSM_REG_TYPE_PS, TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN, TRUE, NULL, TRUE, NULL); @@ -1382,6 +1420,7 @@ change_pin (MMModemGsmCard *modem, static void reg_info_updated (MMGenericGsm *self, gboolean update_rs, + MMGenericGsmRegType rs_type, MMModemGsmNetworkRegStatus status, gboolean update_code, const char *oper_code, @@ -1389,13 +1428,17 @@ reg_info_updated (MMGenericGsm *self, const char *oper_name) { MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); + MMModemGsmNetworkRegStatus old_status; gboolean changed = FALSE; if (update_rs) { - if (status != priv->reg_status) { - priv->reg_status = status; + g_return_if_fail ( rs_type == MM_GENERIC_GSM_REG_TYPE_CS + || rs_type == MM_GENERIC_GSM_REG_TYPE_PS); + + old_status = gsm_reg_status (self); + priv->reg_status[rs_type - 1] = status; + if (gsm_reg_status (self) != old_status) changed = TRUE; - } } if (update_code) { @@ -1416,7 +1459,7 @@ reg_info_updated (MMGenericGsm *self, if (changed) { mm_modem_gsm_network_registration_info (MM_MODEM_GSM_NETWORK (self), - priv->reg_status, + gsm_reg_status (self), priv->oper_code, priv->oper_name); } @@ -1495,8 +1538,11 @@ read_operator_code_done (MMAtSerialPort *port, if (!error) { oper = parse_operator (response->str, MM_MODEM_CHARSET_UNKNOWN); - if (oper) - reg_info_updated (self, FALSE, 0, TRUE, oper, FALSE, NULL); + if (oper) { + reg_info_updated (self, FALSE, MM_GENERIC_GSM_REG_TYPE_UNKNOWN, 0, + TRUE, oper, + FALSE, NULL); + } } } @@ -1512,8 +1558,11 @@ read_operator_name_done (MMAtSerialPort *port, if (!error) { oper = parse_operator (response->str, priv->cur_charset); - if (oper) - reg_info_updated (self, FALSE, 0, FALSE, NULL, TRUE, oper); + if (oper) { + reg_info_updated (self, FALSE, MM_GENERIC_GSM_REG_TYPE_UNKNOWN, 0, + FALSE, NULL, + TRUE, oper); + } } } @@ -1570,6 +1619,7 @@ get_reg_act_done (MMModem *modem, void mm_generic_gsm_set_reg_status (MMGenericGsm *self, + MMGenericGsmRegType rs_type, MMModemGsmNetworkRegStatus status) { MMGenericGsmPrivate *priv; @@ -1577,13 +1627,18 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *self, g_return_if_fail (MM_IS_GENERIC_GSM (self)); + g_return_if_fail ( rs_type == MM_GENERIC_GSM_REG_TYPE_CS + || rs_type == MM_GENERIC_GSM_REG_TYPE_PS); + priv = MM_GENERIC_GSM_GET_PRIVATE (self); - if (priv->reg_status == status) + if (priv->reg_status[rs_type - 1] == status) return; - g_debug ("Registration state changed: %d", status); - priv->reg_status = status; + g_debug ("%s registration state changed: %d", + (rs_type == MM_GENERIC_GSM_REG_TYPE_CS) ? "CS" : "PS", + status); + priv->reg_status[rs_type - 1] = status; port = mm_generic_gsm_get_best_at_port (self, NULL); @@ -1611,14 +1666,17 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *self, MM_GENERIC_GSM_GET_CLASS (self)->get_access_technology (self, get_reg_act_done, NULL); } } else - reg_info_updated (self, FALSE, 0, TRUE, NULL, TRUE, NULL); + reg_info_updated (self, FALSE, rs_type, 0, TRUE, NULL, TRUE, NULL); mm_generic_gsm_update_enabled_state (self, TRUE, MM_MODEM_STATE_REASON_NONE); } /* Returns TRUE if the modem is "done", ie has registered or been denied */ static gboolean -reg_status_updated (MMGenericGsm *self, int new_value, GError **error) +reg_status_updated (MMGenericGsm *self, + MMGenericGsmRegType rs_type, + int new_value, + GError **error) { MMModemGsmNetworkRegStatus status; gboolean status_done = FALSE; @@ -1647,7 +1705,7 @@ reg_status_updated (MMGenericGsm *self, int new_value, GError **error) break; } - mm_generic_gsm_set_reg_status (self, status); + mm_generic_gsm_set_reg_status (self, rs_type, status); /* Registration has either completed successfully or completely failed */ switch (status) { @@ -1678,6 +1736,12 @@ reg_status_updated (MMGenericGsm *self, int new_value, GError **error) return status_done; } +static MMGenericGsmRegType +cgreg_to_reg_type (gboolean cgreg) +{ + return (cgreg ? MM_GENERIC_GSM_REG_TYPE_PS : MM_GENERIC_GSM_REG_TYPE_CS); +} + static void reg_state_changed (MMAtSerialPort *port, GMatchInfo *match_info, @@ -1700,21 +1764,14 @@ reg_state_changed (MMAtSerialPort *port, return; } - /* Don't update reg state on CGREG responses since for many devices it's - * unclear what that registration state that actually reflects. We'll - * take CGREG registration state into account later when we have a more - * consistent way of handling it. - */ - if (cgreg == FALSE) { - 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. - */ - if (priv->pending_reg_id) { - g_source_remove (priv->pending_reg_id); - priv->pending_reg_id = 0; - } + if (reg_status_updated (self, cgreg_to_reg_type (cgreg), 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. + */ + if (priv->pending_reg_id) { + g_source_remove (priv->pending_reg_id); + priv->pending_reg_id = 0; } } @@ -1795,9 +1852,9 @@ handle_reg_status_response (MMGenericGsm *self, if (act != -1) mm_generic_gsm_update_access_technology (self, etsi_act_to_mm_act (act)); - if ((cgreg == FALSE) && status >= 0) { + if (status >= 0) { /* Update cached registration status */ - reg_status_updated (self, status, NULL); + reg_status_updated (self, cgreg_to_reg_type (cgreg), status, NULL); } return TRUE; @@ -1815,6 +1872,7 @@ get_reg_status_done (MMAtSerialPort *port, MMGenericGsm *self = MM_GENERIC_GSM (info->modem); MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self); guint id; + MMModemGsmNetworkRegStatus status; /* This function should only get called during the connect sequence when * polling for registration state, since explicit registration requests @@ -1851,9 +1909,10 @@ get_reg_status_done (MMAtSerialPort *port, goto reg_done; } - 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) { + status = gsm_reg_status (self); + if ( status != MM_MODEM_GSM_NETWORK_REG_STATUS_HOME + && status != MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING + && 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. */ @@ -1910,9 +1969,14 @@ registration_timed_out (gpointer data) g_warn_if_fail (info == priv->pending_reg_info); - /* Clear out registration info */ + /* Clear out circuit-switched registration info... */ reg_info_updated (MM_GENERIC_GSM (info->modem), - TRUE, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE, + TRUE, MM_GENERIC_GSM_REG_TYPE_CS, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE, + TRUE, NULL, + TRUE, NULL); + /* ... and packet-switched registration info */ + reg_info_updated (MM_GENERIC_GSM (info->modem), + TRUE, MM_GENERIC_GSM_REG_TYPE_PS, MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE, TRUE, NULL, TRUE, NULL); @@ -1938,12 +2002,13 @@ do_register (MMModemGsmNetwork *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; char *command = NULL; /* Clear any previous registration */ - mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem)); + mm_generic_gsm_pending_registration_stop (self); info = mm_callback_info_new (MM_MODEM (modem), callback, user_data); @@ -1958,7 +2023,7 @@ do_register (MMModemGsmNetwork *modem, if (network_id) { command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id); priv->manual_reg = TRUE; - } else if (reg_is_idle (priv->reg_status) || priv->manual_reg) { + } else if (reg_is_idle (gsm_reg_status (self)) || priv->manual_reg) { command = g_strdup ("+COPS=0,,"); priv->manual_reg = FALSE; } @@ -1996,7 +2061,7 @@ gsm_network_reg_info_invoke (MMCallbackInfo *info) MMModemGsmNetworkRegInfoFn callback = (MMModemGsmNetworkRegInfoFn) info->callback; callback (MM_MODEM_GSM_NETWORK (info->modem), - priv->reg_status, + gsm_reg_status (MM_GENERIC_GSM (info->modem)), priv->oper_code, priv->oper_name, info->error, @@ -3295,10 +3360,13 @@ simple_state_machine (MMModem *modem, GError *error, gpointer user_data) str = simple_get_string_property (info, "number", &info->error); if (!info->error) { if (simple_get_bool_property (info, "home_only", &home_only, &info->error)) { + MMModemGsmNetworkRegStatus status; + priv->roam_allowed = !home_only; /* Don't connect if we're not supposed to be roaming */ - if (home_only && (priv->reg_status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)) { + status = gsm_reg_status (MM_GENERIC_GSM (modem)); + if (home_only && (status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING)) { info->error = g_error_new_literal (MM_MOBILE_ERROR, MM_MOBILE_ERROR_GPRS_ROAMING_NOT_ALLOWED, "Roaming is not allowed."); diff --git a/src/mm-generic-gsm.h b/src/mm-generic-gsm.h index 4fca7b42..de9dc897 100644 --- a/src/mm-generic-gsm.h +++ b/src/mm-generic-gsm.h @@ -49,6 +49,11 @@ typedef enum { MM_GENERIC_GSM_PROP_ACCESS_TECHNOLOGY } MMGenericGsmProp; +typedef enum { + MM_GENERIC_GSM_REG_TYPE_UNKNOWN = 0, + MM_GENERIC_GSM_REG_TYPE_CS = 1, + MM_GENERIC_GSM_REG_TYPE_PS = 2 +} MMGenericGsmRegType; typedef struct { MMModemBase parent; @@ -122,6 +127,7 @@ void mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem); gint mm_generic_gsm_get_cid (MMGenericGsm *modem); void mm_generic_gsm_set_reg_status (MMGenericGsm *modem, + MMGenericGsmRegType reg_type, MMModemGsmNetworkRegStatus status); MMModemCharset mm_generic_gsm_get_charset (MMGenericGsm *modem); |