aboutsummaryrefslogtreecommitdiff
path: root/src/mm-generic-gsm.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mm-generic-gsm.c')
-rw-r--r--src/mm-generic-gsm.c273
1 files changed, 175 insertions, 98 deletions
diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c
index a76cd698..b13623cd 100644
--- a/src/mm-generic-gsm.c
+++ b/src/mm-generic-gsm.c
@@ -22,11 +22,16 @@ typedef struct {
char *oper_name;
guint32 modem_type;
guint32 ip_method;
+ gboolean unsolicited_registration;
+
MMModemGsmNetworkRegStatus reg_status;
+ guint pending_registration;
+
guint32 signal_quality;
guint32 cid;
} MMGenericGsmPrivate;
+static void pending_registration_stop (MMGenericGsm *self);
static void get_registration_status (MMSerial *serial, MMCallbackInfo *info);
static void read_operator_done (MMSerial *serial,
GString *response,
@@ -47,6 +52,15 @@ mm_generic_gsm_new (const char *serial_device, const char *driver)
}
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));
@@ -62,6 +76,14 @@ mm_generic_gsm_get_cid (MMGenericGsm *modem)
return MM_GENERIC_GSM_GET_PRIVATE (modem)->cid;
}
+static void
+got_signal_quality (MMModem *modem,
+ guint32 result,
+ GError *error,
+ gpointer user_data)
+{
+}
+
void
mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
MMModemGsmNetworkRegStatus status)
@@ -75,11 +97,13 @@ mm_generic_gsm_set_reg_status (MMGenericGsm *modem,
if (priv->reg_status != status) {
priv->reg_status = status;
+ g_debug ("Registration state changed: %d", status);
+
if (status == MM_MODEM_GSM_NETWORK_REG_STATUS_HOME ||
status == MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING) {
mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,2;+COPS?", 3, read_operator_done, GINT_TO_POINTER (0));
mm_serial_queue_command (MM_SERIAL (modem), "+COPS=3,0;+COPS?", 3, read_operator_done, GINT_TO_POINTER (1));
- mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), NULL, NULL);
+ mm_modem_gsm_network_get_signal_quality (MM_MODEM_GSM_NETWORK (modem), got_signal_quality, NULL);
} else {
g_free (priv->oper_code);
g_free (priv->oper_name);
@@ -162,8 +186,11 @@ init_done (MMSerial *serial,
info->error = g_error_copy (error);
mm_callback_info_schedule (info);
} else {
- /* Disable unsolicited registration state changes, these will mess up our response parser */
- mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL);
+ if (MM_GENERIC_GSM_GET_PRIVATE (serial)->unsolicited_registration)
+ mm_serial_queue_command (serial, "+CREG=1", 5, NULL, NULL);
+ else
+ mm_serial_queue_command (serial, "+CREG=0", 5, NULL, NULL);
+
mm_serial_queue_command (serial, "+CFUN=1", 5, enable_done, user_data);
}
}
@@ -204,6 +231,8 @@ enable (MMModem *modem,
info = mm_callback_info_new (modem, callback, user_data);
if (!do_enable) {
+ pending_registration_stop (MM_GENERIC_GSM (modem));
+
if (mm_serial_is_connected (MM_SERIAL (modem)))
mm_serial_flash (MM_SERIAL (modem), 1000, disable_flash_done, info);
else
@@ -496,32 +525,124 @@ read_operator_done (MMSerial *serial,
}
}
+/* Registration */
+
+static gboolean
+pending_registration_timed_out (gpointer data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) data;
+
+ MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration = 0;
+
+ return FALSE;
+}
+
+static void
+pending_registration_stop (MMGenericGsm *self)
+{
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (self);
+
+ if (priv->pending_registration) {
+ g_source_remove (priv->pending_registration);
+ priv->pending_registration = 0;
+ }
+}
+
+static void
+pending_registration_done (gpointer data)
+{
+ MMCallbackInfo *info = (MMCallbackInfo *) data;
+
+ if (!info->error) {
+ switch (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->reg_status) {
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
+ /* Successfully registered */
+ break;
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED:
+ info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED);
+ break;
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING:
+ info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NETWORK_TIMEOUT);
+ break;
+ case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE:
+ info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_NO_NETWORK);
+ break;
+ default:
+ info->error = mm_mobile_error_for_code (MM_MOBILE_ERROR_UNKNOWN);
+ break;
+ }
+ }
+
+ mm_callback_info_schedule (info);
+}
+
+static void
+reg_status_updated (MMGenericGsm *self, int new_value)
+{
+ MMModemGsmNetworkRegStatus status;
+
+ switch (new_value) {
+ case 0:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
+ break;
+ case 1:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME;
+ break;
+ case 2:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING;
+ break;
+ case 3:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED;
+ break;
+ case 4:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
+ break;
+ case 5:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
+ break;
+ default:
+ status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
+ break;
+ }
+
+ mm_generic_gsm_set_reg_status (self, status);
+
+ /* Stop the pending registration in case of success or certain failure */
+ 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)
+
+ pending_registration_stop (self);
+}
+
+static void
+reg_state_changed (MMSerial *serial,
+ GMatchInfo *match_info,
+ gpointer user_data)
+{
+ char *str;
+
+ str = g_match_info_fetch (match_info, 1);
+ reg_status_updated (MM_GENERIC_GSM (serial), atoi (str));
+ g_free (str);
+}
+
static gboolean
reg_status_again (gpointer data)
{
MMCallbackInfo *info = (MMCallbackInfo *) data;
- guint32 counter;
-
- counter = GPOINTER_TO_UINT (mm_callback_info_get_data (info, "reg-status-counter"));
- if (counter > 60) {
- /* That's 60 seconds */
- info->error = g_error_new_literal (MM_MOBILE_ERROR,
- MM_MOBILE_ERROR_NETWORK_TIMEOUT,
- "Registration timed out");
- mm_callback_info_schedule (info);
- } else {
- mm_callback_info_set_data (info, "reg-status-counter",
- GUINT_TO_POINTER (++counter), NULL);
+
+ if (MM_GENERIC_GSM_GET_PRIVATE (info->modem)->pending_registration)
get_registration_status (MM_SERIAL (info->modem), info);
- }
- return TRUE;
+ return FALSE;
}
static void
-reg_status_remove (gpointer data)
+reg_status_again_remove (gpointer data)
{
- g_source_remove (GPOINTER_TO_UINT (data));
+ g_source_remove (GPOINTER_TO_INT (data));
}
static void
@@ -531,90 +652,37 @@ get_reg_status_done (MMSerial *serial,
gpointer user_data)
{
MMCallbackInfo *info = (MMCallbackInfo *) user_data;
- const char *reply = response->str;
- guint32 id;
- gboolean done = FALSE;
+ MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (serial);
if (error) {
info->error = g_error_copy (error);
- goto out;
+ pending_registration_done (info);
+ return;
}
- if (g_str_has_prefix (reply, "+CREG: ")) {
+ if (g_str_has_prefix (response->str, "+CREG: ")) {
/* Got valid reply */
- int n, stat;
-
- if (sscanf (reply + 7, "%d,%d", &n, &stat)) {
- MMModemGsmNetworkRegStatus status;
-
- switch (stat) {
- case 0:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE;
- break;
- case 1:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_HOME;
- break;
- case 2:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING;
- break;
- case 3:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED;
- break;
- case 4:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
- break;
- case 5:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING;
- break;
- default:
- status = MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN;
- break;
- }
-
- mm_generic_gsm_set_reg_status (MM_GENERIC_GSM (serial), status);
-
- switch (status) {
- case MM_MODEM_GSM_NETWORK_REG_STATUS_HOME:
- case MM_MODEM_GSM_NETWORK_REG_STATUS_ROAMING:
- /* Done */
- done = TRUE;
- break;
- case MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE:
- /* Huh? Stupid card, we told it to register, pretend it returned SEARCHING
- (hoping it will eventually start searching) */
- /* fall through */
- case MM_MODEM_GSM_NETWORK_REG_STATUS_SEARCHING:
- /* Wait more until the timeout expires. */
- id = GPOINTER_TO_INT (mm_callback_info_get_data (info, "reg-status-timeout"));
- if (!id) {
- id = g_timeout_add (1000, reg_status_again, info);
- mm_callback_info_set_data (info, "reg-status-timeout", GUINT_TO_POINTER (id),
- reg_status_remove);
- }
- break;
- case MM_MODEM_GSM_NETWORK_REG_STATUS_DENIED:
- info->error = g_error_new_literal (MM_MOBILE_ERROR,
- MM_MOBILE_ERROR_NETWORK_NOT_ALLOWED,
- "Network no allowed");
- break;
- case MM_MODEM_GSM_NETWORK_REG_STATUS_UNKNOWN:
- default:
- info->error = g_error_new_literal (MM_MODEM_ERROR,
- MM_MODEM_ERROR_GENERAL,
- "Unknown network status");
- break;
+ int unsolicited, stat;
+
+ if (sscanf (response->str + 7, "%d,%d", &unsolicited, &stat)) {
+ reg_status_updated (MM_GENERIC_GSM (serial), stat);
+
+ if (!unsolicited && priv->pending_registration) {
+ guint id;
+
+ 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);
}
}
} else {
- g_debug ("unknown response: %s", reply);
+ g_debug ("unknown response: %s", response->str);
info->error = g_error_new_literal (MM_MODEM_ERROR,
MM_MODEM_ERROR_GENERAL,
"Could not parse the response");
+ pending_registration_done (info);
}
-
- out:
- if (done || info->error)
- mm_callback_info_schedule (info);
}
static void
@@ -629,13 +697,8 @@ register_done (MMSerial *serial,
GError *error,
gpointer user_data)
{
- MMCallbackInfo *info = (MMCallbackInfo *) user_data;
-
- if (error) {
- info->error = g_error_copy (error);
- mm_callback_info_schedule (info);
- } else
- get_registration_status (serial, info);
+ /* Ignore errors here, get the actual registration status */
+ get_registration_status (serial, (MMCallbackInfo *) user_data);
}
static void
@@ -649,12 +712,18 @@ do_register (MMModemGsmNetwork *modem,
info = mm_callback_info_new (MM_MODEM (modem), callback, user_data);
+ MM_GENERIC_GSM_GET_PRIVATE (modem)->pending_registration =
+ g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 60,
+ pending_registration_timed_out,
+ info,
+ pending_registration_done);
+
if (network_id)
command = g_strdup_printf ("+COPS=1,2,\"%s\"", network_id);
else
command = g_strdup ("+COPS=0,,");
- mm_serial_queue_command (MM_SERIAL (modem), command, 60, register_done, info);
+ mm_serial_queue_command (MM_SERIAL (modem), command, 5, register_done, info);
g_free (command);
}
@@ -1366,10 +1435,16 @@ modem_simple_init (MMModemSimple *class)
static void
mm_generic_gsm_init (MMGenericGsm *self)
{
+ GRegex *regex;
+
mm_serial_set_response_parser (MM_SERIAL (self),
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_serial_add_unsolicited_msg_handler (MM_SERIAL (self), regex, reg_state_changed, NULL, NULL);
+ g_regex_unref (regex);
}
static void
@@ -1432,6 +1507,8 @@ finalize (GObject *object)
{
MMGenericGsmPrivate *priv = MM_GENERIC_GSM_GET_PRIVATE (object);
+ pending_registration_stop (MM_GENERIC_GSM (object));
+
g_free (priv->driver);
g_free (priv->data_device);
g_free (priv->oper_code);