aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2009-11-30 15:41:09 -0800
committerDan Williams <dcbw@redhat.com>2009-11-30 15:41:09 -0800
commit0f595adb7f07f575627667480f23775b21f9efb2 (patch)
treeb899f4363df180b8882e441e3df3d138686d6e0d
parentf205c814b58d37b3865c42e7f8b5112ce0f78aa0 (diff)
gsm: fix unsolicited registration segfaults
By decoupling the solicited registration callback from unsolicited replies, we can be sure of the call flow and avoid issues where unsolicited registration will be processed when an explicit registration request is no longer in progress. Also ups the timeout on CREG=0,, to 120 seconds because that appears to trigger an internal scan on some of the 'hso' devices that I have, and can take up to 60 or more seconds to complete or fail.
-rw-r--r--src/mm-generic-gsm.c124
1 files changed, 83 insertions, 41 deletions
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index 18901963..95b5d883 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -801,6 +801,7 @@ read_operator_name_done (MMSerialPort *port,
}
/* Registration */
+#define REG_STATUS_AGAIN_TAG "reg-status-again"
void
mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem)
@@ -808,14 +809,23 @@ mm_generic_gsm_pending_registration_stop (MMGenericGsm *modem)
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (modem);
if (priv->pending_reg_id) {
+ /* Clear the registration timeout handler */
g_source_remove (priv->pending_reg_id);
priv->pending_reg_id = 0;
+ }
+
+ if (priv->pending_reg_info) {
+ /* Clear any ongoing registration status callback */
+ mm_callback_info_set_data (priv->pending_reg_info, REG_STATUS_AGAIN_TAG, NULL, NULL);
+
+ /* And schedule the callback */
+ mm_callback_info_schedule (priv->pending_reg_info);
priv->pending_reg_info = NULL;
}
}
static gboolean
-reg_status_updated (MMGenericGsm *self, int new_value, MMCallbackInfo *info)
+reg_status_updated (MMGenericGsm *self, int new_value, GError **error)
{
MMModemGsmNetworkRegStatus status;
gboolean status_done = FALSE;
@@ -851,31 +861,25 @@ reg_status_updated (MMGenericGsm *self, int new_value, MMCallbackInfo *info)
case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
/* Successfully registered - stop registration */
- if (info)
- mm_callback_info_schedule (info);
- mm_generic_gsm_pending_registration_stop (self);
status_done = TRUE;
break;
case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED:
/* registration failed - stop registration */
- if (info) {
- info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED);
- mm_callback_info_schedule (info);
- }
- mm_generic_gsm_pending_registration_stop (self);
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED);
status_done = TRUE;
break;
case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING:
- if (info)
- info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
break;
case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE:
- if (info)
- info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK);
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK);
break;
default:
- if (info)
- info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
+ if (error)
+ *error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
break;
}
return status_done;
@@ -887,23 +891,21 @@ reg_state_changed (MMSerialPort *port,
gpointer user_data)
{
MMGenericGsm *self = MM_GENERIC_GSM (user_data);
- MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
char *str;
str = g_match_info_fetch (match_info, 1);
- if (!reg_status_updated (self, atoi (str), priv->pending_reg_info) && priv->pending_reg_info)
- g_clear_error (&priv->pending_reg_info->error);
-
+ reg_status_updated (self, atoi (str), NULL);
g_free (str);
}
static gboolean
reg_status_again (gpointer data)
{
- MMGenericGsmPrivate *priv;
MMCallbackInfo *info = (MMCallbackInfo *) data;
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ g_warn_if_fail (info == priv->pending_reg_info);
- priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
if (priv->pending_reg_id)
get_registration_status (priv->primary, info);
@@ -913,7 +915,11 @@ reg_status_again (gpointer data)
static void
reg_status_again_remove (gpointer data)
{
- g_source_remove (GPOINTER_TO_INT (data));
+ guint id = GPOINTER_TO_UINT (data);
+
+ /* Technically the GSource ID can be 0, but in practice it won't be */
+ if (id > 0)
+ g_source_remove (id);
}
static void
@@ -925,41 +931,72 @@ get_reg_status_done (MMSerialPort *port,
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
MMGenericGsm *self = MM_GENERIC_GSM (info->modem);
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
- int unsolicited, stat;
+ int reg_status = -1;
+ GRegex *r;
+ GMatchInfo *match_info;
+ char *tmp;
guint id;
+ g_warn_if_fail (info == priv->pending_reg_info);
+
if (error) {
info->error = g_error_copy (error);
- mm_generic_gsm_pending_registration_stop (self);
- mm_callback_info_schedule (info);
- return;
+ goto reg_done;
}
- if ( !g_str_has_prefix (response->str, "+CREG: ")
- || (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat) != 2)) {
- g_debug ("%s: unknown response: %s", __func__, response->str);
- info->error = g_error_new_literal (MM_MODEM_ERROR,
+ 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,
- "Could not parse the response");
- mm_generic_gsm_pending_registration_stop (self);
- mm_callback_info_schedule (info);
- return;
+ "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);
}
- if (!reg_status_updated (self, stat, info) && priv->pending_reg_id) {
+ if ( reg_status >= 0
+ && !reg_status_updated (self, reg_status, &info->error)
+ && priv->pending_reg_id) {
g_clear_error (&info->error);
- /* Registration is still going */
+
+ /* 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",
- GINT_TO_POINTER (id),
- reg_status_again_remove);
+ mm_callback_info_set_data (info, REG_STATUS_AGAIN_TAG,
+ GUINT_TO_POINTER (id),
+ reg_status_again_remove);
+ return;
}
+
+reg_done:
+ /* This will schedule the callback for us */
+ mm_generic_gsm_pending_registration_stop (self);
}
static void
get_registration_status (MMSerialPort *port, MMCallbackInfo *info)
{
- mm_serial_port_queue_command (port, "+CREG?", 3, get_reg_status_done, info);
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+
+ g_warn_if_fail (info == priv->pending_reg_info);
+
+ mm_serial_port_queue_command (port, "+CREG?", 10, get_reg_status_done, info);
}
static void
@@ -978,6 +1015,8 @@ registration_timed_out (gpointer data)
MMCallbackInfo *info = (MMCallbackInfo *) data;
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (info->modem);
+ g_warn_if_fail (info == priv->pending_reg_info);
+
priv->pending_reg_id = 0;
priv->pending_reg_info = NULL;
priv->reg_status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
@@ -997,6 +1036,9 @@ do_register (MMModemGsmNetwork *modem,
MMCallbackInfo *info;
char *command;
+ /* Clear any previous registration */
+ mm_generic_gsm_pending_registration_stop (MM_GENERIC_GSM (modem));
+
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
priv->pending_reg_id = g_timeout_add_seconds (60, registration_timed_out, info);
@@ -1007,7 +1049,7 @@ do_register (MMModemGsmNetwork *modem,
else
command = g_strdup ("+COPS=0,,");
- mm_serial_port_queue_command (priv->primary, command, 30, register_done, info);
+ mm_serial_port_queue_command (priv->primary, command, 120, register_done, info);
g_free (command);
}