huawei: delay processing of network-initiated disconnection

Originally developed by:
  Prathmesh Prabhu <pprabhu@chromium.org>
  Ben Chan <benchan@chromium.org>

Huawei MU736 prematurely fires a ^NDISSTAT unsolicited message upon a
network-initiated disconnection. The modem can go into a bad state if a
reconnect attempt happens before the disconnection completes. This patch works
around the issue by delaying the reporting of the disconnection.
This commit is contained in:
Aleksander Morgado
2013-09-20 19:29:38 +02:00
parent d818d9fe06
commit 21a5aaf4fe
3 changed files with 61 additions and 3 deletions

View File

@@ -37,6 +37,8 @@ G_DEFINE_TYPE (MMBroadbandBearerHuawei, mm_broadband_bearer_huawei, MM_TYPE_BROA
struct _MMBroadbandBearerHuaweiPrivate {
gpointer connect_pending;
gpointer disconnect_pending;
/* Tag for the post task for network-initiated disconnect */
guint network_disconnect_pending_id;
};
/*****************************************************************************/
@@ -232,6 +234,11 @@ connect_3gpp_context_step (Connect3gppContext *ctx)
return;
}
/* Network-initiated disconnect should not be outstanding at this point,
* because it interferes with the connect attempt.
*/
g_assert (ctx->self->priv->network_disconnect_pending_id == 0);
switch (ctx->step) {
case CONNECT_3GPP_CONTEXT_STEP_FIRST: {
MMBearerIpFamily ip_family;
@@ -598,6 +605,11 @@ disconnect_3gpp_context_step (Disconnect3gppContext *ctx)
return;
case DISCONNECT_3GPP_CONTEXT_STEP_LAST:
if (ctx->self->priv->network_disconnect_pending_id != 0) {
g_source_remove (ctx->self->priv->network_disconnect_pending_id);
ctx->self->priv->network_disconnect_pending_id = 0;
}
/* Clear context */
ctx->self->priv->disconnect_pending = NULL;
/* Set data port as result */
@@ -640,6 +652,18 @@ disconnect_3gpp (MMBroadbandBearer *self,
/*****************************************************************************/
static gboolean
network_disconnect_3gpp_delayed (MMBroadbandBearerHuawei *self)
{
mm_dbg ("Disconnect bearer '%s' on network request.",
mm_bearer_get_path (MM_BEARER (self)));
self->priv->network_disconnect_pending_id = 0;
mm_bearer_report_connection_status (MM_BEARER (self),
MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
return FALSE;
}
static void
report_connection_status (MMBearer *bearer,
MMBearerConnectionStatus status)
@@ -647,6 +671,7 @@ report_connection_status (MMBearer *bearer,
MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (bearer);
g_assert (status == MM_BEARER_CONNECTION_STATUS_CONNECTED ||
status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING ||
status == MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
/* When a pending connection / disconnection attempt is in progress, we use
@@ -664,7 +689,22 @@ report_connection_status (MMBearer *bearer,
/* We already use ^NDISSTATQRY? to poll the connection status, so only
* handle network-initiated disconnection here. */
mm_dbg ("Disconnect bearer '%s'", mm_bearer_get_path (MM_BEARER (self)));
if (status == MM_BEARER_CONNECTION_STATUS_DISCONNECTING) {
/* MM_BEARER_CONNECTION_STATUS_DISCONNECTING is used to indicate that the
* reporting of disconnection should be delayed. See MMBroadbandModemHuawei's
* bearer_report_connection_status for details. */
if (self->priv->network_disconnect_pending_id == 0) {
mm_dbg ("Delay network-initiated disconnection of bearer '%s'",
mm_bearer_get_path (MM_BEARER (self)));
self->priv->network_disconnect_pending_id = (g_timeout_add_seconds (
4,
(GSourceFunc) network_disconnect_3gpp_delayed,
self));
}
return;
}
/* Report disconnected right away */
MM_BEARER_CLASS (mm_broadband_bearer_huawei_parent_class)->report_connection_status (
bearer,
MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
@@ -692,6 +732,19 @@ mm_broadband_bearer_huawei_new_finish (GAsyncResult *res,
return MM_BEARER (bearer);
}
static void
dispose (GObject *object)
{
MMBroadbandBearerHuawei *self = MM_BROADBAND_BEARER_HUAWEI (object);
if (self->priv->network_disconnect_pending_id != 0) {
g_source_remove (self->priv->network_disconnect_pending_id);
self->priv->network_disconnect_pending_id = 0;
}
G_OBJECT_CLASS (mm_broadband_bearer_huawei_parent_class)->dispose (object);
}
void
mm_broadband_bearer_huawei_new (MMBroadbandModemHuawei *modem,
MMBearerProperties *config,
@@ -728,6 +781,7 @@ mm_broadband_bearer_huawei_class_init (MMBroadbandBearerHuaweiClass *klass)
g_type_class_add_private (object_class, sizeof (MMBroadbandBearerHuaweiPrivate));
object_class->dispose = dispose;
bearer_class->report_connection_status = report_connection_status;
broadband_bearer_class->connect_3gpp = connect_3gpp;
broadband_bearer_class->connect_3gpp_finish = connect_3gpp_finish;

View File

@@ -1525,11 +1525,14 @@ bearer_report_connection_status (MMBearer *bearer,
{
if (ndisstat_result->ipv4_available) {
/* TODO: MMBroadbandBearerHuawei does not currently support IPv6.
* When it does, we should check the IP family associated with each bearer. */
* When it does, we should check the IP family associated with each bearer.
*
* Also, send DISCONNECTING so that we give some time before actually
* disconnecting the connection */
mm_bearer_report_connection_status (bearer,
ndisstat_result->ipv4_connected ?
MM_BEARER_CONNECTION_STATUS_CONNECTED :
MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
MM_BEARER_CONNECTION_STATUS_DISCONNECTING);
}
}

View File

@@ -70,6 +70,7 @@ typedef enum { /*< underscore_name=mm_bearer_status >*/
typedef enum { /*< underscore_name=mm_bearer_connection_status >*/
MM_BEARER_CONNECTION_STATUS_UNKNOWN,
MM_BEARER_CONNECTION_STATUS_DISCONNECTED,
MM_BEARER_CONNECTION_STATUS_DISCONNECTING,
MM_BEARER_CONNECTION_STATUS_CONNECTED,
MM_BEARER_CONNECTION_STATUS_CONNECTION_FAILED,
} MMBearerConnectionStatus;