summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2016-04-13 12:16:57 -0500
committerDan Williams <dcbw@redhat.com>2016-04-15 11:49:03 -0500
commit80ce59385fb0bc24f9167f339d5673ede735e1c3 (patch)
tree255e7b7a1f8fb5f4b8298cc2c42245ab4c09d16c
parent650f4a2e911e5d626e1c9d365437c7e1b2191fcc (diff)
downloadModemManager-80ce59385fb0bc24f9167f339d5673ede735e1c3.tar.gz
bearer-qmi: listen for Packet Service Status indication and handle disconnection
Hook up to the WDS Packet Service Status indication, listen for disconnection events, and disconnect the bearer when we get one.
-rw-r--r--src/mm-bearer-qmi.c139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c
index 74e74f7d6..e750bf240 100644
--- a/src/mm-bearer-qmi.c
+++ b/src/mm-bearer-qmi.c
@@ -49,7 +49,11 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBearerQmiPrivate {
/* State kept while connected */
QmiClientWds *client_ipv4;
+ guint packet_service_status_ipv4_indication_id;
+
QmiClientWds *client_ipv6;
+ guint packet_service_status_ipv6_indication_id;
+
MMPort *data;
guint32 packet_data_handle_ipv4;
guint32 packet_data_handle_ipv6;
@@ -214,17 +218,24 @@ reload_stats (MMBaseBearer *self,
/*****************************************************************************/
/* Connect */
+static void common_setup_cleanup_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ gboolean enable,
+ guint *indication_id);
+
typedef enum {
CONNECT_STEP_FIRST,
CONNECT_STEP_OPEN_QMI_PORT,
CONNECT_STEP_IPV4,
CONNECT_STEP_WDS_CLIENT_IPV4,
CONNECT_STEP_IP_FAMILY_IPV4,
+ CONNECT_STEP_ENABLE_INDICATIONS_IPV4,
CONNECT_STEP_START_NETWORK_IPV4,
CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4,
CONNECT_STEP_IPV6,
CONNECT_STEP_WDS_CLIENT_IPV6,
CONNECT_STEP_IP_FAMILY_IPV6,
+ CONNECT_STEP_ENABLE_INDICATIONS_IPV6,
CONNECT_STEP_START_NETWORK_IPV6,
CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6,
CONNECT_STEP_LAST
@@ -247,6 +258,7 @@ typedef struct {
gboolean ipv4;
gboolean running_ipv4;
QmiClientWds *client_ipv4;
+ guint packet_service_status_ipv4_indication_id;
guint32 packet_data_handle_ipv4;
MMBearerIpConfig *ipv4_config;
GError *error_ipv4;
@@ -254,6 +266,7 @@ typedef struct {
gboolean ipv6;
gboolean running_ipv6;
QmiClientWds *client_ipv6;
+ guint packet_service_status_ipv6_indication_id;
guint32 packet_data_handle_ipv6;
MMBearerIpConfig *ipv6_config;
GError *error_ipv6;
@@ -267,6 +280,20 @@ connect_context_complete_and_free (ConnectContext *ctx)
g_free (ctx->apn);
g_free (ctx->user);
g_free (ctx->password);
+
+ if (ctx->packet_service_status_ipv4_indication_id) {
+ common_setup_cleanup_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ FALSE,
+ &ctx->packet_service_status_ipv4_indication_id);
+ }
+ if (ctx->packet_service_status_ipv6_indication_id) {
+ common_setup_cleanup_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ FALSE,
+ &ctx->packet_service_status_ipv6_indication_id);
+ }
+
g_clear_error (&ctx->error_ipv4);
g_clear_error (&ctx->error_ipv6);
g_clear_object (&ctx->client_ipv4);
@@ -745,6 +772,75 @@ set_ip_family_ready (QmiClientWds *client,
}
static void
+packet_service_status_indication_cb (QmiClientWds *client,
+ QmiIndicationWdsPacketServiceStatusOutput *output,
+ MMBearerQmi *self)
+{
+ QmiWdsConnectionStatus connection_status;
+
+ if (qmi_indication_wds_packet_service_status_output_get_connection_status (
+ output,
+ &connection_status,
+ NULL,
+ NULL)) {
+ MMBearerConnectionStatus bearer_status = mm_base_bearer_get_status (MM_BASE_BEARER (self));
+
+ if (connection_status == QMI_WDS_CONNECTION_STATUS_DISCONNECTED &&
+ bearer_status != MM_BEARER_CONNECTION_STATUS_DISCONNECTED &&
+ bearer_status != MM_BEARER_CONNECTION_STATUS_DISCONNECTING) {
+ QmiWdsCallEndReason cer;
+ QmiWdsVerboseCallEndReasonType verbose_cer_type;
+ gint16 verbose_cer_reason;
+
+ if (qmi_indication_wds_packet_service_status_output_get_call_end_reason (
+ output,
+ &cer,
+ NULL))
+ mm_info ("bearer call end reason (%u): '%s'",
+ cer,
+ qmi_wds_call_end_reason_get_string (cer));
+
+ if (qmi_indication_wds_packet_service_status_output_get_verbose_call_end_reason (
+ output,
+ &verbose_cer_type,
+ &verbose_cer_reason,
+ NULL))
+ mm_info ("bearer verbose call end reason (%u,%d): [%s] %s",
+ verbose_cer_type,
+ verbose_cer_reason,
+ qmi_wds_verbose_call_end_reason_type_get_string (verbose_cer_type),
+ qmi_wds_verbose_call_end_reason_get_string (verbose_cer_type, verbose_cer_reason));
+
+ mm_base_bearer_report_connection_status (MM_BASE_BEARER (self), MM_BEARER_CONNECTION_STATUS_DISCONNECTED);
+ }
+ }
+}
+
+static void
+common_setup_cleanup_unsolicited_events (MMBearerQmi *self,
+ QmiClientWds *client,
+ gboolean enable,
+ guint *indication_id)
+{
+ if (!client)
+ return;
+
+ /* Connect/Disconnect "Packet Service Status" indications */
+ if (enable) {
+ g_assert (*indication_id == 0);
+ *indication_id =
+ g_signal_connect (client,
+ "packet-service-status",
+ G_CALLBACK (packet_service_status_indication_cb),
+ self);
+ } else {
+ g_assert (*indication_id != 0);
+ g_signal_handler_disconnect (client, *indication_id);
+ *indication_id = 0;
+ }
+}
+
+static void
qmi_port_allocate_client_ready (MMPortQmi *qmi,
GAsyncResult *res,
ConnectContext *ctx)
@@ -887,6 +983,14 @@ connect_context_step (ConnectContext *ctx)
/* Just fall down */
ctx->step++;
+ case CONNECT_STEP_ENABLE_INDICATIONS_IPV4:
+ common_setup_cleanup_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ TRUE,
+ &ctx->packet_service_status_ipv4_indication_id);
+ /* Just fall down */
+ ctx->step++;
+
case CONNECT_STEP_START_NETWORK_IPV4: {
QmiMessageWdsStartNetworkInput *input;
@@ -976,6 +1080,14 @@ connect_context_step (ConnectContext *ctx)
/* Just fall down */
ctx->step++;
+ case CONNECT_STEP_ENABLE_INDICATIONS_IPV6:
+ common_setup_cleanup_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ TRUE,
+ &ctx->packet_service_status_ipv6_indication_id);
+ /* Just fall down */
+ ctx->step++;
+
case CONNECT_STEP_START_NETWORK_IPV6: {
QmiMessageWdsStartNetworkInput *input;
@@ -1016,6 +1128,8 @@ connect_context_step (ConnectContext *ctx)
g_assert (ctx->self->priv->client_ipv4 == NULL);
if (ctx->packet_data_handle_ipv4) {
ctx->self->priv->packet_data_handle_ipv4 = ctx->packet_data_handle_ipv4;
+ ctx->self->priv->packet_service_status_ipv4_indication_id = ctx->packet_service_status_ipv4_indication_id;
+ ctx->packet_service_status_ipv4_indication_id = 0;
ctx->self->priv->client_ipv4 = g_object_ref (ctx->client_ipv4);
}
@@ -1023,6 +1137,8 @@ connect_context_step (ConnectContext *ctx)
g_assert (ctx->self->priv->client_ipv6 == NULL);
if (ctx->packet_data_handle_ipv6) {
ctx->self->priv->packet_data_handle_ipv6 = ctx->packet_data_handle_ipv6;
+ ctx->self->priv->packet_service_status_ipv6_indication_id = ctx->packet_service_status_ipv6_indication_id;
+ ctx->packet_service_status_ipv6_indication_id = 0;
ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6);
}
@@ -1357,6 +1473,11 @@ disconnect_context_step (DisconnectContext *ctx)
if (ctx->packet_data_handle_ipv4) {
QmiMessageWdsStopNetworkInput *input;
+ common_setup_cleanup_unsolicited_events (ctx->self,
+ ctx->client_ipv4,
+ FALSE,
+ &ctx->self->priv->packet_service_status_ipv4_indication_id);
+
input = qmi_message_wds_stop_network_input_new ();
qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv4, NULL);
@@ -1378,6 +1499,11 @@ disconnect_context_step (DisconnectContext *ctx)
if (ctx->packet_data_handle_ipv6) {
QmiMessageWdsStopNetworkInput *input;
+ common_setup_cleanup_unsolicited_events (ctx->self,
+ ctx->client_ipv6,
+ FALSE,
+ &ctx->self->priv->packet_service_status_ipv6_indication_id);
+
input = qmi_message_wds_stop_network_input_new ();
qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv6, NULL);
@@ -1544,6 +1670,19 @@ dispose (GObject *object)
{
MMBearerQmi *self = MM_BEARER_QMI (object);
+ if (self->priv->packet_service_status_ipv4_indication_id) {
+ common_setup_cleanup_unsolicited_events (self,
+ self->priv->client_ipv4,
+ FALSE,
+ &self->priv->packet_service_status_ipv4_indication_id);
+ }
+ if (self->priv->packet_service_status_ipv6_indication_id) {
+ common_setup_cleanup_unsolicited_events (self,
+ self->priv->client_ipv6,
+ FALSE,
+ &self->priv->packet_service_status_ipv6_indication_id);
+ }
+
g_clear_object (&self->priv->data);
g_clear_object (&self->priv->client_ipv4);
g_clear_object (&self->priv->client_ipv6);