diff options
author | Dan Williams <dcbw@redhat.com> | 2009-11-30 15:44:20 -0800 |
---|---|---|
committer | Dan Williams <dcbw@redhat.com> | 2009-11-30 15:44:20 -0800 |
commit | 909b8b7c8dbe2de97d9550004641c70411d92c56 (patch) | |
tree | a7546e1461ed81ff9ba9a251fd3fe2decdacf4ad | |
parent | c463b5a4005b9e55d0faeb887debe327118ef230 (diff) | |
parent | 0f595adb7f07f575627667480f23775b21f9efb2 (diff) |
Merge commit 'origin/master' into states
-rw-r--r-- | plugins/mm-modem-hso.c | 105 | ||||
-rw-r--r-- | plugins/mm-modem-huawei-cdma.c | 67 | ||||
-rw-r--r-- | plugins/mm-modem-zte.c | 44 | ||||
-rw-r--r-- | plugins/mm-plugin-huawei.c | 1 | ||||
-rw-r--r-- | src/mm-callback-info.c | 2 | ||||
-rw-r--r-- | src/mm-callback-info.h | 1 | ||||
-rw-r--r-- | src/mm-errors.c | 1 | ||||
-rw-r--r-- | src/mm-errors.h | 3 | ||||
-rw-r--r-- | src/mm-generic-cdma.c | 39 | ||||
-rw-r--r-- | src/mm-generic-cdma.h | 3 | ||||
-rw-r--r-- | src/mm-generic-gsm.c | 127 | ||||
-rw-r--r-- | src/mm-plugin-base.c | 67 | ||||
-rw-r--r-- | src/mm-serial-parsers.c | 14 | ||||
-rw-r--r-- | src/mm-serial-port.c | 21 | ||||
-rwxr-xr-x | test/mm-test.py | 26 |
15 files changed, 404 insertions, 117 deletions
diff --git a/plugins/mm-modem-hso.c b/plugins/mm-modem-hso.c index 26dc17d6..a2e3b408 100644 --- a/plugins/mm-modem-hso.c +++ b/plugins/mm-modem-hso.c @@ -47,10 +47,23 @@ G_DEFINE_TYPE_EXTENDED (MMModemHso, mm_modem_hso, MM_TYPE_GENERIC_GSM, 0, #define MM_MODEM_HSO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_HSO, MMModemHsoPrivate)) +static void _internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info); + +const char *auth_commands[] = { + "$QCPDPP", + /* Icera-based devices (GI0322/Quicksilver, iCON 505) don't implement + * $QCPDPP, but instead use _OPDPP with the same arguments. + */ + "_OPDPP", + NULL +}; + typedef struct { /* Pending connection attempt */ MMCallbackInfo *connect_pending_data; guint connect_pending_id; + + guint32 auth_idx; } MMModemHsoPrivate; #define OWANDATA_TAG "_OWANDATA: " @@ -72,6 +85,8 @@ mm_modem_hso_new (const char *device, NULL)); } +#define IGNORE_ERRORS_TAG "ignore-errors" + static void hso_call_control_done (MMSerialPort *port, GString *response, @@ -80,7 +95,7 @@ hso_call_control_done (MMSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; - if (error && !mm_callback_info_get_data (info, "ignore-errors")) + if (error && !mm_callback_info_get_data (info, IGNORE_ERRORS_TAG)) info->error = g_error_copy (error); mm_callback_info_schedule (info); @@ -110,7 +125,7 @@ hso_call_control (MMModemHso *self, MMSerialPort *primary; info = mm_callback_info_new (MM_MODEM (self), callback, user_data); - mm_callback_info_set_data (info, "ignore-error", GUINT_TO_POINTER (ignore_errors), NULL); + mm_callback_info_set_data (info, IGNORE_ERRORS_TAG, GUINT_TO_POINTER (ignore_errors), NULL); command = g_strdup_printf ("AT_OWANCALL=%d,%d,1", hso_get_cid (self), activate ? 1 : 0); primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); @@ -172,9 +187,9 @@ hso_enabled (MMModem *modem, } static void -hso_disabled (MMModem *modem, - GError *error, - gpointer user_data) +clear_old_context (MMModem *modem, + GError *error, + gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; @@ -195,13 +210,58 @@ auth_done (MMSerialPort *port, { MMCallbackInfo *info = (MMCallbackInfo *) user_data; MMModemHso *self = MM_MODEM_HSO (info->modem); + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); - } else + priv->auth_idx++; + if (auth_commands[priv->auth_idx]) { + /* Try the next auth command */ + _internal_hso_modem_authenticate (self, info); + } else { + /* Reset to 0 so that something gets tried for the next connection */ + priv->auth_idx = 0; + + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } + } else { + priv->auth_idx = 0; + /* success, kill any existing connections first */ - hso_call_control (self, FALSE, FALSE, hso_disabled, info); + hso_call_control (self, FALSE, TRUE, clear_old_context, info); + } +} + +static void +_internal_hso_modem_authenticate (MMModemHso *self, MMCallbackInfo *info) +{ + MMModemHsoPrivate *priv = MM_MODEM_HSO_GET_PRIVATE (self); + MMSerialPort *primary; + guint32 cid; + char *command; + const char *username, *password; + + primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); + g_assert (primary); + + cid = hso_get_cid (self); + + username = mm_callback_info_get_data (info, "username"); + password = mm_callback_info_get_data (info, "password"); + + if (!username && !password) + command = g_strdup_printf ("%s=%d,0", auth_commands[priv->auth_idx], cid); + else { + command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"", + auth_commands[priv->auth_idx], + cid, + password ? password : "", + username ? username : ""); + + } + + mm_serial_port_queue_command (primary, command, 3, auth_done, info); + g_free (command); } void @@ -212,32 +272,17 @@ mm_hso_modem_authenticate (MMModemHso *self, gpointer user_data) { MMCallbackInfo *info; - MMSerialPort *primary; g_return_if_fail (MM_IS_MODEM_HSO (self)); g_return_if_fail (callback != NULL); info = mm_callback_info_new (MM_MODEM (self), callback, user_data); + if (username) + mm_callback_info_set_data (info, "username", g_strdup (username), g_free); + if (password) + mm_callback_info_set_data (info, "password", g_strdup (password), g_free); - primary = mm_generic_gsm_get_port (MM_GENERIC_GSM (self), MM_PORT_TYPE_PRIMARY); - g_assert (primary); - - if (username || password) { - char *command; - - // FIXME: if QCPDPP fails, try OPDPP. AT&T Quicksilver uses a different - // chipset (ie, not Qualcomm) and the auth command is OPDPP instead of - // the Qualcomm-specific QCPDPP. - - command = g_strdup_printf ("AT$QCPDPP=%d,1,\"%s\",\"%s\"", - hso_get_cid (self), - password ? password : "", - username ? username : ""); - - mm_serial_port_queue_command (primary, command, 3, auth_done, info); - g_free (command); - } else - auth_done (primary, NULL, NULL, info); + _internal_hso_modem_authenticate (self, info); } /*****************************************************************************/ @@ -304,7 +349,7 @@ disable (MMModem *modem, info = mm_callback_info_new (modem, callback, user_data); /* Kill any existing connection */ - hso_call_control (MM_MODEM_HSO (modem), FALSE, FALSE, disable_done, info); + hso_call_control (MM_MODEM_HSO (modem), FALSE, TRUE, disable_done, info); } static void diff --git a/plugins/mm-modem-huawei-cdma.c b/plugins/mm-modem-huawei-cdma.c index 25c7b373..cc0aa4ef 100644 --- a/plugins/mm-modem-huawei-cdma.c +++ b/plugins/mm-modem-huawei-cdma.c @@ -56,25 +56,53 @@ mm_modem_huawei_cdma_new (const char *device, /* Unsolicited message handlers */ -static void -handle_signal_quality_change (MMSerialPort *port, - GMatchInfo *match_info, - gpointer user_data) +static gint +parse_quality (const char *str, const char *detail) { - MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); - char *str; - long int quality; - - str = g_match_info_fetch (match_info, 1); + long int quality = 0; errno = 0; quality = strtol (str, NULL, 10); if (errno == 0) { quality = CLAMP (quality, 0, 100); - g_debug ("Signal quality: %ld", quality); - mm_generic_cdma_update_signal_quality (MM_GENERIC_CDMA (self), (guint32) quality); + g_debug ("%s: %ld", detail, quality); + return (gint) quality; } + return -1; +} + +static void +handle_1x_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); + char *str; + gint quality; + + str = g_match_info_fetch (match_info, 1); + quality = parse_quality (str, "1X signal quality"); + g_free (str); + + if (quality >= 0) + mm_generic_cdma_update_cdma1x_quality (MM_GENERIC_CDMA (self), (guint32) quality); +} + +static void +handle_evdo_quality_change (MMSerialPort *port, + GMatchInfo *match_info, + gpointer user_data) +{ + MMModemHuaweiCdma *self = MM_MODEM_HUAWEI_CDMA (user_data); + char *str; + gint quality; + + str = g_match_info_fetch (match_info, 1); + quality = parse_quality (str, "EVDO signal quality"); g_free (str); + + if (quality >= 0) + mm_generic_cdma_update_evdo_quality (MM_GENERIC_CDMA (self), (guint32) quality); } /*****************************************************************************/ @@ -233,11 +261,26 @@ grab_port (MMModem *modem, port = mm_generic_cdma_grab_port (MM_GENERIC_CDMA (modem), subsys, name, suggested_type, user_data, error); if (port && MM_IS_SERIAL_PORT (port)) { + gboolean evdo0 = FALSE, evdoA = FALSE; + g_object_set (G_OBJECT (port), MM_PORT_CARRIER_DETECT, FALSE, NULL); + /* 1x signal level */ regex = g_regex_new ("\\r\\n\\^RSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); - mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_signal_quality_change, modem, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_1x_quality_change, modem, NULL); g_regex_unref (regex); + + g_object_get (G_OBJECT (modem), + MM_GENERIC_CDMA_EVDO_REV0, &evdo0, + MM_GENERIC_CDMA_EVDO_REVA, &evdoA, + NULL); + + if (evdo0 || evdoA) { + /* EVDO signal level */ + regex = g_regex_new ("\\r\\n\\^HRSSILVL:(\\d+)\\r\\n", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + mm_serial_port_add_unsolicited_msg_handler (MM_SERIAL_PORT (port), regex, handle_evdo_quality_change, modem, NULL); + g_regex_unref (regex); + } } return !!port; diff --git a/plugins/mm-modem-zte.c b/plugins/mm-modem-zte.c index 536de0ec..95fb1b0a 100644 --- a/plugins/mm-modem-zte.c +++ b/plugins/mm-modem-zte.c @@ -28,6 +28,12 @@ static void modem_init (MMModem *modem_class); G_DEFINE_TYPE_EXTENDED (MMModemZte, mm_modem_zte, MM_TYPE_GENERIC_GSM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_MODEM, modem_init)) +#define MM_MODEM_ZTE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_MODEM_ZTE, MMModemZtePrivate)) + +typedef struct { + gboolean init_retried; +} MMModemZtePrivate; + MMModem * mm_modem_zte_new (const char *device, const char *driver, @@ -79,6 +85,10 @@ pin_check_done (MMModem *modem, GError *error, gpointer user_data) } } +static void enable_flash_done (MMSerialPort *port, + GError *error, + gpointer user_data); + static void pre_init_done (MMSerialPort *port, GString *response, @@ -86,10 +96,18 @@ pre_init_done (MMSerialPort *port, gpointer user_data) { MMCallbackInfo *info = (MMCallbackInfo *) user_data; + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (info->modem); if (error) { - info->error = g_error_copy (error); - mm_callback_info_schedule (info); + /* Retry the init string one more time; the modem sometimes throws it away */ + if ( !priv->init_retried + && g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_RESPONSE_TIMEOUT)) { + priv->init_retried = TRUE; + enable_flash_done (port, NULL, user_data); + } else { + info->error = g_error_copy (error); + mm_callback_info_schedule (info); + } } else { /* Now check the PIN explicitly, zte doesn't seem to report that it needs it otherwise */ @@ -116,9 +134,12 @@ enable (MMModem *modem, MMModemFn callback, gpointer user_data) { + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); MMCallbackInfo *info; MMSerialPort *primary; + priv->init_retried = FALSE; + /* First, reset the previously used CID */ mm_generic_gsm_set_cid (MM_GENERIC_GSM (modem), 0); @@ -136,6 +157,21 @@ enable (MMModem *modem, mm_serial_port_flash (primary, 100, enable_flash_done, info); } +static void +disable (MMModem *modem, + MMModemFn callback, + gpointer user_data) +{ + MMModemZtePrivate *priv = MM_MODEM_ZTE_GET_PRIVATE (modem); + MMModem *parent_modem_iface; + + priv->init_retried = FALSE; + + /* Do the normal disable stuff */ + parent_modem_iface = g_type_interface_peek_parent (MM_MODEM_GET_INTERFACE (modem)); + parent_modem_iface->disable (modem, callback, user_data); +} + static gboolean grab_port (MMModem *modem, const char *subsys, @@ -189,6 +225,7 @@ static void modem_init (MMModem *modem_class) { modem_class->enable = enable; + modem_class->disable = disable; modem_class->grab_port = grab_port; } @@ -200,6 +237,9 @@ mm_modem_zte_init (MMModemZte *self) static void mm_modem_zte_class_init (MMModemZteClass *klass) { + GObjectClass *object_class = G_OBJECT_CLASS (klass); + mm_modem_zte_parent_class = g_type_class_peek_parent (klass); + g_type_class_add_private (object_class, sizeof (MMModemZtePrivate)); } diff --git a/plugins/mm-plugin-huawei.c b/plugins/mm-plugin-huawei.c index b8d8424b..ad799f0a 100644 --- a/plugins/mm-plugin-huawei.c +++ b/plugins/mm-plugin-huawei.c @@ -220,6 +220,7 @@ supports_port (MMPluginBase *base, add_regex (info->serial, "\\r\\n\\^MODE:(\\d),(\\d)\\r\\n", task); add_regex (info->serial, "\\r\\n\\^DSFLOWRPT:(.+)\\r\\n", task); add_regex (info->serial, "\\r\\n\\^BOOT:.+\\r\\n", task); + add_regex (info->serial, "\\r\\r\\^BOOT:.+\\r\\r", task); info->id = g_timeout_add (5000, probe_secondary_timeout, task); diff --git a/src/mm-callback-info.c b/src/mm-callback-info.c index 0aca55da..089f0b73 100644 --- a/src/mm-callback-info.c +++ b/src/mm-callback-info.c @@ -67,6 +67,7 @@ callback_info_done (gpointer user_data) MMCallbackInfo *info = (MMCallbackInfo *) user_data; info->pending_id = 0; + info->called = TRUE; if (info->invoke_fn && info->callback) info->invoke_fn (info); @@ -94,6 +95,7 @@ mm_callback_info_schedule (MMCallbackInfo *info) { g_return_if_fail (info != NULL); g_return_if_fail (info->pending_id == 0); + g_return_if_fail (info->called == FALSE); info->pending_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, callback_info_do, info, callback_info_done); } diff --git a/src/mm-callback-info.h b/src/mm-callback-info.h index 591ac86e..783e1282 100644 --- a/src/mm-callback-info.h +++ b/src/mm-callback-info.h @@ -28,6 +28,7 @@ struct _MMCallbackInfo { MMCallbackInfoInvokeFn invoke_fn; GCallback callback; + gboolean called; gpointer user_data; GError *error; diff --git a/src/mm-errors.c b/src/mm-errors.c index 510fa6d0..6448b807 100644 --- a/src/mm-errors.c +++ b/src/mm-errors.c @@ -39,6 +39,7 @@ mm_serial_error_get_type (void) ENUM_ENTRY (MM_SERIAL_OPEN_FAILED, "SerialOpenFailed"), ENUM_ENTRY (MM_SERIAL_SEND_FAILED, "SerialSendfailed"), ENUM_ENTRY (MM_SERIAL_RESPONSE_TIMEOUT, "SerialResponseTimeout"), + ENUM_ENTRY (MM_SERIAL_OPEN_FAILED_NO_DEVICE, "SerialOpenFailedNoDevice"), { 0, 0, 0 } }; diff --git a/src/mm-errors.h b/src/mm-errors.h index bc43d3e9..4bf8ecad 100644 --- a/src/mm-errors.h +++ b/src/mm-errors.h @@ -22,7 +22,8 @@ enum { MM_SERIAL_OPEN_FAILED = 0, MM_SERIAL_SEND_FAILED = 1, - MM_SERIAL_RESPONSE_TIMEOUT = 2 + MM_SERIAL_RESPONSE_TIMEOUT = 2, + MM_SERIAL_OPEN_FAILED_NO_DEVICE = 3 }; #define MM_SERIAL_ERROR (mm_serial_error_quark ()) diff --git a/src/mm-generic-cdma.c b/src/mm-generic-cdma.c index 0653e708..be95116c 100644 --- a/src/mm-generic-cdma.c +++ b/src/mm-generic-cdma.c @@ -48,7 +48,8 @@ G_DEFINE_TYPE_EXTENDED (MMGenericCdma, mm_generic_cdma, MM_TYPE_MODEM_BASE, 0, #define MM_GENERIC_CDMA_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MM_TYPE_GENERIC_CDMA, MMGenericCdmaPrivate)) typedef struct { - guint32 signal_quality; + guint32 cdma1x_quality; + guint32 evdo_quality; gboolean valid; gboolean evdo_rev0; gboolean evdo_revA; @@ -633,13 +634,33 @@ get_card_info (MMModem *modem, /*****************************************************************************/ void -mm_generic_cdma_update_signal_quality (MMGenericCdma *self, guint32 quality) +mm_generic_cdma_update_cdma1x_quality (MMGenericCdma *self, guint32 quality) { + MMGenericCdmaPrivate *priv; + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); g_return_if_fail (quality >= 0 && quality <= 100); - MM_GENERIC_CDMA_GET_PRIVATE (self)->signal_quality = quality; - mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (self), quality); + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + if (priv->cdma1x_quality != quality) { + priv->cdma1x_quality = quality; + mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (self), quality); + } +} + +void +mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality) +{ + MMGenericCdmaPrivate *priv; + + g_return_if_fail (MM_IS_GENERIC_CDMA (self)); + g_return_if_fail (quality >= 0 && quality <= 100); + + priv = MM_GENERIC_CDMA_GET_PRIVATE (self); + if (priv->evdo_quality != quality) { + priv->evdo_quality = quality; + // FIXME: emit a signal + } } static void @@ -671,9 +692,11 @@ get_signal_quality_done (MMSerialPort *port, quality = CLAMP (quality, 0, 31) * 100 / 31; priv = MM_GENERIC_CDMA_GET_PRIVATE (info->modem); - priv->signal_quality = quality; mm_callback_info_set_result (info, GUINT_TO_POINTER (quality), NULL); - mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (info->modem), quality); + if (priv->cdma1x_quality != quality) { + priv->cdma1x_quality = quality; + mm_modem_cdma_emit_signal_quality_changed (MM_MODEM_CDMA (info->modem), quality); + } } } else info->error = g_error_new (MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, @@ -694,8 +717,8 @@ get_signal_quality (MMModemCdma *modem, connected = mm_port_get_connected (MM_PORT (priv->primary)); if (connected && !priv->secondary) { - g_message ("Returning saved signal quality %d", priv->signal_quality); - callback (MM_MODEM (modem), priv->signal_quality, NULL, user_data); + g_message ("Returning saved signal quality %d", priv->cdma1x_quality); + callback (MM_MODEM (modem), priv->cdma1x_quality, NULL, user_data); return; } diff --git a/src/mm-generic-cdma.h b/src/mm-generic-cdma.h index a1200c36..8d85cb38 100644 --- a/src/mm-generic-cdma.h +++ b/src/mm-generic-cdma.h @@ -64,7 +64,8 @@ MMPort * mm_generic_cdma_grab_port (MMGenericCdma *self, MMSerialPort *mm_generic_cdma_get_port (MMGenericCdma *modem, MMPortType ptype); -void mm_generic_cdma_update_signal_quality (MMGenericCdma *self, guint32 quality); +void mm_generic_cdma_update_cdma1x_quality (MMGenericCdma *self, guint32 quality); +void mm_generic_cdma_update_evdo_quality (MMGenericCdma *self, guint32 quality); /* For unsolicited 1x registration state changes */ void mm_generic_cdma_set_1x_registration_state (MMGenericCdma *self, diff --git a/src/mm-generic-gsm.c b/src/mm-generic-gsm.c index 468cdfc2..74f7cb2f 100644 --- a/src/mm-generic-gsm.c +++ b/src/mm-generic-gsm.c @@ -835,6 +835,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) @@ -842,21 +843,27 @@ 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; - /* info must be set to process status updates - for now */ - g_return_val_if_fail (!!info, status_done); - switch (new_value) { case 0: status = MM_MODEM_GSM_NETWORK_REG_STATUS_IDLE; @@ -888,25 +895,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 */ - 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 */ - 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: - 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: - 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: - 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; @@ -918,23 +925,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); @@ -944,7 +949,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 @@ -956,41 +965,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 @@ -1009,6 +1049,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; @@ -1028,6 +1070,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); @@ -1038,7 +1083,7 @@ do_register (MMModemGsmNetwork *modem, else command = g_strdup ("+COPS=0,,"); - mm_serial_port_queue_command (priv->primary, command, 10, register_done, info); + mm_serial_port_queue_command (priv->primary, command, 120, register_done, info); g_free (command); } @@ -1353,7 +1398,7 @@ cid_range_read (MMSerialPort *port, GRegex *r; GMatchInfo *match_info; - r = g_regex_new ("\\+CGDCONT: \\((\\d+)-(\\d+)\\),\"(\\S+)\"", + r = g_regex_new ("\\+CGDCONT:\\s*\\((\\d+)-(\\d+)\\),\\(?\"(\\S+)\"", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &info->error); if (r) { @@ -1398,7 +1443,7 @@ cid_range_read (MMSerialPort *port, mm_callback_info_set_data (info, "cid", GUINT_TO_POINTER (cid), NULL); - command = g_strdup_printf ("+CGDCONT=%d, \"IP\", \"%s\"", cid, apn); + command = g_strdup_printf ("+CGDCONT=%d,\"IP\",\"%s\"", cid, apn); mm_serial_port_queue_command (port, command, 3, set_apn_done, info); g_free (command); } @@ -1419,7 +1464,7 @@ existing_apns_read (MMSerialPort *port, GRegex *r; GMatchInfo *match_info; - r = g_regex_new ("\\+CGDCONT: (\\d+)\\s*,\"(\\S+)\",\"(\\S+)\",\"(\\S+)\"", + r = g_regex_new ("\\+CGDCONT:\\s*(\\d+)\\s*,\"(\\S+)\",\"(\\S+)\",\"(\\S*)\"", G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW, 0, &info->error); if (r) { diff --git a/src/mm-plugin-base.c b/src/mm-plugin-base.c index 540670aa..ad849218 100644 --- a/src/mm-plugin-base.c +++ b/src/mm-plugin-base.c @@ -85,6 +85,9 @@ typedef struct { GUdevDevice *physdev; char *driver; + guint open_id; + guint32 open_tries; + MMSerialPort *probe_port; guint32 probed_caps; ProbeState probe_state; @@ -201,7 +204,7 @@ mm_plugin_base_supports_task_init (MMPluginBaseSupportsTask *self) } static void -dispose (GObject *object) +supports_task_dispose (GObject *object) { MMPluginBaseSupportsTaskPrivate *priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (object); @@ -214,6 +217,9 @@ dispose (GObject *object) g_free (priv->probe_resp); g_clear_error (&(priv->probe_error)); + if (priv->open_id) + g_source_remove (priv->open_id); + if (priv->probe_id) g_source_remove (priv->probe_id); if (priv->probe_port) @@ -230,7 +236,7 @@ mm_plugin_base_supports_task_class_init (MMPluginBaseSupportsTaskClass *klass) g_type_class_add_private (object_class, sizeof (MMPluginBaseSupportsTaskPrivate)); /* Virtual methods */ - object_class->dispose = dispose; + object_class->dispose = supports_task_dispose; } /*****************************************************************************/ @@ -467,12 +473,52 @@ flash_done (MMSerialPort *port, GError *error, gpointer user_data) mm_serial_port_queue_command (port, "+GCAP", 3, parse_response, user_data); } +static gboolean +try_open (gpointer user_data) +{ + MMPluginBaseSupportsTask *task = MM_PLUGIN_BASE_SUPPORTS_TASK (user_data); + MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); + GError *error = NULL; + + task_priv->open_id = 0; + + if (!mm_serial_port_open (task_priv->probe_port, &error)) { + if (++task_priv->open_tries > 4) { + /* took too long to open the port; give up */ + g_warning ("(%s): failed to open after 4 tries.", + mm_port_get_device (MM_PORT (task_priv->probe_port))); + probe_complete (task); + } else if (g_error_matches (error, + MM_SERIAL_ERROR, + MM_SERIAL_OPEN_FAILED_NO_DEVICE)) { + /* this is nozomi being dumb; try again */ + task_priv->open_id = g_timeout_add_seconds (1, try_open, task); + } else { + /* some other hard error */ + probe_complete (task); + } + g_clear_error (&error); + } else { + /* success, start probing */ + GUdevDevice *port; + + port = mm_plugin_base_supports_task_get_port (task); + g_assert (port); + + g_debug ("(%s): probe requested by plugin '%s'", + g_udev_device_get_name (port), + mm_plugin_get_name (MM_PLUGIN (task_priv->plugin))); + mm_serial_port_flash (task_priv->probe_port, 100, flash_done, task); + } + + return FALSE; +} + gboolean mm_plugin_base_probe_port (MMPluginBase *self, MMPluginBaseSupportsTask *task, GError **error) { - MMPluginBasePrivate *priv = MM_PLUGIN_BASE_GET_PRIVATE (self); MMPluginBaseSupportsTaskPrivate *task_priv = MM_PLUGIN_BASE_SUPPORTS_TASK_GET_PRIVATE (task); MMSerialPort *serial; const char *name; @@ -488,6 +534,12 @@ mm_plugin_base_probe_port (MMPluginBase *self, g_assert (name); serial = mm_serial_port_new (name, MM_PORT_TYPE_PRIMARY); + if (serial == NULL) { + g_set_error_literal (error, MM_MODEM_ERROR, MM_MODEM_ERROR_GENERAL, + "Failed to create new serial port."); + return FALSE; + } + g_object_set (serial, MM_SERIAL_PORT_SEND_DELAY, (guint64) 100000, MM_PORT_CARRIER_DETECT, FALSE, @@ -498,14 +550,9 @@ mm_plugin_base_probe_port (MMPluginBase *self, mm_serial_parser_v1_new (), mm_serial_parser_v1_destroy); - if (!mm_serial_port_open (serial, error)) { - g_object_unref (serial); - return FALSE; - } - - g_debug ("(%s): probe requested by plugin '%s'", name, priv->name); + /* Open the port */ task_priv->probe_port = serial; - mm_serial_port_flash (serial, 100, flash_done, task); + task_priv->open_id = g_idle_add (try_open, task); return TRUE; } diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c index b36496ff..58985d90 100644 --- a/src/mm-serial-parsers.c +++ b/src/mm-serial-parsers.c @@ -26,15 +26,19 @@ response_clean (GString *response) { char *s; - /* Ends with '<CR><LF>' */ + /* Ends with one or more '<CR><LF>' */ s = response->str + response->len - 1; - if (*s == '\n' && *(--s) == '\r') + while ((s > response->str) && (*s == '\n') && (*(s - 1) == '\r')) { g_string_truncate (response, response->len - 2); + s -= 2; + } - /* Starts with '<CR><LF>' */ + /* Starts with one or more '<CR><LF>' */ s = response->str; - if (*s == '\r' && *(++s) == '\n') + while ((response->len >= 2) && (*s == '\r') && (*(s + 1) == '\n')) { g_string_erase (response, 0, 2); + s = response->str; + } } @@ -199,7 +203,7 @@ mm_serial_parser_v1_new (void) parser = g_slice_new (MMSerialParserV1); - parser->regex_ok = g_regex_new ("\\r\\nOK\\r\\n$", flags, 0, NULL); + parser->regex_ok = g_regex_new ("\\r\\nOK(\\r\\n)+$", flags, 0, NULL); parser->regex_connect = g_regex_new ("\\r\\nCONNECT.*\\r\\n", flags, 0, NULL); parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL); diff --git a/src/mm-serial-port.c b/src/mm-serial-port.c index 250e00be..3abd5619 100644 --- a/src/mm-serial-port.c +++ b/src/mm-serial-port.c @@ -479,9 +479,15 @@ mm_serial_port_schedule_queue_process (MMSerialPort *self) MMSerialPortPrivate *priv = MM_SERIAL_PORT_GET_PRIVATE (self); GSource *source; - if (priv->queue_schedule) + if (priv->timeout_id) { + /* A command is already in progress */ + return; + } + + if (priv->queue_schedule) { /* Already scheduled */ return; + } source = g_idle_source_new (); g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_queue_process), G_OBJECT (self))); @@ -568,7 +574,7 @@ mm_serial_port_queue_process (gpointer data) if (mm_serial_port_send_command (self, info->command, &error)) { GSource *source; - source = g_timeout_source_new (info->timeout); + source = g_timeout_source_new_seconds (info->timeout); g_source_set_closure (source, g_cclosure_new_object (G_CALLBACK (mm_serial_port_timed_out), G_OBJECT (self))); g_source_attach (source, NULL); priv->timeout_id = g_source_get_id (source); @@ -791,11 +797,18 @@ mm_serial_port_open (MMSerialPort *self, GError **error) g_message ("(%s) opening serial device...", device); devfile = g_strdup_printf ("/dev/%s", device); + errno = 0; priv->fd = open (devfile, O_RDWR | O_EXCL | O_NONBLOCK | O_NOCTTY); g_free (devfile); if (priv->fd < 0) { - g_set_error (error, MM_SERIAL_ERROR, MM_SERIAL_OPEN_FAILED, + /* nozomi isn't ready yet when the port appears, and it'll return + * ENODEV when open(2) is called on it. Make sure we can handle this + * by returning a special error in that case. + */ + g_set_error (error, + MM_SERIAL_ERROR, + (errno == ENODEV) ? MM_SERIAL_OPEN_FAILED_NO_DEVICE : MM_SERIAL_OPEN_FAILED, "Could not open serial device %s: %s", device, strerror (errno)); return FALSE; } @@ -889,7 +902,7 @@ internal_queue_command (MMSerialPort *self, info = g_slice_new0 (MMQueueData); info->command = g_strdup (command); info->cached = cached; - info->timeout = timeout_seconds * 1000; + info->timeout = timeout_seconds; info->callback = callback; info->user_data = user_data; diff --git a/test/mm-test.py b/test/mm-test.py index 871ba202..cb9ecf0a 100755 --- a/test/mm-test.py +++ b/test/mm-test.py @@ -67,6 +67,12 @@ def cdma_inspect(proxy, dump_private): print "Error reading registration state: %s" % e try: + quality = cdma.GetSignalQuality() + print "Signal quality: %d" % quality + except dbus.exceptions.DBusException, e: + print "Error reading signal quality: %s" % e + + try: info = cdma.GetServingSystem() print "Class: %s" % get_cdma_band_class(info[0]) print "Band: %s" % info[1] @@ -170,10 +176,19 @@ def gsm_inspect(proxy, dump_private): # Gsm.Network interface net = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_GSM_NETWORK) - print "Signal quality: %d" % net.GetSignalQuality() + try: + quality = net.GetSignalQuality() + print "Signal quality: %d" % quality + except dbus.exceptions.DBusException, e: + print "Error reading signal quality: %s" % e print "Scanning..." - results = net.Scan(timeout=120) + try: + results = net.Scan(timeout=120) + except dbus.exceptions.DBusException, e: + print "Error scanning: %s" % e + results = {} + for r in results: status = r['status'] if status == "1": @@ -214,7 +229,12 @@ def gsm_connect(proxy, apn, user, password): # Modem.Simple interface simple = dbus.Interface(proxy, dbus_interface=MM_DBUS_INTERFACE_MODEM_SIMPLE) try: - simple.Connect({'apn': apn, 'number':"*99#"}, timeout=60) + opts = {'apn': apn, 'number':"*99#"} + if user is not None: + opts['username'] = user + if password is not None: + opts['password'] = password + simple.Connect(opts, timeout=120) print "\nConnected!" except Exception, e: print "Error connecting: %s" % e |